Price a swap from today's quotes
You'll turn a morning's quote sheet into a market, book a five-year EURIBOR swap on it, and read what the swap is worth and the fixed rate that would make it fair.
Before you start
- helixio installed, in Rust or Python: see installing.
- The day's par quotes for the curves your swap needs. A EURIBOR swap needs two sets: €STR swaps, which discount, and EURIBOR 3M swaps, which project the floating leg.
Steps
- Write down the close date and the quotes, as a sheet keyed by template and tenor.
- Build the market with
with_recipe_quotes("EUR", ...). The EUR recipe knows which curve each template's quotes solve. - Book the swap from its template, giving only what is particular to your trade: start, length, notional, fixed rate and side.
- Ask the swap for its
npvand itspar_rateon that market.
The numbered comments in the program match these steps.
Rust
use chrono::NaiveDate;
use helixrs::{CollateralSpec, MarketContext, QuoteSheet, Side, Termination, template};
type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
fn main() -> Outcome<()> {
// 1. The close you are marking at, and the quotes on the sheet.
let close = NaiveDate::from_ymd_opt(2026, 10, 1).expect("a real date");
let eur_close = [
("EUR-ESTR-OIS", "1Y", 0.0195),
("EUR-ESTR-OIS", "2Y", 0.0200),
("EUR-ESTR-OIS", "5Y", 0.0215),
("EUR-ESTR-OIS", "10Y", 0.0240),
("EUR-ESTR-OIS", "20Y", 0.0262),
("EUR-ESTR-OIS", "30Y", 0.0265),
("EUR-EURIBOR-3M-IRS", "1Y", 0.0210),
("EUR-EURIBOR-3M-IRS", "2Y", 0.0216),
("EUR-EURIBOR-3M-IRS", "5Y", 0.0232),
("EUR-EURIBOR-3M-IRS", "10Y", 0.0257),
("EUR-EURIBOR-3M-IRS", "20Y", 0.0278),
("EUR-EURIBOR-3M-IRS", "30Y", 0.0280),
];
// 2. Build the market: €STR swaps discount, EURIBOR 3M swaps project.
let market = MarketContext::as_of(close).with_recipe_quotes(
&CollateralSpec::Currency("EUR".parse()?),
&QuoteSheet::from_rows(eur_close.iter().copied())?,
)?;
// 3. Book the swap from its template: receive 2.30% fixed for five years.
let swap = template("EUR-EURIBOR-3M-IRS")?
.swap(close)?
.with_maturity("5Y".parse::<Termination>()?)?
.with_notional(10_000_000.0)
.with_fixed_rate(0.0230)
.with_side(Side::Receiver)
.build()?;
// 4. Read its value and its par rate.
let npv = swap.npv(&market)?;
let par = swap.par_rate(&market)?;
println!("npv {npv:.2}");
println!("par rate {par:.8}");
Ok(())
}Python
from datetime import date
import helixio as hlx
# 1. The close you are marking at, and the quotes on the sheet.
SOFR_CLOSE_DATE = date(2026, 10, 1)
EUR_CLOSE = {
"EUR-ESTR-OIS": {"1Y": 0.0195, "2Y": 0.0200, "5Y": 0.0215, "10Y": 0.0240, "20Y": 0.0262, "30Y": 0.0265},
"EUR-EURIBOR-3M-IRS": {"1Y": 0.0210, "2Y": 0.0216, "5Y": 0.0232, "10Y": 0.0257, "20Y": 0.0278, "30Y": 0.0280},
}
# 2. Build the market: €STR swaps discount, EURIBOR 3M swaps project.
market = hlx.MarketContext(SOFR_CLOSE_DATE).with_recipe_quotes("EUR", EUR_CLOSE)
# 3. Book the swap from its template: receive 2.30% fixed for five years.
swap = (
hlx.templates.get("EUR-EURIBOR-3M-IRS")
.swap(SOFR_CLOSE_DATE)
.with_maturity("5Y")
.with_notional(10_000_000.0)
.with_fixed_rate(0.0230)
.with_side("receiver")
.build()
)
# 4. Read its value and its par rate.
npv = swap.npv(market)
par = swap.par_rate(market)
print(f"npv {npv:.2f}")
print(f"par rate {par:.8f}")npv -9394.85
par rate 0.02320000
Check it worked
- The par rate is the 5Y EURIBOR quote on your sheet. A five-year swap from spot is one of the instruments the curve was solved to reprice, so anything else means the market was built from different quotes.
- You receive 2.30%, below the par rate, so the value is negative. Flip the side to
payerand the value changes sign.
Related
- Interest rate swap, every argument a swap takes.
- Quote sheets and recipes, how a sheet becomes a market.
- Strike a trade at its fair rate, to book the swap at par instead.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.