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.
| Artefakt | Hvad der skal registreres | Hvorfor det er vigtigt |
|---|---|---|
| Markedsdata | Snapshot-ID, skemaversion, justeringer | Dataleverandører retter historikken og reviderer selskabshændelser |
| Eksekvering | Gebyrniveau, fundingserie, indstillinger for fills og markedspåvirkning | Standardindstillinger og kontoantagelser ændrer resultaterne |
| Kørselstid | Kode-commit, motor- og afhængighedsversioner | Biblioteker kan ændre rækkefølge, afrunding eller indikatorer |
| Output | Ordrer, fills, positioner og målepunkter | Viser, 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.
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.
← Alle indlæg


