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

ScenarioWhat gets compiledWhat does NOT get compiled
GBM price-path simulationGBM step, runner, f64 outputHeston, solver, engine, order book
Heston HJB solveHeston process, solver linalg, FD/BSDEEngine, exchange, backtest
Full multi-agent backtestEngine, exchange, matcher, strategiesNothing -- this is the most expensive path
RL trainingEngine, VecEnv, all process types used in configSolver (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.