Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture

QuantSupport separates observable inputs, constructed market objects, instruments and trades, and pricers. This chapter explains why the layers exist and how data flows between them.

flowchart LR
    subgraph Inputs
        Q[QuoteStore]
        F[FixingStore]
        X[FxStore]
        S[Scenarios]
    end
    subgraph Configuration
        CC[CurveConfiguration]
        CR[CreditCurveConfiguration]
        VS[VolatilitySurfaceConfiguration]
        VC[VolatilityCubeConfiguration]
        SC[SimulationConfiguration]
    end
    PC[PricingContext::initialize]
    subgraph Constructed
        DC[Discount curves]
        CU[Credit curves]
        SU[Vol surfaces / cubes]
        SI[Monte Carlo simulations]
    end
    Q & F & X & S --> PC
    CC & CR & VS & VC & SC --> PC
    PC --> DC --> CU --> SU --> SI
    T[Trade] --> P[Pricer]
    DC & CU & SU --> P
    P --> R[EvaluationResults]
    DC --> XVA[XvaEngine / ScriptEngine]

Layer 1 – observable inputs

QuoteStore, FixingStore and FxStore hold what the market actually publishes: par swap rates, deposit rates, basis spreads, caplet and swaption volatilities, FX forwards, CDS spreads, past index fixings and spot FX. Quotes are strings-plus-numbers; nothing has been interpolated or bootstrapped yet. Scenarios (Scenario) act on this layer only, which is what makes shocked valuations consistent: every downstream object is rebuilt from the shocked quotes.

Layer 2 – configuration

Configuration structs say how to turn quotes into objects: which quotes belong to the SOFR curve, which interpolator to use, which caplet quotes form the vol surface, what model drives a simulation. They are plain Serialize/Deserialize data, so the same JSON can drive Rust and Python. See Configuration for schemas.

Layer 3 – constructed elements

PricingContext::initialize() produces the ConstructedElementStore, a set of HashMap<MarketIndex, *Element>:

AccessorElementHolds
discount_curves() / discount_curve(&idx)DiscountCurveElementRc<RefCell<dyn InterestRatesTermStructure<DualFwd>>>
dividend_curves() / dividend_curve(&idx)DividendCurveElementdividend yield curve for equity indices
credit_curves() / credit_curve(&idx)CreditCurveElementsurvival-probability curve from CDS
volatility_surfaces() / volatility_surface(&idx)VolatilitySurfaceElementexpiry × key surface
fx_volatility_surface(&FxPair)OrientedFxVolSurfacesurface oriented for a pair, inverting if only the reciprocal pair exists
volatility_cubes() / volatility_cube(&idx)VolatilityCubeElementexpiry × tenor × key cube
simulations()MonteCarloSimulationElementgenerated paths

Each accessor has a _mut twin so bootstrappers and hand-built markets can insert objects. The order of construction inside initialize() is fixed: scenarios → discount curves (MultiCurveBootstrapper) → credit curves (CreditCurveBootstrapper, which discounts CDS legs on the curves just built) → volatility surfaces → volatility cubes → simulations (SimulationBuilder, which may calibrate models to the surfaces/cubes).

Layer 4 – instruments and trades

An instrument (Swap, CapFloor, FxForward, …) describes cashflows: legs, coupons, indices, dates, strikes. It is built with a Make* builder and knows nothing about who owns it. A trade (SwapTrade, FxForwardTrade, …) adds trade_date, notional and Side. Pricers and the XVA engine consume trades; IntoContingentClaims is implemented on trades.

Instruments implement Discountable (asset class, currency, optional own discount index) so DiscountPolicy objects can decide which curve discounts them.

Layer 5 – pricers and results

A Pricer maps (trade, requests, market) → EvaluationResults. Before pricing, market_data_request(trade) declares the curves, fixings, FX rates and vol objects the pricer needs; the PricingContext (as MarketDataProvider) resolves them through handle_request, returning a MarketData bundle. This indirection is what allows the same pricer to run against a full context, a hand-built store, or a shocked copy.

Evaluator offers dynamic dispatch by trade TypeId when a portfolio mixes instrument types.

Layer 6 – simulation, scripting and XVA

LgmMarketModel and HullWhite read the constructed curves (and calibrate to constructed vol objects); ScriptEngine evaluates payoffs on a MarketModel<DualFwd>; XvaEngine decomposes trades into ContingentClaims and prices them on the simulated paths. Because these layers consume the same DiscountCurveElements that the deterministic pricers use, NPV at \(t_0\) from the exposure engine equals the pricer NPV, and AAD sensitivities flow back to the same quote pillars.

The AD thread running through everything

All constructed elements are built in DualFwd. Quote values become tape leaves during bootstrapping; discount factors, forwards and vols are tape nodes derived from them. Any result computed from the market—an NPV, a CVA, a scripted payoff—can be back-propagated to those leaves. The Automatic Differentiation chapter explains the tape API; the practical consequence is that Request::Sensitivities costs roughly one extra evaluation regardless of the number of quotes.