कैलेंडर वर्ष में 1-मिनट बार की 525,600 पंक्तियाँ होनी चाहिए। 2025 में BTCUSDT परप्स का हमारा डेटा खींचने पर 525,557 पंक्तियाँ मिलीं — 43 मिनट कम, यानी 99.992% पूर्णता दर। उसी वेन्यू पर, उसी डेटा खींचने की प्रक्रिया और उसी कोड पाथ से मिड-कैप ऑल्ट परप में 1,247 पंक्तियाँ कम मिलीं: 99.76%। उसी साल की US इक्विटी मिनट सीरीज़ में क्रिप्टो सीरीज़ से लगभग 427,000 पंक्तियाँ कम थीं, जो कोई अंतराल नहीं है, बस ऐसा बाज़ार है जो बंद होता है।
इनमें से तीन संख्याएँ उबाऊ हैं। जो संख्या हज़ार शब्दों के लायक है, वह 43 है।
चार अलग-अलग चीज़ें, जो आपके dataframe में एक ही खाली जगह जैसी दिखती हैं
43 मिनटों की बात से पहले, यह वर्गीकरण समझ लें, क्योंकि ज़्यादातर अंतराल-संभालने वाला कोड इसी स्तर पर पहले से ग़लत होता है। जब आपकी स्थानीय parquet फ़ाइल में कोई पंक्ति मौजूद नहीं होती, तो आपको पता नहीं होता कि इनमें से किस वजह से वह छूटी, और हर वजह पर सही प्रतिक्रिया अलग होती है।
| प्रकार | असल में क्या हुआ | डेटा में यह कैसे दिखता है | सही प्रतिक्रिया |
|---|---|---|---|
| कोई ट्रेड नहीं | बाज़ार खुला था; उस मिनट किसी ने स्प्रेड पार नहीं किया | कुछ एंडपॉइंट पर शून्य-वॉल्यूम बार (OHLC सभी बराबर, trades=0), अन्य पर पंक्ति अनुपस्थित | इसे रहने दें। यह असली जानकारी है: कोई ट्रेड करना नहीं चाहता था। |
| वेन्यू बंद | मैचिंग इंजन ऑफ़लाइन, तयशुदा वजह से या बिना किसी तयशुदा वजह के | पंक्ति अनुपस्थित, कभी-कभी लगातार कई पंक्तियाँ | पुराना चिह्नित करें। इसके आर-पार ट्रेड न करें। |
| इंस्ट्रूमेंट पर रोक | LULD बैंड का उल्लंघन, ख़बर का इंतज़ार, डीलिस्टिंग की सूचना | पंक्ति अनुपस्थित, फिर दोबारा खुलने की नीलामी का प्रिंट | पुराना चिह्नित करें, और दोबारा खुलने को एक असतत बदलाव मानें। |
| आपकी ग़लती | रेट-लिमिट वाली पेजिनेशन, ऐसा रीट्राई जिसमें एक पेज छूट गया, लूप में टाइमज़ोन-सीमा की गड़बड़ी | पंक्ति अनुपस्थित, ऊपर की वजहों से अलग पहचानना मुश्किल | पता लगाएँ और डेटा दोबारा खींचें। यही एक चीज़ है जिसे आप ठीक कर सकते हैं। |
पहली पंक्ति के मामले में वेन्यू एक-दूसरे से अलग हैं, और यहीं समस्या चुभती है। Coinbase का कैंडल्स एंडपॉइंट खाली बकेट पूरी तरह छोड़ देता है, इसलिए कम लिक्विडिटी वाले पेयर की हिस्ट्री में सचमुच खाली जगहें भरी होती हैं। Binance के klines आम तौर पर ऐसा सिंथेटिक बार देते हैं जिसका वॉल्यूम 0 होता है और open=high=low=close पिछले ट्रेड पर स्थिर रहता है। अंतर्निहित तथ्य एक ही है, लेकिन रूप दो अलग-अलग; और पूरा मिनट-ग्रिड बनाने के लिए रीइंडेक्स करने वाला लोडर आपको बताए बिना एक रूप को दूसरे में बदल देता है। अंतराल भरने वाला कोड लिखने से पहले जाँचें कि आपका वेन्यू क्या करता है, बाद में नहीं।
ऑल्ट के 1,247 छूटे हुए मिनट लगभग पूरी तरह पहली श्रेणी के थे: रविवार को 04:00 UTC पर कम गहराई वाली ऑर्डर बुक, कोई ट्रेड नहीं कर रहा था। झुंझलाने वाली बात, समझना आसान, और ज़्यादातर बेअसर, क्योंकि रणनीति तब ट्रेड ही नहीं करती। और ठीक इसी वजह से मैंने उसे देखना बंद किया और उन 43 मिनटों पर लौट आया।
43 मिनट बेतरतीब ढंग से नहीं बिखरे थे
अगर डेटा छूटना एकसमान होता, तो साल भर के 43 मिनट 43 अलग-अलग अकेले मिनटों के रूप में आते, हर साढ़े आठ दिन में एक, और हर एक इतना छोटा कि उसे गोलाई की त्रुटि समझकर छोड़ दें। हमें ऐसा नहीं मिला। वे 6 समूहों में आए: एक लगातार 19 मिनट का, एक 11 मिनट का, दो 4 मिनट के, और दो जोड़े। 43 हादसे नहीं, 6 घटनाएँ।
और इन घटनाओं का संबंध उसी चीज़ से है जिसकी आपको परवाह है। वेन्यू पर दबाव बढ़ने पर वे बंद होते हैं, और दबाव तब बढ़ता है जब कीमत तेज़ी से चल रही होती है। मैंने साल की हर घंटे को वास्तविक अस्थिरता के हिसाब से समूहित किया और देखा कि छूटे हुए मिनट कहाँ थे: उनमें से 61% शीर्ष दशमांश में थे। किसी भी मिनट के छूटने की बिना शर्त संभावना 0.008% है। शीर्ष-दशमांश की अस्थिरता वाली किसी घंटे के भीतर होने पर यह लगभग 0.05% है — 6 गुना ज़्यादा, और साथ में वे गुच्छों में भी आते हैं।
इसलिए आपके डेटा-गुणवत्ता डैशबोर्ड का पूर्णता माप ग़लत चीज़ माप रहा है। 99.992% सुनने में ऐसा डेटासेट लगता है जिसके बारे में सोचना बंद किया जा सकता है। असल में यह ऐसी सीरीज़ बताता है जो उन घंटों में पूरी है जब आपकी रणनीति कुछ नहीं करती, और उन्हीं घंटों में अधूरी है जब वह सब कुछ करती है। अस्थिरता बढ़ने पर सक्रिय होने वाली मोमेंटम प्रणाली के लिए अंतराल से टकराने की संभावना मुख्य संख्या से काफ़ी अधिक है, और यह टकराव ट्रेड के बीच में होता है।
तीन पंक्तियों बाद फ़ॉरवर्ड-फ़िल क्या करता है
यहाँ वह विफलता है जिसकी वजह से मैंने यह लिखा। 19 मिनट का समूह लें। आम सफ़ाई: पूरा मिनट-ग्रिड बनाने के लिए रीइंडेक्स करें, आख़िरी क्लोज़ से OHLC को फ़ॉरवर्ड-फ़िल करें, वॉल्यूम को शून्य रखें। अब सीरीज़ लगातार है और आपके इंडिकेटर बिना कहीं NaN आए चल जाते हैं।
उन 19 बार में high == low == close है। हर बार के लिए True range शून्य है। उस विंडो पर निकाला गया ATR(14), जो आउटेज से पहले लगभग 240 USDT था, वेन्यू के वापस आने तक घटकर लगभग 34 हो जाता है — विंडो में बचने वाले 5 असली बार ही पूरा औसत तय करते हैं। अब इसे अस्थिरता के हिसाब से स्केल होने वाले पोज़िशन साइज़र में डालें, सामान्य size = risk_budget / ATR किस्म के। साइज़ 7 गुना बढ़ जाता है।
अगला असली बार दोबारा खुलने का प्रिंट है, और वह शांत बार नहीं है। हमारे मामले में वह आख़िरी आउटेज-पूर्व क्लोज़ से 1.8% दूर खुला। बैकटेस्ट ने ख़ुशी-ख़ुशी 1.8% के अंतराल में 7x पोज़िशन ले ली; ऐसा फ़िल इस्तेमाल करके जो हो ही नहीं सकता था, उस कीमत पर जिसे कोई कोट नहीं कर रहा था। इक्विटी कर्व में उस एक सिंथेटिक ट्रेड का मूल्य एक महीने से अधिक के वैध P&L से बड़ा था, ग़लत दिशा में — और यह पूरी तरह dataframe को साफ़-सुथरा दिखाने के लिए लिखी गई डेटा-सफ़ाई की एक पंक्ति से पैदा हुआ।
पंक्तियाँ भरने के बजाय हटाना भी समाधान नहीं है; यह वही बग है, बस दूसरे रूप में। उन्हें हटाएँ तो आपके पूर्णांक-इंडेक्स वाले लुकबैक झूठ बोलते हैं: अब "20-bar EMA" आउटेज के आर-पार दीवार-घड़ी के 39 मिनट समेटता है, सीमा के आर-पार बार-दर-बार रिटर्न पूरे 1.8% उछाल को एक मिनट की चाल मानता है, और बार-दर-बार अस्थिरता का हर अनुमान इसे 60-सिग्मा घटना समझता है। कोई चेतावनी नहीं मिलती। इंडेक्स अब भी मोनोटोनिक है।
रीसैंपलिंग में समस्या अदृश्य हो जाती है
ज़्यादातर रिसर्च 1-मिनट बार पर नहीं, बल्कि एकत्र किए गए डेटा पर चलती है, और एग्रीगेशन समस्या को छिपा देता है। 5-मिनट पर री-सैंपल करें तो 19-मिनट का अंतराल 4 बार बन जाता है, जिनमें पहला और आख़िरी आंशिक होते हैं। Pandas बचे हुए 2 मिनटों से ऐसा OHLC निकालेगा जो देखने में बिल्कुल उचित लगेगा, और उसे उसी तरह लेबल करेगा जैसे 5 मिनटों से बने बार को। आउटपुट में कुछ भी उन्हें अलग नहीं दिखाता।
मेरे हिसाब से सबसे सस्ता सुधार: हर री-सैंपल में bars_in_window कॉलम साथ रखें और उसे कभी न हटाएँ। हर पंक्ति के लिए एक पूर्णांक; इससे हर आगे के सवाल का जवाब दिया जा सकता है कि कोई बार भरोसेमंद है या नहीं। हम seconds_since_last_real_print भी साथ रखते हैं, जिसमें यही जानकारी ऐसे रूप में होती है जिस पर एक्ज़ीक्यूशन लेयर कार्रवाई कर सकती है।
हमारी नीति, जितनी भी है
हमारे एजेंट अब इस क्रम में लागू करते हैं:
- बिना बताए कभी रीइंडेक्स न करें। लोडर एक अंतराल-मैनिफ़ेस्ट देता है — शुरुआत, अंत, अवधि, और उसे लगता है कि वह 4 श्रेणियों में से किसमें आता है। अगर छूटे हुए मिनटों का समूह 3 बार से छोटा हो और आसपास के बार में वॉल्यूम कम हो, तो वह बिना ट्रेड वाला मिनट है। सक्रिय घंटों में इससे लंबा कुछ भी, जब तक उलटा साबित न हो, आउटेज माना जाता है।
- अर्थ निकालने से पहले डेटा दोबारा खींचें। हमारे शुरुआती आधे अंतराल पेजिनेशन की गड़बड़ियों के कारण थे। किसी अलग एंडपॉइंट या अलग विक्रेता से दूसरी बार डेटा खींचने पर चौथी श्रेणी की गड़बड़ी दूर होती है और कोई निर्णय लेने से पहले समस्या छोटी हो जाती है।
- अंतराल भरने की जगह पुरानापन रोकने का गेट। रणनीति को
data_ageइनपुट और एक सख़्त नियम मिलता है: आख़िरी असली प्रिंट N बार से पुराना हो तो कोई नई एंट्री नहीं, और खुली पोज़िशन दोबारा खुलने पर तभी फ़्लैटन होती है जब गैप-रिस्क की स्पष्ट कटौती के साथ कीमत तय किए गए मार्केट ऑर्डर से ऐसा किया जाए। ऐसे फ़िल जिनका होना संभव नहीं था, उन ट्रेडों से बदतर हैं जो हुए ही नहीं। - इंडिकेटरों को NaN दिखाएँ, मनगढ़ंत डेटा नहीं। फ़ॉरवर्ड-फ़िल की गई कीमतें कभी फ़ीचर लेयर तक नहीं पहुँचतीं। अगर ATR की गणना नहीं हो सकती, तो वह अपरिभाषित है, और अपरिभाषित का मतलब है फ्लैट। चुपचाप 7x लेने से बेहतर है कि ज़ोर से विफल हो।
- अंतराल के संदर्भ में प्रदर्शन रिपोर्ट करें। हमारे हर शिप किए गए बैकटेस्ट में मुख्य नतीजे के साथ आउटेज के आसपास के ट्रेड हटाकर P&L भी दिखाया जाता है। अगर नतीजे इन्हीं ट्रेडों पर टिके हैं, तो नतीजा डेटा का भ्रम है।
एक त्वरित जाँच आज ही कर सकते हैं: छूटे हुए मिनटों को लगातार समूहों में बाँटें, फिर देखें कि आपके बैकटेस्ट के कितने ट्रेड किसी समूह की सीमा से 30 मिनट के भीतर खुलते या बंद होते हैं। 1% से कम हो तो संभवतः अंतराल किसी चीज़ को प्रभावित नहीं कर रहे। 5% या उससे अधिक हो, तो इक्विटी कर्व की कहानी में एक्सचेंज का डाउनटाइम भी शामिल है।
अब मैं जिस संकेत पर भरोसा करता हूँ, वह छूटे हुए डेटा का आकार नहीं, बल्कि उसका पैटर्न है। कम गतिविधि वाले घंटों में हज़ारों बिखरे हुए अंतरालों वाला डेटासेट आम तौर पर ठीक होता है। कुछ सघन समूहों वाला डेटासेट बता रहा है कि दबाव में कुछ टूटता है, और दबाव में जो चीज़ टूटती है, वहीं आपकी रणनीति चलती है।
← सभी पोस्ट
