Разработка системы журналирования сделок AI-трейдинг-бота

Расхождение в сделках бота всплывает позже — когда восстановить картину уже нельзя. Мы проектируем и внедряем журналирование сделок под ключ: сквозной след каждой операции, готовые схемы и регламенты разбора. Разбор одного инцидента сокращается в разы.
Часто задаваемые вопросы

Почему журналирование сделок бота ломается на пиках

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

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

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

Ниже разбираем, как это закрывается.

Что даёт сквозной след сделки в торговой системе

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

Время разбора инцидента определяется тем, сколько нужно, чтобы ответить на вопрос «почему бот сделал именно так». По идентификатору поднимается вся цепочка от сигнала до исполнения: видно, какое правило сработало, какие данные были на входе, что ответила площадка. Разбор переходит из ручной сверки логов в один запрос — десятки минут вместо половины рабочего дня.

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

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

Режимы записи журнала: сравнение вариантов

Режим записи журнала определяет, что произойдёт с данными при обрыве канала, рестарте процесса или переполнении диска. Синхронная и пакетная запись — это обмен между задержкой на сделку и риском потерять последние события, поэтому выбор режима мы делаем не «на глаз», а от требований к целостности записей в конкретном контуре.

Вариант Когда подходит Ограничения
Синхронная запись Невысокая частота сделок, жёсткие требования к полноте: событие считается подтверждённым только после фиксации Каждая сделка ждёт запись в хранилище; при недоступности приёмника растёт задержка, часть сигналов приходит с опозданием
Запись через буфер (пакетная) Плотный поток событий и котировок, нужна буферизация событий и группировка перед отправкой При аварийном завершении теряется последний неотправленный пакет — нужен сброс по таймауту и контроль размера буфера
Локальный файловый журнал на стороне бота Автономный режим, нестабильный канал: запись идёт в файл рядом с процессом Файлы растут, нужны ротация и очистка; разбор инцидента требует выгрузки и сопоставления с централизованным журналом
Гибридная схема Продакшн, где важны и скорость реакции, и полнота истории: локальная фиксация плюс асинхронная отправка в централизованный журнал Сложнее настройка: две точки хранения, правила слияния и дедупликации, контроль расхождений между ними

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

Как мы строим журналирование сделок: этапы работ

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

  1. Аудит источников событий. Инвентаризируем, откуда приходят данные: торговый модуль, шлюз брокера, поток котировок, риск-контроль. Для каждого источника фиксируем момент события, точность метки времени и признак, по которому запись опознаётся повторно.
  2. Проект формата записи. Согласуем схему: идентификатор сделки, время, инструмент, сторона, объём, цена, статус, причина отклонения, ссылка на родительское событие. Отдельно — правила версионирования, чтобы записи прошлых периодов читались новым кодом.
  3. Подключение источников событий. Ставим адаптеры, пишем журнал в режиме только на добавление и буферизуем на диск: обрыв канала не должен «съедать» события, а повторная доставка — создавать дубли.
  4. Проверка полноты журнала. Сверяем число записей с независимыми счётчиками — подтверждениями брокера, движением баланса, числом отправленных заявок. Прогоняем журнал на исторических данных и воспроизводим торговый день.
  5. Проверка поведения при сбоях. Смотрим рестарт, обрыв связи и переполнение диска: что теряется, что восстанавливается, сколько занимает сверка после перезапуска.
  6. Передача системы заказчику. Отдаём схему хранения, доступы, регламент ротации и архивации, порядок разбора инцидента — и обучаем команду читать журнал без нас.

Артефакты журнала: схемы, правила, регламенты

Журнал без описания превращается в набор таблиц, к которым через полгода никто не решается притронуться. Поэтому вместе с системой передаём комплект артефактов, по которым записи можно читать, дополнять и разбирать без участия автора.

  • Формат записи событий. Поля, типы, обязательность, правило пустых значений и версионирование схемы: новые поля добавляются, не ломая чтение старых записей.
  • Схема потоков событий. От постановки заявки до исполнения: точки записи, поведение при обрыве канала, правило «событие записано целиком или не записано вовсе».
  • Политики доступа к журналу. Роли и права, разделение чтения и выгрузки, запрет правки записей, отдельный контур для чувствительных полей.
  • Сроки хранения записей. Горячее окно для ежедневного разбора, архив для сверок, автоматическая очистка по истечении срока с журналом самих удалений.
  • Регламент разбора расхождений. Как фиксируется расхождение между намерением и исполнением, кто эскалирует, в какой срок и что попадает в список исправлений.
  • Правила сверки с отчётами площадок. Ключи сопоставления, приоритет источника при конфликте, порядок разбора несовпадений.
  • Инструкция по восстановлению. По каким записям и в каком порядке восстанавливается состояние позиций после сбоя и как проверяется полнота.

