26. september 2026 · research

Vi forsøgte at genskabe en strategis backtest. Her opstod afvigelserne i tallene.

Vi forsøgte at genskabe en strategis backtest. Her opstod afvigelserne i tallene.

Vi kørte en gemt backtest igen og fik en anden egenkapitalkurve. Samme strategikode, samme datointerval, samme symbol. Slutsaldoen afveg med 1.8%, og tre handler var flyttet én bar.

Det er nok til at gøre et forskningsresultat svært at stole på. Hvis en kollega, en fremtidig version af dig selv eller en paper trading-tjeneste ikke kan genskabe kørslen, kan du ikke afgøre, om en ændring forbedrede strategien eller blot ændrede eksperimentet. Her er forløbet, vi fulgte for at finde afvigelserne.

Dag 1: Vi skrev ned, hvad “samme kørsel” betød

Vores første fejl var at behandle en strategifil som selve eksperimentet. Det var den ikke. Kørslen afhang også af inputdataene, motorversionen, kalenderen, instrumentmetadataene og eksekveringsindstillingerne. Koden beskrev kun én del af beregningen.

Vi lavede et kørselsmanifest, før vi ændrede noget. Det registrerede strategiens commit, identifikatorer for datasnapshots, datointerval, handelssted, gebyrplan, fundingkilde, fillmodel og softwareversioner. Vi gemte også de resulterende ordrer og fills, for en egenkapitalkurve alene kan ikke vise, hvor to kørsler først afveg fra hinanden.

ArtefaktHvad der skal registreresHvorfor det er vigtigt
MarkedsdataSnapshot-ID, skemaversion, justeringerDataleverandører retter historikken og reviderer selskabshændelser
EksekveringGebyrniveau, fundingserie, indstillinger for fills og markedspåvirkningStandardindstillinger og kontoantagelser ændrer resultaterne
KørselstidKode-commit, motor- og afhængighedsversionerBiblioteker kan ændre rækkefølge, afrunding eller indikatorer
OutputOrdrer, fills, positioner og målepunkterViser, hvor kørslerne begynder at være uenige

Dag 2: Vi sammenlignede handlerne, ikke Sharpe

Oversigtsmålingerne førte os på afveje. Begge kørsler havde næsten samme Sharpe, men deres fill-logfiler viste det første mismatch ved en fundingafregning. Den ene kørsel opkrævede satsen på positionen, der var åben på afregningstidspunktet; den anden brugte positionen efter rebalanceringen på det tidspunkt.

Strategikoden var ikke ændret. Motorens hændelsesrækkefølge var. En mindre versionsopdatering gjorde rækkefølgen eksplicit, hvor den før afhang af, hvordan to hændelser tilfældigvis blev sorteret.

Vi rettede kørselskontrakten, så rækkefølgen stod klart: Anvend funding på positionen, der videreføres til afregningen, og behandl derefter strategibeslutningerne for det tidspunkt. Den præcise konvention kan variere fra handelssted til motor. Fejlen er at lade den være implicit.

Dag 3: En “samme” datafil viste sig at være anderledes

Efter at have fastlagt hændelsesrækkefølgen var de resterende mismatch koncentreret omkring nogle få aktiehandler. Dataleverandøren havde rettet en historisk splitjustering. Vores fil havde samme navn og antal rækker som før, så den så uændret ud.

Nu laver vi fingeraftryk af hvert uforanderligt datasnapshot og gemmer justeringspolitikken sammen med det. En hash fortæller os, om bytes er ændret; den forklarer ikke hvorfor. Derfor indeholder manifestet også kilde, hentetidspunkt og transformationsversion. For data, der bliver revideret, er disse oplysninger en del af resultatet.

En reproducerbar backtest kræver svar på spørgsmålet: “Hvilken version af fortiden så den?”

Dag 4: Vi fandt en ubemærket standardindstilling

Den sidste forskel skyldtes et maker-gebyr, der var sat til nul, fordi strategiens konfiguration ikke angav feltet. En nyere motor anvendte kontoens standardgebyr. Den ene standardindstilling ændrede marginalhandlerne nok til at forklare det meste af forskellen i slutsaldoen.

Vi gjorde indstillinger med økonomisk betydning eksplicitte og fik motoren til at udskrive den endelige konfiguration i kørselsregistreringen. Standardindstillinger er praktiske, når man udforsker. De er et dårligt grundlag, når man sammenligner resultater over tid.

3årsager til afvigelser fundet
1.8%oprindelig forskel i slutsaldo
0værdi af alene at matche Sharpe

Hvad vi ville springe over næste gang

Vi brugte en halv dag på at sammenligne samlede målinger, før vi kiggede på det første afvigende fill. Start ikke dér. Sortér begge hændelseslogfiler efter tidsstempel, og sammenlign den første afvigelse; senere forskelle følger ofte af den ene årsag.

Vi ville også droppe idéen om, at et containerimage i sig selv gør en kørsel reproducerbar. Det fastlåser en stor del af softwaremiljøet, men ikke en ekstern datafil, en gebyrplan hentet under kørslen eller en dataleverandørs reviderede historik.

Når en backtest ændrer sig, skal du gemme begge kørselsmanifester og logfiler og derefter rette én årsag til afvigelse ad gangen. Det nyttige resultat er ikke blot en kurve, du kan køre igen. Det er en registrering, der forklarer, hvilke data og antagelser der skabte den, og hvorfor den næste kørsel kan afvige.

reproducerbarhedbacktestingdatateknikpaper trading
← Alle indlæg