Model Saya Sedang Membaca Koran Hari Esok

Untuk sementara waktu, salah satu model saya tampak seperti seorang jenius. Backtest-nya bersih, percaya diri, hampir terlalu mulus. Saya ingin percaya bahwa saya akhirnya berhasil membangun sesuatu yang benar-benar bekerja.

Model itu tidak mempelajari apa pun yang layak dipertahankan. Ia belajar untuk curang — secara diam-diam, melalui sebuah kebocoran yang saya bangun sendiri, yang membuatnya bisa membaca informasi dari masa depan sebelum memutuskan apa yang harus dilakukan di masa lalu.

Murid yang mencurigakan karena terlalu baik

Sebuah model yang berkinerja sedikit lebih baik daripada yang Anda perkirakan itu menggembirakan. Sebuah model yang berkinerja jauh lebih baik daripada yang sepatutnya seharusnya membuat Anda takut. Model saya termasuk jenis yang kedua: mulus di tempat yang seharusnya pasar nyata bergerigi, percaya diri di tempat yang seharusnya ia ragu.

Pada tulisan sebelumnya saya menulis bahwa hasil yang paling berbahaya adalah hasil yang menyanjung Anda. Ini adalah pelajaran yang sama lagi, dalam balutan kostum yang lebih halus. Angka-angkanya kali ini tidak absurd — angka-angkanya hanya saja terlalu bagus, dan “terlalu bagus” adalah jenis tanda bahaya tersendiri, karena edge yang sejati di pasar itu kecil, penuh derau, dan sulit diperoleh. Sebuah model yang tampak seolah telah memecahkan pasar hampir selalu sebenarnya menemukan sebuah celah di dalam simulasi Anda.

Tempat masa depan menyusup masuk

Celah itu ada di normalisasi — langkah tak menarik di mana Anda menyekala ulang input mentah agar sebuah model bisa belajar darinya. Untuk menormalisasi, Anda butuh statistik: sebuah pusat, sebuah sebaran. Dan saya menghitung statistik itu atas seluruh dataset sekaligus.

Satu pilihan itulah tempat masa depan bocor masuk. Ketika Anda menghitung statistik melintasi seluruh sejarah lalu menerapkannya pada satu titik di masa lalu, setiap nilai yang “ternormalisasi” pada momen masa lalu itu secara diam-diam mengandung sandi informasi tentang data yang belum terjadi. Model itu, ketika menatap suatu saat tertentu, secara implisit diberi tahu sesuatu tentang bentuk keseluruhan garis waktu — termasuk masa depannya. Ia tidak pernah melihat harga hari esok secara langsung. Ia memang tidak perlu. Ia sedang menghirup udara yang sudah tercampur hari esok di dalamnya.

Mengapa look-ahead begitu mudah dibangun

Yang membuat look-ahead bias berbahaya bukanlah karena ia sulit diciptakan. Melainkan karena ia menyelinap masuk melalui prapemrosesan yang sebenarnya biasa-biasa saja. Cara yang paling alami dan paling mudah dibaca untuk menulis kodenya — fit scaler Anda pada data, lalu transform data tersebut — sudah salah untuk deret waktu, dan tidak ada yang protes. Tidak ada exception yang dilempar. Tidak ada test yang berubah merah. Angka-angkanya tampak sepenuhnya masuk akal. Satu-satunya gejala adalah bahwa model Anda lebih baik daripada yang seharusnya, yang merupakan satu-satunya gejala yang paling tidak Anda termotivasi untuk menyelidikinya.

Itulah jebakan kebocoran: ia tidak tampak seperti sebuah bug. Ia tampak seperti keberhasilan.

Petandanya: cemerlang di dalam sini, mati di luar sana

Ada satu petanda yang menjelaskan, meski ia bukan bukti dengan sendirinya. Sebuah model yang terkorupsi oleh look-ahead cenderung cemerlang pada data yang dipakai untuk menalanya dan ambruk seketika saat ia bertemu data yang tidak bisa diintipnya. Jurang yang lebar antara kejayaan in-sample dan keruntuhan out-of-sample adalah pertanda peringatan adanya kebocoran — meski hal itu juga bisa berasal dari overfitting, pergeseran rezim, atau biaya yang Anda remehkan. Dalam kasus saya, kebocoran itulah pelakunya.

