Esc

    The API is under testing and may change before release.

    Rate futures

    This page lists what you need to price a listed short-term interest rate future: the booking call and its arguments, the fair price and convexity formulas, and the defaults you get. A rate future is a listed contract on a three-month or one-month rate, quoted as 100 less the rate.

    Quick example

    The example reads its quotes from the desk sheets; load them once and it runs as shown.

    This books ten March 2028 SOFR three-month contracts bought at 95.40, reads their fair price and the position's value, then prices them again with Hull-White convexity.

    Rust
    use chrono::NaiveDate;
    use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_LADDER};
    use helixrs::{
        AnyInstrument, AnyTemplate, CollateralSpec, MarketContext, PricingRiskFactors,
        QuoteSheet, RateFuture, futures_convexity, futures_price, 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 position(short: bool) -> Outcome<RateFuture> {
        let AnyTemplate::Future(futures) = template("USD-SOFR-3M-FUT")? else {
            unreachable!("a futures template")
        };
        let future = RateFuture::for_contract(&futures, 2028, 3, 10.0, 95.40)?;
        Ok(if short { future.short() } else { future })
    }
    
    /// The fair price and the convexity adjustment, read on the market's resolved view.
    fn price(future: &RateFuture, market: &MarketContext) -> Outcome<(f64, f64)> {
        let instrument = AnyInstrument::RateFuture(future.clone());
        Ok(market.priced(&instrument.risk_factors()?, |view| {
            Ok((futures_price(future, view)?, futures_convexity(future, view)?))
        })?)
    }
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
        let market = MarketContext::as_of(close)
            .with_recipe_quotes(
                &CollateralSpec::Currency("USD".parse()?),
                &QuoteSheet::from_rows(USD_SOFR_LADDER.iter().copied())?,
            )?
            .with_hull_white("USD".parse()?, 0.03, 0.01);
    
        // Ten March 2028 contracts, bought at 95.40.
        let long = position(false)?;
        let instrument = AnyInstrument::RateFuture(long.clone());
        for (name, day) in instrument.contract_dates() {
            println!("{name:<14} {day}");
        }
    
        // The fair price is 100 less the forward rate over the reference quarter; the
        // position is worth the price move times contracts and the point value.
        let (fair, _) = price(&long, &market)?;
        let npv = instrument.npv(&market)?;
        println!("fair price     {fair:.6}");
        println!("npv            {npv:.2}");
    
        // Under Hull-White the futures rate sits above the forward, so the convexity
        // adjusted price is lower than the forward price. The adjustment reads
        // directly as a rate, and it is the whole gap between the two prices.
        let (adjusted, convexity) = price(&long.clone().with_hull_white_convexity(), &market)?;
        println!("adjusted price {adjusted:.6}");
        println!("convexity bp   {:.4}", convexity * 1e4);
    
        Ok(())
    }
    Python
    import helixio as hlx
    from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_LADDER
    
    market = (
        hlx.MarketContext(SOFR_CLOSE_DATE)
        .with_recipe_quotes("USD", USD_SOFR_LADDER)
        .with_hull_white("USD", 0.03, 0.01)
    )
    futures = hlx.templates.get("USD-SOFR-3M-FUT")
    
    # Ten March 2028 contracts, bought at 95.40.
    position = futures.rate_future_contract(2028, 3, contracts=10.0, traded_price=95.40)
    for name, day in position.contract_dates():
        print(f"{name:<14} {day}")
    
    # The fair price is 100 less the forward rate over the reference quarter; the
    # position is worth the price move times contracts and the point value.
    fair = hlx.rates.futures_price(position, market)
    npv = position.npv(market)
    print(f"fair price     {fair:.6f}")
    print(f"npv            {npv:.2f}")
    
    # Under Hull-White the futures rate sits above the forward, so the convexity
    # adjusted price is lower than the forward price. The adjustment reads
    # directly as a rate, and it is the whole gap between the two prices.
    modelled = position.with_hull_white_convexity()
    adjusted = hlx.rates.futures_price(modelled, market)
    convexity = hlx.rates.futures_convexity(modelled, market)
    print(f"adjusted price {adjusted:.6f}")
    print(f"convexity bp   {convexity * 1e4:.4f}")
    accrual_start  2028-03-15
    accrual_end    2028-06-21
    fair price     95.277505
    npv            -3062.38
    adjusted price 95.263478
    convexity bp   1.4026

    With the model switched on, the fair price drops below the forward price: that gap is the convexity adjustment.

    How it's priced

    The fair price is p=100 (1−rf)p = 100\,(1 - r_f), where the futures rate is the forward plus the convexity adjustment, rf=F+cr_f = F + c. A position of nn contracts bought at p0p_0 is worth

    V=n⋅(p−p0)⋅M,V = n \cdot (p - p_0) \cdot M,

    with MM the contract's value per index point. A three-month SOFR contract compounds the overnight rate in arrears over its reference period, from ss, the first day not yet fixed, to its end EE, with accrual τ\tau. Under Hull-White with mean reversion aa and volatility σ\sigma its futures rate is

    rf=(1+Fτ) eΛ−1τ,Λ=σ2B2 1−e−2as2a+σ2B Bs22+σ2a2(τE−2(1−e−aτE)a+1−e−2aτE2a),r_f = \frac{(1 + F\tau)\,e^{\Lambda} - 1}{\tau}, \qquad \Lambda = \sigma^2 B^2\,\frac{1 - e^{-2as}}{2a} + \frac{\sigma^2 B\,B_s^2}{2} + \frac{\sigma^2}{a^2}\left(\tau_E - \frac{2(1 - e^{-a\tau_E})}{a} + \frac{1 - e^{-2a\tau_E}}{2a}\right),

    with τE=E−s\tau_E = E - s, B=(1−e−aτE)/aB = (1 - e^{-a\tau_E})/a and Bs=(1−e−as)/aB_s = (1 - e^{-as})/a. The first two terms are the convexity of a term rate fixing at ss; the third is the variance the rate accrues while it fixes. So c=rf−Fc = r_f - F is positive, and it grows with σ2\sigma^2 and with the time to the contract. The gap between the forward price and the adjusted price is the adjustment, in price points: 100 (1−F)−p=100 c100\,(1 - F) - p = 100\,c. Why daily margining creates that gap is covered in how rates products work.

    API

    Symbol Meaning Rust Python
    the contract a listed contract RateFuture::for_contract Template.rate_future_contract
    short the other side RateFuture::short short=True
    pp the fair price futures_price inside MarketContext::priced rates.futures_price
    a,σa, \sigma the model MarketContext::with_hull_white MarketContext.with_hull_white
    cc modelled Hull-White convexity RateFuture::with_hull_white_convexity Instrument.with_hull_white_convexity
    Λ\Lambda the convexity exponent HullWhiteParameters::compounded_convexity_exponent models.HullWhite.compounded_convexity_exponent
    s,E,τ,τE,B,Bss, E, \tau, \tau_E, B, B_s the contract's dates and the model's duration factors inside RateFuture::with_hull_white_convexity inside Instrument.with_hull_white_convexity
    cc the adjustment, as a rate futures_convexity inside MarketContext::priced rates.futures_convexity
    VV the position's value AnyInstrument::npv Instrument.npv

    Template.rate_future_contract takes:

    Argument Type Default Meaning
    year, month int required the contract's delivery month
    contracts float required the number of contracts
    traded_price float required the price you traded at
    short bool False book the short side; Rust .short()
    convexity_adjustment float or None None a fixed adjustment, as a rate, in place of a model

    In Rust, RateFuture::for_contract(template, year, month, contracts, traded_price) takes the same values positionally.

    Defaults and limits

    • Side: a contract is booked long. Pass short=True in Python, or call .short() in Rust, for the other side.
    • Convexity: none is added unless you ask for it. The Hull-White adjustment needs the model on the market (with_hull_white) and on the instrument (with_hull_white_convexity).
    • Contracts: codes, reference periods and the value per point come from the listed contract; see listed futures and rates conventions.
    • Forwards: the fair price is one reading of the forward over the reference quarter; the forward itself is read on reading a curve.

    See also

    • Source: crates/helixrs/src/instruments/rates.rs, crates/helixrs/src/market/pricers.rs
    • Related pages:
      • Listed futures, for contract codes and periods.
      • Curve helpers, for futures as curve rungs.
      • Hull-White, for calibrating the model.

    From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.