REST API розробка історичних даних: де губиться історія
Сервіс історичних ринкових даних ламається не тоді, коли падає, а коли відповідає кодом успіху з неповним тілом. Клієнт запитує діапазон свічок, отримує коректну відповідь і будує на ній розрахунок — а всередині діапазону бракує кількох інтервалів.
Такі прогалини в котируваннях не видно в логах: ні збою, ні затримки, просто історія коротша за заявлену.
Причина зазвичай у способі збирання відповіді. Дані витягуються посторінково, упираються в ліміти запитів у джерела, частина сторінок надходить після повторів, частина — з іншою розміткою мітки часу.
Якщо сторінки склеюються без перевірки безперервності, на стиках з’являються діри, а часовий пояс і округлення мітки добивають картину: свічка формально є, але стоїть не на своєму місці.
Ціна для продукту — не косметика. Бектест на рваній історії показує дохідність, якої не було, індикатори рахуються за неповним вікном, звіт розходиться з графіком. Далі руйнується довіра до аналітики, і відновити його дорожче, ніж полагодити віддачу.
Розбираємо, як це закривається в REST API розробці під історичні ринкові дані: контракт на діапазон і точність мітки, перевірка цілісності історії на вході, ідемпотентне довантаження пропущеного, кеш під повторні запити й режим віддачі, який не залежить від лімітів запитів зовнішнього джерела.
Що дає вивантаження історії: повнота, швидкість, стійкість
Повнота історії — перше, що перевіряють на прийманні. Раніше котирування бралися з одного джерела, і на тонких інструментах в архіві залишалися прогалини.
Тепер вивантаження котирувань іде з кількох джерел, ряди приводяться до єдиної схеми, а пробіли закриваються повторним запитом — звіт про якість показує, які ділянки підтверджені незалежно, а які відновлені.
Швидкість відклику тримається на попередньо порахованих вітринах: типовий запит діапазону віддається за частки секунди, важкі вибірки йдуть в асинхронну підготовку файлу з повідомленням про готовність. 95-й перцентиль часу відповіді більше не залежить від того, за скільки років запрошено історію.
Стійкість сервісу дають кеш, обмеження частоти звернень до джерел і повторні спроби з наростаючою паузою. Якщо зовнішнє джерело недоступне, віддається остання узгоджена версія ряду, а не помилка — термінал і аналітика продовжують працювати. Незалежною перевіркою слугує звірка з публічними агрегаторами, наприклад CoinMarketCap.
Ручні правки прогалин пішли з процесу аналітиків: розходження між джерелами потрапляють у звіт, а не в графік. Замовники у відгуках відзначають саме це — «архів перестав бути окремою задачею: дані за потрібний період готові до початку торгового дня, а не до його середини».
REST API розробка: варіанти віддачі історії та обмеження
Одну й ту саму історію котирувань можна віддавати чотирма способами, і вибір диктує профіль споживача: сервісу перевірки стратегій потрібен весь масив одразу, торговому роботу — безперервний потік, аналітику — довільний зріз за інструментами й періодами.
Ми розводимо ці сценарії за окремими контурами: потокова підписка не конкурує за з’єднання з пакетним вивантаженням, а важкі зрізи йдуть на репліку сховища й не гальмують запис нових даних. Різниця між режимами не в зручності, а в тому, які обмеження ви приймаєте разом із ними.
Чотири режими та їхні межі
| Режим | Коли підходить | Обмеження |
|---|---|---|
| Посторінкова вибірка | Інтеграції, звіти, добір пропущених інтервалів | Кількість запитів зростає з глибиною історії; під час рестарту потрібен курсор, інакше отримаєте повтори або діри |
| Пакетне вивантаження | Первинне завантаження, перевірка стратегій, відновлення архіву | Дані відстають від поточного моменту; розкласти файл за своєю схемою доведеться на боці споживача |
| Потокова підписка | Онлайн-котирування, ризик-моніторинг, розрахунок індикаторів на льоту | Віддає лише «з цього моменту»; після розриву з’єднання потрібен догін через історичний запит |
| Пряма вибірка зі сховища | Аналітика, довільні зрізи, звірка даних | Лише читання й окрема копія; схема фіксована, будь-які зміни — через погодження |
Обмеження — властивість режиму, а не дефект: потік не віддає глибину, файл не наздоганяє реальний час, репліка не пише. Тому в описі API ми фіксуємо межі кожного режиму й правила переходу між ними — догін після розриву, курсор під час рестарту, звірку даних між копіями.
Від аудиту джерел до передачі: етапи інженерної роботи
Проєкт починається не з коду, а з розбору того, звідки надходять дані. Біржові вивантаження, агрегатори й внутрішні архіви відрізняються гранулярністю, таймзоною та правилами ревізії — уже тут видно, які ряди доведеться зрощувати, а які можна віддавати як є.
- Аудит джерел даних. Дивимося формати вивантажень, глибину історії, обмеження на частоту запитів і поведінку майданчика під час збоїв. На виході — перелік джерел із позначками, де очікуються розриви й дублікати.
- Проєктування схеми даних. Фіксуємо сутності (угоди, свічки, стакан), таймзони, точність округлення та правила зрощування рядів. Схема стає контрактом: за нею будується і сховище, і структура відповідей API.
- Реалізація. Збираємо шар завантаження з повторними спробами та ідемпотентним записом, щоб повторний імпорт не плодив дублікати, і шар віддачі — з курсорною пагінацією та передбачуваними кодами помилок.
- Перевірка на еталонному наборі. Невеликий відрізок історії розмічаємо вручну й звіряємо з джерелом до останнього запису. Розходження ловимо до того, як вони поїдуть в аналітику.
- Приймальна перевірка. Проганяємо сценарії замовника: рідкісні інструменти, межі періодів, пікові навантаження. Фіксуємо, що віддача вкладається в обумовлені строки й не втрачає записи.
- Передача й спостереження. Віддаємо опис схеми, конфігурацію та порядок розгортання, а після запуску стежимо за свіжістю даних і затримкою відповіді.
Порядок не випадковий: кожен крок спирається на артефакт попереднього — перелік джерел, схему, еталон. Тому приймання закривається до передачі, а не після.
Що залишається у вас: специфікація, схеми, регламенти
Сервіс історичних ринкових даних живе довше, ніж команда, яка його запустила: за півроку його розвиватиме інша зміна інженерів — або ваша, без авторів під рукою.
Тому працюючий інтерфейс ми передаємо разом із пакетом артефактів, за якими сервіс можна підтримувати, не відновлюючи логіку за кодом.
- Специфікація інтерфейсу — методи, параметри, формати відповідей, коди помилок і ліміти звернень у машинозчитуваному вигляді: з неї генеруються клієнти й тести.
- Схема даних — таблиці, ключі, типи, індекси та правила зберігання історії; за нею видно, чому вибірка за діапазоном дат залишається швидкою на зростаючому обсязі.
- Регламент оновлення — розклад завантаження, вікно довантаження пропущених періодів, перевірка цілісності та звірка з джерелом.
- Політика доступу — ролі та області видимості, строки життя ключів, квоти на споживача, журнал звернень і порядок відкликання доступу.
- Документація для команди — локальний запуск, змінні середовища, міграції, метрики, чек-лист релізу й порядок відкату.
- Автотести та еталонні приклади запитів — фіксують очікувану поведінку, щоб правки в розборі історичних даних ловилися до викату.
- Інструкція на випадок збою — що робити при пропусках або повільних відповідях джерела, як переключитися на резервний потік і чим підтвердити відновлення.
Практика: як закрили діри в історії та прискорили віддачу
Проєкт — агрегатор біржових даних: сервіс REST API віддавав історичні свічки й угоди для аналітики та торгових роботів.
На вході за окремі періоди бракувало до 3% хвилинних свічок, а збирання річного набору за інструментом займало близько сорока хвилин — клієнти витягували історію посторінково й часто обривалися на часі очікування.
Відновлення історії
Репліки знімалися з кількох джерел, кожна угода зводилася до первинного ключа «інструмент + мітка часу», розходження розбиралися правилом пріоритету джерела. Пропущені інтервали добиралися з архівних дампів і логів, агрегати за спірні вікна перераховувалися заново.
Після прогону повнота хвилинного ряду вийшла на 100%, а розходження між джерелами впали до часток відсотка.
Пікове навантаження та віддача
Під піком сервіс раніше ріс у затримці: черга запитів накопичувалася, частина клієнтів не дочікувалася відповіді на довгих діапазонах. Гарячі діапазони ми винесли в колонкове сховище ClickHouse, попередньо розрахували тижневі й місячні агрегати та обмежили розмір сторінки.
На піку в двадцять разів вищому за звичайний віддача тримається рівно, збирання річного набору скоротилося до хвилини.
Результат проєкту: історія без пропусків, віддача під піком без обривів, документовані правила зведення джерел і повторюваний прогін відновлення за розкладом.
Чим вивантаження історії відрізняється від потоку реального часу
Архівне вивантаження в REST-інтерфейсі відповідає на запит за діапазоном: клієнт передає інтервал часу та ідентифікатор інструмента, сервіс віддає сторінку записів із курсором на наступну.
Результат детермінований — повтор із тим самим курсором повертає ті самі рядки, тому обрив зв’язку лікується повторним запитом, а не відновленням підписки.
Потік реального часу влаштований інакше: підписка живе в постійному з’єднанні, і кожен запис доставляється один раз. Збій тут сам не повторюється — втрачене повідомлення довантажують із журналу за міткою останнього прийнятого запису, інакше в історії залишається діра.
Затримка віддачі в потоці вимірюється хвостом розподілу, а не середнім: рідкісні сплески на піку обсягу споживач помічає одразу.
Звідси різні вимоги до зберігання. Архіву потрібен повний масив, розбитий за датами, з індексом за ключем і часом — він зростає монотонно, тому його виводять у холодний контур, де читання дорожче за запис.
Потоку достатньо короткого гарячого буфера, але буфер зобов’язаний переживати перезапуск вузла: без журналу на диску підписка після падіння втрачає вікно, яке з джерела вже не підняти.
Довантаження змін — окремий контур. В архівному вивантаженні вікно навмисно перекривають на кілька інтервалів і звіряють краї діапазону, щоб не втратити записи на межі. У потоці довантаження йде за курсором з ідемпотентним ключем запису: дублі відсікаються, пропуски закриваються.
І там, і там контрольні суми — кількість записів і крайні мітки часу — звіряють регулярно, інакше розходження спливе вже у звітності замовника.
Практичний висновок: один інтерфейс може віддавати обидва режими, але за ним працюють два різні сервіси зі своїми схемами зберігання, лімітами на глибину запиту та бюджетом затримки.
Проєктування починається з питання, що робити під час розриву — перегравати діапазон чи довантажувати за курсором. Від відповіді залежить і схема зберігання, і те, як виглядатиме звірка даних.
Технічні сумніви перед стартом робіт
Збої та краї даних інженерні команди перевіряють до старту — і питання у всіх приблизно одні: пропуски, поведінка при відмові джерела, межі історії, звірка та вивантаження. Нижче прямі відповіді, без обіцянок «усе буде стабільно».
Що робити з пропусками в історичних даних джерела?
Пропуск не заповнюємо мовчки: він позначається явною ознакою повноти у відповіді, а не зникає між сусідніми точками. Діапазон із пробілом можна перезапросити — повторне довантаження ідемпотентне, дублі не з’являються.
Окремий ендпоінт віддає карту покриття за інструментами й періодами, щоб споживач бачив межі достовірності заздалегідь.
Що станеться, якщо джерело перестане відповідати?
Віддаємо останній підтверджений зріз разом із часом його актуальності та ознакою застарівання. Споживач сам вирішує, працювати з цими даними чи чекати — замість порожньої відповіді й розбору помилки на своєму боці.
Чи можна розширити глибину історії пізніше?
Так, бекфіл іде діапазонами й порціями, контракт відповіді не змінюється — розширення не ламає вже написаних клієнтів. Нові та раніше завантажені ділянки звіряються на стиках, щоб на межі не з’явилося подвійних або роз’їханих значень.
Як перевіряється, що дані збігаються з джерелом?
Звірка йде за контрольними сумами зрізів за вибіркові періоди; розходження потрапляють у звіт, а не розчиняються в логах. Це дає відтворюваність: те саме вікно, запитане повторно, повертає той самий результат.
Ми можемо забрати своє вивантаження?
Вивантаження віддається потоком у файл за тією ж схемою полів, що й у REST-відповідях, — дані переносні й не замкнені всередині сервісу. Формат обираємо під ваш конвеєр обробки.
Далі — обговорення: опишіть задачу, повернемося з оцінкою та планом.Обговоримо задачу та повернемося з оцінкою й планом
Перша розмова потрібна не для знайомства, а щоб зафіксувати межі робіт: без розуміння глибини історії, набору інструментів і допустимої затримки актуалізації оцінка перетворюється на вилку, а схема даних — на переробку в середині проєкту. Тому уточнюємо те, від чого напряму залежить обсяг робіт.
- Глибина історії та джерела: біржові стрічки, вендорські архіви, власні вивантаження — за який період потрібні дані й де можливі пропуски.
- Склад даних: тики чи агреговані свічки, інтервали, перелік інструментів і майданчиків, чи потрібне коригування котирувань на спліти й дивіденди.
- Вимоги до якості: повнота рядів, точність до знака, єдина часова зона, правила розв’язання розходжень між джерелами та відсів дублів.
- Формат відповіді та навігація за обсягом: JSON, CSV або Parquet; посторінкова видача, курсори, фільтри за діапазоном дат і інструментом.
- Навантаження та доступи: скільки одночасних запитів витримує споживач, чи потрібні ключі, розподіл прав і обмеження частоти звернень.
- Актуалізація та стійкість: як часто довантажуємо нові дані, за який час закриваємо пропуск в архіві, що бачить клієнт при відмові джерела.
- Приймання: на яких наборах звіряємо видачу з еталоном і яку частку запитів вважаємо такою, що вклалася в цільовий час відповіді.
Опишіть задачу у вільній формі та позначте, що з переліченого вже вирішено, — повернемося з оцінкою робіт, схемою даних і планом впровадження за етапами.







