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

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

Чому журналювання угод бота ламається на піках

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

Щойно буфер переповнюється або процес перезапускається за станом здоров’я, частина записів губиться безповоротно — причому мовчки, без помилки в самому журналі.

Друга поломка — розрив серії записів. Кожна подія повинна мати наскрізний номер і посилання на попередню, інакше журнал перестає бути відтворюваним ланцюжком.

Якщо нумерація наскрізна лише в межах процесу, а не в межах торгової сесії, після рестарту номери починаються заново й накладуться на старі — під час розбору неможливо відрізнити два різні ордери з однаковим ідентифікатором.

Третє й найдорожче — розбіжність сигналу та виконання. Сигнал фіксується на боці стратегії, виконання — на боці брокера, у кожного своє джерело часу.

Підсумок: у журналі видно умову входу, але не видно фактичної ціни та часу постановки, а стоп у звіті спрацював інакше, ніж у записі. Відновлення ходу угоди перетворюється на ворожіння, і на питання «чому позиція закрилася не там» відповісти нічим.

Нижче розбираємо, як це закривається.

Що дає наскрізний слід угоди в торговій системі

Наскрізний слід угоди — це запис на кожен стан заявки: сформований сигнал стратегії, момент відправлення, відповідь майданчика, факт і обсяг виконання, часткове закриття, скасування.

Події зшиваються єдиним ідентифікатором кореляції й потрапляють до журналу лише на дозапис — переписати історію заднім числом не можна, можна лише додати нову подію. Такий журнал стає джерелом істини і для розбору збоїв, і для звітності.

Час розбору інциденту визначається тим, скільки потрібно, щоб відповісти на питання «чому бот зробив саме так». За ідентифікатором піднімається весь ланцюжок від сигналу до виконання: видно, яке правило спрацювало, які дані були на вході, що відповів майданчик.

Розбір переходить із ручного звіряння логів в один запит — десятки хвилин замість половини робочого дня.

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

Розбіжність між позицією системи та даними майданчика перестає бути незрозумілою: частка угод із повністю відновлюваною історією наближається до межі, крім випадків, коли майданчик не повернув відповідь узагалі.

Контроль виконання заявок і перевірка торгової стратегії спираються на той самий запис.

Журнал проганяється заново на накопиченій історії: той самий набір правил і параметрів дає ту саму послідовність рішень, що й у реальній торгівлі, тому зміна логіки перевіряється за один прогін, а не за тижні спостереження.

Заодно зникає клас відмов, який раніше не закривався нічим — суперечка про те, чий збій призвів до розбіжності.

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

Режим запису журналу визначає, що станеться з даними під час обриву каналу, рестарту процесу або переповнення диска.

Синхронний і пакетний запис — це обмін між затримкою на угоду та ризиком втратити останні події, тому вибір режиму ми робимо не «на око», а від вимог до цілісності записів у конкретному контурі.

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

Гібридний варіант ми зазвичай ставимо за замовчуванням: подія спочатку потрапляє до локального сегмента, а звідти йде пачками до централізованого журналу.

Цілісність записів тримається на порядковому номері події та повторному відправленні непідтверджених пакетів, тому розрив зв’язку не перетворюється на дірку в історії.

Перемикання між режимами не вимагає переписувати бота — воно задається конфігурацією, і це дає змогу підняти частоту запису без перезбирання логіки торгівлі.

Як ми будуємо журналювання угод: етапи робіт

Журнал угод потрібен не заради логів: за ним розбирають розбіжності між наміром стратегії та фактичним виконанням, відновлюють ланцюжок заявка → виконання → скасування й підтверджують операції перед перевіряльниками.

Тому формат запису затверджується до підключення джерела — інакше історію доведеться переписувати заднім числом.

  1. Аудит джерел подій. Інвентаризуємо, звідки надходять дані: торговий модуль, шлюз брокера, потік котирувань, ризик-контроль. Для кожного джерела фіксуємо момент події, точність позначки часу та ознаку, за якою запис розпізнається повторно.
  2. Проєкт формату запису. Узгоджуємо схему: ідентифікатор угоди, час, інструмент, сторона, обсяг, ціна, статус, причина відхилення, посилання на батьківську подію. Окремо — правила версіонування, щоб записи минулих періодів читалися новим кодом.
  3. Підключення джерел подій. Ставимо адаптери, пишемо журнал у режимі лише на додавання й буферизуємо на диск: обрив каналу не повинен «з’їдати» події, а повторна доставка — створювати дублі.
  4. Перевірка повноти журналу. Звіряємо кількість записів із незалежними лічильниками — підтвердженнями брокера, рухом балансу, кількістю надісланих заявок. Проганяємо журнал на історичних даних і відтворюємо торговий день.
  5. Перевірка поведінки під час збоїв. Дивимося рестарт, обрив зв’язку та переповнення диска: що губиться, що відновлюється, скільки займає звіряння після перезапуску.
  6. Передача системи замовнику. Віддаємо схему зберігання, доступи, регламент ротації та архівації, порядок розбору інциденту — і навчаємо команду читати журнал без нас.

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