Jadi pengujian yang penting tidak pernah berupa “seberapa bagus backtest-nya?” Melainkan “seberapa lebih buruk jadinya ketika saya membuat simulasinya jujur?” Ketika saya menutup kebocoran itu, kinerjanya turun — dan penurunan itu, sesakit apa pun, adalah angka pertama yang layak dipercaya yang pernah ditunjukkan sistem itu kepada saya.

Perbaikannya: jangan pernah gunakan apa pun yang belum bisa Anda ketahui

Perbaikannya adalah sebuah prinsip, bukan sebaris kode: pada momen mana pun dalam simulasi, model hanya boleh menggunakan informasi yang sudah ada pada atau sebelum momen tersebut. Untuk normalisasi, itu berarti menurunkan statistik dari jendela masa lalu yang menjuntai ke belakang — fit pada apa yang tersedia hingga suatu waktu tertentu, tidak pernah pada keseluruhan garis waktu, dan tidak pernah pada satu titik masa depan.

Itu lebih lambat. Itu lebih merepotkan. Itu menghasilkan backtest yang tampak lebih buruk. Ketiga hal itu adalah fitur, bukan biaya, karena masing-masing adalah simulasi yang menolak membiarkan model menjelajah waktu. Sebuah pipeline yang jujur membayar pajak dalam bentuk kinerja yang tampak menurun, dan pajak itu adalah harga dari angka-angka yang benar-benar bisa Anda percaya.

Kebocoran adalah sebuah keluarga, bukan satu bug tunggal

Bagian tersulit dalam memperbaiki ini bukanlah si normalizer. Melainkan menyadari bahwa normalizer itu hanyalah satu pintu, dan masa depan bisa berjalan masuk melalui banyak pintu. Kesalahan yang sama bersembunyi di dalam cara label dibangun, di dalam fitur yang diam-diam meringkas keseluruhan deret, di dalam langkah prapemrosesan mana pun yang menyentuh masa lalu dan masa depan secara bersamaan. Tutup satu kebocoran dan Anda belum menutup kebocoran itu sendiri; Anda baru saja mempelajari seperti apa rupanya, sehingga Anda bisa pergi mencari yang lainnya.

Saya berhenti memandang look-ahead sebagai bug yang harus diperbaiki dan mulai memperlakukannya sebagai properti yang harus diaudit — sesuatu yang kini saya buru dengan sengaja, dengan asumsi bahwa jika sebuah hasil tampak terlalu bersih, maka masa depan sedang menyusup masuk di suatu tempat yang belum saya periksa.

Mengapa backtest memberi ganjaran pada kecurangan

Di balik semua ini ada sebuah kebenaran yang tidak nyaman tentang backtest: backtest tidak bisa membedakan keterampilan dari kebijaksanaan setelah peristiwa terjadi. Sebuah backtest membayar persis sama untuk model yang benar-benar mengantisipasi pasar maupun model yang diam-diam membaca kunci jawaban. Keduanya menghasilkan kurva yang indah. Data yang ketat ke depan dan belum pernah dilihat adalah pengujian yang lebih kuat yang mulai memisahkan keduanya — meski bahkan itu pun, dengan sendirinya, tidak bisa membuktikan bahwa sebuah pipeline bebas dari kebocoran.

Itulah sebabnya “ia berkinerja baik dalam backtest” adalah awal dari sebuah penyelidikan, bukan akhirnya. Sebuah equity curve yang tampak bagus adalah sebuah pertanyaan: apakah model ini benar-benar mendapatkan ini, atau apakah saya tanpa sengaja membiarkannya curang? Keseluruhan disiplin riset yang jujur hanyalah belajar mengajukan pertanyaan itu sebelum pasar mengajukannya untuk Anda.

Sebuah model yang membaca koran hari esok akan selalu tampak seperti seorang jenius. Kejeniusan itu milik Anda, bukan miliknya — dan ia sepenuhnya bersemayam di dalam kebocoran yang lupa Anda tutup.

— Tidak ada sinyal, tidak ada imbal hasil, bukan nasihat investasi.