Esc

    The API is under testing and may change before release.

    Book a trade with a CSA counterparty

    You'll book a USD swap facing a counterparty whose CSA posts EUR collateral, so it discounts on the USD-under-EUR curve, and check that's the curve it values on.

    Before you start

    • helixio installed: see installing.
    • Quotes for both currencies' overnight curves, the EUR/USD spot, and a cross sheet: FX swaps quoted as outrights in EUR per USD, and EUR/USD basis swaps. The example states them inline.
    • The counterparty's acronym and the overnight index of the collateral its CSA posts.

    Steps

    1. State the close, the quotes, the spot and the cross sheet.
    2. Build the market. Add the spot with with_fx_rates before the cross sheet, then with_recipe_quotes("USD-CSA-EUR", cross) builds the curve a USD trade discounts on under a EUR CSA.
    3. Put the counterparty master on the market with with_counterparties, each counterparty as Counterparty(acronym, overnight_index).
    4. Look the counterparty up with market.counterparty(acronym) and book the trade facing it with with_counterparty.
    5. Value it, and read its discount curve from market_data_needs.

    The comments in the program match these steps.

    Rust
    use chrono::NaiveDate;
    use helixrs::{
        CollateralSpec, Counterparties, Counterparty, CsaSpec, CurrencyPair, FxRates, MarketContext,
        QuoteSheet, Termination, market_data_needs, template,
    };
    
    type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
    
    fn main() -> Outcome<()> {
        // 1. The close, the quotes for both currencies, the EUR/USD spot, and the
        //    cross sheet: FX swaps as outrights in EUR per USD, then the basis.
        let close = NaiveDate::from_ymd_opt(2026, 10, 1).expect("a real date");
        let usd_close = [
            ("USD-SOFR-OIS", "1M", 0.0391),
            ("USD-SOFR-OIS", "3M", 0.0402),
            ("USD-SOFR-OIS", "1Y", 0.0439),
            ("USD-SOFR-OIS", "2Y", 0.0459),
            ("USD-SOFR-OIS", "3Y", 0.0468),
            ("USD-SOFR-OIS", "4Y", 0.0469),
            ("USD-SOFR-OIS", "5Y", 0.0471),
            ("USD-SOFR-OIS", "7Y", 0.0475),
            ("USD-SOFR-OIS", "10Y", 0.0483),
            ("USD-SOFR-OIS", "15Y", 0.0494),
            ("USD-SOFR-OIS", "20Y", 0.0499),
            ("USD-SOFR-OIS", "30Y", 0.0492),
        ];
        let eur_close = [
            ("EUR-ESTR-OIS", "1Y", 0.0195),
            ("EUR-ESTR-OIS", "2Y", 0.0200),
            ("EUR-ESTR-OIS", "5Y", 0.0215),
            ("EUR-ESTR-OIS", "10Y", 0.0240),
            ("EUR-ESTR-OIS", "20Y", 0.0262),
            ("EUR-ESTR-OIS", "30Y", 0.0265),
            ("EUR-EURIBOR-3M-IRS", "1Y", 0.0210),
            ("EUR-EURIBOR-3M-IRS", "2Y", 0.0216),
            ("EUR-EURIBOR-3M-IRS", "5Y", 0.0232),
            ("EUR-EURIBOR-3M-IRS", "10Y", 0.0257),
            ("EUR-EURIBOR-3M-IRS", "20Y", 0.0278),
            ("EUR-EURIBOR-3M-IRS", "30Y", 0.0280),
        ];
        let cross = [
            ("USD-EUR-FXSWAP", "3M", 0.8496),
            ("USD-EUR-FXSWAP", "6M", 0.8441),
            ("USD-EUR-FXSWAP", "1Y", 0.8337),
            ("EUR-USD-ESTR-SOFR-BASIS", "2Y", -0.0009),
            ("EUR-USD-ESTR-SOFR-BASIS", "5Y", -0.0013),
            ("EUR-USD-ESTR-SOFR-BASIS", "10Y", -0.0012),
        ];
    
        // 2. Build the market, with the curve a USD trade discounts on under a EUR CSA.
        let market = MarketContext::as_of(close)
            .with_recipe_quotes(&CollateralSpec::Currency("USD".parse()?), &QuoteSheet::from_rows(usd_close)?)?
            .with_recipe_quotes(&CollateralSpec::Currency("EUR".parse()?), &QuoteSheet::from_rows(eur_close)?)?
            .with_fx_rates(FxRates::from_quotes(&[("EUR/USD".parse::<CurrencyPair>()?, 1.1720)])?)
            .with_recipe_quotes(&"USD-CSA-EUR".parse::<CollateralSpec>()?, &QuoteSheet::from_rows(cross)?)?;
    
        // 3. Put the counterparty master on the market: each counterparty's acronym
        //    and its CSA, named by the overnight index of the collateral it posts.
        let market = market
            .with_counterparties(Counterparties::new().with(Counterparty::new("EXCPTY", CsaSpec::new("EUR-ESTR"))));
    
        // 4. Look the counterparty up and book the trade facing it.
        let counterparty = market.counterparty("EXCPTY").expect("in the master").clone();
        let draft = template("USD-SOFR-OIS")?
            .swap(close)?
            .with_maturity("3Y".parse::<Termination>()?)?
            .with_notional(20_000_000.0)
            .with_fixed_rate(0.0462);
        let swap = draft.clone().with_counterparty(&counterparty).build()?;
    
        // 5. Value it, and see which curve it discounts on.
        let needs = market_data_needs(&swap)?.needs();
        let discount = &needs.iter().find(|(role, _)| role == "discount").expect("a discount curve").1;
        let npv = swap.npv(&market)?;
        println!("discounts on      {discount}");
        println!("npv facing EXCPTY {npv:.2}");
        println!("npv, USD CSA      {:.2}", draft.clone().build()?.npv(&market)?);
        let fair = draft.clone().with_counterparty(&counterparty).at_par(&market)?;
        println!("par rate facing   {:.8}", fair.par_rate(&market)?);
        println!("npv at par        {:.2}", fair.npv(&market)?.abs());
    
        Ok(())
    }
    Python
    from datetime import date
    
    import helixio as hlx
    
    # 1. The close, the quotes for both currencies, the EUR/USD spot, and the
    #    cross sheet: FX swaps as outrights in EUR per USD, then the basis.
    SOFR_CLOSE_DATE = date(2026, 10, 1)
    USD_SOFR_CLOSE = {
        "USD-SOFR-OIS": {
            "1M": 0.0391, "3M": 0.0402, "1Y": 0.0439, "2Y": 0.0459, "3Y": 0.0468, "4Y": 0.0469,
            "5Y": 0.0471, "7Y": 0.0475, "10Y": 0.0483, "15Y": 0.0494, "20Y": 0.0499, "30Y": 0.0492,
        },
    }
    EUR_CLOSE = {
        "EUR-ESTR-OIS": {"1Y": 0.0195, "2Y": 0.0200, "5Y": 0.0215, "10Y": 0.0240, "20Y": 0.0262, "30Y": 0.0265},
        "EUR-EURIBOR-3M-IRS": {"1Y": 0.0210, "2Y": 0.0216, "5Y": 0.0232, "10Y": 0.0257, "20Y": 0.0278, "30Y": 0.0280},
    }
    CROSS = {
        "USD-EUR-FXSWAP": {"3M": 0.8496, "6M": 0.8441, "1Y": 0.8337},
        "EUR-USD-ESTR-SOFR-BASIS": {"2Y": -0.0009, "5Y": -0.0013, "10Y": -0.0012},
    }
    
    # 2. Build the market, with the curve a USD trade discounts on under a EUR CSA.
    market = (
        hlx.MarketContext(SOFR_CLOSE_DATE)
        .with_recipe_quotes("USD", USD_SOFR_CLOSE)
        .with_recipe_quotes("EUR", EUR_CLOSE)
        .with_fx_rates([("EUR", "USD", 1.1720)])
        .with_recipe_quotes("USD-CSA-EUR", CROSS)
    )
    
    # 3. Put the counterparty master on the market: each counterparty's acronym and
    #    its CSA, named by the overnight index of the collateral it posts.
    market = market.with_counterparties([hlx.Counterparty("EXCPTY", "EUR-ESTR")])
    
    # 4. Look the counterparty up and book the trade facing it.
    counterparty = market.counterparty("EXCPTY")
    draft = (
        hlx.templates.get("USD-SOFR-OIS")
        .swap(SOFR_CLOSE_DATE)
        .with_maturity("3Y")
        .with_notional(20_000_000.0)
        .with_fixed_rate(0.0462)
    )
    swap = draft.with_counterparty(counterparty).build()
    
    # 5. Value it, and see which curve it discounts on.
    needs = dict(swap.market_data_needs())
    npv = swap.npv(market)
    print(f"discounts on      {needs['discount']}")
    print(f"npv facing EXCPTY {npv:.2f}")
    print(f"npv, USD CSA      {draft.build().npv(market):.2f}")
    fair = draft.with_counterparty(counterparty).at_par(market)
    print(f"par rate facing   {fair.par_rate(market):.8f}")
    print(f"npv at par        {abs(fair.npv(market)):.2f}")
    discounts on      USD-CSA-EUR
    npv facing EXCPTY -33802.45
    npv, USD CSA      -33336.72
    par rate facing   0.04680572
    npv at par        0.00

    Check it worked

    • The trade discounts on USD-CSA-EUR.
    • Its value differs from the same trade under a USD CSA.
    • Struck at par facing the counterparty, it's worth nothing on the market.
    • Counterparties and CSA, the reference for counterparties, the cross curve and what its recipe refuses.
    • How trades are built, for why collateral decides the curve.
    • Cross-currency calibration, for solving the cross curve explicitly.

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