← Kembali ke artikel
July 8, 2026
5 menit baca

Membuktikan Tidak Ada Look-Ahead pada Backtest Multi-Timeframe: Ganggu Masa Depan, Buktikan Masa Lalu Tidak Bisa Melihatnya

Membuktikan Tidak Ada Look-Ahead pada Backtest Multi-Timeframe: Ganggu Masa Depan, Buktikan Masa Lalu Tidak Bisa Melihatnya
#algotrading
#backtest
#bias look-ahead
#multi-timeframe
#kebocoran data
#validasi
🎯
Part 2 of 9 · Collection
Backtesting Without Fooling Yourself

Bagian dari seri "Backtests Without Illusions".

Artikel sebelumnya berakhir dengan sebuah diagnostik: tes pergeseran satu bar (one-bar shift test). Geser setiap fill satu bar lebih lambat; jika performa runtuh, berarti Anda sedang bertrading di masa lalu. Tes itu cukup untuk strategi single-timeframe, di mana look-ahead bersembunyi di eksekusi — Anda memutuskan pada bar i dan fill juga di bar i.

Strategi multi-timeframe punya tempat kedua yang lebih senyap untuk bocor, dan tes pergeseran tidak menjangkaunya. Ketika sebuah keputusan pada bar 1 menit merujuk pada tren 1 jam, kebocorannya bukan lagi di fill — melainkan di indikator, yang dihitung dari bar timeframe lebih tinggi yang belum selesai terbentuk. Artikel ini membahas kebocoran itu, dan bagaimana membuktikan — secara mekanis, bukan dengan menatapi kode — bahwa engine Anda tidak memilikinya.

Klaim yang kami buat di akhir tulisan ini kuat: engine multi-TF kami lolos 25 dari 25 pemeriksaan parity dan kebocoran pada 86,400 bar data ETHUSDT 1m yang nyata, termasuk probe shifted-future yang mengganggu setiap bar masa depan dan memastikan tidak satu pun keputusan masa lalu bergeser. Dan karena engine ini bit-identical dengan inti kanonik bot live, bukti ini menjadi properti dari sistem live, bukan sekadar backtest.

Ladang ranjau multi-timeframe

Sebuah keputusan 1 menit yang menjangkau tiga timeframe sekaligus, dengan candle timeframe tertinggi masih terbentuk dan close akhirnya tersembunyi di balik batas waktu

Strategi multi-timeframe (multi-TF) membuat keputusan pada jam cepat sambil merujuk pada jam-jam yang lebih lambat. Engine kami adalah contoh konkretnya: sebuah aturan momentum triple-TF di mana tren higher-timeframe (HTF) dan tren mid-timeframe (MTF) berfungsi sebagai gate yang menyaring entry, sementara cross pada lower-timeframe (LTF) yang memicu entry tersebut. Pada jam dasar 1m, entry hanya tereksekusi ketika cross HMA/HMA3 pada 1m searah dengan tren 1 jam dan tren 15 menit; exit tereksekusi ketika tren timeframe yang lebih tinggi berbalik melawan posisi, atau LTF cross kembali ke arah semula.

Bahayanya bersifat struktural. Setiap nilai timeframe yang lebih tinggi itu harus dijawab pada setiap bar 1m — 60 kali per jam — dan setiap jawaban itu hanya boleh menggunakan apa yang sudah diketahui oleh bot live pada momen itu. Begitu salah satu dari sekitar 180 pembacaan per-bar itu (tiga timeframe, tren entry dan exit, gate separasi) menyentuh bar yang belum tercetak, Anda punya look-ahead. Area permukaan untuk kesalahan off-by-one sangat besar, dan tidak seperti kebocoran eksekusi, kebocoran ini tidak mengumumkan dirinya lewat pergeseran fill.

Bar yang sedang terbentuk belum punya close akhir

Inilah jebakan persisnya. Misalkan sekarang pukul 10:37 dan Anda sedang memutuskan berdasarkan bar 1m yang baru saja closed. Aturan Anda membutuhkan tren 1 jam. Candle 1 jam yang mencakup 10:00–11:00 masih terbentuk — close akhirnya baru akan diketahui pada 10:59:59. Apa yang sebenarnya Anda ketahui pada 10:37? Hanya running close dari candle itu sejauh ini, yaitu close dari bar 10:37. Close akhir candle itu masih 23 menit lagi di masa depan.

