Look-Ahead Bias: कैसे एक-बार की गलती शुद्ध शोर से 15 का Sharpe गढ़ देती है
बैकटेस्ट को चुपचाप फुला देने वाले सूक्ष्म look-ahead leaks का एक नियंत्रित अध्ययन। बिना किसी वास्तविक edge के, एक same-bar फिल शुद्ध शोर से +14.8 का वार्षिक Sharpe गढ़ देता है; एक-बार आगे झांकता इंडिकेटर, +4.8। वर्गीकरण, मापी गई मात्राएं, और आपकी जेब से पैसा जाने से पहले हर leak को कैसे पकड़ें।

"Backtests Without Illusions" श्रृंखला का हिस्सा।
📄 यह लेख एक शोध पत्र में बदल गया। तीन सूक्ष्म look-ahead leaks को ज्ञात ग्राउंड ट्रुथ (4,000 सिमुलेटेड इतिहास) के विरुद्ध एक नियंत्रित परीक्षण में परखा गया है। ऑनलाइन पेपर (इंटरैक्टिव वर्शन + PDF) lookahead.marketmaker.cc पर पढ़ें, कोड और डेटा github.com/suenot/lookahead-inflation पर।
कुछ हफ्ते पहले हमारा पैरामीटर-सर्च बेंचमार्क हमसे झूठ बोल रहा था, और हमने लगभग इसे नोटिस ही नहीं किया।
इंजन साफ-सुथरा दिख रहा था। क्लोज़्ड-बार लॉजिक, एक ईमानदार रोलिंग वॉक-फॉरवर्ड स्प्लिट, पैरामीटर स्पेस पर एक Sobol/QMC सर्च, एक होल्ड-आउट टेस्ट विंडो। सर्च ने ऐसे कॉन्फ़िगरेशन खोजे जो इन-सैंपल में अच्छे दिखते थे। बस एक समस्या थी: आउट-ऑफ़-सैंपल में लगभग सब कुछ नेगेटिव था। हमने मान लिया कि स्ट्रैटेजी बस कमज़ोर है।
फिर हमें एक लाइन मिली। सिग्नल बार i के क्लोज़ पर तय होता था, लेकिन फिल उसी बार i पर बुक हो रहा था, न कि अगले बार के ओपन पर। एक्ज़ीक्यूशन इंडेक्स में एक ऑफ-बाय-वन। हमने फिल को open[i+1] पर शिफ्ट किया — वह इकलौता प्राइस जिस पर बार-i का क्लोज़ देखने के बाद आप वास्तव में ट्रांज़ैक्ट कर सकते थे — और आउट-ऑफ़-सैंपल नतीजे ने साइन पलट दिया। Sobol सर्च घाटे से मुनाफ़े में बदल गई। स्ट्रैटेजी में कुछ भी नहीं बदला था। हमने बस अतीत में ट्रेड करना बंद कर दिया था।
यही है look-ahead bias, और परेशान करने वाली बात यह है कि गलती कितनी छोटी थी और नतीजा कितना बड़ा। यह लेख एक नियंत्रित सेल्फ-ऑडिट है: हम एक सिमुलेटर बनाते हैं जिसमें ग्राउंड ट्रुथ निर्माण से ही ज्ञात है, सूक्ष्म leaks को एक-एक करके इंजेक्ट करते हैं, और मापते हैं कि हर एक बैकटेस्ट को ठीक कितना फुलाता है। मुख्य बात: बिल्कुल भी असली edge न होने पर, एक same-bar फिल शुद्ध शोर से +14.8 का वार्षिक Sharpe गढ़ देता है।
Look-ahead bias असल में क्या है

