Orderbook Update 2: Implementing the Matching Engine
Implementing a C++ Matching Engine: Deep Dive into Orderbook Design
In this post, we'll explore the implementation of an orderbook's matching engine, focusing on data structure choices and algorithmic efficiency. Let's break down the key components and design decisions that make up an efficient matching system.
Core Data Structures
The heart of our orderbook lies in three main data structures:
std::map<Price, OrderPointers, std::greater<Price>> bids_; std::map<Price, OrderPointers, std::less<Price>> asks_; std::unordered_map<OrderId, OrderEntry> orders_;
This design showcases several key considerations:
- Price Level Organization: Using
std::mapfor bids and asks provides automatic price-time priority through ordered storage. Note the different comparators -std::greaterfor bids (highest price first) andstd::lessfor asks (lowest price first). - Order Access: The
unordered_mapprovides O(1) lookup for order management operations using order IDs as keys. - Memory Management: Orders are stored as
shared_ptr(OrderPointer) to handle memory automatically while maintaining references across containers.
The Matching Algorithm
The core matching logic demonstrates how to efficiently pair orders:
Trades MatchOrders() { Trades trades; trades.reserve(orders_.size()); // Optimization to prevent reallocation while (true) { if (bids_.empty() || asks_.empty()) break; auto& [bidPrice, bids] = *bids_.begin(); auto& [askPrice, asks] = *asks_.begin(); if (bidPrice < askPrice) break;
This implementation:
- Maintains price-time priority naturally through container ordering
- Uses structured bindings for cleaner code
- Pre-reserves trade vector space for performance
Order Lifecycle Management
Order management involves three main operations:
1. Adding Orders
Trades AddOrder(OrderPointer order) { if (orders_.contains(order->GetOrderId())) return { }; if (order->GetOrderType() == OrderType::FillAndKill && !CanMatch(order->GetSide(), order->GetPrice())) return { };
The addition process:
- Validates order uniqueness
- Handles FillAndKill orders specially
- Maintains iterator stability through careful container management
2. Cancelling Orders
void CancelOrder(OrderId orderId) { if (!orders_.contains(orderId)) return; const auto& [order, iterator] = orders_.at(orderId); orders_.erase(orderId);
Cancellation ensures:
- Clean removal from all containers
- Proper price level cleanup when empty
- Iterator validity maintenance
3. Order Modification
Trades MatchOrder(OrderModify order) { if (!orders_.contains(order.GetOrderId())) return { }; const auto& [existingOrder, _] = orders_.at(order.GetOrderId()); CancelOrder(order.GetOrderId()); return AddOrder(order.ToOrderPointer(existingOrder->GetOrderType())); }
Modification is implemented as cancel-replace, which:
- Maintains fairness in price-time priority
- Simplifies the implementation
- Provides clean order history
Market Data Generation
The system can efficiently generate market data snapshots:
OrderbookLevelInfos GetOrderInfos() const { LevelInfos bidInfos, askInfos; // ... aggregation logic ... return OrderbookLevelInfos { bidInfos, askInfos }; }
This provides:
- Efficient level aggregation
- Clean separation of market data from matching logic
- Easy integration with market data distribution systems
Performance Considerations
Several optimizations are worth noting:
- Iterator Stability: Using
std::listfor OrderPointers ensures iterators remain valid during modifications. - Memory Pre-allocation: Strategic use of
reserve()reduces reallocations. - Reference Usage: Careful use of references prevents unnecessary copying.
- Efficient Lookups: O(1) order lookups via
unordered_mapcombined with O(log n) price level access.
Next Steps
Future enhancements could include:
- Multi-threaded order processing
- More sophisticated order types
- Improved market data generation
- Performance benchmarking infrastructure
The current implementation provides a solid foundation for these additions while maintaining clean, efficient code structure. Thank you for following along on this journey. Stay tuned for more updates on the Order book.