← Makalelere geri dön
July 2, 2026
5 dakikalık okuma

Çerçeve Vergisi: Backtest Kütüphaneniz Basit Bir Pandas Döngüsünden Yavaş Olduğunda

Çerçeve Vergisi: Backtest Kütüphaneniz Basit Bir Pandas Döngüsünden Yavaş Olduğunda
#algo ticaret
#backtest
#performans
#parametre araması
#vektörleştirme
#kıyaslama
Part 2 of 10 · Collection
High-Performance Backtest Engines

"İllüzyonsuz Backtestler" serisinin bir parçası.

Çoğu algo ticaret projesinin temelinde rahat bir varsayım yatar: olgun, çok yıldızlı bir backtest kütüphanesinin hızlı olduğu. Yılların katkılarına, gerçek bir olay döngüsüne, bir aracı kurum modeline, bir komisyon şemasına sahiptir. Kendinizin yazacağı o baştan savma pandas döngüsünü elbette yener. Bu yüzden ona uzanır, stratejinizi bağlar ve bir parametre taraması başlatırsınız — birkaç bin konfigürasyon, gece boyu süren bir iş. Sabah geri geldiğinizde hâlâ çalışıyordur.

Sekiz backtest motorunu tek ve aynı parametre taramasında kıyasladık ve bir arama aracını seçme şeklinizi değiştirmesi gereken bir şey bulduk. En popüler açık kaynaklı olay güdümlü çerçevelerden ikisi — backtrader ve bt — taramayı atılacak bir temel çizgi olarak yazdığımız basit bir pandas döngüsünden daha yavaş çalıştırdı. Biraz daha yavaş değil. backtrader, pandas temel çizgisinin kabaca 2.5 katı kadar sürdü; bt, yaklaşık 4.7 katı. Bu arada aynı işte vektörleştirilmiş/derlenmiş bir motor, bt'den yaklaşık 13,000× daha hızlı çalıştı.

İşte bu çerçeve vergisidir. Popüler backtest araçları tek dürüst bir çalıştırma için inşa edilmiştir — bir strateji, bir veri seti, dikkatli dolumlar, aracı kurum gibi davranan bir aracı kurum. Nihai bir doğrulama veya canlı-parite kontrolü için tam da istediğiniz şey budur. Ancak algo araştırmasının zamanını gerçekten harcadığı şey için tam olarak yanlış araçtır: aynı stratejiyi biraz farklı düğmelerle on bin kez çalıştırmak. Bu makale vergiyi ölçüyor, mekanizmayı açıklıyor ve "gerçek" bir backtest çerçevesinin yanlış seçim olduğu durumlar için bir karar kuralı veriyor.

Buradaki her sayı, tek bir yeniden üretilebilir düzenekten (benchmarks/bench_oss_engines.py, commit 250dbb5) aynı, pariteye kilitli iş yükü üzerinde geliyor. Bir motoru kendimiz çalıştırmadığımız yerde, bunu söylüyoruz ve bir figür uydurmak yerine ayrı bir dürüstlük bölümüne koyuyoruz.

Bir çalıştırma karşısında on bin

Aynı strateji hesaplamasının üç ıraksak yol boyunca aşağı akışı: her bir bar-istasyonunda durmaya zorlanan kırmızı-turuncu renkte olay güdümlü bir hat, kehribar renginde algo düğümlerinden oluşan bir ağırlık/yeniden dengeleme ağacı ve tüm seriyi tek seferde çözen tek bir vektörleştirilmiş zümrüt-camgöbeği ışın demeti — tarama hızı için paradigma kaderdir

Parametre aramasını tanımlayan gerçek, motorun binlerce kez çalışması ama analizin bir kez gerçekleşmesidir. Bir backtest kurmak için ödediğiniz her sabit maliyet — olay döngüsünü kurmak, bir aracı kurum örneği oluşturmak, bar başına bir nesne tahsis etmek — bunu her tek kombinasyonda ödersiniz. Tek bir çalıştırmada görünmez olan bir maliyet ("6 saniyeyi kim umursar?"), bir tarama boyunca faturanın tamamı hâline gelir ("6 saniye × 10,000 = 16.6 saat").

