Функція називалася enter_pack. Це вже мало насторожити. Дослідницький агент написав BTC кеш-енд-кері: коли перпетуал торгується більш ніж на вісім базисних пунктів вище споту, купуй спот, шорть перп, сиди. Фандинг і базис зроблять решту. Бектест виконував обидві ноги в ту саму мілісекунду, по mid, плюс тік ввічливості. Крива капіталу була сходами. Ніхто б не назвав це захопливим. У цьому й була принада.
У понеділок ми винесли її на папір.
Понеділок: пакет, який завжди виконувався
Перший сигнал прийшов о 11:17 UTC. Базис на екрані був 9.4 bps, перп дорожчий. У бектесті enter_pack повернула два виконання і нульовий залишок. На папері повернула спотове виконання 0.40 BTC по ask і пасивний шорт на перпі, який не проторгувався.
Спот заплатив 4 bp taker-комісії і близько 1 bp спреду. Ціна перпа, яку ми хотіли, ще була в книзі, потім уже ні. Через двадцять секунд котируваний базис був 5 bps, бо ми щойно підняли спот. Через сорок секунд перп виконався, уже після руху. Ми були в пакеті. Ми були в пакеті на базисі, який уже не оплачував вхід і вихід.
Бектест записав би «+9.4 bps кері, залишок 0». У блотері було 4 bps спотової taker-вартості, 40-секундний незахеджований лонг і вхідний геп близько 5. У мене була вибірка з одного — і все одно забагато віри в сходи.
Вівторок: ми відкрили функцію
enter_pack брала список ніг і цільовий mid. Циклувала, виконувала кожну ногу по mid * (1 ± slip) і писала один рядок портфеля. Другої часової мітки не було. Не було гілки, в якій другої ноги не існує. Юніт-тест стверджував residual == 0.
Ось тест, від якого почуваєшся дорослим. Він ніколи не впаде.
Ми розбили пакет на два ордери з власним станом: поданий, частковий, виконаний, скасований. Незавершене стало об'єктом першого класу. В історичному реплеї все ще не було справжньої книги, тож ми схитрували. Затримати друге виконання випадковим витягом із понеділкового очікування (середнє 26 секунд, хвіст, який мені не подобався) і зсунути його ціну на те, що зробив спот у цьому вікні. Грубо, але до обіду на сходах з'явилися щербини: пакети, що входили по 3 bps, пакети, що так і не стали пакетами.
Середа: три секунди — і скидаємо
Якщо друга нога не стала протягом трьох секунд, флетенимо першу. З цього й треба було починати. Звучить консервативно, доки не оціниш аборт. Флетенити означає, що ти перетнув спот, щоб увійти, і перетинаєш його, щоб вийти. Дві taker-комісії, два спреди, ніякого кері, ніякого фандингу. У переписаному реплеї такі аборти не були рідкістю. Вони збиралися на барах, де базис виглядав найбільш «реальним», а це зазвичай бари, де крізь спот уже щось тече.
До середи ввечері у нас було сімнадцять паперових сигналів, з якими можна було сперечатися.
| Бектест, понеділок вранці | Папір, до середи | |
|---|---|---|
| Вхід | Обидві ноги по mid, одна часова мітка | Спот по ask; перп через 5–40 с, або ніколи |
| Залишок | Завжди 0 | Незахеджований на 10 з 17 сигналів |
| Комісії на робочій угоді | Два taker | Два taker, або чотири, якщо абортуємо |
| Драйвер PnL | Базис + фандинг | Те, що робив спот, поки ми чекали |
У колеги досі є ноутбук, який трактує пакет як лістингований продукт, BTC_CARRY, з фіксованим 2 bp «податком на виконання». Мені той ноутбук подобався. Він видавав ті самі сходи, що й агент. Податок — це середнє. Сироти — це розподіл. Середні не переїжджають у тонку годину; 0.40 BTC залишкового споту — так.
Четвер: залишок, потім кава
Три секунди абортували майже все, щойно ми ще й чекали в черзі. Ми послабили папір до п'ятнадцяти. О 02:11 UTC у четвер спот виконавсь, а перп — ні. Я ще був на стадії скорботи «він постоїть, його знімуть», коли пішов варити каву.
02:11:08 виконано BTCUSDT 0.40 buy
02:11:08 подано BTCUSDT-PERP 0.40 sell, post-only
02:11:41 досі в роботі
02:26:00 mark залишку −18.1 bps
BTC просів на 18 bps у тихому вікні. Залишок і був усією угодою. Фандинг, коли надрукувався, зарахувався на перп, якого в нас не було. Спот просто сидів лонгом. У нічному шляху оригінального бектесту ніколи не було одноногої позиції, тож він ніколи не ставив питання, яке ми ставимо кожній іншій книзі: який найгірший бар може надрукувати цей залишок і де стоп для позиції, яку ми не збиралися тримати?
Сім повних пакетів теж не були героями. Доки друкувалася друга нога, наш власний підйом споту вже натиснув на індекс, і вхідний базис був на 3–5 bps гірший за знімок, який дав сигнал. Поріг у вісім bps був числом з екрана.
Що ми наступного разу пропустимо
Ми не відвантажимо багатоногу як одну функцію виконання. Те саме правило для perp-spot кері, календаря чи вертикалі колів, яку агент подає двома лімітами і потім святкує, коли обидва «виконуються» по mid.
- Стан ордера на кожну ногу, включно з частковими. Трактувати пакет як батька двох тікетів.
- Таймер max-orphan, який справді спрацьовує, і шлях флетену, який платить два спреди на виході.
- Mark-to-book на залишку, зі стопом, розміреним під залишок. Нетто зібраного пакета — неправильний нотіонал.
- Жодного зарахованого кері, жодного нарахованого фандингу, доки обидві ноги не стоять. Незавершене є незавершеним.
- Не ставити mid-on-mid за замовчуванням для будь-чого з двома тікетами. Якщо вже гадати — гадайте гірше на тонкій нозі і пізніше на другому виконанні.
І ми не пустимо test_pack_is_atomic у репо. Atomic — властивість матчинг-рушія, а не допоміжної функції. BTC-спот і BTC-перпетуал — дві книги, які іноді римуються. Папір — це місце, де вони перестають римуватися, і лише тому сходи взагалі були цікавими.



