「バックテストで年利25%出た!これはいける!」
そう思って実際にトレードしたら、最初の2週間で資金の8%が消えた。僕の話だ。
当時は何が悪いのか全くわからなかった。パラメータも丁寧に最適化したし、バックテスト結果も綺麗だった。なのになぜ?
後から気づいたのは、僕がやっていたのは「過去に最適化した戦略を未来に当てはめる」という、アルゴトレードで最もやりがちな罠だった。いわゆる過学習(オーバーフィッティング)だ。
過学習とは何か
過学習とは、モデルやパラメータが過去のデータに最適化されすぎて、未知のデータ(つまり未来)に対してうまく機能しない状態のことだ。
株のバックテストで言えば、「2021〜2023年のデータでRSI=13、保有5日が最適」と出ても、それは2021〜2023年という特定の相場環境に合わせた数字であって、2024〜2025年に同じ条件が機能するとは限らない。
過学習を見抜く3つのチェックポイント
① インサンプル/アウトオブサンプル分割
データを「最適化に使う期間(インサンプル)」と「検証に使う期間(アウトオブサンプル)」に分ける。最低でも全期間の30%はアウトオブサンプルとして残す。
# 期間分割の例
import pandas as pd
df = pd.read_csv("prices.csv", parse_dates=["Date"])
split_date = "2023-01-01"
in_sample = df[df["Date"] < split_date] # パラメータ最適化に使う
out_sample = df[df["Date"] >= split_date] # 検証専用(触らない!)
② パラメータ数に対してトレード数が多いか
パラメータ3つで最適化したなら、インサンプルのトレード数が最低でも30〜50件はほしい。トレード数が少なすぎる状態での最適化は、ほぼ確実に過学習になる。
③ ウォークフォワード分析で頑健性を確認
これが一番確実な方法だ。データを時系列に沿って複数の窓に分割し、「最適化→検証→次の窓へ移動」を繰り返す。
import numpy as np
def walk_forward_test(df, strategy_fn, optimize_fn,
train_months=12, test_months=3):
results = []
dates = pd.date_range(df["Date"].min(), df["Date"].max(), freq="MS")
for i in range(0, len(dates) - train_months - test_months, test_months):
train_end = dates[i + train_months]
test_end = dates[i + train_months + test_months]
train = df[(df["Date"] >= dates[i]) & (df["Date"] < train_end)]
test = df[(df["Date"] >= train_end) & (df["Date"] < test_end)]
best_params = optimize_fn(train) # 訓練期間で最適化
result = strategy_fn(test, best_params) # テスト期間で検証
results.append(result)
return pd.DataFrame(results)
# アウトオブサンプル全期間の勝率・期待値が安定しているか確認
wf = walk_forward_test(df, run_backtest, optimize_params)
print(wf.describe())
ウォークフォワードの結果が、インサンプルのバックテスト結果と大きく乖離するなら過学習の可能性が高い。目安は「アウトオブサンプルの期待値がインサンプルの50%以上」だ。
まとめ:過学習を疑うべきサイン
- バックテストが美しすぎる(勝率80%超、ほぼ右肩上がり)
- パラメータが小数点以下まで最適化されている
- 実運用に移した途端に結果が大きく変わる
僕が今使っているのは、シグナル条件はシンプルに3つ以内に絞って、ウォークフォワードで2年以上の期間を検証するやり方だ。地味だけど、これが一番信頼できる。
次は実際のウォークフォワード結果を使ったパラメータ安定性の可視化を試してみようと思っている。

