quantsupport 0.1.7

Rust quantitative finance library for derivatives pricing, yield-curve bootstrapping, AAD risk, Monte Carlo exposure, and XVA.
Documentation
# QuantSupport

QuantSupport is a quantitative-finance library written in Rust, with Python bindings provided in the same repository. It combines instrument construction, market-data bootstrapping, pricing, automatic differentiation, payoff scripting, Monte Carlo exposure simulation, and XVA in one toolkit.

## Quick start: price and risk a swap

This complete example values a five-year receive-fixed USD swap against a flat SOFR curve and asks for NPV, par rate, cashflows, and curve sensitivity.

```rust
use std::{cell::RefCell, rc::Rc};

use quantsupport::prelude::*;

fn main() -> Result<()> {
    let valuation_date = Date::new(2024, 1, 15);
    let maturity_date = Date::new(2029, 1, 15);
    let notional = 10_000_000.0;

    let swap = MakeSwap::<DualFwd>::default()
        .with_identifier("USD_IRS_5Y".to_string())
        .with_start_date(valuation_date)
        .with_maturity_date(maturity_date)
        .with_fixed_rate(0.03)
        .with_notional(notional)
        .with_rate_definition(RateDefinition::new(
            DayCounter::Actual360,
            Compounding::Simple,
            Frequency::Semiannual,
        ))
        .with_currency(Currency::USD)
        .with_market_index(MarketIndex::SOFR)
        .with_side(Side::LongReceive)
        .with_fixed_leg_frequency(Frequency::Semiannual)
        .with_floating_leg_frequency(Frequency::Semiannual)
        .build()?;
    let trade = SwapTrade::new(swap, valuation_date, notional, Side::LongReceive);

    let curve = FlatForwardTermStructure::new(
        valuation_date,
        DualFwd::from(0.03),
        RateDefinition::new(
            DayCounter::Actual360,
            Compounding::Continuous,
            Frequency::Annual,
        ),
    )
    .with_pillar_label("SOFR_flat".to_string());

    let mut elements = ConstructedElementStore::default();
    elements.discount_curves_mut().insert(
        MarketIndex::SOFR,
        DiscountCurveElement::new(MarketIndex::SOFR, Rc::new(RefCell::new(curve))),
    );

    let context = PricingContext::new()
        .with_quote_store(QuoteStore::new(valuation_date))
        .with_fixing_store(FixingStore::default())
        .with_base_currency(Currency::USD)
        .with_constructed_elements(elements);

    let pricer = DiscountedCashflowPricer::<Swap<DualFwd>, SwapTrade<DualFwd>>::new();
    let requests = [
        Request::Value,
        Request::FairRate,
        Request::Cashflows,
        Request::Sensitivities,
    ];
    let results = pricer.evaluate(&trade, &requests, &context)?;

    println!("NPV: {:.2}", results.price().unwrap_or_default());
    println!(
        "Par rate: {:.6}",
        results.fair_rate().unwrap_or_default()
    );

    if let Some(risk) = results.sensitivities() {
        for (pillar, exposure) in risk.instrument_keys().iter().zip(risk.exposure()) {
            println!("dPV/dQuote {pillar}: {exposure:.4}");
        }
    }

    if let Some(cashflows) = results.cashflows() {
        println!("Cashflows: {}", cashflows.payment_dates().len());
    }

    Ok(())
}
```

The same program, with a more detailed cashflow report, is available in [`examples/valuation`](examples/valuation).

## Capabilities

