4. september 2026 · backtesting

Dit stop og dit mål lå begge i samme candle. Hvilket valgte din backtest?

Dit stop og dit mål lå begge i samme candle. Hvilket valgte din backtest?

Long fra 84,120. Stop ved 84,036, mål ved 84,271. Den næste candles minut åbner: open 84,118, high 84,290, low 84,010, close 84,240.

Begge niveauer ligger i den candle. Stoppen blev ramt, og målet blev ramt, men de fire OHLC-tal indeholder præcis nul information om, hvad der skete først. Din backtest returnerede alligevel et tal. Et sted i løkken traf en linje beslutningen — sandsynligvis en linje, du ikke opfattede som en modelantagelse, da du skrev den.

Det her er den største enkeltkilde til falsk performance, jeg ser i strategier med kort tidshorisont, større end gebyrer og slippage, fordi det ikke ligner en antagelse. Det ligner VVS-arbejde.

Forkert måde 1: lad if-kæden bestemme

Det sædvanlige mønster:

if bar.high >= target:
    exit(target, "tp")
elif bar.low <= stop:
    exit(stop, "sl")

Ingen valgte "mål afvikles før stop". Måltjekket blev bare skrevet først, fordi det er den ønskede vej, og det var den, du tænkte på. Byt om på de to grene, og aktiekurven ændrer sig; alene det burde fortælle dig, at strategiens P&L delvist afhænger af din editor.

Jeg kørte en helt almindelig mean-reversion-scalper på BTCUSDT perp over tre måneder med 1-minute candles, 4,812 handler, stop 0.10% og mål 0.18% fra indgangen. Samme signaler, samme gebyrer, kun tie-break-konventionen ændret.

12.6%af handlerne ramte begge niveauer i én candle
2.31Sharpe, mål afvikles først
0.18Sharpe, stop afvikles først
0.94Sharpe, afgjort med 1-sekunds-candles

En ottendedel af handlerne bærer hele resultatet. Sådan fungerer et tæt stop-mål-par rent matematisk: De tvetydige handler er dem, hvor prisen gik begge veje, og det gælder de fleste af de interessante tilfælde. Hver af dem er værd hele afstanden fra stop til mål, afhængigt af møntkastet. 12.6% af handlerne × et spænd på 0.28% giver 3.5% af den nominelle bruttoværdi i omsætning pr. sampleenhed, langt mere end strategiens reelle edge.

Raten afhænger af candles størrelse i forhold til afstanden mellem dine niveauer, og den stiger hurtigt, når candles bliver længere. Samme stop og mål, samme signaler, genberegnet med andre intervaller:

Candlens intervalHandler med begge niveauer i én candleSharpe (mål først)
1s0.3%0.91
1m12.6%2.31
5m34%3.60
15m49%4.42
1h71%5.88

Se på, hvad tabellen faktisk siger. Grovere candles forbedrede backtesten. Enhver researchers instinkt siger, at time-candles er det forsigtige valg: mindre støj, mindre overtilpasning til markedsmikrostruktur. Men med en intrabar-konvention, der afgør sagen til din fordel, er en grovere candle bare en større kasse, hvor du kan antage, at du var heldig. Med 1h-candles er syv ud af ti handler ren konvention. Den backtest afprøver ikke en strategi; den afprøver rækkefølgen af to `if`-sætninger 3,400 gange.

Forkert måde 2: antag, at stoppen blev udført til stopprisen

Lad os sige, at du retter rækkefølgen. Stoppen afvikles først, du bogfører et tab på præcis 0.10% plus taker-gebyr, og du føler dig grundig. Der er stadig to ting galt.

Det første er, at et stop er en trigger, ikke en udførelse. På Binance USDⓈ-M bliver en STOP_MARKET-ordre til en markedsordre, så snart triggerbetingelsen er opfyldt, og den tager derefter, hvad der ligger i ordrebogen. I et roligt minut betyder det en eller to ticks i slippage. I det minut, der faktisk udløste din stopordre — det med en candlekrop på 40 point og en likvidationskaskade under sig — er ordrebogen tynd netop på den side, du handler imod. I mit sample var den mediane udførelsespris 1.4 bps dårligere end triggerprisen, når jeg matchede stoptriggere med tickdata, og 95-percentilen var 11 bps. Med et stop på 10 bps koster halen dig en ekstra tiendedel af den risiko, du troede, du havde defineret.

Det andet er mere subtilt og specifikt for perps: hvilken pris der udløser den. Binance bruger som standard mark price til stopordrer, og mark price beregnes ud fra indekset plus et udjævnet basis, ikke ud fra den seneste handel på den pågældende børs. Din OHLC-serie er baseret på seneste pris. Det er to forskellige serier, og forskellen mellem dem er størst netop under de hændelser, der udløser stopordrer.

Seneste pris (dine klines)Mark price (standardtrigger)
Kildehandler på denne børsindeks fra flere børser + basis
Wick-adfærdhele udsvingetkraftigt dæmpet
Typisk afvigelse1–3 bps i roligt marked, 20–35 bps i et kaskademinut
Konsekvens for backtestenstop, der blev udløst uden grund, og omvendt

