1-minute بارز کے ایک تقویمی سال میں 525,600 قطاریں ہونی چاہئیں۔ 2025 میں BTCUSDT پرپس کے لیے ہمارا ڈیٹا 525,557 قطاروں کے ساتھ واپس آیا — 43 منٹ کم، یعنی تکمیل کی شرح 99.992%۔ اسی وینیو سے، اسی ڈیٹا کھینچنے اور اسی کوڈ کے ذریعے ایک mid-cap alt پرپ کے ڈیٹا میں 1,247 قطاریں کم تھیں: 99.76%۔ اور اسی سال کی امریکی ایکویٹی منٹ سیریز میں کرپٹو سیریز سے تقریباً 427,000 کم قطاریں تھیں، جو خلا نہیں بلکہ ایک ایسے بازار کی علامت ہے جو بند ہوتا ہے۔
ان اعداد میں سے تین بے مزہ ہیں۔ جس ایک پر ہزار لفظ لکھے جا سکتے ہیں وہ 43 ہے۔
چار الگ چیزیں جو آپ کے dataframe میں ایک جیسے خلا دکھائی دیتی ہیں
43 پر آنے سے پہلے، ذرا اقسام سمجھ لیں، کیونکہ زیادہ تر gap-handling کوڈ اسی مرحلے پر پہلے ہی غلط ہوتا ہے۔ جب آپ کی مقامی parquet فائل میں کوئی قطار موجود نہیں تو آپ کو معلوم نہیں ہوتا کہ ان میں سے کون سی وجہ ہے، اور ہر صورت میں درست ردعمل مختلف ہے۔
| قسم | حقیقت میں کیا ہوا | یہ ڈیٹا میں کیسے دکھتا ہے | درست ردعمل |
|---|---|---|---|
| کوئی ٹریڈ نہیں ہوئی | بازار کھلا تھا؛ اس منٹ میں کسی نے اسپریڈ عبور نہیں کیا | کچھ endpoints پر صفر والیوم بار (OHLC سب برابر، trades=0)، اور دوسروں پر قطار غائب | اسے برقرار رکھیں۔ یہ حقیقی معلومات ہیں: کوئی ٹریڈ کرنا نہیں چاہتا تھا۔ |
| وینیو بند | میچنگ انجن آف لائن، طے شدہ طور پر یا اچانک | قطار غائب، کبھی مسلسل کئی قطاریں | اسے stale نشان زد کریں۔ اس کے پار ٹریڈ نہ کریں۔ |
| انسٹرومنٹ پر تعطل | LULD بینڈ کی خلاف ورزی، خبر زیرِ انتظار، ڈی لسٹنگ کا نوٹس | قطار غائب، پھر دوبارہ کھلنے کی نیلامی کا پرنٹ | اسے stale نشان زد کریں، اور دوبارہ کھلنے کو ایک غیر مسلسل تبدیلی سمجھیں۔ |
| آپ کی غلطی | ریٹ لِمٹڈ صفحہ بندی، ایسا retry جس نے ایک صفحہ گرا دیا، یا لوپ میں ٹائم زون کی حد سے متعلق بگ | قطار غائب، اوپر والی وجوہ سے الگ پہچانی نہیں جا سکتی | اسے پکڑیں اور دوبارہ ڈیٹا لیں۔ یہی وہ مسئلہ ہے جسے آپ ٹھیک کر سکتے ہیں۔ |
وینیوز اس جدول کی پہلی قسم پر متفق نہیں، اور یہی بات مسئلہ بناتی ہے۔ Coinbase کا candles endpoint خالی buckets کو مکمل طور پر چھوڑ دیتا ہے، اس لیے کم لیکویڈیٹی والے جوڑے کی ہسٹری میں لفظی خلا بھرے ہوتے ہیں۔ Binance کے klines عموماً ایک مصنوعی بار دیتے ہیں جس کا والیوم 0 ہوتا ہے اور جس کے open=high=low=close پچھلی ٹریڈ پر مقرر ہوتے ہیں۔ بنیادی حقیقت ایک، شکلیں دو؛ اور جو loader پورے منٹوں کی گرڈ پر دوبارہ index کرتا ہے، وہ آپ کو بتائے بغیر ایک شکل کو دوسری میں بدل دیتا ہے۔ gap-filler لکھنے سے پہلے دیکھیں کہ آپ کا وینیو کیا کرتا ہے، بعد میں نہیں۔
alt کے 1,247 غائب منٹ تقریباً سب پہلی قسم کے تھے: اتوار کو 04:00 UTC پر آرڈر بک میں کم لیکویڈیٹی تھی اور کوئی ٹریڈ نہیں کر رہا تھا۔ پریشان کن، سمجھنے میں آسان، اور بڑی حد تک بے ضرر، کیونکہ اس وقت اسٹریٹجی نے ویسے بھی ٹریڈ نہیں کرنی تھی۔ اسی لیے میں نے اسے دیکھنا چھوڑا اور واپس 43 پر آ گیا۔
43 منٹ بکھرے ہوئے نہیں تھے
اگر غیاب یکساں طور پر ہوتا تو پورے سال میں 43 منٹ، 43 الگ تھلگ منٹوں کی صورت آتے، ہر ساڑھے آٹھ دن بعد ایک، اور ہر ایک اتنا معمولی کہ نظر انداز ہو جائے۔ ہمیں ایسا نہیں ملا۔ وہ 6 سلسلوں میں آئے: ایک مسلسل 19 منٹ کی، ایک 11 کی، دو 4 منٹ کی، اور دو جوڑے۔ 6 واقعات، 43 اتفاقی خلا نہیں۔
اور یہ واقعات اس چیز سے جڑے ہیں جس کی آپ کو پروا ہے۔ وینیوز پر بوجھ بڑھنے پر بند ہوتے ہیں، اور قیمت حرکت کر رہی ہو تو بوجھ بڑھتا ہے۔ میں نے سال کے ہر گھنٹے کو حاصل شدہ والیٹیلیٹی کے لحاظ سے گروپ کیا اور دیکھا کہ غائب منٹ کہاں تھے: ان میں سے 61% ٹاپ ڈیسیل میں تھے۔ کسی بھی منٹ کے غائب ہونے کا غیر مشروط امکان 0.008% ہے۔ ٹاپ ڈیسیل والیٹیلیٹی والے گھنٹے میں ہونے کی شرط پر یہ تقریباً 0.05% ہے — 6 گنا زیادہ، اور اس کے علاوہ یہ منٹ جھرمٹ بنا کر آتے ہیں۔
اس لیے آپ کے ڈیٹا کوالٹی ڈیش بورڈ پر تکمیل کا پیمانہ غلط چیز ناپ رہا ہے۔ 99.992% سن کر لگتا ہے کہ ڈیٹاسیٹ کے بارے میں مزید سوچنے کی ضرورت نہیں۔ حقیقت میں یہ ایسی سیریز بیان کرتا ہے جو ان اوقات میں مکمل ہے جب آپ کی اسٹریٹجی کچھ نہیں کرتی، اور انہی اوقات میں سوراخوں سے بھری ہے جب وہ سب کچھ کرتی ہے۔ والیٹیلیٹی کے پھیلاؤ پر فعال ہونے والے مومینٹم سسٹم کے لیے gap سے ٹکرانے کا امکان سرخی کے عدد سے کہیں زیادہ ہے، اور یہ ٹریڈ کے بیچ میں gap سے ٹکراتا ہے۔
فارورڈ فل تین سطروں بعد کیا کرتا ہے
یہ وہ خرابی ہے جس نے مجھے یہ لکھنے پر آمادہ کیا۔ 19 منٹ کا سلسلہ لیں۔ معمول کی صفائی: پورے منٹوں کی گرڈ پر دوبارہ index کریں، OHLC کو آخری close سے forward-fill کریں، اور والیوم صفر رکھیں۔ اب سیریز مسلسل ہے اور آپ کے indicators بغیر کسی NaN کے چلتے ہیں۔
ان 19 بارز میں high == low == close ہے۔ ہر ایک کے لیے true range صفر ہے۔ اس ونڈو پر نکالا گیا ATR(14)، جو تعطل سے پہلے تقریباً 240 USDT تھا، وینیو واپس آنے تک گھٹ کر تقریباً 34 رہ جاتا ہے — ونڈو میں بچ جانے والی 5 حقیقی بارز پورا اوسط اٹھائے ہوئے ہیں۔ اب اسے والیٹیلیٹی کے مطابق پوزیشن سائز طے کرنے والے عام size = risk_budget / ATR کو دیں۔ پوزیشن سائز 7 گنا بڑھ جاتا ہے۔
اگلی حقیقی بار دوبارہ کھلنے کا پرنٹ ہے، اور یہ پرسکون بار نہیں۔ ہمارے معاملے میں یہ تعطل سے پہلے کے آخری close سے 1.8% دور کھلی۔ بیک ٹیسٹ نے خوشی سے 1.8% کے gap میں 7x پوزیشن لے لی، ایسی fill پر جو ممکن ہی نہیں تھی، ایسی قیمت پر جس کی کوئی کوٹ نہیں دے رہا تھا۔ ایک ہی مصنوعی ٹریڈ نے ایکویٹی کرو میں ایک ماہ کے جائز P&L سے زیادہ قدر بنائی، غلط سمت میں — اور یہ سب صرف dataframe کو صاف ستھرا بنانے کے لیے لکھی گئی ڈیٹا صفائی کی ایک سطر سے ہوا۔
قطاریں بھرنے کے بجائے گرا دینا بھی حل نہیں، یہ وہی بگ ہے جس نے بس دوسرا روپ پہن لیا ہے۔ انہیں گرا دیں تو integer-indexed lookbacks غلط تاثر دیتے ہیں: اب "20-bar EMA" تعطل کے دوران گھڑی کے 39 منٹ پر پھیلا ہے، حد کے پار بار بہ بار ریٹرن پورا 1.8% کا اچھلاؤ ہے جسے ایک منٹ کی حرکت سمجھا جاتا ہے، اور فی بار والیٹیلیٹی کا ہر تخمینہ اسے 60-sigma واقعہ پڑھتا ہے۔ کوئی چیز خبردار نہیں کرتی۔ انڈیکس اب بھی مسلسل بڑھ رہا ہوتا ہے۔
ری سیمپلنگ میں مسئلہ نظروں سے اوجھل ہو جاتا ہے
زیادہ تر تحقیق 1-minute بارز پر نہیں بلکہ جمع شدہ ڈیٹا پر چلتی ہے، اور aggregation مسئلے کو چھپا دیتی ہے۔ 5-minute پر ری سیمپل کریں تو 19 منٹ کا خلا 4 بارز بن جاتا ہے، جن میں پہلی اور آخری جزوی ہیں۔ Pandas دو بچ جانے والے منٹوں سے بظاہر بالکل معقول OHLC نکال کر انہیں اسی طرح لیبل کرتا ہے جیسے 5 منٹ سے بنی بار کو۔ آؤٹ پٹ میں کوئی فرق نہیں دکھاتا۔
سب سے سستا حل جو مجھے معلوم ہے: ہر ری سیمپل کے دوران bars_in_window کالم ساتھ رکھیں اور اسے کبھی نہ پھینکیں۔ ہر قطار کے لیے ایک عدد، اور اس کے بعد آنے والے ہر سوال کا جواب دیا جا سکتا ہے کہ آیا کوئی بار قابلِ اعتماد ہے۔ ہم seconds_since_last_real_print بھی ساتھ رکھتے ہیں، جو یہی معلومات ایسی شکل میں دیتا ہے جس پر execution layer عمل کر سکتی ہے۔
ہماری پالیسی، جتنی بھی ہے
اب ہمارے agents یہ اقدامات ترتیب سے کرتے ہیں:
- خاموشی سے کبھی دوبارہ index نہ کریں۔ Loader ایک gap manifest جاری کرتا ہے — آغاز، اختتام، دورانیہ، اور چار اقسام میں سے وہ قسم جس کا اسے گمان ہے۔ اگر غائب منٹوں کا سلسلہ 3 بارز سے کم ہو اور آس پاس کی بارز میں والیوم کم ہو تو اسے ایسا منٹ سمجھا جاتا ہے جس میں کوئی ٹریڈ نہیں ہوئی۔ فعال اوقات میں اس سے طویل ہر خلا کو، جب تک اس کے برعکس ثابت نہ ہو، تعطل سمجھا جاتا ہے۔
- تشریح سے پہلے دوبارہ ڈیٹا لیں۔ ہمارے ابتدائی خلا میں سے نصف pagination bugs تھے۔ کسی دوسرے endpoint یا vendor سے دوسری بار ڈیٹا لینے سے چوتھی قسم کا مسئلہ حل ہو جاتا ہے اور فیصلہ کرنے کی ضرورت سے پہلے مسئلہ چھوٹا ہو جاتا ہے۔
- خلا بھرنے کے بجائے تازگی کی حد لگائیں۔ اسٹریٹجی کو
data_ageinput دیں اور سخت اصول رکھیں: جب آخری حقیقی پرنٹ N بارز سے پرانا ہو تو نئی پوزیشن نہ کھولیں، اور کھلی پوزیشنیں دوبارہ کھلنے پر صرف ایسے market order سے بند ہوں جس کی قیمت میں gap-risk کے لیے واضح رعایت شامل ہو۔ ناممکن fills، ایسی ٹریڈز سے بدتر ہیں جو ہوئی ہی نہیں۔ - Indicators کو NaN دکھائیں، من گھڑت قیمتیں نہیں۔ فارورڈ فل کی گئی قیمتیں feature layer تک کبھی نہیں پہنچتیں۔ اگر ATR نہیں نکالا جا سکتا تو وہ غیر متعین ہے، اور غیر متعین ہونے کا مطلب ہے پوزیشن فلیٹ۔ خاموش 7x سے بلند اور واضح ناکامی بہتر ہے۔
- خلا کے آس پاس کی کارکردگی رپورٹ کریں۔ ہم جو بھی بیک ٹیسٹ جاری کرتے ہیں، اس میں سرخی والے نتیجے کے ساتھ، تعطل کے قریب والی ٹریڈز نکال کر P&L بھی دکھایا جاتا ہے۔ اگر نتیجہ انہی ٹریڈز کے سہارے ہے تو وہ ڈیٹا کا مصنوعی اثر ہے۔
ایک فوری آڈٹ جو آپ آج کر سکتے ہیں: اپنے غائب منٹوں کو مسلسل سلسلوں میں گروپ کریں، پھر دیکھیں کہ بیک ٹیسٹ کی کتنی ٹریڈز کسی سلسلے کی حد سے 30 منٹ کے اندر کھلتی یا بند ہوتی ہیں۔ یہ تناسب 1% سے کم ہے تو غالباً خلا کسی چیز کو متاثر نہیں کر رہے۔ اگر 5% یا زیادہ ہے تو ایکویٹی کرو کی کہانی کا کچھ حصہ ایکسچینج کے بند رہنے کے بارے میں ہے۔
اب میں جس اشارے پر بھروسا کرتا ہوں وہ غائب ڈیٹا کی مقدار نہیں بلکہ اس کا انداز ہے۔ کم سرگرمی کے اوقات میں ہزاروں بکھرے ہوئے خلا والا ڈیٹاسیٹ عموماً ٹھیک ہوتا ہے۔ چند گنجان جھرمٹوں والا ڈیٹاسیٹ بتاتا ہے کہ بوجھ کے تحت کچھ ٹوٹتا ہے، اور بوجھ کے تحت جو چیز ٹوٹتی ہے، آپ کی اسٹریٹجی عین وہیں چلتی ہے۔
← تمام پوسٹس
