Почему журналирование сделок бота ломается на пиках
Журналирование сделок чаще всего ломается не на спокойном рынке, а в момент всплеска: стратегия генерирует в десятки раз больше событий, чем обычно, а запись идёт через буфер в памяти с пакетным сбросом на диск. Пока поток ровный, всё выглядит целым. Как только буфер переполняется или процесс перезапускается по здоровью, часть записей теряется безвозвратно — причём молча, без ошибки в самом журнале.
Вторая поломка — разрыв серии записей. Каждое событие должно иметь сквозной номер и ссылку на предыдущее, иначе журнал перестаёт быть воспроизводимой цепочкой. Если нумерация сквозная только в пределах процесса, а не в пределах торговой сессии, после рестарта номера начинаются заново и наложатся на старые — при разборе невозможно отличить два разных ордера с одинаковым идентификатором.
Третье и самое дорогое — расхождение сигнала и исполнения. Сигнал фиксируется на стороне стратегии, исполнение — на стороне брокера, у каждого свой источник времени. Итог: в журнале видно условие входа, но не видно фактической цены и времени постановки, а стоп в отчёте сработал иначе, чем в записи. Восстановление хода сделки превращается в гадание, и на вопрос «почему позиция закрылась не там» ответить нечем.
Ниже разбираем, как это закрывается.
Что даёт сквозной след сделки в торговой системе
Сквозной след сделки — это запись на каждое состояние заявки: сформированный сигнал стратегии, момент отправки, ответ площадки, факт и объём исполнения, частичное закрытие, отмена. События сшиваются единым идентификатором корреляции и попадают в журнал только на дозапись — переписать историю задним числом нельзя, можно лишь добавить новое событие. Такой журнал становится источником истины и для разбора сбоев, и для отчётности.
Время разбора инцидента определяется тем, сколько нужно, чтобы ответить на вопрос «почему бот сделал именно так». По идентификатору поднимается вся цепочка от сигнала до исполнения: видно, какое правило сработало, какие данные были на входе, что ответила площадка. Разбор переходит из ручной сверки логов в один запрос — десятки минут вместо половины рабочего дня.
Восстановление истории операций держится на том, что журнал пишется в двух местах: рядом с торговым ядром, для мгновенного доступа, и в аналитическом хранилище на ClickHouse — для выборок по месяцам. Расхождение между позицией системы и данными площадки перестаёт быть необъяснимым: доля сделок с полностью восстановимой историей приближается к пределу, кроме случаев, когда площадка не вернула ответ вовсе.
Контроль исполнения заявок и проверка торговой стратегии опираются на одну и ту же запись. Журнал прогоняется заново на накопленной истории: тот же набор правил и параметров даёт ту же последовательность решений, что и в реальной торговле, поэтому изменение логики проверяется за один прогон, а не за недели наблюдения. Заодно уходит класс отказов, который раньше не закрывался ничем — спор о том, чей сбой привёл к расхождению.
Режимы записи журнала: сравнение вариантов
Режим записи журнала определяет, что произойдёт с данными при обрыве канала, рестарте процесса или переполнении диска. Синхронная и пакетная запись — это обмен между задержкой на сделку и риском потерять последние события, поэтому выбор режима мы делаем не «на глаз», а от требований к целостности записей в конкретном контуре.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Синхронная запись | Невысокая частота сделок, жёсткие требования к полноте: событие считается подтверждённым только после фиксации | Каждая сделка ждёт запись в хранилище; при недоступности приёмника растёт задержка, часть сигналов приходит с опозданием |
| Запись через буфер (пакетная) | Плотный поток событий и котировок, нужна буферизация событий и группировка перед отправкой | При аварийном завершении теряется последний неотправленный пакет — нужен сброс по таймауту и контроль размера буфера |
| Локальный файловый журнал на стороне бота | Автономный режим, нестабильный канал: запись идёт в файл рядом с процессом | Файлы растут, нужны ротация и очистка; разбор инцидента требует выгрузки и сопоставления с централизованным журналом |
| Гибридная схема | Продакшн, где важны и скорость реакции, и полнота истории: локальная фиксация плюс асинхронная отправка в централизованный журнал | Сложнее настройка: две точки хранения, правила слияния и дедупликации, контроль расхождений между ними |
Гибридный вариант мы обычно ставим по умолчанию: событие сначала попадает в локальный сегмент, а оттуда уходит пачками в централизованный журнал. Целостность записей держится на порядковом номере события и повторной отправке неподтверждённых пакетов, поэтому разрыв связи не превращается в дыру в истории. Переключение между режимами не требует переписывать бота — оно задаётся конфигурацией, и это позволяет поднять частоту записи без пересборки логики торговли.
Как мы строим журналирование сделок: этапы работ
Журнал сделок нужен не ради логов: по нему разбирают расхождения между намерением стратегии и фактическим исполнением, восстанавливают цепочку заявка → исполнение → отмена и подтверждают операции перед проверяющими. Поэтому формат записи утверждается до подключения источника — иначе историю придётся переписывать задним числом.
- Аудит источников событий. Инвентаризируем, откуда приходят данные: торговый модуль, шлюз брокера, поток котировок, риск-контроль. Для каждого источника фиксируем момент события, точность метки времени и признак, по которому запись опознаётся повторно.
- Проект формата записи. Согласуем схему: идентификатор сделки, время, инструмент, сторона, объём, цена, статус, причина отклонения, ссылка на родительское событие. Отдельно — правила версионирования, чтобы записи прошлых периодов читались новым кодом.
- Подключение источников событий. Ставим адаптеры, пишем журнал в режиме только на добавление и буферизуем на диск: обрыв канала не должен «съедать» события, а повторная доставка — создавать дубли.
- Проверка полноты журнала. Сверяем число записей с независимыми счётчиками — подтверждениями брокера, движением баланса, числом отправленных заявок. Прогоняем журнал на исторических данных и воспроизводим торговый день.
- Проверка поведения при сбоях. Смотрим рестарт, обрыв связи и переполнение диска: что теряется, что восстанавливается, сколько занимает сверка после перезапуска.
- Передача системы заказчику. Отдаём схему хранения, доступы, регламент ротации и архивации, порядок разбора инцидента — и обучаем команду читать журнал без нас.
Артефакты журнала: схемы, правила, регламенты
Журнал без описания превращается в набор таблиц, к которым через полгода никто не решается притронуться. Поэтому вместе с системой передаём комплект артефактов, по которым записи можно читать, дополнять и разбирать без участия автора.
- Формат записи событий. Поля, типы, обязательность, правило пустых значений и версионирование схемы: новые поля добавляются, не ломая чтение старых записей.
- Схема потоков событий. От постановки заявки до исполнения: точки записи, поведение при обрыве канала, правило «событие записано целиком или не записано вовсе».
- Политики доступа к журналу. Роли и права, разделение чтения и выгрузки, запрет правки записей, отдельный контур для чувствительных полей.
- Сроки хранения записей. Горячее окно для ежедневного разбора, архив для сверок, автоматическая очистка по истечении срока с журналом самих удалений.
- Регламент разбора расхождений. Как фиксируется расхождение между намерением и исполнением, кто эскалирует, в какой срок и что попадает в список исправлений.
- Правила сверки с отчётами площадок. Ключи сопоставления, приоритет источника при конфликте, порядок разбора несовпадений.
- Инструкция по восстановлению. По каким записям и в каком порядке восстанавливается состояние позиций после сбоя и как проверяется полнота.
Каждый артефакт версионируется и лежит рядом с кодом: изменение логики и правка описания — одна задача, а не два несвязанных дела. Именно это делает журнал пригодным для передачи другой команде и для проверки.
Кейс: как расхождение в сделках нашли по журналу
Расхождение заметили не сразу: бот показывал позицию закрытой, а у брокера она оставалась открытой. Журнал тогда фиксировал только факт отправки ордера — без идентификатора брокера, состояния до и после и версии правил риск-контроля, поэтому причину искали по косвенным признакам и в среднем тратили на это полтора суток. Доля необъяснённых расхождений держалась около 9% от дневного числа сделок, часть операций закрывали вручную.
Что изменили в схеме журнала
Каждой сделке присвоили сквозной идентификатор, одинаковый у бота и в ответах брокера. Журнал перестал перезаписываться: он только дополняется переходами состояний (создан, отправлен, подтверждён, исполнен частично, закрыт), а рядом ложатся сырые ответы брокера, версия риск-правил на момент сделки и метка времени. Хранилище — PostgreSQL с неизменяемыми записями и контрольной суммой цепочки: подмена или потерянный фрагмент видны сразу, а не на месячной сверке.
Появилось воспроизведение по журналу: состояние бота восстанавливается на любую метку времени, поэтому сбой локализуется сравнением двух прогонов. Первый же разбор занял 20 минут вместо полутора суток — причиной оказался конфликт между отменой и частичным исполнением.
Результат в метриках
Доля необъяснённых расхождений упала с 9% до 0,4%, время локализации причины — с 36 часов до 20 минут. Восстановили 340 операций за квартал, ни одну не пришлось сверять вручную.
Что делать, если бот уже пишет события в файлы?
Файловый журнал на торговом сервере держится до первого сбоя ротации. Бот пишет в активный файл, старый удаляется по расписанию — и вместе с ним уходят записи о сделках за прошлый период, которые уже никто не восстановит. Второй источник потерь — буферизация: при аварийном завершении процесса последние строки не доходят до диска, и в файле остаётся разрыв ровно там, где нужно разбирать поведение стратегии.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Оставить файловый журнал | Один процесс, разбор вручную, короткий срок хранения | Потери при ротации и падении, нет единого поиска по нескольким ботам |
| Добавить выгрузку записей | Нужен архив без перестройки бота, файл остаётся основным | Задержка между событием и архивом, при обрыве выгрузки разрыв сохраняется |
| Централизованное хранение | Несколько ботов, разбор спорных сделок, длительное хранение | Требует доработки отправки событий и контроля доставки |
Миграция журнала событий начинается с переноса уже накопленных файлов: записи разбираются построчно, приводятся к единой схеме и загружаются пакетами, чтобы не смешивать форматы старых и новых событий. Дальше бот переводится на отправку событий сразу при записи, а файл остаётся страховочной копией на время перехода.
Ключевой момент — контроль доставки: если событие не подтверждено хранилищем, оно остаётся в локальной очереди и уходит повторно после восстановления связи. Так ротация файлов перестаёт быть точкой потери, а выгрузка записей работает как догоняющий канал, а не как единственный.
Технические сомнения перед стартом внедрения
Перед стартом поднимают одни и те же вопросы: не просядет ли торговый контур, что будет с потерянными событиями, кто получит доступ к журналу и как убедиться, что записи не задвоятся. Ниже — как каждый из них закрывается.
Не создаст ли журналирование нагрузку на торговый контур?
Запись выносим за критический путь: бот отдаёт событие в асинхронную очередь, сохранение идёт отдельным контуром. Время принятия торговых решений не меняется, а глубину очереди и долю отказов видно на графиках Prometheus.
Что делать с пропущенными событиями после сбоя?
Каждое событие получает монотонный идентификатор, бот хранит последний подтверждённый. После перезапуска он досылает пропущенный диапазон и сверяет его с историей исполнения — журнал сходится без ручной склейки.
Как разграничивается доступ к журналу сделок?
Роли разделены: стратег видит свои сигналы, риск-контроль — агрегаты, аудит — полный лог. Доступ выдаётся сервисными учётными записями с ограниченным сроком, экспорт фиксируется отдельным следом.
Как убедиться, что записи не дублируются?
Идемпотентный ключ из идентификатора сделки и события отсекает повтор на уровне уникального индекса хранилища. Дополнительно идёт сверка: число записей за период против числа исполненных заявок.
Можно ли внедрить без остановки торговли?
Да. Сначала журнал включается в теневом режиме: пишется, но не влияет на решения бота, затем переключается чтение отчётов. Откат — снятие флага, логика торговли не затрагивается.
Обсудим задачу и вернёмся с оценкой и планом
Чтобы оценка и план работ опирались на факты, а не на предположения, на первом разговоре достаточно показать, как сделки фиксируются сейчас. Мы смотрим на источники событий, формат хранения и на то, какие вопросы журнал должен закрывать: разбор ошибочных входов, восстановление истории за произвольный период, сверку с выписками брокера.
Для предметной оценки нужны исходные данные:
- Схема торгового контура: где принимается решение, где выставляется заявка, какая система подтверждает исполнение.
- Примеры текущих записей — сырые выгрузки из таблиц или журналов, даже неполные и разнородные.
- Перечень событий, обязательных к фиксации: заявка, исполнение, частичное закрытие, отмена, отказ брокера.
- Требования к хранению и доступу: срок жизни записей, кто их читает, нужна ли защита от правки задним числом.
- Ограничения окружения: как разворачивается бот, какие внешние системы недоступны в тестовом контуре.
- Критерии приёмки: полнота записей, скорость восстановления истории, поведение при обрыве связи.
После разговора возвращаемся с оценкой и планом работ: этапы, состав журнала, точки промежуточной проверки и порядок старта. Обычно он такой — согласуем схему записи событий, собираем журнал на исторических данных, проверяем на сбоях и передаём с описанием формата. Опишите задачу — вернёмся с планом.







