0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ウォークフォワード検証とVIXフィルターで過学習を防ぐ

0
Posted at

バックテストの最大の罠は**過学習(オーバーフィッティング)**です。過去データで最適化したパラメータが未来では全く機能しない状態です。Hermes Agent ではこれを防ぐために ウォークフォワード検証 と VIX レジームフィルター を導入しています。


過学習の問題

グリッドサーチで最適化すると、過去データへの「暗記」が起きます。

過去3年データで最適化:
  RSI_ob=62, SMA20/50, RR=2.5 → SQN=3.2, WR=68%  ← 過去データでは優秀

でも未来(直近1年)では:
  同じパラメータ → SQN=0.3, WR=41%  ← 使い物にならない

「過去に当てはまる」と「未来でも機能する」は全く別の話です。


ウォークフォワード検証の仕組み

データを時系列で分割して「最適化期間(IS)」と「検証期間(OOS)」を分けます。

全データ: ─────────────────────────────────────────
              IS (最適化)          OOS (検証)
              ├──────────────────┤├──────────────┤
              2年                  1年

複数ウィンドウで繰り返す:
Window 1: IS[0-24ヶ月] → OOS[24-36ヶ月]
Window 2: IS[6-30ヶ月] → OOS[30-42ヶ月]
Window 3: IS[12-36ヶ月] → OOS[36-48ヶ月]

IS で最良パラメータを選び、OOS でそのパラメータを未来として検証します。OOS で崩れるなら過学習です。

# python/backtest/walkforward.py(概略)
def run_walkforward(df: pd.DataFrame, is_months=24, oos_months=12, step_months=6):
    results = []
    for start in range(0, total_months - is_months - oos_months, step_months):
        is_data  = df[start : start + is_months]
        oos_data = df[start + is_months : start + is_months + oos_months]

        # IS で最良パラメータを見つける
        best_params = optimize_params(is_data)

        # OOS でそのパラメータを検証
        oos_result = backtest(oos_data, best_params)
        results.append({
            "window": start,
            "is_sqn":  best_params["sqn"],
            "oos_sqn": oos_result["sqn"],
            "oos_wr":  oos_result["win_rate"],
        })
    return results

IS vs OOS の比較

OOS SQN が IS SQN の 50% 以上 を維持できているパラメータのみを採用します。

# 採用基準
is_sqn  = result["is_sqn"]
oos_sqn = result["oos_sqn"]
degradation = (is_sqn - oos_sqn) / is_sqn

if degradation > 0.5:
    print(f"❌ 過学習: IS={is_sqn:.2f} → OOS={oos_sqn:.2f} ({degradation*100:.0f}% 劣化)")
else:
    print(f"✅ 採用: IS={is_sqn:.2f} → OOS={oos_sqn:.2f}")

実際に採用したパラメータの例:

Symbol Engine IS SQN OOS SQN 劣化率
GBP_USD structural_htf 4.8 3.9 19% ✅
META structural 2.3 1.9 17% ✅
BTC/USD rebound 2.8 0.9 68% ❌ → 不採用

VIX レジームフィルター

ウォークフォワードで選んだパラメータでも、相場のレジーム(状態) が変わると機能しなくなります。VIX(恐怖指数)が高い時期(market stress)は戦略の有効性が下がることが多いため、フィルターを設けました。

# python/backtest/optimize.py(VIX フィルター)
def apply_vix_filter(df: pd.DataFrame, vix_df: pd.DataFrame, threshold=25) -> pd.DataFrame:
    """VIX が threshold を超えている期間のシグナルをフィルタリング"""
    merged = df.merge(vix_df[["date", "vix_close"]], on="date", how="left")
    # VIX が高い期間は HOLD とする
    merged.loc[merged["vix_close"] > threshold, "signal"] = 0
    return merged

バックテスト上でも、VIX フィルターを適用したほうが多くの銘柄でリスク調整後リターンが向上しました。


自動最適化パイプライン

# run_fx_optimize.sh(概略)
#!/usr/bin/env bash

# Step 1: データ取得
python fetch_oanda_data.py --symbol EUR_USD --granularity H1

# Step 2: グリッドサーチ最適化(IS)
python optimize.py --symbol EUR_USD --timeframe H1 --save-csv

# Step 3: ウォークフォワード検証
python walkforward.py --symbol EUR_USD --is-months 24 --oos-months 12

# Step 4: 採用基準チェック(OOS SQN >= 1.0)
python apply_best_params.py --symbol EUR_USD --min-oos-sqn 1.0

# Step 5: HTML レポート生成
python generate_report.py --symbol EUR_USD

このパイプラインを新しいシンボルを追加するたびに実行し、採用基準を通ったパラメータのみを strategy-params.json に追加します。


Dollar SQN vs R-Multiple SQN

最初は Dollar SQN(損益額ベース)を使っていましたが、アカウントサイズに依存してしまう問題がありました。

Dollar SQN: SQN = (avg_profit_$) / std_profit_$ × √n
R-Multiple SQN: SQN = (avg_R) / std_R × √n

R(リスク倍数)は「実際の損益 ÷ 想定リスク(エントリーからSLまで)」です。ポジションサイズに依存しないため、銘柄をまたいだ比較が正確にできます。


まとめ

手法 目的
グリッドサーチ 最良パラメータの探索
ウォークフォワード 過学習の検出・排除
VIX フィルター レジーム変化への対応
R-Multiple SQN 銘柄横断の公平な比較

バックテストで「完璧な結果」が出るほど過学習を疑うべきです。OOS で SQN が半分以下になったパラメータは実戦では機能しません。厳しい検証を通過したパラメータだけを本番に使うことが、システムの長期的な有効性を保つ鍵です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?