3 September 2026 · rekayasa data

Menit yang tidak ada: celah, penghentian, dan bar bervolume nol dalam riwayat OHLCV

Menit yang tidak ada: celah, penghentian, dan bar bervolume nol dalam riwayat OHLCV

Data bar 1 menit selama 1 tahun kalender seharusnya berisi 525,600 baris. Data perpetual BTCUSDT yang kami ambil untuk 2025 ternyata berisi 525,557 — kurang 43 menit, dengan tingkat kelengkapan 99.992%. Data perpetual altcoin berkapitalisasi menengah di venue yang sama, dari pengambilan yang sama dan jalur kode yang sama, ternyata kurang 1,247 menit: 99.76%. Sementara itu, rangkaian data per menit saham AS untuk tahun yang sama memiliki sekitar 427,000 baris lebih sedikit daripada data kripto. Itu bukan celah sama sekali, melainkan pasar yang memang tutup.

Tiga angka itu tidak istimewa. Angka yang layak dibahas panjang lebar adalah 43.

525,600bar 1 menit dalam tahun biasa
0.008%hilang pada BTCUSDT, 2025
61%dari menit tersebut terjadi pada jam dengan volatilitas desil teratas

Empat hal berbeda yang semuanya tampak seperti celah dalam dataframe Anda

Sebelum membahas 43 menit itu, mari kita lihat taksonominya, karena sebagian besar kode penanganan celah sudah keliru pada tahap ini. Saat sebuah baris tidak ada dalam berkas parquet lokal Anda, Anda tidak tahu penyebabnya. Tindakan yang tepat pun berbeda untuk tiap penyebab.

JenisApa yang sebenarnya terjadiBagaimana tampilannyaTindakan yang tepat
Tidak ada transaksiPasar buka; tidak ada yang mengambil spread pada menit ituBar bervolume nol (OHLC sama semua, trades=0) pada sebagian endpoint, baris tidak ada pada endpoint lainPertahankan. Ini informasi nyata: tak ada yang ingin bertransaksi.
Venue matiMesin pencocokan order luring, terjadwal atau tidakBaris tidak ada, kadang dalam rentetanTandai sebagai basi. Jangan bertransaksi melewati periode ini.
Instrumen dihentikan sementaraBatas LULD terlampaui, berita ditunggu, pemberitahuan delistingBaris tidak ada, lalu muncul cetakan lelang pembukaan kembaliTandai sebagai basi, dan perlakukan pembukaan kembali sebagai diskontinuitas.
Kesalahan AndaPagination terkena batas laju, percobaan ulang yang melewatkan satu halaman, bug batas zona waktu dalam loopBaris tidak ada, tak bisa dibedakan dari penyebab di atasDeteksi dan ambil ulang. Inilah yang bisa Anda perbaiki.

Venue berbeda pendapat soal baris pertama dalam tabel itu, dan di situlah masalahnya muncul. Endpoint candle Coinbase melewatkan bucket kosong seluruhnya, sehingga riwayat pasangan yang tidak likuid penuh dengan celah nyata. Klines Binance umumnya memberi Anda bar sintetis dengan volume 0 dan open=high=low=close yang dipatok pada transaksi sebelumnya. Fakta dasarnya sama, tetapi bentuk datanya berbeda. Loader yang mengindeks ulang ke kisi menit penuh mengubah satu bentuk menjadi bentuk lain tanpa memberi tahu Anda. Periksa perilaku venue sebelum menulis pengisi celah, bukan sesudahnya.

Sebagian besar dari 1,247 menit yang hilang pada altcoin itu masuk kategori satu: order book tipis pada Minggu pukul 04:00 UTC, tak ada yang bertransaksi. Menyebalkan, mudah dipahami, dan sebagian besar tidak berbahaya karena strategi memang tidak akan bertransaksi saat itu. Justru itulah sebabnya saya berhenti memeriksanya dan kembali melihat 43 menit itu.

Ke-43 menit itu tidak tersebar

