Look-Ahead Bias: бір бардың қатесі таза шудан қалай 15 Sharpe жасап шығарады
"Backtests Without Illusions" сериясының бөлігі.
📄 Бұл мақала зерттеу мақаласына айналды. Үш нәзік look-ahead ағып кетуі белгілі ground truth-қа қарсы бақыланатын тестке салынды (4000 симуляцияланған тарих). Мақаланы онлайн оқыңыз (интерактивті нұсқа + PDF) lookahead.marketmaker.cc сайтында, код пен деректер github.com/suenot/lookahead-inflation мекенжайында.
Бірнеше апта бұрын біздің параметрлерді іздеу benchmark-ымыз бізді алдап жатты, біз мұны байқамай қалатын едік.
Қозғалтқыш таза көрінді. Жабық бар логикасы, адал rolling walk-forward бөлінуі, параметрлер кеңістігі бойынша Sobol/QMC іздеуі, бөлек ұсталған тест терезесі. Іздеу in-sample жақсы көрінетін конфигурацияларды тапты. Жалғыз мәселе: out-of-sample нәтижелердің барлығы дерлік теріс болды. Біз стратегия жай ғана әлсіз деп ойладық.
Содан кейін біз бір жолды таптық. Сигнал i барының жабылуында шешілетін, бірақ орындалу сол бір i барында тіркелетін, келесі бардың ашылуында емес. Орындау индексіндегі off-by-one қатесі. Біз орындалуды open[i+1]-ге ауыстырдық — бар i жабылуын көргеннен кейін нақты мәміле жасауға болатын жалғыз баға — және out-of-sample нәтижесі таңбасын өзгертті. Sobol іздеуі шығыннан пайдаға көшті. Стратегияда еш нәрсе өзгерген жоқ. Біз жай ғана өткенде сауда жасауды тоқтаттық.
Бұл look-ahead bias, әрі алаңдатарлығы — қате қаншалықты кішкентай болғаны және салдары қаншалықты үлкен болғаны. Бұл мақала — бақыланатын өзін-өзі аудиттеу: біз ground truth құрылымы бойынша белгілі симулятор құрамыз, нәзік ағып кетулерді бір-бірден енгіземіз және әрқайсысы бэктестті нақты қаншалықты ісіндіретінін өлшейміз. Басты нәтиже: ешбір нақты edge болмаса да, бір бардағы орындалу таза шудан +14.8 жылдық Sharpe жасап шығарады.
Look-ahead bias шын мәнінде не?

Look-ahead bias — бұл сіздің pipeline-ыңыздағы шешім немесе өлшем нақты уақытта, қолданылған сәтте қолжетімді болмайтын ақпаратты пайдаланатын кез келген нүкте. Оқулық мысалдары дөрекі — қаңтарда акцияның толық жылдық пайдасын пайдалану немесе әлі жарияланбаған қайта есептеу. Оларды байқау оңай. Код шолуынан аман қалатындары нәзік және үш жерде жасырылады:
- Орындалу — сіз
iбарында шешім қабылдайсыз жәнеiбарында орындайсыз (немесе сигналды тудырған сол барда стоп үшінiбарының жоғарғы/төменгі мәнін пайдаланасыз). Сізді іске қосқан нәрсемен корреляцияланған бағамен мәміле жасайсыз. - Нормализация — сіз бүкіл серия бойынша, соның ішінде болашақты қоса есептелген статистиканы пайдаланып, ерекшелікті z-score, min-max немесе басқаша масштабтайсыз. Scaler тест жиынтығын "біледі".
- Индикаторлар / ерекшеліктер — сіз орталықтандырылған (немесе басқаша алдын ала қарайтын) тереземен тегістейсіз немесе сүзесіз, сондықтан
iбарындағы мән қазірдің өзіндеi+1барының бір бөлігін қамтиды.
Үшеуі де машиналық оқыту әдебиеті leakage деп атайтын нәрсенің түрлері: мақсаттың болашағынан алынған ақпаратпен оқыту/бағалаудың ластануы (Kaufman et al., 2012; Kapoor & Narayanan, 2023). Қаржыда канондық еңбек — López de Prado-ның Advances in Financial Machine Learning (2018) кітабы — purged cross-validation, embargoing, бэктестингтің қауіптері. Point-in-time тәртібі кем дегенде Fama & French (1992) еңбегіне дейін барады, олар бухгалтерлік деректерді әдейі алты айға кешіктіреді, сол арқылы айнымалы ол түсіндіретін кірістен бұрын белгілі болады.
Бұл мақала жауап беретін сұрақ сандық сипатта: "leakage жаман ба" емес (бәрі келіседі), керісінше "әр форма қанша Sharpe ұпайын сатып алады, әрі қайсылары қауіпті?" Санынсыз бұл туралы ойлана алмайсыз. +0.3 ісінудің шу екенін немесе +14 ісінудің дәлел екенін ажырата алмайсыз.
Белгілі ground truth-і бар симулятор

