2Brute2Best
πŸ”₯0
B9

Walking Price Levels Without Exposing Order-Book Internals

Pro OnlyIterator std::ranges::views (C++20) std::generator (C++23)
⚑ 60-Second Architecture SummaryB9 · Iterator Pattern
Naive Bottleneck→Iterator Pattern Cure
The Naive Bottleneck: Internal data structure changes break callers across the codebase

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.

The Pattern Cure: 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.

Sprint 9 of 9 Β· Zero-Copy Streaming Over Trade History & Order Book Levels
Story B β€” Low-Latency Trading System β€” Progressive System Build
1. Where our codebase left off
← B8

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).

2. What we are building now

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.

3. The pain point we hit

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.

4. What this unlocks next

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.

01

The product ticket

Ticket Β· B9TRADE-209 Β· Walk Order-Book Levels and Fills via an Iterator

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.

Requirements
  • β€’

    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 next requirement that breaks naive code

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.

02

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.

Naive design (what not to ship) Β· naive.cpp
#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;
}
Internal data structure changes break callers across the codebase

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.

Encapsulation leak and state corruption

Exposing mutable public containers lets any rogue caller insert, modify, or erase price levels directly, corrupting price-time priority invariants.

Duplicated traversal and filtering logic

Filtering block trades requires every client to copy-paste the same if (trade.quantity >= minQty) check into their own loops.

03

Step 2 β€” The fix: Iterator Pattern

Core idea in one sentence

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.

Pattern roles in our story
Aggregate

OrderBook β€” holds the private collections of price levels and trade fills, and provides bids() and blockTrades() factory methods.

Iterator

PriceLevelIterator and BlockFillIterator β€” encapsulate traversal state, advancing pointers and filtering on-the-fly.

Client

DepthVisualizer and RiskAuditor β€” consume the elements sequentially through standard range-based for loops.

Brute2Best Pro Content

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.

All 440 visual dry runs & algorithms
Interactive step-by-step playback with pointer & array state transitions
52 comprehensive LLD chapters
Interactive UML diagrams, real-world stories (Food Delivery, Trading, RPG), and practice solutions
40 C++ basics labs & hardware memory models
Vtables, byte layouts, assembly insights, and multi-threading race conditions
Production-ready C++23 source code
Modern idioms, clean contracts, time/space complexity derivations, and interview takeaways

First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.

Systems Integration

Related C++ Systems & Algorithmic Foundations

Deep dive into underlying layers