Look-Ahead Bias: bitta bar xatosi sof shovqindan qanday qilib 15 ta Sharpe yasaydi
"Backtests Without Illusions" seriyasining bir qismi.
📄 Bu maqola tadqiqot maqolasiga aylandi. Uchta nozik look-ahead sizib chiqishi ma'lum ground truth-ga qarshi nazorat qilinadigan sinovdan o'tkazildi (4000 ta simulyatsiya qilingan tarix). Maqolani onlayn o'qing (interaktiv versiya + PDF) lookahead.marketmaker.cc manzilida, kod va ma'lumotlar github.com/suenot/lookahead-inflation manzilida.
Bir necha hafta oldin bizning parametr qidiruv benchmark'imiz bizga yolg'on gapirayotgan edi, va biz buni deyarli sezmay qolardik.
Dvigatel toza ko'rinardi. Yopiq bar mantiqi, adolatli rolling walk-forward bo'linishi, parametrlar maydoni bo'yicha Sobol/QMC qidiruvi, ajratib qo'yilgan test oynasi. Qidiruv in-sample yaxshi ko'rinadigan konfiguratsiyalarni topdi. Yagona muammo: out-of-sample deyarli hamma narsa manfiy edi. Biz strategiya shunchaki zaif deb o'yladik.
Keyin biz bitta qatorni topdik. Signal i barining yopilishida hal qilinardi, lekin ijro xuddi shu i barida qayd etilardi, keyingi barning ochilishida emas. Ijro indeksida off-by-one xatosi. Biz ijroni open[i+1]ga ko'chirdik — i barining yopilishini ko'rgandan keyin haqiqatan ham savdo qilish mumkin bo'lgan yagona narx — va out-of-sample natija belgisini o'zgartirdi. Sobol qidiruvi zarardan foydaga o'tdi. Strategiyada hech narsa o'zgarmagan edi. Biz shunchaki o'tmishda savdo qilishni to'xtatdik.
Bu look-ahead bias, va bezovta qiluvchi joyi shundaki, xato qanchalik kichkina bo'lsa, oqibat shunchalik katta edi. Bu maqola nazorat qilinadigan o'z-o'zini audit qilishdir: biz ground truth qurilish orqali ma'lum bo'lgan simulyator quramiz, nozik sizib chiqishlarni birma-bir kiritamiz va har biri bektestni aniq qanchalik shishirishini o'lchaymiz. Asosiy xulosa: hech qanday haqiqiy edge bo'lmasa ham, bitta bardagi ijro sof shovqindan +14.8 yillik Sharpe yasaydi.
Look-ahead bias aslida nima

Look-ahead bias — bu sizning pipeline'ingizdagi qaror yoki o'lchov real vaqtda, undan foydalanilgan paytda mavjud bo'lmagan ma'lumotdan foydalanadigan har qanday nuqta. Darslik misollari qo'pol — yanvar oyida aksiyaning to'liq yillik daromadidan foydalanish, yoki hali e'lon qilinmagan qayta hisoblash. Bularni payqash oson. Kod ko'rigidan omon qoladiganlari nozik bo'ladi va uch joyda yashiringan:
- Ijro — siz
ibarida qaror qabul qilasiz vaibarida ijro qilasiz (yoki signalni yaratgan xuddi shu bardagi stoplar uchunibarining yuqori/quyi qiymatidan foydalanasiz). Siz sizni ishga tushirgan narsaga bog'liq narxda savdo qilasiz. - Normalizatsiya — siz butun seriya bo'yicha, kelajakni ham o'z ichiga olgan statistikadan foydalanib, xususiyatni z-score, min-max yoki boshqa usulda masshtablaysiz. Scaler test to'plamini "biladi".
- Indikatorlar / xususiyatlar — siz markazlashtirilgan (yoki boshqacha oldinga qaraydigan) oyna bilan tekislaysiz yoki filtrlaysiz, shuning uchun
ibardagi qiymat allaqachoni+1barining bir qismini o'z ichiga oladi.
Uchalasi ham mashina o'rganish adabiyoti leakage deb ataydigan narsaning shakllari: maqsadning kelajagidan olingan ma'lumot bilan o'qitish/baholashning ifloslanishi (Kaufman et al., 2012; Kapoor & Narayanan, 2023). Moliyada kanonik yondashuv — López de Prado'ning Advances in Financial Machine Learning (2018) kitobi — purged cross-validation, embargoing, bektestlashning xavflari. Point-in-time intizomi kamida Fama & French (1992) ishiga borib taqaladi, ular buxgalteriya ma'lumotlarini ataylab olti oyga kechiktirishadi, shunda o'zgaruvchi u tushuntiradigan daromaddan oldin ma'lum bo'ladi.
Bu maqola javob beradigan savol miqdoriy: "leakage yomonmi" emas (hamma rozi), balki "har bir shakl qancha Sharpe ballini sotib oladi, va qaysilari xavfli?" Raqamsiz siz bu haqda fikr yurita olmaysiz. +0.3 shishishning shovqin ekanligini yoki +14 shishishning aniq dalil ekanligini ajrata olmaysiz.
Ma'lum ground truth'ga ega simulyator

