ウォークフォワード検証とは?過剰最適化を見破る方法【Python実装】
「バックテストでは年利30%だったのに、実運用したら負けた」——これは初心者だけでなく、経験者でも陥る罠です。原因のほとんどは 過剰最適化(オーバーフィッティング) です。
そして、それを見破る唯一の現実的な方法が ウォークフォワード検証(Walk-Forward Validation, WFV) です。
私自身、ある戦略をバックテストしたら「3窓でCAGR +14%」という好成績が出ました。しかし窓を増やして正しく検証し直したら +7.4% まで落ちました。この記事では、その経験をもとにWFVの考え方とPython実装を解説します。
⚠️ 本記事は教育目的の情報共有です。投資は自己責任で行ってください。
目次
- なぜ普通のバックテストは嘘をつくのか
- ウォークフォワード検証とは
- Pythonでの実装イメージ
- 「過剰最適化」を見破る指標
- 実例:3窓 vs 多窓で結果がどう変わったか
- まとめ
1. なぜ普通のバックテストは嘘をつくのか
普通のバックテストは、過去データ全体でパラメータを調整します。
例:「移動平均は20日と50日が一番成績が良かった」
しかしこれは「過去にたまたま一番当てはまった数字」を選んだだけかもしれません。未来の相場でも20日・50日が最適とは限らない。これが過剰最適化です。
過剰最適化された戦略は、過去データには完璧にフィットしますが、未来(=実運用)ではただのノイズに負けます。
2. ウォークフォワード検証とは
WFVは「過去で最適化したパラメータが、その先の未知のデータで通用するか」を測る手法です。
仕組みはシンプルです。
[訓練窓(IS)] → ここでパラメータを最適化
↓
[検証窓(OOS)] → 最適パラメータを「未知のデータ」に適用、成績を記録
↓
窓を前にスライドして繰り返す
- IS(In-Sample)= 訓練窓:パラメータを決める期間
- OOS(Out-of-Sample)= 検証窓:決めたパラメータを試す「未来」の期間
OOSの成績だけが本物です。ISでどれだけ勝っても、それは「カンニングした答案」にすぎません。
具体例(2年訓練 → 1年検証 を前進)
IS 2021-2022 → OOS 2023
IS 2022-2023 → OOS 2024
IS 2023-2024 → OOS 2025
各OOS(2023, 2024, 2025)の成績を集めたものが、戦略の「真の期待値」です。
3. Pythonでの実装イメージ
擬似コードで全体像を示します(戦略部分は自分のものに置き換え)。
import pandas as pd
def walk_forward(data, param_grid,
train_years=2, test_years=1):
results = []
start = data.index.min()
while True:
train_end = start + pd.DateOffset(years=train_years)
test_end = train_end + pd.DateOffset(years=test_years)
if test_end > data.index.max():
break
# 訓練窓・検証窓を切り出す
is_data = data[(data.index >= start) & (data.index < train_end)]
oos_data = data[(data.index >= train_end) & (data.index < test_end)]
# ① IS でパラメータ最適化(グリッドサーチ)
best_param, best_score = None, -1e9
for param in param_grid:
score = backtest(is_data, param) # 自分の戦略
if score > best_score:
best_score, best_param = score, param
# ② OOS で best_param を適用(ここが本番)
oos_score = backtest(oos_data, best_param)
results.append({
"test_period": f"{train_end.date()}〜{test_end.date()}",
"best_param": best_param,
"is_score": best_score, # 訓練窓の成績(参考)
"oos_score": oos_score, # ★これが本物
})
# 窓を前進
start += pd.DateOffset(years=test_years)
return pd.DataFrame(results)
ポイントは ②のOOSでは、①で決めた best_param を一切変えずに適用すること。ここで再調整したら検証になりません。
4. 「過剰最適化」を見破る指標
WFVの結果から、戦略が本物か見抜くチェックポイント。
| 指標 | 健全な状態 | 危険な状態 |
|---|---|---|
| IS成績 vs OOS成績の乖離 | 近い | OOSが大きく劣化=過剰最適化 |
| 正リターンのOOS窓の割合 | 多い(6割以上) | 半分以下=運頼み |
| 最適パラメータの安定性 | 窓ごとに近い値 | 毎回バラバラ=偶然を拾っている |
特に 「ISで+20%だったのにOOSで+3%」のような大きな劣化は、過剰最適化の典型サインです。
5. 実例:3窓 vs 多窓で結果がどう変わったか
冒頭の私の経験です。あるモメンタム戦略を検証しました。
| 検証方法 | OOS CAGR |
|---|---|
| 3窓(2年訓練→1年検証) | +14.1% |
| 多窓(2年訓練→0.5年検証・窓を増やす) | +7.4% |
3窓では「たまたま絶好調だった1年」が結果を押し上げていました。窓を増やして平均すると、楽観バイアスが薄まり +14% → +7.4% に。
さらに60年分・120窓で検証すると、2008年のような暴落局面では一窓で -60% を記録する窓もありました。3窓だけ見ていたら、この致命的なリスクを見逃していたでしょう。
教訓:窓は多いほどいい。少ない窓の好成績は信じない。
6. まとめ
- 普通のバックテストは「過去への当てはめ」で嘘をつきやすい
- WFVはOOS(未知のデータ)での成績だけを信じる手法
- IS→OOSの大きな劣化、不安定な最適パラメータは過剰最適化のサイン
- 窓は多く取る。少ない窓の好成績は楽観バイアス
- 検証で「7%が真の実力」と分かれば、過大な期待で実弾投入する悲劇を防げる
「バックテストで勝った!」で実運用に進む前に、必ずWFVを通してください。それだけで、勝てない戦略に資金を入れる事故の多くを防げます。
バックテストの基礎はこちら → yfinanceで株価バックテストを始める方法
質問や感想は X (@QuantNobu) までどうぞ。
⚠️ 免責:本記事は教育目的の情報共有であり、投資助言ではありません。実際の投資判断はご自身の責任で行ってください。