An interactive visualisation  ::  five sections

vol-surface: an arbitrage-free volatility surface you can break

An implied-volatility slice is a claim about the risk-neutral distribution of the terminal price. Push the parameters far enough and the claim becomes incoherent: negative density, and a static arbitrage. SVI and SABR calibration with the Gatheral-Jacquier conditions screened on every fit, computed in this tab.

What the numbers are. Nobody quotes an option's price in dollars for long. They quote the implied volatility: the single number you would have to feed Black-Scholes to make it return the price actually being paid. It's a change of units, in the way a bond's yield is a change of units on its price, and it makes options with different strikes and dates comparable.

Where on the x-axis. Strike is written as log-moneyness, k = log(K / F), the strike over the forward price. So k = 0 is at-the-money, negative k is a downside put, and k = -0.4 is a strike about a third below the forward. Using a ratio rather than a dollar strike lets one picture serve any underlying.

Why it's a shape and not a number. If Black-Scholes were true, every strike would imply the same volatility and the plot would be flat. It never is. Plot implied vol against k for one expiry and you get a curve. In FX it is roughly symmetric and genuinely earns the name smile; in equity index options it's monotone decreasing across most of the traded range, so skew or smirk is the accurate word and the one used below, because downside puts are persistently bid for crash protection. Both are slices of the same object. Stack them across expiries and you have a surface. Fitting one means choosing a function that passes near every quote, and this library uses SVI, Gatheral's stochastic-volatility-inspired parameterisation, in raw form w(k) = a + b(ρ(k-m) + √((k-m)² + σ²)). Five numbers per expiry, working in total implied variance w = σ²BST rather than volatility, because total variance is the quantity that must be monotone in maturity for the surface to be free of calendar arbitrage. Slices are fitted quasi-explicitly: with m and σ held fixed the problem is linear in the other three, so the search is two-dimensional rather than five.

00
The surface · fitted from quotes

Five expiries, one surface

Twenty-one strikes at each of five expiries, quoted with noise, fitted slice by slice and then asked for a vol nobody quoted.

Log-moneyness runs across, expiry runs back, and the height of the sheet is the implied volatility the fitted surface returns there. The ridges picked out in dark are the five expiries that were actually quoted; everything between them is the surface interpolating, which is the whole point of fitting one. A desk needs a vol for a strike and a date nobody printed, and the surface is the object that supplies it.

The left edge rises because downside puts trade at higher implied vol than upside calls, which is the equity skew. The sheet flattens going back because uncertainty about the next month is more concentrated than uncertainty about the next two years, once both are expressed as an annualised rate.

Three handles reshape it, and each one refits all five slices from scratch rather than redrawing a formula. Level scales total variance, lifting the whole sheet. Skew leans SVI's rho toward the downside, steepening the left wing against the right. Wings steepens b, lifting both wings away from the money. Drag the sheet itself to turn it.

checking
Vol level1.00×
Skew1.00×
Wings1.00×
waiting

01
The inversion · where every quoted vol comes from

Price in, volatility out

Before there is a surface there is one option, one price, and the number you have to solve for to explain it.

That surface is built out of numbers that have to be extracted from prices first, so it is worth going back a step. Black-Scholes maps a volatility to a price in closed form, and that direction is easy. The direction a desk actually needs is the other one: a price is on the screen, and the volatility that produced it has to be solved for, because the formula can't be inverted algebraically. This library uses Brent's method, bracketed between the no-arbitrage bounds on the price.

Below, a volatility is turned into a price at every strike and then recovered back out of that price. If the two halves disagree, nothing further up the page means anything, so the round trip is the first thing worth checking:

checking
Volatility in20.0%
Maturity1.00y
Quote noise for the screen25 bp
waiting
Finding · experiment 01

02
Break it · the arbitrage check

Break the density

Five numbers describe one slice. Two of them, pushed far enough, produce a slice that admits static arbitrage, and the library says so instead of handing it back quietly.

