SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 4.3.1 The Hessian and second-order Taylor expansionConcept · 35 min
      2. 4.3.2 Minima, maxima and saddle pointsConcept · 30 min
      3. 4.3.3 Practice set: second-order methodsPractice set · 20 min
      4. 4.3.4 Case study: delta–gamma–vega P&LCase study · 35 min

Level 4, Multivariable calculus and optimisation 4.3 Second order Lesson 4 of 4

Case studyLesson 4.3.4 in Level 4, Multivariable calculus and optimisation. About 35 minutes.

Case study: delta–gamma–vega P&L

The call as a function of two variables

Hold the strike KK, the rate rr and the time to expiry TT fixed. Then the call's value V(S,σ)V(S, \sigma) is a function of two variables: the stock price SS and the volatility σ\sigma, a decimal per year. Case study 4.1.4 took its partial derivatives one at a time. Its gradient is (Δ,vega)(\Delta, \text{vega}): delta ∂V∂S=0.570158\frac{\partial V}{\partial S} = 0.570158 and vega ∂V∂σ=27.7721\frac{\partial V}{\partial \sigma} = 27.7721, or 0.2777 per vol point, for the six-month call at the money.

Its Hessian (lesson 4.3.1) has three different entries:

H=(Γvannavannavolga)H = \begin{pmatrix} \Gamma & \text{vanna} \\ \text{vanna} & \text{volga} \end{pmatrix}
  • Gamma Γ=∂2V∂S2\Gamma = \frac{\partial^2 V}{\partial S^2}, the change in delta per dollar (case study 2.7.4).
  • Vanna =∂2V∂S ∂σ= \frac{\partial^2 V}{\partial S\,\partial\sigma}. Mixed partials do not depend on the order (lesson 4.1.2), so it is two things at once: the change in delta per unit of σ\sigma, and the change in vega per dollar.
  • Volga =∂2V∂σ2= \frac{\partial^2 V}{\partial \sigma^2}, the change in vega per unit of σ\sigma.

The Derivatives Modelling course derives the formulas; here, take them as given and check them by bumping. They use d1d_1 and d2=d1−σTd_2 = d_1 - \sigma\sqrt{T} from case study 2.4.6, and the standard normal density φ\varphi.

Rule: Vanna and volga of a call

vanna=−φ(d1) d2σvolga=vega⋅d1d2σ\begin{gathered} \text{vanna} = -\frac{\varphi(d_1)\,d_2}{\sigma} \\[6pt] \text{volga} = \text{vega} \cdot \frac{d_1d_2}{\sigma} \end{gathered}

With Δ=N(d1)\Delta = N(d_1), vega =ST φ(d1)= S\sqrt{T}\,\varphi(d_1) and Γ=φ(d1)SσT\Gamma = \frac{\varphi(d_1)}{S\sigma\sqrt{T}} (case studies 2.7.4 and 2.4.6), these give the gradient and the Hessian of V(S,σ)V(S, \sigma).

strike 100strike 120
price6.37100.8914
d1d_1, d2d_20.1768, 0.0354−1.1124-1.1124, −1.2539-1.2539
delta Δ\Delta0.57020.1330
vega27.772115.1941
gamma Γ\Gamma0.0277720.015194
vanna−0.0694-0.06941.3471
volga0.8679105.9655

At the money, d2=0.0354d_2 = 0.0354 is close to 0, so vanna and volga are both small. At strike 120, d1d_1 and d2d_2 are both near −1.2-1.2. Vanna is positive there: a rise in volatility makes the call more likely to finish in the money, so its delta rises. Volga is large: vega itself grows with volatility.

Per vol point. Desks measure volatility moves in vol points, 0.01 of σ\sigma (case study 2.4.6). For strike 120, vanna × 0.01=0.0135\times\ 0.01 = 0.0135 is the rise in delta for one vol point; the delta formula at 21% gives 0.1463, up from 0.1330. Volga × 0.012=0.0106\times\ 0.01^2 = 0.0106 is the rise in vega per vol point, for one vol point; at 21% it rises by 0.0102.

Try it

