Esc

    The API is under testing and may change before release.

    Cross-currency swap

    This page lists what you need to value a cross-currency swap: the booking call and its arguments, how the value and each leg are priced, and the defaults you get. A cross-currency swap exchanges interest in two currencies on notionals exchanged at the start and returned at the end.

    Quick example

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

    This marks a EUR against USD market with the euro curve collateralised in dollars, values a five-year €STR against SOFR basis swap from both sides, and reads its par spread.

    Rust
    use chrono::NaiveDate;
    use helixio_docs_sheets::{EUR_CLOSE, SOFR_CLOSE_DATE, Sheet, USD_SOFR_CLOSE};
    use helixrs::{
        AnyInstrument, AnyTemplate, CollateralSpec, CrossCurrencySwap, CurrencyPair, FxRates,
        MarketContext, QuoteSheet, template,
    };
    
    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 BASIS: Sheet = &[
        ("EUR-USD-ESTR-SOFR-BASIS", "1Y", -0.0010),
        ("EUR-USD-ESTR-SOFR-BASIS", "2Y", -0.0012),
        ("EUR-USD-ESTR-SOFR-BASIS", "5Y", -0.0015),
        ("EUR-USD-ESTR-SOFR-BASIS", "10Y", -0.0016),
    ];
    
    fn key(spelling: &str) -> Outcome<CollateralSpec> {
        Ok(spelling.parse()?)
    }
    
    fn spot(base: &str, quote: &str, rate: f64) -> Outcome<FxRates> {
        Ok(FxRates::from_quotes(&[(
            CurrencyPair::new(base.parse()?, quote.parse()?),
            rate,
        )])?)
    }
    
    fn xccy(
        code: &str,
        end: NaiveDate,
        notional: f64,
        fx_rate: f64,
        quote: f64,
        pays_quoted_leg: bool,
        resets: bool,
    ) -> Outcome<AnyInstrument> {
        let AnyTemplate::Xccy(mut t) = template(code)? else {
            unreachable!("a cross-currency template")
        };
        if !resets {
            t = t.with_resetting_leg(None);
        }
        let mut swap =
            CrossCurrencySwap::from_template(&t, date((2026, 10, 5)), end, notional, fx_rate, quote)?;
        if pays_quoted_leg {
            swap = swap.reversed();
        }
        Ok(AnyInstrument::CrossCurrency(Box::new(swap)))
    }
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
        let market = MarketContext::as_of(close)
            .with_recipe_quotes(
                &key("USD")?,
                &QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?,
            )?
            .with_recipe_quotes(
                &key("EUR")?,
                &QuoteSheet::from_rows(EUR_CLOSE.iter().copied())?,
            )?
            .with_fx_rates(spot("EUR", "USD", 1.08)?)
            .with_recipe_quotes(
                &key("USD-CSA-EUR")?,
                &QuoteSheet::from_rows(BASIS.iter().copied())?,
            )?;
        let basis_swap = |spread: f64, pays: bool, resets: bool| {
            xccy(
                "EUR-USD-ESTR-SOFR-BASIS",
                date((2031, 10, 5)),
                10_000_000.0,
                1.08,
                spread,
                pays,
                resets,
            )
        };
    
        for (role, key) in market.contents() {
            println!("{role:<11} {key}");
        }
    
        // The swap values in its quoted leg's currency, euros, whichever side the
        // holder takes. Paying the quoted leg is the other side of the same trade, so
        // the two values sum to zero as read, with no conversion.
        let flat = basis_swap(0.0, false, true)?;
        let other = basis_swap(0.0, true, true)?;
        let flat_npv = flat.npv(&market)?;
        println!("swap rec. EUR    {flat_npv:>12.2} {}", flat.currency());
        println!("swap pay EUR     {:>12.2} {}", other.npv(&market)?, other.currency());
    
        // Five years of €STR plus a spread against SOFR, notionals exchanged at the
        // spot rate. The quote the curve was solved to reprices to the spread.
        let spread = flat.par_spread(&market)?;
        println!("npv at zero spread  {flat_npv:.2}");
        println!("par spread          {spread:.8}");
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    from desk_sheets import EUR_CLOSE, SOFR_CLOSE_DATE, USD_SOFR_CLOSE
    
    BASIS = {"EUR-USD-ESTR-SOFR-BASIS": {"1Y": -0.0010, "2Y": -0.0012, "5Y": -0.0015, "10Y": -0.0016}}
    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.08)])
        .with_recipe_quotes("USD-CSA-EUR", BASIS)
    )
    eurusd = hlx.templates.get("EUR-USD-ESTR-SOFR-BASIS")
    
    def basis_swap(spread, pays_quoted_leg=False, template=eurusd):
        return template.cross_currency_swap(
            dt.date(2026, 10, 5),
            dt.date(2031, 10, 5),
            notional=10_000_000.0,
            fx_rate=1.08,
            spread=spread,
            pays_quoted_leg=pays_quoted_leg,
        )
    
    for role, key in market.contents():
        print(f"{role:<11} {key}")
    
    # The swap values in its quoted leg's currency, euros, whichever side the
    # holder takes. Paying the quoted leg is the other side of the same trade, so
    # the two values sum to zero as read, with no conversion.
    flat = basis_swap(0.0)
    other = basis_swap(0.0, pays_quoted_leg=True)
    print(f"swap rec. EUR    {flat.npv(market):>12.2f} {flat.npv_ccy()}")
    print(f"swap pay EUR     {other.npv(market):>12.2f} {other.npv_ccy()}")
    
    # Five years of €STR plus a spread against SOFR, notionals exchanged at the
    # spot rate. The quote the curve was solved to reprices to the spread.
    spread = flat.par_spread(market)
    print(f"npv at zero spread  {flat.npv(market):.2f}")
    print(f"par spread          {spread:.8f}")
    discount    EUR
    discount    USD
    discount    EUR-CSA-USD
    projection  EUR-ESTR
    projection  EUR-EURIBOR-3M
    projection  USD-SOFR
    fx          EUR/USD
    swap rec. EUR        72307.51 EUR
    swap pay EUR        -72307.51 EUR
    npv at zero spread  72307.51
    par spread          -0.00150000

    Both sides of one trade value in euros and sum to zero, and the par spread is the five-year basis quote the curve was solved from.

    How it's priced

    On notional NN in the quoted currency and NX0N X_0 in the other, with X0X_0 the exchange rate on the trade, each leg's value is its coupons plus its notional exchanges, discounted on that currency's curve under the collateral. A resetting leg also exchanges the change in its notional at each reset. Write VqV_q and VoV_o for the two legs' values to the holder, each in its own currency and signed, and XX for the spot in units of the other currency per unit of the quoted one. The swap's value is

    V=Vq+VoXV = V_q + \frac{V_o}{X}

    in the quoted leg's currency, whichever side the holder takes. Paying the quoted leg flips the sign of both legs, so the two sides of one trade sum to zero as read.

    A book reports in its own P&L currency pp. The value, and each leg, converts at the spot Xq→pX_{q \to p}:

    Vp=V Xq→p=Vq Xq→p+Vo Xo→p.V_p = V\,X_{q \to p} = V_q\,X_{q \to p} + V_o\,X_{o \to p}.

    The legs, each in its own currency, sum to the value in any currency. The par spread makes V=0V = 0, so at par the two legs cancel. Why a dollar-collateralised swap discounts its euro flows on a curve implied from basis quotes, and how each leg is valued by hand, is covered in how rates products work.

    API

    Symbol Meaning Rust Python
    the market both curves, spot, and the basis curve with_recipe_quotes on each key, MarketContext::with_fx_rates MarketContext.with_recipe_quotes, MarketContext.with_fx_rates
    the trade a swap on its template CrossCurrencySwap::from_template Template.cross_currency_swap
    other side pays the quoted leg CrossCurrencySwap::reversed pays_quoted_leg=True
    the npv currency the quoted leg's AnyInstrument::currency Instrument.npv_ccy
    VqV_q, VoV_o and their sum in each currency the value by leg AnyInstrument::leg_npvs(market, pnl) Instrument.leg_npvs(market, pnl_ccy=)
    pp the P&L currency, default the npv currency the pnl argument pnl_ccy=
    a constant notional no leg resets XccySwapTemplate::with_resetting_leg(None) Template.with_resetting_leg(None)
    a leg on another index one leg projects on another index the leg's with_index, then XccySwapTemplate::with_leg Template.with_leg_index(leg, index)
    s∗s^\ast the par spread AnyInstrument::par_spread Instrument.par_spread

    The leg_npvs columns are side, ccy (the leg's currency), npv_local (in it), npv_ccy and npv (in the swap's currency), and pnl_ccy and npv_pnl (in the P&L currency).

    Template.cross_currency_swap takes:

    Argument Type Default Meaning
    effective date required the first notional exchange
    termination date required the final notional exchange
    notional float required in the quoted leg's currency
    fx_rate float required units of the other leg's currency per unit of the quoted leg's
    spread float or None None the quote on a floating quoted leg; give this or fixed_rate
    fixed_rate float or None None the quote on a fixed quoted leg
    pays_quoted_leg bool False book the side paying the quoted leg; Rust .reversed()
    own_curves bool False no CSA: discount each leg on its own currency's curve

    In Rust, CrossCurrencySwap::from_template(template, effective, termination, notional, fx_rate, quote) takes the same values positionally, quote being the spread or the fixed rate.

    Defaults and limits

    • Collateral: the recipe for the key USD-CSA-EUR builds the euro curve collateralised in dollars from basis quotes; see cross-currency calibration. From basis quotes alone it files that curve under EUR-CSA-USD, the key a euro leg asks for, as the output above shows. Given the cross sheet's FX swaps too, it also files USD-CSA-EUR, as on counterparties and CSAs.
    • Resets: the EUR-USD basis template resets the SOFR leg's notional to the forward FX rate every period. with_resetting_leg(None) keeps both notionals constant.
    • Legs by hand: for a resetting swap, leg_npvs is the only public view of each leg, because the resets are not among the leg's coupons.
    • Another index: with_leg_index names the leg by its currency, or as "leg1" or "leg2", and takes a registry code or an Index value. The leg's kind follows the index, so an overnight leg moved onto a term index becomes a term leg; every other convention is kept.
    • Templates: both legs, and which one is quoted, come from the template; see rates conventions.

    More examples

    This program shows the value by leg in a P&L currency, the same swap without resets, a fixed-float TRY against USD swap, and the EUR leg moved onto EURIBOR 3M:

    Rust
    use helixrs::statics::templates::xccy::{XccyLegSide, XccySwapTemplate};
    use chrono::NaiveDate;
    use helixio_docs_sheets::{EUR_CLOSE, SOFR_CLOSE_DATE, Sheet, USD_SOFR_CLOSE};
    use helixrs::{
        AnyInstrument, AnyTemplate, CollateralSpec, CrossCurrencySwap, CurrencyPair, FxRates,
        MarketContext, QuoteSheet, Records, template,
    };
    
    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 BASIS: Sheet = &[
        ("EUR-USD-ESTR-SOFR-BASIS", "1Y", -0.0010),
        ("EUR-USD-ESTR-SOFR-BASIS", "2Y", -0.0012),
        ("EUR-USD-ESTR-SOFR-BASIS", "5Y", -0.0015),
        ("EUR-USD-ESTR-SOFR-BASIS", "10Y", -0.0016),
    ];
    const TRY_OIS: Sheet = &[
        ("TRY-TLREF-OIS", "1Y", 0.38),
        ("TRY-TLREF-OIS", "2Y", 0.34),
        ("TRY-TLREF-OIS", "5Y", 0.30),
    ];
    const TRY_CCS: Sheet = &[
        ("TRY-USD-CCS", "1Y", 0.39),
        ("TRY-USD-CCS", "2Y", 0.35),
        ("TRY-USD-CCS", "5Y", 0.31),
    ];
    
    fn key(spelling: &str) -> Outcome<CollateralSpec> {
        Ok(spelling.parse()?)
    }
    
    fn spot(base: &str, quote: &str, rate: f64) -> Outcome<FxRates> {
        Ok(FxRates::from_quotes(&[(
            CurrencyPair::new(base.parse()?, quote.parse()?),
            rate,
        )])?)
    }
    
    fn xccy(
        code: &str,
        end: NaiveDate,
        notional: f64,
        fx_rate: f64,
        quote: f64,
        pays_quoted_leg: bool,
        resets: bool,
    ) -> Outcome<AnyInstrument> {
        let AnyTemplate::Xccy(mut t) = template(code)? else {
            unreachable!("a cross-currency template")
        };
        if !resets {
            t = t.with_resetting_leg(None);
        }
        let mut swap =
            CrossCurrencySwap::from_template(&t, date((2026, 10, 5)), end, notional, fx_rate, quote)?;
        if pays_quoted_leg {
            swap = swap.reversed();
        }
        Ok(AnyInstrument::CrossCurrency(Box::new(swap)))
    }
    
    fn text<'r>(rows: &'r Records, name: &str) -> &'r [String] {
        rows[name].text().expect("a text column")
    }
    
    fn numbers<'r>(rows: &'r Records, name: &str) -> &'r [f64] {
        rows[name].numbers().expect("a number column")
    }
    
    fn show(rows: &Records) {
        let header = |name: &str| format!("in {}", text(rows, name)[0]);
        println!(
            "{:<8} {:<4} {:>12} {:>12} {:>12}",
            "side", "ccy", "local", header("npv_ccy"), header("pnl_ccy")
        );
        let (side, ccy) = (text(rows, "side"), text(rows, "ccy"));
        let (local, npv, pnl) = (numbers(rows, "npv_local"), numbers(rows, "npv"), numbers(rows, "npv_pnl"));
        for i in 0..side.len() {
            println!("{:<8} {:<4} {:>12.2} {:>12.2} {:>12.2}", side[i], ccy[i], local[i], npv[i], pnl[i]);
        }
    }
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
        let market = MarketContext::as_of(close)
            .with_recipe_quotes(
                &key("USD")?,
                &QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?,
            )?
            .with_recipe_quotes(
                &key("EUR")?,
                &QuoteSheet::from_rows(EUR_CLOSE.iter().copied())?,
            )?
            .with_fx_rates(spot("EUR", "USD", 1.08)?)
            .with_recipe_quotes(
                &key("USD-CSA-EUR")?,
                &QuoteSheet::from_rows(BASIS.iter().copied())?,
            )?;
        let basis_swap = |spread: f64, pays: bool, resets: bool| {
            xccy(
                "EUR-USD-ESTR-SOFR-BASIS",
                date((2031, 10, 5)),
                10_000_000.0,
                1.08,
                spread,
                pays,
                resets,
            )
        };
        let usd = "USD".parse()?;
    
        let flat = basis_swap(0.0, false, true)?;
        let spread = flat.par_spread(&market)?;
    
        // The value by leg: each leg in its own currency, in the swap's and in a P&L
        // currency, dollars here, both converted at the spot. The pay leg is the SOFR
        // leg, with its notional exchanges and resets. Each column sums to the value.
        let rows = flat.leg_npvs(&market, Some(usd))?;
        show(&rows);
        let npv = numbers(&rows, "npv");
    
        // At the par spread the legs cancel: what the €STR leg is worth in euros, the
        // SOFR leg is worth too.
        let par_rows = basis_swap(spread, false, true)?.leg_npvs(&market, None)?;
        show(&par_rows);
    
        // The template resets the SOFR leg's notional to the forward FX rate every
        // period. Without resets the notional stays at 10.8 million dollars: the €STR
        // leg is the same, and only the SOFR leg moves.
        let constant = basis_swap(0.0, false, false)?;
        let fixed_rows = constant.leg_npvs(&market, None)?;
        show(&fixed_rows);
        let fixed_npv = numbers(&fixed_rows, "npv");
        println!("resets add {:.2} EUR to the SOFR leg", npv[0] - fixed_npv[0]);
    
        // A fixed-float swap on a market whose local leg is fixed: 41.5 million lira at
        // 36% against a million dollars on SOFR, discounted on the TRY curve
        // collateralised in dollars. The lira leg is quoted, so the swap values in lira
        // and its fx_rate is dollars per lira. By leg, in dollars.
        let try_market = MarketContext::as_of(close)
            .with_recipe_quotes(
                &key("USD")?,
                &QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?,
            )?
            .with_recipe_quotes(
                &key("TRY")?,
                &QuoteSheet::from_rows(TRY_OIS.iter().copied())?,
            )?
            .with_fx_rates(spot("USD", "TRY", 41.5)?)
            .with_recipe_quotes(
                &key("TRY-CSA-USD")?,
                &QuoteSheet::from_rows(TRY_CCS.iter().copied())?,
            )?;
        let lira = xccy("TRY-USD-CCS", date((2028, 10, 5)), 41_500_000.0, 1.0 / 41.5, 0.36, false, true)?;
        let try_rows = lira.leg_npvs(&try_market, Some(usd))?;
        show(&try_rows);
    
        // A template's leg can project on another index: take the EUR leg, move it
        // onto the index with `with_index`, and put it back with `with_leg`; the
        // leg's kind follows the index. The EUR
        // leg on EURIBOR 3M instead of €STR receives a rate that fixes higher, so the
        // par basis that makes the swap fair falls by about the €STR-EURIBOR basis.
        let AnyTemplate::Xccy(eurusd) = template("EUR-USD-ESTR-SOFR-BASIS")? else {
            unreachable!("a cross-currency template")
        };
        let at_par = |t: &XccySwapTemplate| -> Outcome<f64> {
            let swap = CrossCurrencySwap::from_template(
                t,
                date((2026, 10, 5)),
                date((2031, 10, 5)),
                10_000_000.0,
                1.08,
                0.0,
            )?;
            Ok(AnyInstrument::CrossCurrency(Box::new(swap)).par_spread(&market)?)
        };
        let side = [XccyLegSide::Leg1, XccyLegSide::Leg2]
            .into_iter()
            .find(|s| eurusd.leg(*s).currency == "EUR")
            .expect("a EUR leg");
        let on_euribor_leg = eurusd.leg(side).clone().with_index("EUR-EURIBOR-3M")?;
        let on_euribor_template = eurusd.clone().with_leg(side, on_euribor_leg)?;
        let (on_estr, on_euribor) = (at_par(&eurusd)?, at_par(&on_euribor_template)?);
        println!("par basis, EUR leg on ESTR          {:.6} bp", on_estr * 10_000.0);
        println!("par basis, EUR leg on EURIBOR 3M    {:.6} bp", on_euribor * 10_000.0);
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    from desk_sheets import EUR_CLOSE, SOFR_CLOSE_DATE, USD_SOFR_CLOSE
    
    BASIS = {"EUR-USD-ESTR-SOFR-BASIS": {"1Y": -0.0010, "2Y": -0.0012, "5Y": -0.0015, "10Y": -0.0016}}
    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.08)])
        .with_recipe_quotes("USD-CSA-EUR", BASIS)
    )
    eurusd = hlx.templates.get("EUR-USD-ESTR-SOFR-BASIS")
    
    def basis_swap(spread, pays_quoted_leg=False, template=eurusd):
        return template.cross_currency_swap(
            dt.date(2026, 10, 5),
            dt.date(2031, 10, 5),
            notional=10_000_000.0,
            fx_rate=1.08,
            spread=spread,
            pays_quoted_leg=pays_quoted_leg,
        )
    
    flat = basis_swap(0.0)
    spread = flat.par_spread(market)
    
    def show(rows):
        print(f"{'side':<8} {'ccy':<4} {'local':>12} {'in ' + rows['npv_ccy'][0]:>12} {'in ' + rows['pnl_ccy'][0]:>12}")
        for side, ccy, local, npv, pnl in zip(rows["side"], rows["ccy"], rows["npv_local"], rows["npv"], rows["npv_pnl"]):
            print(f"{side:<8} {ccy:<4} {local:>12.2f} {npv:>12.2f} {pnl:>12.2f}")
    
    # The value by leg: each leg in its own currency, in the swap's and in a P&L
    # currency, dollars here, both converted at the spot. The pay leg is the SOFR
    # leg, with its notional exchanges and resets. Each column sums to the value.
    rows = flat.leg_npvs(market, pnl_ccy="USD").to_dict()
    show(rows)
    
    # At the par spread the legs cancel: what the €STR leg is worth in euros, the
    # SOFR leg is worth too.
    par_rows = basis_swap(spread).leg_npvs(market).to_dict()
    show(par_rows)
    
    # The template resets the SOFR leg's notional to the forward FX rate every
    # period. Without resets the notional stays at 10.8 million dollars: the €STR
    # leg is the same, and only the SOFR leg moves.
    constant = basis_swap(0.0, template=eurusd.with_resetting_leg(None))
    fixed_rows = constant.leg_npvs(market).to_dict()
    show(fixed_rows)
    print(f"resets add {rows['npv'][0] - fixed_rows['npv'][0]:.2f} EUR to the SOFR leg")
    
    # A fixed-float swap on a market whose local leg is fixed: 41.5 million lira at
    # 36% against a million dollars on SOFR, discounted on the TRY curve
    # collateralised in dollars. The lira leg is quoted, so the swap values in lira
    # and its fx_rate is dollars per lira. By leg, in dollars.
    try_market = (
        hlx.MarketContext(SOFR_CLOSE_DATE)
        .with_recipe_quotes("USD", USD_SOFR_CLOSE)
        .with_recipe_quotes("TRY", {"TRY-TLREF-OIS": {"1Y": 0.38, "2Y": 0.34, "5Y": 0.30}})
        .with_fx_rates([("USD", "TRY", 41.5)])
        .with_recipe_quotes("TRY-CSA-USD", {"TRY-USD-CCS": {"1Y": 0.39, "2Y": 0.35, "5Y": 0.31}})
    )
    lira = hlx.templates.get("TRY-USD-CCS").cross_currency_swap(
        dt.date(2026, 10, 5), dt.date(2028, 10, 5), notional=41_500_000.0, fx_rate=1 / 41.5, fixed_rate=0.36
    )
    try_rows = lira.leg_npvs(try_market, pnl_ccy="USD").to_dict()
    show(try_rows)
    
    # A template's leg can project on another index: with_leg_index names the leg,
    # by its currency, and the index, and the leg's kind follows the index. The EUR
    # leg on EURIBOR 3M instead of €STR receives a rate that fixes higher, so the
    # par basis that makes the swap fair falls by about the €STR-EURIBOR basis.
    def at_par(template):
        swap = template.cross_currency_swap(
            dt.date(2026, 10, 5), dt.date(2031, 10, 5), notional=10_000_000.0, fx_rate=1.08, spread=0.0
        )
        return swap.par_spread(market)
    
    on_estr = at_par(eurusd)
    on_euribor = at_par(eurusd.with_leg_index("EUR", "EUR-EURIBOR-3M"))
    print(f"par basis, EUR leg on ESTR          {on_estr * 10_000:.6f} bp")
    print(f"par basis, EUR leg on EURIBOR 3M    {on_euribor * 10_000:.6f} bp")
    side     ccy         local       in EUR       in USD
    pay      USD        868.75       804.40       868.75
    receive  EUR      71503.11     71503.11     77223.36
    side     ccy         local       in EUR       in EUR
    pay      USD        868.75       804.40       804.40
    receive  EUR       -804.40      -804.40      -804.40
    side     ccy         local       in EUR       in EUR
    pay      USD        814.62       754.28       754.28
    receive  EUR      71503.11     71503.11     71503.11
    resets add 50.12 EUR to the SOFR leg
    side     ccy         local       in TRY       in USD
    pay      USD         26.10      1083.09        26.10
    receive  TRY     529644.80    529644.80     12762.53
    par basis, EUR leg on ESTR          -15.000000 bp
    par basis, EUR leg on EURIBOR 3M    -28.592493 bp

    Each column of the leg table sums to the swap's value; at the par spread the legs cancel. Without resets only the SOFR leg moves. On EURIBOR 3M the EUR leg receives a rate that fixes higher, so the par basis falls.

    See also

    • Source: crates/helixrs/src/instruments/rates.rs
    • Related pages:
      • Cross-currency calibration, for the basis curve.
      • Non-deliverable swaps, for swaps settled in dollars.

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