A low-latency limit order book implementation in C++20 with real-time market data integration. Built to handle high-frequency trading workloads with microsecond-level latency.
This project implements a matching engine and order book that supports multiple order types, priority-based matching, and real-time market data processing. The architecture is designed for performance-critical applications where latency matters.
Key metrics:
- Order insertion: ~400,000 orders/sec
- Order matching: ~350,000 matches/sec
- Order cancellation: ~2,000,000 cancels/sec
- Average operation latency: 2-4 μs
- Order Types: GoodTillCancel, Market, ImmediateOrCancel, FillOrKill, GoodForDay
- Matching Algorithm: Price-time priority (FIFO within price levels)
- Data Structures: O(1) order lookup, O(log n) price level access
- Trade Execution: Automatic matching with partial fill support
- Real-time orderbook snapshots via Binance REST API
- Incremental update processing (new orders, cancellations, modifications)
- Batch message processing for improved throughput
- Sequence number tracking for gap detection
- Latency monitoring and statistics
- Real-time visualization of cryptocurrency orderbooks
- Configurable refresh rates and depth levels
- Bid-ask spread analysis and mid-price calculation
- Market microstructure metrics
- CMake 3.10+
- C++20 compatible compiler (GCC 10+, Clang 10+, MSVC 2019+)
- Dependencies (automatically fetched via CMake):
- libcurl 8.4.0
- nlohmann/json 3.11.3
mkdir build && cd build
cmake ..
cmake --build . --config Release# Run functionality and performance tests
./OrderBookTests
# Live cryptocurrency orderbook
./LiveMarketData SOLUSDT 1 20
# Args: [SYMBOL] [REFRESH_SECONDS] [DEPTH_LEVELS]classDiagram
%% Core Type Aliases
class Types {
<<typedef>>
+Price: int32_t
+Quantity: uint32_t
+OrderId: uint64_t
}
%% Enumerations
class OrderType {
<<enumeration>>
GoodTillCancel
ImmediateOrCancel
Market
GoodForDay
FillOrKill
}
class Side {
<<enumeration>>
Buy
Sell
}
class MessageType {
<<enumeration>>
NewOrder
CancelOrder
ModifyOrder
Trade
BookSnapshot
}
%% Constants
class Constants {
<<static>>
+InvalidPrice: Price
}
%% Order Classes
class Order {
-orderType_: OrderType
-orderId_: OrderId
-side_: Side
-price_: Price
-initialQuantity_: Quantity
-remainingQuantity_: Quantity
+Order(OrderType, OrderId, Side, Price, Quantity)
+Order(OrderId, Side, Quantity)
+GetOrderId(): OrderId
+GetSide(): Side
+GetPrice(): Price
+GetOrderType(): OrderType
+GetInitialQuantity(): Quantity
+GetRemainingQuantity(): Quantity
+GetFilledQuantity(): Quantity
+IsFilled(): bool
+Fill(Quantity): void
+ToGoodTillCancel(Price): void
}
class OrderModify {
-orderId_: OrderId
-price_: Price
-side_: Side
-quantity_: Quantity
+OrderModify(OrderId, Side, Price, Quantity)
+GetOrderId(): OrderId
+GetPrice(): Price
+GetSide(): Side
+GetQuantity(): Quantity
+ToOrderPointer(OrderType): OrderPointer
}
%% Trade Classes
class TradeInfo {
+orderId_: OrderId
+price_: Price
+quantity_: Quantity
}
class Trade {
-bidTrade_: TradeInfo
-askTrade_: TradeInfo
+Trade(TradeInfo, TradeInfo)
+GetBidTrade(): TradeInfo
+GetAskTrade(): TradeInfo
}
%% Level Info Classes
class LevelInfo {
+price_: Price
+quantity_: Quantity
}
class OrderbookLevelInfos {
-bids_: LevelInfos
-asks_: LevelInfos
+OrderbookLevelInfos(LevelInfos, LevelInfos)
+GetBids(): LevelInfos
+GetAsks(): LevelInfos
}
%% Market Data Messages
class NewOrderMessage {
+type: MessageType
+orderId: OrderId
+side: Side
+price: Price
+quantity: Quantity
+orderType: OrderType
+timestamp: time_point
}
class CancelOrderMessage {
+type: MessageType
+orderId: OrderId
+timestamp: time_point
}
class ModifyOrderMessage {
+type: MessageType
+orderId: OrderId
+side: Side
+newPrice: Price
+newQuantity: Quantity
+timestamp: time_point
}
class TradeMessage {
+type: MessageType
+buyOrderId: OrderId
+sellOrderId: OrderId
+price: Price
+quantity: Quantity
+timestamp: time_point
}
class SnapshotLevel {
+price: Price
+quantity: Quantity
+orderCount: int
}
class BookSnapshotMessage {
+type: MessageType
+bids: vector~SnapshotLevel~
+asks: vector~SnapshotLevel~
+timestamp: time_point
+sequenceNumber: uint64_t
}
class MarketDataStats {
+messagesProcessed: uint64_t
+newOrders: uint64_t
+cancellations: uint64_t
+modifications: uint64_t
+trades: uint64_t
+snapshots: uint64_t
+errors: uint64_t
+sequenceGaps: uint64_t
+totalProcessingTime: microseconds
+maxLatency: microseconds
+minLatency: microseconds
+Reset(): void
+GetAverageLatencyMicros(): double
}
%% Main Orderbook Class
class Orderbook {
-OrderEntry: struct
-bids_: map~Price, OrderPointers~
-asks_: map~Price, OrderPointers~
-orders_: unordered_map~OrderId, OrderEntry~
-lastDayReset_: time_point
-dayResetHour_: hours
-dayResetMinute_: int
-stats_: MarketDataStats
-lastSequenceNumber_: uint64_t
-isInitialized_: bool
-CanMatch(Side, Price): bool
-CheckAndResetDay(): void
-CancelGoodForDayOrders(): void
-CollectMatchesForFillOrKill(): vector
-ExecuteMatchesForFillOrKill(): Trades
-MatchFillOrKill(OrderPointer): Trades
-MatchOrders(): Trades
-ProcessNewOrder(NewOrderMessage): void
-ProcessCancel(CancelOrderMessage): void
-ProcessModify(ModifyOrderMessage): void
-ProcessTrade(TradeMessage): void
-ProcessSnapshot(BookSnapshotMessage): void
+Orderbook()
+SetDayResetTime(int, int): void
+AddOrder(OrderPointer): Trades
+CancelOrder(OrderId): void
+MatchOrder(OrderModify): Trades
+Size(): size_t
+GetOrderInfos(): OrderbookLevelInfos
+ProcessMarketData(MarketDataMessage): bool
+ProcessMarketDataBatch(vector): size_t
+GetMarketDataStats(): MarketDataStats
+ResetMarketDataStats(): void
+IsInitialized(): bool
+GetLastSequenceNumber(): uint64_t
}
class OrderEntry {
<<internal struct>>
+order_: OrderPointer
+location_: iterator
}
%% Relationships
Order --> OrderType: uses
Order --> Side: uses
OrderModify --> Side: uses
OrderModify --> Order: creates
Trade *-- TradeInfo: contains 2
OrderbookLevelInfos *-- LevelInfo: contains many
Orderbook *-- OrderEntry: contains
Orderbook o-- Order: manages
Orderbook --> Trade: produces
Orderbook --> OrderbookLevelInfos: produces
Orderbook *-- MarketDataStats: tracks
NewOrderMessage --> MessageType: uses
NewOrderMessage --> Side: uses
NewOrderMessage --> OrderType: uses
CancelOrderMessage --> MessageType: uses
ModifyOrderMessage --> MessageType: uses
ModifyOrderMessage --> Side: uses
TradeMessage --> MessageType: uses
BookSnapshotMessage --> MessageType: uses
BookSnapshotMessage *-- SnapshotLevel: contains many
Orderbook --> NewOrderMessage: processes
Orderbook --> CancelOrderMessage: processes
Orderbook --> ModifyOrderMessage: processes
Orderbook --> TradeMessage: processes
Orderbook --> BookSnapshotMessage: processes
Constants --> Types: uses
Orderbook
├── bids_: map<Price, OrderPointers, greater> // Best bid first
├── asks_: map<Price, OrderPointers, less> // Best ask first
└── orders_: unordered_map<OrderId, OrderEntry> // O(1) lookup
Price levels are stored in ordered maps for efficient best bid/ask access. Within each price level, orders are maintained in a FIFO queue for time priority.
Order lookup uses a hash map storing both the order pointer and its iterator position, enabling O(1) cancellation.
Orders match when:
- Buy price ≥ Best ask price, or
- Sell price ≤ Best bid price
Matching proceeds in price-time priority:
- Best price levels matched first
- Within a price level, earliest orders matched first (FIFO)
- Partial fills supported for all order types except FillOrKill
The orderbook can be initialized and updated via market data messages:
- BookSnapshotMessage: Full orderbook rebuild
- NewOrderMessage: Add order to book
- CancelOrderMessage: Remove order from book
- ModifyOrderMessage: Modify existing order (cancel + add)
- TradeMessage: Record executed trade (informational)
Processing pipeline tracks sequence numbers, latency, and message statistics.
| Operation | Complexity | Measured Throughput |
|---|---|---|
| Add Order | O(log n) | ~400K ops/sec |
| Cancel Order | O(1) | ~2M ops/sec |
| Modify Order | O(log n) | ~270K ops/sec |
| Match Orders | O(k log n) | ~350K matches/sec |
| Get Order Info | O(m) | ~500K snapshots/sec |
n = number of price levels, k = number of matches, m = number of orders
Benchmarks run on typical development hardware. Actual performance depends on system configuration and workload characteristics.
Why separate bids/asks maps? Allows different sorting orders (descending for bids, ascending for asks) and simplifies best bid/ask access.
Why store iterators in OrderEntry? Enables O(1) removal from the price level list when canceling orders.
Why shared_ptr for orders? Allows orders to be referenced in multiple places (main map, price level list) without copying. Trade-off between memory overhead and simplicity.
Market order conversion: Market orders are converted to limit orders at extreme prices (max/min) to reuse the matching logic.
- GoodTillCancel: Remains active until filled or cancelled
- Market: Immediately converted to aggressive limit order
- ImmediateOrCancel: Partial fills accepted, unfilled portion cancelled
- FillOrKill: All-or-nothing execution, rejected if can't fill completely
- GoodForDay: Cancelled at configured time (default 15:59)
The live display fetches orderbook snapshots from Binance's REST API at configurable intervals. Each snapshot replaces the previous orderbook state. This approach is suitable for visualization but not for production trading systems, which would use WebSocket feeds for incremental updates.
Functionality tests verify correctness of matching logic, order types, and edge cases.
Performance benchmarks measure throughput and latency under various workloads.
Market data tests simulate realistic trading scenarios with thousands of orders.
Run all tests:
./OrderBookTests- Algorithmic trading strategy backtesting
- Market microstructure research
- Order routing simulation
- Exchange matching engine prototyping
- Educational tool for understanding limit order books
- Single instrument (no multi-asset support)
- No persistence layer
- Live data uses polling instead of WebSocket streaming
- Synthetic order IDs for aggregated book levels
- No regulatory compliance features (audit logs, trade reporting)
Potential extensions for production use:
- WebSocket feed integration for lower latency
- Position and risk management
- Multiple matching algorithms (pro-rata, size pro-rata)
- Iceberg and other advanced order types
- Historical data replay and backtesting framework
- Market impact modeling