Ісінуді өлшеу үшін ақиқатты білу керек. Нақты деректер ешқашан ақиқатты айтпайды — олар бір ғана жүзеге асуды береді, оракул жоқ. Сондықтан біз edge-ті өзіміз белгілейтін синтетикалық нарық құрамыз.
Деректер генерациялау процесі қатаң түрде себеп-салдарлық (causal) және жарылмайтын:
Мұнда — экзогенді тұрақты жасырын дрейф (φ = 0.95 бар AR(1)), ал бар кірісі бір бар бұрын белгілі болатын кішкентай дрейфіне ие. өткен кірістерге тәуелді болмағандықтан, кері байланыс жоқ және ештеңе жарылмайды. параметрі қанша нақты edge бар екенін реттейтін тұтқа:
- — null: мүлдем edge жоқ. Кез келген оң бэктест Sharpe-і 100% жасанды.
- — нақты, сауда жасауға болатын edge: адал momentum ережесі шынымен ақша табады.
Стратегия әдейі қарапайым — momentum таңба ережесі. Ерекшелік — trailing- кірістер сомасы ( бар), ал позиция — оның таңбасы:
csum = np.concatenate(([0.0], np.cumsum(r))) # csum[k] = sum r[0..k-1]
mom = np.full(n, np.nan)
tt = np.arange(L - 1, n)
mom[tt] = csum[tt + 1] - csum[tt - L + 1]
signal = np.sign(mom) # position for the next bar
Бұл momentum ерекшелігі бір-бар ағып кетуін зерттеу үшін тамаша құрал, өйткені онда нақты индикаторлар бөлісетін қасиет бар: ол ағымдағы барды механикалық түрде қамтиды. mom[t] құрамында r[t] бар. Сондықтан r[t]-ны мәміле ретінде тіркесеңіз, сіз өзіңіздің сигналыңыздың ішінде қазірдің өзінде бар шамаға жартылай тігесіз. Бұл — нақтыланған ағып кету.
Баптау: (бар сайын 1% волатильділік), 0.00045 бір бағыттағы комиссия (round-trip 0.09%, біздің қозғалтқышымызға сәйкес), Sharpe арқылы жылдық есептеледі (сағаттық бар), әрқайсысы 4000 бардан тұратын 4000 тәуелсіз тарих. Барлығы seeded және детерминистік.
Адал pipeline (жалғыз сауда жасауға болатын)
t барының жабылуында шешім қабылдау, келесі бардың кірісін табу, позиция өзгерген кезде комиссия төлеу:
def sharpe(sig, ret_booked):
dpos = np.abs(np.diff(np.concatenate(([0.0], sig))))
pnl = sig * ret_booked - FEE_ONEWAY * dpos
return pnl.mean() / pnl.std() * np.sqrt(8760)
honest = sharpe(signal[idx], r[idx + 1]) # earn r[t+1]: tradable
Үш ағып кету, әрқайсысы жалғыз хирургиялық өзгеріс
same_bar = sharpe(signal[idx], r[idx])
z_full = (mom - mom[valid].mean()) / mom[valid].std()
norm_full = sharpe(np.sign(z_full[idx]), r[idx + 1])
z_sm = (mom[:-2] + mom[1:-1] + mom[2:]) / 3.0 # uses t-1, t, t+1
indicator = sharpe(np.sign(z_sm[idx]), r[idx + 1])
Әр ағып кету адал pipeline-нен бар-жоғы бір жол қашықтықта. Бүкіл мәселе осында: бұлар экзотикалық қателер емес, олар шолудан өтетін нәрселер.
Нәтижелер: әр ағып кетудің шамасы