Backtest multi-TF yang naif melakukan sesuatu yang tampak sepenuhnya tidak berbahaya: ia me-resample seluruh seri 1m menjadi 1 jam sekali di awal, lalu, untuk setiap bar 1m, membaca "close 1 jam." Tapi nilai yang dibacanya untuk setiap bar 1m antara 10:00 dan 11:00 adalah close akhir dari 10:00–11:00 — sebuah angka yang, dalam waktu nyata, belum ada sampai jam itu berakhir. Setiap keputusan di dalam jam itu diam-diam diberi akses ke hingga 59 menit masa depan. Dan ini bukan kebocoran kecil: close timeframe yang lebih tinggi adalah salah satu prediktor terkuat yang mungkin ada untuk return 1m di masa dekat yang akan Anda tradingkan, sehingga membocorkannya hampir sama dengan membaca kunci jawaban. Ini adalah kanal kebocoran-indikator dari taksonomi, yang diperbesar: intipannya bukan satu bar, melainkan hingga satu periode HTF penuh.

Tes pergeseran satu bar dari artikel sebelumnya tidak menangkap ini. Anda bisa menggeser fill satu bar lebih lambat dan seri 1 jam hasil resample tetap terkontaminasi — kebocorannya ada pada bagaimana indikator itu dibangun, bukan pada kapan Anda bertransaksi.

Aturan bot live, direproduksi persis: semantik closed-bar

Buffer timeframe lebih tinggi pada bar i: sebaris close dari candle yang sudah selesai closed, diikuti satu candle yang sedang terbentuk dengan nilai berupa base close saat ini, dengan close akhir yang masih tersembunyi dicoret

Aturan yang benar adalah aturan yang sudah dijalankan oleh bot live. Di codebase kami, ini berupa sebuah class kecil, RunningCandleBuffer, yang dipindahkan verbatim dari live tick simulator. Class ini men-stream bar dasar menjadi candle berperiode tetap, dan pada setiap bar dasar, menghitung indikator HTF dari sebuah array yang sangat spesifik:

all_closes = np.array(self.closes + [self.current_close], dtype=np.float64)

Bacalah itu secara harfiah. self.closes adalah close akhir dari candle-candle yang sudah closed — sebuah candle hanya ditambahkan ketika sebuah batas periode baru dilewati, dan nilai yang disimpannya adalah close dari bar dasar terakhir di dalam periode itu (candle_buffer.py:39–44). Candle yang sedang terbentuk hanya menyumbang tepat satu angka, self.current_close, yaitu close running — close dari bar dasar paling baru, base_close[i]. Itu adalah kuantitas yang, menurut definisinya, sudah diketahui pada bar i. Close akhir dari candle yang sedang terbentuk tidak pernah dipakai, karena belum ada.

Jadi indikator HTF pada pukul 10:37 melihat [..., close(9:00 candle), close(10:37 so far)]. Ketika 10:38 tercetak, slot terakhir diperbarui menjadi close(10:38). Ketika 11:00 terlewati, close(10:59) menjadi nilai akhir dari candle yang baru saja closed, dan sebuah slot forming baru terbuka. Tidak pernah ada satu titik pun di mana keputusan di dalam jam itu menyentuh close akhir 10:00–11:00. Itulah semantik closed-bar: candle yang sudah closed menyumbang close yang sudah final, candle yang sedang forming hanya menyumbang running close-nya.

Engine cepat kami (engine_multitf.py) adalah reimplementasi yang divektorisasi dan dikompilasi numba dari logika ini. Alih-alih loop Python dengan list yang terus bertambah, engine ini menghitung di muka, untuk setiap bar dasar i, berapa banyak candle yang sudah closed sepenuhnya (n_closed[i]) dan menyusun window HMA/HMA3 sebagai [closed candle closes…, base_close[i]] — running close yang disematkan di slot terakhir (engine_multitf.py:168–169). Ini adalah matematika yang sama, diurai (unrolled) demi kecepatan di tiga timeframe sekaligus dengan gate separasi arah. Kontraknya eksplisit: nilai pada bar i hanya bergantung pada base_close[0..i].

