Lang posisjon fra 84,120. Stop-loss på 84,036, kursmål på 84,271. Candlen for neste minutt kommer inn: åpning 84,118, høyeste 84,290, laveste 84,010, slutt 84,240.
Begge nivåene ligger innenfor denne candlen. Stop-loss-nivået ble nådd, og kursmålet ble nådd, men de fire OHLC-tallene inneholder nøyaktig null informasjon om hva som skjedde først. Backtesten din ga likevel et tall. Et sted i løkken tok en linje en avgjørelse — sannsynligvis en linje du ikke tenkte på som en modelleringsantakelse da du skrev den.
Dette er den største enkeltkilden til oppdiktet avkastning jeg ser i strategier med kort horisont, foran gebyrer og slippage, fordi det ikke ser ut som en antakelse. Det ser ut som rørleggerarbeid.
Feil nummer én: la if-kjeden avgjøre
Den vanlige formen:
if bar.high >= target:
exit(target, "tp")
elif bar.low <= stop:
exit(stop, "sl")
Ingen valgte «kursmål avklares før stop-loss». Sjekken av kursmålet ble bare skrevet først fordi det er det ønskede utfallet, og det var det du tenkte på. Bytt om på de to grenene, så endres egenkapitalkurven; det alene burde fortelle deg at strategiens P&L delvis er en egenskap ved teksteditoren din.
Jeg kjørte en helt ordinær scalper med gjennomsnittsreversjon på BTCUSDT perp, tre måneder med 1-minuttscandles, 4,812 handler, stop-loss 0.10% og kursmål 0.18% fra inngang. Samme signaler, samme gebyrer, bare tie-break-konvensjonen var endret.
En åttendedel av handlene står for hele resultatet. Det er regnestykket bak et tett stop-loss- og kursmålpar: De tvetydige handlene er dem der prisen gikk begge veier, som gjelder de fleste interessante handlene, og hver av dem er verdt hele avstanden fra stop-loss til kursmål avhengig av myntkastet. 12.6% av handlene × 0.28% av spennvidden er 3.5% av brutto nominell omsetning per utvalgsenhet, noe som langt overgår strategiens faktiske fortrinn.
Andelen avhenger av candle-størrelsen sammenlignet med avstanden mellom nivåene dine, og den forverres raskt når du bruker lengre tidsintervaller. Samme stop-loss og kursmål, samme signaler, nye intervaller:
| Candleintervall | Handler med begge nivåene innenfor én candle | Sharpe (kursmål først) |
|---|---|---|
| 1s | 0.3% | 0.91 |
| 1m | 12.6% | 2.31 |
| 5m | 34% | 3.60 |
| 15m | 49% | 4.42 |
| 1h | 71% | 5.88 |
Se på hva tabellen faktisk forteller. Grovere candles gjorde backtesten bedre. Alle som driver med research, vil instinktivt tenke at timecandles er det konservative valget: mindre støy, mindre overtilpasning til markedsmikrostrukturen. Men med en intrabar-konvensjon som avgjør i din favør, er en grovere candle bare en større boks der du kan anta at du hadde flaks. Med 1h-candles er 7 av 10 handler rene utslag av konvensjonen. Den backtesten tester ikke en strategi; den tester rekkefølgen på to `if`-setninger 3,400 ganger.
Feil nummer to: anta at stop-loss-ordren ble fylt på stop-loss-prisen
La oss si at du fikser rekkefølgen. Stop-loss avklares først, du bokfører et tap på nøyaktig 0.10% pluss taker-gebyr, og du føler deg grundig. To ulike ting er fortsatt feil.
Det første er at en stop-loss er en utløser, ikke en fylling. På Binance USDⓈ-M blir en STOP_MARKET-ordre til en markedsordre idet utløserbetingelsen oppfylles, og den tar deretter det som finnes i ordreboken. I et rolig minutt betyr det ett eller to ticks i slippage. I minuttet som faktisk utløste stop-loss-ordren din — det med en candlekropp på 40 punkter og en likvidasjonskaskade under den — er ordreboken tynn akkurat på siden du krysser. I utvalget mitt, der jeg matchet utløste stop-loss-ordrer mot tickdataene, var medianfyllingen 1.4 bps forbi utløsernivået, og 95-persentilen var 11 bps. Med en stop-loss på 10 bps koster halen deg en ekstra tidel av risikoen du trodde du hadde definert.
Det andre er mer subtilt og spesifikt for perps: hvilken pris som utløser ordren. Binance bruker markedspris som standard for stopordrer, og markedsprisen beregnes fra indeksen pluss en utjevnet basis, ikke fra siste handel på den handelsplassen. OHLC-serien din viser siste pris. Det er to forskjellige serier, og de avviker mest under akkurat de hendelsene som utløser stopordrer.
| Siste pris (klines-dataene dine) | Markedspris (standardutløser) | |
|---|---|---|
| Kilde | handler på denne handelsplassen | indeks fra flere handelsplasser + basis |
| Wick-atferd | fullt utslag | kraftig dempet |
| Typisk avvik | 1–3 bps i rolige perioder, 20–35 bps i et kaskademinutt | |
| Konsekvens for backtesten | stopordrer som ble utløst uten at de skulle ha blitt det, og omvendt | |
Dermed kan en wick på 25 bps i dataserien for siste pris utløse stop-loss i backtesten, selv om live markedspris aldri kom nærmere enn 10 bps fra utløsernivået. Eller det motsatte kan skje den dagen indeksen beveger seg og handelsplassen din henger etter. Hvis du setter workingType til CONTRACT_PRICE, samsvarer live-atferden i det minste med dataene dine. Det er som regel riktig valg for en som driver med research, siden en ærlig simulering av en utløser basert på markedspris krever at du fører en ekstra serie gjennom hele fyllemotoren.
Dette husker jeg best: En av oss «forbedret» en strategi ved å flytte take-profit fra 0.18% til 0.21%. Sharpe gikk fra 2.3 til 3.1. Ingen ny edge. Kursmålet hadde bare flyttet seg utenfor den tetteste delen av 1-minuttsfordelingen for wicks, så færre handler havnet i den tvetydige gruppen der koden i det stille ga dem seieren. De hadde optimalisert tie-break-regelen.
Feil nummer tre: anta alltid det verste og kall det konservativt
Den instinktive løsningen er pessimisme. Hvis begge nivåene nås, bokfør stop-loss. Ferdig, ikke mer optimisme, send det ut.
Jeg pleide å gjøre dette. Det er bedre enn alternativet, men fortsatt feil, av 2 grunner.
Det forkaster strategier som fungerer fint. En pessimistisk avklaring av 12.6% av handlene kostet denne strategien 2.1 Sharpe-poeng sammenlignet med 0.94 når den ble avklart med tickdata. Hvis det sanne tallet er 0.94, mens konvensjonen din viser 0.18, forkaster du ideen og går videre til å jobbe med noe dårligere. Konservatisme som bommer med 2 Sharpe-poeng, er ikke konservatisme; det er støy med en moralsk positur.
Enda verre er det at det forvrenger optimaliseringen. Gir du et parametersøk en pessimistisk tie-break-regel, lærer optimaliseringen å unngå tvetydighet fordi tvetydighet nå bare gir straff. Den beveger seg mot brede stop-loss-nivåer og nære kursmål, eller mot lange tidsintervaller der de to nivåene sjelden forekommer samtidig, og viser deg parametere som er valgt ut fra fyllkonvensjonen din, ikke markedet. Samme feil som med den generøse varianten, bare med motsatt fortegn, og like usynlig i resultatrapporten.
Tommelfingerregel vi bruker før alt annet: Hvis stop_distance + target_distance er mindre enn 75-persentilen for spennvidden i candleintervallet ditt, påvirker intrabar-antakelsen P&L mer enn signalet ditt. Beregn begge tallene. Det tar 4 linjer og har satt en stopper for flere strategigjennomganger enn noen annen enkeltkontroll.
Dette fungerer i praksis
Prisbanen innenfor candlen er data. Hent den, eller sett grenser for det du ikke kan hente.
- Avklar med den mest detaljerte serien du har. Binance aggTrades for de aktuelle minuttene består av noen hundre rader og avgjør spørsmålet direkte: Hvilket nivå ble nådd først, og til hvilken pris ble ordreboken feid? Du trenger ikke tickdata for hele backtesten, bare for de tvetydige candlene. I utvalget mitt gjaldt det 606 minutter av 129,600. Det er en liten nedlasting, ikke et infrastrukturprosjekt.
- Hvis tickdata ikke er tilgjengelig, gå ned ett eller to tidsintervaller bare for avklaringen. Signaler på 15m, utganger avklart på 1s- eller 1m-candles. Tvetydigheten faller fra 49% til en brøkdel av en prosent, og det som gjenstår, er lite nok til at du ærlig kan se bort fra det.
- Rapporter alltid intervallet. Kjør hver backtest to ganger, med optimistisk og pessimistisk avklaring, og skriv ut begge Sharpe-tallene ved siden av det avklarte tallet. Spennet viser usikkerheten innenfor candlene, og hører hjemme i resultatrapporten ved siden av konfidensintervallet du ville oppgitt for selve Sharpe-tallet. Når intervallet er 0.2–2.3, er ingen konklusjon innenfor det reell.
- Følg med på tvetydighetsandelen som en egen hovedmåling. Vår står øverst på hvert strategikort, ved siden av antall handler og omsetning. En andel over omtrent 5% betyr at det er utgangslogikken, ikke inngangslogikken, som blir testet.
- Modeller utløseren separat fra fyllingen. Bruk prisserien handelsplassen faktisk bruker som utløser, og modeller fylling på utløserpris pluss slippage trukket fra en fordeling kalibrert mot markedsdataene, ikke på selve utløserprisen.
Aksjer har det samme problemet, bare i en annen innpakning. En stop-loss på 62.00 for en aksje som åpner på 58.40 etter et kursgap over natten, fylles ikke på 62.00; den fylles et sted under åpningskursen. En backtest med dagscandles som bokfører −$0.00 i slippage ved kursgap forbi stop-loss-nivået, vil gladelig fortelle deg at et stop-loss-lag forbedret drawdownen din. Det gjorde det ikke. Det ble bare aldri testet på dagene som betyr noe. Handelsstans gir samme problem: Stop-loss-ordren blir faktisk gjennomført i gjenåpningsauksjonen, til en pris som dagens laveste nivå aldri viser.
Ingenting av dette er uvanlig. Det handler om å innse at en candle er et sammendrag, og at en strategi med stop-loss og kursmål satser på rekkefølgen av hendelser som sammendraget har kastet bort. Når papirhandelsmotoren endelig kjører strategien mot live markedsdata, har markedet en mening om rekkefølgen, og det har aldri brydd seg om hvilken gren i if-setningen du skrev først.
← Alle innlegg