call is case study 2.4.6's formula with SS and σ\sigma first. greeks returns delta, vega, gamma, vanna and volga, with d1d_1 and d2d_2.

The loop prints each row of the table for the two strikes. The last two lines compare vanna and volga per vol point with repricing at 21%.

⌘/Ctrl + Enter runs the cell

Checking by bumping

Lesson 4.1.2's central differences estimate all three entries from prices alone. With case study 2.8.4's bumps, $1 to the stock (h=1h = 1, or 1%) and one vol point to the volatility (k=0.01k = 0.01), it takes nine prices: today's, the stock bumped up and down, the volatility bumped up and down, and the four corners for vanna.

gammavannavolga
strike 100, formula0.027772−0.069430-0.0694300.867879
strike 100, bumped0.027762−0.069081-0.0690810.871773
strike 120, formula0.0151941.347119105.965466
strike 120, bumped0.0151931.344299105.995559

Every bumped value is within about 0.5% of the formula. At the money the relative errors look largest only because vanna and volga are so small there.

Try it

bumped applies the second differences and the four-corner formula of lesson 4.1.2 to the price. Each line prints a Greek from the formula, bumped, and the relative error.

⌘/Ctrl + Enter runs the cell

The second-order estimate

Write ΔS\Delta S for the change in the stock price and Δσ\Delta\sigma for the change in volatility, as a decimal. The step is h=(ΔS,Δσ)\mathbf{h} = (\Delta S, \Delta\sigma), and lesson 4.3.1's quadratic copy of VV gives the change ΔV\Delta V.

Rule: Second-order P&L estimate

ΔV≈Δ⋅ΔS+vega⋅Δσ+12Γ (ΔS)2+vanna⋅ΔS Δσ+12 volga (Δσ)2\begin{gathered} \Delta V \approx \Delta \cdot \Delta S + \text{vega} \cdot \Delta\sigma \\ + \tfrac{1}{2}\Gamma\,(\Delta S)^2 + \text{vanna} \cdot \Delta S\,\Delta\sigma \\ + \tfrac{1}{2}\,\text{volga}\,(\Delta\sigma)^2 \end{gathered}

The first line is ∇V⋅h\nabla V \cdot \mathbf{h}, the first-order estimate of case study 4.2.4. The rest is 12h⊤Hh\frac{1}{2}\mathbf{h}^\top H\mathbf{h}; as in lesson 4.3.1, the cross term has no 12\frac{1}{2}. With Δσ=0\Delta\sigma = 0 it is case study 2.7.4's delta–gamma estimate; without vanna and volga it is case study 4.1.4's.

Time is held fixed: this is an instant move, as in a stress test. Case studies 4.1.4 and 4.2.4 add theta for the passage of a day.

The letter Δ\Delta still has two jobs, as in case study 2.7.4: alone it is delta, and in ΔS\Delta S it means "change in". Here is the sell-off of the hook, ΔS=−8\Delta S = -8 and Δσ=0.05\Delta\sigma = 0.05, term by term:

termstrike 100strike 120
Δ⋅ΔS\Delta \cdot \Delta S−4.5613-4.5613−1.0638-1.0638
vega ⋅ Δσ\cdot\ \Delta\sigma1.38860.7597
12Γ(ΔS)2\frac{1}{2}\Gamma(\Delta S)^20.88870.4862
vanna ⋅ ΔS Δσ\cdot\ \Delta S\,\Delta\sigma0.0278−0.5388-0.5388
12\frac{1}{2}volga(Δσ)2(\Delta\sigma)^20.00110.1325
delta, gamma and vega−2.2840-2.28400.1821
all five terms−2.2551-2.2551−0.2243-0.2243
repriced−2.4233-2.4233−0.2438-0.2438

For strike 120, delta, gamma and vega add up to a gain of 0.1821. The vanna term, −0.5388-0.5388, turns it into a loss: the rise in volatility lifted delta, so the fall in the stock cost more than delta alone says. With all five terms the estimate is within 2 cents of repricing.

At the money the two cross terms add only 0.0289, and the estimate stays 17 cents off. The section on failures explains that gap.

Try it

The loop prints the five terms for each strike, then the sum of the first three, the sum of all five, and the repriced change. The second line for each strike adds the two cross terms.

⌘/Ctrl + Enter runs the cell

A grid of scenarios

A risk system does not look at one scenario. It reprices a grid: here the stock moves by −10-10, −5-5, −2-2, 0, 2, 5 or 10 dollars and the volatility by −5-5, −2-2, 0, 2 or 5 vol points, 35 scenarios in all. Each estimate is scored by its largest error and by its root-mean-square error: square each error, average, and take the square root.

estimatestrike 100: RMSlargeststrike 120: RMSlargest
delta–gamma (2.7.4)0.86921.38920.59161.6049
first order, delta and vega (4.2.4)0.77011.81270.51761.6049
delta, gamma and vega (4.1.4)0.13200.42410.27100.8452
full second order0.13240.45780.09440.3501

Neither first-order nor stock-only estimates are good enough once both inputs move: their RMS errors are 52 to 87 cents. At the money, gamma and vega do the rest of the work, and vanna and volga change almost nothing. At strike 120 they cut the RMS error almost three times, from 0.2710 to 0.0944.

The pattern holds away from the money on either side:

strikevannavolgaRMS error without vanna and volgaRMS error with them
90−1.0178-1.017866.34170.21490.1155
100−0.0694-0.06940.86790.13200.1324
1101.125739.57460.22450.0944
1201.3471105.96550.27100.0944

The error of the estimated change in the strike-120 call over a grid of stock and volatility moves, shaded where it is under 2 cents. Without the cross terms the estimate is good only when one input stays still; with vanna and volga it holds along the sell-off diagonal too.

Try it

errors scores the four estimates on the 35 scenarios for one strike and expiry. The first loop prints the first table, one line per estimate.

The second prints the rows of the strike table: vanna, volga and the two RMS errors.

⌘/Ctrl + Enter runs the cell
Check yourself
A risk report shows delta, gamma and vega for a book of out-of-the-money calls, and nothing else. In a sell-off the stock falls and volatility rises. Which missing term matters most?

Where it fails

At the money: the terms of degree 3

Case study 4.1.4's estimate missed its worst night, the stock down $10 and volatility up 5 points, by −0.2165-0.2165. It traced the miss to vega changing with the stock: 26.34 at $95, against 27.77 at $100. Vanna is that change per dollar, but measured at $100 itself.

There vanna is only −0.0694-0.0694: vega peaks at $99.50, so at $100 it is almost flat. Moved by vanna alone, vega would be 28.12 at $95, higher rather than lower. What brings it down to 26.34 is the bend of vega in SS, about −0.138-0.138 per dollar squared, and that is a term of degree 3 in the expansion of VV (lesson 4.3.1). With the bend, the estimate of vega at $95 is 26.40.

So at the money the second-order estimate still misses the $8 sell-off of the term table by −0.1682-0.1682. Its largest term of degree 3 is 12∂Γ∂σ(ΔS)2Δσ\frac{1}{2}\frac{\partial\Gamma}{\partial\sigma}(\Delta S)^2\Delta\sigma. Since the order of differentiation does not matter, ∂Γ∂σ\frac{\partial\Gamma}{\partial\sigma} is the same bend of vega, −0.1383-0.1383 by a central difference.

That term is −0.2213-0.2213, and the next one, 16∂Γ∂S(ΔS)3\frac{1}{6}\frac{\partial\Gamma}{\partial S}(\Delta S)^3, is +0.0531+0.0531. Together they make −0.1682-0.1682, the whole miss. The second-order estimate cannot see them, however carefully its five Greeks are computed.

Large moves

The error of degree 3 grows like the cube of the move. In a crash, with the stock down $20 and volatility up 10 points, the at-the-money call loses 4.7508. The second-order estimate says 2.9283, off by 1.8225, or 38% of the loss. For strike 120 and a $20 rally with volatility down 5 points, the estimate is off by 1.3657.

Near expiry

