2. september 2026 · mikrostruktur

Din limitordre blev ikke udført: et brev om køposition, maker-backtests og den Sharpe, du fandt på

Din limitordre blev ikke udført: et brev om køposition, maker-backtests og den Sharpe, du fandt på

Du sendte notesbogen i sidste uge: samme signal, samme univers, de samme 14 måneders BTCUSDT-perp-data. Den eneste ændring var eksekveringen. Du holdt op med at krydse spreadet og begyndte i stedet at lægge bud én tick indenfor, og Sharpe steg fra 0.42 til 2.14. Du spurgte, om det var ægte, eller om du havde ødelagt noget.

Du har ødelagt noget. Jeg vil gennemgå præcis hvor, for fejlen er én linje, og læren bag den er langt større end linjen.

Her er din udførelsesregel, kopieret fra din motor:

if bar.low <= limit_price: fill(limit_price)

Det betyder: Hvis der blev handlet til eller under min pris i løbet af dette minut, blev min ordre udført til min pris. Det, den faktisk koder, er, at prisen rørte dit niveau. At røre er ikke det samme som at blive udført. Mellem de to ting står en kø, og du har modelleret køen, som om den var tom.

Hvad der står foran dig

Når du lægger et bud på 84,120.0 på BTCUSDT-perp, stiller du dig bagerst i den kø, der allerede ligger der. På kontraktens bedste budniveau er der typisk et sted mellem 4 og 30 BTC afhængigt af tidspunktet på dagen; medianen i dit datavindue ligger omkring 12 BTC. Din ordre er på 0.4 BTC. For at din ordre kan blive handlet, skal sælgere ramme niveauet med tilstrækkelig samlet størrelse til at rydde alle, der kom før dig, og det skal ske, før niveauet bliver annulleret under dig, eller markedet bevæger sig væk.

Så spørgsmålet i din backtest bør ikke være »nåede prisen 84,120.0«, men »blev der udført mindst 12.4 BTC i markedsalg på 84,120.0, mens min ordre lå der«. Det er vidt forskellige hændelser. I dine data er der omkring en faktor 3 til forskel på dem.

12.4 BTCmedianstørrelse foran dig, når kursen rører niveauet
100%udførelsesgrad, som din backtest antager
31%af berøringerne, hvor køen foran dig blev ryddet
4,180handler, der blev til cirka 1,300

3 udførelsesregler, 3 forskellige strategier

Jeg kørte dit signal igen med de samme indgange og 3 eksekveringsmodeller. Samme alpha, samme gebyrer, samme funding. Kun udførelseslogikken ændrede sig.

UdførelsesregelUdførelserGennemsnitlig edge 60s efter udførelseSharpe
Berøring: low <= limit4,180+2.6 bp2.14
Gennembrud: low < limit - 1 tick1,712+0.4 bp0.61
Køsimulering med volumen på niveauet1,306+0.9 bp0.77

Den midterste række er den grove løsning, alle først prøver: Tæl kun en ordre som udført, hvis markedet handlede strengt forbi din pris, ud fra tanken om, at hvis kursen gik videre forbi dig, må den have handlet mod din ordre. Det er rigtigt i store træk og afliver det meste af fantasien. Men det giver også en grim skævhed, som jeg kommer til.

Den nederste række er den model, du bør bygge. Den kræver hverken et L3-feed eller rekonstruktion af hver enkelt ordre. Du har allerede det meste af det, du skal bruge.

En køsimulering, du kan bygge ud fra aggTrades

Hent den aggregerede handelsstrøm i stedet for klines. Hver handel indeholder pris, mængde, tidsstempel og et maker-side-flag, der fortæller dig, om den aggressive part købte eller solgte. Det er nok til en brugbar simulering af din egen passive ordre:

  1. Når din ordre lægges, så tag et øjebliksbillede af den hvilende volumen på din pris. Hvis du kun har et 100ms-book-feed, så brug det seneste øjebliksbillede; fejlen er lille sammenlignet med den fejl, du prøver at rette.
  2. Sæt queue_ahead = resting_size. Vær pessimistisk, og antag, at du står sidst. Det gør du, medmindre det er dig, der opretter niveauet.
  3. Gå handelsstrømmen igennem. Hver handel med en aggressiv sælger på eller under din pris reducerer queue_ahead med handelsmængden. Når den bliver negativ, er din ordre udført til din pris på det pågældende tidspunkt.
  4. Hvis prisen flytter sig en tick væk, skal du ikke nulstille køen. Lad den svinde ind. Nogle af ordrerne foran dig bliver annulleret, når niveauet bliver uaktuelt, andre bliver liggende. Et fald på 30-40% for hvert helt sekund, hvor prisen er væk fra dit niveau, passede bedre med mine rekonstruktioner end nogen af yderpunkterne.
  5. Hvis din strategi ville annullere og lægge en ny ordre, så modellér genindsendelsen som en ny ordre bagerst i den nye kø. Det trin springer folk over, og dér gemmer den resterende fantasi sig.

Det er ved trin 2, du får lyst til at diskutere med mig. Ja, nogle gange står du tæt på fronten, fordi du lagde ordren, så snart niveauet blev dannet. Fint — mål det, og antag det ikke. Log den hvilende volumen, når ordren lægges, og lad dataene vise dig, hvor stor en andel af dine ordrer der faktisk kommer tidligt ind. I din strategi var det 11%, fordi dit signal udløses efter en bevægelse. Det betyder, at niveauet, du slutter dig til, allerede findes, og at der allerede er en kø.

De udførelser, du får, er dem, du helst ville have været foruden

