Esc

    The API is under testing and may change before release.

    Overnight indexed swap

    This page lists everything you need to book and value an overnight indexed swap (OIS), fresh, seasoned or forward-starting: the builder's arguments and defaults, how it's priced, and an example you can run. An OIS exchanges a fixed rate for the compounded daily overnight rate, such as SOFR.

    Quick example

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

    This prices a ten-year SOFR swap at par, lists the prints a swap traded on 24 September needs and values it, and reads the par rate of a two-year swap starting in two years. The market comes from the USD SOFR close and its prints on the quote sheets and recipes.

    Rust
    use chrono::NaiveDate;
    use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_CLOSE, USD_SOFR_PRINTS};
    use helixrs::{
        CollateralSpec, FixingKey, FixingStore, MarketContext, QuoteSheet, Side, Termination,
        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")
    }
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
        let prints: Vec<(FixingKey, NaiveDate, f64)> = USD_SOFR_PRINTS
            .iter()
            .map(|(index, day, value)| (FixingKey::Typed((*index).to_owned()), date(*day), *value))
            .collect();
        let market = MarketContext::as_of(close)
            .with_fixings(FixingStore::from_observations_checked(&prints)?)
            .with_recipe_quotes(
                &CollateralSpec::Currency("USD".parse()?),
                &QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?,
            )?;
        let sofr = template("USD-SOFR-OIS")?;
    
        // A ten-year swap from spot is one of the sheet's quotes, so it prices to par.
        let fresh = sofr
            .swap(close)?
            .with_maturity("10Y".parse::<Termination>()?)?
            .with_notional(25_000_000.0)
            .at_par(&market)?;
        let par = fresh.par_rate(&market)?;
        println!("10Y par     {par:.8}");
    
        // Traded on 24 September, the swap has accrued since Monday 28 September and
        // needs the three prints published since then — not the close's own print.
        let seasoned = sofr
            .swap(date((2026, 9, 24)))?
            .with_maturity("5Y".parse::<Termination>()?)?
            .with_notional(50_000_000.0)
            .with_fixed_rate(0.0466)
            .with_side(Side::Payer)
            .build()?;
        let needed = seasoned.fixings_needed(close);
        for (index, day) in &needed {
            println!("needs       {index} {day}");
        }
        let npv = seasoned.npv(&market)?;
        println!("npv         {npv:.2}");
    
        // A forward start is spelled as two tenors: two years, starting in two.
        let forward = sofr
            .swap(close)?
            .with_maturity("2Y2Y".parse::<Termination>()?)?
            .with_notional(10_000_000.0)
            .with_fixed_rate(0.0)
            .build()?;
        let dates = forward.contract_dates();
        println!("effective   {}", dates[0].1);
        println!("termination {}", dates[1].1);
        println!("2Y2Y par    {:.8}", forward.par_rate(&market)?);
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_CLOSE, USD_SOFR_PRINTS
    
    market = (
        hlx.MarketContext(SOFR_CLOSE_DATE)
        .with_fixings(USD_SOFR_PRINTS)
        .with_recipe_quotes("USD", USD_SOFR_CLOSE)
    )
    sofr = hlx.templates.get("USD-SOFR-OIS")
    
    # A ten-year swap from spot is one of the sheet's quotes, so it prices to par.
    fresh = sofr.swap(SOFR_CLOSE_DATE).with_maturity("10Y").with_notional(25_000_000.0).at_par(market)
    par = fresh.par_rate(market)
    print(f"10Y par     {par:.8f}")
    
    # Traded on 24 September, the swap has accrued since Monday 28 September and
    # needs the three prints published since then — not the close's own print.
    seasoned = (
        sofr.swap(dt.date(2026, 9, 24))
        .with_maturity("5Y")
        .with_notional(50_000_000.0)
        .with_fixed_rate(0.0466)
        .with_side("payer")
        .build()
    )
    needed = seasoned.fixings_needed(SOFR_CLOSE_DATE)
    for index, day in needed:
        print(f"needs       {index} {day}")
    npv = seasoned.npv(market)
    print(f"npv         {npv:.2f}")
    
    # A forward start is spelled as two tenors: two years, starting in two.
    forward = sofr.swap(SOFR_CLOSE_DATE).with_maturity("2Y2Y").with_notional(10_000_000.0).with_fixed_rate(0.0)
    forward = forward.build()
    dates = dict(forward.contract_dates())
    print(f"effective   {dates['effective']}")
    print(f"termination {dates['termination']}")
    print(f"2Y2Y par    {forward.par_rate(market):.8f}")
    10Y par     0.04830000
    needs       USD-SOFR 2026-09-28
    needs       USD-SOFR 2026-09-29
    needs       USD-SOFR 2026-09-30
    npv         103160.20
    effective   2028-10-05
    termination 2030-10-05
    2Y2Y par    0.04799884

    The ten-year swap reprices its own quote. The seasoned swap needs the three prints published since it started accruing, not the close's own print, which is published the next morning.

    How it's priced

    Over a period from tst_s to tet_e the floating leg pays the compounded rate

    R=1τ(∏d(1+rd δd)−1),R = \frac{1}{\tau}\left(\prod_{d} \left(1 + r_d\, \delta_d\right) - 1\right),

    where rdr_d is the overnight print for day dd and δd\delta_d its accrual. Days already published use their prints; the rest project from the curve, which for a single curve collapses to (P(ts)/P(te)−1)/τ\left(P(t_s)/P(t_e) - 1\right)/\tau over the unpublished part. The par rate, linearity in the fixed rate and the payer-receiver mirror are those of every swap: see interest rate swap. How OIS quotes build the discount curve is covered in how rates products work.

    API

    Argument Type Default Meaning
    Template.swap(trade_date) date required opens a draft on the template; Rust template(code)?.swap(d)?
    .with_maturity(end) date or tenor str "10Y" when the swap ends, as a date or a tenor from spot; also .with_termination; Rust takes a Termination
    .with_notional(notional) float 10_000_000 the size
    .with_fixed_rate(rate) float none: set it, or use .at_par(market) the fixed rate; build() without one raises ValueError
    .with_side(side) "payer" or "receiver" "receiver" which way the fixed rate flows; Rust Side::Payer / Side::Receiver
    .at_par(market) MarketContext — strikes the draft at the par rate instead of build()
    .with_csa(csa), .with_counterparty(c), .with_discount_key(key) str, Counterparty, str the template's discounting collateral and discounting; see counterparties and CSAs
    Symbol Meaning Rust Python
    rdr_d the published prints MarketContext::with_fixings MarketContext.with_fixings
    the curve SOFR discount and projection MarketContext::with_recipe_quotes on USD MarketContext.with_recipe_quotes on "USD"
    the trade a swap on its template template("USD-SOFR-OIS")?.swap(d)?…build()? templates.get("USD-SOFR-OIS").swap(d)…build()
    prints needed the accrued days AnyInstrument::fixings_needed Instrument.fixings_needed
    SS the par rate AnyInstrument::par_rate Instrument.par_rate

    Defaults and limits

    • Prints: a seasoned swap needs every print between its start and the close, exclusive of the close itself. Load them with with_fixings before you value it; fixings_needed lists them.
    • Forward starts: spell a forward start as two tenors, such as 2Y2Y: two years, starting in two.
    • Conventions: each currency's OIS conventions are in rates conventions.
    • Coupons: the compounded coupon, print by print, is built on legs and coupons and fixings: prints and projections.

    See also

    • Source: crates/helixrs/src/instruments/rates.rs
    • Related pages:
      • Interest rate swap, for the term-rate swap.
      • The dated market, for prints across dates.
      • Your first trade, for the same swap end to end.

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