Case study 2.7.4 found gamma growing like 1T\frac{1}{\sqrt{T}} at the money, as the price curve closes in on the payoff's corner. The same happens to the whole expansion. On the same 35-scenario grid, for the at-the-money call:

time to expirypriceΓ\GammaRMS error, full second orderlargest error
6 months6.37100.02780.13240.4578
1 month2.42710.06890.38911.1656
1 week1.13520.14381.77653.67

With a week left the typical error is larger than the price of the option. For short-dated options near the money, desks reprice every scenario.

Try it

The first line is vega at $95 and $100, where vega peaks, and vega at $95 moved by vanna alone and with its bend. The second is the at-the-money sell-off: the miss, ∂Γ∂σ\frac{\partial\Gamma}{\partial\sigma} by a central difference of the gamma formula, and the two terms of degree 3.

The next two lines are the large moves: true change, estimate, error and error as a share of the true change. The loop scores the grid at three expiries.

⌘/Ctrl + Enter runs the cell

Try it

The first line is the short position's vanna term in the sell-off. The second is det⁡H\det H for strike 120, and the bend term for ΔS=−8\Delta S = -8, Δσ=0.10\Delta\sigma = 0.10.

The last line measures σ\sigma in vol points instead, which multiplies vanna by 0.01 and volga by 0.0120.01^2: the eigenvalues change, the sign of the determinant does not.

⌘/Ctrl + Enter runs the cell
Check yourself
For the at-the-money call, adding vanna and volga to delta, gamma and vega barely changes the error on the grid. Why?
Exercise

The second-order P&L estimate

Write two functions for a Black–Scholes call, using norm.cdf and norm.pdf from scipy.stats.

  1. call_second_order(S, K, T, r, sigma) returns the tuple (gamma, vanna, volga), the entries of the Hessian of V(S,σ)V(S, \sigma), from the formulas above.
  2. second_order_change(S, K, T, r, sigma, dS, dsigma) returns the second-order estimate of the change in the call's value when the stock moves by dS and the volatility by dsigma (a decimal: 0.05 is 5 vol points).

If T≤0T \le 0 or σ≤0\sigma \le 0, both raise ValueError: the formulas divide by them.

