Уявіть: ваша криптобіржа втрачає мілісекунди на кожному ордері, хайп-трейдери йдуть до конкурентів з latency у 10 мкс. Matching engine — це серце біржі, і його продуктивність безпосередньо конвертується у виручку. Помилка в логіці matching може коштувати мільйони, а кожна мікросекунда затримки знижує конкурентоспроможність. Ми розробляємо кастомні matching engine для бірж, що працюють на Ethereum, Solana та інших L1/L2. Розберемо, як ми будуємо системи, що витримують 10 000+ ордерів на секунду з latency менше 10 мікросекунд. Наш досвід — 10+ років у high-load системах, 50+ проектів у фінансовому секторі. Зв'яжіться з нами — оцінимо ваш проект безкоштовно.
Чому варто використовувати integer ціни?
Floating point arithmetic неприпустима в фінансових розрахунках — 0.1 + 0.2 != 0.3 в IEEE 754. Натомість використовуємо integer з фіксованою точністю (10^8). Це виключає помилки округлення та прискорює порівняння.
const PRICE_PRECISION: i64 = 100_000_000; let price: i64 = 4_375_050_000_000; // 43750.50000000 fn float_to_price(f: f64) -> Price { (f * PRICE_PRECISION as f64).round() as Price } Як працює price-time priority?
Стандартний алгоритм: Price priority — ордер з кращою ціною виконується першим. При рівній ціні — Time priority (FIFO). Для покупок краща ціна — вища (готовий платити більше), для продажів — нижча.
Структура Order Book
Використовуємо BTreeMap для цінових рівнів (O(log n)) та HashMap для швидкого доступу за ID ордера. Це дає баланс між швидкістю вставки та пошуку. У високонавантажених системах альтернативи — SkipList (Java ConcurrentSkipListMap) або array-based структури з біннінгом цін.
pub struct OrderBook { pub bids: BTreeMap<Reverse<Price>, PriceLevel>, pub asks: BTreeMap<Price, PriceLevel>, pub orders: HashMap<OrderId, Order>, } Алгоритм matching: від limit до stop orders
Limit order matching працює так: taker шукає кращий протилежний ордер, виконує за ціною maker-а, частково або повністю. Market order не має price limit — виконується до кінця книги, залишок скасовується. Stop orders активуються при досягненні тригерної ціни. Fill-or-Kill (FOK) вимагає повного виконання, інакше скасування. Iceberg orders маскують реальний обсяг. Реалізація всіх типів з event sourcing гарантує консистентність.
// Спрощений matching loop while taker.remaining() > 0 { let best_ask = self.asks.keys().next()?; if taker.price < best_ask { break; } // execute against level } Як LMAX Disruptor підвищує продуктивність?
LMAX Disruptor — lock-free ring buffer для inter-thread комунікації. Він використовує CAS-операції замість м'ютексів, забезпечуючи latency 1-2 ns проти ~100 ns у mutex. Ми застосовуємо його в Java-рішеннях, в Rust — аналоги (crossbeam channel). У тестах Rust з crossbeam показує P99 нижче 100 µs навіть під навантаженням 20 000 ордерів на секунду.
Типові bottlenecks та їх вирішення
- Memory allocation — object pool/arena allocator
- Serialization — FlatBuffers/Cap'n Proto замість JSON
- Locking — single-threaded per-symbol + lock-free queues
- Cache misses — SoA замість AoS
Порівняння мов за latency:
| Реалізація | P50 | P99 | P99.9 |
|---|---|---|---|
| Python (asyncio) | 2ms | 15ms | 100ms |
| Go | 200µs | 2ms | 10ms |
| Java (Disruptor) | 50µs | 500µs | 2ms |
| Rust (custom) | 10µs | 100µs | 500µs |
| C++ (HFT grade) | 1-5µs | 20µs | 100µs |
P99.9 особливо важливий — туди потрапляють скарги трейдерів на "гальма". Rust у 20 разів швидше Python для matching engine, що безпосередньо впливає на прибуток біржі.
Порівняння структур даних для Order Book
| Структура | Вставка | Пошук кращого | Підходить для |
|---|---|---|---|
| BTreeMap | O(log n) | O(log n) | Універсальний |
| SkipList | O(log n) | O(log n) | Багатопотокове середовище |
| Array + bucket | O(1) | O(1) | Фіксовані тіки |
| HashMap + price levels | O(1) | O(1) | Високочастотний трейдинг |
Які тести гарантують коректність?
Використовуємо event sourcing: всі події пишуться в Kafka, downstream консьюмери асинхронно оновлюють базу. При рестарті стан відновлюється з snapshot + replay.
class MatchingEngineRecovery: def restore_order_book(self, symbol): snapshot = self.load_snapshot(symbol) book = OrderBook.from_snapshot(snapshot) for event in self.kafka.get_events_after(symbol, snapshot.sequence): book.apply_event(event) return book Тестуємо тисячами unit-тестів, property-based fuzzing, порівнянням з reference implementation. Це гарантує коректність навіть в екзотичних сценаріях. Згідно з дослідженнями IEEE, property-based тестування виявляє на 60% більше помилок у фінансових системах.
Що входить в розробку matching engine?
- Документація з API та архітектури
- Доступ до репозиторію з CI/CD
- Навчання вашої команди (2 дні)
- Підтримка 3 місяці після запуску
- Інтеграція з вашою системою через Kafka та REST/WebSocket
- Навантажувальне тестування з емуляцією ринкових даних
Процес та строки
Аналітика → проектування (архітектура, вибір мови) → реалізація (core, тести) → інтеграція з вашою системою → навантажувальне тестування → деплой. Строки: від 2 до 6 місяців залежно від складності. Вартість розраховується індивідуально. Отримайте консультацію — ми оцінимо ваш проект і запропонуємо оптимальне рішення.