Itulah klaimnya. Sisa artikel ini membahas bagaimana kami membuktikannya, karena klaim di dalam docstring tidak ada artinya.

Parity itu perlu, tapi tidak cukup

Engine numba yang divektorisasi, yang menguraikan sebuah streaming class menjadi loop eksplisit, adalah tempat yang paling subur bagi kesalahan off-by-one untuk berkembang biak. Karena itu, gate pertama adalah tes parity bit-for-bit terhadap referensi kanonik, pada potongan data nyata — ETHUSDT 1m, Januari–Februari 2024, 86,400 bar.

Kami memeriksa dua hal yang independen terhadap dua referensi yang independen pula:

  • Indikator dan cross dibandingkan dengan RunningCandleBuffer yang dijalankan bar demi bar. Untuk setiap timeframe kami memutar ulang class live tersebut pada seluruh 86,400 bar dan membandingkan cross event — bar-nya, arahnya, separasinya — untuk kecocokan yang eksak, ditambah nilai HMA/HMA3 hingga toleransi floating-point (referensi menggunakan np.dot, engine menggunakan loop eksplisit, sehingga urutan penjumlahan berbeda pada kisaran ~1e-15). Cross-nya sejajar secara eksak: 408 cross pada HTF (1 jam, panjang HMA 21), 2,792 pada MTF (15m/14), 3,691 pada LTF (1m/50). Tidak satu pun cross event yang berbeda, baik dari sisi bar maupun arah.
  • Trade dibandingkan dengan simulasi pure-Python independen dari aturan trading, yang digerakkan oleh cross milik referensi itu sendiri. Ini mereproduksi loop backtest live — tren entry menjadi gate bagi entry, pembalikan tren exit atau cross LTF yang berlawanan menutup posisi, fill pada open[i+1], fee round-trip 0.09%, force-close pada bar terakhir — tanpa menggunakan sedikit pun mesin numba milik engine. Kami lalu membandingkan trade demi trade: bar entry/exit, sisi (side), harga entry/exit, PnL, alasan exit, dan total active-time dalam posisi.

Ambang batas separasi dalam tes ini bukan default yang asal-asalan. Ambang batas itu dipilih untuk menyentuh kasus sudut yang janggal — ambang exit pada MTF diset lebih tinggi daripada ambang entry yang sepadan — yang memaksa cabang "definisi pertama dari tren exit ketika sebuah posisi masih terbuka," yang oleh referensi diperlakukan sebagai pembalikan (reversal). Parity harus tetap berlaku pada kasus-kasus sudut ini, bukan hanya pada jalur yang mudah.

Field demi field, trade-nya identik: 466 trade untuk konfigurasi dual, 211 trade untuk triple, dengan total PnL yang cocok hingga 1e-12 dan setiap field trade sama dalam batas toleransi. Dua implementasi yang tidak berbagi kode sama sekali — sebuah engine vektorisasi terkompilasi dan sebuah loop Python naif di atas cross milik implementasi ketiga — menghasilkan trade yang sama hingga desimal terakhir.

Itu adalah hasil yang kuat, dan itu bukan bukti tidak adanya look-ahead. Parity hanya mengatakan bahwa engine cepat mereproduksi referensi dengan setia. Jika referensinya sendiri bocor — jika RunningCandleBuffer mengintip — parity akan dengan setia mereproduksi kebocoran itu dan tetap lolos. Kesesuaian antar-implementasi memberi tahu Anda bahwa keduanya sama, bukan bahwa keduanya kausal. Untuk kausalitas, Anda butuh jenis tes yang berbeda, yang bertanya langsung pada engine apakah masa lalu bisa melihat masa depan.

Probe shifted-future: bukti sesungguhnya

Sebuah potongan vertikal pada 60 persen dari seri data: segala sesuatu di sebelah kanan dikalikan dan diguncang, segala sesuatu di sebelah kiri beku identik bit-for-bit

