2Brute2Best
πŸ”₯0
B8

TWAP & VWAP Execution Algos Sharing a Skeleton

Pro OnlyTemplate Method Deducing this (C++23) vs CRTP
⚑ 60-Second Architecture SummaryB8 · Template Method
Naive Bottleneck→Template Method Cure
The Naive Bottleneck: Compliance divergence and audit loopholes

Developers copy-pasted the 4-step execution lifecycle (`validateParentOrder β†’ computeSlices β†’ dispatchSlices β†’ recordExecutionSummary`) into separate classes. When risk compliance added a price-band check to validation, it was updated in TWAP but forgotten in VWAP, allowing unchecked institutional orders to violate exchange limits.

Because execution steps are duplicated across algorithm classes, a risk fix applied to TWAP is easily omitted in VWAP, allowing unvetted institutional orders to slip through.

The Pattern Cure: Template Method

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses, letting subclasses redefine certain steps without changing the algorithm's structure.

We define ExecutionAlgo with a public, non-virtual execute(ParentOrder) method (the Non-Virtual Interface / NVI idiom). This method coordinates the invariant 4-step sequence. Step 2 computeSlices(ParentOrder) is declared as a protected virtual std::vector<int> = 0 primitive hook. TwapAlgo and VwapAlgo inherit from ExecutionAlgo and override only computeSlices. They cannot bypass validation or reorder execution.

Sprint 8 of 9 Β· Standardizing the Execution & Settlement Pipeline
Story B β€” Low-Latency Trading System β€” Progressive System Build
1. Where our codebase left off
← B7

In Sprint 7 (B7 Β· Proxy), we deployed MarketDataProxy to cache quotes and protect Level-2 depth behind a clean interface. With market data streaming smoothly, institutional clients now need to execute large parent block orders (e.g. 10,000 shares) without moving the market price.

2. What we are building now

We are building quantitative execution algorithms: TWAP (Time-Weighted Average Price) which slices orders evenly across time intervals, and VWAP (Volume-Weighted Average Price) which weights slices by intraday volume profiles, before routing child slices to exchange settlement.

3. The pain point we hit

Developers copy-pasted the 4-step execution lifecycle (validateParentOrder β†’ computeSlices β†’ dispatchSlices β†’ recordExecutionSummary) into separate classes. When risk compliance added a price-band check to validation, it was updated in TWAP but forgotten in VWAP, allowing unchecked institutional orders to violate exchange limits.

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

With Template Method (ExecutionAlgo::execute()), the base class locks the invariant 4-step execution sequence using the Non-Virtual Interface (NVI) idiom, deferring only the slicing formula hook (computeSlices()) to subclasses. Next in Sprint 9 (B9 Β· Iterator), after executing millions of child order slices and fills across segmented order-book buckets, risk analysts and analytics visualizers need to stream and filter historical fills without copying gigabytes of internal vectors.

01

The product ticket

Ticket Β· B8TRADE-208 Β· Standardize TWAP and VWAP Execution Skeletons

Institutional trading desks execute large parent orders (e.g., 10,000 shares of RELIANCE) using quantitative execution algorithms: TWAP (Time-Weighted Average Price) divides quantity evenly across time intervals, while VWAP (Volume-Weighted Average Price) weights slices according to historical intraday market volumes.

Requirements
  • β€’

    Both algorithms must strictly adhere to the exact 4-step execution lifecycle: validateParentOrder β†’ computeSlices β†’ dispatchSlices β†’ recordExecutionSummary.

  • β€’

    Enforce pre-trade compliance checks (positive quantity, price limits) so no algorithm can ever dispatch child slices with invalid parameters.

  • β€’

    Allow quant developers to create new execution strategies (such as Iceberg or Arrival Price) by implementing only their mathematical slicing formula without touching order routing or audit logging.

The next requirement that breaks naive code

In the naive codebase, developers copy-pasted the 4-step lifecycle across separate files. When compliance added a pre-trade circuit band check to Step 1, someone updated TWAP but forgot VWAP, causing an unchecked VWAP order to violate exchange risk limits.

02

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

NaiveTwapAlgo and NaiveVwapAlgo each implement their own execute() method, copy-pasting order validation, child-order dispatch loops, and audit summary generation.

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

struct ParentOrder {
    std::string orderId;
    std::string symbol;
    int totalQty;
    double limitPrice;
};

struct ExecutionSummary {
    std::string orderId;
    int totalSlices;
    int filledQty;
    double avgPrice;
    bool success;
};

// Naive: TWAP algo copy-pastes the entire 4-step execution lifecycle
class NaiveTwapAlgo {
    int intervals_{4};

public:
    explicit NaiveTwapAlgo(int intervals) : intervals_(intervals) {}

