Vi genstartede en paper-strategi midt i en handel og fik en anden ordresekvens end den, den havde produceret før genstarten. Samme markedsdata, samme strategiversion, samme kontosaldo. Signalet blev beregnet ens. Strategiens hukommelse om positionen og de ventende ordrer gjorde ikke.
Vi sporede uoverensstemmelsen gennem en dags replayarbejde. Den nyttige læring var enkel: En genstart er en markedshændelse for din software. Hvis du ikke tester, hvordan strategien genskaber sin tilstand, kan en ren backtest skjule et paper-system, der glemmer, hvad det ejer.
09:10 — Vi valgte en kedelig position
Teststrategien handlede en likvid perpetual: Den gik long, når et kort glidende gennemsnit krydsede over et længere, og lukkede derefter positionen ved det modsatte kryds. Vi startede replayen med en lille, allerede åben position og en reduce-only-limitordre, der lå klar til at reducere den. Så gendannelsen skulle rekonstruere 2 ting: hvad vi ejede, og hvad vi allerede havde bedt handelsplatformen om at gøre.
Ved kontrolpunktet havde kontoen 0.04 kontrakter. Ordren var på 0.01. Strategiprocessen havde cachet begge værdier i hukommelsen, men dens opstartsrutine hentede kun positionen. Den antog, at den cachede ordre var væk.
09:25 — Den første dubletordre dukkede op
Efter genstarten så strategien den eksisterende long-position, kørte sin signallogik og indsendte endnu en trimordre på 0.01. Paper-platformen havde nu 2 aktive ordrer. Ingen af dem var i sig selv en dårlig ordre. Tilsammen kunne de sælge dobbelt så meget som tilsigtet, hvis begge blev udført.
Først gav vi signalløkken skylden. Det var ikke løkken, men det ufuldstændige øjebliksbillede. Strategien spurgte: »Hvilken position har jeg?«, men spurgte aldrig: »Hvilke ordrer er stadig aktive?«
| Tilstand efter genstart | Processens opfattelse | Hvad kontoen faktisk havde |
|---|---|---|
| Position | Long 0.04 | Long 0.04 |
| Åbne trimordrer | Ingen | 2 på hver 0.01 |
| Tilsigtet eksponering efter én udførelse | Long 0.03 | Kunne ende på long 0.02 |
10:00 — Vi rettede gendannelsen og fandt derefter en timingkant
Vi ændrede opstarten, så tilstanden blev genskabt ud fra kontoens positioner og åbne ordrer, før nye beslutninger blev aktiveret. Det fjernede dubletten. Derefter gjorde vi afbrydelsen mindre velordnet: En ordre blev udført, mens strategien var offline, og udførelsesmeddelelsen kom efter genopkoblingen.
Kontoens øjebliksbillede afspejlede allerede udførelsen. Den forsinkede meddelelse reducerede derefter den lokale position endnu en gang. I nogle sekunder troede strategien, at den havde 0.02 kontrakter, mens kontoen havde 0.03. Dens næste rebalancering byggede på en fiktiv mangel.
Vi tilføjede afstemningsregler: Brug kontoens øjebliksbillede som udgangspunkt, brug hændelsesidentifikatorer til at ignorere udførelser, der allerede er afspejlet dér, og indsend ikke ordrer, før den indledende synkronisering er færdig. En meddelelse kan komme sent eller to gange. Gendannelsen skal kunne håndtere begge dele.
13:40 — Replayen fangede en diskret uoverensstemmelse
Vi afspillede det samme prisforløb for det oprindelige og det genstartede forløb. En sammenligning af den endelige P&L ville have overset problemet: De 2 versioner endte med samme position, efter at markedet vendte. Sammenligningen af ordrehændelser afslørede det.
Vi loggede hver beslutning sammen med den tilstand, den læste: position, åbne ordrer, senest behandlede udførelsesidentifikator, signalværdi og strategiversion. Så kunne den første afvigende hændelse forklares. Det ene forløb så en aktiv ordre; det andet så en tom liste. Senere anvendte det ene en udførelse 2 gange.
En identisk slutsaldo beviser ikke, at forløbene var ens. Sammenlign sekvensen af beslutninger og ordrer, især omkring grænser for gendannelse.
16:20 — Hvad vi ville gøre anderledes næste gang
Vi havde brugt for lang tid på at afspille prisdata, før vi undersøgte kontoens tilstandsovergange. Næste gang ville vi først indføre fejltilfældene og holde markedsforløbet næsten fladt. Så er softwarefejlen let at få øje på, uden at en volatil bevægelse slører diagnosen.
- Genstart med en position og en delvist udført ordre.
- Afbryd forbindelsen efter indsendelse, og opret så forbindelse igen, før udførelsesmeddelelsen kommer.
- Send den samme udførelseshændelse 2 gange, og kontrollér, at den kun ændrer tilstanden én gang.
- Blokér nye ordrer, indtil både positioner og åbne ordrer er afstemt.
- Sammenlign beslutnings- og ordrelogfiler for uafbrudte og genstartede forløb.
Vi lærte også at gemme et gendannelsesøjebliksbillede sammen med strategiversionen og hændelsesloggen. Så kunne fejlen genskabes på minutter i stedet for at være afhængig af, at nogen kunne huske den præcise genopkoblingssekvens.
En paper-strategi, der kun opfører sig korrekt, mens processen kører, har ikke bestået en fuld generalprøve. Genstart den midt i en position, lad udførelser komme sent, og undersøg hver ordre, den sender bagefter. Pointen er ikke at bevise, at den aldrig fejler. Det handler om at gøre gendannelsen synlig, før paper-kontoen må lære dig den samme lektie på den hårde måde.
← Alle indlæg


