دادههای یک سال تقویمی با کندلهای 1دقیقهای باید 525,600 ردیف داشته باشد. دادههایی که برای قراردادهای دائمی BTCUSDT در سال 2025 دریافت کردیم، 525,557 ردیف داشت — 43 دقیقه کمتر، یعنی نرخ کاملبودن 99.992%. دادههای یک قرارداد دائمی آلتکوین با ارزش بازار متوسط، از همان صرافی و با همان دریافت و همان مسیر کد، 1,247 ردیف کمتر داشت: 99.76%. و سری دقیقهای سهام آمریکا برای همان سال، حدود 427,000 ردیف کمتر از سری رمزارز داشت؛ که اصلاً شکاف نیست، فقط بازاری است که تعطیل میشود.
سهتا از این عددها خستهکنندهاند. آن یکی که هزار کلمه میارزد، 43 است.
چهار اتفاق متفاوت که همگی در دیتافریمتان شبیه یک حفرهاند
پیش از پرداختن به 43، اول دستهبندی را روشن کنیم؛ چون بیشتر کدهای رسیدگی به شکاف، همینجا از اساس اشتباهاند. وقتی ردیفی در فایل محلی parquet شما وجود ندارد، نمیدانید کدامیک از این اتفاقها باعثش شده، و پاسخ درست برای هرکدام فرق میکند.
| نوع | واقعاً چه اتفاقی افتاده | چطور در داده دیده میشود | واکنش درست |
|---|---|---|---|
| معاملهای انجام نشد | بازار باز بود؛ آن دقیقه کسی از اسپرد عبور نکرد | کندل با حجم صفر (OHLC همگی برابر، trades=0) در بعضی endpointها؛ در بعضی دیگر ردیف وجود ندارد | نگهش دارید. اطلاعات واقعی است: کسی نخواسته معامله کند. |
| ازکارافتادگی صرافی | موتور تطبیق سفارشها از دسترس خارج شده، چه برنامهریزیشده چه نه | ردیف وجود ندارد؛ گاهی چند ردیف پشتسرهم | آن را کهنه علامت بزنید. در این بازه معامله نکنید. |
| توقف نماد | عبور از باند LULD، انتشار خبر در انتظار، اطلاعیه حذف از فهرست | ردیف وجود ندارد و بعد، معاملهای در حراج بازگشایی ثبت میشود | آن را کهنه علامت بزنید و بازگشایی را گسست در نظر بگیرید. |
| اشتباه از شماست | صفحهبندی محدودشده با نرخ درخواست، تلاشی دوباره که یک صفحه را جا انداخته، یا باگی در حلقه هنگام عبور از مرز منطقه زمانی | ردیف وجود ندارد و از موارد بالا قابلتشخیص نیست | پیدایش کنید و دوباره بگیرید. این مورد را میتوانید رفع کنید. |
صرافیها درباره ردیف اول این جدول با هم فرق دارند؛ همین تفاوت دردسرساز میشود. endpoint کندل Coinbase بازههای خالی را کلاً حذف میکند، برای همین تاریخچه یک جفتارز کمنقدشونده پر از حفرههای واقعی است. klines بایننس معمولاً کندلی مصنوعی تحویل میدهد که حجمش 0 است و open=high=low=close روی آخرین معامله ثابت مانده. یک واقعیت واحد با دو شکل متفاوت؛ و بارگذاریای که دادهها را دوباره روی شبکه کامل دقیقهای میچیند، یکی را بیسروصدا به دیگری تبدیل میکند. پیش از نوشتن کد پرکردن شکاف، بررسی کنید صرافیتان چه رفتاری دارد؛ نه بعد از آن.
1,247 دقیقه گمشده در آن آلتکوین تقریباً همگی از دسته اول بودند: دفتر سفارش کمعمق در ساعت 04:00 UTC یکشنبه، و کسی معامله نمیکرد. آزاردهنده بود، فهمیدنش آسان بود و تا حد زیادی بیضرر، چون استراتژی بههرحال آن موقع معامله نمیکرد. دقیقاً به همین دلیل بود که بررسیاش را کنار گذاشتم و برگشتم سراغ 43 دقیقه.
این 43 دقیقه پراکنده نبودند
اگر دادههای گمشده یکنواخت پخش شده بودند، 43 دقیقه در طول یک سال به 43 مورد تکافتاده تبدیل میشدند؛ هر هشت روز و نیم یکی، و هرکدام خطایی ناچیز. اما اینطور نبود. آنها در شش بازه پشتسرهم قرار داشتند: یکی 19 دقیقه، یکی 11 دقیقه، دو مورد 4دقیقهای و چند جفت. شش رویداد، نه 43 اتفاق تصادفی.
این رویدادها به همان چیزی وابستهاند که برایتان اهمیت دارد. صرافیها وقتی زیر بار هستند از کار میافتند، و وقتی قیمت حرکت میکند زیر بار میروند. ساعتهای سال را بر اساس نوسان تحققیافته دستهبندی کردم و پرسیدم دقیقههای گمشده کجا بودند: 61% آنها در دهک بالای نوسان قرار داشتند. احتمال کلی گمشدن یک دقیقه مشخص 0.008% است. اما اگر آن دقیقه در ساعت دهک بالای نوسان باشد، این احتمال حدود 0.05% است — شش برابر بیشتر، و تازه این دقیقهها بهشکل خوشهای هم کنار هم میآیند.
پس معیاری که داشبورد کیفیت دادهتان نشان میدهد، چیز اشتباهی را میسنجد. 99.992% طوری بهنظر میرسد که انگار دیگر لازم نیست به مجموعهداده فکر کنید. اما این عدد در واقع سریای را توصیف میکند که در ساعتهایی که استراتژیتان کاری نمیکند کامل است و در ساعتهایی که همهچیز را انجام میدهد حفره دارد. احتمال برخورد یک سیستم مومنتوم که با گسترش نوسان فعال میشود به شکاف، بسیار بیشتر از چیزی است که عدد کلی نشان میدهد؛ و شکاف را وسط معامله تجربه میکند.
پرکردن رو به جلو، سه خط کد بعدتر چه میکند
این همان خطایی است که باعث شد این مطلب را بنویسم. آن بازه 19دقیقهای را در نظر بگیرید. پاکسازی استاندارد: دادهها را دوباره روی شبکه کامل دقیقهای بچینید، OHLC را از آخرین قیمت بستهشدن به جلو پر کنید و حجم را صفر بگذارید. حالا سری پیوسته است و اندیکاتورها بدون اینکه NaN ببینند اجرا میشوند.
در آن 19 کندل، high == low == close است. محدوده واقعی برای تکتکشان صفر است. ATR(14) که روی این بازه محاسبه میشود، از مقدار حدود 240 USDT پیش از قطعی شروع میکند و تا زمان بازگشت صرافی به حدود 34 کاهش مییابد — میانگین تقریباً فقط از 5 کندل واقعی باقیمانده در بازه ساخته میشود. حالا این مقدار را به یک محاسبهگر اندازه موقعیت مقیاسشده با نوسان بدهید، از نوع معمولی size = risk_budget / ATR؛ اندازه موقعیت هفت برابر میشود.
کندل واقعی بعدی همان معامله ثبتشده هنگام بازگشایی است و کندل آرامی نیست. در مورد ما، قیمت بازگشاییاش 1.8% با آخرین قیمت بستهشدن پیش از قطعی فاصله داشت. بکتست با کمال میل وارد موقعیتی 7برابری در دل یک شکاف 1.8% شد؛ با اجرایی که نمیتوانست رخ داده باشد، در قیمتی که هیچکس اعلام نمیکرد. همان معامله مصنوعی بهاندازه بیش از یک ماه سود و زیان واقعی در منحنی سرمایه اثر داشت، آن هم در جهت اشتباه — و تمامش از یک خط پاکسازی داده بهوجود آمد که فقط قرار بود دیتافریم را مرتب کند.
حذفکردن ردیفها بهجای پرکردنشان هم راهحل نیست؛ همان باگ است با ظاهری دیگر. اگر ردیفها را حذف کنید، بازههای بازنگری مبتنی بر اندیس صحیح دروغ میگویند: یک «EMA با دوره 20 کندل» حالا در طول قطعی 39 دقیقه زمان واقعی را پوشش میدهد، بازده از کندلی به کندل دیگر در مرز شکاف کل جهش 1.8% را حرکت یکدقیقهای تلقی میکند، و هر برآورد نوسان بهازای هر کندل آن را رویدادی با مقدار 60-sigma میخواند. هیچ هشداری نمیگیرید. اندیس همچنان صعودی و مرتب است.
بازنمونهگیری جایی است که مشکل نامرئی میشود
بیشتر پژوهشها با کندلهای 1دقیقهای انجام نمیشوند؛ با دادههای تجمیعشده انجام میشوند، و تجمیع مشکل را میپوشاند. اگر بازنمونهگیری را با بازه 5دقیقهای انجام دهید، یک حفره 19دقیقهای به 4 کندل تبدیل میشود که اولی و آخری ناقصاند. Pandas از 2 دقیقه باقیمانده، OHLC کاملاً معقولی محاسبه میکند و همان برچسبی را به آن میدهد که به کندلی ساختهشده از 5 دقیقه میدهد. هیچچیز در خروجی این دو را از هم متمایز نمیکند.
ارزانترین راهحلی که میشناسم: یک ستون bars_in_window را در تمام مراحل بازنمونهگیری همراه دادهها نگه دارید و هیچوقت دورش نیندازید. در هر ردیف یک عدد صحیح؛ با آن میتوان به هر پرسش بعدی درباره قابلاعتمادبودن کندل پاسخ داد. ما seconds_since_last_real_print را هم نگه میداریم که همین اطلاعات است، اما بهشکلی که لایه اجرا بتواند بر اساسش اقدام کند.
سیاست ما، هرچه که هست
عاملهای ما حالا این کارها را بهترتیب انجام میدهند:
- هرگز بیسروصدا بازاندیسگذاری نکنید. بارگذار فهرستی از شکافها تولید میکند — زمان شروع، پایان، طول و یکی از چهار دستهای که احتمالاً به آن تعلق دارند. اگر بازه دقیقههای گمشده کمتر از 3 کندل باشد و حجم کندلهای اطراف کم باشد، آن را دقیقهای بدون معامله میدانیم. هر بازه طولانیتری در ساعات فعال، تا وقتی خلافش ثابت نشده، ازکارافتادگی محسوب میشود.
- پیش از تفسیر، داده را دوباره بگیرید. نیمی از شکافهای اولیه ما بهخاطر باگهای صفحهبندی بودند. دریافت دوباره از endpoint یا ارائهدهندهای دیگر، دسته چهارم را مشخص میکند و پیش از نیاز به هرگونه قضاوت، ابعاد مشکل را کوچکتر میکند.
- دروازه کهنگی، نه پرکردن شکاف. ورودی استراتژی
data_ageاست و یک قانون سخت دارد: وقتی آخرین معامله واقعی قدیمیتر از N کندل است، ورود جدید ممنوع؛ موقعیتهای باز هم فقط هنگام بازگشایی و با سفارش بازار بسته میشوند، آن هم با تعدیل مشخصی برای ریسک شکاف در قیمت. اجرایی که نمیتوانست رخ دهد، بدتر از معاملهای است که انجام نشده. - اندیکاتورها NaN میبینند، نه داده جعلی. قیمتهای پرشده رو به جلو هرگز به لایه ویژگی نمیرسند. اگر ATR قابلمحاسبه نباشد، تعریفنشده است و تعریفنشده یعنی بدون موقعیت. شکست آشکار بهتر از موقعیت آرام و بیهشدار 7برابری است.
- عملکرد مشروط به شکاف را گزارش کنید. در هر بکتستی که منتشر میکنیم، کنار نتیجه کلی، سود و زیان را با حذف معاملات نزدیک به ازکارافتادگی نشان میدهیم. اگر نتیجه را همین معاملات میسازند، با یک مصنوع داده طرفیم.
یک بررسی سریع که همین امروز میتوانید انجام دهید: دقیقههای گمشده را به بازههای پیوسته تقسیم کنید و بعد بررسی کنید چه سهمی از معاملات بکتستتان حداکثر تا 30 دقیقه پیش یا پس از مرز یک بازه باز یا بسته شدهاند. اگر کمتر از 1% است، احتمالاً شکافها اثر تعیینکنندهای ندارند. اگر 5% یا بیشتر است، بخشی از منحنی سرمایهتان درواقع داستانی درباره ازکارافتادگی صرافی است.
نشانهای که یاد گرفتهام به آن اعتماد کنم، شکل دادههای گمشده است، نه تعدادشان. مجموعهدادهای با هزاران حفره پراکنده در ساعتهای کمفعالیت معمولاً مشکلی ندارد. مجموعهدادهای با چند خوشه فشرده دارد میگوید چیزی زیر بار از کار میافتد؛ و همان چیزی که زیر بار از کار میافتد، جایی است که استراتژی شما زندگی میکند.
← همهٔ مطالب