4000 seed бойынша жүргізілген, мұнда әр pipeline хабарлайтын жылдық Sharpe, null (edge жоқ) кезінде және нақты edge (, адал Sharpe сенімді +1.57 болатындай реттелген) кезінде:
| Pipeline | Null (edge жоқ) | Нақты edge |
|---|---|---|
| Адал (ақиқат) | −0.74 | +1.57 |
| Same-bar орындалу | +14.79 | +15.85 |
| Индикатор алдын ала қарауы (1 бар) | +4.76 | +6.62 |
| Whole-series нормализация | −0.84 | +1.46 |
Seed-тер бойынша 95% сенімділік интервалдары әр ұяшықта ±0.05 немесе одан тар; әсер нақты жерде ісінуге жасалған paired t-тестілер астрономиялық түрде маңызды (t > 400, p ≈ 0).
Алдымен null бағанын оқыңыз, өйткені бұл ең таза мүмкін эксперимент: edge жоқ, сондықтан адал pipeline дұрыс түрде ақша жоғалтады (−0.74, шуды сауда жасау үшін комиссия төлеудің кедергісі). Енді ағып кетулер сол ешнәрсеге не істейтінін көріңіз:
- Same-bar орындалу: −0.74 → +14.79. Ешбір болжамдық қуаты жоқ, кездейсоқ шуды сауда жасайтын стратегия шамамен 15 жылдық Sharpe хабарлайды. Бұл нәзік bias емес; бұл жасандылық. Механизм дәл біз құрастырған нәрсе: momentum ерекшелігінде
r[t]бар, сондықтанr[t]-ны тіркеу — өз сигналыңызға тігу деген сөз. - Индикатор алдын ала қарауы: −0.74 → +4.76. Тегістеушіге болашаққа бір бар қарауға рұқсат беру шудан 5-ке жақын Sharpe жасайды, өйткені
tбарындағы тегістелген мән енді сіз таба жатырғанr[t+1]-мен корреляцияланады. - Whole-series нормализация: −0.74 → −0.84. Іс жүзінде ешбір ісіну жоқ. Бұл адал, айқын емес нәтиже (төменде толығырақ).
Edge бағаны одан да қатерлі хабарды береді. Нақты edge бар болғанда (адал +1.57), ағып кетулер тек тұрақтыны қоспайды — олар өлшенген Sharpe-ті сіз шынымен сауда жасай алатын +1.57-ден әлдеқайда жоғары, +15.85 және +6.62-ге дейін итереді. Сондықтан өлшенген сан шеберлікті ағып кетуден ажырата алмайды. Ағып кеткен +6 мен адал +6 есепте бірдей көрінеді. Сіз капитал салғаннан кейін ғана қайсысы болғанын білесіз.
Ағып кету — градиент, ауыстырғыш емес

Табиғи қарсылық: "бүкіл сигнал барын тіркеу — экстремалды, шынайы емес қате." Сондықтан біз дозаны сынадық — ағып кету ұстап алатын сигнал барының бөлігі, 0-ден (адал) 1-ге (толық same-bar) дейін:
| Ұстап алу бөлігі | Null Sharpe | Edge Sharpe |
|---|---|---|
| 0.00 (адал) | −0.74 | +1.57 |
| 0.25 | +3.90 | +6.41 |
| 0.50 | +9.86 | +12.20 |
| 1.00 (толық ағып кету) | +14.79 | +15.85 |
Сигнал барының тек төрттен бірін ұстап алу edge-і жоқ стратегияны −0.74-тен +3.90-ге дейін жеткізеді. Алдануыңыз үшін толық off-by-one қажет емес; сәл ыңғайлырақ орындалу — сигнал барындағы оптимистік slippage тиюі, оны тудырған сол барға қарсы тексерілген intrabar стоп — көптеген "орналастыруға болатын" шектерден өту үшін жеткілікті. Ісіну сіз қазіргі уақыттың қанша бөлігін сауда жасауға өзіңізге рұқсат беретініңізге қарай тегіс және монотонды.
Бұл шығынды стратегияны қаншалықты жиі өндіріске жібереді?
Практикті алаңдату керек сан — жалған орналастыру жылдамдығы: ағып кету шынымен ақша жоғалтатын конфигурацияны сіз оны жасыл жарыққа шығару үшін қолданатын шектен қаншалықты жиі өткізеді. "Жылдық Sharpe ≥ 1.0" орналастыру критерийі ретінде пайдаланып, null кезінде:
- Same-bar орындалу: edge-і жоқ стратегиялардың 68%-ы орналастыруға болатындай көрінеді және шынымен ақша жоғалтады. Таза шудың үш конфигурациясының екеуі Sharpe-≥-1 қақпасынан өтіп, тірі жүйеде ақша жоғалтатын еді. (Бұл жылдамдық мұнда таза анықталған, өйткені ағып кету таза орындалуда; адал баламасы — адал орындалуы бар сол сигнал.)
- Индикатор алдын ала қарауы: ол іс жүзінде edge-і жоқ әрбір конфигурацияны да орналастыру шегінен өткізеді (99.9% Sharpe ≥ 1-ден өтеді) — ол шуды тікелей өндіріске сілтейтін еді.
- Whole-series нормализация: 12% шектен өтеді — бұл негізінен шудың базалық жылдамдығы, ешбір leakage сыйақысы жоқ.
Таксономия, әрі әрқайсысын қалай анықтау керек
Үш ағып кету бірдей қауіпті емес, әрі айырмашылықтар пайдалы сабақ береді.
1. Орындалу leakage-і (қымбат түрі)