Backtest motorları üç paradigmaya ayrılır ve paradigma, tarama performansı için kaderdir:

  1. Olay güdümlü — motor bar bar yürür, olaylar yayar, sizin next()/onBars() geri çağrınızı çağırır, emirleri bir aracı kurum nesnesi üzerinden yönlendirir. Bu, backtrader, backtesting.py, PyAlgoTrade, zipline ve nautilus_trader'ın mimarisidir. Canlı ticaretin gerçekte nasıl çalıştığını yansıtır ki gerçekçilik için güvenilmesinin nedeni tam da budur — ve yavaş olmasının nedeni de tam olarak budur: bar başına Python ek yükü kombinasyon başına 150,000 kez ödenir.
  2. Ağırlık/yeniden dengelemebt burada oturur. Ona bir hedef-ağırlık matrisi verirsiniz ve belirttiğiniz tarihlerde yeniden dengeler. Bar başına geri çağrı yok, ama yine de Python içinde değerlendirilen olay başına bir nesne grafiği (bir algo ağacı, bir işlem defteri) var.
  3. Vektörleştirilmiş / derlenmiş — tüm strateji dizi işlemleri (vectorbt), JIT ile derlenmiş bir çekirdek (numba) veya yerel kod (Rust, bir MLX GPU çekirdeği) olarak ifade edilir. Bar başına hiç Python yoktur. Varsa döngü, makine hızında çalışır.

Bu makalenin geri kalanı, o sınıflandırmanın ampirik sonucudur. Tek bir iş yükü kurduk, her motorun kanıtlanabilir biçimde aynı işi yapmasını sağladık ve süreyi ölçtük.

İş yükü: bir strateji, seksen düğme, herkes için aynı

Bir kıyaslama ancak her motor aynı işi yapıyorsa dürüsttür. Bizimki kasıtlı olarak sade — motorlar arasında farklı olan tek şeyin motorun kendisi olduğu bir iş yükü.

  • Veri. Tek bir sentetik geometrik Brownian hareketi kapanış serisi: 150,000 bar, seed=42, bar başına oynaklık sigma=0.0008, x0=30000. Deterministik, dolayısıyla herkes bunu bit-bit yeniden üretebilir. Yapısı gereği yalnızca kapanış — OHLC her ayakta kapanışa ayarlanır, çünkü strateji bir kapanış-kesişimidir.
  • Strateji. Bir Hull Moving Average kesişimi: L uzunluğunda bir HMA'ya karşı daha hızlı bir HMA-üçte-bir varyantı (HMA'ya karşı HMA3). Her zaman piyasada, her kesişimde uzun/kısa çevir. Bu gerçek, önemsiz olmayan bir göstergedir — iki iç içe ağırlıklı hareketli ortalama artı bir karekök-pencere yumuşatıcı — oyuncak bir SMA değil, dolayısıyla bar başına iş temsilcidir.
  • Tarama. 6..200 arasını kapsayan 80 HMA uzunluğu. Doğrudan ölçülebilecek kadar küçültülmüş "on bin çalıştırma" budur: 80 bağımsız kombinasyon, her biri 150k bar üzerinde tam bir backtest.
  • Maliyetler. 0.09% gidiş-dönüş ücreti, yan komisyonları modelleyen motorlar için taraf başına bölünmüş. close[i]'de aynı-bar dolumu — bar i üzerindeki sinyal o barın kapanışında yürütülür, üretim motorumuzun kullandığı konvansiyon.

Kombinasyon başına zamanlayıcı tam olarak iki şeyi sarar: numpy HMA ön hesabı ve motor çalıştırması. Gerçekten bir kerelik olan kurulum (veri yükleme, bar nesnelerini oluşturma) zamanlayıcının dışında yer alır. Bir ısınma çalıştırması var, ardından N'nin-en-iyisi tekrarlar ve — olay güdümlü motorlar 80 tam kombinasyonun birçok dakikadan bir saatin üstüne kadar sürmesine yetecek kadar yavaş olduğundan — ızgaranın tekdüze bir örneğini zamanlar ve doğrusal olarak ekstrapole ederiz. Kombinasyonlar bağımsızdır, dolayısıyla doğrusal ekstrapolasyon beklentide kesindir; aynı konvansiyon pandas temel çizgisine de uygulanır, dolayısıyla hiçbir motor bundan avantaj sağlamaz.

Parite: herkesin aynı işi yaptığını kanıtlamak

İşte basit bir motor karşılaştırmasının düştüğü tuzak: "hızlı" bir motor aslında sadece daha az iş yapıyor olabilir. backtrader 2,700 işlem kaydederse ve vektörleştirilmiş motorunuz 40 kaydederse, vektörleştirilmiş motor daha hızlı değildir — yanlıştır ve karşılaştırma anlamsızdır.

Bu yüzden karşılaştırmayı bir işlem sayısı paritesi kontrolü ile kilitleriz. L=104'te, numpy referansı tam olarak 2,707 kapanmış işlem üretir. Her motor bunu ±1 toleransla yeniden üretmelidir, aksi hâlde çalıştırma bir work-parity FAILED iddiasıyla iptal olur. Tolerans yalnızca motorların defter tutma konvansiyonlarında anlaşamamasından kaynaklanır — nihai açık pozisyonun zorla kapatılıp sayılıp sayılmadığı, ilk girişin bir "işlem" olup olmadığı — işlemlerin kendisinde değil:

Motor Bildirilen işlemler @ L=104 Konvansiyon
numpy referansı 2707 kapanmış gidiş-dönüşler
backtesting.py 2708 +1: nihai pozisyon sonda zorla kapatıldı
backtrader 2707 nihai açık pozisyon sayılmadı
bt 2708 +1: ilk giriş bir işlem olarak sayıldı
PyAlgoTrade 2708 +1: ilk giriş bir dolum olarak sayıldı

Her motor 2707 ± 1'e konar. Hız farkları ne çıkarsa çıksın, bunlar bir motorun sessizce iş atlamasının bir eseri değildir. Olay güdümlü bir çerçeveyi ve bir GPU çekirdeğini aynı tabloya koyup bunu ciddiye almamızı sağlayan disiplin budur.

Sonuçlar

İşte en hızlıdan en yavaşa sıralanmış tüm tablo. combos/s verimdir; son sütun, tam 80 kombinasyonluk taramanın ne kadar sürdüğüdür. Temel çizgi satırı M0'dır — basit pandas motoru, skaler defter tutmayla barlar üzerinde bir for döngüsü, bir öğleden sonra yazıp atacağınız şey. Bu temel çizgiden daha yavaş olan her şey kalın.

Motor combos/s Paradigma Tam 80 kombinasyon taraması
MLX GPU çekirdeği 779 vektörleştirilmiş (Apple GPU) 0.10 s
yerel Rust ~350 derlenmiş 0.23 s
mp + numba 246 derlenmiş JIT + çoklu işlem 0.33 s
vectorbt 56.9 vektörleştirilmiş (numpy/numba) 1.4 s
numba (tek çekirdek) 39.7 derlenmiş JIT 2.0 s
backtesting.py 1.42 olay güdümlü 56 s
PyAlgoTrade 0.51 olay güdümlü 2.6 dk
M0 — basit pandas + döngü 0.28 skaler temel çizgi 4.8 dk
backtrader 0.11 olay güdümlü 12.7 dk
bt 0.06 ağırlık / yeniden dengeleme 22.5 dk