| Area | Current support |
| --- | --- |
| Instruments | Fixed-rate deposits and bonds, floating-rate notes, rate futures, swaps, basis swaps, caps/floors, caplets/floorlets, European swaptions, fixed/float and float/float cross-currency swaps, equity forwards and European options, FX forwards and options, futures, and credit default swaps |
| Pricing | Generic discounted-cashflow pricing; Black equity, FX, caplet, and cap/floor pricing; Monte Carlo equity option pricing; Hull-White caplet, cap/floor, and European swaption pricing; rate-futures and CDS pricing |
| Results and risk | NPV, fair rate, cashflow tables, and quote-pillar sensitivities through automatic differentiation; type-erased pricer dispatch through `Evaluator` |
| Curves | Flat and interpolated term structures, multi-curve bootstrapping, cross-curve dependencies, FX-implied collateral curves, and CDS-based survival-curve bootstrapping |
| Volatility | Interpolated volatility surfaces and cubes, Black and normal volatility conventions, FX surface orientation, and constant, surface-, cube-, or calibration-driven volatility sources |
| Models and simulation | Brownian motion, Hull-White, and LGM models; Hull-White/LGM volatility calibration; seeded Monte Carlo path generation from serializable configurations |
| Exposure and XVA | Contingent-claim decomposition, fixing preprocessing, claim compression, netting sets, CSA terms, NPV cubes, EPE/ENE/EE, CVA, DVA, FVA, and parallel AAD sensitivities |
| Scripting | Payoff scripting language (assignments, `if`/`else`, `for`, `pays`, `RateIndex`, `Df`, `Spot`, `cvg`, `fif`, arrays); dated event streams; single-tape and Rayon-parallel Monte Carlo evaluation with AAD sensitivities and expected cashflows; smoothed conditionals for digital payoffs; scripted products as XVA contingent claims |
| Market data | Quote, fixing, and FX stores; bid/mid/ask selection; absolute and relative quote scenarios that rebuild dependent curves, volatility objects, and simulations |
| Conventions and numerics | Dates, periods, schedules, IMM dates, calendars, business-day conventions, day counts, compounding, interpolation, root solvers, FFT, and probability utilities |
| Languages | Native Rust API and PyO3-based Python bindings with pandas result tables |

The Rust prelude re-exports the types used by the main workflows:

```rust
use quantsupport::prelude::*;
```

## Installation

Add the latest Rust crate release:

```bash
cargo add quantsupport
```

To work from this checkout instead:

```toml
[dependencies]
quantsupport = { path = "../quantsupport" }
```

Build and test the Rust library with:

```bash
cargo build -p quantsupport
cargo test -p quantsupport
```

## Configuration-driven market setup

`PricingContext::initialize` builds the requested market objects in dependency order: scenario-shocked quotes, discount curves, credit curves, volatility surfaces, volatility cubes, then model-driven simulations. All configuration types support Serde, so production inputs can live in JSON rather than application code.

```rust,ignore
// `quotes`, `fixings`, `fx`, and the configuration vectors can be
// deserialized from the JSON schemas used under examples/*/data/.
let mut context = PricingContext::new()
    .with_quote_store(quotes)
    .with_fixing_store(fixings)
    .with_fx_store(fx)
    .with_base_currency(Currency::USD)
    .with_base_index(MarketIndex::SOFR)
    .with_curve_configurations(curve_configs)
    .with_credit_curve_configurations(credit_curve_configs)
    .with_volatility_surface_configurations(surface_configs)
    .with_volatility_cube_configurations(cube_configs)
    .with_simulation_configurations(simulation_configs);

context.initialize()?;

let market = context.constructed_elements();
let sofr_curve = market
    .discount_curve(&MarketIndex::SOFR)
    .expect("SOFR was configured");
let five_year_df = sofr_curve
    .curve()
    .discount_factor(context.evaluation_date() + Period::from_str("5Y")?)?;
println!("SOFR 5Y discount factor: {:.8}", five_year_df.value());
```

For a complete configuration-loading implementation, see [`examples/bootstrap`](examples/bootstrap).

## Scenario analysis

A scenario can target one exact quote identifier or match identifier segments such as `SOFR`, `OIS_USD_SOFR`, or `Swaption_USD`. Absolute shocks are added to quote values; relative shocks multiply them by `1 + shock`.