Definisi look-ahead bersifat operasional, jadi ujilah secara operasional juga. Look-ahead berarti sebuah keputusan masa lalu bergantung pada data masa depan. Kontraposisinya adalah sebuah tes yang bisa Anda jalankan: jika Anda mengubah masa depan dan ada keputusan masa lalu yang bergeser, berarti masa lalu tadi membaca masa depan. Jadi, ubahlah masa depan — secara brutal — dan amati masa lalunya.

Pilih titik potong j pada 60% dari seri data (bar 51,840 dari 86,400). Ganggu setiap bar mulai dari j dan seterusnya: kalikan semua close dan open masa depan dengan 1.05. Hitung ulang seluruh stack sinyal untuk ketiga timeframe pada data yang sudah diganggu. Lalu pastikan (assert) bahwa segala sesuatu yang secara ketat berada sebelum j identik bitwise dengan run yang tidak diganggu:

j = int(n * 0.6)                       # bar 51,840
cl2 = cl.copy(); cl2[j:] *= 1.05       # shove the future up 5%
op2 = op.copy(); op2[j:] *= 1.05

base = [precompute_tf_signals(cl,  ts, p, L) for (p, L) in tf_params]
pert = [precompute_tf_signals(cl2, ts, p, L) for (p, L) in tf_params]

for s0, s1 in zip(base, pert):
    assert eq_nan(s0.hma[:j],  s1.hma[:j])      # HMA identical, NaNs included
    assert eq_nan(s0.hma3[:j], s1.hma3[:j])
    assert np.array_equal(s0.cross[:j], s1.cross[:j])   # every cross event
    assert np.array_equal(s0.sep[:j],   s1.sep[:j])     # every separation

Bukan "mendekati." Bukan "dalam batas toleransi." np.array_equal, dengan NaN yang wajib cocok dengan NaN — setiap nilai HMA, setiap nilai HMA3, setiap flag cross, dan setiap separasi pada 51,840 bar masa lalu harus berupa float yang persis sama. Lalu asersi yang sama untuk trade: setiap trade yang exit-nya jatuh sebelum j harus tidak berubah field demi field. Jika dorongan 5% ke masa depan menggeser satu saja HMA masa lalu pada desimal kedua belas, berarti sebuah keputusan masa lalu telah merujuk pada masa depan, dan probe ini gagal.

Engine kami lolos tes ini — untuk ketiga timeframe, dan untuk simulasi trade dual maupun triple. Mengganggu 34,560 bar masa depan meninggalkan 51,840 bar masa lalu dan setiap trade yang closed di antaranya persis seperti semula. Ini bukan sekadar kesesuaian antar-implementasi; ini adalah demonstrasi langsung bahwa batas informasi dalam waktu benar-benar bertahan.

Tes yang tidak mungkin gagal tidak membuktikan apa pun

Ada cara untuk melolosi probe di atas yang sama sekali tidak membuktikan apa-apa: jika perturbasinya adalah no-op — jika mengalikan masa depan dengan 1.05 tidak mengubah apa pun di mana pun — maka "masa lalu tidak berubah" menjadi benar secara trivial dan sama sekali tidak informatif. Tanda centang hijau pada tes yang tidak mungkin gagal lebih buruk daripada tidak ada tes sama sekali, karena ia memproduksi kepercayaan diri yang palsu. Karena itu, probe ini membawa dua asersi tambahan yang memberinya taring.

Masa depan harus benar-benar berubah. Kami memastikan (assert) bahwa perturbasi tersebut memang mengubah cross di suatu tempat dalam [j, n):

assert not np.array_equal(s0.cross[j:], s1.cross[j:])   # probe has teeth

Sekarang hasilnya jadi bermakna: dorongan 5% yang sama, yang menulis ulang masa depan, meninggalkan masa lalu identik bit-for-bit. Perturbasinya nyata, ia merambat ke depan, dan berhenti total tepat di titik potong. Kebocoran satu arah — masa lalu membaca masa depan — akan merembes ke belakang melewati j; namun hal itu tidak terjadi.

