В прошлом месяце на выходе генератора стратегий появилась кривая капитала с просадкой 63%. Глубокая, неприятная просадка, а затем чистое восстановление за следующие 6 недель — на такую форму исследователь смотрит, прищурившись, и решает, что её можно пережить, если уменьшить размер позиции. Я прогнал стратегию через маржинальный движок, который учитывает уровни поддерживающей маржи. Счёт ликвидировали на 41-й день, прямо посреди просадки, а всё, что было правее 41-го дня, оказалось выдумкой.
Это самый дорогой класс ошибок бэктеста из всех, что я знаю: он искажает доходность не на несколько базисных пунктов, а удаляет из симуляции состояние, из которого нельзя выйти. Стратегия, способная обнулиться, и стратегия, которая не может обнулиться, — это разные стратегии. Бэктест, который никогда не проверяет маржу, не подскажет, какая из них у вас.
Вот 3 ошибки, которые я встречаю снова и снова.
Ошибка 1: плечо как множитель доходности
Самый распространённый подход. Вы рассчитываете ряд доходности по своим сигналам, решаете использовать плечо 10x и умножаете на него результат. Иногда подход чуть сложнее — размер позиции рассчитывается по формуле equity * leverage / price, — но состояние счёта всё равно представляет собой скаляр, который то растёт, то падает. Нет остатка маржи, нет лимита номинала, нет условия ликвидации.
Последствия неочевидны, поэтому эта ошибка и проходит ревью. Коэффициент Sharpe не зависит от масштаба: при смене плеча с 3x на 30x заголовочное значение не меняется, и исследователь решает, что плечо — бесплатный регулятор, который обменивает волатильность на доходность. Максимальная просадка линейно масштабируется и по случайности арифметики остаётся ниже 100%: ряд умноженных доходностей асимптотически приближается к нулю, но не пересекает его. Умножьте дневное падение -9% на 12: в корректной версии получите -108%, а в наивной — «-108%, но кривая капитала всё ещё выше нуля» — в зависимости от того, начисляли ли вы сложный процент или просто суммировали доходности. Я видел оба варианта.
Упускается то, что плечо применяется не к доходности, а к вашему обеспечению. Обеспечение — конечная величина, за которой биржа следит каждую секунду. Как только оно падает ниже требования по поддерживающей марже, позиция перестаёт быть вашей.
Признак ошибки: если симуляция не может привести к полной потере средств при любом плече, она не моделирует плечо.
Ошибка 2: цена ликвидации, рассчитанная только по плечу
Следующий уровень сложности — добавить проверку ликвидации, обычно примерно так: при плече 20x неблагоприятное движение на 5% съедает маржу, значит, для длинной позиции ликвидируем по цене входа × 0.95. Просто, интуитивно и сразу неверно по 4 причинам.
Площадки бессрочных фьючерсов не используют заданное вами плечо, чтобы определить момент ликвидации. Они применяют ставку поддерживающей маржи, которая повышается вместе с номиналом позиции, согласно таблице уровней. Примерная структура уровней BTCUSDT USDⓈ-M; она меняется, когда биржи пересматривают параметры:
| Номинал позиции (USDT) | Ставка поддерживающей маржи | Макс. плечо |
|---|---|---|
| 0 – 50,000 | 0.40% | 125x |
| 50,000 – 600,000 | 0.50% | 100x |
| 600,000 – 3,000,000 | 1.00% | 50x |
| 3,000,000 – 12,000,000 | 2.50% | 20x |
| 12,000,000 – 70,000,000 | 5.00% | 10x |
Значит, неблагоприятное движение, которое выдержит изолированная длинная позиция, составляет примерно 1/L − MMR, а не 1/L. При плече 20x на втором уровне это 5% − 0.5% = 4.5%. Полпроцента кажется округлением. Но при цене BTC 84,000 это 420 долларов движения цены, а в час резких колебаний 420 долларов отделяют фитиль, который выбил вашу позицию, от того, который не выбил. Каждый фитиль превращается в подбрасывание монеты, и вы заранее настроили его в свою пользу.
Теперь комиссии. Комиссия taker за вход списывается с маржи сразу после исполнения: 0.045% от номинала 500,000 — это 225 USDT при остатке маржи 25,000, что сдвигает цену ликвидации ещё до того, как позиция успела измениться. То же происходит с финансированием, только постоянно, а при расчёте от номинала позиции с плечом оно обходится намного дороже, чем ожидают исследователи:
За 30 дней без движения — ни неблагоприятного изменения цены, ни сделок — позиция израсходует почти пятую часть обеспечения и заметно приблизит цену ликвидации. Вот 3-я и 4-я причины ошибки: при увеличении позиции меняется уровень, поэтому позиция, начатая на уровне 0.5% и увеличенная до уровня 1.0%, получает цену ликвидации хуже той, что вы рассчитали при входе; а кросс-маржа объединяет обеспечение нескольких позиций, поэтому выживание вашей длинной позиции по BTC зависит от того, что происходит с короткой позицией по ETH. Если вы симулируете кросс-маржу как набор независимых изолированных позиций, вы неверно моделируете корреляцию внутри собственного счёта.
Ошибка 3: ликвидация по неверной цене, не в тот момент и с неверным исполнением
Допустим, уровень срабатывания вы определили верно. Теперь вопросы: какая цена его пересекает, когда именно вы её проверяете и по какой цене исполняется закрытие?
Ликвидация срабатывает по mark price — индексу нескольких спотовых площадок со сглаженной базисной составляющей, специально рассчитанному так, чтобы единичный биржевой фитиль не вызывал срабатывание. Ваш стоп-лосс, в зависимости от настроек, скорее всего, срабатывает по цене последней сделки. Эти две цены расходятся как раз в самый важный момент. Во время каскадной ликвидации последняя цена бессрочного фьючерса может на десятки секунд уйти на 1–2% ниже mark price. Бэктест, который запускает и стоп, и ликвидацию по одному минимуму OHLCV, моделирует несуществующую биржу.
В этой ситуации возможны обе ошибки, и они не компенсируют друг друга. Если использовать последнюю цену, ликвидация произойдёт на фитилях, которые биржа проигнорировала. Если использовать mark price, вы не учтёте сценарий, когда двинулась сама mark price: расхождение индекса на спотовой площадке действительно может привести к ликвидации по цене, которая вообще не отображалась на площадке с бессрочным фьючерсом, которым вы торговали.
Теперь об исполнении. Наивные движки закрывают позицию по цене ликвидации и учитывают убыток так, будто сработал стоп. В действительности позиция переходит к движку ликвидации по цене банкротства — уровню, на котором ваша маржа ровно равна нулю и цена хуже уровня срабатывания. Кроме того, взимается комиссия за принудительное закрытие: она зависит от уровня и на нижних уровнях составляет около 1% от номинала. При плече 20x 1% от номинала — это 20% оставшейся маржи. Если движок исполняет ордер ниже цены банкротства, разницу покрывает страховой фонд; на некоторых площадках, если фонд исчерпан, убытки распределяются и среди прибыльных участников рынка. Фактический убыток превышает просадку до цены ликвидации. И превышение максимально именно в те дни, когда стакан был слишком тонким.
Вопрос о порядке событий внутри свечи здесь особенно острый. Если 1-минутная свеча включает и уровень тейк-профита, и уровень ликвидации, бэктест, который сначала проверяет выходы и только потом маржу, спокойно зафиксирует прибыль. Биржа проверяет маржу при каждом обновлении mark price — примерно раз в секунду, до всего остального, что вы могли запланировать.
Что должен учитывать движок
Ничего экзотического для этого не нужно. Нужен учёт состояния. Счёт — это объект с балансом, а не ряд доходности. На каждой свече его состояние обновляется в порядке, соответствующем работе биржи:
- Баланс кошелька и нереализованный PnL — отдельно друг от друга. Маржинальное отношение — это поддерживающая маржа, делённая на маржинальный баланс, который включает нереализованный PnL. Если смешивать эти величины, убыточная позиция выглядит обеспеченной лучше, чем она есть на самом деле.
- Таблица уровней для каждого символа с версиями по датам. Уровни пересматривают. Бэктест 2023 года, запущенный с таблицей уровней за 2026 год, незаметно использует данные из будущего — и обычно в выгодную для стратегии сторону, поскольку со временем биржи в целом снижали требования для крупных активов.
- Ряд mark price, а не только свечи. Если достать историю mark price нельзя, укажите это в результатах и используйте консервативное приближение. Не подменяйте её последней ценой молча.
- Проверка маржи должна идти первой в порядке событий свечи, до стопов, целей, сигналов или ребалансировки.
- Исполнение по цене банкротства плюс комиссия за принудительное закрытие; ставку комиссии берите из той же строки таблицы уровней, что и MMR.
- Поглощающее конечное состояние. Когда капитал обнуляется, прогон заканчивается. Без переноса базового уровня, без «считайте, что трейдер пополнил счёт» и без продолжения ряда с новым номиналом.
Последний пункт вызывает больше всего споров. Кто-нибудь обязательно замечает, что реальный деск внёс бы дополнительное обеспечение, поэтому остановка симуляции — чрезмерно жёсткое допущение. Возможно. Но утверждение «эта стратегия работает, если продолжать вливать деньги после того, как она обнулила счёт» нужно формулировать именно так и проверять отдельно, задав пополнения как явный параметр, а не незаметно принимать их по умолчанию. Когда формулируешь это прямо, большинство понимает, что вовсе не это имело в виду.
Практический результат таков: когда вы прогоняете ту же стратегию на бумаге параллельно с торговлей на бирже, маржинальное отношение, которое показывает биржа, и значение симулятора должны совпадать с точностью до 1–2 базисных пунктов в течение всего дня. Это можно бесплатно проверять каждую минуту для каждой открытой позиции. Это самая дешёвая проверка соответствия во всём стеке, но её почти никто не делает. А когда значения расходятся, права биржа: у вас ошибка, которую стоит найти до того, как просадка найдёт её за вас.
← Все статьи


