Building a Limit Order Book: Lessons in Market Microstructure
Most developers interested in finance naturally gravitate toward trading strategies, machine learning models, or backtesting frameworks. I took a different path: I wanted to understand the plumbing. How does a trade actually execute inside an exchange? What happens when market orders collide with resting limit orders? Why do market makers fear adverse selection?
These questions led me to build a limit order book simulator from scratch in C++. What started as intellectual curiosity became a masterclass in market microstructure, performance optimization, and the subtle complexities that textbooks rarely cover.
Why Build This?
As I prepare for the CQF (Certificate in Quantitative Finance), I've become increasingly aware that understanding markets requires more than knowing Black-Scholes or CAPM. The real action happens at the microstructure level—the mechanics of how buyers meet sellers, how liquidity forms and evaporates, and how milliseconds (or microseconds) determine winners and losers.
Reading papers on high-frequency trading and market making raised fundamental questions:
- How exactly does price-time priority work?
- What prevents someone from gaming iceberg orders?
- Why does order size impact execution quality so dramatically?
The only way to truly understand was to build it myself.
The Core Challenge: Price-Time Priority
Every modern exchange operates on price-time priority: orders at better prices execute first, and among orders at the same price, earlier orders have priority. Simple in theory. Devilishly nuanced in practice.
Consider iceberg orders—large orders that only show a small "visible" quantity to prevent market impact. When the visible portion executes and new quantity "reloads" from the hidden portion, what happens to the order's priority?
The answer: It loses time priority and moves to the back of the queue at that price level.
This anti-gaming mechanism exists in real exchanges (NSE, CME, NASDAQ) but rarely appears in academic treatments. Implementing it required careful tracking of order insertion times and rebuilding queue positions on every reload. The devil, as always, lives in the implementation details.
// When an iceberg order reloads, it loses time priority
if (order->isIceberg() && order->hasHiddenQuantity()) {
// Remove from current position
removeFromQueue(order);
// Reload visible quantity from hidden
order->reload();
// Re-insert at BACK of queue at this price level
insertAtEndOfQueue(order);
}
Performance: The Nanosecond Game
After implementing the basic matching logic, I built a latency monitor to measure performance at nanosecond precision. The results were eye-opening:
- Mean order processing: ~150 nanoseconds
- P99 latency: ~1 microsecond
For context, network latency between New York and Chicago is roughly 7 milliseconds—nearly 50,000 times slower than a single order operation. This explains why high-frequency trading firms:
- Co-locate servers inside exchange data centers
- Use FPGAs instead of general-purpose CPUs
- Obsess over cache locality and data structure selection
I initially tested using std::vector for order queues (better cache locality) but found the insertion/deletion overhead uneconomical. Switching to std::list improved performance for the access patterns of a matching engine. At this scale, algorithmic complexity matters more than cache effects.
Market Impact: The Cost of Impatience
The most illuminating experiment involved simulating large market orders "walking the book"—consuming liquidity at progressively worse prices.
Testing a 700-share market order against a populated order book showed:
- First 50 shares: minimal slippage
- Next 200 shares: moderate degradation
- Final 450 shares: $0.85 per share worse than the initial price
The impact wasn't linear—it accelerated. This mathematical reality is why institutional traders use execution algorithms (TWAP, VWAP, implementation shortfall) rather than blasting market orders. Every share you demand consumes liquidity, and the book gets progressively more expensive.
// Cumulative market impact calculation
double cumulativeImpact = 0.0;
for (const auto& fill : execution.fills) {
double slippage = fill.price - initialBestPrice;
cumulativeImpact += slippage * fill.quantity;
}
double avgImpactPerShare = cumulativeImpact / totalQuantity;
This simple simulation made real what I'd only understood theoretically: liquidity is not infinite, and impatience is expensive.
Technical Architecture
The matching engine uses three core data structures:
std::map<Price, OrderQueue>for price levels—automatic price ordering with log(n) accessstd::list<Order>for FIFO order queues—O(1) insertion/deletion at arbitrary positionsstd::unordered_map<OrderID, Iterator>for order lookup—O(1) cancellations and modifications
The design prioritizes the access patterns of a matching engine: frequent insertions, deletions, and price-level traversal. While a production exchange would use custom memory allocators and lock-free structures, this architecture captures the essential tradeoffs.
What I Learned
Markets are emergent systems: The complexity doesn't come from individual components but from their interactions. Simple matching rules create sophisticated behaviors—price discovery, liquidity clustering, adverse selection.
Performance is measurable: Before this project, "nanosecond latency" was an abstract concept. Now I understand why HFT firms pay millions for microsecond advantages—when your operations take 150 nanoseconds, every optimization compounds.
Textbooks omit crucial details: Academic finance focuses on equilibrium models. Real markets are state machines with discrete events, queue management, and anti-gaming mechanisms. The gap between theory and implementation is vast.
Market microstructure matters: Understanding order books, tick sizes, maker-taker fees, and queue priority is essential for anyone serious about quantitative finance. These mechanics determine execution quality, transaction costs, and ultimately, strategy profitability.
What's Next
This project has several natural extensions:
- Adverse selection simulation: Modeling how market makers get "picked off" when informed traders arrive
- Stop orders and conditional logic: Adding more order types to match real exchange capabilities
- Memory optimization: Exploring object pools and custom allocators for production-grade performance
- Multi-level books: Simulating how aggregated depth impacts institutional execution strategies
More importantly, this project has deepened my curiosity about quantitative finance. Understanding how trades execute is foundational for understanding market making, liquidity provision, and the economics of trading venues.
As I continue toward the CQF and deeper quantitative finance knowledge, projects like this remind me that the best learning comes from building. You can read a hundred papers on market microstructure, but nothing compares to implementing price-time priority at nanosecond precision and watching slippage compound on a 700-share order.
Final Thoughts
If you're interested in quantitative finance, don't just study strategies—study the infrastructure. Build an order book. Simulate execution algorithms. Measure latency. The financial markets are the world's most sophisticated distributed systems, and understanding their plumbing is as valuable as understanding their mathematics.
The code is open source and available on GitHub. Whether you're preparing for the CQF, exploring HFT, or just curious about market mechanics, I hope this project offers a useful reference.
The markets are waiting. Time to dig deeper.
Want to discuss market microstructure, quantitative finance, or C++ performance optimization? Reach out—I'm always happy to talk about the plumbing.