Симптом: орындалу бағасы сигналмен корреляцияланған, өйткені олар бір бардан келеді. Шамасы: орасан зор (толық доза кезінде шудан +15, ширек доза кезінде +3.9). Неге ол ең жаманы: сіздің сигналыңыз, дерлік анықтамасы бойынша, соңғы баға қозғалысынан құрылады, сондықтан сигнал барының кірісі — сіздің ерекшелігіңіз ең көп корреляцияланатын дәл сол нәрсе. Оны тіркеу жауапты іздеп табуға жақын.
Анықтау — бір-бар ауыстыру тесті. Бұл осы мақаладағы ең құнды диагностика. Бэктестіңізді алып, әр орындалуды бір барға кейінірек ауыстырыңыз (i-де шешім қабылдау, open[i+1]-де орындау). Егер нәтиже әрең қозғалса, орындалуыңыз адал болды. Егер нәтиже құлдырап кетсе немесе таңбасын өзгертсе, сіз өткенде сауда жасап жаттыңыз. Дәл осы біздің Sobol іздеуімен болды: орындалуларды ауыстырыңыз, "пайдалы" OOS шығын болып шықты — дұрысырағы, ағып кету жойылғаннан кейін нақты байланыс беткейге шықты.
entry_price = open_[i + 1] # NOT close[i], NOT open[i]
2. Индикатор / ерекшелік leakage-і (тыныш түрі)
Симптом: i барындағы индикатор i+1 немесе одан кейінгі деректерге тәуелді — орталықтандырылған жылжымалы орта, себеп-салдарлық кешігуі жоқ сүзгі, растау үшін болашақ барлар қажет ететін шың/аңғар белгісі, болашақ шамдарды қабылдайтын Heikin-Ashi тәрізді түрлендіру. Шамасы: үлкен (шудан +4.8). Неге жасырынады: ағып кету кітапхана шақыруының ішінде көмілген. scipy.signal.filtfilt нөлдік-фазалы — ал нөлдік-фаза себеп-салдарлық емес дегенді білдіреді. "Бұл бар — локалды максимум" ерекшелігі келесі бар шыққанға дейін белгісіз болып қалады.
Анықтау: әр индикатор үшін сұраңыз: ол оқитын ең жоғары индекс қандай? Егер t-дегі мәнді есептеу қашанда t+1-ге тисе, ол себеп-салдарлық емес. Индикаторларды кеңейетін/жылжымалы себеп-салдарлық терезеде есептеп, t-ден кейінгі барлар массивте бар-жоғына қарамастан t барындағы мән бірдей екенін тексеріңіз. (Біздің HMA/ADX іске асыруларымыз бұл тесттен өтеді: t-дегі әр шығыс тек ≤ t кірістерін оқиды.)
3. Нормализация leakage-і (арнаға тәуелді түрі)
Симптом: scaler (StandardScaler, min-max, ғаламдық z-score) тест жиынтығын қоса, бүкіл деректер жиынына бейімделеді. Канондық ML ескертулері бұл туралы нақты айтады — Hastie, Tibshirani & Friedman-ның Elements of Statistical Learning §7.10.2 ("the wrong and right way to do cross-validation") және scikit-learn-нің өзінің жалпы қателер бойынша нұсқаулығы: "the average should be the average of the train subset, not the average of all the data."
Біздің тестегі шамасы: ≈ нөл (−0.74 → −0.84). Бұл — таңғаларлық, адал нәтиже, әрі оны жаттап алудың орнына түсіну маңызды.
Ол неге ісінбеді? Себебі біздің стратегия ерекшелікті тек оның таңбасы арқылы пайдаланады (нөлдік шек). Стандартты ауытқу арқылы масштабтау ешқашан таңбаны өзгертпейді, ал ғаламдық-орта-центрлеу нөлдік кесіп өтуді сәл ғана жылжытады. Сондықтан таза таңба ережесінің whole-series стандарттауы дерлік зиянсыз.
Мұны шамадан тыс жалпылап жіберменіз. Нормализация leakage-і арнаға тәуелді. Стратегияңыз ерекшеліктің шамасын пайдаланған сәтте — z-score-ке пропорционал позиция мөлшерлеу, масштабталған үлестірімге қарап таңдалған нөлден ерекшеленетін кіру шегі, стандартталған кірістерді қабылдайтын нейрондық желі — болашақты білетін scaler маңызды бола бастайды, әрі ғаламдық статистика себеп-салдарлықтан неғұрлым көп ерекшеленсе, соғұрлым маңызды бола түседі. Біздің нәтиже "нормализация leakage-і қауіпсіз" дегенді білдірмейді. Ол "leakage шамасы ағып кеткен шама шешімге кіретін арнаға байланысты, әрі оны болжамдаудың орнына өлшеу керек" дегенді білдіреді. Таңба ережесі — бұл нақты ағып кетудің арзан болатын жалғыз жағдайы.
Бұл қайда байланысады
Look-ahead bias — осы серия құжаттап келе жатқан тізбектің бірінші буыны:
- Ол валидацияның кірісін ластайды. Ағып кеткен бэктест walk-forward бөлінуінен кедергісіз өтіп, overfit шыңынан гөрі кең плато сияқты көрінеді — ағып кету қатпарлар (folds) бойынша тұрақты, сондықтан cross-validation оны ұстай алмайды. Leakage — overfitting-тен жоғарыдағы ақаулық режимі, әрі одан кейінгі ешбір адал валидация сізді құтқармайды.
- Ол параметрлерді іздеумен әрекеттеседі: ағып кеткен деректерде мыңдаған сынақтар бойынша іздеу ағып кетуді ең агрессивті пайдаланатын конфигурацияны табады. "Жеңімпаз" — ең жаман бұзушы.
- Сол себепті бэктест-тірі жүйе паритеті алшақтайды. Ағып кету — бэктест пен бот арасындағы 30–50% алшақтықтың ең таза түсіндірмесі, өйткені тірі сауда, механикалық түрде, алдын ала қарауға болмайтын жалғыз орын.
Мұның бәрін ұстайтын тәртіп — академиялық әдебиет жылдар бойы талап етіп келе жатқан дәл сол тәртіп: бэктестті қатаң ақпараттық шекарасы бар статистикалық эксперимент ретінде қараңыз. Bailey, Borwein, López de Prado & Zhu overfitting-тің жалған өнімділікті қаншалықты оңай жасап шығаратынын көрсетті (2014); Arnott, Harvey & Markowitz-тың бэктестинг хаттамасы (2019) осы гигиенаны кодификациялайды. Look-ahead bias — барлық шекаралардың ең негізгісі — уақыттағы шекара — және кездейсоқ бұзуға ең арзаны.
Негізгі қорытындылар

