Die Funktion hieß enter_pack. Das hätte die Warnung sein sollen. Ein Research-Agent hatte einen BTC-Cash-and-Carry geschrieben: wenn der Perpetual mehr als 8 Basispunkte über Spot handelte, Spot kaufen, den Perp shorten, stillhalten. Funding und Basis würden den Rest erledigen. Der Backtest hat beide Legs in derselben Millisekunde ausgeführt, zum Mid, plus einem Tick Höflichkeit. Die Equity-Kurve war eine Treppe. Niemand würde sie aufregend nennen. Genau das war das Verkaufsargument.
Wir haben sie an einem Montag auf Paper gesetzt.
Montag: das Pack, das immer ausgeführt wurde
Das erste Signal landete um 11:17 UTC. Die Basis auf dem Screen lag bei 9.4 bps, Perp teuer. Im Backtest lieferte enter_pack 2 Fills und eine Residual von 0. Im Paper lieferte sie einen Spot-Fill von 0.40 BTC zum Ask und einen ruhenden Short auf dem Perp, der nicht gehandelt hat.
Spot hatte 4 bp Taker-Gebühr und etwa 1 bp Spread gezahlt. Der Perp-Preis, den wir wollten, stand noch im Book, dann nicht mehr. 20 Sekunden später lag die quotierte Basis bei 5 bps, weil wir gerade Spot genommen hatten. 40 Sekunden später wurde der Perp ausgeführt, nach der Bewegung. Wir waren gepackt. Wir waren gepackt zu einer Basis, die den Ein- und Ausstieg nicht mehr bezahlte.
Der Backtest hätte „+9.4 bps Carry, Residual 0“ gebucht. Im Blotter standen 4 bps Spot-Taker-Kosten, eine 40-sekündige ungesicherte Long und eine Entry-Lücke von etwa 5. Ich hatte eine Stichprobe von 1 und trotzdem zu viel Vertrauen in die Treppe.
Dienstag: wir haben die Funktion aufgemacht
enter_pack nahm eine Liste von Legs und ein Ziel-Mid. Sie lief in einer Schleife, führte jedes Leg bei mid * (1 ± slip) aus und schrieb eine Portfolio-Zeile. Es gab keinen zweiten Timestamp. Es gab keinen Branch, in dem Leg 2 nicht existierte. Der Unit-Test hat residual == 0 geprüft.
Das ist ein Test, der einen erwachsen fühlen lässt. Er wird nie fehlschlagen.
Wir haben das Pack in 2 Orders mit eigenem State zerlegt: submitted, partial, filled, canceled. Incomplete wurde ein First-Class-Objekt. Das historische Replay hatte immer noch kein echtes Book, also haben wir geschummelt. Den zweiten Fill um eine Ziehung aus der Wartezeit vom Montag verzögern (Mittel 26 Sekunden, ein Tail, der mir nicht gefiel) und seinen Preis um das driften lassen, was Spot in diesem Fenster getan hat. Grob, aber die Treppe bekam vor dem Mittag Kerben: Packs, die bei 3 bps eingestiegen sind, Packs, die nie zu Packs wurden.
Mittwoch: 3 Sekunden, dann raus damit
Wenn das zweite Leg nicht innerhalb von 3 Sekunden drauf ist, die erste Seite glattstellen. Da hätten wir anfangen sollen. Das klingt konservativ, bis man den Abort bepreist. Glattstellen heißt: du hast Spot gekreuzt, um reinzukommen, und du kreuzt ihn, um rauszukommen. 2 Taker-Gebühren, 2 Spreads, kein Carry, kein Funding. Im umgeschriebenen Replay waren diese Aborts nicht selten. Sie ballten sich auf den Bars, auf denen die Basis am „echtesten“ wirkte, also meist auf den Bars, auf denen durch Spot schon etwas fließt.
Bis Mittwochabend hatten wir 17 Paper-Signale, über die wir streiten konnten.
| Backtest, Montagmorgen | Paper, bis Mittwoch | |
|---|---|---|
| Einstieg | Beide Legs zum Mid, ein Timestamp | Spot zum Ask; Perp 5–40s später, oder nie |
| Residual | Immer 0 | Ungesichert bei 10 von 17 Signalen |
| Gebühren bei einem funktionierenden Trade | 2 Taker | 2 Taker, oder 4 wenn wir aborten |
| PnL-Treiber | Basis + Funding | Was Spot tat, während wir warteten |
Ein Kollege hat immer noch ein Notebook, das das Pack als gelistetes Produkt behandelt, BTC_CARRY, mit einer pauschalen 2 bp „Execution-Tax“. Ich mochte dieses Notebook. Es hat dieselbe Treppe produziert wie der Agent. Eine Tax ist ein Mittelwert. Waisen sind eine Verteilung. Mittelwerte werden in einer dünnen Stunde nicht überfahren; 0.40 BTC übrig gebliebener Spot schon.
Donnerstag: Rest, dann Kaffee
3 Sekunden haben fast alles abgebrochen, sobald wir auch noch auf eine Queue gewartet haben. Wir haben Paper auf 15 gelockert. Um 02:11 UTC am Donnerstag wurde Spot ausgeführt und der Perp nicht. Ich war noch in der Trauerphase „er bleibt liegen, er wird genommen“, als ich Kaffee machen ging.
02:11:08 filled BTCUSDT 0.40 buy
02:11:08 submitted BTCUSDT-PERP 0.40 sell, post-only
02:11:41 immer noch working
02:26:00 Residual-Mark −18.1 bps
BTC ist in einem ruhigen Fenster 18 bps abverkauft. Die Residual war der ganze Trade. Funding, als es gedruckt hat, hat einen Perp gutgeschrieben, den wir nicht hatten. Spot saß einfach da und war long. Der Overnight-Pfad des ursprünglichen Backtests enthielt nie eine Position mit nur einem Leg, also hatte er nie die Frage gestellt, die wir an jedes andere Book stellen: was ist die schlechteste Bar, die dieser Rest drucken kann, und wo ist der Stop für eine Position, die wir nicht halten wollten?
Die 7 vollständigen Packs waren auch keine Helden. Bis das zweite Leg gedruckt hat, hatte unser eigenes Spot-Lift den Index schon belastet, und die Entry-Basis war 3–5 bps schlechter als der Snapshot, der das Signal ausgelöst hatte. Die 8-bps-Schwelle war eine Zahl auf einem Screen.
Was wir nächstes Mal lassen würden
Wir würden ein Multi-Leg nicht als einzelne Fill-Funktion ausliefern. Dieselbe Regel für einen Perp-Spot-Carry, einen Calendar oder den Call-Vertical, den ein Agent als 2 Limits schickt und dann feiert, wenn beide zum Mid „fillen“.
- Order-State pro Leg, einschließlich Partials. Das Pack als Parent von 2 Tickets behandeln.
- Ein Max-Orphan-Timer, der tatsächlich feuert, und ein Flatten-Pfad, der auf dem Weg raus 2 Spreads zahlt.
- Mark-to-Book auf der Residual, mit einem Stop, der für den Rest dimensioniert ist. Das gepackte Netto ist das falsche Nominal.
- Kein Carry gutgeschrieben, kein Funding aufgelaufen, bis beide Legs drauf sind. Unvollständig ist unvollständig.
- Mid-on-Mid als Default für alles ablehnen, das 2 Tickets hat. Wenn du schätzen musst, schätze schlechter auf dem dünnen Leg und später beim zweiten Fill.
Und wir würden test_pack_is_atomic nicht ins Repo lassen. Atomic ist eine Eigenschaft einer Matching Engine, nicht einer Hilfsfunktion. BTC-Spot und BTC-Perpetual sind zwei Books, die manchmal reimen. Paper ist dort, wo sie aufhören zu reimen – der einzige Grund, warum die Treppe je interessant war.