```rust
use std::str::FromStr;

use quantsupport::prelude::*;

fn main() -> Result<()> {
    let mut quotes = QuoteStore::new(Date::new(2025, 11, 11));
    let details = QuoteDetails::from_str("OIS_USD_SOFR_1Y")?;
    quotes.add_quote(Quote::new(details, QuoteLevels::with_mid(0.04)));

    // Add 100 basis points to every quote with a SOFR identifier segment.
    let scenario = Scenario::new("SOFR", 0.01, ScenarioType::Absolute);
    let shocked_quotes = scenario.apply(&mut quotes)?;

    let shocked_mid = quotes
        .quote("OIS_USD_SOFR_1Y")
        .and_then(|quote| quote.levels().mid())
        .unwrap_or_default();
    println!(
        "Shocked {shocked_quotes} quote(s); new 1Y OIS rate: {:.2}%",
        shocked_mid * 100.0
    );

    Ok(())
}
```

Attach scenarios with `.with_scenarios(...)` before `PricingContext::initialize()` to rebuild the full market consistently from shocked inputs.

## Scripting

Bespoke payoffs can be described as dated scripts instead of new Rust instruments. A script is a list of `CodedEvent`s (date + source); the `ScriptEngine` parses and indexes them once, derives the discount factors, forward rates, FX rates, and spots it needs from the market model, and evaluates every Monte Carlo path in `DualFwd`, so NPV, pillar sensitivities, and expected cashflows come out of the same run.

The language supports `=`/`+=`/`-=`/`*=`/`/=`, arithmetic (`+ - * / **`), comparisons combined with `and`/`or`/`not`, `if { } else { }`, `for x in range(a, b) { }`, arrays (`[..]`, `.append`, `.mean`, `.std`, indexing), `exp`, `ln`, `pow`, `min`, `max`, `cvg(start, end, day_counter)`, the smoothed indicator `fif(x, a, b, eps)`, market observations `RateIndex("SOFR", start, end)`, `Df(date[, curve])`, `Spot("AAPL")` / `Spot("USD", "CLP")`, and payments `acc pays amount on "date" in "CCY";`. Conditionals are evaluated with scale-aware smoothing so digital payoffs keep finite AAD sensitivities.

```rust,ignore
use quantsupport::prelude::*;

// One event per accrual period: observe SOFR on the start date, pay the net coupon at the end.
let events: Vec<CodedEvent> = periods
    .iter()
    .enumerate()
    .map(|(i, (start, end))| {
        let init = if i == 0 { "swap = 0; fixed_rate = 0.035;" } else { "" };
        CodedEvent::new(*start, format!(r#"
            {init}
            accrual = cvg("{start}", "{end}", "Actual360");
            floating_rate = RateIndex("SOFR", "{start}", "{end}");
            swap pays 10000000 * (fixed_rate - floating_rate) * accrual on "{end}";
        "#))
    })
    .collect();

let engine = ScriptEngine::new(EventStream::try_from(events)?, ref_date, Currency::USD, MarketIndex::SOFR)?;

// Any MarketModel<DualFwd> works; here an LGM model whose curve pillars are on the AD tape.
let (values, cashflows) = engine.evaluate_with_cashflows(&mut lgm_model, Some("swap"))?;
println!("NPV = {}", values["swap"]);          // pillar.adjoint() now holds dNPV/dPillar
for cf in cashflows {
    println!("{} {} amount={:.2} pv={:.2}", cf.date, cf.currency, cf.amount, cf.present_value);
}

// Multi-threaded evaluation rebuilds the model per Rayon worker through `ScriptModelSetup`
// and returns values, labelled sensitivities, and cashflows.
let parallel: ParallelScriptEvaluation = engine.evaluate_parallel(&setup, Some("swap"))?;

// The same script enters the XVA engine as ordinary contingent claims.
let claims = ScriptedProduct::new("note", EventStream::try_from(events)?, ref_date, Currency::USD, MarketIndex::SOFR)?
    .contingent_claims()?;
```

