Volatility Surfaces and Calibration

Finance

Black-Scholes assumes the underlying follows a geometric Brownian motion with constant volatility . Real markets ALMOST NEVER trade as if this were true. If you take the market price of an option, invert the Black-Scholes formula to recover the consistent with that price, and then PLOT the resulting "implied volatility" against the option's strike and expiry, you get a SURFACE with structure: U-shaped or skewed in strike (the SMILE / SKEW), and varying with maturity (the TERM STRUCTURE). The whole edifice of practical derivatives pricing — quoting, calibration, risk management, exotic option valuation — operates on this surface rather than on a single number.

For the derivatives quant, the volatility surface IS the market. Black-Scholes provides only a coordinate system on it (implied vol as the inverse function of price); the surface ITSELF is an empirical object that must be observed, fit, and used in calibration of more complete pricing models. This page introduces the SHAPE of the surface, the standard SVI parameterization, and the role calibration plays in derivative pricing.

The smile

Take a fixed expiry. Plot Black-Scholes implied volatility against strike (or against log-moneyness where is the forward price). The resulting curve is rarely flat. In equity indices, it tilts: PUTS (low strikes) have HIGHER implied vol than CALLS (high strikes). The pattern is called the SKEW or SMIRK. In currency options it's more symmetric — both deep-OTM puts and deep-OTM calls are pricier than ATM — and the curve is a true SMILE. The structure varies by market, asset, and date but has been a persistent feature of every options market since at least 1987 (where it appeared abruptly in equity index options after Black Monday).

Why does the smile exist? Several non-exclusive explanations:

The term structure

The second axis. ATM implied vol typically varies with time-to-expiration:

Combined, the smile and term structure form a 2D SURFACE . Quoted daily by exchanges and OTC desks; updated continuously by market makers.

The SVI parameterization

The most-widely-used parameterization of a single-expiry vol smile is Jim Gatheral's STOCHASTIC VOLATILITY INSPIRED (SVI) family (2004). Fit TOTAL VARIANCE as a function of log-moneyness :

Five parameters: . Interpretation: shifts the level, sets the slope of the wings, is the skew (the negative slope at ), centres the smile, controls the curvature near the centre. The parameterization derives from the asymptotic behavior of Heston-style stochastic volatility models — "inspired by" stochastic volatility — but is used purely as a flexible functional form.

Two key properties make SVI dominant in practice. (1) ARBITRAGE-FREE conditions can be enforced as linear inequalities on the parameters (no butterfly arbitrage, no calendar arbitrage), so calibration can be made strictly admissible. (2) The form has the right ASYMPTOTIC SHAPE: linear wings in at , which matches Lee's moment formula for the wings of the smile (Lee 2004).

Code: surface construction and SVI fit

# Implied vol from option prices via root-finding; demonstration of the
# vol smile/skew; SVI parameterization fit to a synthetic surface.

import numpy as np
from scipy.stats import norm
from scipy.optimize import brentq, least_squares

# Black-Scholes call (vectorised, scalar args)
def bs_call(S, K, T, r, sigma):
    if T <= 0: return max(S - K, 0)
    d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
    d2 = d1 - sigma*np.sqrt(T)
    return S * norm.cdf(d1) - K * np.exp(-r*T) * norm.cdf(d2)

def implied_vol(C, S, K, T, r):
    """Recover Black-Scholes sigma from a market quote via Brent."""
    f = lambda sigma: bs_call(S, K, T, r, sigma) - C
    return brentq(f, 1e-6, 5.0)

# Mock "market": parabolic smile in moneyness (calls and puts OTM are
# priced with higher implied vol than ATM).
def market_vol(K, S=100.0, base=0.20, strength=0.5):
    return base + strength * (K/S - 1)**2

# Generate market prices, recover implied vols (sanity check).
S, T, r = 100.0, 0.5, 0.03
strikes = np.arange(85, 116, 5)
true_vols = market_vol(strikes)
prices = np.array([bs_call(S, K, T, r, sigma) for K, sigma in zip(strikes, true_vols)])
implied = np.array([implied_vol(C, S, K, T, r) for C, K in zip(prices, strikes)])

print(f"Vol smile across strikes (S = {S}, T = {T}, r = {r:.0%}):")
print(f"  {'Strike':>8s}  {'Price':>8s}  {'IV (market)':>12s}  {'IV (recovered)':>16s}")
for K, C, iv_t, iv_r in zip(strikes, prices, true_vols, implied):
    print(f"  {K:>8.0f}  {C:>8.4f}  {iv_t:>12.4f}  {iv_r:>16.6f}")

# SVI (Stochastic Volatility Inspired) parameterization, Gatheral 2004.
# Fits TOTAL VARIANCE w = sigma² * T as a function of log-moneyness k.
#   w(k) = a + b * [ rho * (k - m) + sqrt((k - m)² + sigma²) ]
# Five parameters: (a, b, rho, m, sigma_param).

