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