Nu kommer det, der faktisk betyder noget, og forklaringen på, at reglen om gennembrud giver en skævhed.

Tænk over, hvornår dit bud bliver fuldt ud handlet. Det sker, når salgspresset er stort nok til at æde hele niveauet. Det vil sige, at det per definition sker, når markedet bevæger sig ned gennem din pris. De udførelser, du er mest sikker på, er dem, hvor du straks står på den forkerte side af markedet.

Opdel dine udførelser efter, hvordan de skete, og mål 60-sekunders mark-out:

UdførelsestypeAndel af udførelserMark-out efter 60s
Niveau handlet, prisen steg igen38%+3.1 bp
Niveau handlet, prisen flad21%+0.2 bp
Prisen handlede 2+ ticks forbi41%−2.4 bp

Din naive backtest gav dig alle 3 grupper og prissatte dem alle, som om de var gratis. Reglen om gennembrud giver dig næsten udelukkende den tredje gruppe, hvilket er grunden til, at dens edge faldt endnu mere end køsimuleringens. Ingen af dem er rigtig. Køsimuleringen giver dig en realistisk blanding, og blandingen er hele spillet: Passiv eksekvering indtjener spreadet og betaler dig tilbage i form af negativ selektion, og forholdet mellem de to er din faktiske strategi.

Den gamle måde at se det på fra aktiehandelsbordet holder stadig: En makerordre er en gratis option, du har skrevet til markedet. Nogen udnytter den, når det kan betale sig. Din backtest indkasserede præmien og glemte, at optionen også har en udbetalingsside.

De handler, du ikke fik, ændrer strategien, ikke kun omkostningerne

Det her er den pointe, jeg især gerne vil have dig til at tænke over. Når du modellerer taker-eksekvering dårligt, får du de rigtige handler til den forkerte pris, og en korrektion af gebyrerne retter det meste. Når du modellerer maker-eksekvering dårligt, får du det helt forkerte sæt handler. Omkring 2,900 af dine 4,180 indgange blev aldrig til noget. Nogle af dem var dine bedste signaler, på candles, der spidsede op og vendte om — præcis det mønster, hvor markedet stak af uden dig.

Så logikken for den gren, hvor ordren ikke bliver udført, skal være reel. Hvad gør strategien, når indgangen ikke bliver udført, inden signalet bliver for gammelt? Jager den med en takerordre og betaler spreadet plus markedsimpact? Lægger den en ny ordre lavere og accepterer et andet indgangsniveau? Springer den handlen over og forbliver flad? Hvert valg giver en markant anderledes egenkapitalkurve, og ingen af dem betyder »antag, at ordren blev udført«. I vores kørsler hentede en realistisk jagtregel (kryds efter 20 sekunder uden udførelse, med et loft på 3 bp slippage) omkring en tredjedel af de manglende handler tilbage og omtrent halvdelen af forskellen mellem naiv Sharpe og Sharpe fra køsimuleringen. Det er et virkelig interessant resultat, og det dukker først op, når udførelsesmodellen er realistisk nok til, at spørgsmålet giver mening.

En billig realitetstest på 10 minutter: Tag din live paper-trading-log og din backtest for det samme tidsvindue. Sammenlign udførelsesgraden, ikke PnL. Hvis backtesten udfører 100% af de hvilende ordrer, og paper-trading udfører 34%, har du ikke en sammenligning af strategier; du har en fejl i udførelsesmodellen. Ret den, før du ser på et eneste afkasttal.

2 mindre ting, mens jeg har dig

Afvisninger af post-only-ordrer. Hvis du bruger post-only for at sikre maker-gebyrniveauet, og ordrebogen flytter sig mellem din beslutning og børsens kvittering, bliver ordren afvist i stedet for at blive liggende. I vores paper-logs gælder det 3-6% af forsøgene på BTCUSDT i normal handelstid og over 15% i minuttet efter en amerikansk CPI-offentliggørelse. En afvist ordre er hverken en udført ordre eller en hvilende ordre, der ikke blev udført; det er en handel, der aldrig fandtes. Hvis din backtest ikke har den tilstand, bliver dit antal handler oppustet netop i de markedsforhold, der betyder mest for dig.

Forebyggelse af selvhandler og dit eget markedsaftryk. Med 0.4 BTC flytter du ikke BTCUSDT, så spring det over. Men du nævnte, at du gerne ville køre det her på en altcoin-perp med middelstor markedsværdi, hvor den bedste budstørrelse ofte er under $15k i nominel værdi. Der udgør din ordre en væsentlig andel af køen, og tallet for volumen foran dig, som du tager et øjebliksbillede af, afspejler også din seneste ordres tilstedeværelse. Når din størrelse overstiger omkring 10% af den hvilende volumen, skal simuleringen tage højde for, at andre markedsdeltagere reagerer på dig. Helt ærligt ville jeg på det tidspunkt stole mere på paper-trading end på enhver simulering, du eller jeg kan skrive.

Kør den igen med køsimuleringen, og send mig mark-out-tabellen opdelt efter udførelsestype. Hvis gruppen, hvor prisen steg igen, stadig står for det meste af edge, og gruppen, hvor prisen handlede forbi, ikke æder det hele, har du måske noget, der er værd at prøve med paper-trading. Hvis det hele skyldtes de 2,900 udførelser, du aldrig ville have fået, er det bedre at finde ud af det nu end efter 4 uger med en paper-konto, der ikke gør det, notesbogen lovede.

limitordrerkøpositionnegativ selektionbacktestingkryptofutures
← Alle indlæg