Skip to content

Volatility

Surface and handoff layer

The volatility namespace covers implied-vol inversion, smile and surface objects, local-vol extraction, and the eSSVI toolbox used by the proof-path pages.

This page stays reference-first. Use it for the raw objects and entry points; the proof-path guides explain the engineering decisions around them.

Implied volatility inversion

These functions cover scalar Black-Scholes/Black-76 implied-vol inversion.

Scalar implied volatility computation for individual options.

implied_vol_bs

implied_vol_bs(mkt_price: float, spec: OptionSpec, market: MarketData | PricingContext, *, cfg: ImpliedVolConfig | None = None, t: float = 0.0, sigma0: float | None = None) -> float

Compute Black-Scholes implied volatility.

Convenience wrapper around :func:implied_vol_bs_result that returns only the implied volatility.

Parameters:

Name Type Description Default
mkt_price float

Observed market option price.

required
spec OptionSpec

Option specification (kind, strike, expiry).

required
market MarketData

Market observables (spot, rate, dividend yield).

required
cfg ImpliedVolConfig or None

Configuration for implied-vol inversion. If None, ImpliedVolConfig() is used.

None
t float

Valuation time in the same units as spec.expiry.

0.0
sigma0 float or None

Initial volatility guess. If None, the seed is chosen according to cfg.seed_strategy.

None

Returns:

Type Description
float

Implied volatility.

Raises:

Type Description
InvalidOptionPriceError

If the input price violates no-arbitrage bounds.

ValueError

If expiry is not strictly greater than t.

ValueError

If sigma0 is required by the configured seed strategy but not provided.

implied_vol_bs_result

implied_vol_bs_result(mkt_price: float, spec: OptionSpec, market: MarketData | PricingContext, *, cfg: ImpliedVolConfig | None = None, t: float = 0.0, sigma0: float | None = None) -> ImpliedVolResult

Compute Black-Scholes implied volatility and return diagnostics.

This function inverts the Black-Scholes price for volatility by solving:

BS_price(sigma) - mkt_price = 0

using a root-finding method selected by cfg.root_method.

Parameters:

Name Type Description Default
mkt_price float

Observed market option price.

required
spec OptionSpec

Option specification (kind, strike, expiry).

required
market MarketData

Market observables (spot, rate, dividend yield).

required
cfg ImpliedVolConfig or None

Configuration for implied-vol inversion. If None, ImpliedVolConfig() is used. Relevant fields:

  • root_method : RootMethod Internal solver selector (dispatched via :func:get_root_method).
  • sigma_lo, sigma_hi : float Volatility search interval.
  • bounds_eps : float Tolerance for no-arbitrage bound checks.
  • seed_strategy : SeedStrategy How to select the initial guess when sigma0 is not provided.
  • numerics : NumericsConfig Tolerances and iteration limits (abs_tol, rel_tol, max_iter), plus safety clamps (min_vega).
None
t float

Valuation time in the same units as spec.expiry.

0.0
sigma0 float or None

Initial volatility guess. If None, the seed is chosen according to cfg.seed_strategy:

  • HEURISTIC: compute a robust seed from time value.
  • USE_GUESS or LAST_SOLUTION: requires sigma0 to be provided.
None

Returns:

Type Description
ImpliedVolResult

Container with implied volatility and root-finder diagnostics.

Raises:

Type Description
InvalidOptionPriceError

If the input price violates no-arbitrage bounds (see :func:_validate_bounds).

ValueError

If expiry is not strictly greater than t.

ValueError

If sigma0 is required by the configured seed strategy but not provided.

Exception

Any exception raised by the root solver is propagated.

Notes

This function is intentionally "config-driven": bounds, tolerances, and the solver method are taken from cfg.

One-off overrides should be done by creating a modified config, e.g.::

from dataclasses import replace
cfg2 = replace(cfg, sigma_hi=10.0)
iv = implied_vol_bs_result(
    price,
    spec,
    market,
    cfg=cfg2,
)

