This repository contains a high-performance cryptocurrency matching engine built in C++. It implements core trading functionalities based on REG NMS-inspired principles of price-time priority and internal order protection.
The system is designed for high throughput and low latency by decoupling order matching from slower I/O operations (like disk writing and WebSocket broadcasting) through asynchronous queues and dedicated writer threads.
The project includes the core matching engine, a persistent Write-Ahead Log (WAL) for state recovery, a REST API for order management, a real-time WebSocket API for market data, and a functional frontend UI to visualize the system in action.
-
Core Matching Logic: Implements strict price-time priority (FIFO) for all orders.
-
Order Type Handling:
- Standard: Market, Limit
- Time-in-Force: Immediate-Or-Cancel (IOC), Fill-Or-Kill (FOK)
- Advanced Order Types (Bonus):
- Stop-Loss (Market)
- Stop-Limit
-
High-Performance Architecture:
- Asynchronous, thread-pooled broadcast queue for non-blocking WebSocket updates.
- Asynchronous Write-Ahead Log (WAL) for persistent logging without blocking the request thread.
- Fine-grained locking using
std::shared_mutexfor concurrent reads of the order book.
-
Persistence & Recovery (Bonus): All orders, trades, and cancels are written to a WAL. The engine replays this log on startup to fully restore the order book state.
-
Real-time Data APIs:
- REST API (via cpp-httplib) for order submission, cancellation, and system stats.
- WebSocket API for streaming L2 order book depth and live trade executions.
-
Fee Model (Bonus): Implements a simple maker-taker fee model, with fees calculated and included in trade reports.
-
Testing: Includes unit tests for core matching logic and a high-load benchmark client.
-
Frontend: A simple HTML/JS frontend is provided to visualize the order book, trade feed, and submit orders.
The engine is designed for high throughput. Benchmarking was performed using the included benchmark tool (tests/benchmark.cpp).
Benchmark Results:
========================================
Benchmark Results
========================================
Total Orders: 128000
Successful: 128000
Failed: 0
Success Rate: 100%
----------------------------------------
Duration: 85.876 seconds
Throughput: 1490.52 orders/sec
----------------------------------------
Avg Latency: 21.1665 ms
Min Latency: 0.2273 ms
Max Latency: 3077.38 ms
========================================
The core of the engine is the OrderBook class. It uses two std::maps to manage bids and asks, ensuring price priority.
std::map<long long, std::deque<Order>, std::greater<long long>> bids_
std::map<long long, std::deque<Order>> asks_This design choice provides:
- Price Priority:
std::mapautomatically sorts orders by price (key).std::greateris used for bids to sort them from highest to lowest. - Time Priority:
std::deque<Order>at each price level acts as a FIFO queue, ensuring orders are matched based on their arrival time.
To achieve high performance, the HTTP request thread (which handles order submission) is decoupled from all slow I/O operations.
Order Submission (/orders):
- An HTTP request is received by
server.cpp. - The order is placed in the
global_walqueue (very fast, in-memory push). - The order is given to the
OrderBookfor matching. This is a blocking, in-memory operation. - Any resulting trades are also pushed to the
global_walqueue and theg_broadcast_queue. - The HTTP response is sent immediately to the client.
Asynchronous WAL:
- A dedicated thread in the
WALclass consumes thequeue_. - It batches writes and flushes them to the
data/wal.jsonlfile, ensuring persistence without impacting the order matching latency.
Asynchronous Broadcasting:
- A thread pool in
BroadcastQueueconsumes the message queue (queue_). - Worker threads format the JSON for trades and order book updates.
- The
WebSocketServerthen broadcasts this data to all connected clients.
On startup, main.cpp calls global_wal.replay(). This function reads the entire wal.jsonl file, reconstructs the state of all open orders (handling creations, trades, and cancels), and repopulates the g_order_books map before the server starts accepting connections.
The server runs two services: a REST API (default port 8080) and a WebSocket API (default port 9002).
Submits a new order.
- Body (JSON):
{
"symbol": "BTC-USDT",
"order_type": "limit", // "limit", "market", "ioc", "fok"
"side": "buy",
"quantity": 1.5,
"price": 50000.0 // Required for limit, ioc, fok
}- Success Response (200 OK):
{
"order": {
"order_id": "ORD-123",
"status": "open", // "filled", "partially_filled", "cancelled"
/* ... other order details ... */
},
"trades": [ /* Array of trade execution reports, if any */ ],
"filled_quantity": 0,
"remaining_quantity": 1500000
}Submits a new stop order.
- Body (JSON):
{
"symbol": "BTC-USDT",
"stop_type": "stop_limit", // "stop_loss" or "stop_limit"
"side": "sell",
"quantity": 0.5,
"trigger_price": 49000.0,
"limit_price": 48950.0 // Required for "stop_limit"
}- Success Response (200 OK):
{
"status": "accepted",
"stop_order_id": "STO-124",
"order": { /* ... stop order details ... */ }
}Cancels an active limit or stop order by its ID.
- Success Response (200 OK):
{
"cancelled": true,
"order_id": "ORD-123",
"symbol": "BTC-USDT",
"timestamp": "..."
}Retrieves a snapshot of the order book depth.
-
Example:
/orderbook/BTC-USDT -
Success Response (200 OK):
{
"symbol": "BTC-USDT",
"bids": [ {"price": 49999.0, "quantity": 2.5, "total": 124997.5}, ... ],
"asks": [ {"price": 50000.0, "quantity": 1.5, "total": 75000.0}, ... ],
"timestamp": "..."
}Retrieves recent trades for a symbol (by replaying WAL).
- Example:
/trades/BTC-USDT
Retrieves global engine statistics.
Simple health check endpoint.
Connect to ws://localhost:9002. The server pushes two types of messages:
Sent whenever a trade occurs.
- Format:
{
"type": "trade",
"data": {
"trade_id": "T-100",
"symbol": "BTC-USDT",
"price": 5000000, // Integer price
"quantity": 500000, // Integer quantity
"aggressor_side": "buy",
"maker_order_id": "ORD-123",
"taker_order_id": "ORD-124",
"maker_fee": 5000,
"taker_fee": 10000,
"timestamp": "..."
}
}Sent after any event that changes the order book (new order, cancel, trade).
Sent after any event that changes the order book (new order, cancel, trade).
- Format:
{
"type": "orderbook",
"data": {
"symbol": "BTC-USDT",
"bids": [ {"price": 4999900, "quantity": 2500000}, ... ],
"asks": [ {"price": 5000000, "quantity": 1500000}, ... ],
"timestamp": 1678886400000
}
}The project uses CMake.
Prerequisites (Linux/macOS):
cmake >= 3.10- A C++17 compliant compiler (e.g.,
g++)
The required headers (httplib.h, json.hpp, catch.hpp) are included as vendor files.
# Create vendor directory and download headers
mkdir -p vendor data
cd vendor
curl -L -o httplib.h https://raw.githubusercontent.com/yhirose/cpp-httplib/master/httplib.h
curl -L -o json.hpp https://raw.githubusercontent.com/nlohmann/json/develop/single_include/nlohmann/json.hpp
curl -L -o catch.hpp https://raw.githubusercontent.com/catchorg/Catch2/devel/single_include/catch2/catch.hpp
cd ..mkdir -p build && cd build
cmake ..
cmake --build . -- -j4This starts the HTTP (8080) and WebSocket (9002) servers.
# Run from the 'build' directory
./matching_engine 8080 9002# Run from the 'build' directory
./testsor
ctest --output-on-failure# Run from the 'build' directory
# Usage: ./benchmark [threads] [orders_per_thread]
./benchmark 8 10000The project includes a real-time web dashboard to visualize order book depth, live trades, and submit orders through a user-friendly interface.
Steps to launch the frontend:
-
Start the matching engine (if not already running):
cd build ./matching_engine -
Serve the frontend using Python HTTP server:
cd frontend python -m http.server 3000 -
Open in browser: Navigate to
http://localhost:3000
Features:
- π Live Order Book - Real-time L2 depth visualization with bid/ask spread
- π Trade Feed - Streaming trade executions with maker/taker details
- πΉ Order Entry - Submit market, limit, IOC, and FOK orders
- π― Stop Orders - Create stop-loss and stop-limit orders
- β Order Management - Cancel active orders
- π‘ WebSocket Connection - Live updates via WebSocket (auto-reconnect)
Note: Make sure both the matching engine (port 8080 for HTTP, 9002 for WebSocket) and the frontend server (port 3000) are running simultaneously.