Implied Volatility
Listen to a trader on the phone and you will rarely hear a dollar figure. You will hear
something like "I'll sell you the March 100 calls at 19.5 vol." Nobody quotes option prices in
dollars any more; the whole market quotes in volatility. The
Black–Scholes
formula is the dictionary that makes this possible — but it is being run
backwards. Instead of feeding in a volatility and reading off a price, the market fixes
the price (by trading it) and asks the formula: what \sigma
would have produced this? That number is the implied volatility, and inverting
the formula to find it is the subject of this page.
Formally: given everything else in the Black–Scholes formula —
S_0, K, r, T — and an observed market price
C^{\text{mkt}}, the implied volatility
\sigma_{\text{imp}} is the value solving
C_{BS}(S_0, K, r, \sigma_{\text{imp}}, T) = C^{\text{mkt}}.
A single unknown, a single equation. It sounds like high-school algebra. It is not — and seeing
exactly why is the first thing worth understanding.
Why there is no formula for the inverse
Solving an equation for one variable is easy when that variable is isolated. Here it
is buried three layers deep: \sigma sits inside
d_1 and d_2, which sit inside the
cumulative normal \Phi(\cdot), which has no elementary closed-form
inverse to begin with (it is itself defined by an integral with no antiderivative in elementary
functions). There is simply no sequence of algebraic moves — no "divide both sides by
\Phi" — that peels \sigma back out.
But the news is not all bad. The call price is a strictly increasing function
of \sigma — its slope is exactly
\text{vega} = \partial C/\partial\sigma = S_0\,\varphi(d_1)\sqrt{T} > 0
for every option with positive time left, where \varphi is the
standard normal density. A strictly increasing, continuous function that runs from
0 (as \sigma \to 0) up to
S_0 (as \sigma \to \infty) crosses every
value in between exactly once. So a solution always exists, is always unique —
and can always be found, just not written down. This is a job for a numerical
root-finder, hunting for the zero of
f(\sigma) = C_{BS}(\sigma) - C^{\text{mkt}} = 0.
Worked example: two hand-cranked Newton steps
Take the familiar textbook option: S_0 = K = 100,
r = 5\%, T = 1. Suppose it trades in the
market at C^{\text{mkt}} = \$12.00. We know from the Black–Scholes
page that a volatility of 20\% only produces
C \approx \$10.45 — too cheap, so the market's implied vol must be
higher than 20%. Newton's method finds it fast, using
\sigma_{n+1} = \sigma_n - \dfrac{C_{BS}(\sigma_n) - C^{\text{mkt}}}{\text{vega}(\sigma_n)}.
Step 1 — start at \sigma_0 = 20\%. We already have
C(\sigma_0) = 10.45, and at d_1 = 0.35 the
density is \varphi(0.35) \approx 0.3752, so
\text{vega}(\sigma_0) = 100 \times 0.3752 \times 1 \approx 37.52.
\sigma_1 = 0.20 - \frac{10.45 - 12.00}{37.52} = 0.20 + \frac{1.55}{37.52} \approx 0.2413.
Step 2 — check \sigma_1 = 24.13\%. Recomputing
d_1 = 0.3279, d_2 = 0.0866, and reading
off \Phi(0.3279) \approx 0.6293,
\Phi(0.0866) \approx 0.5345, gives
C(\sigma_1) = 100(0.6293) - 95.12(0.5345) \approx \$12.09.
Nine cents off target after a single step, starting from a guess that was 21% wrong. A
second Newton step (not shown) lands within a hundredth of a cent. This is why every pricing
engine on every trading floor solves for implied vol with Newton's method (or a close relative)
rather than a lookup table.
Coding the solver
Below is exactly the loop above, generalised and run to convergence. It reuses the
Black–Scholes call price and its vega, then Newton-iterates on volatility instead of on price:
// Standard normal cdf (Abramowitz–Stegun) and pdf.
function Phi(x: number): number {
const t = 1 / (1 + 0.2316419 * Math.abs(x));
const d = 0.3989422804014327 * Math.exp(-(x * x) / 2);
const p = d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));
return x >= 0 ? 1 - p : p;
}
function phi(x: number): number {
return 0.3989422804014327 * Math.exp(-(x * x) / 2);
}
function bsCall(S0: number, K: number, r: number, sigma: number, T: number): number {
const v = sigma * Math.sqrt(T);
const d1 = (Math.log(S0 / K) + (r + 0.5 * sigma * sigma) * T) / v;
const d2 = d1 - v;
return S0 * Phi(d1) - K * Math.exp(-r * T) * Phi(d2);
}
function vega(S0: number, K: number, r: number, sigma: number, T: number): number {
const v = sigma * Math.sqrt(T);
const d1 = (Math.log(S0 / K) + (r + 0.5 * sigma * sigma) * T) / v;
return S0 * phi(d1) * Math.sqrt(T);
}
// Newton's method: find sigma so bsCall(...) matches the market price.
function impliedVol(S0: number, K: number, r: number, T: number, marketPrice: number): number {
let sigma = 0.20; // starting guess
for (let n = 1; n <= 20; n++) {
const diff = bsCall(S0, K, r, sigma, T) - marketPrice;
if (Math.abs(diff) < 1e-8) break;
sigma = sigma - diff / vega(S0, K, r, sigma, T);
}
return sigma;
}
const S0 = 100, K = 100, r = 0.05, T = 1, marketPrice = 12.00;
let sigma = 0.20;
for (let n = 1; n <= 6; n++) {
const price = bsCall(S0, K, r, sigma, T);
const diff = price - marketPrice;
console.log(`step ${n}: sigma = ${(sigma * 100).toFixed(4)}% price = ${price.toFixed(4)} diff = ${diff.toFixed(4)}`);
if (Math.abs(diff) < 1e-8) break;
sigma = sigma - diff / vega(S0, K, r, sigma, T);
}
console.log(`implied vol ≈ ${(sigma * 100).toFixed(4)}% (via impliedVol: ${(impliedVol(S0, K, r, T, marketPrice) * 100).toFixed(4)}%)`);
Watch the diff column collapse toward zero in three or four steps — the same quadratic
convergence that made Newton's method so effective on any well-behaved root-finding problem.
When Newton misbehaves: the safety net
Newton's weak point here is exactly the same as everywhere else: it needs
\text{vega} \ne 0, and vega gets tiny for options
deep in or deep out of the money, or very close to expiry. A near-zero vega means a near-flat
tangent — the update step
\text{diff}/\text{vega} can rocket the guess miles away, sometimes
into a negative or absurdly large volatility. The industrial-strength fix is the same hybrid
idea used everywhere in root-finding: fall back on bisection, which cannot be
derailed, whenever Newton wanders outside a sane bracket like
[0.1\%, 500\%].
function Phi2(x: number): number {
const t = 1 / (1 + 0.2316419 * Math.abs(x));
const d = 0.3989422804014327 * Math.exp(-(x * x) / 2);
const p = d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));
return x >= 0 ? 1 - p : p;
}
function callPrice(S0: number, K: number, r: number, sigma: number, T: number): number {
const v = sigma * Math.sqrt(T);
const d1 = (Math.log(S0 / K) + (r + 0.5 * sigma * sigma) * T) / v;
const d2 = d1 - v;
return S0 * Phi2(d1) - K * Math.exp(-r * T) * Phi2(d2);
}
// Bisection is slower but immune to the tiny-vega blow-up: bracket, then squeeze.
function impliedVolBisect(S0: number, K: number, r: number, T: number, marketPrice: number): number {
let lo = 0.001, hi = 5.0; // 0.1% to 500% — a comically wide, always-safe bracket
for (let n = 1; n <= 60; n++) {
const mid = (lo + hi) / 2;
if (callPrice(S0, K, r, mid, T) > marketPrice) hi = mid; else lo = mid;
}
return (lo + hi) / 2;
}
// A deep out-of-the-money option: vega here is a whisper, not a shout.
const S0 = 100, K = 160, r = 0.05, T = 0.25, marketPrice = 0.35;
const iv = impliedVolBisect(S0, K, r, T, marketPrice);
console.log(`deep-OTM implied vol ≈ ${(iv * 100).toFixed(3)}%`);
console.log(`check: bsCall at that vol = ${callPrice(S0, K, r, iv, T).toFixed(4)} (target ${marketPrice})`);
Production systems typically start with a Newton or Newton-like step (secant, Brent's method)
for speed, then quietly switch to bisection the moment a step looks unsafe — exactly the hybrid
philosophy behind general-purpose root-finders.
It is worth pausing on how strong the uniqueness guarantee really is. Vega
S_0\varphi(d_1)\sqrt{T} is a product of three manifestly positive
quantities (for T > 0), so it can never be zero or negative — the
call price curve, plotted against \sigma, only ever climbs, never
levels off or turns back down. Combine that with the two endpoints,
C \to \max(S_0 - Ke^{-rT}, 0) as
\sigma \to 0 and C \to S_0 as
\sigma \to \infty, and any market price strictly between those two
no-arbitrage bounds is guaranteed to have one and only one
\sigma that produces it. This is precisely bisection's favourite
setting: a strictly monotone, continuous function with a guaranteed sign change — the
implied-vol problem is, in a sense, the friendliest root-finding problem in all of
finance.
Newton and bisection will both happily grind away on a bad input and either crash into a
boundary or hand you a nonsense answer. Two traps to know:
-
Impossible prices. Any quoted call price must lie strictly between its two
arbitrage bounds, \max(S_0 - Ke^{-rT}, 0) < C^{\text{mkt}} < S_0.
A price at or outside those bounds implies a volatility of 0 or
\infty — not a modelling glitch, but a genuine arbitrage (or a
stale/bad quote) that should be rejected before the solver ever runs.
-
American vs. European. If you feed an American-style option's
market price into the European Black–Scholes formula, the early-exercise premium
baked into that price gets misread as extra volatility — inflating the "implied vol" for no
volatility-related reason at all. Always match the model to the exercise style of the
contract actually trading.