Batasnya persis di bar saat ini — tidak lebih awal satu bar, tidak lebih lambat satu bar. Kegagalan yang lebih halus adalah sebuah engine yang kausal tapi basi (stale): ia mengabaikan running close dari bar saat ini dan bereaksi satu bar terlambat (tidak ada kebocoran, tapi ada lag yang tidak akan dimiliki oleh trading live), atau yang bereaksi satu bar lebih awal (kebocoran satu bar). Jadi kami mengganggu satu bar tunggal j (dengan 1.02) dan memastikan dua hal sekaligus: masa lalu [0, j) tidak tersentuh, dan hma[j] bereaksi seketika.

cl3 = cl.copy(); cl3[j] *= 1.02        # nudge exactly one bar
s3 = precompute_tf_signals(cl3, ts, p_ltf, L_ltf)
assert eq_nan(s0.hma[:j], s3.hma[:j])  # nothing before j moves
assert s0.hma[j] != s3.hma[j]          # bar j reacts on the same bar

Ini mematok batas tersebut secara presisi. Running close dari candle yang sedang terbentuk masuk ke indikator pada bar j dengan nol delay dan nol antisipasi (foresight): bar j melihat close-nya sendiri secara instan, dan tidak ada satu bar pun sebelumnya yang melihatnya sama sekali. Itulah tepian pisau (knife-edge) presisi tempat semantik closed-bar seharusnya berpijak, dan tes ini mengonfirmasi bahwa engine memang berpijak di sana.

Berikut adalah gate lengkapnya — seluruh 25 pemeriksaan yang berdiri di antara engine ini dan sebuah backtest yang palsu (fabricated):

Grup Apa yang dipastikan oleh setiap pemeriksaan Jumlah
Indikator & cross (×3 timeframe) cross event eksak vs RunningCandleBuffer; separasi pada saat cross; nilai HMA/HMA3 (rtol 1e-9) 9
Trade (dual + triple) jumlah trade; identik field demi field; total PnL hingga 1e-12; active-time dalam posisi 8
Probe shifted-future (dual + triple) sinyal masa lalu tidak berubah secara bitwise; probe punya taring (masa depan memang berubah); trade sebelum j tidak berubah; perturbasi satu-bar terlokalisasi 8
Total 25

Dua kelompok pertama menetapkan bahwa engine cepat adalah referensi live itu sendiri. Kelompok ketiga menetapkan bahwa referensinya kausal. Anda butuh ketiganya: engine cepat yang bocor, engine yang bocor tapi kausal, dan engine kausal yang lag adalah tiga kegagalan yang berbeda, dan gate ini menyingkirkan masing-masingnya.

Mengapa probe ini agnostik terhadap timeframe

Keelokan dari probe shifted-future adalah bahwa ia tidak tahu dan tidak peduli di mana sebuah kebocoran akan berada. Ia tidak pernah menyebut timeframe, resampling, atau batas candle. Ia hanya bertanya: apakah mengganggu masa depan menggeser masa lalu? Itulah yang menjadikannya alat yang tepat justru untuk kebocoran multi-TF, yang luput dari tes shift-the-fill.

Perhatikan langsung bug resample-seluruh-seri yang naif. Jika stream 1 jam dibangun dengan me-resample seluruh seri di awal, "close 1 jam" yang dibaca pada bar j-1000 (jauh di dalam jam yang closed setelah j) akan menjadi close akhir dari sebuah candle yang close akhirnya bergantung pada bar-bar di j dan sesudahnya. Kalikan masa depan dengan 1.05, dan close akhir itu berubah — sehingga indikator HTF pada j-1000 berubah, gate pada j-1000 berubah, dan sebuah keputusan masa lalu pun bergeser. Probe akan langsung menyala pada stream HTF, pada bar seribu langkah sebelum titik potong.

