Forward-rate agreement
This page lists everything you need to book and value a forward-rate agreement (FRA): its arguments and defaults, how it's priced, and an example you can run. A FRA pays the difference between a term rate's fixing and a rate you agree today, over one future period.
Quick example
The example reads its quotes from the desk sheets; load them once and it runs as shown.
This books a EURIBOR 3x6 FRA at 2.10%, prints its contract dates, values it, and strikes a second one at the forward. The market comes from a EUR close on the quote sheets and recipes.
use chrono::NaiveDate;
use helixio_docs_sheets::{EUR_CLOSE, SOFR_CLOSE_DATE};
use helixrs::{
AnyInstrument, AnyTemplate, CollateralSpec, ForwardRateAgreement, MarketContext,
Period, 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")
}
fn fra(close: NaiveDate, rate: f64, receiver: bool) -> Outcome<AnyInstrument> {
let AnyTemplate::Fra(fras) = template("EUR-EURIBOR-3M-FRA")? else {
unreachable!("a FRA template")
};
let start = "3M".parse::<Period>()?;
let mut fra =
ForwardRateAgreement::from_template(&fras, close, Some(start), 10_000_000.0, rate);
if receiver {
fra = fra.receiver();
}
Ok(AnyInstrument::Fra(fra))
}
fn main() -> Outcome<()> {
let close = date(SOFR_CLOSE_DATE);
let market = MarketContext::as_of(close).with_recipe_quotes(
&CollateralSpec::Currency("EUR".parse()?),
&QuoteSheet::from_rows(EUR_CLOSE.iter().copied())?,
)?;
// A 3x6: the period starts three months after spot and runs one index tenor.
let buyer = fra(close, 0.0210, false)?;
for (name, day) in buyer.contract_dates() {
println!("{name:<14} {day}");
}
// The buyer pays the struck rate and receives the fixing.
let forward = buyer.par_rate(&market)?;
let npv = buyer.npv(&market)?;
println!("forward {forward:.8}");
println!("npv {npv:.2}");
// Struck at the forward, the agreement is worth nothing.
let fair = fra(close, forward, false)?.npv(&market)?;
println!("npv at forward {fair:.6}");
Ok(())
}import helixio as hlx
from desk_sheets import EUR_CLOSE, SOFR_CLOSE_DATE
market = hlx.MarketContext(SOFR_CLOSE_DATE).with_recipe_quotes("EUR", EUR_CLOSE)
fras = hlx.templates.get("EUR-EURIBOR-3M-FRA")
# A 3x6: the period starts three months after spot and runs one index tenor.
fra = fras.forward_rate_agreement(SOFR_CLOSE_DATE, "3M", notional=10_000_000.0, struck_rate=0.0210)
for name, day in fra.contract_dates():
print(f"{name:<14} {day}")
# The buyer pays the struck rate and receives the fixing.
forward = fra.par_rate(market)
npv = fra.npv(market)
print(f"forward {forward:.8f}")
print(f"npv {npv:.2f}")
# Struck at the forward, the agreement is worth nothing.
fair = fras.forward_rate_agreement(SOFR_CLOSE_DATE, "3M", notional=10_000_000.0, struck_rate=forward)
print(f"npv at forward {fair.npv(market):.6f}")accrual_start 2027-01-05
accrual_end 2027-04-05
fixing_date 2026-12-31
forward 0.02055978
npv -1089.30
npv at forward 0.000000
The forward is below 2.10%, so paying 2.10% costs you money. Struck at the forward, the FRA is worth nothing.
How it's priced
For an accrual period from to , a fixing and a struck rate , the buyer receives at
The forward , read off the projection curve, is what the fixing is expected to be, so a FRA struck at is worth nothing. The seller holds the other side: . Why FRAs are quoted by their forward is covered in how rates products work.
API
| Argument | Type | Default | Meaning |
|---|---|---|---|
valuation |
date |
None |
the date the start tenor counts from, via spot; give it with forward_start |
forward_start |
tenor str |
None |
how far after spot the period starts, such as "3M" for a 3x6 |
accrual_start, accrual_end, fixing_date |
date |
None |
stated contract dates, instead of valuation and forward_start; give all three or none |
notional |
float |
required | the size |
struck_rate |
float |
required | the rate you agree |
receiver |
bool |
False |
True books the seller; Rust .receiver() on ForwardRateAgreement::from_template(&template, valuation, Some(start), notional, rate) |
| Symbol | Meaning | Rust | Python |
|---|---|---|---|
| the trade | a FRA on its template | ForwardRateAgreement::from_template |
Template.forward_rate_agreement |
| seller | the other side | ForwardRateAgreement::receiver |
receiver=True |
| the forward | AnyInstrument::par_rate |
Instrument.par_rate |
|
| its value | AnyInstrument::npv |
Instrument.npv |
Defaults and limits
- Side: a FRA is booked as the buyer, paying the struck rate. Pass
receiver=Truein Python, or call.receiver()in Rust, to book the seller. - Start: the start tenor counts from spot, not from the trade date. The template sets the index, the spot lag and the settlement rule; see rates conventions.
- Dates:
contract_dates()gives the accrual start and end and the fixing date. The term coupon a FRA settles on is built and read on legs and coupons.
See also
- Source:
crates/helixrs/src/instruments/rates.rs - Related pages:
- Strike a trade at its fair rate, for rebooking a trade at its forward, as the quick example does.
- Rate futures, for the listed contract on the same kind of period.
- Indices, for the fixing date and period.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.