Shishishni o'lchash uchun haqiqatni bilish kerak. Haqiqiy ma'lumotlar hech qachon haqiqatni aytmaydi — ular sizga bitta amalga oshishni beradi va hech qanday oracle bermaydi. Shuning uchun biz edge'ni biz o'zimiz belgilaydigan sintetik bozor quramiz.
Ma'lumot yaratuvchi jarayon qat'iy sabab-oqibatli (causal) va portlamaydigan:
Bu yerda — ekzogen doimiy yashirin drift (φ = 0.95 bo'lgan AR(1)), va bar daromadi bir bar oldindan ma'lum bo'lgan kichik driftiga ega. o'tmishdagi daromadlarga bog'liq bo'lmagani uchun, teskari aloqa yo'q va hech narsa portlamaydi. parametri qancha haqiqiy edge mavjudligini boshqaruvchi dastak:
- — null: umuman edge yo'q. Har qanday ijobiy bektest Sharpe'i 100% sun'iy.
- — haqiqiy, savdo qilinadigan edge: halol momentum qoidasi haqiqatan ham pul topadi.
Strategiya ataylab oddiy qilib yasalgan — momentum belgi qoidasi. Xususiyat — trailing- daromadlar yig'indisi ( bar), va pozitsiya uning belgisi:
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
Bu momentum xususiyati bir-bar sizib chiqishini o'rganish uchun mukammal vositadir, chunki unda haqiqiy indikatorlar baham ko'radigan xususiyat bor: u joriy barni mexanik ravishda o'z ichiga oladi. mom[t] tarkibida r[t] bor. Demak, agar siz r[t]ni savdongiz sifatida qayd qilsangiz, siz qisman allaqachon o'zingizning signalingiz ichida bo'lgan miqdorga tikayapsiz. Bu — aniq shaklga kelgan sizib chiqish.
Sozlash: (har bar uchun 1% o'zgaruvchanlik), 0.00045 bir tomonlama komissiya (round-trip 0.09%, bizning dvigatelimizga mos), Sharpe orqali yillik hisoblanadi (soatlik barlar), har biri 4000 bardan iborat 4000 mustaqil tarix. Hammasi seeded va deterministik.
Halol pipeline (yagona savdo qilinadigan)
t barining yopilishida qaror qabul qilish, keyingi barning daromadini olish, pozitsiya o'zgarganda komissiya to'lash:
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
Uchta sizib chiqish, har biri bitta jarrohlik o'zgarishi
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])
Har bir sizib chiqish halol pipeline'dan atigi bir qator uzoqlikda. Butun gap shunda: bular ekzotik xatolar emas, ular kod ko'rigidan o'tib ketadigan narsalar.
Natijalar: har bir sizib chiqishning miqdori

