Розробка стакану ордерів (order book) для криптобірж

Проблема: latency на matching 200 мс — це не нормально Коли клієнт каже «у нас latency на matching 200 мс» — ми одразу знаємо: проблема в архітектурі order book. У нашій практиці був випадок: біржа втрачала до 10% ордерів через блокування на рівні БД. Рішення — in-memory order book з мінімальною

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

Часті запитання

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

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

Проблема: latency на matching 200 мс — це не нормально

Коли клієнт каже «у нас latency на matching 200 мс» — ми одразу знаємо: проблема в архітектурі order book. У нашій практиці був випадок: біржа втрачала до 10% ордерів через блокування на рівні БД. Рішення — in-memory order book з мінімальною синхронізацією. Ми розробляємо високопродуктивні order book під ключ для crypto-бірж. Наш досвід — 15+ проєктів.

Order book — центральний компонент будь-якої біржі. Це впорядкований список заявок на купівлю та продаж активу. Продуктивність order book безпосередньо визначає можливості всієї торгової системи: від latency виконання до максимального throughput. Підтримуються лімітні та ринкові ордери. Середня економія на інфраструктурних витратах після впровадження in-memory рішення досягає 40% при навантаженні 50 000+ ордерів на секунду.

Як влаштований in-memory order book?

Order book живе в оперативній пам'яті. Доступ до диску при кожному matching — неприйнятний за latency. Класична структура: два відсортованих контейнери — bid side (купівлі, за спаданням ціни) та ask side (продажі, за зростанням ціни). На кожному ціновому рівні — черга ордерів (FIFO для price-time priority).

