The previous article described the application’s architecture. This article focuses on the execution model: how data flows through the engine and how the different components interact during a backtest.
There are several ways to implement a backtesting engine. _Retroedge_uses an event-driven model because it closely mirrors the behaviour of a live trading system. Rather than processing an entire dataset in a single pass, market data arrives one event at a time, allowing strategies, portfolio management, and execution to react in a way that resembles live trading.
The engine revolves around four event types:
MarketEvent– new market data becomes availableSignalEvent– a strategy decides to buy or sellOrderEvent– an order is submitted for executionFillEvent– an order has been executed
With the above in mind, at a high level, the engine reduces to the following control loop:
loop {
data_handler.next_bar();
while let Some(event) = queue.pop() {
match event {
MarketEvent => strategy.on_market(),
SignalEvent => portfolio.on_signal(),
OrderEvent => broker.execute(),
FillEvent => portfolio.on_fill(),
}
}
}
Event lifecycle#
%%{init: {"flowchart": {"useMaxWidth": true}, "themeVariables": {"fontSize": "64px"}}}%%
flowchart LR
M[Market Event]
S[Strategy]
RM[Risk Manager]
SE[Signal Event]
ORE[Order Request Event]
PM[Portfolio Manager]
OE[Order Event]
B[Broker]
FE[Fill Event]
M --> S
S --> SE
SE --> RM
RM --> ORE
ORE --> PM
PM --> OE
OE --> B
B --> FE
FE --> PM
Each component has a single responsibility and communicates exclusively through events. This keeps the engine loosely coupled: strategies can be replaced without modifying the broker, execution models can change without affecting the portfolio, and new event types can be introduced with minimal impact elsewhere.
Vectorised vs event-driven#
Vectorised backtests are often simpler to implement and can be considerably faster for certain classes of strategy. The trade-off is that they process the entire dataset at once, whereas an event-driven engine processes information incrementally. Although this introduces additional complexity, it more closely reflects how a live trading system receives and reacts to market data.
This event loop forms the backbone of the entire engine. Every subsystem participates by consuming events, performing a small piece of work, and emitting new events when appropriate.
The next article examines these individual components in greater detail and discusses how they evolve when moving from historical simulation to live trading.
Feel free to share the article on socials: