Place, Modify & Cancel With Audit Log and Undo
Invoking direct methods (`orderBook.place()`, `orderBook.modify()`, `orderBook.cancel()`) leaves zero audit history and discards previous state. When a runaway algorithmic loop fired 50 rogue orders during options expiry, the desk had no regulatory audit trail of what occurred and no ability to rollback or undo the batch in reverse order.
Direct method calls leave no persistent records. Once modify(101, 500) finishes, the previous quantity (100) and the command parameters are lost to memory.
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
We define an abstract OrderCommand interface with execute(), undo(), and describe(). Concrete command objects (PlaceOrderCommand, ModifyQtyCommand, CancelOrderCommand) store their receiver (OrderBook&), the arguments, and the previous state needed for rollback (e.g. oldQty_ or savedOrder_). A central TradingDeskInvoker executes commands, logs them to an audit trail, and pushes them onto a history stack for LIFO rollback.
In Sprint 3 (B3 Β· Chain of Responsibility), we built the sequential pre-trade risk pipeline that verifies KYC, margin, and circuit limits for every incoming order. Orders that clear risk must now be dispatched to the order book for execution.
We are implementing the execution layer where trading desks invoke Place, Modify, and Cancel actions against the OrderBook.
Invoking direct methods (orderBook.place(), orderBook.modify(), orderBook.cancel()) leaves zero audit history and discards previous state. When a runaway algorithmic loop fired 50 rogue orders during options expiry, the desk had no regulatory audit trail of what occurred and no ability to rollback or undo the batch in reverse order.
By encapsulating each action into an OrderCommand executed by a TradingDeskInvoker, every mutation is logged for regulatory compliance and pushed onto a history stack for instant batch rollback. Next in Sprint 5 (B5 Β· Abstract Factory), our desk expands connectivity from NSE Equities to BSE Derivatives and Crypto Spot, requiring matching families of OrderRouter + FeeCalculator + SymbolValidator per exchange venue.
The product ticket
A broker's algorithmic trading desk executes hundreds of actions per second: Place Order, Modify Quantity, and Cancel Order. Financial regulators mandate a comprehensive audit trail recording every user action. Furthermore, when algorithmic risk tripwires fire (e.g. market crash, runaway loop, or sudden margin breach), the desk must be able to roll back recent actions in reverse order.
- β’
Encapsulate each distinct market action as a self-contained command object.
- β’
Maintain a linear audit log capturing the details, intent, and status of every executed action.
- β’
Implement a reliable rollback/undo mechanism (
undoLast(),rollbackBatch(N)) that reverses placed, modified, and cancelled orders.
During an options expiry session, an algorithmic loop malfunctioned and placed 50 accidental limit orders before modifying prices on existing resting orders. In the naive codebase, operations were invoked as direct method calls on OrderBook. The desk had no audit history to inspect what the runaway algo had done, and no automated rollback capability, resulting in severe financial loss.
Step 1 β The naive code & why it breaks
The naive implementation calls methods directly on OrderBook (place(), modify(), cancel()) from UI event handlers and algorithmic loops without creating command objects or capturing state history.
#include <optional>
#include <print>
#include <string>
#include <unordered_map>
struct BookOrder {
int id;
std::string symbol;
int qty;
double price;
};
// Naive OrderBook: actions are executed as raw, transient method calls.
class NaiveOrderBook {
private:
std::unordered_map<int, BookOrder> orders_;
public:
void place(int id, std::string symbol, int qty, double price) {
orders_[id] = {id, std::move(symbol), qty, price};
}
void modify(int id, int newQty) {
if (auto it = orders_.find(id); it != orders_.end()) {
it->second.qty = newQty;
}
}
void cancel(int id) {
orders_.erase(id);
}
[[nodiscard]] size_t count() const { return orders_.size(); }
[[nodiscard]] const BookOrder* get(int id) const {
auto it = orders_.find(id);
return (it != orders_.end()) ? &it->second : nullptr;
}
};
int main() {
NaiveOrderBook book;
// Direct calls: no command objects exist to track intent, timestamp, or rollback state.
book.place(101, "TCS", 100, 3850.0);
book.modify(101, 500); // Modified quantity
book.cancel(101); // Cancelled order
// PROBLEM: Risk alert trips! A fat-finger error modified order 101 incorrectly.
// There is no record of the prior quantity (100) and no way to roll back!
std::println("Orders in book: {}", book.count());
std::println("No audit trail or undo available in naive implementation.");
}Direct method calls leave no persistent records. Once modify(101, 500) finishes, the previous quantity (100) and the command parameters are lost to memory.
Rolling back a cancellation requires knowing the complete snapshot of the deleted order; rolling back a quantity modification requires remembering the prior quantity. Direct calls discard this state.
Every controller, algo engine, and testing script must directly manage references to OrderBook internals, making queuing, delayed execution, and macro scripting impossible.
Step 2 β The fix: Command Pattern
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
We define an abstract OrderCommand interface with execute(), undo(), and describe(). Concrete command objects (PlaceOrderCommand, ModifyQtyCommand, CancelOrderCommand) store their receiver (OrderBook&), the arguments, and the previous state needed for rollback (e.g. oldQty_ or savedOrder_). A central TradingDeskInvoker executes commands, logs them to an audit trail, and pushes them onto a history stack for LIFO rollback.
OrderCommand β declares execute(), undo(), and describe().
PlaceOrderCommand, ModifyQtyCommand, CancelOrderCommand β store receiver references and state snapshots to execute and invert actions.
OrderBook β manages the underlying order map and performs actual additions, updates, and deletions.
TradingDeskInvoker β executes commands, maintains the history stack, logs audit messages, and coordinates batch rollbacks.
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.