A slice isn't only a curve. It's a complete statement about the risk-neutral distribution of the terminal price. Breeden and Litzenberger showed in 1978 that ∂²C/∂K², the second derivative of the undiscounted call price with respect to strike, is the risk-neutral density: the market's odds on every outcome are already implied by the prices it quotes, and you recover them by differentiating twice. A slice and a distribution are the same object written two ways, and a slice with the wrong convexity in strike describes a measure that isn't a probability measure.

That's what makes this a trade rather than a modelling complaint. Buy one call at K-d, sell two at K, buy one at K+d, and you own a butterfly. It pays out only if the stock lands near K, so it can never be worth less than nothing, and its price is the implied density around K times the width. If that density goes negative the butterfly costs less than nothing: somebody is paid to hold a position that can't lose. That's the arbitrage, and it's why a fitter that ignores the check is worse than useless.

Below is one slice shown three ways. The smile is what a desk publishes. g(k) is the function Gatheral and Jacquier derived in 2014 from w and its first two derivatives in k. Its sign is the condition itself: g(k) ≥ 0 across the strip is equivalent to a non-negative density, and it's far cheaper than differentiating prices, which is why the library checks it rather than the density. The density is the same failure seen literally, as a probability below zero.

The five handles are SVI's parameters. a shifts total variance up and down, b sets how steeply both wings rise, rho tilts the curve so one wing outruns the other, m slides the smile sideways, and sigma controls how rounded the bottom is. Raise b and drop sigma, which is to say make the wings steep and the base sharp, and all three panels turn over together:

checking
a  level0.021
b  wing slope0.075
rho  skew-0.60
m  shift0.00
sigma  curvature0.200
  
Finding · experiment 02

03
Calibrate · noisy quotes

Fitting marks that aren't clean

Real quotes aren't a curve. They are a curve plus whatever the market was doing when you took the snapshot.

Volatility is measured in basis points here, a hundredth of a percentage point, so 25 bp of noise nudges each quote by up to a quarter of a vol point either way. That's roughly what a screen looks like once bid-ask and stale marks are accounted for. The dots below are the perturbed quotes the fitter was handed, the lines are what it returned, RMS err in the table is the root-mean-square gap between them in basis points of vol, and ATM vol is the fitted value at k = 0.

The fit also has to pass two separate no-arbitrage tests. Butterfly is the within-expiry one from the last section: the density along a single smile must stay positive. Calendar is the across-expiry one: total implied variance at fixed k must be non-decreasing in maturity, since a calendar spread has a non-negative payoff, so quoting less total variance for more time is a static arbitrage in the other direction. Together the two are what "free of static arbitrage" means for a surface. Drag the noise up and watch the residuals grow:

Quote noise25 bp
Expiryabrhomsigma ATM volRMS err
waiting
Finding · experiment 03

04
SABR · the other model

A second opinion on the same quotes

SVI is a shape fitted to a slice. SABR is a stochastic-volatility model whose smile comes out of an expansion. Both get pointed at the same marks here.

SABR writes down a story instead of a shape. The forward is assumed to diffuse with a volatility that's itself random, and four numbers govern it. alpha is the starting volatility and sets the level. beta is the backbone: at 1 the forward moves in percentage terms as in Black-Scholes, at 0 it moves in absolute terms, and 0.5 is the usual compromise. Alpha means different things at different betas, because it's the volatility of the forward raised to beta rather than of the forward itself, so moving beta alone throws the level off. rho is the correlation between the forward and its own volatility, and it's what produces skew. nu is the volatility of volatility, and it's what produces curvature.

Hagan's formula turns those four into a smile through an asymptotic expansion, accurate when nu²T is small and drifting when it isn't. That's a known property rather than a bug, and validation.md measures the drift against a Monte Carlo of the SABR SDE instead of taking the formula's word for it: at the money, agreement to 1.5 bp when nu²T = 0.16, and a 393 bp gap when nu²T = 3.2. Here both models fit the one-year slice:

alpha  level1.884
beta  backbone0.50
rho  correlation-0.25
nu  vol of vol0.585
  
waiting
Finding · experiment 04

Five sections, one library, and every number above computed while you read. The same code is scored against Hagan, Gatheral and a Monte Carlo of the SABR SDE in validation.md, including the place where it disagrees with the reference by 393 basis points and says so.