4000 ta seed bo'yicha ishga tushirilgan, mana har bir pipeline xabar beradigan yillik Sharpe, null (edge yo'q) ostida va haqiqiy edge (, halol Sharpe ishonchli +1.57 bo'ladigan qilib sozlangan) ostida:
| Pipeline | Null (edge yo'q) | Haqiqiy edge |
|---|---|---|
| Halol (haqiqat) | −0.74 | +1.57 |
| Bitta bardagi ijro | +14.79 | +15.85 |
| Indikator oldinga qarashi (1 bar) | +4.76 | +6.62 |
| Butun seriya normalizatsiyasi | −0.84 | +1.46 |
Seedlar bo'yicha 95% ishonch intervallari har bir katakchada ±0.05 yoki undan qattiqroq; ta'sir haqiqiy bo'lgan joyda shishishga oid paired t-testlar astronomik darajada ahamiyatli (t > 400, p ≈ 0).
Avval null ustunini o'qing, chunki bu eng toza mumkin bo'lgan tajriba: edge yo'q, shuning uchun halol pipeline to'g'ri ravishda pul yo'qotadi (−0.74, shovqinni savdo qilish uchun komissiya to'lashning ishqalanishi). Endi sizib chiqishlar xuddi shu hech narsaga nima qilishini ko'ring:
- Bitta bardagi ijro: −0.74 → +14.79. Hech qanday bashorat qilish kuchiga ega bo'lmagan, tasodifiy shovqinni savdo qiladigan strategiya deyarli 15 yillik Sharpe xabar qiladi. Bu nozik og'ish emas; bu soxtalashtirish. Mexanizm biz qurgan aynan o'sha narsa: momentum xususiyati
r[t]ni o'z ichiga oladi, shuning uchunr[t]ni qayd qilish o'z signalingizga tikish demakdir. - Indikator oldinga qarashi: −0.74 → +4.76. Tekislovchiga bir bar kelajakka qarashga ruxsat berish shovqindan 5ga yaqin Sharpe yasaydi, chunki
tbardagi tekislangan qiymat endi siz topmoqchi bo'lganr[t+1]bilan bog'liq. - Butun seriya normalizatsiyasi: −0.74 → −0.84. Deyarli hech qanday shishish yo'q. Bu halol, aniq bo'lmagan natija (bu haqda quyida ko'proq).
Edge ustuni yanada xavfli xabar beradi. Haqiqiy edge mavjud bo'lganda (halol +1.57), sizib chiqishlar shunchaki doimiy qo'shimcha qilmaydi — ular o'lchangan Sharpe'ni +15.85 va +6.62ga, siz haqiqatan ham savdo qila oladigan +1.57dan ancha yuqoriga suradi. Shunday qilib, o'lchangan raqam mahoratni sizib chiqishdan ajrata olmaydi. Sizib chiqqan +6 va halol +6 hisobotda bir xil ko'rinadi. Siz kapital joylashtirgandan keyingina qaysi biri bo'lganini bilib olasiz.
Sizib chiqish gradient, kalit emas

Tabiiy e'tiroz: "butun signal barini qayd qilish — ekstremal, real bo'lmagan xato." Shuning uchun biz dozani taradik — sizib chiqish tomonidan ushlanadigan signal barining ulushi, 0dan (halol) 1gacha (to'liq bitta bar):
| Ushlash ulushi | Null Sharpe | Edge Sharpe |
|---|---|---|
| 0.00 (halol) | −0.74 | +1.57 |
| 0.25 | +3.90 | +6.41 |
| 0.50 | +9.86 | +12.20 |
| 1.00 (to'liq sizib chiqish) | +14.79 | +15.85 |
Signal barining atigi chorak qismini ushlash edge'i yo'q strategiyani −0.74dan +3.90ga olib chiqadi. Aldanish uchun sizga to'liq off-by-one kerak emas; biroz ko'proq qulay ijro — signal baridagi optimistik slippage'ning bir tegishi, uni ishga tushirgan xuddi shu barga qarshi tekshirilgan intrabar stop — ko'pchilik "joylashtiriladigan" chegaralardan o'tish uchun yetarli. Shishish hozirgi vaqtning qanchasini savdo qilishga o'zingizga ruxsat berishingizga qarab silliq va monoton.
Bu qanchalik tez-tez zararli strategiyani ishlab chiqarishga kiritadi?
Amaliyotchini tashvishga solishi kerak bo'lgan raqam — soxta joylashtirish darajasi: sizib chiqish haqiqatan ham pul yo'qotadigan konfiguratsiyani uni yashil chiroqqa chiqarish uchun ishlatiladigan chegaradan qanchalik tez-tez o'tkazadi. "Yillik Sharpe ≥ 1.0" ni joylashtirish mezoni sifatida ishlatib, null ostida:
- Bitta bardagi ijro: edge'i yo'q strategiyalarning 68% joylashtirilishi mumkindek ko'rinadi va haqiqatan ham pul yo'qotadi. Sof shovqinning uchta konfiguratsiyasidan ikkitasi Sharpe-≥-1 darvozasidan o'tib, jonli tizimda pul yo'qotadi. (Bu daraja bu yerda toza aniqlangan, chunki sizib chiqish butunlay ijroda; halol hamkasbi — halol ijroga ega xuddi shu signal.)
- Indikator oldinga qarashi: u deyarli har bir edge'i yo'q konfiguratsiyani ham joylashtirish chegarasidan o'tkazadi (99.9% Sharpe ≥ 1dan o'tadi) — u shovqinni to'g'ridan-to'g'ri ishlab chiqarishga olib kiradi.
- Butun seriya normalizatsiyasi: 12% chegaradan o'tadi — bu asosan shovqinning bazaviy darajasi, hech qanday leakage mukofoti yo'q.
Taksonomiya va har birini qanday aniqlash kerak
Uchta sizib chiqish bir xil darajada xavfli emas, va farqlar o'rgatuvchidir.
1. Ijro leakage'i (qimmat turi)

Belgi: ijro narxi signal bilan bog'liq, chunki ikkalasi ham bir bardan keladi. Miqdor: ulkan (to'liq dozada shovqindan +15, chorak dozada +3.9). Nega bu eng yomoni: sizning signalingiz, deyarli ta'rifi bo'yicha, so'nggi narx harakatidan qurilgan, shuning uchun signal barining daromadi aynan sizning xususiyatingiz eng ko'p bog'liq bo'lgan narsadir. Uni qayd qilish javobni qidirib topishga yaqin.
Aniqlash — bir-bar siljitish testi. Bu ushbu maqoladagi eng qimmatli diagnostika. Bektestingizni oling va har bir ijroni bir bar keyinga siljiting (ida qaror qabul qiling, open[i+1]da ijro qiling). Agar natija deyarli qo'zg'almasa, ijroingiz halol edi. Agar natija qulasa yoki belgisini o'zgartirsa, siz o'tmishda savdo qilayotgan edingiz. Bizning Sobol qidiruvimizda aynan shu narsa sodir bo'ldi: ijrolarni siljiting, va "foydali" OOS zarar bo'lib chiqdi — to'g'rirog'i, sizib chiqish olib tashlangandan keyin haqiqiy bog'liqlik yuzaga chiqdi.
entry_price = open_[i + 1] # NOT close[i], NOT open[i]
2. Indikator / xususiyat leakage'i (jim turi)
Belgi: i bardagi indikator i+1 yoki undan keyingi ma'lumotlarga bog'liq — markazlashtirilgan harakatlanuvchi o'rtacha, sabab-oqibatli kechikishsiz filtr, tasdiqlash uchun kelajakdagi barlarni talab qiladigan cho'qqi/chuqurlik yorlig'i, kelajakdagi shamlar bilan boqiladigan Heikin-Ashi kabi transformatsiya. Miqdor: katta (shovqindan +4.8). Nega yashirinadi: sizib chiqish kutubxona chaqiruvi ichida ko'milgan. scipy.signal.filtfilt nol-fazali — va nol-faza sabab-oqibatli emas degani. "Bu bar mahalliy maksimum" xususiyati keyingi bar chop etilmaguncha bilib bo'lmaydigan.
Aniqlash: har bir indikator uchun so'rang: u o'qiydigan eng yuqori indeks qaysi? Agar tdagi qiymatni hisoblash biror marta t+1ga tegsa, u sabab-oqibatli emas. Indikatorlarni kengayuvchi/harakatlanuvchi sabab-oqibatli oynada hisoblang va tdan keyingi barlar massivda mavjud yoki yo'qligidan qat'i nazar t bardagi qiymat bir xil ekanligini tekshiring. (Bizning HMA/ADX amalga oshirishlarimiz bu testdan o'tadi: tdagi har bir chiqish faqat ≤ t dagi kirishlarni o'qiydi.)
3. Normalizatsiya leakage'i (kanalga xos turi)
Belgi: scaler (StandardScaler, min-max, global z-score) test to'plamini ham qo'shib, butun ma'lumotlar to'plamiga moslashtiriladi. Kanonik ML ogohlantirishlari bu haqda aniq — Hastie, Tibshirani & Friedman'ning Elements of Statistical Learning §7.10.2 ("the wrong and right way to do cross-validation"), va scikit-learn'ning o'zining umumiy xatolar bo'yicha qo'llanmasi: "the average should be the average of the train subset, not the average of all the data."
Bizning sinovimizdagi miqdor: ≈ nol (−0.74 → −0.84). Bu — hayratlanarli, halol natija, va uni yodlab olishdan ko'ra tushunish foydaliroq.
Nega u shishmadi? Chunki bizning strategiyamiz xususiyatdan faqat uning belgisi orqali foydalanadi (nol chegara). Standart og'ish bo'yicha masshtablash hech qachon belgini o'zgartirmaydi, va global-o'rtacha-markazlashtirish nol kesishuvini faqat ozgina siljitadi. Shuning uchun sof belgi qoidasining butun seriya standartlashtirishi deyarli zararsiz.
Buni haddan tashqari umumlashtirmang. Normalizatsiya leakage'i kanalga xos. Strategiyangiz xususiyatning miqdorini ishlata boshlagan zahoti — z-score'ga proportsional pozitsiya o'lchash, masshtablangan taqsimotga qarab tanlangan noldan farqli kirish chegarasi, standartlashtirilgan kirishlarni qabul qiladigan neyron tarmoq — kelajakni biladigan scaler ahamiyatli bo'la boshlaydi, va global statistika sabab-oqibatlidan qanchalik ko'p farq qilsa, shunchalik ko'proq ahamiyatli bo'ladi. Bizning natijamiz "normalizatsiya leakage'i xavfsiz" degani emas. Bu "leakage miqdori sizib chiqqan miqdor qarorga kiradigan kanalga bog'liq, va uni taxmin qilish o'rniga o'lchash kerak" degani. Belgi qoidasi — bu aniq sizib chiqish arzon bo'ladigan yagona holat.
Bu qayerga bog'lanadi
Look-ahead bias — bu seriya hujjatlashtirib kelayotgan zanjirning birinchi bo'g'ini:
- U validatsiyaning kirishini ifloslantiradi. Sizib chiqqan bektest walk-forward bo'linishidan to'siqsiz o'tadi va overfit cho'qqisidan ko'ra keng platodek ko'rinadi — sizib chiqish foldlar bo'yicha izchil, shuning uchun cross-validation uni ushlay olmaydi. Leakage — overfitting'dan oldingi nosozlik rejimi, va undan keyingi hech qanday halol validatsiya sizni qutqarmaydi.
- U parametr qidiruvi bilan o'zaro ta'sir qiladi: sizib chiqqan ma'lumotlar bo'yicha minglab urinishlar bo'yicha qidiruv sizib chiqishni eng agressiv tarzda ishlatadigan konfiguratsiyani topadi. "G'olib" — eng yomon buzuvchi.
- Shuning uchun bektest-jonli tizim paritetlik farqlanadi. Sizib chiqish bektest va bot o'rtasidagi 30–50% farqning eng aniq tushuntirishidir, chunki jonli savdo, mexanik jihatdan, siz oldindan qaray olmaydigan yagona joydir.
Bularning barchasini ushlaydigan intizom — akademik adabiyot yillar davomida targ'ib qilib kelayotgan xuddi shu intizom: bektestni qat'iy axborot chegarasiga ega statistik tajriba sifatida ko'rib chiqing. Bailey, Borwein, López de Prado & Zhu overfitting soxta samaradorlikni qanchalik oson yasashini ko'rsatdi (2014); Arnott, Harvey & Markowitz'ning bektestlash protokoli (2019) bu gigienani kodlashtiradi. Look-ahead bias — barcha chegaralarning eng asosiysi — vaqtdagi chegara — va tasodifan buzish uchun eng arzoni.
Asosiy xulosalar

- Look-ahead bias miqdoriy jihatdan ulkan va sifat jihatdan ko'rinmasdir. Yagona bir-bar ijro xatosi −0.74 Sharpe'ni (sof shovqin, to'g'ri ravishda zarar ko'rgan) +14.79ga aylantirdi. Xato bitta qator; oqibat esa soxtalashtirilgan track record.
- Bu gradient. Signal barining atigi 25% ini ushlash ham hech narsadan +3.90 beradi. Sizga oshkora bug kerak emas — ijrolaringizdagi biroz ortiqcha optimizm yetarli.
- O'lchangan raqam mahoratni sizib chiqishdan ajrata olmaydi. Haqiqiy edge mavjud bo'lganda, sizib chiqishlar hisobotni savdo qilinadigan haqiqatdan ancha uzoqqa shishiradi. Yagona himoya — jarayon, ko'rsatkich emas.
- Bir-bar siljitish testi — eng tez diagnostikangiz. Har bir ijroni bir bar keyinga siljiting. Agar samaradorlik qulasa, siz o'tmishda savdo qilayotgan edingiz.
- Leakage miqdori kanalga xos. Ijro va indikator oldinga qarashi halokatli; belgi qoidasining butun seriya normalizatsiyasi deyarli bepul. Sizib chiqishni u haqiqatan ham kiradigan kanal orqali o'lchang — taxmin qilmang.
To'liq nazorat qilinadigan tadqiqot — barcha uchta sizib chiqish, doza taroqlash, soxta joylashtirish tahlili, rasmiy usullar, va bitta deterministik skriptdan qayta yaratiladigan har bir raqam — hamroh maqolada lookahead.marketmaker.cc manzilida, kod va ma'lumotlar github.com/suenot/lookahead-inflation manzilida.
Bizning null tajribamizdagi strategiyada umuman edge yo'q edi. Shunga qaramay, u 15 Sharpe ko'rsatdi. Agar bektestingiz juda yaxshi ko'rinsa, birinchi shubhalanishingiz kerak bo'lgan narsa sizning dahoingiz emas — bu sizning soatingiz.
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.