Architecture
The central abstraction: Simulatable
Everything starts from a single trait in market_model:
#![allow(unused)] fn main() { pub trait Simulatable: Clone + Send + Sync { type Output: Clone + Send + Sync; fn dim(&self) -> usize; fn step(&mut self, dt: f64, dw: &[f64], rng: &mut impl Rng) -> Self::Output; fn current(&self) -> Self::Output; } }
dim() tells the runner how many Brownian drivers are needed. step()
advances the model by one time step. current() snapshots the current state.
This trait is the only thing SimulationRunner knows about. All complexity
above it -- stochastic volatility, jump components, order flow, limit order
books -- composes behind implementations of this single interface.
Composition vs. inheritance
Models do not form a class hierarchy. They compose:
graph TD
S["Simulatable trait"] --> G["GBM: price"]
S --> H["Heston: price, variance"]
S --> JD["JumpDiffusion: price"]
S --> B["Bates: price, variance"]
S --> W["Hawkes: intensity"]
Each leaf is its own impl Simulatable. The runner is monomorphized at
compile time, so there is zero runtime branching on which model is active.
When you run a GBM simulation, no Heston or L3 order book code is compiled
or linked.
Three-layer architecture
graph TD
subgraph L1["Layer 1: market_model (Simulation)"]
SIM["Simulatable trait"]
SR["SimulationRunner"]
PS["PriceStrategy trait"]
end
subgraph L2["Layer 2: solver (Optimization)"]
FD["Finite Difference Policy Iteration"]
BSDE["BSDE LSMC"]
AN["Analytical solutions"]
end
subgraph L3["Layer 3: engine (Backtesting)"]
ENG["Engine"]
EX["Exchange"]
MT["Matcher"]
ST["Strategy trait"]
VE["VecEnv"]
end
SIM --> FD
SIM --> BSDE
SR --> VE
PS --> ST
FD --> ST
BSDE --> ST
Layer 1 generates data. Layer 2 computes optimal controls. Layer 3 runs backtests and trains RL agents. Each layer depends only on the layer below, never above.
Zero-cost principle
| Scenario | What gets compiled | What does NOT get compiled |
|---|---|---|
| GBM price-path simulation | GBM step, runner, f64 output | Heston, solver, engine, order book |
| Heston HJB solve | Heston process, solver linalg, FD/BSDE | Engine, exchange, backtest |
| Full multi-agent backtest | Engine, exchange, matcher, strategies | Nothing -- this is the most expensive path |
| RL training | Engine, VecEnv, all process types used in config | Solver (unless using HJB-derived strategies) |
This is enforced by the crate dependency graph: solver imports only
market_model, and engine imports both. Cargo's dead code elimination
strips everything not reachable from your binary.