Esc

    The API is under testing and may change before release.

    Fixings: prints and projections

    If you value trades that have already started resetting, this page explains where a floating coupon stops reading published prints and starts reading the curve's projection, and which version of a print it reads when a print has been corrected.

    Prints before the valuation date, projections from it

    A trade that has started accruing has already reset on some days. On the close, the print for the close itself isn't out yet: SOFR for a day is published the next morning. So a valuation on day tt reads prints for the days before tt and projects from tt on. For a coupon resetting on days d1,…,dnd_1, \dots, d_n, the prints it needs are

    { dj:dj<t },\{\, d_j : d_j < t \,\},

    and every dj≥td_j \ge t is projected. Moving the valuation to the next business day adds exactly the reset on tt to what it needs. Which days are prints depends on the valuation date alone, so an instrument can tell you what it needs before you have any market at all, with fixings_needed.

    Two ways a market learns a print

    A market learns a print by fetching it from a data source, or by having it pinned by hand. A desk pins a print to override a source it knows is wrong, so for a print with several versions the value read on tt is the latest pinned version known by tt, and otherwise the latest fetched one. Both are kept: the fixing history lists every version with where it came from, so you can see what was overridden.

    A print that arrives late, or is corrected, changes the value of every trade that read it. A paid floating leg is worth less when a print it pays rises, so a higher corrected print lowers the value of a swap paying SOFR.

    A seasoned swap, fetched and then corrected

    This program asks a two-year SOFR swap, traded a week before the close, which prints it needs on the close and on the next day. It then brings the prints in through the market's demand, as a fetch would, and pins a corrected 30 September print over the fetched one. It reads its quotes and prints from the desk sheets.

    Rust
    use chrono::NaiveDate;
    use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_LADDER, USD_SOFR_PRINTS};
    use helixrs::{
        Book, FixingKey, FixingStore, Locator, MarketContext, MarketData, StripQuotes, 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 joined(needs: &[(String, NaiveDate)]) -> String {
        needs
            .iter()
            .map(|(_, d)| d.to_string())
            .collect::<Vec<_>>()
            .join(", ")
    }
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
        let next = date((2026, 10, 2));
        // A two-year SOFR swap traded on 24 September, receiving 4.50% fixed: it pays
        // SOFR from Monday 28 September.
        let swap = template("USD-SOFR-OIS")?
            .swap(date((2026, 9, 24)))?
            .with_maturity("2Y".parse::<Termination>()?)?
            .with_notional(10_000_000.0)
            .with_fixed_rate(0.0450)
            .build()?;
    
        // On the close, the reset of the close itself is projected: the print comes out
        // the next morning. A day later it is a print the valuation needs.
        let on_close = swap.fixings_needed(close);
        let next_day = swap.fixings_needed(next);
        println!("on {close}  {}", joined(&on_close));
        println!("on {next}  {}", joined(&next_day));
    
        // The prints arrive through the market's demand, as a fetch would bring them.
        let book = Book::new().add("SOFR-2Y", swap.clone(), 1.0);
        let offline = MarketContext::as_of(close);
        let demand = offline.demand(&book)?;
        let mut strips = Vec::new();
        for asked in &demand.strips {
            let mut entries = Vec::new();
            for (_, locator, quote) in USD_SOFR_LADDER.iter().filter(|(t, _, _)| *t == asked.template) {
                entries.push((Locator::parse(locator)?, *quote));
            }
            strips.push(StripQuotes {
                template: asked.template.clone(),
                observed: close,
                entries,
            });
        }
        let answer = MarketData {
            strips,
            fixings: USD_SOFR_PRINTS
                .iter()
                .map(|(series, day, value)| ((*series).to_owned(), date(*day), *value))
                .collect(),
            fx: Vec::new(),
        };
        let fetched = offline.with_market_data(&demand, &answer)?;
        println!("npv on fetched prints  {:.2}", swap.npv(&fetched)?);
    
        // A corrected 30 September print, pinned by hand, is read over the fetched one;
        // the fetched version stays in the history.
        let correction = [(
            FixingKey::Typed("USD-SOFR".to_owned()),
            date((2026, 9, 30)),
            0.0395,
        )];
        let corrected = fetched
            .clone()
            .with_fixings(FixingStore::from_observations_checked(&correction)?);
        for (_, day, _, pinned, value) in corrected.fixing_history().versions() {
            let source = if pinned { "pinned" } else { "fetched" };
            println!("{day}  {source:<7}  {value:.4}");
        }
        let read = corrected.try_fixing("USD-SOFR", date((2026, 9, 30))).expect("a print");
        println!("read on 2026-09-30  {read:.4}");
        println!("npv on the correction  {:.2}", swap.npv(&corrected)?);
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_LADDER, USD_SOFR_PRINTS
    
    # A two-year SOFR swap traded on 24 September, receiving 4.50% fixed: it pays
    # SOFR from Monday 28 September.
    swap = (
        hlx.templates.get("USD-SOFR-OIS")
        .swap(dt.date(2026, 9, 24))
        .with_maturity("2Y")
        .with_notional(10_000_000.0)
        .with_fixed_rate(0.0450)
        .build()
    )
    
    # On the close, the reset of the close itself is projected: the print comes out
    # the next morning. A day later it is a print the valuation needs.
    on_close = swap.fixings_needed(SOFR_CLOSE_DATE)
    next_day = swap.fixings_needed(dt.date(2026, 10, 2))
    print(f"on {SOFR_CLOSE_DATE}  {', '.join(d.isoformat() for _, d in on_close)}")
    print(f"on 2026-10-02  {', '.join(d.isoformat() for _, d in next_day)}")
    
    # The prints arrive through the market's demand, as a fetch would bring them.
    book = hlx.Book().add("SOFR-2Y", swap, 1.0)
    offline = hlx.MarketContext(SOFR_CLOSE_DATE)
    demand = offline.demand(book)
    answer = hlx.MarketData(
        strips=[
            (template, SOFR_CLOSE_DATE, list(USD_SOFR_LADDER[template].items()))
            for template in demand.strips.to_dict()["template"]
        ],
        fixings=list(USD_SOFR_PRINTS),
    )
    fetched = offline.with_market_data(demand, answer)
    print(f"npv on fetched prints  {swap.npv(fetched):.2f}")
    
    # A corrected 30 September print, pinned by hand, is read over the fetched one;
    # the fetched version stays in the history.
    corrected = fetched.with_fixings([("USD-SOFR", dt.date(2026, 9, 30), 0.0395)])
    history = corrected.fixing_history().to_dict()
    for day, source, value in zip(history["date"], history["source"], history["value"]):
        print(f"{day}  {source:<7}  {value:.4f}")
    read = corrected.fixing("USD-SOFR", dt.date(2026, 9, 30))
    print(f"read on 2026-09-30  {read:.4f}")
    print(f"npv on the correction  {swap.npv(corrected):.2f}")
    on 2026-10-01  2026-09-28, 2026-09-29, 2026-09-30
    on 2026-10-02  2026-09-28, 2026-09-29, 2026-09-30, 2026-10-01
    npv on fetched prints  -15533.96
    2026-09-28  fetched  0.0390
    2026-09-29  fetched  0.0388
    2026-09-30  fetched  0.0390
    2026-09-30  pinned   0.0395
    read on 2026-09-30  0.0395
    npv on the correction  -15547.84

    On the close the swap needs three prints, and a day later it also needs the close's own. After the correction, the history holds both versions of 30 September, the pinned one is what's read, and the swap, which pays SOFR, is worth less.

    From formula to API

    Symbol Meaning Rust Python
    {dj:dj<t}\{ d_j : d_j < t \} the prints a valuation on tt needs AnyInstrument::fixings_needed Instrument.fixings_needed
    — prints installed from a fetch MarketContext::with_market_data MarketContext.with_market_data
    — prints pinned by hand MarketContext::with_fixings MarketContext.with_fixings
    — the version a valuation reads MarketContext::try_fixing MarketContext.fixing
    — every version, with its source MarketContext::fixing_history MarketContext.fixing_history

    See also

    • Source: crates/helixrs/src/marketdata/fixings.rs, crates/helixrs/src/market/store.rs, crates/helixrs/src/market/source.rs
    • Related pages:
      • The dated market, for how prints are kept across closes.
      • Connect a data source, for fetching prints.
      • Mark a daily close, for pinning each day's prints.
      • Cashflow records, for each coupon's rate as read.

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