SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 2.8.1 Finite differences and their errorsConcept · 35 min
      2. 2.8.2 Trapezoid and Simpson rulesConcept · 35 min
      3. 2.8.3 Practice set: numerical calculusPractice set · 20 min
      4. 2.8.4 Case study: bump-and-reprice GreeksCase study · 35 min

Level 2, Single-variable calculus 2.8 Numerical calculus Lesson 4 of 4

Case studyLesson 2.8.4 in Level 2, Single-variable calculus. About 35 minutes.

Case study: bump-and-reprice Greeks

Greeks by repricing

The sensitivities of an option's value to its inputs are its Greeks. Each is a derivative with respect to one input, all the others held fixed. Take the call of case studies 2.4.6 and 2.6.5: stock price S=100S = 100, strike K=100K = 100, T=0.5T = 0.5 years, rate r=0.03r = 0.03 and volatility σ=0.2\sigma = 0.2, worth $6.3710.

  • Delta Δ\Delta is the slope of the price cc in SS. Case study 2.7.4 gave it as N(d1)=0.570158N(d_1) = 0.570158, in dollars of option per dollar of stock.
  • Gamma Γ\Gamma is the second derivative in SS, φ(d1)SσT=0.027772\frac{\varphi(d_1)}{S\sigma\sqrt{T}} = 0.027772 (case study 2.7.4): the change in delta per dollar.
  • Vega is the slope in σ\sigma, ST φ(d1)=27.7721S\sqrt{T}\,\varphi(d_1) = 27.7721 (case study 2.4.6). Desks quote it per vol point, a change of 0.01 in σ\sigma: 0.2777.

Here NN is the standard normal cumulative distribution, φ\varphi its density, and d1d_1 the one in case study 2.4.6's formula. A pricing system replaces each derivative by a finite difference of the price, as in lesson 2.8.1.

Rule: Bump-and-reprice

Δ≈c(S+h)−c(S−h)2hΓ≈c(S+h)−2c(S)+c(S−h)h2vega≈c(σ+k)−c(σ−k)2k\begin{gathered} \Delta \approx \frac{c(S + h) - c(S - h)}{2h} \\[6pt] \Gamma \approx \frac{c(S + h) - 2c(S) + c(S - h)}{h^2} \\[6pt] \text{vega} \approx \frac{c(\sigma + k) - c(\sigma - k)}{2k} \end{gathered}

hh is the bump to the stock price, in dollars, and kk the bump to the volatility. Five prices give all three Greeks: today's, two with the stock bumped and two with the volatility bumped.

Case study 2.7.4 checked delta and gamma with a 1-cent bump. Desks usually bump the stock by 1% and the volatility by one vol point, k=0.01k = 0.01:

Greekformulabumpederror
delta0.5701580.570054−1.0×10−4-1.0 \times 10^{-4}
gamma0.0277720.027762−9.9×10−6-9.9 \times 10^{-6}
vega per vol point0.2777210.277717−4.5×10−6-4.5 \times 10^{-6}

These are truncation errors (lesson 2.8.1), 0.02% of delta and 0.04% of gamma. That is far below the doubt in the inputs themselves: nobody knows the volatility to four digits.

Try it

call is case study 2.4.6's formula, with SS and σ\sigma first. formulas returns the three Greeks from their closed forms, vega per vol point.

The cell bumps the stock by $1, which is 1%, and the volatility by 0.01. The last column is each error as a share of the Greek.

⌘/Ctrl + Enter runs the cell

How big a bump?

Lesson 2.8.1 split the error of a difference into two parts. Truncation grows with the bump; rounding grows as the bump shrinks, and for a second difference it grows like 1h2\frac{1}{h^2}. Here is the call with bumps from 10% of the stock price down to 10−910^{-9} of it:

