← Terug naar artikelen
July 2, 2026
5 min leestijd

De framework-belasting: wanneer je backtestbibliotheek trager is dan een naïeve pandas-loop

De framework-belasting: wanneer je backtestbibliotheek trager is dan een naïeve pandas-loop
#algotrading
#backtest
#prestaties
#parameterzoektocht
#vectorisatie
#benchmark
Part 2 of 10 · Collection
High-Performance Backtest Engines

Onderdeel van de serie "Backtests zonder illusies".

Er ligt een comfortabele aanname aan de basis van de meeste algotrading-projecten: dat een volwassen, veel-gesterde backtestbibliotheek snel is. Ze heeft jaren aan bijdragen, een echte event loop, een broker-model, een commissieschema. Ze verslaat toch zeker die knullige pandas-loop die je zelf zou hebben geschreven. Dus je grijpt ernaar, koppelt je strategie aan en start een parameter-sweep — een paar duizend configuraties, een nachtklus. Je komt 's ochtends terug en hij draait nog steeds.

We hebben acht backtest-engines gebenchmarkt op één identieke parameter-sweep en iets ontdekt dat zou moeten veranderen hoe je een tool voor het zoeken kiest. Twee van de populairste open-source event-driven frameworks — backtrader en bt — draaiden de sweep trager dan een naïeve pandas-loop die we als wegwerp-basislijn schreven. Niet een beetje trager. backtrader deed er ruwweg 2.5× zo lang over als de pandas-basislijn; bt, ongeveer 4.7×. Ondertussen draaide een gevectoriseerde/gecompileerde engine op hetzelfde werk ongeveer 13,000× sneller dan bt.

Dit is de framework-belasting. Populaire backtesters zijn gebouwd voor één eerlijke run — één strategie, één dataset, zorgvuldige fills, een broker die zich gedraagt als een broker. Dat is precies wat je wilt voor een eindvalidatie of een live-pariteitscheck. Het is precies het verkeerde gereedschap voor datgene waar algo-onderzoek zijn tijd werkelijk aan besteedt: dezelfde strategie tienduizend keer draaien met iets andere knoppen. Dit artikel meet de belasting, legt het mechanisme uit en geeft een beslisregel voor wanneer een "echt" backtest-framework de verkeerde keuze is.

Elk getal hier komt uit één reproduceerbaar meetkader (benchmarks/bench_oss_engines.py, commit 250dbb5) op een identieke, in pariteit vastgezette werklast. Waar we een engine niet zelf hebben gedraaid, zeggen we dat en zetten we het in een aparte eerlijkheidssectie in plaats van een cijfer te verzinnen.

Eén run versus tienduizend

Dezelfde strategieberekening die langs drie divergerende paden naar beneden stroomt: een event-driven baan in rood-oranje die gedwongen wordt te stoppen bij elk bar-station, een gewichten/herbalanceer-boom van algo-knopen in amber, en één enkele gevectoriseerde smaragd-cyaan bundel die de hele reeks in één keer oplost — paradigma is het lot voor sweep-snelheid

Het bepalende feit van parameterzoeken is dat de engine duizenden keren draait maar de analyse één keer gebeurt. Welke vaste kosten je ook betaalt om een backtest op te zetten — de event loop bouwen, een broker instantiëren, een object per bar alloceren — je betaalt die op elke afzonderlijke combinatie. Kosten die onzichtbaar zijn in één run ("wie geeft er om 6 seconden?") worden de hele rekening over een sweep ("6 seconden × 10,000 = 16.6 uur").