Look-ahead bias आपके पाइपलाइन में वह कोई भी बिंदु है जहां कोई निर्णय या माप ऐसी जानकारी का उपयोग करता है जो उपयोग के समय, रीयल टाइम में, उपलब्ध नहीं होती। पाठ्यपुस्तक के उदाहरण मोटे होते हैं — जनवरी में किसी स्टॉक की पूरे साल की कमाई का उपयोग करना, या ऐसा पुनर्कथन जो अभी प्रकाशित ही नहीं हुआ था। इन्हें पहचानना आसान है। जो कोड रिव्यू से बच निकलते हैं वे सूक्ष्म होते हैं, और तीन जगहों पर छिपते हैं:
- एक्ज़ीक्यूशन — आप बार
iपर निर्णय लेते हैं और बारiपर फिल करते हैं (या उसी बार के हाई/लो का उपयोग स्टॉप्स के लिए करते हैं जिसने सिग्नल जनरेट किया)। आप ऐसे प्राइस पर ट्रांज़ैक्ट करते हैं जो उस चीज़ से सहसंबंधित है जिसने आपको ट्रिगर किया। - नॉर्मलाइज़ेशन — आप z-score, min-max, या किसी अन्य तरीके से किसी फीचर को पूरी सीरीज़ पर गणना किए गए आंकड़ों से स्केल करते हैं, जिसमें भविष्य भी शामिल है। स्केलर टेस्ट सेट को "जानता" है।
- इंडिकेटर / फीचर्स — आप एक ऐसी विंडो से स्मूद या फ़िल्टर करते हैं जो केंद्रित है (या किसी अन्य तरीके से आगे झांकती है), जिससे बार
iपर मान पहले से ही बारi+1का एक हिस्सा समाहित कर लेता है।
तीनों ही उस चीज़ के रूप हैं जिसे मशीन-लर्निंग साहित्य leakage कहता है: टार्गेट के भविष्य की जानकारी से ट्रेनिंग/मूल्यांकन का दूषित होना (Kaufman et al., 2012; Kapoor & Narayanan, 2023)। फाइनेंस में इसका मानक उपचार López de Prado की Advances in Financial Machine Learning (2018) में है — पर्ज्ड क्रॉस-वैलिडेशन, एम्बार्गोइंग, बैकटेस्टिंग के खतरे। पॉइंट-इन-टाइम अनुशासन कम से कम Fama & French (1992) तक जाता है, जो जानबूझकर अकाउंटिंग डेटा को छह महीने पीछे रखते हैं ताकि वेरिएबल उस रिटर्न से पहले ज्ञात हो जिसे वह समझाता है।
इस लेख का जवाब मात्रात्मक है: यह नहीं कि "क्या leakage बुरा है" (सब सहमत हैं), बल्कि "हर रूप कितने Sharpe पॉइंट्स खरीदता है, और कौन-से खतरनाक हैं?" बिना किसी संख्या के आप इस पर तर्क नहीं कर सकते। आप नहीं बता सकते कि +0.3 की महंगाई शोर है या +14 की महंगाई पक्का सबूत है।
ज्ञात ग्राउंड ट्रुथ वाला एक सिमुलेटर

महंगाई मापने के लिए आपको सच्चाई जाननी होगी। असली डेटा कभी सच्चाई नहीं बताता — वह आपको एक ही रियलाइज़ेशन देता है, कोई oracle नहीं। इसलिए हम एक सिंथेटिक मार्केट बनाते हैं जहां edge हम तय करते हैं।
डेटा-जनरेटिंग प्रोसेस पूरी तरह causal और non-explosive है:
यहां एक बहिर्जात (exogenous) दृढ़ अव्यक्त ड्रिफ्ट है (φ = 0.95 के साथ एक AR(1)), और बार रिटर्न में एक छोटा ड्रिफ्ट है जो एक बार पहले से ज्ञात है। चूंकि पिछले रिटर्न्स पर निर्भर नहीं करता, इसलिए कोई फीडबैक नहीं है और कुछ भी विस्फोटक नहीं होता। पैरामीटर यह तय करता है कि कितना असली edge मौजूद है:
- — null: कोई edge बिल्कुल नहीं। कोई भी सकारात्मक बैकटेस्ट Sharpe 100% कृत्रिम है।
- — एक असली, ट्रेड करने योग्य edge: एक ईमानदार मोमेंटम रूल वाकई पैसा कमाता है।
स्ट्रैटेजी जानबूझकर सरल रखी गई है — एक मोमेंटम साइन रूल। फीचर है trailing- रिटर्न्स का योग ( बार), और पोज़ीशन उसका साइन है:
# causal momentum feature at bar t: mom[t] = sum r[t-L+1 .. t] (indices <= t)
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
यह मोमेंटम फीचर same-bar leak का अध्ययन करने के लिए एकदम सही वाहन है, क्योंकि इसमें वह गुण है जो असली इंडिकेटर्स साझा करते हैं: यह यांत्रिक रूप से मौजूदा बार को समाहित करता है। mom[t] में r[t] शामिल है। तो अगर आप r[t] को अपने ट्रेड के रूप में बुक करते हैं, तो आप आंशिक रूप से उस मात्रा पर दांव लगा रहे हैं जो पहले से ही आपके अपने सिग्नल के अंदर है। यही है वह leak, ठोस रूप में।
सेटअप: (1% प्रति-बार वोलैटिलिटी), 0.00045 का वन-वे फ़ीस (राउंड-ट्रिप 0.09%, हमारे इंजन से मेल खाता), Sharpe को (घंटे के बार) से वार्षिक किया गया, 4,000 स्वतंत्र इतिहास, हर एक में 4,000 बार। सब कुछ seeded और deterministic है।
ईमानदार पाइपलाइन (इकलौती ट्रेड करने योग्य)
बार 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
तीन leaks, हर एक एक सर्जिकल बदलाव
# 1. SAME-BAR FILL: book the signal bar r[t] instead of the next bar r[t+1].
# This is exactly the bench_search off-by-one that started this whole thing.
same_bar = sharpe(signal[idx], r[idx])
# 2. WHOLE-SERIES NORMALIZATION: standardize the feature with global mean/std
# (uses the future); execution stays honest (still earns r[t+1]).
z_full = (mom - mom[valid].mean()) / mom[valid].std()
norm_full = sharpe(np.sign(z_full[idx]), r[idx + 1])
# 3. INDICATOR PEEK: smooth the feature with a CENTERED filter that sees t+1;
# execution stays honest.
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])
हर leak ईमानदार पाइपलाइन से बस एक लाइन दूर है। यही पूरी बात है: ये कोई विदेशी गलतियां नहीं हैं, ये वैसी चीज़ें हैं जो रिव्यू से पार हो जाती हैं।
नतीजे: हर leak की मात्रा

