Kami menjalankan ulang backtest tersimpan dan mendapatkan kurva ekuitas yang berbeda. Kode strategi sama, rentang tanggal sama, simbol sama. Saldo akhir meleset 1.8%, dan tiga transaksi bergeser satu bar.
Itu cukup untuk membuat hasil riset sulit dipercaya. Jika rekan satu tim, versi Anda di masa depan, atau layanan paper trading tidak bisa mereproduksi prosesnya, Anda tak bisa tahu apakah perubahan memperbaiki strategi atau sekadar mengubah eksperimennya. Berikut urutan langkah yang kami ikuti untuk menemukan pergeseran tersebut.
Hari 1: Kami mencatat arti “proses yang sama”
Kesalahan pertama kami adalah menganggap berkas strategi sebagai keseluruhan eksperimen. Padahal bukan. Prosesnya juga bergantung pada data masukan, versi engine, kalender, metadata instrumen, dan pengaturan eksekusi. Kode hanya menjelaskan satu bagian dari perhitungan.
Sebelum mengubah apa pun, kami membuat manifes proses. Isinya mencatat commit strategi, ID snapshot data, rentang tanggal, venue, jadwal biaya, sumber funding, model pengisian order, dan versi perangkat lunak. Kami juga menyimpan order dan pengisian yang dihasilkan, karena kurva ekuitas saja tidak menunjukkan titik awal perbedaan kedua proses.
| Artefak | Yang perlu dicatat | Alasannya |
|---|---|---|
| Data pasar | ID snapshot, versi skema, penyesuaian | Vendor memperbaiki riwayat dan merevisi aksi korporasi |
| Eksekusi | Tingkat biaya, seri funding, pengaturan pengisian dan dampak | Default dan asumsi akun mengubah hasil |
| Runtime | Commit kode, versi engine dan dependensi | Pustaka dapat mengubah urutan, pembulatan, atau indikator |
| Keluaran | Order, pengisian, posisi, dan metrik | Menunjukkan titik awal perbedaan kedua proses |
Hari 2: Kami membandingkan transaksi, bukan Sharpe
Metrik ringkasan justru mengalihkan perhatian. Sharpe kedua proses nyaris sama, tetapi log pengisian menunjukkan ketidakcocokan pertama pada penyelesaian funding. Satu proses membebankan suku bunga pada posisi yang terbuka tepat pada timestamp penyelesaian; proses lainnya menggunakan posisi setelah penyeimbangan ulang pada timestamp tersebut.
Kode strategi tidak berubah. Urutan peristiwa di engine-lah yang berubah. Pembaruan versi kecil membuat urutannya eksplisit, padahal sebelumnya bergantung pada bagaimana dua peristiwa kebetulan diurutkan.
Kami memperjelas kontrak proses dengan menetapkan urutannya: terapkan funding pada posisi yang dibawa ke waktu penyelesaian, lalu proses keputusan strategi untuk timestamp tersebut. Konvensi persisnya bisa berbeda menurut venue dan engine. Membiarkannya tidak dinyatakan adalah sumber bug-nya.
Hari 3: Berkas data yang “sama” ternyata berbeda
Setelah urutan peristiwa ditetapkan, ketidakcocokan yang tersisa terkonsentrasi pada beberapa transaksi ekuitas. Vendor telah mengoreksi penyesuaian pemecahan saham historis. Nama dan jumlah baris berkas kami sama seperti sebelumnya, sehingga tampak seolah-olah tidak berubah.
Kini kami membuat sidik jari untuk setiap snapshot data yang tidak berubah dan menyimpan kebijakan penyesuaiannya bersamanya. Hash memberi tahu kami apakah byte-nya berubah; hash tidak menjelaskan alasannya. Karena itu, manifes juga mencatat sumber, waktu pengambilan, dan versi transformasi. Untuk data yang direvisi, detail tersebut merupakan bagian dari hasil.
Backtest yang dapat direproduksi perlu menjawab pertanyaan, “versi masa lalu yang mana yang dilihatnya?”
Hari 4: Kami menemukan satu default yang luput
Perbedaan terakhir adalah biaya maker yang bernilai nol karena konfigurasi strategi tidak menyertakan kolom tersebut. Engine versi lebih baru menerapkan biaya default akun. Default tunggal itu mengubah transaksi marginal hingga menjelaskan sebagian besar selisih saldo akhir.
Kami menetapkan secara eksplisit pengaturan yang berdampak secara ekonomi dan meminta engine mencetak konfigurasi yang telah ditetapkan ke dalam catatan proses. Default memang memudahkan eksplorasi. Namun, default bukan bukti yang kuat saat membandingkan hasil dari waktu ke waktu.
Yang akan kami hindari lain kali
Kami menghabiskan setengah hari membandingkan metrik agregat sebelum melihat pengisian pertama yang berbeda. Jangan mulai dari sana. Urutkan kedua log peristiwa berdasarkan timestamp dan bandingkan titik pertama yang berbeda; perbedaan berikutnya sering kali merupakan akibat dari satu penyebab itu.
Kami juga tak akan lagi menganggap image container cukup untuk membuat proses dapat direproduksi. Image itu mengunci sebagian besar lingkungan perangkat lunak, tetapi tidak mencakup berkas data eksternal, jadwal biaya yang diambil saat runtime, atau riwayat vendor yang telah direvisi.
Saat backtest berubah, simpan manifes dan log kedua proses, lalu perbaiki satu sumber pergeseran setiap kalinya. Hasil yang berguna bukan sekadar kurva yang bisa dijalankan ulang. Hasil itu juga berupa catatan yang menjelaskan data dan asumsi apa yang menghasilkannya, serta alasan proses berikutnya mungkin berbeda.
← Semua artikel


