Vi kjørte en lagret backtest på nytt og fikk en annen egenkapitalkurve. Samme strategikode, samme datoperiode, samme symbol. Sluttsaldoen avvek med 1.8%, og tre handler hadde flyttet seg én bar.
Det er nok til at det blir vanskelig å stole på et forskningsresultat. Hvis en kollega, en framtidig versjon av deg selv eller en paper trading-tjeneste ikke kan gjenskape kjøringen, kan du ikke vite om en endring forbedret strategien eller bare endret forsøket. Slik gikk vi fram for å finne avvikene.
Dag 1: Vi skrev ned hva «samme kjøring» betydde
Den første feilen vår var å behandle en strategifil som selve forsøket. Det var den ikke. Kjøringen var også avhengig av inndataene, motorversjonen, kalenderen, instrumentmetadataene og innstillingene for utførelse. Koden beskrev bare én del av beregningen.
Vi laget et kjøringsmanifest før vi endret noe. Der registrerte vi strategiens commit, identifikatorer for øyeblikksbilder av dataene, datoperiode, handelsplass, gebyrplan, finansieringskilde, fyllmodell og programvareversjoner. Vi lagret også ordre og fyllinger fra kjøringen, siden en egenkapitalkurve alene ikke viser hvor to kjøringer først skilte lag.
| Artefakt | Hva som skal registreres | Hvorfor det er viktig |
|---|---|---|
| Markedsdata | Øyeblikksbilde-ID, skjemaversjon, justeringer | Dataleverandører retter historikk og reviderer selskapshendelser |
| Utførelse | Gebyrnivå, finansieringsserie, innstillinger for fylling og markedspåvirkning | Standardinnstillinger og kontoforutsetninger endrer resultatene |
| Kjøremiljø | Kodecommit, motor- og avhengighetsversjoner | Biblioteker kan endre rekkefølge, avrunding eller indikatorer |
| Resultat | Ordre, fyllinger, posisjoner og måltall | Viser hvor kjøringene begynner å avvike |
Dag 2: Vi sammenlignet handlene, ikke Sharpe
Sammendragsmåltallene forstyrret oss. Begge kjøringene hadde nesten samme Sharpe, men fyllingsloggene viste at det første avviket oppsto ved en finansieringsoppgjøring. I den ene kjøringen ble satsen belastet posisjonen som var åpen på oppgjørstidspunktet. I den andre ble den belastet posisjonen etter rebalanseringen på samme tidspunkt.
Strategikoden var uendret. Rekkefølgen på hendelsene i motoren var endret. En liten versjonsoppdatering hadde gjort rekkefølgen eksplisitt, der den tidligere avhang av hvordan to hendelser tilfeldigvis ble sortert.
Vi oppdaterte kjøringskontrakten slik at rekkefølgen ble fastslått: Først påføres finansieringen posisjonen som ble holdt fram til oppgjøret, deretter behandles strategiavgjørelsene for det aktuelle tidspunktet. Den nøyaktige konvensjonen kan variere mellom handelsplasser og motorer. Å la den være implisitt er feilen.
Dag 3: En «samme» datafil viste seg å være annerledes
Etter at vi hadde fastsatt hendelsesrekkefølgen, gjensto avvikene i noen få aksjehandler. Dataleverandøren hadde korrigert en historisk splitjustering. Filen vår hadde samme navn og radantall som før, så den så uendret ut.
Nå lager vi et fingeravtrykk for hvert uforanderlige øyeblikksbilde av dataene og lagrer justeringspraksisen sammen med det. En hash viser om byteinnholdet er endret, men forklarer ikke hvorfor. Derfor inneholder manifestet også kilde, hentetidspunkt og versjon av datatransformasjonen. For data som revideres, er disse detaljene en del av resultatet.
En reproduserbar backtest må kunne svare på spørsmålet: «Hvilken versjon av fortiden så den?»
Dag 4: Vi fant én stille standardinnstilling
Det siste avviket skyldtes et maker-gebyr som var satt til null fordi strategiens konfigurasjon manglet feltet. En nyere motor brukte standardgebyret for kontoen. Denne ene standardinnstillingen endret de marginale handlene nok til å forklare det meste av forskjellen i sluttsaldo.
Vi gjorde innstillinger med økonomisk betydning eksplisitte og lot motoren skrive den effektive konfigurasjonen inn i kjøringsloggen. Standardinnstillinger er praktiske når man utforsker. De er svakt grunnlag når man sammenligner resultater over tid.
Dette ville vi droppet neste gang
Vi brukte en halv dag på å sammenligne samlede måltall før vi så på den første fyllingen som var ulik. Ikke begynn der. Sorter begge hendelsesloggene etter tidsstempel og sammenlign det første avviket. Senere forskjeller følger ofte av den ene årsaken.
Vi ville også droppet tanken om at et containerbilde alene gjør en kjøring reproduserbar. Det låser mye av programvaremiljøet, men ikke en ekstern datafil, en gebyrplan som hentes ved kjøring eller en dataleverandørs reviderte historikk.
Når en backtest endrer seg, bør du ta vare på begge kjøringsmanifestene og loggene og deretter rette én kilde til avvik om gangen. Det nyttige resultatet er ikke bare en kurve du kan gjenskape. Det er en logg som forklarer hvilke data og forutsetninger som ga den, og hvorfor neste kjøring kan bli annerledes.
← Alle innlegg