`examples/scripting` prices a swap both natively and as a script and checks that NPV, pillar sensitivities, EPE, and CVA/FVA sensitivities agree. The [Scripting](book/src/scripting/overview.md) part of the book documents the full language and runtime.

## Runnable Rust examples

All examples below are workspace packages and use local JSON market data where appropriate.

| Example | Demonstrates | Run |
| --- | --- | --- |
| [`valuation`](examples/valuation) | Flat-curve swap NPV, cashflows, and AAD sensitivity | `cargo run -p valuation` |
| [`bootstrap`](examples/bootstrap) | JSON quote loading and dependent USD/CLP multi-curve bootstrapping | `cargo run -p bootstrap` |
| [`sensitivity`](examples/sensitivity) | Multi-curve pricing of SOFR, Term SOFR, ICP, and cross-currency swaps with pillar DV01 | `cargo run -p sensitivity` |
| [`volatilitysurface`](examples/volatilitysurface) | Building and querying an interpolated SOFR caplet Black-volatility surface | `cargo run -p volatilitysurface` |
| [`hullwhite`](examples/hullwhite) | Curve construction, caplet-vol calibration, Hull-White pricing, simulation, and plots | `cargo run -p hullwhite` |
| [`pfe`](examples/pfe) | Multi-currency LGM exposure simulation for swaps, FX products, and cross-currency swaps | `cargo run -p pfe` |
| [`cva`](examples/cva) | High-level netting-set XVA with CSA, credit/funding inputs, CVA/FVA values, exposure profiles, and AAD sensitivities | `cargo run -p cva` |
| [`scripting`](examples/scripting) | Scripted swap vs native swap: NPV and pillar sensitivities through `ScriptEngine` | `cargo run -p scripting-examples --bin valuation` |
| [`scripting`](examples/scripting) | Scripted product as XVA contingent claims: EPE and CVA/FVA sensitivities vs native swap | `cargo run -p scripting-examples --bin xva` |

The `plot` Cargo feature enables the library's plotting helpers:

```bash
cargo add quantsupport --features plot
```

## Python bindings

The Python are under development, but a package exposes typed dates and enums, market-data/configuration objects, curve/volatility/simulation exploration, the supported trade specifications, pricing results as pandas tables, quote scenarios, and the high-level XVA workflow.

Build it into the active virtual environment from the repository root:

```bash
python -m pip install maturin
maturin develop -m bindings/python/Cargo.toml --release
```

Minimal usage:

```python
import quantsupport as qs

quotes = qs.QuoteStore.from_json("quotes.json")
curves = qs.CurveConfiguration.from_json("curve_specs.json")
discounting = qs.DiscountingConfig(
    currency=qs.Currency.USD,
    index=qs.MarketIndex.SOFR,
)

with qs.PricingContext(
    quotes=quotes,
    curves=curves,
    discounting=discounting,
) as context:
    sofr = context.curve(qs.MarketIndex.SOFR)
    print(sofr.nodes())
    print(sofr.discount_factor(quotes.reference_date + "5Y"))
```

See the [Python README](bindings/python/README.md) and [guided notebook](bindings/python/examples/tour.ipynb) for pricing and XVA examples.

## Book

The [QuantSupport Book](https://jmelo11.github.io/quantsupport/) covers installation, market construction, pricing, risk, scripting, simulation, and XVA. It is published to GitHub Pages on every push to `main`; the sources live under [`book/src`](book/src/SUMMARY.md). To build it locally, install [mdBook](https://rust-lang.github.io/mdBook/), then from the repository root:

```bash
mdbook build
mdbook serve --open
```

Generated HTML is written to `book/html/`.

## Contributing

Contributions are welcome. For small fixes, feel free to open a pull request directly. For larger changes or design discussions, please open an issue first.

## License

QuantSupport is released under the [MIT License](LICENSE).

## Contact

For business inquiries, contact <jmelo@live.cl>.