Så en wick på 25 bps i serien med seneste priser stopper dig ud i backtesten, mens den live mark price aldrig kom inden for 10 bps af din trigger. Eller det modsatte, den dag indekset flytter sig, og din børs halter bagefter. Hvis du indstiller workingType til CONTRACT_PRICE, matcher du i det mindste live-adfærden med dine data. Det er som regel det rigtige valg for en researcher, for en ærlig simulering af en mark price-trigger kræver, at du fører en ekstra serie gennem hele din fill engine.

Den version husker jeg bedst: En på vores hold "forbedrede" en strategi ved at flytte take-profit fra 0.18% til 0.21%. Sharpe steg fra 2.3 til 3.1. Ingen ny edge. Målet var bare flyttet uden for den tætte del af 1-minuts-wickfordelingen, så færre handler endte i den tvetydige gruppe, hvor koden stiltiende gav dem sejren. De havde optimeret tie-breakeren.

Forkert måde 3: antag altid det værste, og kald det forsigtigt

Den umiddelbare løsning er pessimisme. Hvis begge niveauer rammes, så tag stoppen. Færdig, ingen optimisme, send den ud.

Det plejede jeg at gøre. Det er bedre end alternativet, men stadig forkert af to grunde.

Det slår strategier ihjel, som ellers fungerer fint. En pessimistisk afvikling på 12.6% af handlerne kostede denne strategi 2.1 Sharpe-point sammenlignet med 0.94, når udførelsen blev afgjort ud fra ticks. Hvis det reelle tal er 0.94, og din konvention viser 0.18, smider du idéen væk og går i gang med noget værre. Forsigtighed, der rammer 2 Sharpe-point forkert, er ikke forsigtighed; det er støj med en moralsk positur.

Endnu værre: Det forvansker optimeringen. Giv en parametersøgning et pessimistisk tie-break, og optimeringsalgoritmen lærer at undgå tvetydighed, fordi tvetydighed nu er en ren straf. Den bevæger sig mod brede stop og nærliggende mål eller mod langsomme candles, hvor de to niveauer sjældent falder sammen, og viser dig parametre, der er valgt ud fra din udførelseskonvention i stedet for markedet. Det er samme fejl som med den generøse konvention, bare med modsat fortegn, og lige så usynlig i tearsheetet.

Tommelfingerregel, som vi bruger først: Hvis stop_distance + target_distance er mindre end 75-percentilens spænd for dit candleinterval, fylder din intrabar-antagelse mere i P&L end dit signal. Beregn begge tal. Det tager fire linjer og har afsluttet flere strategigennemgange end nogen anden enkelt kontrol.

Det, der faktisk virker

Forløbet inden i candlen er data. Hent dem, eller afgræns det, du ikke kan hente.

  1. Afgør udfaldet med den fineste serie, du har. Binance aggTrades for de relevante minutter består af nogle få hundrede rækker og afgør spørgsmålet direkte: Hvilket niveau blev ramt først, og til hvilken pris blev ordren udført under bevægelsen? Du behøver ikke tickdata til hele backtesten, kun til de tvetydige candles. I mit sample var det 606 minutter ud af 129,600. Det er en lille download, ikke et infrastrukturprojekt.
  2. Hvis tickdata ikke er tilgængelige, så gå et eller to timeframe-niveauer ned, kun når udfaldet skal afgøres. Signaler på 15m, exits afgjort med 1s- eller 1m-candles. Tvetydigheden falder fra 49% til en brøkdel af en procent, og den resterende del er lille nok til, at man ærligt kan se bort fra den.
  3. Rapportér altid spændet. Kør hver backtest to gange med optimistisk og pessimistisk afvikling, og vis begge Sharpe-tal ved siden af det afgjorte resultat. Det spænd er din intrabar-usikkerhed, og det hører hjemme i tearsheetet sammen med konfidensintervallet for selve Sharpe-tallet. Når spændet er 0.2–2.3, er ingen konklusion inden for det reelt underbygget.
  4. Følg tvetydighedsraten som en central måling. Vores står øverst på hvert strategikort ved siden af antal handler og omsætning. En rate over cirka 5% betyder, at det er exitlogikken, ikke entrylogikken, der bliver testet.
  5. Modellér triggeren adskilt fra udførelsen. Brug den prisserie, børsen faktisk benytter, til triggeren. Sæt udførelsesprisen til triggerprisen plus en slippageværdi trukket fra en fordeling kalibreret ud fra tickdata, ikke til triggerprisen.

Aktier har samme problem i en anden indpakning. Et stop ved 62.00 på en aktie, der åbner med et gap ved 58.40 efter natten, bliver ikke udført ved 62.00; det bliver udført et sted under åbningsprisen. En backtest med daily-candles, der bogfører −$0.00 i slippage ved gap gennem stopprisen, fortæller dig gerne, at et stop-loss-overlay forbedrede dit drawdown. Det gjorde det ikke. Det blev bare aldrig testet på de dage, der betyder noget. Handelsstop skaber samme problem: Genåbningsauktionen afgør, hvor din stopordre faktisk bliver gennemført, til en pris, som candlens low aldrig viser.

Intet af det her er eksotisk. Det handler om at forstå, at en candle er et resumé, og at en stop-mål-strategi satser på rækkefølgen af hændelser, som resuméet har smidt væk. Når papertrading-motoren omsider kører strategien mod live-data, har markedet en mening om den rækkefølge, og det har aldrig været optaget af, hvilken gren af if-sætningen du skrev først.

intrabar-udførelserstopordrerbacktestingkryptofuturesmarkedsmikrostruktur
← Alle indlæg