Журнал без опису перетворюється на набір таблиць, до яких за пів року ніхто не наважується торкнутися. Тому разом із системою передаємо комплект артефактів, за якими записи можна читати, доповнювати й розбирати без участі автора.

  • Формат запису подій. Поля, типи, обов’язковість, правило порожніх значень і версіонування схеми: нові поля додаються, не ламаючи читання старих записів.
  • Схема потоків подій. Від постановки заявки до виконання: точки запису, поведінка під час обриву каналу, правило «подія записана цілком або не записана взагалі».
  • Політики доступу до журналу. Ролі та права, розділення читання й вивантаження, заборона правки записів, окремий контур для чутливих полів.
  • Терміни зберігання записів. Гаряче вікно для щоденного розбору, архів для звірянь, автоматичне очищення після закінчення терміну з журналом самих видалень.
  • Регламент розбору розбіжностей. Як фіксується розбіжність між наміром і виконанням, хто ескалює, у який термін і що потрапляє до списку виправлень.
  • Правила звіряння зі звітами майданчиків. Ключі зіставлення, пріоритет джерела під час конфлікту, порядок розбору невідповідностей.
  • Інструкція з відновлення. За якими записами і в якому порядку відновлюється стан позицій після збою та як перевіряється повнота.

Кожен артефакт версіонується й лежить поруч із кодом: зміна логіки та правка опису — одне завдання, а не дві не пов’язані справи. Саме це робить журнал придатним для передачі іншій команді та для перевірки.

Кейс: як розбіжність в угодах знайшли за журналом

Розбіжність помітили не одразу: бот показував позицію закритою, а в брокера вона залишалася відкритою.

Журнал тоді фіксував лише факт відправлення ордера — без ідентифікатора брокера, стану до й після та версії правил ризик-контролю, тому причину шукали за непрямими ознаками й у середньому витрачали на це півтори доби.

Частка нез’ясованих розбіжностей трималася близько 9% від денної кількості угод, частину операцій закривали вручну.

Що змінили у схемі журналу

Кожній угоді присвоїли наскрізний ідентифікатор, однаковий у бота та у відповідях брокера.

Журнал перестав перезаписуватися: він лише доповнюється переходами станів (створено, надіслано, підтверджено, виконано частково, закрито), а поруч лягають сирі відповіді брокера, версія ризик-правил на момент угоди та позначка часу.

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

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

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

Частка нез’ясованих розбіжностей упала з 9% до 0,4%, час локалізації причини — з 36 годин до 20 хвилин. Відновили 340 операцій за квартал, жодну не довелося звіряти вручну.

Що робити, якщо бот уже пише події у файли?

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

Друге джерело втрат — буферизація: під час аварійного завершення процесу останні рядки не доходять до диска, і у файлі залишається розрив рівно там, де потрібно розбирати поведінку стратегії.

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

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

Далі бот переводиться на відправлення подій одразу під час запису, а файл залишається страхувальною копією на час переходу.

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

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

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

Чи не створить журналювання навантаження на торговий контур?

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

Що робити з пропущеними подіями після збою?

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

Як розмежовується доступ до журналу угод?

Ролі розділено: стратег бачить свої сигнали, ризик-контроль — агрегати, аудит — повний лог. Доступ видається сервісними обліковими записами з обмеженим терміном, експорт фіксується окремим слідом.

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

Ідемпотентний ключ з ідентифікатора угоди та події відсікає повтор на рівні унікального індексу сховища. Додатково йде звіряння: кількість записів за період проти кількості виконаних заявок.

Чи можна впровадити без зупинки торгівлі?

Так. Спочатку журнал вмикається в тіньовому режимі: пишеться, але не впливає на рішення бота, потім перемикається читання звітів. Відкат — зняття прапорця, логіка торгівлі не зачіпається.

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

Щоб оцінка та план робіт спиралися на факти, а не на припущення, на першій розмові достатньо показати, як угоди фіксуються зараз.

Ми дивимося на джерела подій, формат зберігання й на те, які питання журнал має закривати: розбір помилкових входів, відновлення історії за довільний період, звіряння з виписками брокера.

Для предметної оцінки потрібні вихідні дані:

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

Після розмови повертаємося з оцінкою та планом робіт: етапи, склад журналу, точки проміжної перевірки й порядок старту. Зазвичай він такий — узгоджуємо схему запису подій, збираємо журнал на історичних даних, перевіряємо на збоях і передаємо з описом формату. Опишіть задачу — повернемося з планом.

Напрямки AI-розробки

Останні роботи

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