bump, share of SShhdelta errorbumped gammagamma error
10%109.4×10−39.4 \times 10^{-3}0.0268129.6×10−49.6 \times 10^{-4}
1%11.0×10−41.0 \times 10^{-4}0.0277629.9×10−69.9 \times 10^{-6}
0.01%0.011.0×10−81.0 \times 10^{-8}0.0277721.0×10−91.0 \times 10^{-9}
10−610^{-6}10−410^{-4}1.9×10−111.9 \times 10^{-11}0.0277721.4×10−71.4 \times 10^{-7}
10−810^{-8}10−610^{-6}5.7×10−95.7 \times 10^{-9}0.0355277.8×10−37.8 \times 10^{-3}
10−910^{-9}10−710^{-7}3.1×10−83.1 \times 10^{-8}−0.710543-0.7105430.74

Gamma is the sensitive one. Its best bump is near 1 cent, which is lesson 2.8.1's ε1/4s\varepsilon^{1/4}s with the scale s=S=100s = S = 100: 0.012. Delta's best bump, near ε1/3s≈6×10−4\varepsilon^{1/3}s \approx 6 \times 10^{-4}, is smaller. Rounding barely touches delta: even with a bump of 10−710^{-7} dollars it is off by only 3.1×10−83.1 \times 10^{-8}.

So why do desks bump by 1%, a hundred times the best step for this formula? First, a 1% move is one the market really makes, so the bumped numbers describe a plausible day. Case study 2.2.4 said the same of the 1 bp bump.

Second, most pricers are far noisier than this formula.

Try it

The loop prints each row of the table: the bump as a share of SS, the bumped delta's error, the bumped gamma and its error. The last column is gamma's error as a share of gamma.

⌘/Ctrl + Enter runs the cell

A noisy pricer

Case study 2.6.5 also priced this call with quad over the whole line, corner and all, and got 6.37102797: about 3×10−83 \times 10^{-8} above the formula. That error changes a little whenever SS moves. In lesson 2.8.1's rounding ceiling, 2ε∣c∣h2\frac{2\varepsilon|c|}{h^2}, it takes the place of ε∣c∣≈10−15\varepsilon|c| \approx 10^{-15}.

The bumped gammas from that pricer show the effect. A 1% bump gives 0.027762 and a 10-cent bump 0.027766, both fine. A 1-cent bump gives 0.027315, 1.6% low, and a bump of a tenth of a cent gives −0.031782-0.031782.

The error of the bumped gamma for two pricers of the same call: the noisier pricer needs a bigger bump. With a day to expiry, a 1% bump is as wide as the peak of gamma itself and flattens it.

The noisier the pricer, the larger the best bump. Trees and simulations, met in later courses, are noisier still. So desks keep bumps near 1% and check that halving the bump barely changes the answer.

Try it

by_quad is case study 2.6.5's integral over the whole line. Each line prints a bump, the bumped gamma from that pricer and its error as a share of the formula's 0.027772.

⌘/Ctrl + Enter runs the cell

Try it

The first lines compare a $1 bump with a 1% bump on a $5 stock with strike $5. Then the right and the wrong vega bump, the one-sided delta, and the delta of a 100-share contract.

⌘/Ctrl + Enter runs the cell
Check yourself
A system moves the volatility from 20% to 20.2% and to 19.8%. It divides the price change by 2×0.012 \times 0.01 to report vega per vol point, and shows 0.0555. What went wrong?

Where bumping misbehaves

Close to expiry

Case study 2.7.4 found that close to expiry the price curve nears the payoff's corner at the strike. The bend crowds into a range of about SσTS\sigma\sqrt{T}: $14.14 for six months, $1.05 for a day. A 1% bump is $1, so a day out it is as wide as the bend.

time to expirySσTS\sigma\sqrt{T}gamma1% bumperror
6 months14.140.02780.0278−0.0%-0.0\%
1 month5.770.06890.0688−0.2%-0.2\%
1 week2.770.14380.1422−1.1%-1.1\%
1 day1.050.38110.3545−7.0%-7.0\%