Tabloyu yukarıdan aşağı okuyun, paradigma kendini sıralar: ilk beşin hepsi vektörleştirilmiş veya derlenmiş, son beşin hepsi olay güdümlü veya nesne grafiği tabanlı — basit pandas döngüsü ise iki olgun, popüler çerçevenin üstünde oturuyor. Yukarıdan aşağıya yayılım dört büyüklük mertebesidir. Tam olarak aynı 2,707 işlemlik iş yükünde, MLX çekirdeği taramayı saniyenin onda birinde bitirir; bt yirmi iki buçuk dakikaya ihtiyaç duyar. Bu kabaca 13,000× faktörüdür.

Tablonun ortasındaki skandal

Tablonun ortasındaki paradoks: sade gri-mavi basit bir for-döngüsü koşucusu, hâlâ arkada didinen, aracı kurum modelleri ve analizör yığınlarıyla yüklü, kırmızı-turuncu parlayan iki süslü, özellik-ağırlıklı olay güdümlü çerçeve makinesinin önünde bitiş çizgisini geçiyor — mütevazı temel çizgi, olgun çerçeveleri aynı 2,707 işlemlik iş yükünde yeniyor

Göz alıcı sayılar uçlardadır, ama öğretici sonuç ortadadır: backtrader (0.11 combos/s) ve bt (0.06 combos/s), her ikisi de basit pandas temel çizgisinden (0.28 combos/s) daha yavaştır.

Bunun sindirilmesi gerekir. M0 zekice bir motor değildir. Bir DataFrame'e indeksleyen, bir pozisyonu ve nakiti düz skalerlerde izleyen ve işlemleri bir listeye ekleyen bir Python for döngüsüdür — açıkça kötü, yenilecek bir şeye sahip olmak için dahil ettiğimiz kasıtlı olarak optimize edilmemiş "kontrol". pandas'ın satır başına erişimi ünlü şekilde yavaştır ve buna yaslandık. Yine de ekosistemdeki en çok önerilen backtest kütüphanelerinden ikisi ona yenilir: backtrader 2.5×, bt 4.7× ile.

Bunu dürüst tutan nüans: her olay güdümlü motor pandas'tan yavaş değildir. backtesting.py (1.42 combos/s), temel çizgiyi 5× yener, çünkü bar başına nesne oluşturmayı asgaride tutan zayıf, numpy destekli bir olay döngüsüdür. PyAlgoTrade (0.51) da temel çizginin önüne biraz geçer. Yani "olay güdümlü" otomatik olarak bir ölüm fermanı değildir — ama bar başına makine ne kadar ağırsa o kadar kötüleşir ve backtrader ile bt buradaki en ağır makineyi taşır. Paradigma tavanı belirler; uygulama, o tavanın altında nereye ineceğinize karar verir.

Mesele bunların kötü kütüphaneler olduğu değil. backtrader'ın aracı kurum modeli ve bt'nin algo-ağacı tasarımı size doğruluk ve ifade gücü satın almak için vardır — gerçekçi emir işleme, portföy yeniden dengeleme, analizörler. Bu özelliklerin bir çalışma zamanı maliyeti vardır ve o maliyet, bir kez çalıştırdığınızda görünmezdir. Bir tarama boyunca ise hikayenin tamamıdır.

Olay güdümlü motorlar vergiyi neden öder

Çerçeve vergisinin bar başına nerede tahsil edildiği: tek bir bar-yinelemesi maliyetli bileşenlerine ayrılmış — somutlaştırılmış bir bar nesnesi, bir geri çağrı çerçevesi, bir emir yönlendiren aracı kurum, eklenen bir defter — her biri bir jeton alan kırmızı-turuncu parlayan bir gişe, on iki milyon özdeş bar-yinelemesinden oluşan uçsuz bucaksız, geri çekilen bir koridor boyunca tekrarlanan