The objective uses :func:option_pricing.bs_greeks for both price and vega. Vega is clamped below by cfg.numerics.min_vega for robustness.

Volatility surfaces and smiles

These are the common surface objects exposed near the pricing API and used throughout the guides.

Core volatility surface implementation.

This module contains the main VolSurface class for managing implied volatility surfaces with per-expiry smiles and time interpolation.

VolSurface dataclass

Total-variance volatility surface over expiry.

Within each expiry slice (Smile), total variance is interpolated using: - Fritsch-Carlson on monotone (or split-monotone) data, else linear fallback.

Across expiry, total variance is linearly interpolated in time between smiles.

from_grid classmethod

from_grid(rows: Iterable[tuple[float, float, float]], forward: ScalarFn, *, expiry_round_decimals: int = 10) -> VolSurface

Build a surface from market quotes (T, K, iv) on a grid.

Parameters:

Name Type Description Default
rows Iterable[tuple[float, float, float]]

Iterable of (T, K, iv) market data points.

required
forward ScalarFn

Callable forward(T) -> float.

required
expiry_round_decimals int

Number of decimals to round each T for bucketing (default: 10).

10

Returns:

Type Description
VolSurface

Surface with Smile slices per unique expiry.

from_svi classmethod

from_svi(rows: Iterable[tuple[float, float, float]], forward: ScalarFn, *, expiry_round_decimals: int = 10, calibrate_kwargs: dict | None = None) -> VolSurface

Build a surface by calibrating an SVI smile per expiry.

Parameters:

Name Type Description Default
rows Iterable[tuple[float, float, float]]

Iterable of (T, K, iv) market points.

required
forward ScalarFn

forward(T) -> float.

required
expiry_round_decimals int

Bucketing tolerance for float maturities.

10
calibrate_kwargs dict | None

Optional kwargs forwarded to option_pricing.vol.svi.calibrate_svi.

None

Returns:

Type Description
VolSurface

A surface whose slices are analytic SVI smiles.

Notes

This keeps VolSurface itself dependency-light: SciPy is only imported if you call this method.

slice

slice(T: float, method: Literal['no_arb'] = 'no_arb') -> SmileSlice
slice(T: float, method: Literal['linear_w']) -> DifferentiableSmileSlice
slice(T: float, method: TimeInterp = 'no_arb') -> SmileSlice

Return a callable single-expiry smile slice at maturity T.

  • Node expiry: returns the stored slice (grid or SVI).
  • Off-node: returns InterpolatedSmileSlice, linear in total variance.
Notes

The surface is defined in log-moneyness y = ln(K/F(T)). Interpolating in y between expiries corresponds to constant log-moneyness interpolation.

iv

iv(K: ArrayLike, T: float) -> FloatArray

Implied volatility at given strikes and maturity.

Parameters:

Name Type Description Default
K ArrayLike

Strike price(s).

required
T float

Maturity time.

required

Returns:

Type Description
FloatArray

Implied volatility at (K, T).

w

w(y: ArrayLike, T: float) -> FloatArray

Total variance at given log-moneyness and maturity.

Parameters:

Name Type Description Default
y ArrayLike

Log-moneyness y = ln(K/F).

required
T float

Maturity time.

required

Returns:

Type Description
FloatArray

Total variance w(y, T) = T * iv(y, T)^2.

Smile grid interpolation and manipulation helpers.

This module provides the Smile class and supporting functions for interpolating total-variance grids using adaptive interpolation strategies (Fritsch-Carlson for monotone regions, piecewise stitching for non-monotone smiles, linear fallback).

Smile dataclass

Total-variance smile at a single expiry on a log-moneyness grid.

The smile is represented in terms of total variance:

w(y) = T * iv(y)^2

on a strictly increasing log-moneyness grid:

y = ln(K / F(T)).

