2 Сентября 2026 · микроструктура

Ваш лимитный ордер не исполнился: письмо об очереди, maker-бэктестах и выдуманном Sharpe

Ваш лимитный ордер не исполнился: письмо об очереди, maker-бэктестах и выдуманном Sharpe

На прошлой неделе вы прислали ноутбук: тот же сигнал, та же вселенная, те же 14 месяцев данных по бессрочному контракту BTCUSDT. Изменилось только исполнение. Вы перестали пересекать спред и начали выставлять лимитные заявки на покупку на 1 тик ниже, и Sharpe вырос с 0.42 до 2.14. Вы спросили, правда ли это или вы что-то сломали.

Вы что-то сломали. Хочу подробно показать, где именно, потому что ошибка занимает одну строку, а вывод из неё гораздо шире.

Вот ваше правило исполнения, скопированное из движка:

if bar.low <= limit_price: fill(limit_price)

Оно говорит: если за эту минуту прошла сделка по цене не выше моей, значит, мой ордер исполнился по моей цене. На самом деле оно означает лишь, что цена коснулась вашего уровня. Касание — не исполнение. Между ними стоит очередь, а вы смоделировали её так, будто она пуста.

Кто стоит перед вами

Когда вы выставляете заявку на покупку BTCUSDT perp по 84,120.0, она встаёт в конец очереди уже выставленных там заявок. На верхнем уровне стакана этого контракта обычно стоит от 4 до 30 BTC, в зависимости от времени суток; медиана за ваше окно выборки — около 12 BTC. Размер вашей заявки — 0.4 BTC. Чтобы состоялась сделка, продавцы должны ударить по этому уровню на совокупный объём, достаточный, чтобы пройти всех, кто встал в очередь раньше вас, и сделать это до того, как уровень отменят у вас из-под носа или рынок уйдёт вверх.

Значит, бэктест должен спрашивать не «доходила ли цена до 84,120.0», а «исполнились ли продажи по рынку объёмом хотя бы 12.4 BTC по цене 84,120.0, пока моя заявка стояла там». Это совершенно разные события. В ваших данных частота таких событий различается примерно втрое.

12.4 BTCмедианный объём перед вами в очереди при касании уровня
100%доля исполнений, заложенная в бэктесте
31%касания уровня, при которых очередь перед вами была пройдена
4,180сделок, из которых осталось примерно 1,300

Три правила исполнения — три разные стратегии

Я повторно прогнал ваш сигнал с теми же входами и тремя моделями исполнения. Та же альфа, те же комиссии, то же фондирование. Изменилась только логика исполнения.

Правило исполненияИсполненияСредний edge за 60 с после исполненияSharpe
Касание: low <= limit4,180+2.6 bp2.14
Строгое прохождение: low < limit - 1 tick1,712+0.4 bp0.61
Симуляция очереди по объёму на уровне1,306+0.9 bp0.77

Средняя строка — это грубое исправление, к которому все обращаются в первую очередь: считать исполнение только если рынок проторговал цену строго ниже вашего уровня, исходя из логики, что раз цена прошла дальше, значит, она должна была исполнить вашу заявку. В целом это верно и устраняет большую часть фантазийных исполнений. Но здесь есть серьёзное смещение, о котором поговорим ниже.

Нижняя строка — модель, которую вам стоит реализовать. Для неё не нужны поток L3 и реконструкция каждой заявки. Большая часть необходимых данных у вас уже есть.

Симуляция очереди на данных aggTrades

Используйте поток агрегированных сделок вместо klines. Каждая запись содержит цену, объём, временную метку и флаг стороны maker, который показывает, покупал или продавал агрессор. Этого достаточно для рабочей симуляции собственного пассивного ордера:

  1. При выставлении ордера снимите объём заявок в стакане на вашей цене. Если у вас есть только снимок стакана раз в 100ms, возьмите последний снимок: погрешность мала по сравнению с той, которую вы сейчас исправляете.
  2. Задайте queue_ahead = resting_size. Будьте пессимистичны и считайте, что вы в самом конце очереди. Так и есть, если только вы сами не создали этот уровень.
  3. Обрабатывайте поток сделок. Каждая сделка, где агрессор — продавец и цена не выше вашей, уменьшает queue_ahead на свой объём. Когда значение становится отрицательным, ваш ордер исполнен по вашей цене в этот момент времени.
  4. Если цена отходит на 1 тик, не обнуляйте очередь. Постепенно уменьшайте её. Когда уровень устаревает, часть заявок перед вами отменяют, часть остаётся. В моих реконструкциях снижение очереди на 30-40% за каждую полную секунду вне лучшей цены совпадало с данными лучше, чем любой из крайних вариантов.
  5. Если стратегия отменяет и выставляет заявку заново, моделируйте повторную заявку как новый ордер в конце новой очереди. Именно этот шаг часто пропускают — здесь и скрывается оставшаяся часть фантазии.

На шаге 2 вам захочется со мной поспорить. Да, иногда вы оказываетесь почти в начале очереди, потому что выставили заявку сразу после появления уровня. Хорошо — измеряйте это, а не предполагайте. Записывайте объём заявок при выставлении и по данным определяйте, какая доля ваших ордеров действительно выставлена заранее. В вашей стратегии это было 11%: сигнал срабатывает после движения, а значит, уровень, к которому вы присоединяетесь, уже существует и на нём уже есть очередь.

Исполнения, которых вам хотелось бы избежать

