Торговий робот своїми руками: більше деталей, більше нюансів

Торговий робот своїми руками: більше деталей, більше нюансів

У першій частині ми розібрали базову ідею: торговий радник — це не «робот, який торгує замість вас», а система, яка отримує ринкові дані, формує сигнал (і пояснення) і показує результат людині.

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

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

Які дані брати на старті

На старті у вас є три основні класи ринкових даних, які можна отримувати з біржі через API. Найзрозуміліший і найпоширеніший - OHLCV-свічки: Open/High/Low/Close плюс обсяг за інтервал. Це “стисле” уявлення ціни, яке зручно зберігати, кешувати та передавати у модель як стабільний шматок контексту (наприклад, останні N свічок обраного таймфрейму1).

Якщо потрібно бачити, що відбувається всередині свічки, підключають стрічку угод (trades / tape) — потік фактичних виконань: ціна, кількість, час (іноді сторона). Такий шар додає деталізацію за активністю та імпульсами і зазвичай використовується як більш “зернисте” джерело, ніж свічки.

Третій варіант - склянка заявок (order book): рівні bid/ask та обсяги на кожному рівні. Це вже дані про мікроструктуру та ліквідність: не “що сталося”, а “що зараз стоїть у черзі”. Зазвичай його беруть як окремий шар контексту, коли потрібно враховувати глибину ринку, дисбаланс попит/пропозицію та реакцію на рівні.

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

Єдиний формат даних та історія

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

Якщо не ввести єдиний формат на своїй стороні, система швидко перетворюється на набір милиць: кожне нове джерело тягне за собою окремі парсери, винятки та “особливі випадки”. Тому в модулі отримання котирувань майже завжди потрібний загальний внутрішній формат — мінімально достатній для подальшого аналізу. Для нашого поточного рівня це зазвичай: інструмент, таймфрейм1, timestamp

Рейт-ліміти: чому це важливо

Майже кожна біржа має обмеження на частоту запитів — рейт-ліміти. Причина проста: якщо дати всім клієнтам нескінченно "смикати" котирування, API ляже навіть без DDoS.

У контексті радника це важливо подвійно. Запити виконуються не безперервно, а за таймером - і якщо ви неправильно вибрали періодичність або помножили її на кількість інструментів, ви дуже швидко упретеся в ліміт. Результат зазвичай однаковий: помилки 429/ban на ключ, дірки в даних та «мовчання» системи саме тоді, коли ринок рухається найсильніше.

Робочий підхід тут прагматичний: заздалегідь вважати бюджет запитів (періодичність × інструменти × типи даних), кешувати результати, не вимагати те саме двічі, і коректно обробляти ліміти (backoff/повтор через паузу замість спаму запитами).

Промпт та нейромережі: той самий API, інші правила

З отриманням даних розібралися — тепер можна переходити до наступного рівня: промпт та робота з нейромережами.

На перший погляд це дуже схоже на запит котирувань: знову API, знову обмеження по частоті, знову потрібно думати про ретраї, таймаути та кеші. Тільки замість “дай свічки” у вас запит виду “ось дані, ось контекст, поверни сигнал та пояснення”.

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

Момент, який дуже спрощує життя на інтеграції: у промпті можна заздалегідь зафіксувати структуру відповіді. Наприклад, попросити модель повернути результат суворо в JSON — з полями на кшталт signal, confidence та reasons. Це не “магія”, але як мінімум дисциплінує формат, а на стороні програми дає можливість автоматично парсувати та валідувати відповіді замість того, щоб розбирати вільний текст.

Особливо варто згадати MCP (Model Context Protocol): це підхід/протокол, який допомагає стандартизувати, як моделі отримують контекст і як підключаються зовнішні джерела даних та інструменти. Навіть якщо зараз ви використовуєте прямі виклики конкретного API, пам'ятати MCP-подібний шар корисно — він дисциплінує архітектуру і спрощує розширення.

І так, у перспективі “аналітик” не може бути зовнішнім сервісом. Якщо з'являться ресурси та мотивація, цей модуль можна замінити власною моделлю (або донавченою опенсорсною), не переписуючи решту системи — за умови, що інтерфейси спочатку розділені правильно.

Сховище: простіше, ніж здається

На цьому етапі ми вже маємо мінімум два “масиви” даних: ринкові свічки та результати аналізу. А якщо дивитися трохи вперед — додадуться історія новин (і їх саммарі) плюс збереження промптів/відповідей моделі для дебага і повторного аналізу. Іншими словами, даних стає досить багато, і вони з'являються у різних місцях конвеєра.

Тому має сенс заздалегідь подумати про уніфіковане зберігання. Специфіка таких даних часто дозволяє не тягнути повноцінну SQL-схему: у багатьох випадках вистачає бази, що вбудовується, і простого ключ-значення підходу.

KV-сховище (key-value) — це модель, де дані зберігаються як пари “ключ → значення”: по ключу ви швидко дістаєте потрібний об'єкт (наприклад, свічки для інструменту та таймфрейму1..

Інтерфейс: мінімум, без якого радник не є корисним

І нарешті - інтерфейс. У UI/UX зараз не заглиблюємося, але нагадаю базову думку з першої частини: як мінімум потрібен веб-інтерфейс, а як приємне доповнення — повідомлення Telegram.

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

Ядро: “кільце”, яке все пов'язує

У Толкіна було “одне кільце, щоб правити всіма... і в темряві їх зв'язати”. В архітектурі радника це кільце теж є лише без пафосу: ядро системи, яке тримає всі модулі разом.

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

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

На цьому місці можна зупинитися і чесно сказати: “скелет” радника вже видно. Дані надходять, промпт збирається, модель відповідає, результат зберігається і показується користувачеві - і все це тримається на ядрі, яке керує життєвим циклом. У наступній статті я спущусь на рівень нижче: розберемо технічні деталі реалізації - як виглядає єдиний інтерфейс джерел котирувань, як влаштувати кеш і ретраї, де зберігати історію, як валідувати відповіді моделі (включаючи JSON-формат), і що робити, щоб система не ламалася від лімітів, часу..

Виноски

  1. Таймфрейм – це тривалість однієї свічки (інтервал агрегації даних): наприклад 1m, 5m, 1h, 1d. Він визначає, як саме "стискається" потік цін у OHLCV.
  2. Timestamp – це мітка часу (зазвичай у UTC), до якої прив'язана свічка/угода. Класична помилка: переплутати секунди та мілісекунди (або локальний час та UTC), після чого дані «з'їжджають» та аналіз ламається.

За матеріалами itprolab.dev

Читайте також

82018-06-29

Приймаємо оплату в bitcoin: Частина шоста. Нюанси, знову нюанси

У минулій частині ми зупинилися на тому, що не погано було б дізнатися про факт платежу від bitcoind замість того, щоб перебирати всі видані адреси.

Розробнику
42026-02-11

Golang у крипті: коли швидкість і передбачуваність важливіша за моду

Якщо ви коли-небудь запускали кампанію, яка “все зробила правильно”, а сайт у самий пік раптово почав гальмувати – ви вже розумієте, звідки взагалі береться розмова про мову розробки. У такі моменти з'ясовується неприємна річ...

Розробнику, Технології, Ethereum, Навчання

Останні статті з розділу Розробнику

Нове відео на каналі