Backtest-engines vallen in drie paradigma's uiteen, en het paradigma is het lot voor sweep-prestaties:

  1. Event-driven — de engine loopt bar voor bar, zendt events uit, roept jouw next()/onBars()-callback aan, routeert orders door een broker-object. Dit is de architectuur van backtrader, backtesting.py, PyAlgoTrade, zipline en nautilus_trader. Het weerspiegelt hoe live handel daadwerkelijk werkt, wat precies de reden is dat het vertrouwd wordt voor realisme — en precies de reden dat het traag is: de per-bar Python-overhead wordt 150,000 keer per combinatie betaald.
  2. Gewichten/herbalanceringbt zit hier. Je geeft het een doelgewichten-matrix en het herbalanceert op de data die je opgeeft. Geen per-bar callback, maar nog steeds een per-event objectgraaf (een boom van algo's, een transactiegrootboek) die in Python geëvalueerd wordt.
  3. Gevectoriseerd / gecompileerd — de hele strategie wordt uitgedrukt als array-bewerkingen (vectorbt), een JIT-gecompileerde kernel (numba) of native code (Rust, een MLX GPU-kernel). Er is helemaal geen per-bar Python. De loop, als die er is, draait op machinesnelheid.

De rest van dit artikel is het empirische gevolg van die taxonomie. We bouwden één werklast, zorgden dat elke engine aantoonbaar hetzelfde werk deed, en klokten het.

De werklast: één strategie, tachtig knoppen, identiek voor iedereen

Een benchmark is alleen eerlijk als elke engine dezelfde klus doet. De onze is bewust gewoon — een werklast waarin het enige wat verschilt tussen engines de engine zelf is.

  • Data. Eén synthetische close-reeks met geometrische Brownse beweging: 150,000 bars, seed=42, per-bar volatiliteit sigma=0.0008, x0=30000. Deterministisch, zodat iedereen het bit voor bit kan reproduceren. Per constructie alleen-close — OHLC wordt op elke leg op de close gezet, want de strategie is een close-cross.
  • Strategie. Een Hull Moving Average-cross: een HMA van lengte L tegen een snellere HMA-van-een-derde-variant (HMA versus HMA3). Altijd in de markt, wissel long/short bij elke cross. Dit is een echte, niet-triviale indicator — twee geneste gewogen voortschrijdende gemiddelden plus een vierkantswortel-venster-smoother — geen speelgoed-SMA, dus het per-bar werk is representatief.
  • De sweep. 80 HMA-lengtes die 6..200 beslaan. Dat is de "tienduizend runs" klein genoeg gemaakt om direct te meten: 80 onafhankelijke combinaties, elk een volledige backtest over 150k bars.
  • Kosten. Een round-trip-vergoeding van 0.09%, per zijde gesplitst voor engines die zijdecommissies modelleren. Same-bar fill op close[i] — het signaal op bar i wordt uitgevoerd op de close van die bar, de conventie die onze productie-engine gebruikt.

De per-combinatie-timer omvat precies twee dingen: de numpy HMA-precompute en de engine-run. Opzet die echt eenmalig is (data laden, de bar-objecten bouwen) valt buiten de timer. Er is één opwarmrun, dan best-of-N-herhalingen en — omdat de event-driven engines traag genoeg zijn dat 80 volledige combinaties vele minuten tot meer dan een uur zouden duren — klokken we een uniforme steekproef van het grid en extrapoleren we lineair. Combinaties zijn onafhankelijk, dus lineaire extrapolatie is exact in verwachting; dezelfde conventie wordt toegepast op de pandas-basislijn, zodat geen engine erdoor bevoordeeld wordt.

Pariteit: bewijzen dat iedereen hetzelfde werk doet

Hier is de valkuil waar een naïeve engine-vergelijking in trapt: een "snelle" engine doet misschien gewoon minder. Als backtrader 2,700 trades boekt en jouw gevectoriseerde engine er 40 boekt, is de gevectoriseerde engine niet sneller — hij is fout, en de vergelijking is betekenisloos.

Dus vergrendelen we de vergelijking met een trade-count-pariteitscheck. Bij L=104 produceert de numpy-referentie precies 2,707 gesloten trades. Elke engine moet dat reproduceren binnen een tolerantie van ±1, anders wordt de run afgebroken met een work-parity FAILED-assertie. De tolerantie bestaat alleen omdat engines het oneens zijn over boekhoudkundige conventies — of de laatste open positie geforceerd gesloten en meegeteld wordt, of de initiële instap een "trade" is — niet over de trades zelf:

Engine Gerapporteerde trades @ L=104 Conventie
numpy-referentie 2707 gesloten round-trips
backtesting.py 2708 +1: laatste positie geforceerd gesloten aan het eind
backtrader 2707 laatste open positie niet meegeteld
bt 2708 +1: initiële instap geteld als transactie
PyAlgoTrade 2708 +1: initiële instap geteld als fill

Elke engine landt op 2707 ± 1. Wat de snelheidsverschillen ook blijken te zijn, ze zijn geen artefact van een engine die stiekem werk overslaat. Dit is de discipline waarmee we een event-driven framework en een GPU-kernel in dezelfde tabel kunnen zetten en het menen.

De resultaten

Hier is de hele tabel, gesorteerd van snelst naar traagst. combos/s is de doorvoer; de laatste kolom is hoe lang de volledige 80-combinatie-sweep duurt. De basislijn-rij is M0 — de naïeve pandas-engine, een for-loop over bars met scalaire boekhouding, het ding dat je in een middag zou schrijven en weggooien. Alles wat trager is dan die basislijn staat vet.

Engine combos/s Paradigma Volledige 80-combinatie-sweep
MLX GPU-kernel 779 gevectoriseerd (Apple GPU) 0.10 s
native Rust ~350 gecompileerd 0.23 s
mp + numba 246 gecompileerde JIT + multiproces 0.33 s
vectorbt 56.9 gevectoriseerd (numpy/numba) 1.4 s
numba (single core) 39.7 gecompileerde JIT 2.0 s
backtesting.py 1.42 event-driven 56 s
PyAlgoTrade 0.51 event-driven 2.6 min
M0 — naïeve pandas + loop 0.28 scalaire basislijn 4.8 min
backtrader 0.11 event-driven 12.7 min
bt 0.06 gewichten / herbalancering 22.5 min

Lees de tabel van boven naar beneden en het paradigma sorteert zichzelf: de bovenste vijf zijn allemaal gevectoriseerd of gecompileerd, de onderste vijf zijn allemaal event-driven of gebaseerd op een objectgraaf — met de naïeve pandas-loop boven twee volwassen, populaire frameworks. De spreiding van boven naar beneden is vier ordes van grootte. Op precies dezelfde 2,707-trade werklast rondt de MLX-kernel de sweep af in een tiende van een seconde; bt heeft tweeëntwintig en een halve minuut nodig. Dat is een factor van ongeveer 13,000×.

Het schandaal in het midden van de tabel

De paradox in het midden van de tabel: een gewone grijs-blauwe naïeve for-loop-loper die de finish oversteekt vóór twee sierlijke, functie-zware event-driven framework-machines die rood-oranje gloeien en nog steeds achter zwoegen, verzwaard door broker-modellen en analyzer-stapels — de nederige basislijn die de volwassen frameworks verslaat op de identieke 2,707-trade werklast

De oogverblindende getallen staan aan de uitersten, maar het leerzame resultaat staat in het midden: backtrader (0.11 combos/s) en bt (0.06 combos/s) zijn beide trager dan de naïeve pandas-basislijn (0.28 combos/s).

Dit verdient het om te bezinken. M0 is geen slimme engine. Het is een Python for-loop die in een DataFrame indexeert, een positie en cash in gewone scalars bijhoudt, en trades aan een lijst toevoegt — de opzettelijk niet-geoptimaliseerde "controle" die we opnamen om iets overduidelijk slechts te hebben om te verslaan. De per-rij-toegang van pandas is berucht traag, en daar leunden we op. En toch verliezen twee van de meest aanbevolen backtestbibliotheken in het ecosysteem ervan: backtrader met 2.5×, bt met 4.7×.

De nuance die dit eerlijk houdt: niet elke event-driven engine is trager dan pandas. backtesting.py (1.42 combos/s) verslaat de basislijn met 5×, omdat het een slanke, numpy-ondersteunde event loop is die per-bar objectcreatie tot een minimum beperkt. PyAlgoTrade (0.51) komt ook net voor de basislijn uit. Dus "event-driven" is niet automatisch een doodvonnis — maar hoe zwaarder de per-bar machinerie, hoe erger het wordt, en backtrader en bt dragen hier de zwaarste machinerie. Het paradigma stelt het plafond; de implementatie beslist waar onder dat plafond je landt.

Het punt is niet dat dit slechte bibliotheken zijn. Het broker-model van backtrader en het boom-van-algo's-ontwerp van bt bestaan om je correctheid en expressiviteit te kopen — realistische orderafhandeling, portefeuille-herbalancering, analyzers. Die functies hebben een runtime-kost, en die kost is onzichtbaar wanneer je één keer draait. Over een sweep is het het hele verhaal.

Waarom event-driven engines de belasting betalen

Waar de framework-belasting geheven wordt, per bar: één enkele bar-iteratie uiteengelegd in zijn dure componenten — een gematerialiseerd bar-object, een callback-frame, een broker die een order routeert, een grootboek dat wordt aangevuld — elk een gloeiend rood-oranje tolhuisje dat een munt afroomt, herhaald langs een enorme wegvloeiende gang van twaalf miljoen identieke bar-iteraties

Het mechanisme is niet mysterieus. Een event-driven backtest doet per bar zoiets als dit:

  1. Zet de klok vooruit, snijd de volgende bar van de data-feed af en materialiseer die als een object (een Bar, een Line, een dict).
  2. Vuur een callback af naar gebruikerscode (next(), onBars()), wat een Python-functieaanroep met een eigen frame is.
  3. Bevraag binnen de callback de broker/positie-status, opnieuw via methodeaanroepen en attribuut-lookups.
  4. Als een order wordt aangemaakt, routeer het door de broker: valideer het, controleer marge/cash, plan een fill, muteer een portefeuille-object, voeg toe aan een transactiegrootboek.
  5. Werk analyzers, observers en welke boekhouding het framework ook bijhoudt bij.

Vermenigvuldig nu met 150,000 bars, dan met 80 combinaties: twaalf miljoen bar-iteraties per sweep, elk een handvol object-allocaties op Python-niveau en dynamische dispatches. De per-operatie-overhead van Python — tientallen tot honderden nanoseconden voor een attribuut-lookup of een kleine allocatie — is triviaal één keer en verwoestend twaalf miljoen keer. De bt-casus is een variant van dezelfde ziekte: ook al herbalanceert het alleen op trade-data in plaats van elke bar, elke herbalancering evalueert een boom van algo-objecten en raakt een pandas-ondersteund portefeuillegrootboek aan, en dat zijn er 2,707 per combinatie, keer 80.

De naïeve pandas-loop verslaat backtrader en bt om een botte reden: het doet minder per bar. Het slaat de broker, de event-objecten, de analyzer-stapel, de order-routing-toestandsmachine over. Het betaalt de lelijke per-rij-belasting van pandas, maar die enkele lelijke belasting is nog steeds goedkoper dan de nette, functie-complete, object-per-event-belasting van het framework. Wanneer je een backtest terugbrengt tot "positie × volgend-rendement − vergoedingen", is het meeste van wat een framework per bar doet overhead die je tijdens een zoektocht niet gebruikt.

En dit is de valkuil: de overhead is de reden dat je het framework koos. Je wilde de realistische broker. Je wilde de analyzers. Tijdens de eindvalidatie wil je het allemaal. Tijdens een zoektocht van 10,000 combinaties waarbij je aan de andere kant maar één scalaire doelstelling afleest, betaal je voor een limousine om rondjes op het circuit te rijden.

Het andere uiteinde van de tabel: gevectoriseerd en gecompileerd

De top van de tabel is wat er gebeurt wanneer je de per-bar Python volledig schrapt.

  • vectorbt (56.9 combos/s) drukt de hele strategie uit als numpy/numba array-bewerkingen. Er is geen bar-loop in Python — het signaal, de positie, de PnL zijn allemaal op array-niveau. Het draait de sweep in 1.4 seconden tegenover de 22.5 minuten van bt: ongeveer 950× sneller op identiek werk. (We behandelen het ontwerp van vectorbt diepgaander in het vectorbt-overzicht en de bredere pandas-vs-polars-vergelijking.)
  • numba (39.7) is de pandas-loop, ongewijzigd in vorm, JIT-gecompileerd naar native code. Hetzelfde algoritme als M0, maar @njit verandert 0.28 combos/s in 39.7 — een ~140× versnelling van een decorator, omdat de interpreter-overhead die de scalaire loop domineerde eenvoudigweg verdampt.
  • mp + numba (246) draait de gecompileerde kernel over CPU-cores. Combinaties zijn beschamend parallel — elk is onafhankelijk — dus multiprocessing schaalt bijna lineair bovenop de JIT.
  • native Rust (~350) verwijdert het laatste beetje Python-lijm: de hele sweep is native.
  • MLX GPU (779) wijst de sweep toe aan een Apple-silicium GPU-kernel. 80 combinaties worden 80 parallelle banen van rekenwerk; de sweep is klaar voordat je de entertoets loslaat.

Twee dingen zijn het waard om precies te benoemen. Ten eerste, numba bewijst dat het paradigma meer telt dan de taal. M0 en numba draaien hetzelfde algoritme — het verschil is puur dat de een geïnterpreteerde per-bar Python is en de ander gecompileerd. Dat is de hele framework-belasting in een gecontroleerde A/B: ~140× voor het schrappen van de interpreter uit de binnenste loop. Ten tweede gaat de sprong van numba (39.7) naar mp+numba (246) naar MLX (779) helemaal niet meer over de engine — het gaat over orkestratie en hardware. Zodra de per-bar belasting weg is, wordt snelheid een kwestie van hoeveel combinaties je parallel draait en op welk silicium. We doorlopen de volledige progressie in de snelheidsladder van backtest-engines, en de reden dat de laatste mijl gedomineerd wordt door proces/serialisatie-kosten in het IPC-belasting-artikel.

Wat we niet hebben gemeten (en waarom we het je vertellen)

De geloofwaardigheid van een benchmark leeft in wat het weigert te vervalsen. We draaiden acht engines van begin tot eind onder pariteit. Op verschillende bekende frameworks zetten we geen getal, en we sommen ze liever eerlijk op dan een cijfer te extrapoleren dat we niet hebben gemeten:

  • zipline / zipline-reloaded — event-driven, Quantopian-afkomst. Zware opzet (een volledige handelskalender en data-bundle), wat een appels-met-appels per-combinatie-timing lastig maakt. Architectonisch zit het bij backtrader in het event-driven kamp; we zouden het dicht bij dat uiteinde van de tabel verwachten, maar we hebben het niet bewezen.
  • nautilus_trader — event-driven met een Rust/Cython-kern, expliciet ontworpen voor live-pariteit. De kern is gecompileerd, dus het is de event-driven engine die het meest waarschijnlijk niet de volledige Python-belasting betaalt — een oprecht interessante meting die we nog niet hebben uitgevoerd.
  • QuantConnect Lean — op C# gebaseerd, een geheel andere runtime; niet direct vergelijkbaar in een Python-meetkader.
  • Jesse — event-driven, crypto-gericht; we bekeken het ontwerp ervan in een aparte notitie maar benchmarkten het hier niet.
  • QSTrader — event-driven, portefeuille-georiënteerd; dezelfde paradigma-voorbehouden.
  • fastquant — we probeerden het; de installatie/API was kapot in onze omgeving, dus er is geen getal. We gaan er geen raden.

Twee eerlijke voorbehouden over de getallen die we wel rapporteren. De cijfers voor vectorbt, numba, mp+numba, native-Rust en MLX komen uit onze eigen engine-ladder op de identieke werklast, niet uit het OSS-meetkader dat de vier event-driven rijen produceerde — het is dezelfde werklast maar een ander meetopstelling, en het native-Rust-cijfer is een benaderend ~350, geen strakke meting. En absolute combos/s is hardware-specifiek; wat meereist is de ordening en de verhoudingen, die groot genoeg zijn (13,000× van boven naar beneden, 2.5–4.7× voor de pandas-vs-framework-omkering) dat geen redelijk hardware-verschil ze omdraait.

Ter verdediging van event-driven engines

De event-driven engine in zijn juiste rol: één nauwgezette validatie-run met hoge betrouwbaarheid van één overlevende strategie, weergegeven als een precies smaragd-en-cyaan instrument dat een realistische fill modelleert, marge en de order-levenscyclus respecteert — dezelfde per-bar machinerie die tijdens het zoeken pure verspilling was, hier getoond als waardevol vakmanschap, één zorgvuldige generale repetitie vóór uitrol

Het zou gemakkelijk zijn om dit te lezen als "event-driven backtesters zijn slecht". Dat is de verkeerde les, en een oneerlijke.

Event-driven engines zijn gebouwd voor een andere klus, en daar zijn ze goed in. De per-bar broker, de order-levenscyclus, de fill-logica, de analyzers — die machinerie bestaat om een backtest zo dicht mogelijk op live handel te laten lijken. Wanneer je doel een enkele, betrouwbare generale repetitie is van een strategie die je op het punt staat uit te rollen, wil je dat de engine over elke fill zweet, gedeeltelijke uitvoeringen modelleert, marge respecteert en weigert je te laten handelen tegen prijzen die je niet had kunnen krijgen. Die betrouwbaarheid is het product. De runtime-kost ervan is de prijs van realisme, en voor één run is de prijs verwaarloosbaar.

Het falen is niet de engine; het is hem gebruiken voor de verkeerde fase. Onderzoek heeft twee afzonderlijke fases met tegengestelde eisen:

  • Zoeken wil doorvoer. Je verkent een landschap, waarvan het meeste rommel is, en je moet duizenden punten evalueren om de paar te vinden die een tweede blik waard zijn. Betrouwbaarheid per punt maakt nauwelijks uit — je rangschikt, niet uitrolt. Hier is de framework-belasting pure verspilling.
  • Validatie wil betrouwbaarheid. Je hebt een handvol kandidaten en je moet zo precies mogelijk weten of ze realistische uitvoering, vergoedingen, slippage en de look-ahead-valkuilen die papierrendementen opblazen, overleven. Hier verdient de event-driven engine zijn kost.

De fout die de framework-belasting bestraft is je zoektocht draaien op je validatie-engine — limousine-tarieven betalen om een landschap te verkennen waarvan je 99% gaat weggooien.

De beslisregel

De praktische conclusie comprimeert tot één beslissing:

Zoek op een gevectoriseerde/gecompileerde engine. Valideer de overlevenden op een event-driven engine.

Concreet:

  1. Bouw of leen een snelle kernel voor de sweep. vectorbt als je het kant-en-klaar wilt; een numba-gecompileerde loop als je strategie niet netjes vectoriseert (alleen de @njit-decorator kocht hier ~140×). Laat de volledige parameterruimte er doorheen lopen.
  2. Draai nooit een grote sweep op backtrader, bt, zipline of enig zwaar event-driven framework. Als een sweep op een van die je knelpunt is, is de oplossing niet een grotere machine — het is de verkeerde engine. Zelfs de naïeve pandas-loop zou er twee van verslaan.
  3. Promoveer een shortlist naar de event-driven engine voor een betrouwbaarheidscheck. Neem het handvol overlevenden en draai ze opnieuw op de realistische engine, waar het broker-model en de fill-logica problemen kunnen blootleggen die de snelle kernel wegabstraheerde.
  4. Handhaaf pariteit tussen de twee. De snelle engine en de betrouwbaarheidsengine moeten het eens zijn over trade-counts en PnL op een vaste configuratie (onze ±1-trade-check bij L=104), anders meten het zoeken en de validatie verschillende strategieën en is de hele pijplijn een leugen.

Dit is dezelfde tweesnelheden-architectuur die opduikt wanneer zoeken en validatie tegengestelde kostenprofielen hebben, en het is de reden dat onze eigen stack een snel gevectoriseerd/gecompileerd pad aanhoudt voor de parameterzoektocht en de zware machinerie reserveert voor de uiteindelijke doelstellingsevaluatie en plateau-checks.

Kernpunten

  1. Populair ≠ snel voor sweeps. Op één identieke, in pariteit vastgezette werklast (150k bars, 80 HMA-cross-combinaties, 2,707 trades) draaiden backtrader (0.11 combos/s) en bt (0.06) beide trager dan een naïeve pandas-loop (0.28). Een volwassen, veel-gesterd framework is niet automatisch de snelle keuze.
  2. De framework-belasting is per-bar, en een sweep vermenigvuldigt haar. Twaalf miljoen bar-iteraties per sweep, elk met een event-object, een callback en een broker-round-trip. Kosten die onzichtbaar zijn in één run zijn de hele rekening over tienduizend.
  3. Het paradigma stelt het plafond. Gevectoriseerde/gecompileerde engines (vectorbt 56.9, numba 39.7, mp+numba 246, native Rust ~350, MLX 779) verslaan de event-driven engines met twee tot vier ordes van grootte — tot ~13,000× van boven naar beneden. Hetzelfde algoritme, enkel JIT-gecompileerd, ging 140× sneller.
  4. Niet alle event-driven engines zijn gelijk. backtesting.py (1.42) en PyAlgoTrade (0.51) verslaan nog steeds de naïeve basislijn; de belasting schaalt met hoe zwaar de per-bar machinerie is. De implementatie beslist waar onder het plafond je landt.
  5. Twee engines, twee fases. Zoek op een gevectoriseerde/gecompileerde kernel; valideer de overlevenden op de realistische event-driven engine. Handhaaf trade-count/PnL-pariteit tussen hen zodat beide dezelfde strategie meten.
  6. Wees eerlijk over wat je gemeten hebt. We benchmarkten acht engines onder pariteit en somden degene op die we niet deden (zipline, nautilus_trader, Lean, Jesse, QSTrader en de fastquant-installatie die niet wilde draaien) in plaats van getallen voor ze te verzinnen.

De ongemakkelijke samenvatting: als een parameter-sweep je knelpunt is, is het probleem waarschijnlijk niet je machine en niet je strategie. Het is dat je een zoektocht draait op een engine die gebouwd is voor één enkele eerlijke run — en je zou sneller zijn met de pandas-loop die je te gênant vond om te bewaren.

Het volledige meetkader, de pariteits-asserties en de ruwe per-engine JSON-resultaten leven in benchmarks/bench_oss_engines.py en benchmarks/results_oss/ bij commit 250dbb5. Voor het gecompileerde/GPU-uiteinde van de ladder, zie de snelheidsladder van backtest-engines en de IPC-belasting-analyse.

Disclaimer: De informatie in dit artikel is uitsluitend bedoeld voor educatieve en informatieve doeleinden en vormt geen financieel, beleggings- of handelsadvies. Het handelen in cryptovaluta brengt een aanzienlijk risico op verlies met zich mee.

Auteurs

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

Blijf de markt voor

Abonneer je op onze nieuwsbrief voor exclusieve AI-handelsinzichten, marktanalyses en platformupdates.

We respecteren je privacy. Je kunt je op elk moment afmelden.