Walking Price Levels Without Exposing Order-Book Internals
Exposing internal `std::deque<std::vector<Fill>>` or raw container structures (`bidsVector_` and `fillsList_`) leaks implementation details, breaks callers whenever internal storage changes, and forces risk queries to allocate gigabytes of temporary vectors just to filter institutional block trades.
When the order book swaps std::vector for a balanced tree, skiplist, or flat_map, every external loop relying on vector[i] indexing or std::list iterators breaks compilation.
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
We keep the order book's internal containers strictly private and expose standard iterator factory methods (bidsBegin(), bidsEnd(), blockTrades(minQty)). PriceLevelIterator and BlockFillIterator encapsulate the current pointer and traversal logic. Callers iterate using clean C++ range-based for loops (for (const auto& level : book.bids())). Internal storage can be swapped freely without affecting any caller.
Across Sprints 1 through 8, we engineered a full trading platform: immutable validated orders (B1), centralized config (B2), pre-trade risk pipelines (B3), auditable commands (B4), multi-exchange families (B5), mediator order matching (B6), cached market data proxies (B7), and standardized execution algorithm skeletons (B8).
We are providing analytics screens, risk monitors, and post-trade auditing tools with safe, performant access to resting order-book depth and millions of historical trade fills stored across internal buckets.
Exposing internal std::deque<std::vector<Fill>> or raw container structures (bidsVector_ and fillsList_) leaks implementation details, breaks callers whenever internal storage changes, and forces risk queries to allocate gigabytes of temporary vectors just to filter institutional block trades.
With the Iterator pattern (PriceLevelIterator, BlockFillIterator, and C++20 <ranges> / C++23 std::generator pipelines), external callers traverse depth levels and stream-filtered block trades using zero-copy range-based for loops without mutating internal storage or allocating temporary buffers β completing our production-grade Trading & Matching Engine architecture.
The product ticket
Trading analytics screens, risk monitors, and algorithmic execution bots need to inspect the OrderBook in two ways: scanning the top-N aggregated bid price levels (depth of market) and filtering institutional block trades from the trade fill history.
- β’
Allow clients to traverse price levels in order (best price to worst) without exposing whether the order book uses a vector, map, flat_map, or skiplist.
- β’
Provide a specialized block-trade iterator that yields only institutional fills (
quantity >= minQty) on-the-fly without copying entire history vectors. - β’
Ensure the iterator protects order-book encapsulation so external callers cannot mutate resting price levels or corrupt internal state.
The performance engineering team refactored the order book's internal bid storage from a contiguous std::vector to a cache-optimized skiplist. Because callers across the codebase directly accessed book.bidsVector_[i], dozens of analytics and risk screens failed to compile.
Step 1 β The naive code & why it breaks
OrderBook makes its internal containers public (bidsVector_ and fillsList_). Callers use raw array indexing and container-specific iterator loops.
#include <list>
#include <print>
#include <string>
#include <vector>
struct PriceLevel {
double priceRupees;
int quantity;
int orderCount;
};
struct TradeFill {
std::string tradeId;
std::string symbol;
double priceRupees;
int quantity;
};
// Naive: OrderBook exposes its internal containers directly to external callers.
class NaiveOrderBook {
public:
// Leaks internal container types and storage representation!
std::vector<PriceLevel> bidsVector_;
std::list<TradeFill> fillsList_;
void addBid(double price, int qty, int count) {
bidsVector_.push_back(PriceLevel{price, qty, count});
}
void addFill(std::string id, std::string symbol, double price, int qty) {
fillsList_.push_back(TradeFill{std::move(id), std::move(symbol), price, qty});
}
};
// Caller 1: Direct index traversal coupled to std::vector
void naivePrintTopLevels(const NaiveOrderBook& book, size_t n) {
std::println("Walking top {} bid levels via direct vector indexing:", n);
// If OrderBook switches from std::vector to a skiplist, map, or tree, this breaks!
for (size_t i = 0; i < std::min(n, book.bidsVector_.size()); ++i) {
const auto& level = book.bidsVector_[i]; // Direct container access
std::println(" Level {}: Rs.{:.2f} (Qty: {}, Orders: {})",
i + 1, level.priceRupees, level.quantity, level.orderCount);
}
}
// Caller 2: Coupled to std::list iterator
void naiveFilterBlockTrades(const NaiveOrderBook& book, int minBlockQty) {
std::println("Filtering block trades (qty >= {}) via raw list iterator:", minBlockQty);
for (auto it = book.fillsList_.begin(); it != book.fillsList_.end(); ++it) {
if (it->quantity >= minBlockQty) {
std::println(" Block trade: {} - {} shares @ Rs.{:.2f}",
it->tradeId, it->quantity, it->priceRupees);
}
}
}
int main() {
NaiveOrderBook book;
book.addBid(3500.0, 150, 3);
book.addBid(3499.0, 300, 5);
book.addBid(3498.0, 500, 8);
book.addFill("TRD-1", "TCS", 3500.0, 50);
book.addFill("TRD-2", "TCS", 3500.0, 1000); // Block trade
naivePrintTopLevels(book, 2);
naiveFilterBlockTrades(book, 500);
return 0;
}When the order book swaps std::vector for a balanced tree, skiplist, or flat_map, every external loop relying on vector[i] indexing or std::list iterators breaks compilation.
Exposing mutable public containers lets any rogue caller insert, modify, or erase price levels directly, corrupting price-time priority invariants.
Filtering block trades requires every client to copy-paste the same if (trade.quantity >= minQty) check into their own loops.
Step 2 β The fix: Iterator Pattern
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
We keep the order book's internal containers strictly private and expose standard iterator factory methods (bidsBegin(), bidsEnd(), blockTrades(minQty)). PriceLevelIterator and BlockFillIterator encapsulate the current pointer and traversal logic. Callers iterate using clean C++ range-based for loops (for (const auto& level : book.bids())). Internal storage can be swapped freely without affecting any caller.
OrderBook β holds the private collections of price levels and trade fills, and provides bids() and blockTrades() factory methods.
PriceLevelIterator and BlockFillIterator β encapsulate traversal state, advancing pointers and filtering on-the-fly.
DepthVisualizer and RiskAuditor β consume the elements sequentially through standard range-based for loops.
Unlock Full UML Diagram & C++23 Solution
The interactive UML architecture diagrams, refactored C++23 code, real-world extensions, and interview Q&A are part of Brute2Best Pro. All 7 foundation chapters are 100% free.
First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.