Блог им. SergeyBagdasarov_d69
У торгового бота обычно смотрят на итог — сколько он заработал. Мне же понадобился ответ на более скучный, но проверяемый вопрос: по какой цене ордера реально исполнялись относительно той цены, что бот сам указал в заявке. Это другой вопрос, чем «сколько прибыли», и отвечает он не на «хорошо ли торгует стратегия», а на «нет ли у нас систематического проигрыша на самом исполнении».
Ниже — как я это померил по фактическим данным, какие две ловушки на этом пути почти поймали меня самого, и почему получившийся «ноль исполнений хуже лимита» не стоит подавать как достижение.
Источник — локальные копии баз двух боевых ботов (SQLite вместе с файлами журнала изменений WAL), снятые для разбора. Анализ выполнялся на копиях, рабочие базы при этом не менялись. Окно — доступная история ордеров с 29 августа по 22 октября 2026 года. Никаких бэктестов и синтетики: только то, что реально записано по отправленным и исполненным заявкам.
Из всех заявок я оставил лимитные. Причина не вкусовая: у лимитной заявки есть явная граница цены — «не дороже X» на покупку, «не дешевле X» на продажу. Эту границу можно взять из записи заявки в базе и честно сравнить со средней ценой фактического исполнения. У рыночной заявки такой зафиксированной границы нет, и сравнивать её исполнение с каким-либо «ориентиром» — значит сравнивать с числом, которое само по себе ничего не гарантирует.
Важно сразу очертить, с чем я сравнивал и с чем — нет:
То есть это не измерение «полной стоимости исполнения» и не доказательство отсутствия проскальзывания. Это ответ ровно на один вопрос: как соотносится средняя цена исполнения с ценой, записанной в самой заявке.
И сразу честная оговорка про сами поля. Я брал сохранённую цену заявки и среднюю цену исполнения как они лежат в базе. Историю формирования первого поля для каждого исторического ордера я отдельно не восстанавливал. Поэтому результат описывает соотношение двух записанных в базе чисел, а не независимую сверку со всеми сообщениями биржи по каждому ордеру.
Всего лимитных заявок в выборке — 2 242. Первое, что нужно сделать до любой арифметики о ценах, — отделить заявки, которые вообще исполнились, от тех, что не исполнились:
2 242 = 2 219 с исполнением + 23 без исполнения (отмена, ноль исполнения)
Дальше про цены говорим только о первой группе — о 2 219 заявках, у которых есть хотя бы частичное исполнение. Сравнив среднюю цену исполнения с записанной ценой каждой такой заявки (дешевле купить — лучше, дороже продать — лучше), получаем второе разделение:
2 219 = 1 759 по цене заявки + 460 лучше + 0 хуже
Эти два разделения важны, потому что числа отвечают на разные вопросы. 460 — это заявки с улучшением цены среди 2 219 получивших исполнение. Ещё 23 заявки отменены без исполнения и в сравнение цен не входят вообще. Показать их рядом можно; нельзя смешивать их смысл и подавать как числа из одного знаменателя.
При подготовке короткой заметки я сам на этом споткнулся: в черновик про лимитные заявки попало число лучших исполнений из общего подсчёта — 516 вместо 460. Число было настоящим, просто отвечало на другой вопрос: общий подсчёт включал и рыночные заявки. Поймалось это только сверкой выборок перед публикацией. Вывод практический: когда считаешь «лучше/хуже», первым делом фиксируй знаменатель — какие именно заявки вошли в подсчёт, — и не давай числу из одной выборки утечь в текст про другую.
Ещё одна деталь, которую легко замести под ковёр. «С исполнением» — это не «исполнена полностью». Внутри 2 219 есть 22 частично исполненные заявки: часть объёма получена, остаток — нет. Это не то же самое, что отмена без единого исполнения. При сравнении цен я брал среднюю цену уже исполненной части, а не выдавал всю заявку за завершённую.
Я отдельно посмотрел, как боты открывали и наращивали позиции (первые входы и последующие доборы здесь объединены в одну группу) и как выходили из них. Все числа ниже — только по лимитным заявкам с исполнением.
| Что делал бот | С исполнением | По цене заявки | Лучше | Хуже |
|---|---|---|---|---|
| Входы и доборы | 1 327 | 1 101 | 226 | 0 |
| Выходы | 892 | 658 | 234 | 0 |
| Вместе | 2 219 | 1 759 | 460 | 0 |
На выходах лучших цен оказалось 234, на входах и доборах — 226, хотя выходных заявок меньше. Это наблюдение, а не объяснение: по этой таблице нельзя сказать, почему так — из-за движения рынка, выбранных цен или прочих условий исполнения. И уж точно «234 против 226» не значит, что выходы принесли больше денег: здесь считаются случаи, а не суммы, а заявки различаются и по объёму, и по величине улучшения цены.
Лимитная заявка устроена буквально: покупка — «не дороже указанной цены», продажа — «не дешевле». Если сравнивать цену исполнения именно с лимитом этой же заявки и без учёта комиссий, то исполнение хуже лимита просто не соответствовало бы условию заявки. Поэтому ноль худших — это не способность бота «переиграть биржу», а свойство самого инструмента: мы проверили, что записанные исполнения в выборке не оказались хуже записанных лимитов, и получили ожидаемый по определению результат.
Ценность такой проверки не в сенсации, а в том, что она ловит аномалии: если бы «хуже лимита» вдруг оказалось не ноль, это был бы сигнал о баге в учёте, в данных или в логике, а не нормальная торговля.
И отдельно: условие по цене ничего не обещает про объём. Можно получить исполнение по лимиту или лучше, можно — только часть, а можно не получить ничего. Само наличие заявки не означает, что сделка состоялась.
У этих 23 лимитных заявок в базе статус отмены и нулевое исполнение. Это всё, что можно утверждать об их результате в рамках данного подсчёта.
Назвать их «23 упущенными прибыльными сделками» было бы выдумкой: мы не считали, что было бы при другом решении. Отмена не равна убытку, но и исполненной её считать нельзя. К тому же заявка не тождественна позиции — в одной позиции может быть несколько заявок. Причину каждой отмены (не хватило встречного объёма, ушла цена, появилась ли потом другая заявка) я по этим данным не устанавливал и удобное объяснение подбирать не стал.
Важны эти 23 ровно тем, что показывают границу честности отчёта: если показать только цены состоявшихся исполнений, можно незаметно вычеркнуть заявки, которые до исполнения не дошли. Тогда всё сказанное останется правдой, но картина — неполной.
Чтобы число не начало жить своей жизнью, перечислю, чего этот замер НЕ говорит:
Я получил не доказательство идеального исполнения, а более точное описание выборки: среди исполненных лимитных заявок случаев хуже записанной цены не нашлось, часть получила цену лучше, отдельно остались отмены и частичные исполнения.
Поэтому, когда вижу красивое число про исполнение, задаю три вопроса — и советую задавать их к любому такому числу, своему или чужому:
Ответы не делают результат хуже — они показывают, что именно он значит. Хорошая цена важна. Просто она ещё не означает, что заявка превратилась в сделку.
Это разбор конкретного измерения, не рекомендация совершать сделки.
У меня по тестам системы, допустим 100 сделок в месяц. 60 в плюс, 40 в минус. И хорошая эквити.
Сделки, например, покупать доллар по 80 каждый час.
Вопрос, смогу ли я совершить эти 100 сделок с помощью FILL_OR_KILL, чтобы повторить результаты тестов, или тесты этому исполнению не соответствуют?
Вопрос два: если двигать лимитку в сторону исполнения, это будет лучше, чем пропустить сделку?
Задача трех тел, Вы спрашиваете как раз о том, чего наш подсчёт не показывает: что лучше — дождаться исполнения, передвинуть заявку или отказаться от входа. По одним ценам состоявшихся исполнений это не решить. В выборке есть 23 отменённые заявки без исполнения, но что было бы при другом способе входа, я не проверял. Поэтому советовать «двигать» или «пропускать» по этому разбору было бы необоснованно — тут нужно учитывать и сделки, в которые каждый вариант вообще не вошёл.
а все из-за некорректной постановки задачи.
как там было у классиков: чтобы задать правильный вопрос надо знать половину ответа.
Надо сверять цену исполнения не с ценой лимитной заявки, а с «теоретической» ценой, которая должна быть записана алгоритмом тогда, когда появился сигнал на сделку.
Дмитрий Овчинников, Согласен: чтобы оценить, сколько теряется между сигналом и исполнением, нужна цена, относительно которой алгоритм принял решение. Сравнение с лимитом самой заявки на этот вопрос не отвечает. В этом разборе я сопоставил два сохранённых поля — цену заявки и среднюю цену исполнения; получилась проверка записей в базе, а не оценка качества исполнения стратегии. Во вступлении я сформулировал задачу шире, чем позволяет такой замер, — это стоило написать точнее. Полезное здесь — отдельно показать неисполненные и частичные заявки, которые теряются за красивой статистикой цен. Но ни «ноль хуже лимита», ни 460 исполнений лучше сами по себе не говорят, насколько хорошо бот исполняет сигналы.
главное справится с делами до рассвета :)
SergeyJu, Вступление действительно получилось шире самого исследования. Я проверил соотношение записанной цены заявки и исполнения, но начал с вопроса о потерях на исполнении. Это разные задачи — границу между ними стоило обозначить точнее с самого начала.
все меньше реальных авторов и все больше ИИ.
я просто боюсь, а чтобы не бояться пью коньяк :)
__rtx, Замечание про заявки, которые стоят в стакане, и те, что сразу исполняются по встречным предложениям, — по делу. В опубликованном подсчёте такого разделения нет, поэтому вывести из 460 улучшений, сколько заявок сразу забрали встречный объём, я не могу. И да, «ноль хуже лимита» сам по себе не открытие — для проверки записей в базе это ожидаемый результат, и как показатель качества исполнения бота его подавать нельзя. Здесь согласен.
__rtx, Да, здесь я неточно выразился. В обычном стакане исполнение лучше действующего лимита указывает на то, что заявка забрала встречный объём. Моя фраза «не могу вывести» получилась слишком общей.
Но исполнение ровно по лимиту не обязательно означает, что заявка стояла в стакане: сразу исполниться можно и по своей предельной цене. Поэтому разделить всю выборку на активные и пассивные только по этим двум ценам не получится. Тем более у нас подсчёт по заявкам со средней ценой исполнения, а не по каждому отдельному исполнению.
Мы сейчас разрабатываем торговую платформу и обкатываем собственные машины — и на реальных деньгах, и в демо. Для этой статьи взяли именно базы двух машин с реального счёта. Поэтому вопрос для нас практический: разобраться, что действительно произошло с отправленными заявками и что из сохранённых данных можно установить. Спасибо за уточнение про встречный объём — эту часть в предыдущем ответе стоило объяснить точнее.
В отчет брокера можно смотреть.
Если таких сделок больше чем неисполненных, значит бот хромает. Тщательнее надо. Должно быть наоборот. Или использовать костыли - заявки с флагом BoC (только пассивная).
Контекст здесь очевиден.
Биржевая комиссия >> брокерская комиссия
В Т-инвестициях, например, он совсем другой.
Если не знать, как и зачем выставлялись заявки, какие действия были при неисполнении и неполном исполнении, вообще непонятно, что исследовалось и, главное, для чего.
хуже, если все стали бы тарелочницами.
Задача трех тел, с соотношением результата сделки и расходов на исполнение согласен. Только по одной средней сделке я бы способ входа не выбирал: важны ещё размер заявки и доступный объём в стакане.
Мы сейчас обкатываем собственную торговую платформу: есть машины на реальных деньгах и отдельные демо-машины. Поэтому вопрос для нас вполне практический. Но в этой статье замер был уже: посмотрели записанные цены и отдельно учли частичные исполнения и отмены. Сколько преимущества съедает исполнение, этим подсчётом мы ещё не установили.
Вы способ входа фиксируете для всей стратегии или меняете в зависимости от текущей ликвидности?
Я держу лимитную заявку пару-тройку минут до ее исполнения. Как правило всегда наливают. Если нет, то передвигаю ее, либо ее остаток в новый спред.
Позиция держится от нескольких часов до нескольких дней.
Задача трех тел, спасибо, теперь понятно, почему для вас важен полный вход: система тестировалась постоянным лотом, и в реальной торговле вы сохраняете тот же объём.
Просто потому, что борьба за спред предполагает (а) издержки на дополнительное программирование (б) расходы на быстрый доступ и оборудование для него. И для них принципиальны не потери на спреде, а исполнение заявки, поэтому неисполненная будет переставляться, поэтому морока с расщеплением заявок на более мелкие и далее можно дойти до айсбергов и тому подобного.
В этом контексте замкнутое исследование должно включать в себя финрез от правила определения цены заявки при условии, что её надо выставить по не очень свежим данным, то есть с задержкой 0,1 секунды, например, и правила перестановки заявки при неполном исполнении.
И все это непросто потому, что задача зависит как от общей ликвидности инструмента, так и от текущей краткосрочной волатильности его. Что там у ХФТ я не знаю. Да и пограничные с ХФТ области понимаю смутно, не мой хлеб.
SergeyJu, здесь важный момент: если менять правило перестановки, меняется не только цена исполнения, но и набор состоявшихся сделок. Поэтому по одним исполненным заявкам такие варианты полноценно не сравнить — нужно учитывать и частичные исполнения, и то, что осталось без исполнения.
В статье мы разбирали более узкий вопрос по базам двух работающих машин: как записанная цена исполнения соотносится с сохранённым лимитом заявки. Влияние задержки данных и разных правил перестановки на итоговый результат здесь не проверялось.
Для такого сравнения я бы рассматривал оба правила вместе, на одинаковых исходных условиях и с учётом комиссий. Иначе можно получить более выгодную цену у исполненных заявок, но худший общий результат из-за тех, которые так и не исполнились.
пишите и получите отклик
SergeyJu, спасибо за замечания и полезный разговор.
А зря. Такой проверкой Вы получите ответ на вопрос какую «альфу» используете execution или direction(при 1 секунде задержки 100% отделить можно). И соответственно горизонт времени жизни сигнала. Если он длинный и средний результат знатно выше комисии и спреда то direction моменты эксплуатируете это почти всегда масштабируется очень хорошо коррелирует с правильным бэктестом менее подвержено конкуренции ноль затрат на инфраструктуру и дольше живёт(такая альфа). Если execution то то что перечисленно умножается на -1 и ещё возможны неприятные сюрпризы например комиссия в этом контексте может быть лишь частью проблемы и не самой затратной.
__rtx, да, такую проверку стоит сделать. В нашем замере её не было, поэтому выводов об источнике преимущества из него не делаем. Только «выдержало задержку» я бы пока считал признаком, а не доказательством заработка именно на направлении. Вы обычно проверяете несколько задержек и смотрите, как меняется результат?
Т.е. для конкуренции на равных с этими ребятами нужны вначале мозги, потом большие вложения в инфраструктуру, а потом и неформально близкие знакомства с ребятами, которые там.
Тут знаете, как в шоу бизнесе. Не достаточно быть томной и красивой, не достаточно, чтобы за тебя забашлял папик, иногда таки придется переспать с продюсером.