    ExecutionSummary execute(const ParentOrder& order) {
        // Step 1: Pre-trade validation (duplicated!)
        if (order.totalQty <= 0 || order.limitPrice <= 0.0) {
            std::println("[TWAP ERROR] Invalid order parameters for {}", order.orderId);
            return ExecutionSummary{order.orderId, 0, 0, 0.0, false};
        }

        // Step 2: Slice calculation (TWAP-specific: equal slices)
        std::vector<int> slices;
        int baseQty = order.totalQty / intervals_;
        int remainder = order.totalQty % intervals_;
        for (int i = 0; i < intervals_; ++i) {
            slices.push_back(baseQty + (i == intervals_ - 1 ? remainder : 0));
        }

        // Step 3: Dispatch child orders to exchange (duplicated!)
        int filled = 0;
        double totalCost = 0.0;
        for (size_t i = 0; i < slices.size(); ++i) {
            filled += slices[i];
            totalCost += slices[i] * order.limitPrice;
            std::println("[TWAP DISPATCH] Slice {}/{} dispatched: {} shares @ Rs.{:.2f}",
                         i + 1, slices.size(), slices[i], order.limitPrice);
        }

        // Step 4: Audit logging and summary report (duplicated!)
        double avg = filled > 0 ? (totalCost / filled) : 0.0;
        std::println("[TWAP AUDIT] Order {} completed: {} shares filled, avg price Rs.{:.2f}",
                     order.orderId, filled, avg);
        return ExecutionSummary{order.orderId, static_cast<int>(slices.size()), filled, avg, true};
    }
};

// Naive: VWAP algo duplicates the exact same validation, dispatch, and reporting steps
class NaiveVwapAlgo {
    std::vector<double> volumeWeights_{0.4, 0.2, 0.4};

public:
    ExecutionSummary execute(const ParentOrder& order) {
        // Step 1: Pre-trade validation (duplicated!)
        if (order.totalQty <= 0 || order.limitPrice <= 0.0) {
            std::println("[VWAP ERROR] Invalid order parameters for {}", order.orderId);
            return ExecutionSummary{order.orderId, 0, 0, 0.0, false};
        }

        // Step 2: Slice calculation (VWAP-specific: volume-weighted)
        std::vector<int> slices;
        int allocated = 0;
        for (size_t i = 0; i < volumeWeights_.size(); ++i) {
            if (i == volumeWeights_.size() - 1) {
                slices.push_back(order.totalQty - allocated);
            } else {
                int qty = static_cast<int>(order.totalQty * volumeWeights_[i]);
                slices.push_back(qty);
                allocated += qty;
            }
        }

        // Step 3: Dispatch child orders to exchange (duplicated!)
        int filled = 0;
        double totalCost = 0.0;
        for (size_t i = 0; i < slices.size(); ++i) {
            filled += slices[i];
            totalCost += slices[i] * order.limitPrice;
            std::println("[VWAP DISPATCH] Slice {}/{} dispatched: {} shares @ Rs.{:.2f}",
                         i + 1, slices.size(), slices[i], order.limitPrice);
        }

        // Step 4: Audit logging and summary report (duplicated!)
        double avg = filled > 0 ? (totalCost / filled) : 0.0;
        std::println("[VWAP AUDIT] Order {} completed: {} shares filled, avg price Rs.{:.2f}",
                     order.orderId, filled, avg);
        return ExecutionSummary{order.orderId, static_cast<int>(slices.size()), filled, avg, true};
    }
};

int main() {
    ParentOrder order{"PARENT-101", "RELIANCE", 1000, 2900.0};

    NaiveTwapAlgo twap(4);
    twap.execute(order);

    NaiveVwapAlgo vwap;
    vwap.execute(order);

    return 0;
}
Compliance divergence and audit loopholes

Because execution steps are duplicated across algorithm classes, a risk fix applied to TWAP is easily omitted in VWAP, allowing unvetted institutional orders to slip through.

Subclasses can reorder or bypass critical steps

Without a centralized framework skeleton, an algorithm implementation could accidentally dispatch orders before validating limits or omit writing trade records to the compliance ledger.

Rampant copy-paste boilerplate

Every new algorithm (Iceberg, Percent-of-Volume) must duplicate 40 lines of child-order routing and price calculation logic just to alter 5 lines of slicing math.

03

Step 2 β€” The fix: Template Method

Core idea in one sentence

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses, letting subclasses redefine certain steps without changing the algorithm's structure.

We define ExecutionAlgo with a public, non-virtual execute(ParentOrder) method (the Non-Virtual Interface / NVI idiom). This method coordinates the invariant 4-step sequence. Step 2 computeSlices(ParentOrder) is declared as a protected virtual std::vector<int> = 0 primitive hook. TwapAlgo and VwapAlgo inherit from ExecutionAlgo and override only computeSlices. They cannot bypass validation or reorder execution.

Pattern roles in our story
AbstractClass

ExecutionAlgo β€” defines the non-virtual template method execute() and declares the protected pure virtual hook computeSlices().

ConcreteClass (TWAP)

TwapAlgo β€” implements computeSlices() by partitioning total quantity evenly across time intervals.

ConcreteClass (VWAP)

VwapAlgo β€” implements computeSlices() by scaling quantity according to historical intraday volume percentages.

Client

The trading desk controller, which holds an ExecutionAlgo pointer and invokes execute(order).

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