Mekanizma gizemli değildir. Olay güdümlü bir backtest, bar başına, şöyle bir şey yapar:

  1. Saati ilerlet, veri akışından bir sonraki barı dilimle ve onu bir nesne (bir Bar, bir Line, bir dict) olarak somutlaştır.
  2. Kullanıcı koduna bir geri çağrı ateşle (next(), onBars()), ki bu kendi çerçevesine sahip bir Python fonksiyon çağrısıdır.
  3. Geri çağrının içinde, aracı kurum/pozisyon durumunu sorgula, yine yöntem çağrıları ve öznitelik aramaları aracılığıyla.
  4. Bir emir oluşturulursa, onu aracı kurum üzerinden yönlendir: doğrula, marjı/nakiti kontrol et, bir dolum zamanla, bir portföy nesnesini muta et, bir işlem defterine ekle.
  5. Analizörleri, gözlemcileri ve çerçevenin sürdürdüğü herhangi bir defter tutmayı güncelle.

Şimdi 150,000 barla, sonra 80 kombinasyonla çarpın: tarama başına on iki milyon bar-yinelemesi, her biri bir avuç dolusu Python düzeyinde nesne tahsisi ve dinamik gönderim. Python'un işlem başına ek yükü — bir öznitelik araması veya küçük bir tahsis için düzinelerce ila yüzlerce nanosaniye — bir kez önemsiz ama on iki milyon kez yıkıcıdır. bt durumu aynı hastalığın bir varyantıdır: her bar yerine yalnızca işlem tarihlerinde yeniden dengelese de, her yeniden dengeleme bir algo nesneleri ağacını değerlendirir ve pandas destekli bir portföy defterine dokunur ve bunlardan kombinasyon başına 2,707 tane vardır, 80 ile çarpı.

Basit pandas döngüsü backtrader ve bt'yi keskin bir nedenle yener: bar başına daha az iş yapar. Aracı kurumu, olay nesnelerini, analizör yığınını, emir-yönlendirme durum makinesini atlar. pandas'ın çirkin satır başına vergisini öder, ama o tek çirkin vergi bile çerçevenin düzenli, özellik-eksiksiz, olay başına-nesne vergisinden hâlâ ucuzdur. Bir backtesti "pozisyon × sonraki-getiri − ücretler"e indirdiğinizde, bir çerçevenin bar başına yaptığının çoğu, bir arama sırasında kullanmadığınız ek yüktür.

Ve işte tuzak: o ek yük, çerçeveyi seçme nedeninizdir. Gerçekçi aracı kurumu istediniz. Analizörleri istediniz. Nihai doğrulama sırasında hepsini istersiniz. Diğer uçtan yalnızca tek bir skaler amaç okuduğunuz 10,000 kombinasyonluk bir arama sırasında, pistlerde tur atması için bir limuzin parası ödüyorsunuz.

Tablonun diğer ucu: vektörleştirilmiş ve derlenmiş

