2Brute2Best
πŸ”₯0
B1

Orders With Optional Limit, Stop, Validity & Iceberg Qty

High-frequency interview topic Pro OnlyBuilder Designated initializers (C++20) Deducing this (C++23)
⚑ 60-Second Architecture SummaryB1 · Builder Pattern
Naive Bottleneck→Builder Pattern Cure
The Naive Bottleneck: Swapped argument bug on identical primitive types

A 9-argument telescoping constructor (`TradeOrder("RELIANCE", BUY, LIMIT, 100, 2850.5, 0.0, true, false, 20)`) causes traders to swap `limitPrice` and `stopPrice` or construct contradictory orders (e.g. a `MARKET` order with a limit price).

When adjacent parameters share the same type (int qty and int displayIcebergQty, or double limitPrice and double stopPrice), the compiler cannot detect when a developer inverts their order at the call site.

The Pattern Cure: Builder Pattern

Separate the construction of a complex object from its representation, allowing step-by-step configuration and enforcing cross-field invariants before creating an immutable product.

We hide Order's constructor and expose an OrderBuilder. Mandatory fields (symbol, side, qty) are required upfront in the builder's constructor. Fluent, self-describing methods (withLimitPrice(), withIceberg()) configure optional attributes. Finally, build() validates all cross-field rules (e.g. iceberg <= qty, limitPrice > 0) and returns a std::expected<Order, std::string>. Once built, Order has no setters and remains completely immutable.

Sprint 1 of 9 Β· Constructing Validated TradeOrder Objects
Story B β€” Low-Latency Trading System β€” Progressive System Build
1. Where our codebase left off

Day 1 of building our low-latency Trading Platform (Zerodha / Binance style). Before we can route, risk-check, or match orders, we need a rock-solid TradeOrder domain object representing Market, Limit, Stop-Loss, and Iceberg orders.

2. What we are building now

We are designing the TradeOrder creation API used by trader UIs and algorithmic strategies.

3. The pain point we hit

A 9-argument telescoping constructor (TradeOrder("RELIANCE", BUY, LIMIT, 100, 2850.5, 0.0, true, false, 20)) causes traders to swap limitPrice and stopPrice or construct contradictory orders (e.g. a MARKET order with a limit price).

4. What this unlocks next
Next: B2 β†’

With TradeOrder::Builder enforcing cross-field invariants before creating an immutable TradeOrder, we now have valid orders. Next in Sprint 2 (B2 Β· Singleton), we need a single process-wide ExchangeConfig & MarketClock registry so all trading threads read consistent tick sizes and circuit-breaker limits.

01

The product ticket

Ticket Β· B1TRADE-201 Β· Flexible Order Construction With Invariant Validation

Our trading terminal handles high-speed equity orders. An Order has mandatory core parameters (symbol, side Buy/Sell, total quantity) alongside multiple optional execution parameters: limitPrice (for Limit orders), stopPrice (for Stop-Loss triggers), timeInForce (DAY, IOC, FOK), displayIcebergQty (hidden liquidity where only a slice is visible on the public order book), and a client routing tag.

Requirements
  • β€’

    Allow constructing orders with any valid combination of optional parameters without a combinatorial explosion of constructors.

  • β€’

    Validate domain invariants before the object is created: quantity must be positive, limit prices cannot be negative, and displayIcebergQty must never exceed total quantity.

  • β€’

    Ensure constructed Order objects are strictly immutable so they can be safely passed to concurrent matching engines and risk checks without lock contention.

The next requirement that breaks naive code

A trading desk deployed a hotfix with an 8-parameter constructor Order("RELIANCE", Side::Buy, 100, 2450.0, 0.0, DAY, 1000, "DESK_1"). The developer accidentally swapped total quantity (100) and iceberg quantity (1000), sending an invalid order straight to the exchange. Furthermore, callers can construct invalid half-built orders with limit prices set to zero.

02

Step 1 β€” The naive code & why it breaks

The naive implementation relies on telescoping constructors with default arguments, or a parameter-heavy constructor that forces callers to pass dummy values for unused optional fields.

Naive design (what not to ship) Β· naive.cpp
#include <print>
#include <string>

enum class Side { Buy, Sell };
enum class TimeInForce { DAY, IOC, FOK };

// Naive Order with an 8-parameter telescoping constructor.
// Multiple parameters of the same type lead to catastrophic swapped-argument bugs.
class NaiveOrder {
public:
    std::string symbol;
    Side side;
    int qty;
    double limitPrice;
    double stopPrice;
    TimeInForce tif;
    int displayIcebergQty;
    std::string clientTag;

    NaiveOrder(std::string sym, Side s, int q, double lPrice = 0.0,
               double sPrice = 0.0, TimeInForce t = TimeInForce::DAY,
               int iceberg = 0, std::string tag = "")
        : symbol(std::move(sym)), side(s), qty(q), limitPrice(lPrice),
          stopPrice(sPrice), tif(t), displayIcebergQty(iceberg), clientTag(std::move(tag)) {}
};

int main() {
    // BUG 1: Trader meant qty=1000, iceberg=100, but swapped int parameters at call site!
    // The compiler silently accepts this, causing an unwanted 100-share iceberg with 1000 visible!
    NaiveOrder badOrder1("RELIANCE", Side::Buy, 100, 2450.0, 0.0, TimeInForce::DAY, 1000, "DESK_1");
    std::println("Bad Order 1: qty={}, iceberg={}", badOrder1.qty, badOrder1.displayIcebergQty);

    // BUG 2: Limit order intended, but limitPrice omitted and defaulted to 0.0!
    // No validation ensures limitPrice > 0 for non-market orders.
    NaiveOrder badOrder2("INFY", Side::Sell, 50);
    std::println("Bad Order 2: symbol={}, limitPrice={}", badOrder2.symbol, badOrder2.limitPrice);
}
Swapped argument bug on identical primitive types

When adjacent parameters share the same type (int qty and int displayIcebergQty, or double limitPrice and double stopPrice), the compiler cannot detect when a developer inverts their order at the call site.

Invalid half-built orders bypass validation

Using mutable setters (setLimitPrice, setQty) leaves the object in a partially configured, invalid state until all setters are invoked, exposing incomplete state to other threads.

Combinatorial constructor explosion

Supporting combinations of Limit, Stop, Iceberg, and IOC requires dozens of overloaded constructor signatures that become impossible to maintain.

03

Step 2 β€” The fix: Builder Pattern

Core idea in one sentence

Separate the construction of a complex object from its representation, allowing step-by-step configuration and enforcing cross-field invariants before creating an immutable product.

We hide Order's constructor and expose an OrderBuilder. Mandatory fields (symbol, side, qty) are required upfront in the builder's constructor. Fluent, self-describing methods (withLimitPrice(), withIceberg()) configure optional attributes. Finally, build() validates all cross-field rules (e.g. iceberg <= qty, limitPrice > 0) and returns a std::expected<Order, std::string>. Once built, Order has no setters and remains completely immutable.

Pattern roles in our story
Product

Order β€” the immutable object containing validated trading fields, with private constructors and public const accessors.

Builder

OrderBuilder β€” collects parameters fluently, checks business invariants in build(), and constructs the Order.

Client

Trading strategies and desk algorithms that configure orders with readable method chaining.

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