def svi(k, a, b, rho, m, sig):
    return a + b * (rho*(k - m) + np.sqrt((k - m)**2 + sig**2))

def fit_svi(strikes, ivs, S, T, r):
    F = S * np.exp(r*T)
    k = np.log(strikes / F)
    w = ivs**2 * T               # total variance
    def residuals(params):
        return svi(k, *params) - w
    x0 = [0.04, 0.1, -0.5, 0.0, 0.1]
    res = least_squares(residuals, x0, method='lm')
    return res.x, res.cost

params, cost = fit_svi(strikes, implied, S, T, r)
print(f"\nSVI calibration (a, b, rho, m, sigma_param):")
print(f"  {np.round(params, 4)}")
print(f"  Residual sum-of-squares: {cost:.2e}  (essentially exact fit)")

# Verify the fit reproduces the smile
F = S * np.exp(r*T)
k_grid = np.log(strikes / F)
w_fit = svi(k_grid, *params)
iv_fit = np.sqrt(w_fit / T)
print(f"\nSVI implied vols vs market:")
print(f"  {'K':>5s}  {'IV (mkt)':>10s}  {'IV (SVI fit)':>14s}")
for K, iv_m, iv_f in zip(strikes, implied, iv_fit):
    print(f"  {K:>5.0f}  {iv_m:>10.6f}  {iv_f:>14.6f}")

Output:

Vol smile across strikes (S = 100.0, T = 0.5, r = 3%):
    Strike     Price   IV (market)    IV (recovered)
        85   17.0493        0.2113          0.211250
        90   12.8923        0.2050          0.205000
        95    9.2816        0.2013          0.201250
       100    6.3710        0.2000          0.200000
       105    4.2131        0.2013          0.201250
       110    2.7370        0.2050          0.205000
       115    1.7924        0.2113          0.211250

SVI calibration (a, b, rho, m, sigma_param):
  [-0.1028  0.4074  0.7816  0.5903  0.4831]
  Residual sum-of-squares: 8.55e-11  (essentially exact fit)

SVI implied vols vs market:
      K    IV (mkt)    IV (SVI fit)
     85    0.211250        0.211250
     90    0.205000        0.205000
     95    0.201250        0.201250
    100    0.200000        0.200000
    105    0.201250        0.201250
    110    0.205000        0.205000
    115    0.211250        0.211250

Three things to read off. (1) The implied-volatility extraction from prices is exact to — Brent root-finding on the BS-call function is straightforward. (2) The mock market has a clean parabolic smile centred at : ATM vol is 20%, deep OTM (K=85 puts, K=115 calls) is 21.1%. (3) The SVI fit nails the curve essentially exactly (residual ) — the five parameters can absorb a wide variety of empirical smile shapes. Real-world fits have residuals on the order of bid-ask spread (basis points of vol), but SVI is flexible enough to be limited by data quality rather than functional form.

Calibration in practice

Daily routine in a derivatives trading desk:

  1. Collect MARKET QUOTES: bid/ask vols (or prices) for every traded strike and expiry in liquid options.
  2. SMOOTH each expiry's strike slice: fit SVI (or similar) to the observed strikes, getting a continuous for that expiry. Repeat for each maturity.
  3. STITCH expiries: enforce CALENDAR no-arbitrage (total variance must be monotone non-decreasing in ). Interpolation in time can be linear in , or cubic-spline in , etc.
  4. CALIBRATE a model: fit Heston, SABR, or a local-vol model to the smoothed surface. The MODEL is what gets used to price EXOTIC options (barrier, lookback, Asian) that aren't directly quoted.
  5. UPDATE every few minutes during market hours. The surface moves with the underlying and with vol-of-vol shocks.

Local vol vs stochastic vol

Two model families dominate exotic-option pricing.

LOCAL VOLATILITY (Dupire 1994): pick a function such that the Black-Scholes PDE with this position-dependent vol reproduces the observed surface EXACTLY. Dupire derived the explicit formula

where are the option prices on the surface. CONSISTENT with the entire vanilla surface by construction. The catch: local vol is a NON-MARKOV deterministic function — it ignores the fact that volatility itself moves through time independent of . Dynamics of the smile under local vol are unrealistic (the future smile collapses too fast).

STOCHASTIC VOLATILITY (Heston 1993): introduce a second stochastic process for instantaneous variance. The Heston model is the workhorse:

with . The negative correlation for equities produces the SKEW; the volatility of variance produces the CURVATURE. Five parameters are typically calibrated against the entire surface daily. Heston's vanilla option pricing has a SEMI-ANALYTIC characteristic-function form (the Lewis / Lipton inversion formula), making calibration tractable.

Modern production codes often use LOCAL STOCHASTIC VOLATILITY (LSV): Heston-style dynamics multiplied by a local-vol correction that ensures exact calibration to the surface. Get the realistic dynamics of stochastic vol AND the exact-fit property of local vol.

What's used when

Related