type Order struct { ID string UserID int64 Side Side // Buy | Sell Type OrderType // Limit | Market Price decimal.Decimal Quantity decimal.Decimal FilledQty decimal.Decimal CreatedAt int64 // nanosecond timestamp } type PriceLevel struct { Price decimal.Decimal Orders []*Order // FIFO queue } type OrderBook struct { Bids *redblacktree.Tree // Price -> *PriceLevel, descending Asks *redblacktree.Tree // Price -> *PriceLevel, ascending Orders map[string]*Order // OrderID -> Order (для швидкого скасування) mu sync.RWMutex } 

Вибір tree-структури:

  • Red-black tree: O(log n) для всіх операцій. Бібліотека emirpasic/gods — хороша Go реалізація.
  • Skip list: конкурентний, але складніший. Lock-free skip list дає кращу масштабованість для багатоядерних систем.
  • Sorted slice + binary search: O(n) insert, O(log n) search. Працює для невеликих books (< 1000 рівнів).

Чому скасування ордера має бути O(1)?

Якщо скасування ордера вимагає пошуку по всіх рівнях, latency зростає лінійно. Ми використовуємо hashmap Orders map[string]*Order — пряма індексація за ID. Це критично для high-frequency trading, де кожна мікросекунда на рахунку.

Matching алгоритм: FIFO vs Pro-Rata

Price-Time Priority (FIFO) — стандарт для більшості бірж. FIFO до 2 разів швидше Pro-Rata при високому навантаженні, але Pro-Rata стимулює великі заявки. Ми обираємо алгоритм під ваші задачі.

func (ob *OrderBook) matchOrder(taker *Order) []Trade { var trades []Trade counterSide := ob.getCounterBook(taker.Side) for taker.RemainingQty().IsPositive() { bestLevel := ob.getBestLevel(counterSide) if bestLevel == nil { break } if !ob.priceCrosses(taker, bestLevel.Price) { break } for len(bestLevel.Orders) > 0 && taker.RemainingQty().IsPositive() { maker := bestLevel.Orders[0] fillQty := decimal.Min(taker.RemainingQty(), maker.RemainingQty()) trades = append(trades, Trade{ Price: bestLevel.Price, Quantity: fillQty, TakerOrderID: taker.ID, MakerOrderID: maker.ID, TakerSide: taker.Side, Timestamp: time.Now().UnixNano(), }) taker.FilledQty = taker.FilledQty.Add(fillQty) maker.FilledQty = maker.FilledQty.Add(fillQty) if maker.RemainingQty().IsZero() { bestLevel.Orders = bestLevel.Orders[1:] delete(ob.Orders, maker.ID) } } if len(bestLevel.Orders) == 0 { ob.removeLevel(counterSide, bestLevel.Price) } } return trades } 

Снепшот та інкрементальні оновлення

Клієнт не отримує весь order book при підключенні — це можуть бути мегабайти. Архітектура:

  1. Клієнт запитує снепшот (top N рівнів, наприклад 50) через REST.
  2. Підписується на WebSocket channel diff updates.
  3. Застосовує diff до локальної копії.
Сценарій Дія
Нове підключення GET /api/v1/orderbook/snapshot?symbol=BTCUSDT&depth=50
Пропуск diff (sequence gap) Запросити новий снепшот
Нормальний потік Застосовувати diff з sequence+1
type OrderBookDiff = { sequence: number; bids: [string, string][]; asks: [string, string][]; }; class OrderBookClient { private bids = new Map<string, string>(); private asks = new Map<string, string>(); private lastSeq = 0; applyDiff(diff: OrderBookDiff) { if (diff.sequence <= this.lastSeq) return; if (diff.sequence !== this.lastSeq + 1) { this.requestSnapshot(); return; } diff.bids.forEach(([p, s]) => s === '0' ? this.bids.delete(p) : this.bids.set(p, s)); diff.asks.forEach(([p, s]) => s === '0' ? this.asks.delete(p) : this.asks.set(p, s)); this.lastSeq = diff.sequence; } } 

Візуалізація: групування по тікам та depth chart

Реальний order book може мати тисячі рівнів. Для відображення — групування по «тіку» (мінімальний крок ціни). Користувач перемикає тік (0.01, 0.1, 1 для BTC/USDT), і ми агрегуємо обсяги. Depth chart показує відносну глибину кожного рівня — зелений для bids, червоний для asks.

Типові проблеми та їх вирішення - **Race conditions при скасуванні та виконанні**: використовуємо RWMutex, але для високої конкурентності — lock-free структури. - **Пропуск diff оновлень**: клієнт зберігає sequence, при розриві запитує повний снепшот. - **Забагато рівнів у відповіді**: обмежуємо depth (top 50) і даємо групування.

Що входить в розробку order book

  • Архітектура та проектування: вибір алгоритму matching, схеми реплікації.
  • Реалізація in-memory engine на Go з latency < 100 μs.
  • REST API та WebSocket feed зі снепшотами та diff updates.
  • Frontend-компоненти на React з grouping та depth chart.
  • Інтеграція з PostgreSQL для персистентності ордерів.
  • Навантажувальне тестування (100k+ ордерів/сек).
  • Документація: OpenAPI, опис протоколу, інструкція з розгортання.
  • Навчання команди замовника.
  • Гарантія та підтримка: 3 місяці після деплою.

Як ми розробляємо order book під ключ

  1. Аналіз вимог та архітектура.
  2. Реалізація in-memory matching engine.
  3. REST API + WebSocket feed.
  4. Frontend-візуалізація.
  5. Інтеграція із зовнішніми системами.
  6. Навантажувальне тестування та оптимізація.
  7. Документація та передача.

Продуктивність

Для біржі з 10–20 парами та помірним обсягом: один Go-процес обробляє > 50 000 ордерів/сек. При необхідності — шардинг по парах та горизонтальне масштабування API. Зниження витрат на інфраструктуру за рахунок in-memory підходу становить до 30-40% при високому навантаженні.

Метрика Значення Умови
Matching latency < 100 μs In-memory, single core
Add order throughput 100k+ ops/sec Go, Red-Black tree
Cancel order O(1) Hash map lookup
Snapshot generation < 1 ms Top 50 levels
WS diff broadcast < 1 ms After each trade

Зв'яжіться з нами для оцінки вашого проєкту. Замовте розробку order book під ключ — отримайте консультацію інженера з досвідом 10+ років у blockchain. Економія коштів на інфраструктурі та підвищення продуктивності торгової платформи — реальні результати наших проєктів.