A bump must be small next to the scale on which the price bends, which here is SσTS\sigma\sqrt{T}, not SS. With a day left, a 10-cent bump gives 0.3808. The figure shows the 1% bump flattening the whole peak of gamma, not just its top.

At expiry: a corner and a jump

At expiry the call is worth its payoff, max⁡(S−K,0)\max(S - K, 0), with a corner at the strike. The gamma formula divides by T=0\sqrt{T} = 0 there. The bump still returns a number: h−2⋅0+0h2=1h\frac{h - 2 \cdot 0 + 0}{h^2} = \frac{1}{h}, whatever hh is.

A digital call pays $1 if the stock ends above the strike and nothing otherwise. At expiry its value jumps at the strike, and its bumped delta there is 12h\frac{1}{2h}.

bump hhcall gamma at the strikedigital delta at the strike
110.5
0.1105
0.0110050

The bumped number measures the bump, not the option: this is lesson 2.8.1's corner inside the step. Desks therefore treat such payoffs with care, for instance booking a digital as a narrow call spread, which has a slope rather than a jump.

Try it

The loop prints, for each expiry, SσTS\sigma\sqrt{T}, the formula's gamma, the 1% bump and its error. Then the one-day call with a 10-cent bump.

The last loop is at expiry, where the prices are the payoffs.

⌘/Ctrl + Enter runs the cell
Check yourself
A pricer is accurate to about 10−810^{-8}, not to the last digit. Which stock bump gives the most trustworthy gamma for the six-month call?

A numerical present value

Case study 2.5.5 valued $10,000 a year of tolls for 10 years at a continuous 5%: $78,693.87 exactly. Its payment schedules are lesson 2.8.2's rules. Payments at year-starts and year-ends are the left and right sums, so their average, $78,710.26, is the trapezoid rule; mid-year payments, $78,685.67, are the midpoint sum.

Combined as 2M+T3\frac{2M + T}{3}, they give Simpson's rule with 20 half-year strips: $78,693.87, right to the cent. Simpson's rule needs only 2 strips, values at years 0, 5 and 10, to come within $1.70. With 4 strips it is 11 cents off.

The same few lines value a stream whose discounted rate has no antiderivative, where no closed form exists to check against.

Try it

simpson_rule is lesson 2.8.2's, applied to the discounted rate 10,000 e−0.05t10{,}000\,e^{-0.05t}.

The first line rebuilds Simpson's rule from case study 2.5.5's three schedules. The loop prints Simpson's value and error with 2, 4 and 10 strips.

⌘/Ctrl + Enter runs the cell
Exercise

Greeks by bump-and-reprice

Write bump_greeks(price, S, sigma, rel_bump=0.01, vol_bump=0.01). Here price(S, sigma) is any function that returns an option's value. Return (delta, gamma, vega) by central differences, with:

  1. the stock bumped by h=h = rel_bump ×S\times S, a relative bump;
  2. the volatility bumped by k=k = vol_bump, an absolute bump;
  3. vega reported per vol point: the slope in σ\sigma divided by 100.

Raise ValueError if S, sigma, rel_bump or vol_bump is not positive, or if vol_bump is not below sigma, which would make σ−k\sigma - k zero or negative.

The tests use the six-month call, a $5 stock where a fixed $1 bump fails, and a price function whose Greeks you can work out by hand.

See the tests
import math

from scipy.stats import norm


def _call(S, sigma, K=100.0, T=0.5, r=0.03):
    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 _formulas(S, sigma, K=100.0, T=0.5, r=0.03):
    d1 = (math.log(S / K) + (r + sigma**2 / 2) * T) / (sigma * math.sqrt(T))
    return norm.cdf(d1), norm.pdf(d1) / (S * sigma * math.sqrt(T)), S * math.sqrt(T) * norm.pdf(d1) / 100


