3 november 2026 · exekvering

Vi paper-tradade en basis trade. Ett ben fylldes.

Vi paper-tradade en basis trade. Ett ben fylldes.

Funktionen hette enter_pack. Det borde ha varit varningen. En research agent hade skrivit en BTC cash-and-carry: när perpetualen handlades mer än åtta baspunkter över spot, köp spot, shorta perpen, sitt stilla. Funding och basis skulle göra resten. Backtestet fyllde båda benen i samma millisekund, på mid, plus en tick av artighet. Equity-kurvan var en trappa. Ingen skulle kalla den spännande. Det var säljargumentet.

Vi lyfte den till paper på en måndag.

Måndag: paketet som alltid fylldes

Första signalen landade 11:17 UTC. Basis på skärmen var 9.4 bps, perp rich. I backtestet returnerade enter_pack två fills och en residual på noll. I paper returnerade den en spot-fill på 0.40 BTC på ask, och en vilande short på perpen som inte gick.

Spot hade betalat 4 bp i taker-avgift och ungefär 1 bp i spread. Perp-priset vi ville ha låg fortfarande i boken, sen gjorde det inte det. Tjugo sekunder senare var den noterade basisen 5 bps, för att vi precis hade lyft spot. Fyrtio sekunder senare fylldes perpen, efter rörelsen. Vi hade paketet. Vi hade paketet på en basis som inte längre betalade för att komma in och ut.

Backtestet skulle ha bokfört "+9.4 bps carry, residual 0." Blottern hade 4 bps i spot-takerkostnad, en 40-sekunders ohedgad long och ett entry-gap på ungefär 5. Jag hade ett sample på ett och, ändå, för mycket tilltro till trappan.

Tisdag: vi öppnade funktionen

enter_pack tog en lista med ben och en target mid. Den loopade, fyllde varje ben på mid * (1 ± slip), och skrev en portföljrad. En andra tidsstämpel fanns inte. Det fanns ingen gren där ben två inte fanns. Enhetstestet assertade residual == 0.

Det är ett test som får en att känna sig vuxen. Det kommer aldrig att faila.

Vi bröt paketet i två ordrar med eget state: inskickad, partiell, fylld, annullerad. Incomplete blev ett first-class-objekt. Den historiska replayen hade fortfarande ingen riktig bok, så vi fuskade. Fördröj den andra fillen med ett slumpdrag ur måndagens väntetid (medel 26 sekunder, en svans jag inte gillade), och drifta priset med vad spot än gjorde i det fönstret. Grovt, men trappan fick hack före lunch: paket som gick in på 3 bps, paket som aldrig blev paket.

Onsdag: tre sekunder, sen dumpa

Om det andra benet inte är på inom tre sekunder, flattena det första. Vi borde ha börjat där. Det låter konservativt tills man prissätter aborten. Att flattena betyder att du korsade spot för att komma in och du korsar den för att komma ut. Två taker-avgifter, två spreadar, ingen carry, ingen funding. I den omskrivna replayen var de aborterna inte sällsynta. De klustrade på de bars där basisen såg mest "verklig" ut, vilket oftast är de bars där något redan flödar genom spot.

Till onsdagskvällen hade vi sjutton papersignaler att bråka med.

17papersignaler på fyra dagar
7paket med båda benen fyllda
18 bpssenare rörelse mot en överbliven long
Backtest, måndag morgonPaper, till onsdagen
IngångBåda benen på mid, en tidsstämpelSpot på ask; perp 5–40s senare, eller aldrig
ResidualAlltid 0Ohedgad på 10 av 17 signaler
Avgifter på en aktiv tradeTvå takersTvå takers, eller fyra om vi avbryter
PnL-drivareBasis + fundingVad spot än gjorde medan vi väntade
Ett paket är komplett när båda ordrarna är fyllda, annars är det en riktningsposition du inte dimensionerade för. Markera residualen mot live-boken hela tiden den finns. Om du inte kan markera den kan du inte köra strategin.

En kollega har fortfarande en notebook som behandlar paketet som en noterad produkt, BTC_CARRY, med en platt 2 bp "exekveringsskatt". Jag gillade den notebooken. Den producerade samma trappa som agenten. En skatt är ett medelvärde. Föräldralösa är en fördelning. Medelvärden blir inte överkörda i en tunn timme; 0.40 BTC överbliven spot kan.

Torsdag: överblivet, sen kaffe

Tre sekunder abortade nästan allt när vi dessutom väntade i en kö. Vi luckrade paper till femton. 02:11 UTC på torsdagen fylldes spot och perpen gjorde det inte. Jag var fortfarande i "den kommer vila, den kommer lyfta"-fasen av sorgearbetet när jag gick och kokade kaffe.

02:11:08 fylld BTCUSDT 0.40 köp
02:11:08 inskickad BTCUSDT-PERP 0.40 sälj, post-only
02:11:41 fortfarande working
02:26:00 residualmark −18.1 bps

BTC föll 18 bps i ett tyst fönster. Residualen var hela traden. Funding, när den printade, krediterade en perp vi inte hade. Spot bara satt där och var long. Det ursprungliga backtestets overnight-path innehöll aldrig en enbensposition, så det hade aldrig ställt frågan vi ställer till varje annan bok: vad är den värsta bar den här överblivna kan printa, och var är stoppen för en position vi inte menade att hålla?

De sju fulla paketen var inga hjältar heller. När det andra benet printade hade vår egen spot-lift redan tryckt på indexet, och entry-basis var 3–5 bps sämre än snapshotet som hade triggat signalen. Åtta-bps-tröskeln var ett tal om en skärm.

Vad vi skulle skippa nästa gång

Vi skulle inte shippa en multi-leg som en enda fill-funktion. Samma regel för en perp-spot-carry, en calendar, eller den call vertical en agent skickar in som två limitordrar och sen firar när båda "fylls" på mid.

  1. Orderstate per ben, inklusive partials. Behandla paketet som förälder till två tickets.
  2. En max-orphan-timer som faktiskt triggar, och en flatten-väg som betalar två spreadar på vägen ut.
  3. Mark-to-book på residualen, med en stop dimensionerad för det överblivna. Det packade nettot är fel notional.
  4. Ingen carry krediterad, ingen funding upplupen, förrän båda benen är på. Incomplete är incomplete.
  5. Vägra mid-på-mid som default för något som har två tickets. Om du måste gissa, gissa sämre på det tunna benet och senare på den andra fillen.

Och vi skulle inte släppa in test_pack_is_atomic i repot. Atomic är en egenskap hos en matching engine, inte en hjälpfunktion. BTC spot och BTC perpetual är två böcker som ibland rimmar. Paper är där de slutar rimma, vilket är den enda anledningen till att trappan någonsin var intressant.

backtestingcash-and-carryexekveringpaper tradingkrypto-futures
← Alla inlägg