Stream HTF milik engine kami tidak bergeser, karena pada j-1000 candle yang sedang forming hanya menyumbang base_close[j-1000] — sebuah close masa lalu — dan close akhir candle itu sama sekali tidak pernah dirujuk sampai batasnya terlewati. Probe ini buta terhadap mekanismenya dan tetap menangkap bug tersebut, yang justru merupakan hal yang Anda inginkan dari sebuah bukti: ia membatasi perilaku (tidak ada keputusan masa lalu yang bergantung pada data masa depan), bukan mengaudit implementasi (apakah kita mengindeks resample dengan benar?). Perilaku adalah apa yang bertrading; implementasi adalah apa yang Anda harap cocok dengannya.

Backtest dan live berbagi satu kebenaran

Dua panel berbagi satu tulang punggung berlabel RunningCandleBuffer: sweep backtest di satu sisi, bot live di sisi lain, keduanya terhubung ke inti yang sama

Ada satu alasan lagi mengapa ini lebih penting daripada audit backtest pada umumnya. Referensi yang menjadi acuan pembuktian engine ini — RunningCandleBuffer — bukanlah test fixture yang ditulis agar backtest terlihat bagus. Ini adalah logika candle milik bot live itu sendiri, diangkat verbatim dari tick simulator yang berjalan di produksi. Aturan closed-bar yang divalidasi oleh probe adalah aturan yang benar-benar dieksekusi oleh bot live, bar demi bar.

Jadi gate parity ini menjalankan dua tugas sekaligus. Ia membuktikan bahwa engine cepat cocok dengan referensi, dan karena referensinya adalah inti live itu sendiri, ia sekaligus membuktikan bahwa engine cepat cocok dengan live. Artikel sebelumnya memperingatkan bahwa kebocoran adalah penjelasan paling bersih untuk selisih parity backtest-live — bot live adalah satu-satunya tempat di mana Anda secara mekanis tidak bisa mengintip, sehingga backtest yang mengintip akan menyimpang begitu ia dijalankan secara live. Di sini, selisih itu ditutup secara konstruksi: backtest dan bot berbagi satu candle buffer, satu aturan cross, satu definisi tentang "diketahui pada bar i." Angka yang dioptimalkan oleh proses pencarian adalah angka yang benar-benar bisa ditradingkan oleh bot.

Itulah inti dari membuktikan tidak adanya look-ahead, alih-alih hanya mengasumsikannya. Sebuah pencarian multi-TF menjalankan ribuan konfigurasi; jika engine-nya bocor, pencarian itu akan menemukan konfigurasi yang mengeksploitasi kebocoran itu paling agresif dan menyerahkan kepada Anda seorang pemenang yang palsu (fabricated) — mode kegagalan yang diukur oleh taksonomi pada Sharpe 15 dari murni noise. Probe inilah yang memungkinkan Anda memercayai bahwa pemenang itu nyata, sebelum Anda menyambungkannya ke modal.

Apa yang dibuktikan probe ini, dan apa yang tidak

Kerigoran soal tes ini berlaku dua arah, jadi bersikaplah presisi soal cakupannya. Probe shifted-future membuktikan satu properti tunggal yang spesifik: pada data ini, tidak ada keputusan sinyal atau trade pada atau sebelum bar j yang bergantung pada bar mana pun setelah j — batas informasi dalam waktu bertahan sepanjang jalur indikator, cross, dan trade. Itulah persis kebocoran multi-TF yang ingin kami hilangkan, dan itu adalah properti yang tidak bisa ditetapkan oleh code review.

Ia tidak membuktikan bahwa strategi ini punya edge. Sebuah engine yang sepenuhnya kausal tetap bisa kehilangan uang secara jujur, dan probe ini diam soal itu — sebagaimana seharusnya; membuktikan tidak adanya kebocoran dan menemukan edge yang nyata adalah dua pekerjaan yang terpisah, dan mencampuradukkan keduanya adalah cara bagaimana backtest yang bocor akhirnya di-deploy. Probe ini tidak mencakup bias non-temporal: survivorship pada instrumen, selection bias dari menjalankan probe hanya setelah engine-nya terlihat bagus, atau model fee yang terlalu murah hati. Dan probe ini, dengan sendirinya, tidak membuktikan bahwa fill live cocok dengan fill backtest — slippage dan latency adalah kesenjangan nyata yang tidak bisa dilihat oleh probe, karena ia beroperasi pada jalur keputusan, bukan pada venue eksekusi. Yang menutup kesenjangan itu adalah fakta terpisah bahwa inti candle milik engine ini bit-identical dengan milik bot live.

