Представьте: ваша криптобиржа теряет миллисекунды на каждом ордере, хайп-трейдеры уходят к конкурентам с 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 месяцев в зависимости от сложности. Стоимость рассчитывается индивидуально. Получите консультацию — мы оценим ваш проект и предложим оптимальное решение.







