Розробка системи зберігання order book snapshots

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи зберігання order book snapshots
Складний
~5 днів
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1288
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    860

Розробка системи зберігання order book snapshots

Дані order book — найбільш інформативні та найскладніші для зберігання біржові дані. Повний стакан BTC/USDT на Binance містить 5000 рівнів з обох сторін, оновлюється кілька разів в секунду й генерує сотні мегабайтів даних на годину. Наївний підхід до зберігання приводить до катастрофічного зростання обсягів; правильна система балансує повноту даних з практичними обмеженнями.

Типи даних order book

Перед проектуванням сховища важливо розуміти, які дані насправді потрібні:

Full snapshots — повний зріз стакана в момент часу. Великі (кілька КБ на снапшот), але дозволяють точно відновити стан ринку. При 1 снапшот/сек на 100 символах — ~100 GB на день.

Depth snapshots — перші N рівнів (зазвичай 5, 10, 20). Достатньо для більшості стратегій, вимагають 50–250 разів менше місця.

Order book diffs — лише зміни (додання/видалення/оновлення рівнів). Мінімальний обсяг, але вимагають повного снапшоту для відновлення стану.

Mid-price та spread — агреговані похідні. Мікроскопічний обсяг, підходить для довгострокового аналізу.

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

Формат зберігання: Delta Encoding

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

Snapshot @ T=0:
  bids: [(43250.0, 1.5), (43249.5, 2.0), (43249.0, 0.8)]
  asks: [(43251.0, 1.2), (43251.5, 3.0), (43252.0, 0.5)]

Diff @ T=1 (тільки зміни):
  bids_updated: [(43250.0, 2.1)]    # обсяг змінився
  bids_removed: [(43249.5, 0)]      # рівень зник
  bids_added:   [(43248.5, 1.0)]    # новий рівень
  asks_updated: []
  asks_removed: []
  asks_added:   [(43251.75, 0.3)]

Повний снапшот: ~10 KB. Diff: ~200 байт. При 5 оновленнях/сек та снапшоті раз в хвилину — 300 diffs + 1 snapshot = ~60 KB/хв замість 3 MB/хв.

Схема бази даних

Використовуємо ClickHouse з власною сериалізацією:

-- Повні снапшоти (раз в хвилину)
CREATE TABLE orderbook_snapshots (
    exchange     LowCardinality(String),
    symbol       LowCardinality(String),
    snapshot_time DateTime64(3, 'UTC'),
    depth        UInt16,
    bids         Array(Tuple(Decimal(24,8), Decimal(24,8))),  -- [(price, qty)]
    asks         Array(Tuple(Decimal(24,8), Decimal(24,8)))
)
ENGINE = MergeTree()
PARTITION BY (exchange, toYYYYMM(snapshot_time))
ORDER BY (exchange, symbol, snapshot_time);

-- Дельти між снапшотами
CREATE TABLE orderbook_diffs (
    exchange      LowCardinality(String),
    symbol        LowCardinality(String),
    diff_time     DateTime64(3, 'UTC'),
    first_update_id UInt64,
    last_update_id  UInt64,
    bids_changes  Array(Tuple(Decimal(24,8), Decimal(24,8))),  -- qty=0 означає видалення
    asks_changes  Array(Tuple(Decimal(24,8), Decimal(24,8)))
)
ENGINE = MergeTree()
PARTITION BY (exchange, toYYYYMM(diff_time))
ORDER BY (exchange, symbol, diff_time);

Відновлення стану стакана

Ключова операція — відновлення стакана на довільний момент часу:

class OrderBookReplay:
    async def reconstruct_at(self, exchange: str, symbol: str, target_ts: int) -> OrderBook:
        # 1. Знайти останній снапшот до target_ts
        snapshot = await self.storage.get_last_snapshot_before(
            exchange, symbol, target_ts
        )
        
        # 2. Завантажити всі дельти від снапшоту до target_ts
        diffs = await self.storage.get_diffs(
            exchange, symbol,
            from_ts=snapshot.timestamp,
            to_ts=target_ts
        )
        
        # 3. Застосувати дельти послідовно
        book = OrderBook.from_snapshot(snapshot)
        for diff in diffs:
            book.apply_diff(diff)
        
        return book

Критично: підтримувати порядок послідовності diffs та валідувати через update_id — кожен diff має lastUpdateId, наступний diff повинен мати firstUpdateId = lastUpdateId + 1. Пропуски в послідовності вказують на відсутність даних.

Компресія та оптимізація

Order book містить багато подібних чисел (ціни групуються разом). Перед записом у ClickHouse застосовуйте:

Delta encoding для цін — зберігайте різниці цін від найкращого bid/ask в basis points замість абсолютних цін.

Binary serialization — використовуйте Protocol Buffers або MessagePack замість JSON для скорочення розміру в 3–5 разів.

ClickHouse compression — кодек ZSTD забезпечує краще стискання для типів Float/Decimal ніж стандартний LZ4.

Streaming ingestion

Pipeline ingestion для снапшотів та diffs працює паралельно, буферизуючи diffs та періодично зберігаючи снапшоти та партії змін.

Моніторинг

Критично: відслідкувати пропуски в послідовностях diffs. Валідація системи порівнює lastUpdateId кожного diff з firstUpdateId наступного та алертує про пропуски. Пропуски в даних роблять відновлення order book між снапшотами неможливим.