Interpolation: - Uses Fritsch-Carlson monotone cubic interpolation when w(x) is monotone or can be split into two monotone pieces (common U-shape). - Falls back to linear interpolation with flat extrapolation otherwise.

w_at

w_at(xq: ArrayLike) -> FloatArray

Interpolate total variance at given log-moneyness points.

Parameters:

Name Type Description Default
xq ArrayLike

Log-moneyness point(s) at which to evaluate total variance.

required

Returns:

Type Description
FloatArray

Total variance w(x) = T * iv(x)^2 at xq.

iv_at

iv_at(xq: ArrayLike) -> FloatArray

Interpolate implied volatility at given log-moneyness points.

Parameters:

Name Type Description Default
xq ArrayLike

Log-moneyness point(s) at which to evaluate implied volatility.

required

Returns:

Type Description
FloatArray

Implied volatility iv(x) = sqrt(w(x) / T) at xq.

Local volatility surface derived from implied volatility.

This module provides the LocalVolSurface class for computing local volatility from an implied surface using Gatheral's formula.

LocalVolSurface dataclass

Local-vol surface computed from an implied surface in total variance.

For nodal implied surfaces, this class falls back to piecewise-time interpolation and is best treated as a research/audit tool. For Dupire production usage, prefer a continuous time-differentiable implied surface such as ESSVISmoothedSurface.

Requirements

The underlying implied surface must provide, per expiry slice: - w_at(y) - dw_dy(y) - d2w_dy2(y)

i.e. analytic (or otherwise smooth) derivatives in log-moneyness.

Time derivative w_T

w_T is approximated by the same piecewise-linear interpolation you use for implied vols: w_T(y,T) pprox (w(y,T1) - w(y,T0)) / (T1 - T0)

which is piecewise-constant in T between expiries and can create visible "bands" in local vol.

from_implied classmethod

from_implied(implied: VolSurface | TimeDifferentiableImpliedSurface, *, forward: ScalarFn | None = None, discount: ScalarFn | None = None) -> LocalVolSurface

Convenience constructor from an implied volatility surface.

Parameters:

Name Type Description Default
implied VolSurface | TimeDifferentiableImpliedSurface

Implied volatility surface (:class:VolSurface).

required
forward ScalarFn | None

Forward price function. If None, uses implied.forward.

None
discount ScalarFn | None

Discount factor function. If None, uses flat df=1.0.

None

Returns:

Type Description
LocalVolSurface

Local volatility surface derived from implied.

local_var

local_var(K: ArrayLike, T: float, *, eps_w: float = 1e-12, eps_denom: float = 1e-12) -> FloatArray

Local variance sigma_loc^2(K, T).

Computed from implied surface using Gatheral's one-dimensional local-volatility formula in log-moneyness coordinates.

Parameters:

Name Type Description Default
K ArrayLike

Strike price(s).

required
T float

Maturity time.

required
eps_w float

Small value to avoid numerical issues near w=0 (default: 1e-12).

1e-12
eps_denom float

Small value for denominator regularization (default: 1e-12).

1e-12

Returns:

Type Description
FloatArray

Local variance at (K, T).

local_var_diagnostics

local_var_diagnostics(K: ArrayLike, T: float, *, eps_w: float = 1e-12, eps_denom: float = 1e-12) -> GatheralLVReport

Diagnostics for local variance computation.

Returns intermediate quantities (w derivatives, local vol, etc.) useful for analysis and debugging.

Parameters:

Name Type Description Default
K ArrayLike

Strike price(s).

required
T float

Maturity time.

required
eps_w float

Small value to avoid numerical issues near w=0 (default: 1e-12).

1e-12
eps_denom float

Small value for denominator regularization (default: 1e-12).

1e-12

Returns:

Type Description
GatheralLVReport

Report object containing w, derivatives, local vol, and other diagnostics.

local_vol

