26 september 2026 · research

Vi försökte återskapa en strategis backtest. Här började siffrorna glida.

Vi försökte återskapa en strategis backtest. Här började siffrorna glida.

Vi körde om ett sparat backtest och fick en annan aktiekurva. Samma strategikod, samma datumintervall, samma symbol. Slutbalansen skilde sig med 1.8%, och tre affärer hade flyttats en stapel.

Det räcker för att göra det svårt att lita på ett forskningsresultat. Om en kollega, en framtida version av dig själv eller en tjänst för pappershandel inte kan återskapa körningen går det inte att avgöra om en ändring förbättrade strategin eller bara ändrade experimentet. Så här gick vi till väga för att hitta avvikelsen.

Dag 1: Vi skrev ner vad ”samma körning” innebar

Vårt första misstag var att se strategifilen som hela experimentet. Det var den inte. Körningen berodde också på indata, motorversion, kalender, instrumentmetadata och exekveringsinställningar. Koden beskrev bara en del av beräkningen.

Vi skapade ett körningsmanifest innan vi ändrade något. Där noterade vi strategins commit, identifierare för databilder, datumintervall, handelsplats, avgiftsschema, finansieringskälla, fyllnadsmodell och programvaruversioner. Vi sparade också de resulterande ordrarna och avsluten, eftersom en aktiekurva ensam inte visar var två körningar först började skilja sig åt.

ArtefaktVad som ska dokumenterasVarför det spelar roll
MarknadsdataBild-ID, schemaversion, justeringarDataleverantörer korrigerar historik och reviderar bolagshändelser
ExekveringAvgiftsnivå, finansieringsserie, inställningar för fyllnad och marknadspåverkanStandardvärden och kontantaganden ändrar resultatet
KörtidKodens commit samt motor- och beroendeversionerBibliotek kan ändra ordningsföljd, avrundning eller indikatorer
UtdataOrdrar, avslut, positioner och mätvärdenVisar var körningarna börjar skilja sig åt

Dag 2: Vi jämförde affärerna, inte Sharpe

Sammanfattningsmåtten distraherade oss. Båda körningarna hade nästan samma Sharpe, men avslutsloggarna visade att den första avvikelsen uppstod vid en finansieringsavräkning. I den ena körningen debiterades räntan på positionen som var öppen vid avräkningens tidsstämpel; i den andra användes positionen efter ombalanseringen vid samma tidsstämpel.

Strategikoden hade inte ändrats. Det hade däremot motorns händelseordning. En liten versionsuppdatering hade gjort ordningsföljden tydlig, där den tidigare berodde på hur två händelser råkade sorteras.

Vi ändrade körningskontraktet så att ordningsföljden framgår: tillämpa finansieringen på positionen som följer med in i avräkningen och behandla sedan strategibesluten för den tidsstämpeln. Exakt konvention kan variera mellan handelsplatser och motorer. Felet är att låta den vara underförstådd.

Dag 3: En ”samma” datafil visade sig vara annorlunda

När händelseordningen var fastställd återstod avvikelserna främst i några aktieaffärer. Dataleverantören hade korrigerat en historisk splitjustering. Vår fil hade samma namn och antal rader som tidigare, vilket hade fått den att verka oförändrad.

Nu skapar vi fingeravtryck för varje oföränderlig databild och sparar justeringspolicyn tillsammans med den. En hash visar om byten har ändrats, men inte varför. Därför innehåller manifestet även källa, hämtningstid och version av transformationen. För data som revideras är de uppgifterna en del av resultatet.

Ett reproducerbart backtest måste kunna svara på frågan: ”Vilken version av det förflutna såg det?”

Dag 4: Vi hittade ett diskret standardvärde

Den sista skillnaden var en maker-avgift som satts till 0 eftersom strategins konfiguration saknade fältet. En nyare motor tillämpade kontots standardavgift. Det enda standardvärdet ändrade marginalaffärerna tillräckligt för att förklara merparten av skillnaden i slutbalans.

Vi gjorde inställningar med ekonomisk betydelse explicita och lät motorn skriva ut den slutliga konfigurationen i körningsloggen. Standardvärden är praktiska när man utforskar. De är svaga belägg när man jämför resultat över tid.

3källor till avvikelser hittade
1.8%ursprunglig skillnad i slutbalans
0värde i enbart matchande Sharpe

Det här skulle vi hoppa över nästa gång

Vi lade en halv dag på att jämföra sammanlagda mått innan vi tittade på det första avslut som skilde sig. Börja inte där. Sortera båda händelseloggarna efter tidsstämpel och jämför den första avvikelsen; senare skillnader följer ofta av den första orsaken.

Vi skulle också släppa tanken att en containeravbild i sig gör en körning reproducerbar. Den låser fast stora delar av programvarumiljön, men inte en extern datafil, ett avgiftsschema som hämtas vid körning eller en dataleverantörs reviderade historik.

När ett backtest ändras ska du spara båda körningsmanifesten och loggarna och sedan åtgärda en avvikelsekälla i taget. Det användbara resultatet är inte bara en kurva som går att återskapa. Det är en dokumentation som förklarar vilken data och vilka antaganden som gav resultatet, och varför nästa körning kan skilja sig.

reproducerbarhetbacktestingdatateknikpappershandel
← Alla inlägg