The tests check the two strikes of this case study, and compare vanna and volga with central differences for another option. They try the sell-off, a stock move alone (case study 2.7.4's estimate) and a volatility move alone, then pass T=0T = 0 and σ=0\sigma = 0.

See the tests
import math

from scipy.stats import norm


def _call(S, K, T, r, sigma):
    d1 = (math.log(S / K) + (r + sigma**2 / 2) * T) / (sigma * math.sqrt(T))
    return S * norm.cdf(d1) - K * math.exp(-r * T) * norm.cdf(d1 - sigma * math.sqrt(T))


def _triple(result):
    assert isinstance(result, tuple) and len(result) == 3, f"expected a tuple (gamma, vanna, volga), got {result!r}"
    return result


def test_at_the_money():
    """The 6-month call at strike 100: gamma 0.027772, vanna −0.069430, volga 0.867879"""
    gamma, vanna, volga = _triple(call_second_order(100, 100, 0.5, 0.03, 0.2))
    expect_close(gamma, 0.027772131739916564, what="gamma", rel=1e-9)
    expect_close(vanna, -0.06943032934979133, what="vanna", rel=1e-9)
    expect_close(volga, 0.8678791168723916, what="volga", rel=1e-9)


def test_out_of_the_money():
    """Strike 120: vanna 1.347119 and volga 105.9655, far from zero"""
    _, vanna, volga = _triple(call_second_order(100, 120, 0.5, 0.03, 0.2))
    expect_close(vanna, 1.3471194695733808, what="vanna at strike 120", rel=1e-9)
    expect_close(volga, 105.9654660703653, what="volga at strike 120", rel=1e-9)


def test_by_bumping():
    """Vanna and volga match central differences of the price for S = 50, K = 55, T = 0.25, σ = 0.35"""
    S, K, T, r, s, h, k = 50, 55, 0.25, 0.01, 0.35, 1e-2, 1e-3
    vanna_b = (_call(S + h, K, T, r, s + k) - _call(S + h, K, T, r, s - k) - _call(S - h, K, T, r, s + k) + _call(S - h, K, T, r, s - k)) / (4 * h * k)
    volga_b = (_call(S, K, T, r, s + k) - 2 * _call(S, K, T, r, s) + _call(S, K, T, r, s - k)) / k**2
    _, vanna, volga = _triple(call_second_order(S, K, T, r, s))
    expect_close(vanna, vanna_b, what="vanna against the four-corner difference", rel=1e-4)
    expect_close(volga, volga_b, what="volga against the second difference in sigma", rel=1e-4)


def test_sell_off():
    """Strike 120, stock −$8 and volatility +5 points: −0.2243, within 3 cents of repricing (−0.2438)"""
    est = second_order_change(100, 120, 0.5, 0.03, 0.2, -8, 0.05)
    expect_close(est, -0.22428677057242452, what="second_order_change(..., dS=-8, dsigma=0.05)", rel=1e-8)
    true = _call(92, 120, 0.5, 0.03, 0.25) - _call(100, 120, 0.5, 0.03, 0.2)
    assert abs(est - true) < 0.03, f"expected within 0.03 of the repriced change {true:.4f}, got {est:.4f}"


def test_stock_only():
    """With no volatility move it is case study 2.7.4's delta–gamma estimate: −2.5036 for a $5 fall"""
    expect_close(second_order_change(100, 100, 0.5, 0.03, 0.2, -5, 0), -2.503638865254377, what="second_order_change(..., dS=-5, dsigma=0)", rel=1e-8)


def test_volatility_only():
    """With no stock move it is vega·Δσ + ½·volga·Δσ²: 1.3897 for +5 vol points"""
    expect_close(second_order_change(100, 100, 0.5, 0.03, 0.2, 0, 0.05), 1.3896914358919188, what="second_order_change(..., dS=0, dsigma=0.05)", rel=1e-8)


def test_at_expiry():
    """T = 0 raises ValueError: the formulas divide by zero"""
    try:
        call_second_order(100, 100, 0, 0.03, 0.2)
    except ValueError:
        return
    raise AssertionError("expected ValueError for T = 0")


def test_zero_volatility():
    """σ = 0 raises ValueError in second_order_change too"""
    try:
        second_order_change(100, 100, 0.5, 0.03, 0, 1, 0.01)
    except ValueError:
        return
    raise AssertionError("expected ValueError for sigma = 0")

Summary card

  • With KK, rr and TT fixed, a call's value V(S,σ)V(S, \sigma) has gradient (Δ,vega)(\Delta, \text{vega}) and Hessian (Γvannavannavolga)\begin{pmatrix} \Gamma & \text{vanna} \\ \text{vanna} & \text{volga} \end{pmatrix}. Vanna =−φ(d1)d2σ= -\frac{\varphi(d_1)d_2}{\sigma} and volga =vega⋅d1d2σ= \text{vega}\cdot\frac{d_1d_2}{\sigma}; nine bumped prices confirm all three entries.
  • Second-order estimate: ΔV≈Δ⋅ΔS+vega⋅Δσ+12Γ(ΔS)2+vanna⋅ΔS Δσ+12volga(Δσ)2\Delta V \approx \Delta\cdot\Delta S + \text{vega}\cdot\Delta\sigma + \frac{1}{2}\Gamma(\Delta S)^2 + \text{vanna}\cdot\Delta S\,\Delta\sigma + \frac{1}{2}\text{volga}(\Delta\sigma)^2.
  • At the money vanna and volga nearly vanish, and gamma and vega do the work. Away from the money they matter: in a sell-off the vanna term can flip the sign of the estimate.
  • What the expansion leaves out is of degree 3, mostly the change of gamma with volatility, which is also the bend of vega in SS behind case study 4.1.4's leftover. It grows with large moves and near expiry, where desks reprice instead.
  • Mind the units (per vol point), the signs of short positions, and that stock and volatility tend to move together.

Free further reading

Optional. This lesson covers what you need; these add more examples and practice. All are free to read.