local_vol(K: ArrayLike, T: float, *, eps_w: float = 1e-12, eps_denom: float = 1e-12) -> FloatArray

Local volatility sigma_loc(K, T).

Square root of local variance.

Parameters:

Name Type Description Default
K ArrayLike

Strike price(s).

required
T float

Maturity time.

required
eps_w float

Small value to avoid numerical issues near w=0 (default: 1e-12).

1e-12
eps_denom float

Small value for denominator regularization (default: 1e-12).

1e-12

Returns:

Type Description
FloatArray

Local volatility at (K, T).

eSSVI toolbox

The main eSSVI entrypoints are re-exported from option_pricing.vol and grouped under option_pricing.vol.ssvi.

Package-level exports for the eSSVI toolbox.

ESSVICalibrationConfig module-attribute

ESSVICalibrationConfig = ESSVIGlobalCalibrationConfig

ThetaTermStructure dataclass

Bases: MaturityTermStructure

Primary maturity term structure for theta(T).

PsiTermStructure dataclass

Bases: MaturityTermStructure

Primary maturity term structure for psi(T).

EtaTermStructure dataclass

Bases: MaturityTermStructure

Primary maturity term structure for eta(T).

ESSVITermStructures dataclass

Primary eSSVI maturity parametrization in theta(T), psi(T), eta(T).

ESSVINodeSet dataclass

Discrete eSSVI node representation on calibrated expiries.

ESSVIImpliedSurface dataclass

Continuous analytic implied surface driven by an eSSVI parameter surface.

ESSVINodalSurface dataclass

Exact arbitrage-safe nodal eSSVI surface from Mingone interpolation.

ESSVISmoothedSurface dataclass

Bases: ESSVIImpliedSurface

Dupire-oriented continuous surface built from a smooth node projection.

ESSVIFitResult dataclass

ESSVIProjectionConfig dataclass

ESSVIProjectionResult dataclass

build_theta_term_from_quotes

build_theta_term_from_quotes(*, y: NDArray[float64], T: NDArray[float64], price_mkt: NDArray[float64], market: MarketData | PricingContext, is_call: NDArray[bool_] | None = None, atm_y_tol: float = 1e-08, iv_cfg: ImpliedVolConfig | None = None) -> tuple[SampledThetaTermStructure, ATMThetaDiagnostics]

calibrate_essvi

calibrate_essvi(*, y: NDArray[float64], T: NDArray[float64], price_mkt: NDArray[float64], market: MarketData | PricingContext, sqrt_weights: NDArray[float64] | None = None, weights: NDArray[float64] | None = None, is_call: NDArray[bool_] | None = None, cfg: ESSVIGlobalCalibrationConfig | None = None) -> ESSVIFitResult

project_essvi_nodes

project_essvi_nodes(nodes: ESSVINodeSet, *, cfg: ESSVIProjectionConfig | None = None) -> ESSVIProjectionResult

evaluate_essvi_constraints

evaluate_essvi_constraints(params: ESSVITermStructures, expiries: ArrayLike, *, tol: float = 1e-10) -> ESSVIConstraintReport

validate_essvi_nodes

validate_essvi_nodes(nodes: ESSVINodeSet, *, strict: bool = False, tol: float = 1e-10) -> ESSVINodeConstraintReport

validate_essvi_continuous

validate_essvi_continuous(params: ESSVITermStructures, market: MarketData | PricingContext, *, expiries: ArrayLike | None = None, y_grid: ArrayLike | None = None, strict: bool = False, tol: float = 1e-10) -> ESSVIValidationReport

Notes

  • Implied-vol inversion configuration lives in ImpliedVolConfig on the Public API page.
  • The eSSVI objects are available from option_pricing.vol, not from the package root option_pricing.
  • LocalVolSurface remains demo-grade for generic slice-stack interpolation, but it can also consume a time-differentiable surface such as ESSVISmoothedSurface.