Enam minggu setelah menjalankan backtest, kami menjalankannya lagi dengan kode, konfigurasi, dan rentang tanggal yang sama. Ekuitas akhirnya berbeda. Perbedaannya tidak besar, tetapi cukup untuk menggeser rasio Sharpe dari 1.31 menjadi 1.08 dan mengubah set parameter yang tampak paling baik.
Strateginya tidak berubah. Data historisnya yang berubah. Sebuah vendor merevisi beberapa hari data perdagangan dan bar, sementara proses pertama kami tidak menyimpan snapshot input yang persis sama. Beberapa hari berikutnya kami habiskan untuk mencari tahu apakah hasil baru itu berasal dari koreksi data, bug pipeline, atau proses riset yang tidak dapat kami reproduksi.
Senin: hasil pengulangan tidak cocok
Petunjuk pertama adalah selisih kecil pada pasangan spot yang likuid. Bar awal kami menunjukkan harga tertinggi 62,418. Hasil pengulangan menunjukkan 62,431. Selisih itu tampak sepele sampai kami menelusuri pemicu entry strategi: ambangnya berada di antara kedua harga tersebut. Perubahan pada beberapa bar menghasilkan sinyal yang berbeda, lalu posisi yang berbeda beberapa hari kemudian.
Kami membandingkan jumlah perdagangan harian dan menemukan bahwa ketidakcocokan dimulai pada satu tanggal. Lalu kami membandingkan baris mentah yang menjadi input untuk tanggal tersebut. Respons vendor saat ini berisi beberapa catatan perdagangan yang telah dikoreksi dan bar agregat yang telah direvisi. Tidak ada log perubahan yang menyertai respons yang kami gunakan sebelumnya.
Perilaku data seperti ini lazim terjadi. Bursa memperbaiki catatan; vendor mengisi celah data, mengoreksi transaksi dengan harga keliru, dan menyesuaikan logika agregasi. Permintaan untuk “data Januari” bisa menghasilkan jawaban berbeda pada Maret.
Selasa: kami menemukan bukti yang hilang
Kami menyimpan nama vendor, simbol, dan rentang tanggal dalam catatan run. Namun, kami tidak menyimpan respons itu sendiri atau hash konten untuk setiap partisi input. Artinya, kami bisa membuktikan apa yang kami minta, tetapi tidak bisa membuktikan byte apa yang dikembalikan vendor.
Kami mengambil berkas cache tertua yang bisa ditemukan, lalu membandingkannya dengan respons saat ini. Perbedaannya bukan karena penyesuaian split atau konversi zona waktu. Sejumlah kecil catatan perdagangan yang dikoreksi mengalir melalui pembuat bar kami. Ini menyingkirkan satu dugaan, tetapi cache tersebut tidak lengkap, jadi kami tetap tidak bisa merekonstruksi setiap bar yang terdampak secara persis.
| Yang kami catat | Pertanyaan yang bisa kami jawab | Yang belum tercatat |
|---|---|---|
| Vendor, simbol, rentang tanggal | Permintaan yang dibuat oleh run | Isi respons |
| Versi pipeline | Kode yang digunakan untuk membuat bar | Baris input yang persis |
| Waktu run | Kapan pekerjaan dijalankan | Revisi vendor yang tersedia saat itu |
Kami menganggap “kueri yang sama” berarti “data yang sama”. Asumsi itu membuat eksperimen tampak lebih mudah direproduksi daripada kenyataannya.
Rabu: koreksi mengubah ceritanya
Kami memutar ulang tanggal yang terdampak menggunakan kedua versi data yang tersedia. Bar yang direvisi menghapus dua entry dan menggeser satu exit sebanyak satu bar. Ketiga perdagangan itu menjelaskan sebagian besar perubahan Sharpe. Strategi ini peka terhadap beberapa observasi di sekitar pemicunya, meskipun keseluruhan rangkaian harga tampak hampir identik pada grafik.
Itu bukan berarti hasil awalnya curang atau hasil revisi otomatis lebih baik. Artinya, klaim tersebut perlu diberi batasan: run lama menggambarkan snapshot data yang kami miliki saat itu, sedangkan run ulang menggambarkan riwayat vendor saat ini. Kami tidak bisa dengan jujur menyebut yang satu sebagai reproduksi dari yang lain.
Kamis: kami mengubah catatan riset
Kami menambahkan snapshot input yang tidak dapat diubah ke run riset baru. Setiap partisi mendapat hash konten, dan manifes run mencatat sumber, waktu pengambilan, versi skema, kebijakan penyesuaian, serta versi kode transformasi. Untuk kumpulan data besar, kami menyimpan berkas aktual dalam penyimpanan berbasis alamat konten, sehingga partisi identik bisa digunakan bersama tanpa menimpa versi sebelumnya.
Jika aturan penyimpanan atau lisensi melarang kami menyimpan data mentah, kami mempertahankan bukti terkuat yang diizinkan oleh ketentuannya: hash, parameter permintaan, pengenal revisi sumber, dan sampel kecil baris yang telah diurai untuk diagnosis. Hash tidak dapat memulihkan data, tetapi bisa memberi tahu kami ketika dua run tidak menggunakan byte yang identik.
Backtest yang dapat direproduksi memerlukan identitas input, bukan sekadar URL data. Catat apa yang diterima, kapan data itu diterima, dan bagaimana data tersebut diolah menjadi baris yang digunakan strategi Anda.
Jumat: hal yang akan kami hindari lain kali
Kami akan menghindari perbandingan yang hanya melihat metrik utama. Sharpe yang berubah memberi tahu bahwa ada sesuatu yang bergeser; angka itu tidak menunjukkan penyebabnya. Mulailah dengan jumlah baris dan hash partisi, lalu cari tanggal input pertama yang berbeda. Setelah itu, bandingkan baris hasil transformasi, sinyal, order, lalu fill secara berurutan.
Kami juga akan menghindari penggantian diam-diam data lama dengan riwayat yang sudah dikoreksi. Koreksi memang berguna, tetapi run lama harus tetap terikat pada input aslinya. Kumpulan data baru harus menghasilkan run baru, dengan hubungan yang terlihat ke run sebelumnya.
Aturan praktis kami sekarang sederhana: jika run ulang dimaksudkan untuk mereproduksi eksperimen, kunci snapshot datanya. Jika tujuannya memanfaatkan riwayat yang sudah dikoreksi, beri label sebagai eksperimen baru. Kami perlu beberapa hari untuk memahami perbedaannya, dan kini perbedaan itu tercatat dalam satu baris di manifes run.
← Semua artikel


