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 reads prints for the days before and projects from on. For a coupon resetting on days , the prints it needs are
and every is projected. Moving the valuation to the next business day adds exactly
the reset on 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 is the latest pinned version known by , 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.
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(())
}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 |
|---|---|---|---|
| the prints a valuation on 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.