look-ahead bias backtest killer paano maiiwasan
Ang look-ahead bias ay pagtagas ng future data sa backtest. Alamin kung paano pinapalaki ng same bar decisions, survivorship, at revised figures ang resulta, at paano ito i-test.
Ang look-ahead bias ay ang paggamit ng impormasyon sa isang backtest na walang sinuman ang maaaring magkaroon sa sandaling ang simulated trade ay inilagay. Ang equity curve ay umaakyat, ang mga estadistika ay mukhang mahusay, at wala sa mga ito ang nabubuhay sa pakikipag-ugnayan sa isang live order. Ang pahinang ito ay tumutukoy sa bias, naglalakad sa tatlong mekanismo na gumagawa nito, at sinusukat ang bawat isa sa totoong data ng merkado.
Ano ang look-ahead bias
Ang isang backtest ay isang pahayag tungkol sa isang desisyon: sa timestamp na ito, gamit ang impormasyong ito, ang panuntunan ay ginawa ito. Ang pahayag ay totoo lamang kapag ang bawat input na binabasa ng panuntunan ay umiiral na, sa huling anyo nito, bago ang timestamp na iyon. Ang look-ahead bias ay anumang paglabag sa kundisyong iyon, at ito ay tahimik. Walang anuman sa isang spreadsheet o script ang nagrereklamo kapag ang isang row ay nagbalik ng isang halaga na may stamp na mas huli kaysa sa desisyong pinapakain nito.
Tatlong paglabag ang sumasaklaw sa karamihan ng lumalabas sa praktika. Isang desisyon na nagbabasa ng isang price bar na pinagte-trade din nito. Isang universe na binuo mula sa mga pangalan na nakalista pa rin ngayon. Isang data field na binago pagkatapos ng petsang dala nito.
Mekanismo uno: pagdedesisyon sa isang bar na pinagte-trade mo rin
Magsimula sa isang panuntunan na mukhang hindi nakakapinsala. Bumili sa lakas, ibig sabihin sa mga sesyon kung saan ang pondo ay natapos na mas mataas kaysa sa kung saan ito nagbukas. Kung hindi maingat na naka-code, ang test ay pumapasok sa pagbubukas ng sesyong iyon at lumalabas sa pagsasara ng sesyong iyon, habang ang kundisyong sinusuri nito ay ang presyo ng pagsasara ng parehong bar. Walang nakakaalam ng presyo ng pagsasara sa kampana ng pagbubukas.
Ang panel sa ibaba ay nagpapatakbo ng parehong bersyon sa SPY, ang tracker ng S&P 500, sa loob ng sampung taon ng kalendaryo. Ang unang column ay ang imposibleng isa. Ang pangalawa ay nagla-lag ng magkaparehong kundisyon ng isang sesyon, na siyang pinakamaagang maaaring kumilos ang isang funded account dito.
Ang eksaktong SQL sa likod ng bawat numero
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 yearAng paghihiwalay ay hindi banayad. Sa 2016 ang same-bar version ay nag-average ng 0.447% bawat kwalipikadong sesyon habang ang lagged version ay nag-average ng -0.008%, isang spread na 0.455 percentage points bawat araw. Sa 2025 ang dalawa ay nagbasa ng 0.636% at -0.03%. Sa humigit-kumulang 140 kwalipikadong sesyon sa isang taon, ang isang spread na kalahating percentage point bawat sesyon ay nag-compound sa isang equity curve na hindi kailanman na-print ng anumang live account.
Ang buong edge ay depinisyon. Ang pagpili ng mga sesyon na nagsara sa itaas ng kanilang pagbubukas, pagkatapos ay pagsukat ng open-to-close sa parehong mga sesyong iyon, ay sumusukat ng isang dami na garantisadong positibo. Natuklasan ng panuntunan ang sarili nitong filter.
Ang mga variant nito ay laging nakatago sa pang-araw-araw na data: isang moving average na kasama ang pagsasara ngayon at ginagamit upang mag-trade ngayon, isang signal na kinakalkula sa mga presyo na na-adjust na para sa isang split na hindi pa nangyayari, isang intraday stop na inilagay sa isang session high na hindi pa na-print. Ang karaniwang hugis ay isang decision timestamp na mas maaga kaysa sa isa sa sarili nitong mga input.
Mekanismo dalawa: isang universe na iginuhit mula sa mga nakaligtas ngayon
Ang pangalawang mekanismo ay hindi kailanman humahawak sa panuntunan. Ito ay nasa listahan ng mga pangalan na pinapayagang makita ng panuntunan.
Kunin ang isang ticker list mula sa isang kasalukuyang data provider, patakbuhin ito pabalik ng sampung taon, at ang sample ay tahimik na hindi kasama ang bawat kumpanyang nag-bankrupt, na-acquire, o na-delist sa daan. Ang panuntunan ay hindi kailanman inalok ng mga natalo. Sinusukat ng panel ang laki ng pagkukulang na iyon: bawat US-listed na pangalan na nag-trade ng hindi bababa sa 200 sesyon sa itaas ng $1 sa isang partikular na taon ng kalendaryo, ang bahagi ng cohort na iyon na nagpi-print pa rin ng mga presyo noong Hulyo 2026, at ang median calendar-year return ng buong cohort sa tabi ng median ng mga nakaligtas lamang.
Ang eksaktong SQL sa likod ng bawat numero
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 yearSa 7063 na mga pangalan na nag-clear ng bar noong 2015, 52.5% ay nagte-trade pa rin noong Hulyo 2026. Para sa 2024 cohort ang figure ay 87.5%, karamihan ay isang bagay ng lumipas na oras: ang isang pangalan noong 2024 ay may dalawang taon upang mawala at ang isang pangalan noong 2015 ay may labing-isang taon.
Ang mga return column ay nagdadala ng bias mismo. Sa 2017 ang median na pangalan ay nagbalik ng 7.51% habang ang median na nakaligtas ay nagbalik ng 11.32%, isang gap na 3.81 percentage points na ibinigay nang libre. Ang gap ay positibo sa bawat isa sa 10 taon na naka-chart. Ang isang strategy na sinubukan sa mga nakaligtas ay nagmamana nito bago gumawa ng isang desisyon, at ang mga panuntunang nagsa-screen para sa kahinaan ay nagmamana ng higit pa sa median, dahil ang mga pangalang nawala ay hindi proporsyonal na nasa bucket na iyon.
Ang correction ay isang point-in-time universe: ang listahan ng mga pangalan noong ito ay nakatayo sa bawat petsa ng rebalance, kasama ang mga delisting, na may delisted na posisyon na isinara sa huling print nito sa halip na ibinaba mula sa record.
Mekanismo tatlo: data na binago pagkatapos ng date stamp nito
Ang mga presyo ay may stamp minsan. Ang mga fundamentals ay hindi. Ang isang quarterly figure ay nagdadala ng petsa ng pagtatapos ng quarter, ay binago ng isang mas huling filing, at ang isang database na nag-iimbak lamang ng kasalukuyang bersyon ay masayang maghahatid ng binagong numero laban sa orihinal na petsa. Ang isang panuntunang nagsa-screen sa numerong iyon ay nagbabasa ng isang correction na tumagal ng mga buwan bago dumating.
Ang parehong pattern ay tumatakbo sa mga index membership list na itinayong muli sa kasalukuyang constituents, analyst estimate histories na na-overwrite ng final consensus, at corporate action adjustments na inilapat pabalik sa buong price series. Sa bawat kaso, ang nakaimbak na record ay isang present-tense snapshot na may suot na past-tense date.
Dalawang praktika ang pumipigil dito. Gumamit ng source na nagpapanatili ng mga vintage, ibig sabihin ang halaga noong ito ay kilala sa bawat petsa sa halip na ang halaga noong ito ay kilala ngayon. Kung saan ang mga vintage ay hindi available, mag-apply ng reporting lag na sapat na generous upang masakop ang tunay, at ituring ang haba ng lag na iyon bilang isang parameter na sulit subukan sa halip na isang detalye na sulit hulaan.
Ano ang hitsura ng curve kapag ang hinaharap ay tumagas
Ang look-ahead bias ay may signature: performance na nakaupo nang malayo sa kung ano ang inaalok ng pinagbabatayan na merkado. Ang isang kapaki-pakinabang na reference point ay ang hindsight ceiling, ang paglago na gagawin ng isang perpektong one-day-ahead forecast sa isang instrumento.
Ang eksaktong SQL sa likod ng bawat numero
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 yearAng ceiling ay napakalaki. Sa 2020, ang perpektong one-day foresight sa SPY ay nag-multiply ng capital ng 27.7 beses sa loob ng 253 sesyon, laban sa 16.2% para sa simpleng paghawak ng pondo. Sa pinakakalmang taon ng sampu, 2017, ang parehong perpektong forecast ay nagbalik ng 2.2 beses ng capital.
Ang gitnang column ay nagpapakita kung gaano ka-concentrate ang ceiling na iyon. Alisin ang mga sesyon na nakakuha ng higit sa 2%, at ang 2020 ay nagiging mula 16.2% hanggang -44.9%. Ang isang leak na nagkakahalaga lamang ng ilang mga sesyong iyon bawat taon ay nagpapagalaw ng isang backtest nang napakalayo. Ang isang curve na may mababaw na drawdown, mataas na hit rate, at ang pinakamagagandang araw nito ay naka-cluster sa pinakamarahas na sesyon ng merkado ay nakakuha ng audit.
Ang calibration ay mas mahalaga kaysa sa anumang solong threshold. Ang isang panuntunang nag-aangkin ng isang makinis na 40% bawat taon sa loob ng isang dekada ay nag-aangkin ng isang malaking bahagi ng hindsight ceiling, at ang tapat na tanong ay kung aling input ang nagsabi dito kung ano ang mangyayari.
Paano subukan ang isang backtest para sa look-ahead bias
- I-lag ang lahat ng isang bar. Ilipat ang bawat signal ng isang buong panahon mamaya at patakbuhin muli. Ang mga tunay na edge ay nababawasan nang kaunti. Ang mga leak ay gumuho, madalas sa wala.
- Perturb ang hinaharap. Kunin ang data pagkatapos ng bawat decision timestamp, palitan ito ng ingay o isang reshuffle, at patakbuhin muli ang decision logic. Ang bawat desisyon ay dapat lumabas na magkapareho. Anumang desisyon na gumalaw ay nagbasa ng isang bagay na hindi nito maaaring malaman.
- Ituring ang mga timestamp bilang data. Para sa bawat input, iimbak ang sandaling naging available ang isang halaga sa tabi ng sandaling inilalarawan nito. Ang isang join na hindi kailanman nagkukumpara sa dalawang column na iyon ay hindi maaaring magpatupad ng panuntunan.
- Itayo muli ang universe noong petsa. Isama ang mga delisted at acquired na pangalan, isara ang mga ito sa kanilang huling print, at patakbuhin ang parehong panuntunan sa parehong universe upang sukatin ang survivorship contribution.
- Sukatin laban sa ceiling. Ilagay ang return ng strategy sa tabi ng buy and hold figure at isang perfect-foresight figure para sa parehong window. Ang distansya mula sa ceiling ay konteksto na ang isang Sharpe ratio lamang ay hindi nagbibigay.
Ang position sizing ay nasa ibaba ng lahat ng ito. Ang isang inflated win rate ay nagpapakain ng isang inflated bet size, na kung saan ang Kelly criterion position sizing ay ginagawang isang accounting error ang isang drawdown. Ang parehong disiplina ay nalalapat bago gumalaw ang tunay na kapital: ang paper trading bago ang tunay na pera ay sumusubok sa order mechanics, at ang mga automated stack ay nagdadala ng magkaparehong panganib sa bilis ng makina, na sakop sa multi agent AI trading systems. Ang mga epekto na nakaligtas sa maingat na point-in-time testing, tulad ng low volatility anomaly, ay kawili-wili para sa eksaktong kadahilanang iyon.
FAQ
Ano ang look-ahead bias sa backtesting?
Ito ay ang paggamit ng data sa isang simulated trade na hindi available nooong ilalagay ang trade. Ang mga karaniwang anyo ay ang pagdedesisyon sa isang price bar na pinagte-trade din ng trade, pagsa-screen ng isang universe ng mga kumpanyang nakalista pa rin ngayon, at pagbabasa ng isang financial figure na binago pagkatapos ng petsang nakalakip dito.
Paano mo matutukoy ang look-ahead bias?
I-lag ang bawat signal ng isang buong panahon at patakbuhin muli: ang isang leak ay karaniwang gumuho habang ang isang tunay na edge ay dahan-dahang nababawasan. Ang isang mas malakas na test ay pinapalitan ang lahat ng data pagkatapos ng bawat decision timestamp ng ingay at kinukumpirma na ang bawat desisyon ay lumalabas na hindi nagbabago.
Ang survivorship bias ba ay pareho sa look-ahead bias?
Sila ay magkaiba ngunit sila ay naglalakbay nang magkasama. Ang survivorship bias ay isang sample na binuo mula sa mga pangalang tumagal, at ang pag-alam kung aling mga pangalan ang tumagal ay mismong impormasyon mula sa hinaharap, kaya ang isang survivor-only universe ay isang look-ahead leak na matatagpuan sa data sa halip na sa panuntunan.
Bakit mukhang napakaganda ng mga backtest na may look-ahead bias?
Ang leak ay pumipili ng mga kinalabasan sa halip na hulaan ang mga ito. Sa panel sa itaas, ang pagsa-screen ng mga sesyon sa kanilang sariling presyo ng pagsasara ay gumawa ng average session gain na humigit-kumulang 0.455 percentage points sa itaas ng lagged version sa 2016, isang advantage na depinisyon sa halip na kinita.
Ano ang point-in-time data?
Ang point-in-time data ay nag-iimbak ng bawat halaga noong ito ay kilala sa bawat makasaysayang petsa, kasama ang orihinal na figure at bawat susunod na revision, kasama ang universe ng mga securities noong araw na iyon. Ito ang reference na kailangan ng isang backtest upang ipakita na ang mga input nito ay umiral bago ang mga desisyon nito.
Ang bawat figure sa itaas ay isang naka-imbak, naka-version na query sa totoong pang-araw-araw na bar. Palawakin ang SQL ng anumang panel, o patakbuhin ang lag test sa iyong sariling panuntunan sa Strasmore terminal.