def test_six_month_call():
    """The six-month call with 1% and one-vol-point bumps: delta 0.570054, gamma 0.027762, vega 0.277717"""
    delta, gamma, vega = bump_greeks(_call, 100.0, 0.2)
    expect_close(delta, 0.570054062469417, what="delta", rel=1e-9)
    expect_close(gamma, 0.027762255136636327, what="gamma", rel=1e-9)
    expect_close(vega, 0.27771682820363, what="vega per vol point", rel=1e-9)


def test_close_to_formulas():
    """The bumped Greeks agree with the closed forms to within 0.1%"""
    for got, exact, name in zip(bump_greeks(_call, 100.0, 0.2), _formulas(100.0, 0.2), ("delta", "gamma", "vega")):
        expect_close(got, exact, what=name, rel=1e-3)


def test_by_hand():
    """For price S²σ at S = 3, σ = 0.5: delta 3, gamma 1 and vega 0.09 per vol point"""
    delta, gamma, vega = bump_greeks(lambda S, sigma: S**2 * sigma, 3.0, 0.5)
    expect_close(delta, 3.0, what="delta of S²σ", rel=1e-9)
    expect_close(gamma, 1.0, what="gamma of S²σ", rel=1e-6)
    expect_close(vega, 0.09, what="vega of S²σ per vol point", rel=1e-9)


def test_relative_bump():
    """The stock bump is relative: a $5 stock with strike $5 has gamma within 0.1% of 0.555443"""
    price = lambda S, sigma: _call(S, sigma, K=5.0)
    gamma = bump_greeks(price, 5.0, 0.2)[1]
    expect_close(gamma, _formulas(5.0, 0.2, K=5.0)[1], what="gamma of the $5 option", rel=1e-3)


def test_smaller_bump():
    """A smaller relative bump, 0.0001, gives gamma within 10⁻⁸ of the formula"""
    gamma = bump_greeks(_call, 100.0, 0.2, rel_bump=1e-4)[1]
    expect_close(gamma, _formulas(100.0, 0.2)[1], what="gamma with rel_bump=1e-4", abs_tol=1e-8, rel=0)


def test_bad_stock_price():
    """A stock price of 0 raises ValueError"""
    try:
        bump_greeks(_call, 0.0, 0.2)
    except ValueError:
        return
    raise AssertionError("expected ValueError for S = 0")


def test_vol_bump_too_big():
    """A volatility bump of 0.25 when σ = 0.2 raises ValueError"""
    try:
        bump_greeks(_call, 100.0, 0.2, vol_bump=0.25)
    except ValueError:
        return
    raise AssertionError("expected ValueError: σ − 0.25 is negative")

Summary card

  • Bump-and-reprice estimates Greeks by central differences of the price: Δ≈c(S+h)−c(S−h)2h\Delta \approx \frac{c(S + h) - c(S - h)}{2h}, Γ≈c(S+h)−2c(S)+c(S−h)h2\Gamma \approx \frac{c(S + h) - 2c(S) + c(S - h)}{h^2}, vega ≈c(σ+k)−c(σ−k)2k\approx \frac{c(\sigma + k) - c(\sigma - k)}{2k}.
  • For the six-month call, 1% and one-vol-point bumps match the closed forms to 0.04% or better.
  • Gamma is the most sensitive: its rounding error grows like 1h2\frac{1}{h^2}. A noisy pricer raises that error and pushes the best bump up. Desks use about 1% and check that halving it barely changes the answer.
  • Bump the stock in proportion to its price, and the volatility by vol points; know the units and the sign of a position.
  • A bump must be small next to the scale on which the price bends, SσTS\sigma\sqrt{T}. At a corner or a jump, as at expiry, the bumped Greek measures only the bump.
  • Simpson's rule values a continuous cash flow from a few points: case study 2.5.5's schedules combine to the exact $78,693.87.

Free further reading

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