De functie heette enter_pack. Dat had de waarschuwing moeten zijn. Een research agent had een BTC cash-and-carry geschreven: wanneer de perpetual meer dan acht basispunten boven spot handelde, koop spot, short de perp, blijf stilzitten. Funding en basis zouden de rest doen. De backtest vulde beide benen in dezelfde milliseconde, op mid, plus een tick beleefdheid. De equity curve was een trap. Niemand zou het spannend noemen. Dat was juist het verkoopargument.
We promoveerden het naar paper op een maandag.
Maandag: het pakket dat altijd gevuld werd
Het eerste signaal landde om 11:17 UTC. De basis op het scherm was 9.4 bps, perp duur. In de backtest gaf enter_pack twee fills terug en een residual van nul. In paper gaf het een spot-fill van 0.40 BTC op de ask terug, en een rustende short op de perp die niet handelde.
Spot had 4 bp taker fee betaald en ongeveer 1 bp spread. De perp-prijs die we wilden stond nog op het boek, en toen niet meer. Twintig seconden later was de genoteerde basis 5 bps, omdat we zojuist spot hadden gelift. Veertig seconden later werd de perp gevuld, na de beweging. Het pakket was rond. Het pakket was rond op een basis die in- en uitstappen niet langer betaalde.
De backtest zou "+9.4 bps carry, residual 0." hebben geboekt. De blotter had 4 bps spot-takerkosten, een ongehedgede long van 40 seconden, en een instapgap van ongeveer 5. Ik had een steekproef van één en, toch, te veel vertrouwen in de trap.
Dinsdag: we openden de functie
enter_pack nam een lijst van benen en een target mid. Het liep ze af, vulde elk been op mid * (1 ± slip), en schreef één portfoliorij. Er was geen tweede timestamp. Er was geen branch waarin het tweede been niet bestond. De unit test assertte residual == 0.
Dat is een test die je het gevoel geeft een volwassene te zijn. Hij faalt nooit.
We braken het pakket op in twee orders met hun eigen status: ingediend, partieel, gevuld, geannuleerd. Onvolledig werd een first-class object. De historische replay had nog steeds geen echt boek, dus we valsspeelden. Vertraag de tweede fill met een trekking uit de wachttijd van maandag (gemiddelde 26 seconden, een staart die me niet beviel), en drift de prijs met wat spot in dat venster deed. Grof, maar de trap kreeg inkepingen voor de lunch: pakketten die instapten op 3 bps, pakketten die nooit pakketten werden.
Woensdag: drie seconden, dan dumpen
Als het tweede been niet binnen drie seconden aan is, flatten het eerste. Daar hadden we moeten beginnen. Het klinkt conservatief tot je de abort beprijst. Flatten betekent dat je spot hebt gekruist om binnen te komen en je het kruist om eruit te gaan. Twee taker fees, twee spreads, geen carry, geen funding. In de herschreven replay waren die aborts niet zeldzaam. Ze clusterden op de bars waar de basis het meest "echt" leek, en dat zijn meestal de bars waar er al iets door spot stroomt.
Woensdagavond hadden we zeventien paper-signalen om over te twisten.
| Backtest, maandagochtend | Paper, woensdag | |
|---|---|---|
| Instap | Beide benen op mid, één timestamp | Spot op de ask; perp 5–40s later, of nooit |
| Residual | Altijd 0 | Ongehedged op 10 van 17 signalen |
| Fees op een werkende trade | Twee takers | Twee takers, of vier als we aborten |
| PnL-driver | Basis + funding | Wat spot deed terwijl we wachtten |
Een collega heeft nog een notebook dat het pakket behandelt als een genoteerd product, BTC_CARRY, met een vaste 2 bp "executiebelasting." Ik vond dat notebook mooi. Het produceerde dezelfde trap als de agent. Een belasting is een gemiddelde. Wezen zijn een verdeling. Gemiddelden worden niet overreden in een dun uur; 0.40 BTC overgebleven spot wel.
Donderdag: restpositie, dan koffie
Drie seconden aborteerde bijna alles zodra we ook op een queue wachtten. We versoepelden paper naar vijftien. Om 02:11 UTC donderdag werd spot gevuld en de perp niet. Ik zat nog in de "hij blijft rusten, hij wordt gelift"-fase van rouw toen ik koffie ging zetten.
02:11:08 gevuld BTCUSDT 0.40 buy
02:11:08 ingediend BTCUSDT-PERP 0.40 sell, post-only
02:11:41 nog actief
02:26:00 residual-mark −18.1 bps
BTC zakte 18 bps in een rustig venster. De residual was de hele trade. Funding, toen het printte, crediteerde een perp die we niet hadden. Spot zat er gewoon long te zijn. Het overnight pad van de originele backtest bevatte nooit een éénbenige positie, dus het had nooit de vraag gesteld die we aan elk ander boek stellen: wat is de slechtste bar die deze restpositie kan printen, en waar is de stop voor een positie die we niet van plan waren aan te houden?
De zeven volledige pakketten waren ook geen helden. Tegen de tijd dat het tweede been printte, had onze eigen spot-lift al op de index geleund, en de instapbasis was 3–5 bps slechter dan de snapshot die het signaal had afgevuurd. De drempel van acht bps was een getal over een scherm.
Wat we de volgende keer overslaan
We zouden een multi-leg niet als één fill-functie shippen. Dezelfde regel voor een perp-spot carry, een calendar, of de call vertical die een agent als twee limits instuurt en dan viert wanneer beide op mid "fillen".
- Orderstatus per been, inclusief partiële fills. Behandel het pakket als parent van twee tickets.
- Een max-orphan-timer die daadwerkelijk afgaat, en een flatten-pad dat twee spreads betaalt op de weg naar buiten.
- Mark-to-book op de residual, met een stop gesized voor de restpositie. Het packed net is de verkeerde notional.
- Geen carry gecrediteerd, geen funding opgebouwd, tot beide benen aan zijn. Onvolledig is onvolledig.
- Weiger mid-on-mid als default voor alles met twee tickets. Als je moet gokken, gok slechter op het dunne been en later op de tweede fill.
En we zouden test_pack_is_atomic niet in de repo laten. Atomic is een eigenschap van een matching engine, niet van een helperfunctie. BTC spot en BTC perpetual zijn twee boeken die soms rijmen. Paper is waar ze ophouden te rijmen, en dat is de enige reden dat de trap ooit interessant was.



