How rates products work
You'll come away knowing how each rates product trades, how it is quoted, and what its price depends on, so the reference pages read as a list of arguments rather than a puzzle. Nothing here is a step you need to follow; the how-to guides do that.
What every rates product shares
Almost every rates product is quoted by a par rate: the fixed rate, spread or strike at which it is worth nothing today. Quoting the fair rate rather than a price keeps quotes comparable across notionals and dates, and it is why the same quotes can build a curve: a curve is solved so that each quoted instrument prices back to its quote.
Two curves meet in most trades. A discount curve turns a future payment into today's money, and since collateralised trading became the norm, it is the curve of the overnight rate the collateral earns: €STR for euros, SOFR for dollars. A projection curve gives the expected fixing of the index a floating leg pays, such as EURIBOR 3M. When the index is the overnight rate itself, one curve does both jobs.
A swap's value is linear in its fixed rate. The slope, the value of one more unit of fixed rate, is the annuity: the sum of the fixed leg's accruals, each discounted. That is why one basis point of fixed rate moves a swap's value by the same amount whatever the rate.
Forward-rate agreements
FRAs trade on term indices, mostly EURIBOR, by their start and end in months from spot: a 3x6 fixes three months out on the three-month rate. They settle at the start of the period, with the difference discounted over the period at the fixing. Because what you are really trading is the rate for that one period, FRAs are quoted by the forward rate itself.
Interest rate swaps
A EURIBOR swap pays an annual fixed leg against a quarterly or semiannual floating leg that fixes on EURIBOR. It is quoted by its par rate. It discounts on the €STR curve while its floating leg projects EURIBOR from its own curve, so a EUR close carries two sets of quotes: €STR swaps and EURIBOR swaps. The legs' conventions are the market's standing agreement, held by the template, so a trade only states its start, length, notional, fixed rate and side.
Overnight indexed swaps
An OIS floating leg compounds an overnight rate, such as SOFR, €STR or SONIA, over each period, and pays once at the period end after a short payment lag. Desks quote OIS by par rate from a few weeks to fifty years, and the same quotes build the discount curve, so a SOFR swap from spot reprices its own quote exactly. A swap traded before today has accrued over the days since its start, and each of those days' rates is published the next morning, so a seasoned swap needs those prints.
Money market deposits
Deposits are the shortest instruments on a curve: overnight, a week, a month, three. They accrue simple interest on the market's money-market day count and pay principal and interest at maturity, which makes them the curve's first few points.
Zero-coupon swaps
Zero-coupon swaps suit cashflows that arrive once: a pension liability, a structured note's redemption, an inflation hedge's fixed side. The fixed leg accrues, compounded at the quoted rate, and pays everything at maturity. The floating leg pays the term index each period, or compounds it to maturity, as the template says.
Tenor basis swaps
A term rate such as EURIBOR carries bank credit and liquidity that the overnight €STR does not, so the two curves differ, and the difference trades. A basis swap pays the term rate against the compounded overnight rate plus a spread, and is quoted by the spread at which it is fair.
Cross-currency swaps
Most cross-currency swaps are floating against floating: €STR plus a spread against SOFR, with the
spread, the cross-currency basis, quoted by tenor. A swap collateralised in dollars discounts its
euro flows on a euro curve implied from those basis quotes, not on the €STR curve itself; the
recipe for the key USD-CSA-EUR builds that curve. Some markets quote the local leg fixed instead,
as TRY and CNH do against USD.
Each leg's value is just that leg's payments discounted on the curve its currency is collateralised on. The cross-currency swap reference values a swap the short way. The program below rebuilds each of that swap's legs by hand, from its own coupons and curve, and checks the two agree. It reads its quotes from the desk sheets.
By hand, leg by leg, on the curve each currency discounts on:
use chrono::NaiveDate;
use helixio_docs_sheets::{EUR_CLOSE, SOFR_CLOSE_DATE, Sheet, USD_SOFR_CLOSE};
use helixrs::{
AnyInstrument, AnyTemplate, CollateralSpec, CrossCurrencySwap, CurrencyPair, Direction,
FxRates, Leg, 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 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(FxRates::from_quotes(&[(CurrencyPair::new("EUR".parse()?, "USD".parse()?), 1.08)])?)
.with_recipe_quotes(&key("USD-CSA-EUR")?, &QuoteSheet::from_rows(BASIS.iter().copied())?)?;
let AnyTemplate::Xccy(terms) = template("EUR-USD-ESTR-SOFR-BASIS")? else {
unreachable!("a cross-currency template")
};
let constant = AnyInstrument::CrossCurrency(Box::new(CrossCurrencySwap::from_template(
&terms.with_resetting_leg(None),
date((2026, 10, 5)),
date((2031, 10, 5)),
10_000_000.0,
1.08,
0.0,
)?));
// Receive the euro leg on the euro curve under dollar collateral; pay the
// dollar leg on the dollar curve. Each is the leg_npvs row in its currency.
let rows = constant.leg_npvs(&market, None)?;
let (ccys, local) = (
rows["ccy"].text().expect("a text column"),
rows["npv_local"].numbers().expect("a number column"),
);
let mut worst = 0.0_f64;
for leg in constant.legs().expect("a swap has legs") {
let currency = leg.currency().expect("a leg with coupons").code().to_owned();
let (direction, curve) = match currency.as_str() {
"EUR" => (Direction::Receive, "EUR-CSA-USD"),
_ => (Direction::Pay, "USD"),
};
let value = Leg::new(leg.coupons().to_vec(), direction).npv_on(&market, &key(curve)?)?;
let row = ccys.iter().position(|c| *c == currency).expect("a row per leg");
let label = format!("discount({curve})");
println!("{currency} leg on {label:<22} {value:>10.2}");
worst = worst.max((value - local[row]).abs());
}
println!(
"legs match the swap's own valuation: {}",
if worst < 1e-6 { "yes" } else { "no" }
);
Ok(())
}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)
)
constant = (
hlx.templates.get("EUR-USD-ESTR-SOFR-BASIS")
.with_resetting_leg(None)
.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)
)
# Receive the euro leg on the euro curve under dollar collateral; pay the
# dollar leg on the dollar curve. Each is the leg_npvs row in its currency.
rows = constant.leg_npvs(market).to_dict()
on_its_curve = {"EUR": ("Receive", "EUR-CSA-USD"), "USD": ("Pay", "USD")}
worst = 0.0
for leg in constant.legs():
direction, curve = on_its_curve[leg.currency()]
value = hlx.Leg(leg.coupons(), direction, curve).npv(market)
row = rows["ccy"].index(leg.currency())
label = f"discount({curve})"
print(f"{leg.currency()} leg on {label:<22} {value:>10.2f}")
worst = max(worst, abs(value - rows["npv_local"][row]))
print(f"legs match the swap's own valuation: {'yes' if worst < 1e-6 else 'no'}")USD leg on discount(USD) 814.62
EUR leg on discount(EUR-CSA-USD) 71503.11
legs match the swap's own valuation: yes
Rate futures
SOFR futures list for each IMM quarter, quoted as a price of 100 minus the rate, with a fixed value per basis point per contract. The three-month contract settles on compounded SOFR over its reference quarter. Because the position is margined every day, gains are reinvested and losses funded at the rates of the day, which makes the futures rate higher than the forward rate over the same period. That gap is the convexity adjustment. It grows with the time to the contract and with rate volatility.
Inflation swaps
The zero-coupon inflation swap is the benchmark: at maturity one side pays the index's growth over the life, the other the fixed rate compounded. Its par rate is the market's breakeven inflation. The year-on-year swap pays each year's growth against an annual fixed coupon. Both read the index with a lag, typically three months, because a month's index is published weeks after the month.
CMS swaps
A CMS leg lets an investor receive the long end of the curve on a short reset: a 10-year rate paid quarterly. Because each coupon pays the swap rate once, at the period end, rather than as a swap's string of payments, its expected value sits above the forward swap rate. That gap is the CMS convexity adjustment, read from a normal swaption vol and a mean reversion.
Caps and floors
A borrower paying a floating rate buys a cap to limit what it pays; a lender buys a floor. Each caplet pays at the period end the excess of the period's rate over the strike, times the accrual and notional. Caps on SOFR are struck on each quarter's compounded rate, which is not known until the quarter ends, so the option is on an average and has less variance than a term rate's. Cap vols are quoted by expiry, as normal vols in rate units.
Swaptions
A payer swaption gives the right to pay fixed; a receiver, to receive it. The underlying is a forward-starting swap whose par rate at expiry decides whether to exercise. Swaption vols are quoted on a grid of expiry and underlying tenor, mostly as normal vols in rate units, since rates can be near or below zero. A 1Y5Y expires in one year into a five-year swap.
Bermudan swaptions
Bermudans appear wherever a borrower can repay early: a callable bond's issuer is long a receiver Bermudan on the bond's coupon. Exercise is usually allowed on coupon dates after a lockout. With more than one exercise date, the value depends on how rates move between the dates, so a short-rate model is needed. You choose its mean reversion, and its volatility is fitted to the co-terminal European swaptions; see Hull-White.
Callable swaps
A borrower who issues a callable bond and swaps it to floating wants the swap to end when the bond does, so dealers write swaps one side may cancel. A holder paying fixed cancels when rates fall, because the fixed rate it pays is then above the market's; a counterparty with the right cancels when rates rise. The right is an option, and its price is the difference between the callable swap and the plain one, which is why a callable swap is valued as the swap plus a Bermudan on the same dates.
Inflation caps and floors
A zero-coupon inflation cap or floor is one option at maturity on the compounded growth over the life, struck as an annualised rate. A year-on-year cap or floor is a strip of options on each year's growth. Each reads its own vol surface: a CPI surface for the zero-coupon options and a year-on-year surface for the strips, both by expiry and strike.
See also
- Source:
crates/helixrs/src/instruments/rates.rs - Related pages:
- Rates products, the reference page for each product.
- Rates conventions, each market's conventions.
- Rate vol surface, how cap and swaption vols are quoted.
- Inflation curves, the index level curve inflation products read.
- Listed futures, the contract rules for rate futures.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.