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

Pricing Context

PricingContext is the single object most users interact with. It collects raw market data and configuration, builds every derived market object once in initialize(), and then acts as the MarketDataProvider that pricers, the scripting engine and the XVA engine query.

Building a context

use quantsupport::prelude::*;

let mut ctx = PricingContext::new()
    .with_quote_store(quote_store)                          // required: prices + reference date
    .with_fixing_store(fixings)                             // optional: historical fixings
    .with_fx_store(fx_store)                                // optional: spot FX (needed for Collateral curves)
    .with_base_currency(Currency::USD)                      // CSA currency, default USD
    .with_base_index(MarketIndex::SOFR)                     // CSA discount index, default SOFR
    .with_curve_configurations(curve_specs)                 // Vec<CurveConfiguration>
    .with_credit_curve_configurations(credit_specs)         // Vec<CreditCurveConfiguration>
    .with_volatility_surface_configurations(surface_specs)  // Vec<VolatilitySurfaceConfiguration>
    .with_volatility_cube_configurations(cube_specs)        // Vec<VolatilityCubeConfiguration>
    .with_simulation_configurations(sim_specs)              // Vec<SimulationConfiguration>
    .with_scenarios(vec![Scenario::new("SOFR", 0.0001, ScenarioType::Absolute)]);

ctx.initialize()?;

All with_* methods consume and return Self. The evaluation date is not set separately: evaluation_date() returns quote_store.reference_date().

Read accessors

MethodReturns
quote_store()shocked store if scenarios are attached, otherwise the base store
base_quote_store()the unshocked store
fixing_store(), fx_store()the stores as given
scenarios()&Vec<Scenario>
base_currency(), base_index()CSA currency / index
curve_configurations(), credit_curve_configurations(), volatility_surface_configurations(), volatility_cube_configurations(), simulation_configurations()the configuration vectors
constructed_elements() / constructed_elements_mut()the ConstructedElementStore populated by initialize()
evaluation_date()reference date of the quote store

What initialize() does

pub fn initialize(&mut self) -> Result<()>
  1. Scenarios. If scenarios is non-empty, clone the quote store and apply each Scenario in order. A scenario that matches no quote is an error. Everything below reads the shocked copy.
  2. Discount curves. MultiCurveBootstrapper::new(curve_configurations, BootstrapDiscountPolicy::new(base_index, base_currency)).with_fx_store(fx_store).bootstrap(quote_store, Level::Mid). Each resulting DiscountCurveElement is inserted under its MarketIndex.
  3. Credit curves. CreditCurveBootstrapper::bootstrap(quote_store, Level::Mid, discount_curves) — CDS premium and protection legs are discounted on the curves from step 2.
  4. Volatility surfaces. VolatilitySurfaceBuilder::build(quote_store, Level::Mid).
  5. Volatility cubes. VolatilityCubeBuilder::build(quote_store, Level::Mid).
  6. Simulations. SimulationBuilder::build(constructed_elements, quote_store, fixing_store, Level::Mid) — runs last so models can calibrate to the surfaces/cubes and diffuse the bootstrapped curves.

Steps 3–6 are skipped when the corresponding configuration vector is empty. Calling initialize() twice rebuilds everything from the (possibly shocked) quotes.

Serving market data to pricers

PricingContext implements MarketDataProvider:

pub trait MarketDataProvider {
    fn evaluation_date(&self) -> Date;
    fn handle_request(&self, request: &MarketDataRequest) -> Result<MarketData>;
}

A pricer first calls market_data_request(&trade) to describe what it needs, then the context resolves it:

pub struct MarketDataRequest {                 // all fields optional
    constructed_elements_request: Option<Vec<ConstructedElementRequest>>,
    fixings_request: Option<Vec<FixingRequest>>,
    fx_request: Option<Vec<FxRequest>>,
}
pub enum ConstructedElementRequest {
    DiscountCurve { market_index }, DividendCurve { market_index }, CreditCurve { market_index },
    VolatilitySurface { market_index }, VolatilityCube { market_index }, Simulation { market_index },
}

handle_request copies the requested elements into a fresh ConstructedElementStore, gathers the fixings (MarketData::fixings() is a HashMap<MarketIndex, BTreeMap<Date, f64>>), and attaches the FxStore. A missing element produces QSError::NotFoundErr("Discount curve not found for index …") — the most common error when a trade references an index without a curve configuration.

Because pricers only see MarketData, you can bypass the context entirely in tests by constructing MarketData::new(fixings, constructed_elements).with_fx_store(fx) and implementing MarketDataProvider on a small struct, or by populating constructed_elements_mut() directly with hand-built curves as the scripting example does:

let mut store = ConstructedElementStore::default();
store.discount_curves_mut().insert(
    MarketIndex::SOFR,
    DiscountCurveElement::new(MarketIndex::SOFR, Rc::new(RefCell::new(curve))),
);
let ctx = PricingContext::new()
    .with_quote_store(QuoteStore::new(ref_date))
    .with_fixing_store(FixingStore::default())
    .with_constructed_elements(store)
    .with_base_currency(Currency::USD)
    .with_base_index(MarketIndex::SOFR);
// no initialize(): the curves are already there

Scenarios

Scenario::new(target, shock, ScenarioType::{Absolute, Relative}) shocks quotes before bootstrapping:

  • Absolute adds shock to the quote (0.0001 = 1 bp); Relative multiplies by 1 + shock.
  • target is a full identifier ("OIS_USD_SOFR_5Y", one key-rate bump) or a segment selector: every underscore-separated segment of the target must appear among the identifier’s segments. "SOFR" shocks all SOFR quotes (parallel shift), "OIS_USD_SOFR" all USD SOFR OIS pillars, "Swaption_USD" the USD swaption vol cube, "CapletFloorlet_USD_SOFR" the caplet surface.
  • scenario.apply(&mut store) returns the number of quotes shocked and errors when zero matched.

Since all curves, vols and simulations are rebuilt from the shocked quotes, a scenario valuation is a full repricing, not a curve-level approximation. Use AAD sensitivities (Request::Sensitivities) for first-order risk and scenarios for stress tests, bump-and-reprice validation of AAD, or non-linear moves. See Scenarios.

Evaluating trades

The chapter Rust API shows the pricer-based flow (DiscountedCashflowPricer::new().evaluate(&trade, &[Request::Value, Request::Sensitivities], &ctx)), and Pricing Overview lists which pricer handles which trade type and the Evaluator for heterogeneous portfolios. The XVA engine takes the same context: XvaEngine::new(&ctx, config)?.run(&mut netting_sets).

Python

The binding exposes the same object with keyword arguments matching the builders: PricingContext(quotes, curves, fixings=None, fx=None, volatility_surfaces=None, volatility_cubes=None, simulations=None, discounting=None, scenarios=None); initialize() runs on construction, and the object is a context manager that clears the AD tape on exit. See Python API.