Kl. 10:07 en tirsdag markerede vores researchagent et kortsigtet bullish setup i en indeks-ETF. Det underliggende signal var helt almindeligt: Kursen var kommet tilbage over sit 20-bars gennemsnit, den realiserede volatilitet var aftagende, og der var planlagt en makroøkonomisk offentliggørelse i næste session. Den foreslåede handel var et call spread med cirka to uger til udløb.
Backtesten kunne lide det. Paperstrategien afgav ikke ordren. Da vi sammenlignede ordrerne, stemte signalernes tidsstempler overens, og de underliggende priser lå inden for få cent af hinanden. Kontrakterne var forskellige: Backtesten havde købt fredagsudløbet, der så bedst ud i den historiske optionskæde; paperprocessen valgte det næste noterede udløb, en uge senere.
Forskellen skyldtes et kalenderjoin. Vi havde behandlet »dage til udløb« som en egenskab ved en dato og derefter valgt den kontrakt, hvis udløbsdato lå nærmest målet. I de historiske data indeholdt udløbskalenderen en ugentlig notering, som var blevet tilføjet efter den dato, vi testede. Backtesten havde brugt en fremtidig kontrakt på en måde, der virkede som en harmløs genvej.
Først mistænkte vi en tidszonefejl. Udløbsdatoer for optioner er særligt gode til at fremkalde den slags: Handelssessionen slutter i New York, tidsstempler fra børser kan være gemt i UTC, og en dato ved midnat kan forskydes, hvis en pipeline konverterer den skødesløst. Vi undersøgte det. Tidsstemplerne var korrekte. Kontrakten fandtes ganske enkelt ikke i det historiske univers den tirsdag.
Det var den første lære: En historisk optionskæde skal have et univers af noterede kontrakter, som afspejler det konkrete tidspunkt. En kontrakts udløbsdato fortæller dig, hvornår den udløber. Den fortæller dig ikke, hvornår den blev noteret, hvornår den kunne handles, eller om dit system kunne have valgt den på det tidspunkt.
Vi byggede udvælgelsestrinnet om, så det kun brugte de oplysninger, der var tilgængelige på beslutningstidspunktet. For hver kandidatkontrakt krævede vi et noteringstidspunkt, der ikke lå efter beslutningen, en gyldig kurs, som ikke var ældre end vores friskhedsgrænse, og en handelssession, hvor strategien kunne afgive en ordre. Vi gemte også kandidatsættet, før vi rangerede det. Det sidste var vigtigt, fordi den gamle backtest kun gemte vinderen; vi kunne ikke se, om den havde sammenlignet fem kvalificerede kontrakter eller stiltiende valgt fra en kalender, der endnu ikke fandtes.
Dernæst undersøgte vi den kurs, der blev brugt til at prisfastsætte spreadet. Backtesten havde udtaget den seneste kurs for hvert ben uafhængigt af det andet. Købsprisen for call-optionen var fra 10:06:58; salgsprisen for den korte call-option var fra 10:04:11. Begge var teknisk set friske nok efter den overordnede datapolitik, men markedet havde flyttet sig imellem dem. En middelkurs for et spread, beregnet ud fra asynkrone ben, kan beskrive et marked, der aldrig har eksisteret.
Vi ændrede kursreglen, så tidsstemplerne for begge ben højst måtte være to sekunder fra hinanden, og afviste krydsede markeder eller markeder med nul størrelse. Det reducerede antallet af brugbare historiske observationer. Til gengæld blev de resterende markeringer mere troværdige. Når der ikke fandtes synkroniserede kurser, markerede vi kandidaten som utilgængelig i stedet for at udfylde hullet med en forældet pris.
Så kom rangeringsreglen. Agenten havde valgt udløbsdatoen med den laveste noterede spreadomkostning. Det lyder fornuftigt, indtil spreadet kun er én kontrakt bred, og kursen er forældet. Vi gjorde målet eksplicit: Maksimér det forventede afkast pr. enhed defineret risiko blandt kontrakter, der opfylder minimumskravene til kursstørrelse, friskhed og notering. I dette eksempel havde det nærmeste kvalificerede udløb en noteret spreadbredde på $0.42 på tværs af de to ben, mens det senere udløb havde en bredde på $0.31. Det valg, der så billigere ud, havde også kun én kontrakt vist på det korte ben.
| Kandidat | Var udløbet tilgængeligt på det tidspunkt? | Kursens alder | Vist størrelse på det korte ben | Beslutning |
|---|---|---|---|---|
| Ugentligt fredagsudløb | Nej | Ikke relevant | Ikke relevant | Udeluk |
| Følgende fredag | Ja | 1.4 sekunder | 18 kontrakter | Kvalificeret |
| Tredje fredag | Ja | 9.8 sekunder | 1 kontrakt | Udeluk som forældet og tyndt |
Vi afspillede hændelsen igen med de regler. Backtesten valgte nu samme udløb som paper og registrerede, hvorfor de andre kontrakter blev afvist. Spreadets indgangspris var $0.11 dårligere end det oprindelige historiske fill. Det betød ikke, at strategien var værdiløs; det betød, at det gamle performance-tal omfattede et handelsvalg, som liveprocessen ikke kunne reproducere.
En lille gene: Børsernes symbolformater får fejl i udløbsdatoer til at ligne datakvalitetsfejl. Vi fandt en ugentlig kode hos en dataleverandør, som afveg fra den månedlige kode med ét tegn. Jeg gemmer stadig det rå symbol sammen med den normaliserede kontraktnøgle i auditloggene. Det er ikke elegant, men det har forhindret os i at stole for hurtigt på et pænt join.
Hvis du bygger en optionsbacktest, så gem hele kandidatsættet ved hver beslutning sammen med hver kontrakts noteringsstatus, kursernes tidsstempler, størrelser og afvisningsgrund. Gør derefter kontraktvalget til en deterministisk funktion af dette øjebliksbillede. Valget af udløb er en del af strategien. Hvis backtesten kan se en kontrakt, som paper ikke kunne, begynder sammenligningen af signalerne først, efter at svaret allerede har ændret sig.
← Alle indlæg