- Look-ahead bias сандық тұрғыдан орасан зор әрі сапалық тұрғыдан көрінбейді. Жалғыз бір-бар орындалу қатесі −0.74 Sharpe-ті (таза шу, дұрыс шығынға ұшыраған) +14.79-ға айналдырды. Қате бір жол; салдары — жасанды track record.
- Бұл градиент. Сигнал барының тек 25%-ын ұстап алу да ешнәрседен +3.90 береді. Сізге айқын bug қажет емес — орындалуларыңыздағы сәл артық оптимизм жеткілікті.
- Өлшенген сан шеберлікті ағып кетуден ажырата алмайды. Нақты edge болғанда, ағып кетулер есепті сауда жасауға болатын ақиқаттан әлдеқайда алысқа ісіндіреді. Жалғыз қорғаныс — процесс, метрика емес.
- Бір-бар ауыстыру тесті — ең жылдам диагностикаңыз. Әр орындалуды бір барға кейінірек жылжытыңыз. Егер өнімділік құлдыраса, сіз өткенде сауда жасап жатқансыз.
- Leakage шамасы арнаға тәуелді. Орындалу мен индикатор алдын ала қарауы жойқын; таңба ережесінің whole-series нормализациясы дерлік тегін. Ағып кетуді ол шынымен кіретін арна арқылы өлшеңіз — болжамдамаңыз.
Толық бақыланатын зерттеу — үш ағып кетудің барлығы, доза сынағы, жалған орналастыру талдауы, формальды әдістер және бір детерминистік скрипттен қайта жасалатын әрбір сан — серіктес мақалада lookahead.marketmaker.cc сайтында, код пен деректер github.com/suenot/lookahead-inflation мекенжайында.
Біздің null экспериментіміздегі стратегияда мүлдем edge болмады. Ол бәрібір 15 Sharpe көрсетті. Бэктестіңіз тым жақсы көрінсе, күдіктенетін бірінші нәрсе — сіздің данышпандығыңыз емес, ол — сіздің сағатыңыз.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.