バックテストは嘘をつく — ルックアヘッド・多重検定・コストモデルの三大穴
Ember 編集部 ・ 2026-07-16 ・ 約6分 ・ quant-trading
本記事は情報提供・教育を目的としたものであり、特定の金融商品の取得・売買を推奨する投資助言ではありません。投資判断はご自身の責任で行ってください。
個人トレーダーと機関投資家の本当の差は、データでも計算資源でもありません。 検証の規律 です。素晴らしいバックテスト曲線を持った戦略が本番で溶けていくとき、原因はほぼ例外なく次の3つのどれかに帰着します。 1. ルックアヘッドバイアス — 「その時点では知り得なかった情報」がこっそり混入している 2. 多重検定 — 100回試して一番良かったものを選んだ時点で、その成績は水増しされている 3. コストモデルの甘さ — スプレッド・滑り・手数料を「あとで引けばいい」と思っている どれも知識としては有名です。それでも繰り返されるのは、 どこに・どうやって混入するかが具体的に語られない からです。この記事では、3つの穴それぞれについて「混入する典型経路」「数式でどれだけ成績が水増しされるか」「pandas レベルの具体的な対策コード」を示します。読み終わったら、自分のバックテストコードに対するチェックリストとして使ってください。 連載「アルファ以前」の第1回です。戦略のアイデアの話は一切しません — アイデアを 正しく棄却できる足場 を先に作ります。
1. ルックアヘッドバイアス — 混入は「1行」で起きる ルックアヘッドは劇的な形では混入しません。たいてい 1 行のコードです。 典型パターン1: シグナルと約定が同じバー shift(1) の有無で、平均回帰系の戦略は 成績が別物になります 。終値が平均から乖離した日に「その終値で」逆張りエントリーできるなら、それは乖離の一部をタイムマシンで取っているのと同じだからです。検算の習慣として、 shift を外したときに成績が跳ね上がる戦略は、跳ね上がった分だけ幻 だと思ってください。 典型パターン2: 正規化・学習が全期間を見ている 機械学習を挟むならさらに危険で、交差検証の fold を時系列を無視してランダムに切るだけでルックアヘッドになります(時系列は必ず 前で学習して後で検証 )。特徴量の標準化・PCA・ハイパーパラメータ探索のすべてに同じ罠があります。 典型パターン3: データ自体が未来を知っている コードが完璧でも、データが汚染されているケースです。 調整済み株価 : 分割・配当調整は「その後の情報」で過去が書き換わる。調整の仕方によっては過去時点のシグナル計算が未来の配当情報を含む 生存バイアス : 「現在の指数構成銘柄」で過去をテストすると、上場廃止・除外された負け組が最初から存在しない世界でテストしている 遅延のある公表値 : 経済指標や財務データは「対象期間」と「公表日」が違う。対象期間でジョインした時点でルックアヘッド 対策はひとつだけ: すべてのデータに「その値をいつ知り得たか(as of 日付)」を持たせ、as of でジョインする 。pandas なら merge asof がそのためにあります。 2. 多重検定 — 「一番良かったやつ」はいつも過大評価 パラメータを 100 通り試して一番良い組み合わせを選ぶ。誰でもやります。問題は、 真のエッジがゼロでも、100回試せば「見栄えのする成績」が確率的に必ず出る ことです。 エッジのない戦略のシャープレシオ(SR)の推定値は、おおまかに平均0・標準誤差 $\sigma {SR} \approx \sqrt{1/T}$($T$ は年数換算の観測数)の分布に従います。$N$ 個の独立な試行から最大値を選ぶと、その期待値はおおよそ: $E[\max SR] \approx \sigma {SR} \cdot \sqrt{2 \ln N}$ 具体的な数字にしましょう。日次リターン3年分($T = 756$、年率換算での $\sigma {SR} \approx 0.58$)でパラメータを 100 通り試すと: $E[\max SR] \approx 0.58 \times \sqrt{2 \ln 100} \approx 1.76$ エッジが本当にゼロでも、「シャープ 1.76」の戦略が期待値として見つかります 。グリッドサーチの結果画面であなたが見て「これだ」と思ったその数字は、まさにこの分布の右端です。 対策は3層あります。 1. 試行回数を記録する 。自分が何回試したかを知らなければ補正のしようがない。実験台帳(いつ・何を・何通り試したか)は多重検定対策の第一歩です 2. ホールドアウトを最後まで開けない 。直近1〜2年は「一度だけ」使う。開けた瞬間にそのデータは死ぬので、開けるのは出す直前 3. Deflated Sharpe Ratio(DSR)で補正する 。試行回数・成績のばらつき・非正規性からシャープの「合格ライン」自体を引き上げる方法(連載第3回で式ごと扱います) 3. コストモデル — 高回転戦略の成績はコストの関数 バックテストのコストは「後で引く定数」ではありません。 回転数に比例して積み上がる線形項 で、高回転戦略ではこれが支配項になります。 年間の取引コストドラッグは: $\text{年間コスト} = n \times \left(\frac{\text{spread}}{2} + \text{fee} + \text{slippage}\right)$ $n$ は年間の片道取引回数。仮に1回あたりの総コストを 1bp(0.01%)とすると、必要な「コスト前アルファ」は保有期間によってこう変わります: 平均保有期間 年間片道回数(概算) コストだけで消える年率 3ヶ月 8 0.08% 1週間 100 1.0% 1日 500 5.0% 1時間 3,000+ 30%+ 保有1時間の戦略は、 コストを引く前に年率30%稼いでいてやっと損益分岐 です。「バックテストでは勝っていたのに」の多くは、コスト 0.5bp と 1.5bp の差が生死を分ける領域で、楽観側の定数を選んでいただけです。 さらに悪いことに、コストは定数ですらありません。 スプレッドは時間帯の関数 (この連載の第2回で実データの分布を扱います) 滑りはサイズとボラティリティの関数 : 静かな時間の1ロットと、指標発表直後の10ロットは別世界 リミット注文のコストは「機会損失」 : 約定しなかったトレードは、都合の悪い方に偏って欠落する(逆選択) 実務的な推奨は、コストのパラメータを1つ選ぶのではなく、 悲観・中立・楽観の3本でエクイティカーブを引く ことです。3本の間で結論(やる/やらない)が変わる戦略は、その時点で「コスト感応度が高すぎる」という重要な情報が得られています。 チェックリスト 出す前に、これだけは機械的に確認してください。 [ ] シグナル列は必ず shift されているか。shift を外すと成績が跳ねる場合、その差分は幻 [ ] 正規化・学習・特徴量生成は「その時点までの情報」だけを使っているか(expanding / as of join) [ ] ユニバースは「当時の構成」か(生存バイアス) [ ] 何通り試したかを記録したか。$\sqrt{2 \ln N}$ 補正後もシャープは正か [ ] ホールドアウトは未開封か [ ] コスト悲観シナリオでも結論が変わらないか [ ] 約定はどちら側の価格か(bid/ask。midなら半スプレッド分の捏造) 最後の項目 — 「midで約定するバックテストは往復1スプレッド分の利益を捏造する」 — は、ローソク足データでは原理的に検出できません。それがこの連載の第2回、ティックデータの話につながります。