4,000 सीड्स पर चलाकर, यहां हर पाइपलाइन द्वारा रिपोर्ट किया गया वार्षिक Sharpe है, null (कोई edge नहीं) के तहत और असली edge (, इस तरह ट्यून किया गया कि ईमानदार Sharpe एक विश्वसनीय +1.57 हो) के तहत:
| पाइपलाइन | Null (कोई edge नहीं) | असली edge |
|---|---|---|
| ईमानदार (सच्चाई) | −0.74 | +1.57 |
| Same-bar फिल | +14.79 | +15.85 |
| इंडिकेटर पीक (1 बार) | +4.76 | +6.62 |
| Whole-series नॉर्मलाइज़ेशन | −0.84 | +1.46 |
सीड्स पर 95% कॉन्फिडेंस इंटरवल हर सेल में ±0.05 या उससे कसे हैं; जहां प्रभाव असली है वहां महंगाई पर paired t-tests खगोलीय रूप से महत्वपूर्ण हैं (t > 400, p ≈ 0)।
पहले null कॉलम पढ़ें, क्योंकि यह सबसे साफ संभव प्रयोग है: कोई edge नहीं है, इसलिए ईमानदार पाइपलाइन सही तरीके से पैसा गंवाती है (−0.74, शोर ट्रेड करने के लिए फ़ीस चुकाने का ड्रैग)। अब देखें कि leaks उसी शून्य के साथ क्या करते हैं:
- Same-bar फिल: −0.74 → +14.79। शून्य प्रेडिक्टिव पावर वाली एक स्ट्रैटेजी, जो रैंडम शोर ट्रेड करती है, लगभग 15 का वार्षिक Sharpe रिपोर्ट करती है। यह कोई सूक्ष्म पूर्वाग्रह नहीं है; यह मनगढ़ंत है। मैकेनिज़्म ठीक वही है जो हमने बनाया: मोमेंटम फीचर में
r[t]शामिल है, तोr[t]बुक करना अपने ही सिग्नल पर दांव लगाना है। - इंडिकेटर पीक: −0.74 → +4.76। स्मूदर को एक बार भविष्य में झांकने देना शोर से लगभग 5 का Sharpe गढ़ देता है, क्योंकि
tपर स्मूद किया गया मान अब उसr[t+1]से सहसंबंधित है जिसे आप कमाने वाले हैं। - Whole-series नॉर्मलाइज़ेशन: −0.74 → −0.84। लगभग कोई महंगाई नहीं। यह ईमानदार, गैर-स्पष्ट खोज है (नीचे इस पर और अधिक)।
edge कॉलम ज़्यादा घातक संदेश देता है। जब कोई असली edge मौजूद है (ईमानदार +1.57), तो leaks सिर्फ एक कॉन्स्टेंट नहीं जोड़ते — वे मापे गए Sharpe को +15.85 और +6.62 तक धकेल देते हैं, उस +1.57 से बहुत ऊपर जिसे आप वाकई ट्रेड कर सकते थे। तो मापी गई संख्या skill को leak से अलग नहीं कर सकती। एक leaked +6 और एक ईमानदार +6 रिपोर्ट पर एक जैसे दिखते हैं। आपको यह पता तभी चलता है कि आपके पास कौन-सा था, जब आप पूंजी लगा चुके होते हैं।
Leak एक ग्रेडिएंट है, स्विच नहीं

