Ana içeriğe geç
mühendislikgeriye dönük testalgoritmalar
Geriye Dönük Testler Neden Yalan Söyler: Göstergeye Dayalı Çıkışları Simüle Etmek Göründüğünden Zordur

Geriye Dönük Testler Neden Yalan Söyler: Göstergeye Dayalı Çıkışları Simüle Etmek Göründüğünden Zordur

Bir geriye dönük test bir sayı üretir ve sayılar ikna edicidir. Sorunun tamamı budur. Bir simülasyon ince bir biçimde yanlış olduğunda çökmez ve sizi uyarmaz. Size gerçeğinden biraz daha iyi bir sonuç verir ve buna göre hareket etmenize izin verir.

Sabit bir kâr alma seviyesini simüle etmek kolaydır: fiyat ya seviyenize ulaşmıştır ya da ulaşmamıştır. İşin zorlaştığı yer, göstergeye dayalı bir girişi veya çıkışı simüle etmektir çünkü artık simülasyonun çok daha çetrefilli bir soruyu yanıtlaması gerekir: bot o anda gerçekte neyi biliyordu?

Bu soruyu yanlış yanıtladığımız üç durum ve her birinin bize öğrettikleri.

Önemli Noktalar

  • Klasik hata, yüksek zaman dilimli bir göstergeyi mumu kapanmadan okumaktır. Mum açılışında bakılan 4 saatlik bir RSI, henüz gerçekleşmemiş dört saatlik fiyat hareketini içinde taşır.
  • Çözüm sezgisel kurallar değil, aritmetiktir: T zaman diliminden gelen bir değer ancak T süresi dolduktan sonra meşru biçimde kullanılabilir, bu yüzden aramayı (tetikleyici zaman dilimi eksi simülasyon zaman dilimi) kadar geriye kaydırın.
  • Canlı motorlar aynı sorunu farklı bir kılıkta yaşar. Hâlâ oluşmakta olan bir mum üzerinde hesaplanan bir gösterge yeniden çizer, yani bir koşul aynı mum içinde önce doğru sonra yanlış olabilir.
  • Tekrar oynatamadığını sessizce atlayan bir simülatör, canlı bottan daha müsamahalıdır ve bu fark her zaman geriye dönük testin lehinedir.
  • Dürüst olan, bu farkı gizlemek yerine sonuçta görünür kılmaktır. Nerede yalan söylediğini size söyleyen bir geriye dönük test, temiz görünen bir tanesinden daha değerlidir.

1. Çok zaman dilimli stratejilerde gizlenen look-ahead hatası

Yazması en kolay, fark edilmesi en zor olan hata budur.

Diyelim ki simülasyon her adımda 5 dakikalık bir mum ilerliyor ve stratejide 4 saatlik bir RSI üzerine bir koşul var. Her 5 dakikalık adımda motorun o RSI için bir değere ihtiyacı var. Naif uygulama, mevcut anı içeren 4 saatlik mumu bulur ve değerini okur.

Bu bir zaman makinesidir.

12:00'de açılan 4 saatlik bir mumun, 16:00'da kapanana kadar nihai bir RSI değeri yoktur. Simülasyon 12:05'te durup o mumu okuyorsa, 12:05'in bakış açısından henüz gerçekleşmemiş dört saatlik fiyat hareketinden hesaplanmış bir sayıyı okuyor demektir. Strateji, önceden göremeyeceği hareketlerin hemen öncesinde giriş yapıyormuş gibi görünür ve sermaye eğrisi mükemmel görünür.

Çözüm bir sezgisel kural değildir. Bir değerin gerçekte ne zaman bilinebilir olduğunu yazıya dökünce kendiliğinden ortaya çıkar. triggerMs zaman diliminde t anında açılan bir mum ancak t + triggerMs anında bilinebilir. Simülasyon kararını mevcut mumun kapanışında, yani candleOpenTime + simulationMs anında verir. Dolayısıyla değer tam olarak şu durumda meşru biçimde kullanılabilir:

t + triggerMs <= candleOpenTime + simulationMs
t             <= candleOpenTime - (triggerMs - simulationMs)

Bu da demektir ki doğru arama, halihazırda sahip olduğunuz aramanın triggerMs - simulationMs kadar geriye kaydırılmış bir zaman damgasına karşı çalıştırılmış hâlidir.

Bu ifadenin işaret etmeye değer iki özelliği vardır çünkü onu her yerde güvenle uygulanabilir kılan şey bunlardır:

  • Aynı zaman dilimindeki koşullar sıfır kaydırma alır. 5 dakikalık bir mumun kapanışında okunan 5 dakikalık bir gösterge, kendi kapanışa dayalı değeridir ve bu her zaman doğruydu. Yalnızca gerçekten daha yüksek zaman dilimleri kayar.
  • Kaydırma yapılandırılmaz, türetilir. Birinin ayarlamayı hatırlaması gereken bir ayardan değil, mumun kendisinden gelir.

Genel ders, yalnızca ticarete değil her simülasyona uygulayacağım bir derstir: look-ahead yanlılığınız olup olmadığından emin değilseniz, her girdinin bilinebilir hâle geldiği zaman damgasını yazın ve onu kullandığınız zaman damgasıyla karşılaştırın. Yanlılık, bu adımı atlayıp bunun yerine sezgiye güvendiğinizde geriye kalan şeydir ve sezgi bu konuda kötüdür çünkü “mevcut 4 saatlik mum” demek kulağa tamamen makul gelir.

2. Aynı hata canlı kodda farklı bir kılığa bürünür

Bu sorunun geriye dönük teste ait olduğunu düşünürsünüz. Değil. Canlı motor da bununla karşılaşır ve orada buna yeniden çizim (repainting) denir.

Herhangi bir anda en son mum hâlâ oluşmaktadır. O mumda biten bir pencere üzerinde hesaplanan bir gösterge, mum değiştikçe değişir. Bir koşul, piyasa olağandışı hiçbir şey yapmadan, aynı mum içinde 12:01'de doğru, 12:03'te yanlış ve 12:04'te yine doğru olabilir. Bot bunların ilkine göre hareket ederse, artık var olmayan bir değere göre hareket etmiştir.

Bizim varsayılanımız, hâlâ oluşmakta olan mumu atmak ve yalnızca doğrulanmış mumlar üzerinde hesaplamaktır, böylece bir sinyal asla yeniden çizmez. Bu aynı zamanda canlı ile geriye dönük testi uyumlu kılan şeydir çünkü simülasyon yapısı gereği kapanmış mumlarla çalışır.

Ama “asla yeniden çizme” bedelsiz değildir. Doğrulamayı beklemek daha geç hareket etmek demektir ve bazı stratejiler için bu gecikme, önlediği gürültüden daha pahalıya patlar. Bu yüzden bu, koşul bazında bir seçim olarak sunulur: doğrulanmış mum üzerinde değerlendirin veya canlı değerlendirin ve değerin ayağınızın altından kayabileceğini kabul edin.

Aklımda tutacağım tasarım noktası şu: bir ödünleşim gerçekten bir ödünleşimse, onu bir varsayılanın içine gömüp bir gerçekmiş gibi davranmayın. Güvenli seçeneği varsayılan yapın, sonra kullanıcının diğerini bilerek seçmesine izin verin. Yapmamanız gereken şey, iki modun canlı ile geriye dönük test arasında farklılaşmasına izin vermektir çünkü o zaman kullanıcı iki farklı botu karşılaştırıyor demektir.

3. Simülatörün tekrar oynatamadığı şey, yanlış yaptığı şeyden daha tehlikelidir

İlk ikisi doğruluk hatalarıdır. Bu ise bir tasarım sorunudur ve bence en az takdir edilenidir.

