Розробка торгового рухаючка (matching engine)
Matching engine — це ядро будь-якої біржи. Він приймає ордери, зводить покупців із продавцями й генерує трейди. Це найпродуктивніший і найкритичніший компонент: затримка в мікросекундах має значення, помилка в логіці ціноутворення — катастрофа. Розберемо архітектуру зсередини.
Що таке matching engine
Функції
- Приймання ордерів: новий ордер валідується й спрямовується в правильну order book
- Matching: пошук сумісних ордерів, генерація трейдів
- Управління ордерами: partial fills, cancellations, expiry
- Публікація подій: трейди, оновлення стакана → downstream системи
Усе це повинно відбуватися послідовно (single-threaded matching core) для запобігання race conditions. Паралельність додається на рівні різних торгових пар — кожен символ має власний thread/core.
Price-Time Priority (FIFO)
Стандартний алгоритм matching:
- Price priority: ордер з кращої ціною виконується першим
- Time priority: при рівній ціні — більш ранній ордер має приоритет
Структури даних
Order Book як sorted data structure
Використання BTreeMap замість HashMap для ціновых рівнів: O(log n) для insert/delete, але ітерація по рівнях = O(1) для best price.
Integer prices замість floating point
Floating point арифметика заборонена у фінансових розрахунках. 0.1 + 0.2 != 0.3 у IEEE 754.
// Неправильно: float
let price: f64 = 43750.50;
// Правильно: integer з фіксованою точністю
const PRICE_PRECISION: i64 = 100_000_000; // 10^8
let price: i64 = 4_375_050_000_000; // 43750.50000000
Алгоритм matching
Limit Order matching
Процес:
- Matching проти протилежної сторони
- Перевірка ціни crossing
- Виконання на матченій ціні рівні (FIFO)
- Оновлення заповнених кількостей
- Видалення заповнених ордерів з book
- Додавання залишку до book якщо не повністю заповнено
Market Order
Виконується негайно по найкращої доступної ціні.
Stop Orders
Stop ордери — це тригери, не активні ордери в book.
Event Sourcing та persistence
Event журнал
Matching engine не записує прямо в БД — це убить продуктивність. Замість цього: всі события записуються в append-only log (Kafka).
Matching Engine → Kafka (trades, order_updates) → DB Writer
→ Balance Updater
→ Market Data Publisher
→ Notification Service
Kafka гарантує ordering в межах partition.
Snapshot + Replay
При рестарті matching engine відновлює стан з останнього snapshot + replay подій.
Продуктивність та оптимізації
Профілювання bottlenecks
Типові bottlenecks matching engine у порядку приоритету:
- Memory allocation: кожен новий ордер = heap allocation. Рішення: object pool / arena allocator
- Serialization: упаковка подій у Kafka. Рішення: FlatBuffers / Cap'n Proto замість JSON
- Locking: якщо кілька потоків, будь-який мьютекс убиває throughput. Рішення: single-threaded per-symbol + lock-free queues
- Cache misses: неоптимальна memory layout. Рішення: cache-friendly структури
LMAX Disruptor паттерн
LMAX Disruptor — lock-free ring buffer для inter-thread комунікації, розроблений для high-frequency trading.
Ring buffer на 2^N елементів, wrapped sequence number. Latency: ~1-2 nanoseconds vs ~100ns для mutex.
Latency benchmarks
| Реалізація | P50 | P99 | P99.9 |
|---|---|---|---|
| Python (asyncio) | 2ms | 15ms | 100ms |
| Go (stdlib) | 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 особливо важлив для репутації — саме туди попадають скарги трейдерів.
Тестування
Matching engine покривається unit-тестами на граничні случаї:
- Partial fill з залишком
- FOK при недостатній ліквідності
- IOC з частичним виконанням
- Одночасна скасування й fill (race condition)
- Stop ордер триггерується в момент свого розміщення
- Decimal overflow на крайніх значеннях
Property-based testing (fuzzing) — генеруються случайні послідовності ордерів, перевіряється інваріант: суммарний обсяг куплено = суммарному обсягу продано, балансі сходяться.
Matching engine — це компонент, який пишеться один раз і підтримується роками. Інвестиції в якість коду, тести та документацію окупаються багатократно.







