Esc

    The API is under testing and may change before release.

    How vol surfaces work

    An option's price depends on one number the market doesn't observe directly: its implied volatility. Desks quote that number on a surface, a grid of vols across expiries and, for most products, strikes. This page explains how an FX surface and a rate surface are quoted, why the nodes sit where they do, and how a vol is read between and beyond them. The FX vol surface and rate vol surface pages list the calls.

    The FX smile

    For each expiry, the interdealer FX market quotes three numbers:

    • the at-the-money (ATM) vol, for the delta-neutral straddle;
    • the 25-delta risk reversal (RR), the call wing's vol less the put wing's;
    • the 25-delta butterfly (BF), how far the average wing sits above ATM.

    The two wings follow from them:

    σ25P  =  σATM+BF−12RR,σ25C  =  σATM+BF+12RR.\sigma_{25P} \;=\; \sigma_{ATM} + BF - \tfrac{1}{2}RR, \qquad \sigma_{25C} \;=\; \sigma_{ATM} + BF + \tfrac{1}{2}RR.

    A positive risk reversal means calls on the base currency are dearer than puts, and a positive butterfly means both wings are dearer than at-the-money, which is the smile's curvature. Helixio keys each node by the quote the market spells: 25P, ATM and 25C, from the lowest strike to the highest.

    Placing a node on the strike axis

    A quote names an option, not a strike. Each node sits at the strike where an option of its kind, priced at the node's own vol σj\sigma_j, has the quoted delta. With FF the forward to delivery, DFbDF_b the base currency's discount factor and τ\tau the time to expiry, under an unadjusted convention:

    Kj  =  F exp⁡ ⁣(12σj2τ−d1 σjτ),d1  =  ω N−1 ⁣(∣Δ∣s),K_j \;=\; F\,\exp\!\left(\tfrac{1}{2}\sigma_j^2\tau - d_1\,\sigma_j\sqrt{\tau}\right), \qquad d_1 \;=\; \omega\,N^{-1}\!\left(\frac{|\Delta|}{s}\right),

    where ω\omega is +1+1 for a call and −1-1 for a put, and ss is DFbDF_b under a spot delta and 11 under a forward delta. A premium-adjusted convention has no closed form, so its strike is solved numerically. The ATM node is the delta-neutral straddle, which sets d1=0d_1 = 0:

    KDNS  =  F eσATM2τ/2.K_{DNS} \;=\; F\,e^{\sigma_{ATM}^2\tau/2}.

    Because each strike depends on the forward, a curve move shifts where the nodes sit: the smile moves with the forward, which is called sticky delta.

    Delta conventions

    Four delta conventions are in use: spot or forward, each plain or premium-adjusted (net of a premium paid in the base currency). Helixio spells them Spot, Fwd, PaSpot and PaFwd. A surface carries its convention, and every node is placed under it. The Garman–Kohlhagen delta of a call struck at KK is s N(d1)s\,N(d_1), and of a put −s N(−d1)-s\,N(-d_1), with d1=(ln⁡(F/K)+12σ2τ)/(στ)d_1 = \bigl(\ln(F/K) + \tfrac{1}{2}\sigma^2\tau\bigr)/(\sigma\sqrt{\tau}). The delta-neutral straddle's call therefore has a forward delta of exactly one half, and a spot delta of DFb/2DF_b/2.

    Reading within a smile

    Between two node strikes Kj<K<Kj+1K_j < K < K_{j+1}, the FX vol is linear in log-moneyness x=ln⁡(K/F)x = \ln(K/F):

    σ(K)  =  σj+(σj+1−σj) x−xjxj+1−xj,xj  =  ln⁡(Kj/F).\sigma(K) \;=\; \sigma_j + (\sigma_{j+1} - \sigma_j)\,\frac{x - x_j}{x_{j+1} - x_j}, \qquad x_j \;=\; \ln(K_j/F).

    Beyond the outer nodes the vol is held at the wing's value: the surface doesn't extrapolate the skew into the tails.

    Across expiries: total variance

    Total variance is σ2τ\sigma^2\tau. Between two stored expiries τ0<τ<τ1\tau_0 < \tau < \tau_1, every surface here, FX or rates, is linear in it:

    σ2(τ) τ  =  (1−w) σ2(τ0) τ0+w σ2(τ1) τ1,w  =  τ−τ0τ1−τ0.\sigma^2(\tau)\,\tau \;=\; (1-w)\,\sigma^2(\tau_0)\,\tau_0 + w\,\sigma^2(\tau_1)\,\tau_1, \qquad w \;=\; \frac{\tau - \tau_0}{\tau_1 - \tau_0}.

    Total variance can only grow with time, so this rule can't create calendar arbitrage between two arbitrage-free smiles, where interpolating the vol itself can. When vols rise with expiry, the interpolated vol sits above the straight line between the two node vols. Before the first expiry and after the last, the vol is held flat.

    Because ww is a ratio of times, a day count that scales every τ\tau alike leaves an interpolated vol unchanged. The day count still matters: an option counts its own life on it.

    Moving a smile

    A scenario states a smile move in vol points, the unit the quotes are made in: 0.002 is 0.20 vol points. A risk reversal move sRRs_{RR} and a butterfly move sBFs_{BF} change the wings by

    Δσ25P  =  sBF−12sRR,Δσ25C  =  sBF+12sRR,ΔσATM  =  0,\Delta\sigma_{25P} \;=\; s_{BF} - \tfrac{1}{2}s_{RR}, \qquad \Delta\sigma_{25C} \;=\; s_{BF} + \tfrac{1}{2}s_{RR}, \qquad \Delta\sigma_{ATM} \;=\; 0,

    so the shocked smile is the one the moved quotes would build. A smile move is always absolute: a relative move is refused, because a risk reversal can be zero or negative.

    Rate vol grids

    Swaptions are quoted on a grid of option expiry by underlying swap tenor, one at-the-money vol per cell. Both axes are in years, so the same grid also serves a bond option, which reads the tenor axis at a duration. A rate vol is quoted in one of two conventions:

    • a normal vol, in basis points of rate per year, priced with the Bachelier formula;
    • a lognormal vol, in percent of the rate, priced with Black-76.

    The surface carries its convention, and a pricer takes the formula from the surface rather than guessing from the level. A read at expiry TT, tenor mm and strike KK collapses one axis at a time: strike, then tenor, then expiry. Across strikes and tenors the vol is linear; between tenor nodes mj<m<mj+1m_j < m < m_{j+1},

    σ(m)  =  σj+(σj+1−σj) m−mjmj+1−mj,\sigma(m) \;=\; \sigma_j + (\sigma_{j+1} - \sigma_j)\,\frac{m - m_j}{m_{j+1} - m_j},

    and across expiries it is linear in total variance, as above. Outside every edge the read is the edge node's vol.

    Strike axes

    A cap market quotes a vol for each expiry and absolute strike on one index tenor. A swaption cube adds strikes to the expiry by tenor grid, quoted as spreads ss to the at-the-money forward FF:

    K  =  F+s.K \;=\; F + s.

    A strike of 3% and a spread of 300 bp look alike as numbers, so the surface records which axis it carries, and a read naming the wrong kind of coordinate is refused rather than misread.

    From formula to API

    Symbol Meaning Rust Python
    σATM\sigma_{ATM}, RRRR, BFBF an FX expiry's three quotes the arguments of FxVolSmile::from_quotes a smile tuple's last three entries, or vols.FxVolSmile.from_quotes
    σj\sigma_j an FX node's vol FxVolSmile::vols; vol_at_quote on FxVolSurface::view vols.FxVolSmile.vols; vols.FxVolSurface.vol_at_quote
    KjK_j, KDNSK_{DNS} a node's strike strike_from_delta, atm_delta_neutral_strike vols.strike_from_delta, vols.atm_delta_neutral_strike
    σ(K)\sigma(K) the FX vol at a strike vol_at_strike on FxVolSurface::view vols.FxVolSurface.vol_at_strike
    Δ\Delta an option's delta delta vols.fx_delta
    ss the spot or forward delta scale DeltaConvention::Spot, DeltaConvention::Fwd vols.DeltaConvention.Spot, vols.DeltaConvention.Fwd
    sRRs_{RR}, sBFs_{BF} a smile move a Shift on Selector::FxSmile with a SmileComponent Scenario.shift on "fx_rr(EUR/USD)" or "fx_bf(EUR/USD)"
    τ\tau, TT time to an expiry FxVolSurface::day_count; a rate read's expiry_years vols.FxVolSurface.day_count; vols.RateVolSurface.vol's expiry_years
    ww the total-variance weight computed inside every read computed inside every read
    mm an underlying tenor in years a rate read's tenor_years; RateVolSurface::tenors vols.RateVolSurface.vol's tenor_years; vols.RateVolSurface.tenors
    KK, ss a rate strike or spread vol_at_strike, vol_at_spread on RateVolSurface::view the strike argument of vols.RateVolSurface.vol
    the convention normal or lognormal RateVolSurface::convention vols.RateVolSurface.vol_convention

    See also

    • FX vol surface and rate vol surface, the calls.
    • FX options, swaptions and caps and floors, which price off these surfaces.
    • SABR, a smile model a market can hold in a rate surface's place.

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