Esc

    The API is under testing and may change before release.

    Equity spots and vols

    This page lists how to give a market what an equity product reads: the share's spot price and a vol surface over expiry and strike, the calls that pin and read them, and how the surface interpolates. You need both to price an equity option or an equity total return swap.

    Quick example

    The example reads its close date from the desk sheets; load them once and it runs as shown. It pins a spot and a surface for a made-up name, ACME, beside the SOFR curve, reads a listed point and a point between listed points, and shows that a name the market wasn't given has no spot.

    Rust
    use chrono::NaiveDate;
    use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_CLOSE};
    use helixrs::{
        AnyInstrument, CollateralSpec, EquityOption, FutureOptionVolSurface, MarketContext,
        OptionKind, QuoteSheet, VolConvention,
    };
    
    type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
    
    /// The spot and the vols are data stated here, not a published close.
    const SPOT: f64 = 120.0;
    
    fn date((year, month, day): (i32, u32, u32)) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
        let usd = "USD".parse()?;
        let expiries = vec![date((2027, 4, 1)), date((2027, 10, 1))];
        let strikes = vec![100.0, 120.0, 140.0];
        let vols = vec![vec![0.29, 0.26, 0.24], vec![0.28, 0.25, 0.235]];
    
        // The ACME spot and its vol surface, beside the SOFR curve.
        let surface = FutureOptionVolSurface::new(
            close,
            expiries.clone(),
            strikes,
            &vols,
            VolConvention::Lognormal,
        )?;
        let market = MarketContext::as_of(close)
            .with_recipe_quotes(
                &CollateralSpec::Currency(usd),
                &QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?,
            )?
            .with_equity_spot("ACME", SPOT)?
            .with_equity_vols("ACME", surface);
        let read = market.equity_vols("ACME")?.view::<f64>();
        let at_the_money = read.vol(expiries[1], 120.0);
        let between = read.vol(date((2027, 7, 1)), 130.0);
        println!(
            "spot              {:.2}",
            market.equity_spot("ACME").expect("a quoted spot")
        );
        println!("vol Oct-27 at 120 {at_the_money:.6}");
        println!("vol Jul-27 at 130 {between:.6}");
    
        // A name the market was not given has no spot, and an option on it is refused.
        println!("spot of OTHER     {:?}", market.equity_spot("OTHER"));
        let option = AnyInstrument::EquityOption(Box::new(EquityOption::new(
            "OTHER",
            usd,
            OptionKind::Call,
            100.0,
            expiries[1],
            1.0,
        )?));
        let refused = option.npv(&market).is_err();
        println!("refused           {}", if refused { "yes" } else { "no" });
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_CLOSE
    
    #: The spot and the vols are data stated here, not a published close.
    SPOT = 120.0
    EXPIRIES = [dt.date(2027, 4, 1), dt.date(2027, 10, 1)]
    STRIKES = [100.0, 120.0, 140.0]
    VOLS = [[0.29, 0.26, 0.24], [0.28, 0.25, 0.235]]
    
    # The ACME spot and its vol surface, beside the SOFR curve.
    surface = hlx.vols.FutureOptionVolSurface(SOFR_CLOSE_DATE, EXPIRIES, STRIKES, VOLS, "lognormal")
    market = (
        hlx.MarketContext(SOFR_CLOSE_DATE)
        .with_recipe_quotes("USD", USD_SOFR_CLOSE)
        .with_equity_spot("ACME", SPOT)
        .with_equity_vols("ACME", surface)
    )
    read = market.equity_vols("ACME")
    at_the_money = read.vol(EXPIRIES[1], 120.0)
    between = read.vol(dt.date(2027, 7, 1), 130.0)
    print(f"spot              {market.equity_spot('ACME'):.2f}")
    print(f"vol Oct-27 at 120 {at_the_money:.6f}")
    print(f"vol Jul-27 at 130 {between:.6f}")
    
    # A name the market was not given has no spot, and an option on it is refused.
    print(f"spot of OTHER     {market.equity_spot('OTHER')}")
    option = hlx.products.equity_option("OTHER", "USD", strike=100.0, expiry=EXPIRIES[1], kind="call")
    try:
        option.npv(market)
        refused = False
    except hlx.MissingMarketInputError:
        refused = True
    print(f"refused           {'yes' if refused else 'no'}")
    spot              120.00
    vol Oct-27 at 120 0.250000
    vol Jul-27 at 130 0.245039
    spot of OTHER     None
    refused           yes

    A listed point reads back as quoted, and a point between listed points reads off the interpolation.

    How it's priced

    The spot SS is a scalar per name. The surface is a grid of vols σjk\sigma_{jk} at expiry TjT_j and strike KkK_k. Between strikes it interpolates linearly in vol; between expiries, linearly in total variance σ2T\sigma^2 T; beyond every edge it is flat. Time is the ACT/365 Fixed year fraction from the surface's reference date. Where equity data comes from on a desk is covered in how a market is built.

    API

    Symbol Meaning Rust Python
    SS the quoted spot MarketContext::with_equity_spot, read by equity_spot MarketContext.with_equity_spot, read by equity_spot
    σjk\sigma_{jk} the vol grid FutureOptionVolSurface::new(reference, expiries, strikes, rows, convention) vols.FutureOptionVolSurface(reference, expiries, strikes, vols, convention)
    the surface on the market pinned under the name MarketContext::with_equity_vols, read by equity_vols MarketContext.with_equity_vols, read by equity_vols
    σ(T,K)\sigma(T, K) a vol read off the surface surface.view::<f64>().vol(expiry, strike) FutureOptionVolSurface.vol(expiry, strike)
    Argument Type Default Meaning
    with_equity_spot(equity, price) str, float required the spot under the equity's name
    with_equity_vols(equity, surface) str, FutureOptionVolSurface required the vol surface under the equity's name
    FutureOptionVolSurface(reference, expiries, strikes, vols) date, list[date], list[float], list[list[float]] required the grid: one row of vols per expiry, one column per strike
    convention VolConvention | str "lognormal" "lognormal" for Black vols, "normal" for Bachelier vols in price points per year
    day_count str | DayCountFraction "ACT/365.FIXED" the year fraction time is measured in
    FutureOptionVolSurface.flat(reference, vol) date, float required a surface at one vol everywhere, with the same two optional arguments

    Defaults and limits

    • Unknown names: a name the market wasn't given has no spot to read, and an option on it is refused rather than priced against nothing.
    • Flat beyond the grid: before the first expiry, after the last, and outside the strikes, the surface returns its edge value.
    • One surface type: the surface is the same listed-option surface the bond future option page uses, keyed here by an equity name rather than a contract.

    See also

    • Source: crates/helixrs/src/market/store.rs, with the surface in crates/helixrs/src/marketdata/future_option_vol.rs
    • Related pages:
      • Load a vol grid, the task step by step.
      • Equity option, for what reads the spot and the surface.
      • How a market is built, for where the data comes from.

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