Проблема: 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 при підключенні — це можуть бути мегабайти. Архітектура:
- Клієнт запитує снепшот (top N рівнів, наприклад 50) через REST.
- Підписується на WebSocket channel diff updates.
- Застосовує 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 під ключ
- Аналіз вимог та архітектура.
- Реалізація in-memory matching engine.
- REST API + WebSocket feed.
- Frontend-візуалізація.
- Інтеграція із зовнішніми системами.
- Навантажувальне тестування та оптимізація.
- Документація та передача.
Продуктивність
Для біржі з 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. Економія коштів на інфраструктурі та підвищення продуктивності торгової платформи — реальні результати наших проєктів.