Теперь самое важное — и причина, по которой правило строгого прохождения даёт смещённую оценку.

Подумайте, когда ваша заявка на покупку исполняется целиком. Это происходит, когда давление продавцов достаточно велико, чтобы съесть весь объём на уровне. То есть по определению именно тогда рынок движется вниз через вашу цену. Исполнения, в которых вы уверены больше всего, — те, после которых вы сразу оказываетесь в невыгодной позиции.

Разбейте исполнения по способу, которым они произошли, и измерьте mark-out через 60 секунд:

Тип исполненияДоля исполненийMark-out через 60 с
Уровень проторгован, цена отскочила вверх38%+3.1 bp
Уровень проторгован, цена не изменилась21%+0.2 bp
Цена прошла дальше более чем на 2 тика41%−2.4 bp

Наивный бэктест дал вам все три группы и оценил их как бесплатные исполнения. Правило строгого прохождения почти целиком оставляет третью группу — поэтому edge в нём просел сильнее, чем в симуляции очереди. Ни один вариант не верен. Симуляция очереди даёт реалистичное соотношение групп, а именно это и решает всё: пассивное исполнение приносит вам спред, но обходится в неблагоприятный отбор, и ваша настоящая стратегия определяется соотношением этих двух факторов.

Подход старого отдела торговли акциями до сих пор актуален: выставляя ордер maker, вы продаёте рынку бесплатный опцион. Его исполняют, когда это выгодно. Ваш бэктест учитывал полученную премию, но забывал, что у опциона есть ещё и выплата.

Неисполненные сделки меняют саму стратегию, а не только издержки

Вот о чём я особенно хочу, чтобы вы задумались. Если плохо моделировать исполнение taker, сделки будут те же, но цены — неверные; поправка на комиссии исправит большую часть ошибки. Если плохо моделировать исполнение maker, вы получите совершенно не тот набор сделок. Примерно 2,900 из ваших 4,180 входов так и не состоялись бы. Среди них были некоторые из лучших сигналов — на свечах с резким всплеском и разворотом, именно в таких ситуациях рынок уходит без вас.

Значит, для ветки с неисполненным ордером нужна настоящая логика. Что делает стратегия, если сигнал устарел, а вход так и не исполнился? Догоняет рынок ордером taker, оплачивая спред и импакт? Выставляет новую заявку ниже, соглашаясь на другую цену входа? Пропускает сделку и остаётся без позиции? Каждый вариант даёт заметно отличающуюся кривую капитала, и ни один из них не сводится к «считать ордер исполненным». В наших прогонах реалистичное правило догоняющего входа (пересечь спред через 20 секунд без исполнения, ограничив проскальзывание уровнем 3 bp) вернуло примерно треть пропущенных сделок и около половины разрыва между наивным Sharpe и Sharpe по симуляции очереди. Результат действительно интересный, и увидеть его можно только тогда, когда модель исполнения достаточно реалистична, чтобы этот вопрос имел смысл.

Простая проверка за 10 минут: возьмите журнал торговли на бумажном счёте и бэктест за одно и то же окно. Сравните долю исполненных ордеров, а не PnL. Если в бэктесте исполняется 100% пассивных ордеров, а на бумажном счёте — 34%, это не сравнение стратегий, а ошибка модели исполнения. Исправьте её, прежде чем смотреть хотя бы на один показатель доходности.

Ещё 2 небольших момента

Отклонения post-only. Если вы используете post-only, чтобы гарантировать комиссию maker, а стакан меняется между вашим решением и подтверждением биржи, ордер отклоняется вместо того, чтобы встать в очередь. В наших журналах торговли на бумажном счёте в обычные часы так отклонялись 3-6% попыток по BTCUSDT и больше 15% в минуту после публикации индекса потребительских цен США. Отклонённый ордер не является ни исполненным, ни неисполненным ордером в очереди: такой сделки вообще не было. Если в бэктесте не предусмотрено это состояние, число сделок завышено именно в тех рыночных режимах, которые вас интересуют больше всего.

Предотвращение самоторговли и ваш рыночный след. С размером 0.4 BTC вы не сдвигаете рынок BTCUSDT, так что этот фактор можно пропустить. Но вы упоминали, что хотите запустить стратегию на бессрочном контракте с альткоином средней капитализации, где номинальный объём верхнего уровня стакана часто меньше $15k. Там ваша заявка уже составляет заметную часть очереди, а зафиксированный объём перед вами в очереди учитывает присутствие вашей предыдущей заявки. Когда ваш размер превышает примерно 10% стоящего в стакане объёма, симуляция должна учитывать, что другие участники реагируют на вас. И, честно говоря, на этом этапе я бы больше доверял торговле на бумажном счёте, чем любой симуляции, которую можем написать вы или я.

Повторно прогоните стратегию с симуляцией очереди и пришлите мне таблицу mark-out с разбивкой по типам исполнения. Если основная часть edge всё ещё приходится на отскоки, а сделки с прохождением уровня не съедают его целиком, возможно, у вас есть что-то, что стоит проверить на бумажном счёте. Если же всё держалось на тех 2,900 исполнениях, которых никогда бы не было, лучше узнать об этом сейчас, чем через 4 недели наблюдений за бумажным счётом, который не делает того, что обещал ноутбук.

лимитные ордерапозиция в очерединеблагоприятный отборбэктестингкриптофьючерсы
ПоделитьсяXLinkedInFacebookRedditHacker NewsWhatsAppTelegramEmail
← Все статьи