В конфиге стояло starting_capital: 100000, а ось y графика говорила USD. Каждое исполнение в логе было числом в USDC. Одиннадцать месяцев этого прогона два факта жили в одной ячейке, и ни у кого не было причины их развести.
Потом рухнул Silicon Valley Bank, Circle заявила, что $3.3 млрд резервов USDC застряли там, и USDC торговался по 87 центов. Наша «долларовая» книга пошла вверх. BTC в цене USDC рванул. Перп напечатал зелёное. Кэш, который мы считали безрисковыми 100,000, и оказался тем, что двинулось.
Я до сих пор ловлю исследовательские пайплайны, которые помечают USDC, USDT и USD как один столбец. Это тот уикенд, разобранный в том порядке, в каком его видел леджер.
Строка, в которой стояло $100,000
Бумажный счёт, BTC-перпетуал с маржой в USDC, скромный лонг. 30,000 USDC BTC-ноционала, около 1.5 BTC по пятничной марке около $20,150. Остальные 70,000 лежали как обеспечение в USDC. Недозагружены нарочно. Мы прогревали стратегию. Остальной стек сидел в кэше.
Функция NAV делала то, что делают почти все такие функции. Складывала кэш в USDC с нереализованным pnl в USDC и писала сумму в ряд с именем equity_usd. В конфиге был комментарий, который я написал сам:
stablecoins = 1 доллар. не считай FX дважды.
Я имел в виду: не превращай funding-платёж в USDT во второе долларовое движение, конвертируя его дважды. На деле я заморозил FX-курс на 1.0 до конца файла.
quote_asset = "USDC"
Площадка была USDC-площадкой. Исполнения, маржа, funding, ликвидационные марки, размер лота, min notional — всё это. Hyperliquid, dYdX v3, Coinbase International, ещё дюжина других — та же схема. Исследовательский ноутбук импортировал свечу BTCUSDC и свечу BTCUSD, торговал по первой, а на график подписывал вторую, потому что в долларах график выглядел профессиональнее.
И вот мелкая, глупая деталь, о которой я до сих пор думаю в странные часы: дефолтный заголовок оси в библиотеке графиков — «USD». Я его так и не переопределил. Люди проходили мимо экрана и читали единицу, которой у ряда не было.
Пятничный вечер, твит Circle
10 марта 2023 года. SVB уже нет. Circle раскрывает, что $3.3 млрд её тогдашних ~$40 млрд резервов USDC лежали в рухнувшем банке, около 8%. Coinbase той же ночью приостанавливает конвертации USDC. Погашение, собственно механизм пега, закрыто на уикенд.
В бэктесте не было строки ни на что из этого. Ни флага остановки погашения, ни поля резервов эмитента, ни «могу ли я получить доллары за этот токен до понедельника». Был close. Пятничный close в нашем FX-файле всё ещё был 1.0000, потому что FX-файл был заглушкой, которая возвращала 1.0 на всё, чей тикер начинался с USD.
Премию USDT я привык считать шумом из учебника — тем, что упоминаешь в сноске и дальше игнорируешь. Однажды мы двадцать минут спорили на кухне, стоит ли 1.03 «проводить по книгам». Мы оба занимались редактурой. Пара — это ряд. Сохраняй его. Хватит по нему голосовать.
0.8789
Субботнее утро. Лоу USDC на CoinGecko — $0.8789. Другие ленты печатают $0.87. Несколько DEX-пулов проходят через $0.80. DAI, набитый USDC как обеспечением, едет вниз следом. USDT идёт в другую сторону: $1.03 на части экранов, $1.06 на Kraken.
Заглушка FX-файла по-прежнему возвращает 1.0. Часы бэктеста знают, что сейчас суббота. Они не знают, что единица книги теперь — проблемный токен.
Принт Kraken на 26,000
Bitstamp BTC/USD кружит вокруг $20,200. Kraken BTC/USDC печатает выше $26,000. Это один и тот же биткоин. Второе число — долларовая цена, делённая на сломанный пег, плюс тот воздух, который тонкая книга USDC добавляет в субботу.
Наша площадка маркировала перпетуал в USDC. Возьмём консервативную ленту: BTC/USDC около $23,000, то есть $20,200 / 0.88. Лонг на 1.5 BTC в плюсе примерно на 4,300 USDC. Счёт в нативных единицах: 104,300 USDC. Движок рапортует $104,300, уикенд +4.3%, и сверху садится funding-платёж в той же единице.
В долларах 70,000 USDC простаивающего обеспечения стоят около $61,600. Все 104,300 USDC стоят около $91,800. Книга, которая «заработала» 4.3%, потеряла 8% долларового капитала, пока в основном сидела в кэше.
Доходность уикенда была сменой единицы, а не сменой удачи. Лонг BTC/USDC в день падения USDC механически лонгует депег. Эту ставку можно хотеть. Её нельзя обнаружить задним числом в ряде с меткой USD.
cash_usd = cash_usdc * 1.0
Эту строку я храню. Она исполнялась на каждом баре, включая бары, где USDCUSD был 0.87. Умножение на 1.0 ощущается как no-op — поэтому оно переживает код-ревью. Это FX-марка. Замороженная.
70,000 USDC обеспечения ни разу не породили сделку, поэтому ни модель проскальзывания на них не смотрела, ни таблица комиссий, ни funding. Кэш должен быть скучным столбцом. На этой книге он был крупнейшим риском — и единственным, который мы не оценивали.
Maker-ребейт на исполнениях BTC в тот уикенд был 2 или 3 bps, если вообще исполнили. Движение котировочного актива — 1,300 bps. У нас целый стек на 3 и заглушка на 1,300.
Зелёный уикенд на графике
Утро понедельника, Slack, до того как кто-либо открыл USDCUSD: мы напечатали 4% за ночь. Кто-то спросил, не увеличить ли бумажный размер. Я посмотрел на график, потом на Kraken, потом на заглушку.
| Статья леджера | Что сохранил движок | Суббота, натив | Тот же момент, в долларах |
|---|---|---|---|
| Простаивающее обеспечение | 70,000 USDC × 1.00 | 70,000 USDC | ~$61,600 |
| Марка BTC-перпа | BTCUSDC last | ~23,000 USDC (26,000 на Kraken) | BTCUSD ~$20,200 |
| Нереализованное на 1.5 BTC | +4,300 USDC | +4,300 USDC | в основном пег, немного биткоина |
| Отчитанный NAV | 104,300 «USD» | 104,300 USDC | ~$91,800 |
У USDT-книг была обратная карикатура. USDT торговался с премией, поэтому плоский счёт с маржой в USDT показывал долларовый плюс, который он тоже не заработал, и марку BTCUSDT, слишком низкую относительно BTCUSD. Если в тот уикенд ранжировать два «одинаковых» BTC-перпа — один на USDC-площадке, другой на USDT-площадке, — вы ранжировали котировочные токены и называли это альфой.
Бумажный счёт — тот, что реально держал USDC, — был ближе к правде, чем исследовательский дашборд. Он никогда не называл себя долларами. Это сделали мы в экспорте.
Экспорт, который мы выгрузили в понедельник
USDC пополз обратно к $1 после того, как Treasury, Fed и FDIC заявили, что вкладчики SVB будут полностью возмещены, и после того, как Circle сказала, что покроет недостачу. К понедельнику пег был в основном залатан. Заглушка FX-файла, которая 48 часов ошибалась, снова была права.
Но дневные бары это спрятали. Сэмпл 00:00 UTC 11 марта застал USDC уже отошедшим или уже отскакивающим — в зависимости от вендора, — а сэмпл 12 марта был другим кино. Если хранить только NAV, а не марку котировки, нельзя было восстановить, что именно. Уродливый уикенд сплющился в всплеск достаточно мелкий, чтобы его игнорировать, — что мы и сделали, потому что сэмплировали по дням.
Что мы теперь держим на книге — три ряда, всегда, даже когда они месяцами лежат друг на друге:
- NAV в котировочном активе площадки — единица, которую реально можно вывести.
- Котировочный актив против USD, настоящая лента, включая субботу, включая 0.87, включая часы, когда Coinbase не конвертировал.
- NAV в долларах — как производный ряд, чтобы у метки USD на графике был родитель.
Если бэктест пишет агент, проверка та же. Спросите у него котировочный актив каждой позиции и кэша. Если он отвечает USD на USDC-площадке, прогон — не долларовый. Март 2023 года — та неделя, которую я проигрываю, когда мне говорят, что различие не двигает Sharpe. Оно сдвинуло этот Sharpe сильнее, чем стратегия, а стратегия как раз была той частью, на которую у нас были модели.
← Все статьи


