Esc

    The API is under testing and may change before release.

    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.

    Rust
    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(())
    }
    Python
    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 τ\tau from tst_s to tet_e, a fixing LL and a struck rate KK, the buyer receives at tst_s

    N (L−K) τ1+L τ.N\,\frac{(L - K)\,\tau}{1 + L\,\tau}.

    The forward F=(Pp(ts)/Pp(te)−1)/τF = (P_p(t_s)/P_p(t_e) - 1)/\tau, read off the projection curve, is what the fixing is expected to be, so a FRA struck at FF is worth nothing. The seller holds the other side: Vseller=−VbuyerV_{\text{seller}} = -V_{\text{buyer}}. 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
    FF the forward AnyInstrument::par_rate Instrument.par_rate
    VV its value AnyInstrument::npv Instrument.npv

    Defaults and limits

    • Side: a FRA is booked as the buyer, paying the struck rate. Pass receiver=True in 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.