ウォークフォワード検証とは?過剰最適化を見破る方法【Python実装】


「バックテストでは年利30%だったのに、実運用したら負けた」——これは初心者だけでなく、経験者でも陥る罠です。原因のほとんどは 過剰最適化(オーバーフィッティング) です。

そして、それを見破る唯一の現実的な方法が ウォークフォワード検証(Walk-Forward Validation, WFV) です。

私自身、ある戦略をバックテストしたら「3窓でCAGR +14%」という好成績が出ました。しかし窓を増やして正しく検証し直したら +7.4% まで落ちました。この記事では、その経験をもとにWFVの考え方とPython実装を解説します。

⚠️ 本記事は教育目的の情報共有です。投資は自己責任で行ってください。

目次

  1. なぜ普通のバックテストは嘘をつくのか
  2. ウォークフォワード検証とは
  3. Pythonでの実装イメージ
  4. 「過剰最適化」を見破る指標
  5. 実例:3窓 vs 多窓で結果がどう変わったか
  6. まとめ

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) までどうぞ。

⚠️ 免責:本記事は教育目的の情報共有であり、投資助言ではありません。実際の投資判断はご自身の責任で行ってください。