Bazı koşullar tekrar oynatılamaz. Harici bir webhook sinyali bunun en net örneğidir: geçen mart ayında bir gün 14:20'de dış bir sistemin ne göndereceğine dair tarihsel bir kayıt yoktur. Simülasyon bunu bilemez, o kadar.

Peki ne yapmalı? Cazip yanıt, o koşulu atlayıp geri kalanını değerlendirmektir. Sonucu eksiksiz gösteren şey budur. Ayrıca hasarın oluştuğu yer de burasıdır ve mekanizmayı açıkça belirtmekte fayda var:

Bir strateji A VE B gerektiriyorsa ve B simüle edilemiyorsa, B'yi atlamak kararı tek başına A'ya bırakır. Simüle edilen bot, canlı botun uzak duracağı koşullarda giriş yapar. Simülasyon, gerçek olandan sistematik olarak daha müsamahalıdır.

Yönüne dikkat edin. Hata rastgele değildir. VE mantığı altında bir koşulu düşürmek yalnızca gerçekten daha fazla işlem üretebilir, asla daha az değil, dolayısıyla yanlılık her seferinde daha iyi görünen bir sonuca işaret eder. Onu tehlikeli kılan şey bu asimetridir: rastgele gürültü birçok çalıştırmada ortalamada birbirini götürür, tek yönlü yanlılık ise götürmez.

Reddetmek yerine simülasyonu çalıştırmaya devam etmeyi seçtik çünkü bilinen bir eksiği olan kısmi bir sonuç yine de kullanışlıdır. Ama o zaman bu eksiğin, bir günlüğe gömülmek yerine çıktıda görünür olması gerekir. Sonuç, kullanıcının kendi dilinde, ne olduğunu söyleyen bir adla açık bir uyarı taşır: “Bazı Giriş Koşulları Simüle Edilmedi.” Çıkışlar için de buna karşılık gelen bir uyarı vardır.

Bu karar, bu sistemin en hararetle savunacağım kısmıdır. Yüksek sesle başarısız olamayan bir simülatör sessizce başarısız olur ve sayı üreten bir şeyde sessiz başarısızlık, olabilecek en kötü başarısızlık biçimidir, çünkü insanlar sayılara göre hareket eder, yığın izlerine (stack trace) göre değil.

Buradan çıkarılacak ders

Bir sayı üretmek için geçmişi tekrar oynatan herhangi bir şey inşa ediyorsanız:

  • Her girdinin bir “şu anda bilinebilir” zaman damgasına ihtiyacı vardır. Bunu belirtemiyorsanız, look-ahead yanlılığını dışlayamazsınız.
  • Hatalarınızın yalnızca büyüklüğünü değil, yönünü de kontrol edin. Her zaman aynı yöne işaret eden bir hata birikir; rastgele yöne işaret eden bir hata ortalamada birbirini götürür. Tek yönlü iyimserlik, look-ahead yanlılığının imzasıdır.
  • Yapabildiğiniz her yerde simülatör ile canlı sistemin karar mantığını paylaşmasını sağlayın. “Bu koşul doğru mu” sorusunun iki ayrı uygulaması birbirinden sapar ve bunu bir testten değil, bir kullanıcıdan öğrenirsiniz.
  • Yapamadığınız şeyi raporlayın. Bir sonucun güvenilirliği, temizliğinden değil, belirtilen sınırlarından gelir.

Rahatsız edici özet şu: bir geriye dönük test, karşı-olgusal bir durum hakkında bir iddiadır ve karşı-olgusal durumları sessizce yanlış yapmak kolaydır. Mühendislik hedefi asla yalan söylemeyen bir simülatör değildir. Nerede yalan söylediğini size söyleyen bir simülatördür.


İlgili Okumalar

Hesap oluştur

Öğrendiğini uygulamaya geç: hemen bir Freya hesabı aç.

Hemen Kaydol
Bu içerik yalnızca eğitim amaçlıdır ve yatırım tavsiyesi niteliği taşımaz. Kripto para ticareti önemli riskler içerir. Asla kaybetmeyi göze alabileceğinizden fazlasını yatırmayın.