Tablonun tepesi, bar başına Python'u tamamen sildiğinizde olan şeydir.

  • vectorbt (56.9 combos/s) tüm stratejiyi numpy/numba dizi işlemleri olarak ifade eder. Python'da bar döngüsü yoktur — sinyal, pozisyon, PnL hepsi dizi düzeyindedir. Taramayı bt'nin 22.5 dakikasına karşı 1.4 saniyede çalıştırır: aynı işte yaklaşık 950× daha hızlı. (vectorbt'in tasarımını vectorbt genel bakış ve daha geniş pandas-vs-polars karşılaştırmasında daha derinlemesine ele alıyoruz.)
  • numba (39.7), şekli değişmemiş, yerel koda JIT ile derlenmiş pandas döngüsüdür. M0 ile aynı algoritma, ama @njit 0.28 combos/s'yi 39.7'ye çevirir — bir dekoratörden ~140× hızlanma, çünkü skaler döngüye hâkim olan yorumlayıcı ek yükü basitçe buharlaşır.
  • mp + numba (246) derlenmiş çekirdeği CPU çekirdekleri arasında çalıştırır. Kombinasyonlar utanç verecek kadar paraleldir — her biri bağımsızdır — dolayısıyla çoklu işlem JIT'in üzerinde neredeyse doğrusal ölçeklenir.
  • yerel Rust (~350) Python yapıştırıcısının son kalıntısını kaldırır: tüm tarama yereldir.
  • MLX GPU (779) taramayı bir Apple-silikon GPU çekirdeğine eşler. 80 kombinasyon, 80 paralel aritmetik şeridine dönüşür; tarama, enter tuşunu bırakmadan önce biter.

İki şey tam olarak adlandırılmayı hak eder. İlk olarak, numba paradigmanın dilden daha önemli olduğunu kanıtlar. M0 ve numba aynı algoritmayı çalıştırır — fark tamamen birinin bar başına yorumlanan Python, diğerinin derlenmiş olmasıdır. Bu, kontrollü bir A/B'de çerçeve vergisinin tamamıdır: iç döngüden yorumlayıcıyı silmek için ~140×. İkincisi, numba'dan (39.7) mp+numba'ya (246) MLX'e (779) sıçrama artık motorla ilgili değildir — orkestrasyon ve donanımla ilgilidir. Bar başına vergi gittiğinde, hız, kaç kombinasyonu paralel çalıştırdığınıza ve hangi silikonda çalıştırdığınıza dönüşen bir soru hâline gelir. Bu tam ilerlemeyi backtest motoru hız merdiveni içinde yürüyoruz ve son mil'in neden süreç/serileştirme maliyeti tarafından domine edildiğini IPC-vergisi makalesi içinde ele alıyoruz.

Ölçmediğimiz şey (ve neden size söylüyoruz)

Bir kıyaslamanın güvenilirliği, uydurmaktan kaçındığı şeyde yaşar. Sekiz motoru parite altında baştan sona çalıştırdık. Birkaç iyi bilinen çerçeveye bir sayı koymadık ve ölçmediğimiz bir figürü ekstrapole etmektense bunları dürüstçe listelemeyi tercih ederiz:

  • zipline / zipline-reloaded — olay güdümlü, Quantopian soyundan. Ağır kurulum (tam bir ticaret takvimi ve veri paketi), bu da elmayla-elma kombinasyon başına zamanlamayı zorlaştırır. Mimari olarak backtrader ile olay güdümlü kampta oturur; tablonun o ucuna yakın beklerdik, ama bunu kanıtlamadık.
  • nautilus_trader — Rust/Cython çekirdekli, açıkça canlı-parite için tasarlanmış olay güdümlü. Çekirdeği derlenmiştir, dolayısıyla tam Python vergisini ödemeyecek en olası olay güdümlü motordur — henüz çalıştırmadığımız gerçekten ilginç bir ölçüm.
  • QuantConnect Lean — C# tabanlı, tamamen farklı bir çalışma zamanı; bir Python düzeneğinde doğrudan karşılaştırılabilir değil.
  • Jesse — olay güdümlü, kripto odaklı; tasarımını ayrı bir notta inceledik ama burada kıyaslamadık.
  • QSTrader — olay güdümlü, portföy odaklı; aynı paradigma uyarıları.
  • fastquant — denedik; kurulum/API ortamımızda bozuktu, dolayısıyla bir sayı yok. Bir tane tahmin etmeyeceğiz.

Bildirdiğimiz sayılar hakkında iki dürüst uyarı. vectorbt, numba, mp+numba, yerel-Rust ve MLX figürleri, dört olay güdümlü satırı üreten OSS düzeneğinden değil, aynı iş yükü üzerinde kendi motor merdivenimizden geliyor — aynı iş yükü ama farklı bir ölçüm düzeneği, ve yerel-Rust figürü sıkı bir ölçüm değil, yaklaşık bir ~350. Ve mutlak combos/s donanıma özgüdür; taşınan şey sıralama ve oranlardır, ki bunlar yeterince büyüktür (yukarıdan aşağıya 13,000×, pandas-vs-çerçeve tersine dönmesi için 2.5–4.7×) ki hiçbir makul donanım farkı bunları tersine çevirmez.

Olay güdümlü motorların savunmasında

Olay güdümlü motor kendi uygun rolünde: hayatta kalan tek bir stratejinin titiz, yüksek-sadakatli tek bir doğrulama çalıştırması, gerçekçi bir dolumu modelleyen, marjı ve emir yaşam döngüsüne saygı gösteren hassas bir zümrüt-ve-camgöbeği enstrümanı olarak resmedilmiş — arama sırasında saf israf olan aynı bar başına makine, burada değerli bir zanaatkârlık olarak, dağıtımdan önce tek bir dikkatli prova olarak gösteriliyor

Bunu "olay güdümlü backtest araçları kötüdür" diye okumak kolay olurdu. Bu yanlış bir ders ve haksız bir dersdir.

Olay güdümlü motorlar farklı bir iş için inşa edilmiştir ve o işte iyidirler. Bar başına aracı kurum, emir yaşam döngüsü, dolum mantığı, analizörler — o makine, bir backtestin canlı ticarete mümkün olduğunca benzemesini sağlamak için vardır. Hedefiniz, dağıtmak üzere olduğunuz bir stratejinin tek, güvenilir bir provası olduğunda, motorun her dolumun üzerine terlemesini, kısmi yürütmeleri modellemesini, marja saygı göstermesini ve alamayacağınız fiyatlardan işlem yapmanıza izin vermeyi reddetmesini istersiniz. O sadakat üründür. Çalışma zamanı maliyeti, gerçekçiliğin bedelidir ve bir çalıştırma için bu bedel ihmal edilebilir.

Başarısızlık motor değildir; onu yanlış aşama için kullanmaktır. Araştırmanın zıt gereksinimlere sahip iki farklı aşaması vardır:

  • Arama verim ister. Çoğu çöp olan bir manzarayı keşfediyorsunuz ve ikinci bir bakışa değecek birkaçını bulmak için binlerce noktayı değerlendirmeniz gerekiyor. Nokta başına sadakat neredeyse önemsizdir — sıralıyorsunuz, dağıtmıyorsunuz. Burada çerçeve vergisi saf israftır.
  • Doğrulama sadakat ister. Elinizde bir avuç aday var ve mümkün olduğunca kesin bilmeniz gerekiyor: gerçekçi yürütme, ücretler, kayma ve kâğıt getirilerini şişiren ileriye-bakma tuzaklarından hayatta kalıyorlar mı. Burada olay güdümlü motor maliyetini hak eder.

Çerçeve vergisinin cezalandırdığı hata, aramanızı doğrulama motorunuzda çalıştırmaktır — %99'unu atacağınız bir manzarayı keşfetmek için limuzin ücreti ödemek.

Karar kuralı

Pratik çıkarım tek bir karara sıkışır:

Vektörleştirilmiş/derlenmiş bir motorda arayın. Hayatta kalanları olay güdümlü bir motorda doğrulayın.

Somut olarak:

  1. Tarama için hızlı bir çekirdek inşa edin veya ödünç alın. Hazır istiyorsanız vectorbt; stratejiniz temiz bir şekilde vektörleşmiyorsa numba ile derlenmiş bir döngü (tek başına @njit dekoratörü burada ~140× kazandırdı). Tüm parametre uzayını onun içinden geçirin.
  2. Büyük bir taramayı asla backtrader, bt, zipline veya herhangi bir ağır olay güdümlü çerçevede çalıştırmayın. Bunlardan birinde bir tarama darboğazınızsa, çözüm daha büyük bir makine değil — yanlış motordur. Basit pandas döngüsü bile onlardan ikisini yenerdi.
  3. Bir kısa listeyi sadakat kontrolü için olay güdümlü motora yükseltin. Bir avuç hayatta kalanı alın ve onları gerçekçi motorda yeniden çalıştırın; burada aracı kurum modeli ve dolum mantığı, hızlı çekirdeğin soyutladığı sorunları açığa çıkarabilir.
  4. İkisi arasında pariteyi uygulayın. Hızlı motor ve sadakat motoru, sabit bir konfigürasyonda işlem sayıları ve PnL üzerinde anlaşmalıdır (L=104'teki ±1-işlem kontrolümüz), aksi hâlde arama ve doğrulama farklı stratejileri ölçüyordur ve tüm boru hattı bir yalandır.

Bu, arama ve doğrulamanın zıt maliyet profillerine sahip olduğu her yerde ortaya çıkan aynı iki-hızlı mimaridir ve kendi yığınımızın parametre araması için hızlı bir vektörleştirilmiş/derlenmiş yol tutmasının ve ağır makineyi nihai amaç değerlendirmesi ile plato kontrolleri için ayırmasının nedeni budur.

Çıkarımlar

  1. Popüler ≠ taramalar için hızlı. Tek bir özdeş, pariteye kilitli iş yükünde (150k bar, 80 HMA kesişim kombinasyonu, 2,707 işlem), backtrader (0.11 combos/s) ve bt (0.06), her ikisi de basit bir pandas döngüsünden (0.28) daha yavaş çalıştı. Olgun, çok yıldızlı bir çerçeve otomatik olarak hızlı seçim değildir.
  2. Çerçeve vergisi bar başınadır ve bir tarama onu çarpar. Tarama başına on iki milyon bar-yinelemesi, her biri bir olay nesnesi, bir geri çağrı ve bir aracı kurum gidiş-dönüşü taşır. Tek bir çalıştırmada görünmez olan bir maliyet, on bin boyunca faturanın tamamıdır.
  3. Paradigma tavanı belirler. Vektörleştirilmiş/derlenmiş motorlar (vectorbt 56.9, numba 39.7, mp+numba 246, yerel Rust ~350, MLX 779), olay güdümlü motorları iki ila dört büyüklük mertebesi yener — yukarıdan aşağıya ~13,000×'e kadar. Aynı algoritma, sadece JIT ile derlenmiş, 140× daha hızlı gitti.
  4. Tüm olay güdümlü motorlar eşit değildir. backtesting.py (1.42) ve PyAlgoTrade (0.51) hâlâ basit temel çizgiyi yener; vergi, bar başına makinenin ne kadar ağır olduğuyla ölçeklenir. Uygulama, o tavanın altında nereye ineceğinize karar verir.
  5. İki motor, iki aşama. Vektörleştirilmiş/derlenmiş bir çekirdekte arayın; hayatta kalanları gerçekçi olay güdümlü motorda doğrulayın. İkisinin de aynı stratejiyi ölçmesi için aralarında işlem sayısı/PnL paritesi uygulayın.
  6. Ölçtüğünüz şey hakkında dürüst olun. Sekiz motoru parite altında kıyasladık ve ölçmediklerimizi (zipline, nautilus_trader, Lean, Jesse, QSTrader ve çalışmayan fastquant kurulumu) onlar için sayı uydurmak yerine listeledik.

Rahatsız edici özet: bir parametre taraması darboğazınızsa, sorun muhtemelen makineniz değil ve stratejiniz değil. Sorun, tek bir dürüst çalıştırma için inşa edilmiş bir motorda bir arama çalıştırıyor olmanızdır — ve tutmaya çok utandığınız pandas döngüsüyle daha hızlı olurdunuz.

Tam düzenek, parite iddiaları ve motor başına ham JSON sonuçları, commit 250dbb5'te benchmarks/bench_oss_engines.py ve benchmarks/results_oss/ içinde yaşar. Merdivenin derlenmiş/GPU ucu için, backtest motoru hız merdiveni ve IPC-vergisi analizine bakın.

Sorumluluk Reddi: Bu makalede sağlanan bilgiler yalnızca eğitim ve bilgilendirme amaçlıdır ve finansal, yatırım veya ticaret tavsiyesi niteliği taşımaz. Kripto para ticareti önemli bir kayıp riski içerir.

Yazarlar

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

Piyasanın Önünde Olun

Özel yapay zeka ticaret içgörüleri, piyasa analizi ve platform güncellemeleri için bültenimize abone olun.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.