Jika kehilangan data tersebar merata, 43 menit sepanjang tahun akan muncul sebagai 43 titik tunggal yang terpisah, satu tiap delapan setengah hari, dan masing-masing hanya menjadi selisih pembulatan. Namun bukan itu yang kami temukan. Menit-menit itu muncul dalam 6 rentetan: satu rentetan 19 menit berturut-turut, satu 11 menit, dua rentetan 4 menit, dan beberapa pasang menit. Ada 6 kejadian, bukan 43 insiden terpisah.

Kejadian-kejadian ini juga berkaitan dengan hal yang Anda pedulikan. Venue mati saat bebannya tinggi, dan beban tinggi terjadi saat harga bergerak. Saya mengelompokkan tiap jam sepanjang tahun berdasarkan volatilitas terealisasi, lalu memeriksa di mana menit yang hilang berada: 61% di antaranya masuk desil teratas. Probabilitas tak bersyarat bahwa satu menit tertentu hilang adalah 0.008%. Jika menit itu berada dalam jam dengan volatilitas desil teratas, probabilitasnya sekitar 0.05% — 6 kali lebih tinggi, dan menit-menitnya juga mengelompok.

Jadi, metrik kelengkapan pada dasbor kualitas data Anda mengukur hal yang keliru. Angka 99.992% terdengar seperti dataset yang tak perlu lagi Anda pikirkan. Padahal, angka itu menggambarkan rangkaian data yang lengkap pada jam-jam ketika strategi Anda tidak melakukan apa-apa, tetapi berlubang saat strategi melakukan segalanya. Sistem momentum yang aktif ketika volatilitas meningkat memiliki peluang yang jauh lebih besar untuk terkena celah daripada yang tersirat oleh angka utama, dan celah itu muncul di tengah transaksi.

Dampak forward-fill beberapa baris kemudian

Inilah kegagalan yang membuat saya menulis artikel ini. Ambil rentetan 19 menit tadi. Langkah pembersihan data standar: indeks ulang ke kisi menit penuh, isi OHLC ke depan dari penutupan terakhir, tetapkan volume ke nol. Kini rangkaian datanya kontinu dan indikator Anda berjalan tanpa NaN.

Ke-19 bar itu memiliki high == low == close. True range bernilai nol pada setiap bar. ATR(14) yang dihitung pada rentang tersebut, setelah sebelumnya berada di sekitar 240 USDT sebelum gangguan, turun menjadi sekitar 34 saat venue kembali aktif — 5 bar nyata yang tersisa dalam rentang itu menanggung seluruh rata-ratanya. Masukkan angka tersebut ke penentu ukuran posisi berskala volatilitas, seperti jenis size = risk_budget / ATR yang umum. Ukuran posisi naik 7 kali lipat.

Bar nyata berikutnya adalah cetakan pembukaan kembali, dan bar itu sama sekali tidak tenang. Dalam kasus kami, pembukaannya berselisih 1.8% dari penutupan terakhir sebelum gangguan. Backtest dengan senang hati mengambil posisi 7x tepat sebelum celah 1.8%, pada eksekusi yang mustahil terjadi, dengan harga yang tidak dikutip siapa pun. Satu transaksi sintetis itu bernilai lebih dari laba rugi sah selama sebulan pada kurva ekuitas, dengan arah yang salah — dan sepenuhnya tercipta dari satu baris pembersihan data yang ditulis agar dataframe tampak rapi.

Menghapus baris alih-alih mengisinya juga bukan solusi; itu bug yang sama dengan tampilan berbeda. Setelah baris dihapus, lookback berindeks bilangan bulat Anda menyesatkan: "EMA 20 bar" kini mencakup 39 menit waktu nyata yang melintasi gangguan, imbal hasil antarbar pada batas tersebut memperlakukan seluruh lonjakan 1.8% sebagai pergerakan dalam 1 menit, dan estimasi volatilitas per bar membacanya sebagai kejadian 60-sigma. Tidak ada peringatan. Indeksnya tetap monoton.

