Последние полтора года я строю торговый движок. И чем дальше захожу, тем меньше мне вообще нравится определение «торговый бот».
Поставить ордер — не самая сложная часть.
Мне нужна система, в которой стратегия не начинает понимать рынок по-другому только потому, что вчера мы исследовали её на истории, а сегодня доверили ей реальные деньги.
Отсюда и появился детерминизм.
Backtest, dryrun и live не должны магическим образом показывать одинаковый PnL. Это было бы нечестно: исторический поток, живой рынок и реальные исполнения на бирже неизбежно отличаются.
Требование у нас другое: одинаковые факты должны иметь одинаковый смысл.
Если взять одно исходное состояние, одну конфигурацию и подать одну последовательность фактов, движок должен одинаково их понять и получить одно следующее состояние.
Среда меняется. Семантика ядра — нет.
Внутри вся эта махина постепенно свелась к трём довольно простым ролям.
Стратегия решает, что делать. Execution — руки системы. Portfolio — правда о капитале.
Стратегия принимает решение. Execution превращает его в действие: моделирует исполнение в backtest, работает с живым рынком в dryrun, а в live будет отправлять реальные заявки на биржу и принимать обратно факты исполнения.
Portfolio отвечает уже не за намерения, а за то, что произошло на самом деле: какие позиции существуют, кому они принадлежат, что исполнилось, какой получен PnL и как каждый новый факт изменил состояние портфеля.
В итоге огромное количество архитектуры укладывается в одну строчку:
Долгое время части этой цепочки существовали отдельно: контракты, бухгалтерия, исполнение, восстановление после перезапуска, работа с неизвестным состоянием.
Теперь они впервые замкнулись в один BACKTEST-контур.
Стратегия получает состояние → принимает решение → решение исполняется → результат становится новым фактом Portfolio → собирается следующее состояние → стратегия снова получает управление.
Круг замкнулся.
Причём за время разработки один принцип стал для меня почти важнее самого happy path:
если система чего-то не знает — она не имеет права это придумать.
UNKNOWN здесь не превращается в ноль, пустое значение или удобную догадку. Если подтверждённого факта недостаточно, контур должен остановиться.
И мне, если честно, гораздо спокойнее от системы, которая иногда говорит «не знаю», чем от системы, которая любой ценой делает вид, что всё понимает.
Мы строим не просто хороший backtester.
Мы пытаемся убрать разрыв между «я исследовал эту стратегию» и «эта стратегия торгует моими деньгами».
Вот ради этого вся конструкция и существует.
Стратегию нужно провести через историю, затем через живой рынок и в итоге до реального исполнения — не переписывая по дороге её смысл и бухгалтерию.
И здесь важно не начать праздновать раньше времени 😄
Сегодня доказан BACKTEST.
Не dryrun. Не live. Реальная биржа ещё не получила право исполнять решения этого контура.
И я хочу именно так вести этот канал дальше: показывать не красивую картинку того, что когда-нибудь должно получиться, а границу того, что система действительно умеет сегодня.
Когда будет доказан dryrun — напишу dryrun.
До live дойдём тогда, когда движок заслужит право работать с реальным капиталом.
Сейчас начинается следующий большой этап: через готовый Portfolio пойдёт первая полноценная стратегия. Дальше — адаптеры, живой рынок и весь путь, который теперь появился и на обложке:
BACKTEST → DRYRUN → LIVE
И вот там будет настоящий экзамен архитектуры.
Не совпадёт ли случайно PnL. А останется ли одна семантика, когда сохранённая история сменится живым рынком, а моделируемое исполнение — реальными фактами биржи.
same state + same config + same facts → same next state
Если это выдержит, всё огромное количество контрактов, UNKNOWN, authority, provenance, certification и прочей архитектурной радости 😄 наконец сложится в довольно простую вещь.