Каждый артефакт версионируется и лежит рядом с кодом: изменение логики и правка описания — одна задача, а не два несвязанных дела. Именно это делает журнал пригодным для передачи другой команде и для проверки.

Кейс: как расхождение в сделках нашли по журналу

Расхождение заметили не сразу: бот показывал позицию закрытой, а у брокера она оставалась открытой. Журнал тогда фиксировал только факт отправки ордера — без идентификатора брокера, состояния до и после и версии правил риск-контроля, поэтому причину искали по косвенным признакам и в среднем тратили на это полтора суток. Доля необъяснённых расхождений держалась около 9% от дневного числа сделок, часть операций закрывали вручную.

Что изменили в схеме журнала

Каждой сделке присвоили сквозной идентификатор, одинаковый у бота и в ответах брокера. Журнал перестал перезаписываться: он только дополняется переходами состояний (создан, отправлен, подтверждён, исполнен частично, закрыт), а рядом ложатся сырые ответы брокера, версия риск-правил на момент сделки и метка времени. Хранилище — PostgreSQL с неизменяемыми записями и контрольной суммой цепочки: подмена или потерянный фрагмент видны сразу, а не на месячной сверке.

Появилось воспроизведение по журналу: состояние бота восстанавливается на любую метку времени, поэтому сбой локализуется сравнением двух прогонов. Первый же разбор занял 20 минут вместо полутора суток — причиной оказался конфликт между отменой и частичным исполнением.

Результат в метриках

Доля необъяснённых расхождений упала с 9% до 0,4%, время локализации причины — с 36 часов до 20 минут. Восстановили 340 операций за квартал, ни одну не пришлось сверять вручную.

Что делать, если бот уже пишет события в файлы?

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

Вариант Когда подходит Ограничения
Оставить файловый журнал Один процесс, разбор вручную, короткий срок хранения Потери при ротации и падении, нет единого поиска по нескольким ботам
Добавить выгрузку записей Нужен архив без перестройки бота, файл остаётся основным Задержка между событием и архивом, при обрыве выгрузки разрыв сохраняется
Централизованное хранение Несколько ботов, разбор спорных сделок, длительное хранение Требует доработки отправки событий и контроля доставки

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

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

Технические сомнения перед стартом внедрения

Перед стартом поднимают одни и те же вопросы: не просядет ли торговый контур, что будет с потерянными событиями, кто получит доступ к журналу и как убедиться, что записи не задвоятся. Ниже — как каждый из них закрывается.

Не создаст ли журналирование нагрузку на торговый контур?

Запись выносим за критический путь: бот отдаёт событие в асинхронную очередь, сохранение идёт отдельным контуром. Время принятия торговых решений не меняется, а глубину очереди и долю отказов видно на графиках Prometheus.

Что делать с пропущенными событиями после сбоя?

Каждое событие получает монотонный идентификатор, бот хранит последний подтверждённый. После перезапуска он досылает пропущенный диапазон и сверяет его с историей исполнения — журнал сходится без ручной склейки.

Как разграничивается доступ к журналу сделок?

Роли разделены: стратег видит свои сигналы, риск-контроль — агрегаты, аудит — полный лог. Доступ выдаётся сервисными учётными записями с ограниченным сроком, экспорт фиксируется отдельным следом.

Как убедиться, что записи не дублируются?

Идемпотентный ключ из идентификатора сделки и события отсекает повтор на уровне уникального индекса хранилища. Дополнительно идёт сверка: число записей за период против числа исполненных заявок.

Можно ли внедрить без остановки торговли?

Да. Сначала журнал включается в теневом режиме: пишется, но не влияет на решения бота, затем переключается чтение отчётов. Откат — снятие флага, логика торговли не затрагивается.

Обсудим задачу и вернёмся с оценкой и планом

Чтобы оценка и план работ опирались на факты, а не на предположения, на первом разговоре достаточно показать, как сделки фиксируются сейчас. Мы смотрим на источники событий, формат хранения и на то, какие вопросы журнал должен закрывать: разбор ошибочных входов, восстановление истории за произвольный период, сверку с выписками брокера.

Для предметной оценки нужны исходные данные:

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

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

Направления AI-разработки

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    816
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142