Resampling membuat masalahnya tak terlihat

Sebagian besar riset tidak memakai bar 1 menit, melainkan data yang sudah diagregasi, dan agregasi menyamarkan masalah ini. Ubah frekuensinya ke 5 menit dan celah 19 menit menjadi 4 bar, dengan bar pertama dan terakhir hanya mencakup sebagian periode. Pandas akan menghitung OHLC yang tampak sepenuhnya masuk akal dari 2 menit yang tersisa, lalu memberi label yang sama seperti bar yang dibentuk dari 5 menit penuh. Tak ada yang membedakan keduanya dalam hasilnya.

Perbaikan termurah yang saya tahu: bawa kolom bars_in_window dalam setiap resampling dan jangan pernah membuangnya. Satu bilangan bulat per baris membuat setiap pertanyaan lanjutan tentang keterpercayaan sebuah bar bisa dijawab. Kami juga membawa seconds_since_last_real_print, yaitu informasi yang sama dalam bentuk yang bisa ditindaklanjuti oleh lapisan eksekusi.

Kebijakan kami saat ini

Inilah urutan yang kini diterapkan agen kami:

  1. Jangan pernah mengindeks ulang secara diam-diam. Loader menghasilkan manifes celah — awal, akhir, durasi, dan kategori yang diyakininya sebagai penyebab. Jika rentetan menit yang hilang kurang dari 3 bar dan volume pada bar-bar di sekitarnya tipis, berarti tidak ada transaksi pada menit tersebut. Apa pun yang lebih lama selama jam aktif dianggap sebagai gangguan sampai terbukti sebaliknya.
  2. Ambil ulang sebelum menganalisis. Separuh celah awal kami disebabkan bug pagination. Pengambilan kedua dari endpoint atau vendor lain mengatasi kategori empat dan memperkecil masalah sebelum perlu penilaian lebih lanjut.
  3. Gunakan gerbang data basi, bukan pengisi celah. Strategi menerima input data_age dan aturan tegas: jangan buka posisi baru jika cetakan nyata terakhir sudah lebih tua dari N bar, dan tutup posisi terbuka saat pasar dibuka kembali hanya dengan market order yang menerapkan pengurang eksplisit untuk risiko celah. Eksekusi yang mustahil terjadi lebih buruk daripada transaksi yang tidak terjadi.
  4. Indikator harus melihat NaN, bukan data rekaan. Harga yang diisi ke depan tidak pernah sampai ke lapisan fitur. Jika ATR tidak bisa dihitung, nilainya tidak terdefinisi, dan artinya posisi harus kosong. Kegagalan yang jelas lebih baik daripada kenaikan 7x yang tak kentara.
  5. Laporkan kinerja dengan mempertimbangkan celah. Setiap backtest yang kami terbitkan menampilkan laba rugi utama sekaligus hasil dengan transaksi di sekitar gangguan dikeluarkan. Jika transaksi tersebut yang menopang hasilnya, berarti hasil itu hanyalah artefak data.

Audit singkat yang bisa Anda lakukan hari ini: kelompokkan menit yang hilang menjadi rentetan menit berturut-turut, lalu periksa proporsi transaksi backtest Anda yang dibuka atau ditutup dalam 30 menit dari batas rentetan. Jika kurang dari 1%, celah itu mungkin tidak berdampak apa-apa. Jika 5% atau lebih, sebagian kurva ekuitas Anda menceritakan kisah tentang waktu henti bursa.

Petunjuk yang kini saya percaya adalah pola kehilangan datanya, bukan jumlahnya. Dataset dengan ribuan celah tersebar pada jam-jam sepi biasanya baik-baik saja. Dataset dengan beberapa kelompok celah yang rapat memberi tahu Anda bahwa ada sesuatu yang rusak saat beban tinggi, dan strategi Anda beroperasi tepat di tempat yang rusak itu.

celah OHLCVrekayasa databacktestingresamplingfutures kripto
← Semua artikel