एक स्वाभाविक आपत्ति: "पूरे सिग्नल बार को बुक करना एक चरम, अवास्तविक गलती है।" तो हमने डोज़ को स्वीप किया — leak द्वारा कैप्चर किए गए सिग्नल बार का अंश , 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 (पूर्ण leak) | +14.79 | +15.85 |
सिग्नल बार का सिर्फ एक चौथाई कैप्चर करना, बिना edge वाली स्ट्रैटेजी को −0.74 से +3.90 पर ले जाता है। धोखा खाने के लिए आपको पूरा ऑफ-बाय-वन नहीं चाहिए; एक फिल जो थोड़ी ज़्यादा अनुकूल है — सिग्नल बार पर आशावादी स्लिपेज का एक स्पर्श, एक इंट्राबार स्टॉप जिसे उसी बार के खिलाफ चेक किया गया जिसने उसे ट्रिगर किया — ज़्यादातर "डिप्लॉय करने योग्य" थ्रेशोल्ड पार करने के लिए काफी है। महंगाई सुचारू और नीरस (monotone) है इस हिसाब से कि आप कितना वर्तमान खुद को ट्रेड करने देते हैं।
यह कितनी बार घाटे वाली स्ट्रैटेजी को प्रोडक्शन में डाल देता है?
जो संख्या एक प्रैक्टिशनर को चिंतित करनी चाहिए वह है false-deployment rate: कितनी बार एक leak किसी सचमुच पैसा गंवाने वाले कॉन्फ़िगरेशन को उस बार से पार करा देता है जिसे आप ग्रीनलाइट देने के लिए इस्तेमाल करते। "वार्षिक Sharpe ≥ 1.0" को डिप्लॉय मानदंड मानकर, null के तहत:
- Same-bar फिल: बिना edge वाली 68% स्ट्रैटेजीज़ डिप्लॉय करने योग्य दिखती हैं और वाकई पैसा गंवाती हैं। तीन में से दो शुद्ध-शोर कॉन्फ़िगरेशन Sharpe-≥-1 गेट पास कर देंगे और लाइव में पैसा गंवा देंगे। (यह दर यहां साफ तरीके से परिभाषित है क्योंकि leak पूरी तरह एक्ज़ीक्यूशन में है — ईमानदार समकक्ष वही सिग्नल है ईमानदार फिल के साथ।)
- इंडिकेटर पीक: यह लगभग हर बिना-edge कॉन्फ़िगरेशन को भी डिप्लॉय बार से ऊपर धकेल देता है (99.9% Sharpe ≥ 1 पार करते हैं) — यह शोर को सीधे प्रोडक्शन में लहरा देगा।
- Whole-series नॉर्मलाइज़ेशन: 12% थ्रेशोल्ड पार करते हैं — मूल रूप से शोर की बेस रेट, कोई leakage प्रीमियम नहीं।
वर्गीकरण, और हर एक को कैसे पकड़ें
तीनों leaks समान रूप से खतरनाक नहीं हैं, और अंतर शिक्षाप्रद हैं।
1. एक्ज़ीक्यूशन leakage (महंगा वाला)