Satu caveat jujur soal desain probe ini sendiri: ia memotong pada satu j tunggal (60% dari seri data). Properti yang diverifikasinya seragam terhadap j — tidak ada yang istimewa dari bar 51,840 — sehingga satu titik potong adalah tes yang adil untuk sebuah properti struktural, tapi versi yang lebih paranoid akan menyapu j di sepanjang seri data. Kami menganggap satu titik potong yang dipilih dengan baik, ditambah pemeriksaan lokalisasi satu-bar, sudah cukup, karena sebuah kebocoran yang bersembunyi dari perturbasi masa depan sepanjang 34,560 bar tetapi muncul pada titik potong lain haruslah bug yang sangat aneh. Intinya adalah mengetahui batas dari bukti Anda, bukan berpura-pura bahwa satu tes adalah kuantifier universal.

Poin-Poin Utama

  1. Strategi multi-timeframe bocor lewat bar yang sedang forming, bukan lewat fill. Sebuah keputusan di dalam periode HTF yang belum selesai hanya boleh menggunakan running close dari candle itu (base close paling akhir), tidak pernah close akhirnya. Backtest resample-seluruh-seri memberikan setiap keputusan intra-periode akses hingga satu periode HTF penuh dari masa depan.
  2. Tes pergeseran satu bar tidak menjangkau kebocoran ini. Ia menangkap look-ahead eksekusi; kebocoran multi-TF ada pada bagaimana indikator itu dibangun. Anda butuh probe yang berbeda.
  3. Reproduksi aturan live secara persis, lalu buktikan. Kami membangun ulang semantik closed-bar RunningCandleBuffer milik bot menjadi sebuah engine yang divektorisasi dan menempatkannya di balik gate 25 pemeriksaan: cross eksak terhadap referensi (408 / 2,792 / 3,691), trade identik field demi field (466 dual, 211 triple), PnL hingga 1e-12.
  4. Parity itu perlu, tapi tidak cukup. Cocok dengan referensi membuktikan bahwa Anda sama, bukan bahwa Anda kausal. Referensi yang bocor, jika direproduksi dengan setia, tetap saja bocor.
  5. Probe shifted-future adalah bukti sesungguhnya. Ganggu setiap bar pada atau setelah j; pastikan setiap sinyal dan trade sebelum j tidak berubah secara bitwise. Jika masa depan bisa menggeser masa lalu, Anda punya look-ahead.
  6. Beri taring pada probe. Pastikan masa depan memang berubah (perturbasinya bukan no-op) dan bahwa sentilan satu-bar bereaksi pada bar yang sama (tidak ada kebocoran, tidak ada lag). Tes yang tidak mungkin gagal tidak membuktikan apa pun.
  7. Ketika backtest dan live berbagi satu inti, buktinya ikut berpindah. Karena engine ini bit-identical dengan logika candle milik bot live, tidak-adanya-look-ahead menjadi properti dari sistem live juga — dan celah parity backtest-live tertutup secara konstruksi.

Artikel sebelumnya menunjukkan bagaimana kebocoran satu baris kode bisa memalsukan Sharpe sebesar 15. Artikel ini menunjukkan disiplin yang sebaliknya: bukan "bagaimana kebocoran mengecoh Anda," melainkan bagaimana membuktikan, secara mekanis dan pada data nyata, bahwa sebuah engine tertentu tidak bocor. Ganggu masa depan. Jika masa lalu tidak bergeming, berarti Anda sedang bertrading di masa kini.

Penafian: Informasi yang disediakan dalam artikel ini hanya untuk tujuan edukasi dan informasi serta tidak merupakan nasihat keuangan, investasi, atau trading. Trading mata uang kripto mengandung risiko kerugian yang signifikan.

Penulis

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Selangkah Lebih Maju dari Pasar

Berlangganan newsletter kami untuk wawasan AI trading eksklusif, analisis pasar, dan pembaruan platform.

Kami menghormati privasi Anda. Berhenti berlangganan kapan saja.