先読みバイアス バックテスト 対策 チェック方法
先読みバイアスは未来のデータがバックテストに漏れる問題。同バー判断、生存バイアス、修正値が結果を水増しする仕組みと検証法を解説。
先読みバイアスとは、バックテストにおいて、シミュレーション上の取引が行われた時点では誰も知り得なかった情報を使用することです。これにより、 equity curve は上昇し、統計値は優れたものに見えますが、実際の取引では全く機能しません。このページでは、このバイアスを定義し、それを生み出す3つのメカニズムを説明し、それぞれを実際の市場データで測定します。
先読みバイアスとは
バックテストとは、ある意思決定に関する主張です。「このタイムスタンプにおいて、この情報を用いれば、このルールはこう動作したはずだ」というものです。この主張が成立するのは、ルールが読み取るすべての入力が、そのタイムスタンプより前に、最終的な形で既に存在していた場合のみです。先読みバイアスとは、この条件が破られることであり、それは静かに発生します。スプレッドシートやスクリプトは、ある行が、それが供給する意思決定よりも後のタイムスタンプが付された値を返しても、何も警告しません。
実際に発生する問題のほとんどは、以下の3つの違反に分類されます。取引対象となる価格バーを読み取って意思決定を行うこと。現在も上場している銘柄のみで構成されたユニバースを使用すること。データの日付よりも後に修正されたデータフィールドを使用すること。
メカニズム1:取引対象のバーで意思決定を行う
まずは無害に見えるルールから始めましょう。「強気に買う」、つまりファンドが寄り付きより高く引けたセッションで買うというルールです。不注意にコード化すると、テストはそのセッションの寄り付きでエントリーし、引けでエグジットしますが、その判断基準は同じバーの終値です。寄り付きの時点では、誰も終値を知りません。
以下のパネルは、SPY(S&P 500トラッカー)において、10暦年にわたって両方のバージョンを実行した結果です。最初の列は不可能なバージョンです。2番目の列は、同一の条件を1セッションだけ遅らせたものです。これは、資金が入った口座がその条件に基づいて行動できる最も早いタイミングです。
各数値の背後にある正確なSQL
WITH bars AS (
SELECT date,
toFloat64(open) AS o,
toFloat64(close) AS c,
toFloat64(close) > toFloat64(open) AS up_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
lagged AS (
SELECT date, o, c, up_day,
any(up_day) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prior_up
FROM bars
)
SELECT toYear(date) AS year,
countIf(up_day) AS signal_days,
round(100 * avgIf(c / o - 1, up_day), 3) AS same_bar_avg_pct,
round(100 * avgIf(c / o - 1, prior_up), 3) AS lagged_avg_pct,
round(100 * (avgIf(c / o - 1, up_day) - avgIf(c / o - 1, prior_up)), 3) AS gap_pp
FROM lagged
WHERE date >= toDate('2016-01-01')
GROUP BY year
ORDER BY yearその差は小さくありません。2016では、同一バー版は条件を満たすセッションあたり平均0.447%であったのに対し、ラグ版は平均-0.008%であり、1日あたり0.455ポイントの差がありました。2025では、両者は0.636%と-0.03%でした。1年に約140の条件適合セッションがある場合、1セッションあたり0.5パーセントポイントの差は、実際の口座では決して達成できない equity curve に複利効果をもたらします。
この優位性のすべては定義上のものです。寄りより高く引けたセッションを選択し、それと同じセッションの寄り付きから引けまでのリターンを測定することは、プラスであることが保証された値を測定することになります。このルールは、自身のフィルターを発見したのです。
このバリエーションは、日次データに常に潜んでいます。今日の終値を含み、それを今日の取引に使用する移動平均、まだ発生していない株式分割に合わせて調整された価格で計算されたシグナル、まだ発生していないセッション高値に設定された日中ストップなどです。共通する形状は、意思決定のタイムスタンプが、自身の入力の1つよりも前に位置していることです。
メカニズム2:今日の生存者から抽出されたユニバース
2番目のメカニズムは、ルール自体には一切触れません。それは、ルールが参照を許可される銘柄のリストに存在します。
現在のデータプロバイダーからティッカーリストを取得し、それを10年前に遡って実行すると、そのサンプルは、途中で倒産した、買収された、または上場廃止になったすべての企業を静かに除外します。ルールには敗者(losers)が提示されることは決してありません。以下のパネルは、その除外の規模を測定します。特定の暦年に、1ドル以上で少なくとも200セッション取引された米国上場の全銘柄について、そのコホートのうち2026年7月時点でまだ価格を出力している銘柄の割合、およびコホート全体の暦年リターンの中央値と、生存者のみの中央値を示しています。
各数値の背後にある正確なSQL
WITH recent AS (
SELECT DISTINCT ticker
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2026-06-15')
AND date <= toDate('2026-07-28')
),
per_name AS (
SELECT toYear(date) AS year,
ticker,
argMin(toFloat64(close), date) AS first_close,
argMax(toFloat64(close), date) AS last_close,
count() AS sessions
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2015-01-01')
AND date <= toDate('2024-12-31')
AND close > 1
GROUP BY year, ticker
HAVING sessions >= 200
)
SELECT year,
uniqExact(ticker) AS names_trading,
round(100 * uniqExactIf(ticker, ticker IN (SELECT ticker FROM recent)) / uniqExact(ticker), 1) AS still_listed_pct,
round(100 * quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker)), 2) AS median_return_all_pct,
round(100 * quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent)), 2) AS median_return_survivors_pct,
round(100 * (quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent))
- quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker))), 2) AS survivor_gap_pp
FROM per_name
GROUP BY year
ORDER BY year2015年に基準をクリアした7063銘柄のうち、52.5%が2026年7月時点でまだ取引されていました。2024年のコホートでは、その数値は87.5%であり、これは主に経過時間の問題です。2024年の銘柄は消滅するまで2年、2015年の銘柄は11年の猶予があったことになります。
リターンの列にはバイアスそのものが現れています。2017年では、全銘柄の中央値リターンは7.51%であったのに対し、生存者の中央値リターンは11.32%であり、3.81ポイントもの差が無償で与えられています。この差は、チャートに示された10年すべてにおいてプラスです。生存者のみでテストされた戦略は、一度も意思決定を行うことなくこの差を継承します。そして、弱さ(weakness)をスクリーニングするルールは、消滅した銘柄がそのバケットに不均衡に多く含まれるため、中央値以上の差を継承します。
この問題の修正方法は、ポイントインタイム・ユニバースです。つまり、各リバランス日時点での銘柄リストであり、上場廃止銘柄も含み、上場廃止ポジションは記録から削除するのではなく、最終取引価格でクローズします。
メカニズム3:日付スタンプ後に修正されたデータ
価格は一度だけスタンプされます。ファンダメンタルズはそうではありません。四半期の数字には四半期末の日付が付けられますが、後の提出書類によって修正され、現在のバージョンのみを保存するデータベースは、修正された数値を元の日付に対して喜んで提供します。その数値でスクリーニングするルールは、到着に数ヶ月を要した修正値を読んでいることになります。
同じパターンは、現在の構成銘柄に再構築されたインデックスメンバーシップリスト、最終的なコンセンサスで上書きされたアナリスト予想履歴、価格系列全体に遡って適用されたコーポレートアクション調整にも見られます。それぞれのケースで、保存された記録は、過去の日付をまとった現在形のスナップショットです。
これに対処するには2つの方法があります。各日付において現在知られている値ではなく、その時点で知られていた値を保持する、ヴィンテージ(vintage)を保存するデータソースを使用すること。ヴィンテージが利用できない場合は、実際の報告ラグを十分にカバーする報告ラグを適用し、そのラグの長さを推測する詳細ではなく、テストする価値のあるパラメータとして扱うことです。
未来が漏れ込んだ場合の曲線の様子
先読みバイアスには特徴があります。それは、対象となる市場が提供するものをはるかに上回るパフォーマンスです。有用な基準点は、 hindsight ceiling(後知恵の天井)、つまり単一の銘柄に対して完全な1日前の予測がもたらしたであろう成長です。
各数値の背後にある正確なSQL
WITH bars AS (
SELECT date,
toFloat64(close) AS c,
any(toFloat64(close)) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_c
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
rets AS (
SELECT toYear(date) AS year,
c / prev_c - 1 AS r
FROM bars
WHERE date >= toDate('2016-01-01')
AND prev_c > 0
)
SELECT year,
count() AS sessions,
round(100 * (exp(sum(log(1 + r))) - 1), 1) AS buy_and_hold_pct,
round(100 * (exp(sumIf(log(1 + r), r < 0.02)) - 1), 1) AS without_big_up_days_pct,
round(exp(sum(log(1 + abs(r)))), 1) AS hindsight_ceiling_multiple
FROM rets
GROUP BY year
ORDER BY yearこの天井は巨大です。2020年では、SPYに対する完全な1日前の予測は、253セッションで資本を27.7倍にしましたが、単にファンドを保有した場合のリターンは16.2%倍でした。10年間で最も穏やかな年である2017年でも、同じ完全予測は資本の2.2倍のリターンをもたらしました。
中央の列は、その天井がどれほど集中しているかを示しています。2%以上上昇したセッションを取り除くと、2020は16.2%から-44.9%に変わります。1年にほんの一握りのそのようなセッションだけでも、バックテストは大きく動きます。浅いドローダウン、高い勝率、そして最高の日が市場の最も激しいセッションに集中している曲線は、監査を受ける価値があります。
単一の閾値よりも、キャリブレーション(較正)の方が重要です。10年間にわたって年間40%の安定したリターンを主張するルールは、後知恵の天井の大部分を主張していることになります。そして、正直な疑問は、どの入力が何が起ころうとしているかをルールに教えたのか、ということです。
バックテストの先読みバイアスをテストする方法
- すべてを1本遅らせる。 すべてのシグナルを1期間分後ろにずらして再実行します。真のエッジは少し減衰します。リークは崩壊し、多くの場合ゼロになります。
- 未来を撹乱する。 各意思決定タイムスタンプ以降のデータを、ノイズまたはシャッフルしたデータに置き換え、意思決定ロジックを再実行します。すべての意思決定は同一であるべきです。変更された意思決定は、知り得なかった何かを読み取ったことを示します。
- タイムスタンプをデータとして扱う。 各入力について、値が利用可能になった瞬間と、それが記述する瞬間を保存します。これら2つの列を比較しない結合は、ルールを強制できません。
- 日付時点のユニバースを再構築する。 上場廃止銘柄や買収された銘柄を含め、それらを最後の取引価格でクローズし、両方のユニバースで同じルールを実行して、サバイバーシップの寄与度を測定します。
- 天井に対して測定する。 戦略のリターンを、同じ期間のバイ・アンド・ホールドの数値および完全予測の数値と比較します。天井からの距離は、シャープレシオだけでは得られないコンテキストを提供します。
ポジションサイジングは、これらすべての下流にあります。水増しされた勝率は、水増しされたベットサイズにつながり、これこそがケリー基準のポジションサイジングにおいて、会計上の誤りをドローダウンに変えるものです。実際の資金を動かす前にも同じ規律が適用されます。実運用前のペーパートレーディングは注文の仕組みをテストし、自動化されたスタックは、マルチエージェントAIトレーディングシステムでカバーされているように、機械的な速度で同一の危険をはらんでいます。注意深いポイントインタイムテストに耐える効果、例えば低ボラティリティ・アノマリーは、まさにその理由で興味深いものです。
FAQ
バックテスティングにおける先読みバイアスとは何ですか?
シミュレーション上の取引において、その取引が行われた時点では利用できなかったデータを使用することです。一般的な形態としては、取引も実行する価格バーで意思決定を行うこと、現在も上場している企業のみのユニバースでスクリーニングすること、およびそのデータに付された日付よりも後に修正された財務数値を読み取ることです。
先読みバイアスをどのように検出できますか?
すべてのシグナルを1期間分遅らせて再実行します。リークは通常崩壊しますが、真のエッジは穏やかに減衰します。より強力なテストは、各意思決定タイムスタンプ以降のすべてのデータをノイズに置き換え、すべての意思決定が変更されないことを確認します。
サバイバーシップバイアスは先読みバイアスと同じですか?
これらは異なりますが、一緒に発生します。サバイバーシップバイアスは、存続した銘柄から構築されたサンプルであり、どの銘柄が存続したかを知ること自体が未来からの情報であるため、生存者のみのユニバースは、ルールではなくデータに存在する先読みリークです。
先読みバイアスがあるバックテストはなぜ非常に良く見えるのですか?
リークは結果を予測するのではなく、選択するからです。上のパネルでは、独自の終値でセッションをスクリーニングすると、2016年において、ラグ版よりも平均セッションゲインが約0.455ポイント高くなりました。これは、獲得されたものではなく、定義上の優位性です。
ポイントインタイムデータとは何ですか?
ポイントインタイムデータは、各履歴日付において知られていた各値を保存します。これには、元の数値とその後のすべての修正、およびその日の証券ユニバースが含まれます。これは、バックテストがその入力が意思決定の前に存在していたことを示すために必要な参照です。
上記のすべての数値は、実際の日次バーに対する保存されバージョン管理されたクエリの結果です。任意のパネルのSQLを展開するか、Strasmore ターミナルで独自のルールに対してラグテストを実行できます。