लक्षण: फिल प्राइस सिग्नल से सहसंबंधित है क्योंकि दोनों एक ही बार से आते हैं। मात्रा: बहुत बड़ी (पूर्ण डोज़ पर शोर से +15, क्वार्टर डोज़ पर +3.9)। यह सबसे खराब क्यों है: आपका सिग्नल, लगभग परिभाषा से, हाल के प्राइस एक्शन से बना होता है, तो सिग्नल बार का रिटर्न ठीक वही है जिससे आपका फीचर सबसे ज़्यादा सहसंबंधित है। इसे बुक करना जवाब देख लेने के करीब है।
पहचान — the one-bar shift test। यह इस लेख का सबसे मूल्यवान डायग्नोस्टिक है। अपना बैकटेस्ट लें और हर फिल को एक बार बाद शिफ्ट करें (i पर तय करें, open[i+1] पर फिल करें)। अगर नतीजा मुश्किल से हिलता है, तो आपका एक्ज़ीक्यूशन ईमानदार था। अगर नतीजा ढह जाता है या साइन पलट देता है, तो आप अतीत में ट्रेड कर रहे थे। हमारे Sobol सर्च के साथ ठीक यही हुआ: फिल्स शिफ्ट करें, और एक "प्रॉफिटेबल" OOS असल में घाटा निकला — या यूं कहें, leak हटते ही असली संबंध सामने आ गया।
# Honest execution: the price you can actually get is the NEXT bar's open.
entry_price = open_[i + 1] # NOT close[i], NOT open[i]
# Stops/targets must be checked against bars AFTER the signal bar, never bar i itself.
2. इंडिकेटर / फीचर leakage (चुप वाला)
लक्षण: बार i पर एक इंडिकेटर i+1 या उसके बाद के डेटा पर निर्भर करता है — एक centered moving average, बिना causal delay वाला फ़िल्टर, एक peak/trough लेबल जिसे कन्फर्म करने के लिए भविष्य के बार चाहिए, एक Heikin-Ashi जैसा ट्रांसफॉर्म जिसे भविष्य के कैंडल दिए गए हों। मात्रा: बड़ी (शोर से +4.8)। यह क्यों छिपता है: leak किसी लाइब्रेरी कॉल के अंदर दबा होता है। scipy.signal.filtfilt zero-phase है — और zero-phase का मतलब है non-causal। "यह बार एक लोकल मैक्सिमम है" वाला फीचर तब तक अज्ञात है जब तक अगला बार प्रिंट नहीं होता।
पहचान: हर इंडिकेटर के लिए पूछें यह सबसे ऊंचा कौन-सा इंडेक्स पढ़ता है? अगर t पर मान की गणना कभी t+1 को छूती है, तो यह non-causal है। इंडिकेटर्स को एक expanding/rolling causal विंडो पर गणना करें और सत्यापित करें कि बार t पर मान एक जैसा है चाहे array में t के बाद के बार मौजूद हों या न हों। (हमारे HMA/ADX इम्प्लीमेंटेशन इस टेस्ट को पास करते हैं: t पर हर आउटपुट सिर्फ ≤ t पर इनपुट पढ़ता है।)
3. नॉर्मलाइज़ेशन leakage (चैनल-स्पेसिफिक वाला)
लक्षण: एक स्केलर (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 की अपनी common-pitfalls गाइड: "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 के अनुपात में पोज़ीशन साइज़िंग, स्केल की गई डिस्ट्रीब्यूशन देखकर चुना गया गैर-शून्य एंट्री थ्रेशोल्ड, स्टैंडर्डाइज़्ड इनपुट लेने वाला एक न्यूरल नेट — भविष्य-जागरूक स्केलर मायने रखने लगता है, और यह उतना ही ज़्यादा मायने रखता है जितनी ग्लोबल स्टैटिस्टिक्स causal वाली से अलग होती हैं। हमारा नतीजा "नॉर्मलाइज़ेशन leakage सुरक्षित है" नहीं है। यह है "leakage की मात्रा उस चैनल पर निर्भर करती है जिससे leaked मात्रा निर्णय में प्रवेश करती है, और आपको इसे मान लेने के बजाय मापना चाहिए।" एक साइन रूल इकलौता मामला है जहां यह विशेष leak सस्ता पड़ता है।
यह कहां जुड़ता है
Look-ahead bias इस श्रृंखला द्वारा दर्ज की जा रही एक श्रृंखला की पहली कड़ी है:
- यह validation के इनपुट को दूषित करता है। एक leaked बैकटेस्ट एक वॉक-फॉरवर्ड स्प्लिट से आराम से पार हो जाएगा और ओवरफिट पीक के बजाय एक ब्रॉड प्लैटो जैसा दिखेगा — leak फोल्ड्स में एकसमान रहता है, तो क्रॉस-वैलिडेशन इसे पकड़ नहीं सकता। Leakage overfitting से पहले की फेलियर मोड है, और नीचे कोई भी ईमानदार वैलिडेशन आपको नहीं बचाएगी।
- यह पैरामीटर सर्च के साथ इंटरैक्ट करता है: leaked डेटा पर हज़ारों ट्रायल्स की सर्च वह कॉन्फ़िगरेशन ढूंढ लेगी जो leak का सबसे आक्रामक तरीके से फायदा उठाती है। "विजेता" सबसे बड़ा अपराधी है।
- यही कारण है कि बैकटेस्ट-लाइव पैरिटी अलग हो जाती है। बैकटेस्ट और बॉट के बीच 30–50% के अंतर की सबसे साफ व्याख्या एक leak है, क्योंकि लाइव ट्रेडिंग, यांत्रिक रूप से, इकलौती जगह है जहां आप झांक नहीं सकते।
वह अनुशासन जो इस सब को पकड़ता है वही है जिसे शैक्षणिक साहित्य सालों से बढ़ावा दे रहा है: बैकटेस्ट को एक सख्त सूचना सीमा वाले सांख्यिकीय प्रयोग की तरह मानें। Bailey, Borwein, López de Prado & Zhu ने दिखाया कि कितनी आसानी से overfitting फ़र्ज़ी परफ़ॉर्मेंस गढ़ लेता है (2014); Arnott, Harvey & Markowitz का बैकटेस्टिंग प्रोटोकॉल (2019) इस स्वच्छता को संहिताबद्ध करता है। Look-ahead bias सबसे बुनियादी सीमा है — समय की सीमा — और गलती से तोड़ने के लिए सबसे सस्ती।
मुख्य बातें

- Look-ahead bias मात्रात्मक रूप से विशाल और गुणात्मक रूप से अदृश्य है। एक अकेली एक-बार की एक्ज़ीक्यूशन गलती ने −0.74 (शुद्ध शोर, सही तरीके से घाटा) के Sharpe को +14.79 में बदल दिया। गलती एक लाइन की है; नतीजा एक गढ़ा हुआ ट्रैक रिकॉर्ड है।
- यह एक ग्रेडिएंट है। सिग्नल बार का सिर्फ 25% कैप्चर करना भी शून्य से +3.90 दे देता है। आपको एक साफ बग की ज़रूरत नहीं है — आपके फिल्स में थोड़ा ज़्यादा आशावाद ही काफी है।
- मापी गई संख्या skill को leak से अलग नहीं बता सकती। जब असली edge मौजूद होता है, तो leaks रिपोर्ट को ट्रेड करने योग्य सच्चाई से बहुत आगे फुला देते हैं। इकलौता बचाव प्रोसेस है, मेट्रिक नहीं।
- The one-bar shift test आपका सबसे तेज़ डायग्नोस्टिक है। हर फिल को एक बार बाद शिफ्ट करें। अगर परफ़ॉर्मेंस ढह जाए, तो आप अतीत में ट्रेड कर रहे थे।
- Leakage की मात्रा चैनल-स्पेसिफिक है। एक्ज़ीक्यूशन और इंडिकेटर पीक्स विनाशकारी हैं; एक साइन रूल का whole-series नॉर्मलाइज़ेशन लगभग मुफ़्त है। leak को उस चैनल से मापें जिससे वह असल में दाखिल होता है — मान न लें।
पूरा नियंत्रित अध्ययन — तीनों leaks, डोज़ स्वीप, false-deployment विश्लेषण, फॉर्मल मेथड्स, और हर संख्या जो एक ही deterministic स्क्रिप्ट से रीप्रोड्यूस होती है — साथी पेपर में है lookahead.marketmaker.cc पर, कोड और डेटा के साथ github.com/suenot/lookahead-inflation पर।
हमारे null प्रयोग की स्ट्रैटेजी में बिल्कुल कोई edge नहीं था। फिर भी उसने 15 का Sharpe दिखाया। अगर आपका बैकटेस्ट बहुत अच्छा लगे, तो सबसे पहले शक आपकी प्रतिभा पर नहीं करना चाहिए — अपनी घड़ी पर करना चाहिए।