Matching Buyers and Sellers Through an Order Book
Desks maintain direct pointers to peer desks, iterating through counterparties to discover liquidity and negotiate trades. This creates an $O(N^2)$ mesh of pointers, introduces re-entrancy deadlocks, and destroys centralized Price-Time Priority (FIFO) because a desk queries peers sequentially rather than matching the best resting price on the book.
Adding or removing a single desk requires updating pointer registries across all other N - 1 desk objects, creating an unmaintainable web of dependencies.
Define an object that encapsulates how a set of objects interact, promoting loose coupling by keeping objects from referring to each other explicitly.
Instead of trading desks talking directly to one another, all desks communicate exclusively with an OrderBookMediator. Desks submit orders to the mediator, and the mediator manages resting bids/asks according to price-time priority. When a trade matches, the mediator dispatches onFill events to the respective buyer and seller desks. Coupling collapses from O(N^2) to O(N).
In Sprint 5 (B5 Β· Abstract Factory), we ensured each exchange session is instantiated with compatible routing, fee, and validation components. Orders now arrive at our matching engine from diverse trading desks (Retail, Institutional, Prop, Market Maker).
We are coordinating order matching and fill notifications across multiple concurrent trading desks operating on the same liquidity pool.
Desks maintain direct pointers to peer desks, iterating through counterparties to discover liquidity and negotiate trades. This creates an O(N^2) mesh of pointers, introduces re-entrancy deadlocks, and destroys centralized Price-Time Priority (FIFO) because a desk queries peers sequentially rather than matching the best resting price on the book.
Using the Mediator pattern, OrderBook acts as a central hub (OrderBookMediator) that decouples all trading desks into an O(N) star topology, enforcing strict Price-Time matching and dispatching onFill callbacks. Next in Sprint 7 (B7 Β· Proxy), thousands of UI terminal widgets and algorithmic trading bots polling live market quotes overwhelm our expensive upstream exchange feed.
The product ticket
Our institutional trading platform connects multiple independent trading desks (RetailDesk, InstitutionalDesk, PropDesk) buying and selling equities. Counterparties need to discover orders, match crossing trades, and receive instant fill confirmations.
- β’
Let multiple buyer and seller trading desks submit limit orders and receive automated fill execution callbacks (
onFill). - β’
Enforce strict Price-Time Priority (FIFO): highest bid matches lowest ask, and older resting orders at the same price level fill first.
- β’
Support partial fills without desks needing to hold direct references or negotiate peer-to-peer.
New desk types (ArbitrageDesk, MarketMakerDesk) are dynamically connecting and disconnecting at runtime. With N desks, maintaining direct counterparty connections creates an O(N^2) mesh of pointers, causing deadlock risks and violating regulatory best-execution transparency.
Step 1 β The naive code & why it breaks
Every trading desk maintains a direct list of pointers to every other desk (counterparties_). When placing a buy order, the desk iterates through peers, directly interrogating their inventory.
#include <print>
#include <string>
#include <vector>
// Naive design: Trading desks maintain direct references to every other desk.
// For N desks, this creates N * (N - 1) / 2 connections (O(N^2) mesh).
class NaiveTradingDesk {
std::string deskId_;
std::vector<NaiveTradingDesk*> counterparties_;
public:
explicit NaiveTradingDesk(std::string id) : deskId_(std::move(id)) {}
void addCounterparty(NaiveTradingDesk* other) {
counterparties_.push_back(other);
}
const std::string& id() const { return deskId_; }
// Direct peer-to-peer negotiation: Desk A interrogates Desk B directly.
bool directMatch(const std::string& symbol, double price, int qty, const std::string& buyerId) {
// Hardcoded matching check: Desk answers for itself without any centralized price discovery
if (symbol == "TCS" && price >= 3500.0 && qty <= 100) {
std::println("[DIRECT MATCH] Seller {} traded {} shares with Buyer {} at Rs.{:.2f}",
deskId_, qty, buyerId, price);
return true;
}
return false;
}
void submitBuyOrder(const std::string& symbol, double price, int qty) {
std::println("{} searching {} counterparties for best price...", deskId_, counterparties_.size());
for (auto* peer : counterparties_) {
if (peer->directMatch(symbol, price, qty, deskId_)) {
return; // Matched first peer found, ignoring price-time priority of other desks!
}
}
std::println("{} found no matching counterparty.", deskId_);
}
};
int main() {
NaiveTradingDesk deskA("Desk-Alpha");
NaiveTradingDesk deskB("Desk-Bravo");
NaiveTradingDesk deskC("Desk-Charlie");
// Tangled N^2 mesh setup: Every desk must know every other desk
deskA.addCounterparty(&deskB);
deskA.addCounterparty(&deskC);
deskB.addCounterparty(&deskA);
deskB.addCounterparty(&deskC);
deskC.addCounterparty(&deskA);
deskC.addCounterparty(&deskB);
deskA.submitBuyOrder("TCS", 3550.0, 50);
}Adding or removing a single desk requires updating pointer registries across all other N - 1 desk objects, creating an unmaintainable web of dependencies.
A desk queries peers sequentially in loop order; Desk A might match Desk C at Rs.3550 simply because Desk C appeared earlier in its peer vector, ignoring a resting ask of Rs.3500 on Desk B.
Desk A calls Desk B's negotiation method; Desk B updates its state and calls back into Desk A, causing stack overflow, double execution, or mutex deadlock.
Step 2 β The fix: Mediator Pattern
Define an object that encapsulates how a set of objects interact, promoting loose coupling by keeping objects from referring to each other explicitly.
Instead of trading desks talking directly to one another, all desks communicate exclusively with an OrderBookMediator. Desks submit orders to the mediator, and the mediator manages resting bids/asks according to price-time priority. When a trade matches, the mediator dispatches onFill events to the respective buyer and seller desks. Coupling collapses from O(N^2) to O(N).
OrderBookMediator β defines the contract (registerDesk, placeOrder) for registering colleagues and accepting incoming orders.
OrderBook β manages resting order queues, executes price-time matching, and notifies matched desks via onFill.
TradingDesk β stores a pointer to OrderBookMediator; sends orders to the mediator and receives fill callbacks.
InstitutionalDesk, RetailDesk β execute trades without any knowledge of who took the opposite side.
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.