TWAP & VWAP Execution Algos Sharing a Skeleton
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.
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.
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.
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.
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.
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.
The product ticket
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.
- β’
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.
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.
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.
#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;
}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.
Without a centralized framework skeleton, an algorithm implementation could accidentally dispatch orders before validating limits or omit writing trade records to the compliance ledger.
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.
Step 2 β The fix: 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.
ExecutionAlgo β defines the non-virtual template method execute() and declares the protected pure virtual hook computeSlices().
TwapAlgo β implements computeSlices() by partitioning total quantity evenly across time intervals.
VwapAlgo β implements computeSlices() by scaling quantity according to historical intraday volume percentages.
The trading desk controller, which holds an ExecutionAlgo pointer and invokes execute(order).
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.