Esc

    The API is under testing and may change before release.

    CDS options

    This page lists everything you need to book and value an option on a CDS: the function's arguments and defaults, the credit vol surface it reads, how it's priced, and an example you can run. A CDS option, or credit swaption, is the right to enter a CDS at a strike spread on a future date. A payer is the right to buy protection, a receiver the right to sell it; how they trade is covered in how credit products work.

    Quick example

    The example reads its rates from the desk sheets; load them once and it runs as shown.

    This pins ACME's survival curve and a credit vol surface, books a payer and a receiver struck at 120bp into a five-year CDS starting on 20 March 2027, and reads back the forward spread and the risky annuity the model priced with.

    Rust
    use chrono::NaiveDate;
    use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_CLOSE};
    use helixrs::{
        AnyInstrument, CdsOptionStyle, CollateralSpec, CreditCurveId, CreditDefaultSwap,
        CreditDefaultSwapOption, CreditVolSurface, Currency, DayCountFraction, FlatHazard,
        MarketContext, QuoteSheet, Survival, SwaptionKind, cds_option, cds_standard,
    };
    
    type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
    
    fn date((year, month, day): (i32, u32, u32)) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    const ACME: &str = "ACME/SeniorUnSec/XR14/USD";
    const STRIKE: f64 = 0.0120;
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
        let expiry = date((2027, 3, 20));
        let usd: Currency = "USD".parse()?;
    
        // Rates from the sheet, the name's survival curve, and its credit vol surface:
        // the Black vol of the forward spread by expiry in years.
        let sheet = QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?;
        let acme: CreditCurveId = ACME.parse()?;
        let vols = CreditVolSurface::new(&[(0.5, 0.45), (1.0, 0.42)])?;
        let market = MarketContext::as_of(close)
            .with_recipe_quotes(&CollateralSpec::Currency(usd), &sheet)?
            .with_credit_curve(
                acme.clone(),
                Survival::FlatHazard(FlatHazard::new(close, 0.02, DayCountFraction::Act365Fixed)?),
                Some(0.4),
            )
            .with_credit_vols(acme, vols.clone());
    
        // The option exercises into a five-year CDS traded on the expiry date.
        let underlying = CreditDefaultSwap::standard(&cds_standard(), expiry, "ACME", usd, 5, 1.0, 0.0100, true);
        let option = |kind| -> Outcome<AnyInstrument> {
            Ok(AnyInstrument::CdsOption(Box::new(CreditDefaultSwapOption::new(
                underlying.clone(),
                expiry,
                STRIKE,
                kind,
                false,
                CdsOptionStyle::SingleName,
            )?)))
        };
        let p = option(SwaptionKind::Payer)?.npv(&market)?;
        let r = option(SwaptionKind::Receiver)?.npv(&market)?;
        println!("payer      {p:.8}");
        println!("receiver   {r:.8}");
    
        // The Black formula returns the forward and the risky annuity it priced with.
        let black = cds_option(&underlying, expiry, STRIKE, vols.vol(0.5), SwaptionKind::Payer, false, &market)?;
        println!("forward    {:.8}", black.forward);
        println!("annuity    {:.6}", black.annuity);
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_CLOSE
    
    ACME = "ACME/SeniorUnSec/XR14/USD"
    EXPIRY = dt.date(2027, 3, 20)
    STRIKE = 0.0120
    
    # Rates from the sheet, the name's survival curve, and its credit vol surface:
    # the Black vol of the forward spread by expiry in years.
    vols = hlx.vols.CreditVolSurface([(0.5, 0.45), (1.0, 0.42)])
    market = (
        hlx.MarketContext(SOFR_CLOSE_DATE)
        .with_recipe_quotes("USD", USD_SOFR_CLOSE)
        .with_credit_curve(ACME, hlx.curves.SurvivalCurve.flat_hazard(SOFR_CLOSE_DATE, 0.02), recovery=0.4)
        .with_credit_vols(ACME, vols)
    )
    
    # The option exercises into a five-year CDS traded on the expiry date.
    underlying = hlx.products.credit_default_swap(EXPIRY, "ACME", "USD", 5, notional=1.0, coupon=0.0100)
    payer = hlx.products.credit_default_swap_option(underlying, EXPIRY, strike=STRIKE, kind="payer")
    receiver = hlx.products.credit_default_swap_option(underlying, EXPIRY, strike=STRIKE, kind="receiver")
    p, r = payer.npv(market), receiver.npv(market)
    print(f"payer      {p:.8f}")
    print(f"receiver   {r:.8f}")
    
    # The Black formula returns the forward and the risky annuity it priced with.
    _, forward, annuity = hlx.credit.cds_option(
        underlying, EXPIRY, strike=STRIKE, vol=vols.vol(0.5), kind="payer", market=market
    )
    print(f"forward    {forward:.8f}")
    print(f"annuity    {annuity:.6f}")
    payer      0.00614337
    receiver   0.00651450
    forward    0.01191441
    annuity    4.335979

    The forward spread sits just under the strike, so the receiver is worth a little more than the payer. Their difference is the annuity times the forward less the strike, whatever the vol.

    How it's priced

    Let FF be the forward par spread of the CDS the option exercises into, AA its risky annuity from today, KK the strike, σ\sigma the vol and TT the time to expiry. Under Black's model on the forward spread, the knockout payer and receiver are

    P=A [F N(d1)−K N(d2)],R=A [K N(−d2)−F N(−d1)],P = A\,\bigl[F\,N(d_1) - K\,N(d_2)\bigr], \qquad R = A\,\bigl[K\,N(-d_2) - F\,N(-d_1)\bigr],

    with d1,2=(ln⁡(F/K)±12σ2T)/(σT)d_{1,2} = \bigl(\ln(F/K) \pm \tfrac12 \sigma^2 T\bigr) / (\sigma \sqrt{T}). Subtracting gives put–call parity, P−R=A (F−K)P - R = A\,(F - K).

    API

    Argument Type Default Meaning
    underlying Instrument required the CDS the option exercises into, a single-name or index contract
    expiry date required the exercise date
    strike float required the strike spread, or a price under strike_type="price"
    kind SwaptionKind or str required "payer", the right to buy protection, or "receiver", the right to sell it
    short bool False True books the seller of the option
    style CdsOptionStyle or str "single_name" single_name knocks out on a default before expiry; front_end_protection adds a payer's protection to expiry instead; index prices an index option with its exercise price
    strike_type CdsStrikeType or str "spread" spread, or price per unit notional under the index style

    The vol surface is vols.CreditVolSurface([(years, vol), ...]), the Black vol of the forward spread by expiry in years, pinned with MarketContext.with_credit_vols(identity, surface). In Rust the option is CreditDefaultSwapOption::new.

    Symbol Meaning Rust Python
    σ(T)\sigma(T) credit vol by expiry CreditVolSurface::new vols.CreditVolSurface
    pinned vols under the name's identity MarketContext::with_credit_vols MarketContext.with_credit_vols
    the option on a forward CDS CreditDefaultSwapOption::new products.credit_default_swap_option
    PP, RR option values AnyInstrument::npv Instrument.npv
    (P,F,A)(P, F, A) Black's model, read out cds_option credit.cds_option

    Defaults and limits

    • The vols: a market must hold a credit vol surface under the underlying's identity; without it the valuation is refused. Between quoted expiries the surface is linear in total variance, and beyond them it is flat.
    • Knockout: under the default single_name style the option is worth nothing on a default before expiry. Index options use the index style, since the index survives a default.
    • The underlying follows the standard terms in the credit conventions catalog; an index underlying is taken at its index factor.

    See also

    • Source: crates/helixrs/src/market/pricers.rs, crates/helixrs/src/marketdata/credit_vol.rs
    • Related pages:
      • How credit products work, the ideas behind the option.
      • Credit default swap, for the contract the option exercises into.
      • Index CDS, for an index underlying.

    From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.