Caching & Permission-Gating Paid Market Data
Every terminal screen and trading bot connects directly to `RemoteMarketDataFeed`. Rapid quote polling racks up astronomical βΉ0.50-per-query vendor fees and adds 50β80 ms of network latency per call. Furthermore, ad-hoc `if (tier == "PRO")` checks copy-pasted across callers leak premium Level-2 depth to unauthorized basic accounts.
Every single quote refresh across dozens of open widgets fires a direct HTTP request to the remote exchange feed, wasting money and adding 50ms latency per click.
Provide a surrogate or placeholder for another object to control access to it, presenting the exact same interface to clients.
We create MarketDataProxy, which implements the exact same MarketDataFeed interface as RemoteMarketDataFeed. The proxy holds a std::unique_ptr<MarketDataFeed> pointing to the real feed. When getQuote() is called, the proxy checks its internal timestamped cache: if fresh, it returns immediately; if expired, it fetches from the real feed and updates cache. When getLevel2Depth() is called, the proxy validates userTier_ == UserTier::Pro before delegating. Downstream code depends only on the abstract MarketDataFeed interface.
In Sprint 6 (B6 Β· Mediator), we centralized order matching into an OrderBookMediator maintaining Price-Time Priority across all trading desks. To make informed trading decisions, these desks and trader terminals require real-time market data quotes and Level-2 order-book depth.
We are integrating MarketDataFeed, which supplies live ticker quotes (getQuote) and Level-2 market depth (getLevel2Depth) to UI widgets and execution algorithms.
Every terminal screen and trading bot connects directly to RemoteMarketDataFeed. Rapid quote polling racks up astronomical βΉ0.50-per-query vendor fees and adds 50β80 ms of network latency per call. Furthermore, ad-hoc if (tier == "PRO") checks copy-pasted across callers leak premium Level-2 depth to unauthorized basic accounts.
By inserting a Proxy (MarketDataProxy) implementing the exact same MarketDataFeed interface, we achieve transparent TTL quote caching (Caching Proxy) and enforce subscription authorization (Protection Proxy) with zero client code modifications. Next in Sprint 8 (B8 Β· Template Method), our institutional desk needs to execute large parent orders via TWAP and VWAP algorithms, but duplicated execution workflows risk skipping critical pre-trade compliance checks.
The product ticket
Our trading terminal consumes live market quotes and Level-2 order-book depth from an external partner (RemoteMarketDataFeed). Every query incurs network latency (50β80 ms) and costs βΉ0.50 per request in vendor API fees.
- β’
Provide real-time ticker quotes (
getQuote) and Level-2 market depth (getLevel2Depth) through a uniformMarketDataFeedinterface. - β’
Implement a Caching Proxy with a configurable Time-To-Live (TTL) so repetitive quote requests return instant in-memory responses without hitting the paid remote gateway.
- β’
Implement a Protection Proxy that gates Level-2 order-book depth: only users with a
Prosubscription may view depth, whileBasicusers receive an access exception.
Downstream consumers (the web trading UI, quantitative execution algos, backtesting tools) must not know whether they are communicating with the real network feed, an in-memory cache, or an access-control validator. The interface contract must be identical.
Step 1 β The naive code & why it breaks
Client desks instantiate RemoteMarketDataFeed directly. In every calling method, developers write ad-hoc permission checks (if (tier == "PRO")) and make duplicate network calls for identical quotes.
#include <print>
#include <string>
#include <vector>
struct Quote {
std::string symbol;
double bid;
double ask;
};
struct MarketDepth {
std::string symbol;
std::vector<double> bidPrices;
std::vector<double> askPrices;
};
// Naive: Direct remote feed without caching or access protection.
class RemoteMarketDataFeed {
int paidApiCalls_{0};
public:
Quote fetchQuote(const std::string& symbol) {
++paidApiCalls_;
// Simulates expensive network latency and βΉ0.50 vendor charge per call
std::println("[REMOTE FEED] Fetching paid quote for {} (Total API calls: {}, Incurred fee: Rs.{:.2f})",
symbol, paidApiCalls_, paidApiCalls_ * 0.50);
return Quote{symbol, 2450.0, 2451.5};
}
MarketDepth fetchLevel2Depth(const std::string& symbol) {
++paidApiCalls_;
std::println("[REMOTE FEED] Fetching Level-2 depth for {} (Total API calls: {})",
symbol, paidApiCalls_);
return MarketDepth{symbol, {2450.0, 2449.5, 2449.0}, {2451.5, 2452.0, 2452.5}};
}
int totalCalls() const noexcept { return paidApiCalls_; }
};
// Callers directly call the remote feed, duplicating access checks and failing to cache
class NaiveClientDesk {
RemoteMarketDataFeed& feed_;
std::string userTier_; // "BASIC" or "PRO"
public:
NaiveClientDesk(RemoteMarketDataFeed& feed, std::string tier)
: feed_(feed), userTier_(std::move(tier)) {}
void renderScreen(const std::string& symbol) {
// Repeated calls hit the network every single time!
auto q1 = feed_.fetchQuote(symbol);
auto q2 = feed_.fetchQuote(symbol); // Duplicate network call
// Ad-hoc permission check scattered in caller code: easy to forget or bypass!
if (userTier_ == "PRO") {
auto depth = feed_.fetchLevel2Depth(symbol);
std::println("Rendered Level-2 depth with {} bid levels.", depth.bidPrices.size());
} else {
std::println("Level-2 depth skipped for basic user.");
}
}
};
int main() {
RemoteMarketDataFeed feed;
NaiveClientDesk desk(feed, "BASIC");
desk.renderScreen("RELIANCE");
desk.renderScreen("RELIANCE");
return 0;
}Every single quote refresh across dozens of open widgets fires a direct HTTP request to the remote exchange feed, wasting money and adding 50ms latency per click.
Permission checks are copy-pasted across terminal screens and API endpoints. If an engineer misses an if (tier == "PRO") check in a new widget, non-paying users leak expensive Level-2 market depth.
Callers directly construct and depend on RemoteMarketDataFeed. Unit testing trading algorithms offline without an active, billable vendor network connection becomes impossible.
Step 2 β The fix: Proxy Pattern
Provide a surrogate or placeholder for another object to control access to it, presenting the exact same interface to clients.
We create MarketDataProxy, which implements the exact same MarketDataFeed interface as RemoteMarketDataFeed. The proxy holds a std::unique_ptr<MarketDataFeed> pointing to the real feed. When getQuote() is called, the proxy checks its internal timestamped cache: if fresh, it returns immediately; if expired, it fetches from the real feed and updates cache. When getLevel2Depth() is called, the proxy validates userTier_ == UserTier::Pro before delegating. Downstream code depends only on the abstract MarketDataFeed interface.
MarketDataFeed β declares virtual Quote getQuote(...) = 0 and virtual MarketDepth getLevel2Depth(...) = 0.
RemoteMarketDataFeed β connects to the external exchange partner, incurring latency and per-call vendor fees.
MarketDataProxy β wraps the real feed, manages TTL-based quote caching (Caching Proxy), and validates subscription tiers (Protection Proxy).
TradingTerminal β interacts strictly with MarketDataFeed& without knowing whether caching or access control is active.
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.