SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 2.3.1 Power, constant and sum rulesConcept · 25 min
      2. 2.3.2 Product and quotient rulesConcept · 25 min
      3. 2.3.3 The chain ruleConcept · 30 min
      4. 2.3.4 Derivatives of exponentials and logarithmsConcept · 30 min
      5. 2.3.5 Derivatives of sine and cosineConcept · 35 min
      6. 2.3.6 Practice set: differentiationPractice set · 25 min
      7. 2.3.7 Case study: modified duration and elasticityCase study · 30 min

Level 2, Single-variable calculus 2.3 Differentiation rules Lesson 7 of 7

Case studyLesson 2.3.7 in Level 2, Single-variable calculus. About 30 minutes.

Case study: modified duration and elasticity

The exact slope of a bond price

A bond pays ctc_t at each time tt (in years). At the yield yy, that payment is worth ct(1+y)−tc_t(1 + y)^{-t} today.

As a function of yy, this is a power of the inner function 1+y1 + y, whose slope is 1. The power rule (lesson 2.3.1) with the chain rule (lesson 2.3.3) gives

ddy ct(1+y)−t=−t ct (1+y)−t−1\frac{d}{dy}\,c_t(1 + y)^{-t} = -t\,c_t\,(1 + y)^{-t-1}

The sum rule (lesson 2.3.1) adds these over all payments, so P′(y)P'(y) is exact, with no bump. Each term is −t-t times the payment's present value PVt=ct(1+y)−tPV_t = c_t(1 + y)^{-t}, times one extra factor 11+y\frac{1}{1 + y}.

Rule: Modified duration, exactly

D=−P′(y)P=DMac1+yDMac=∑tt⋅PVtP\begin{gathered} D = -\frac{P'(y)}{P} = \frac{D_{\text{Mac}}}{1 + y} \\[4pt] D_{\text{Mac}} = \frac{\sum_t t \cdot PV_t}{P} \end{gathered}

DMacD_{\text{Mac}} is the Macaulay duration of case study 2.2.4: the payment times, each weighted by its present value. The factor 11+y\frac{1}{1 + y} is the chain rule's extra power in (1+y)−t−1(1 + y)^{-t-1}.

Try it

SymPy differentiates the price formula exactly and evaluates it at 5%. The second line is the 1 bp bump of case study 2.2.4, for comparison.

The exact slope is about −420.97967-420.97967, and the bump is off by about 0.00003. The duration is D≈4.40031D \approx 4.40031.

⌘/Ctrl + Enter runs the cell

Duration is a balance point

Picture each payment's present value as a weight placed on a timeline at its time. The Macaulay duration is the point where the timeline balances.

For the 5-year 4% bond at 5%, the coupons weigh 3.81, 3.63, 3.46 and 3.29. The last payment weighs 81.49, which is 85% of the price.

Present values of each payment, for 5-year bonds at a 5% yield. Bigger coupons put more weight early, so the balance point (the Macaulay duration) moves from 4.62 to 4.36 years.

Dividing by 1.051.05 turns 4.6203 into the modified duration 4.4003, and 4.3570 into 4.1496, the 8% bond's duration in case study 2.2.4. A zero-coupon bond has one weight, at TT, so DMac=TD_{\text{Mac}} = T and D=T1+yD = \frac{T}{1 + y}: the clean case of case study 2.2.4, now from the formula.

Try it

The cell lists each payment's present value and its share of the price, then both durations, for the 4% and 8% bonds.

⌘/Ctrl + Enter runs the cell

On the desk: conventions and edge inputs

Case study 2.2.4 covered signs, units and DV01. The exact formula adds a convention to get right and some inputs to guard against.

The compounding convention

The factor 11+y\frac{1}{1 + y} came from differentiating (1+y)−t(1 + y)^{-t}, so it depends on how the yield compounds. Many government bonds pay coupons twice a year, with the yield compounded twice a year. Then a payment cc at time tt is worth c(1+y2)−2tc(1 + \frac{y}{2})^{-2t}, and the chain rule, with inner slope 12\frac{1}{2}, gives

ddy(1+y2)−2t=−t (1+y2)−2t−1\frac{d}{dy}\Big(1 + \frac{y}{2}\Big)^{-2t} = -t\,\Big(1 + \frac{y}{2}\Big)^{-2t-1}

So the factor becomes 11+y/2\frac{1}{1 + y/2}. Paid as 2 every half year, the 5-year 4% bond at 5% has DMac=4.5695D_{\text{Mac}} = 4.5695 and D=4.56951.025=4.4581D = \frac{4.5695}{1.025} = 4.4581, which a bump confirms. Dividing by 1.05 instead gives 4.3519, off by 0.1.

Edge inputs

  • Negative yields have happened on some government bonds. The formula still works while 1+y>01 + y > 0: a 5-year 1% bond at −0.5%-0.5\% is priced at 107.61, with D=4.9308D = 4.9308.
  • A yield of −100%-100\% or below makes 1+y1 + y zero or negative. The discount factors (1+y)−t(1 + y)^{-t} then divide by zero or mean nothing, so a function should refuse such a yield.
  • Payments that move. The formula, like the bump, assumes the payments ctc_t do not depend on yy (case study 2.2.4). For bonds the issuer may repay early, they do.

Try it

The first lines price the twice-a-year bond, compare the two factors, and check with a bump. Then come the negative yield and the yield of −100%-100\%.

Python's ZeroDivisionError is what an unchecked price function raises at y=−1y = -1.

⌘/Ctrl + Enter runs the cell

Duration is still a slope. For moves of 1% or more, the bend of the price curve matters: that is convexity, in case study 2.4.5.

With continuous discounting, duration is an average time

Discount continuously instead (case study 1.4.6): each payment is worth cte−ytc_t e^{-yt}. Lesson 2.3.4 gives ddye−yt=−te−yt\frac{d}{dy}e^{-yt} = -te^{-yt}, so

−P′(y)P=∑tt⋅cte−ytP-\frac{P'(y)}{P} = \frac{\sum_t t \cdot c_t e^{-yt}}{P}

There is no 1+y1 + y to divide by: the duration is the present-value-weighted average time. It is also −ddyln⁡P-\frac{d}{dy}\ln P, by the log-of-a-function rule of lesson 2.3.4: duration is the relative rate at which the price falls.

Try it

At a continuous yield of 5% the bond is worth about 95.14. The slope over the price, the weighted average time, and the slope of −ln⁡P-\ln P all give 4.6191 years.

⌘/Ctrl + Enter runs the cell

Elasticity

How much does demand QQ respond to the price pp? The slope dQdp\frac{dQ}{dp} depends on units: quote prices in cents instead of dollars and it shrinks a hundredfold. The elasticity compares percentage changes instead, so it has no units.

Rule: Elasticity

E=dQdp⋅pQE = \frac{dQ}{dp}\cdot\frac{p}{Q}

The percentage change in quantity per 1% change in price. For ordinary goods it is negative; many reports quote its size ∣E∣|E| instead, so check the sign convention.

Constant elasticity. For Q=1000 p−1.5Q = 1000\,p^{-1.5}, the power rule gives dQdp=−1500 p−2.5\frac{dQ}{dp} = -1500\,p^{-2.5}. Then E=−1.5E = -1.5 at every price: any Q=kpaQ = kp^{a} has elasticity aa.

Straight-line demand. For Q=500−20pQ = 500 - 20p, E=−20p500−20pE = \frac{-20p}{500 - 20p} changes along the line. It is 0 at p=0p = 0, −0.67-0.67 at p=10p = 10, −1-1 at p=12.5p = 12.5 and −4-4 at p=20p = 20. At p=25p = 25 demand is 0 and EE is undefined; just below it, EE falls without limit (−24-24 at p=24p = 24).

The same demand with prices in cents is Q=500−0.2cQ = 500 - 0.2c. Its slope is −0.2-0.2 per cent, not −20-20 per dollar, but at c=1000c = 1000 cents the elasticity is still −0.67-0.67.

Revenue and the product rule

Revenue is price times quantity, R=pQR = pQ. The product rule (lesson 2.3.2) gives its slope, and the elasticity appears inside it.

Rule: Revenue and elasticity

dRdp=Q+p dQdp=Q (1+E)\frac{dR}{dp} = Q + p\,\frac{dQ}{dp} = Q\,(1 + E)

If −1<E≤0-1 < E \le 0 (inelastic demand), revenue rises with the price. At E=−1E = -1 it is flat, at its peak. If E<−1E < -1 (elastic demand), a higher price loses revenue.

For Q=500−20pQ = 500 - 20p, the slope of revenue is 500−40p500 - 40p. At p=10p = 10, Q=300Q = 300 and E=−23E = -\frac{2}{3}, so the slope is 300×(1−23)=100300 \times (1 - \frac{2}{3}) = 100. At p=20p = 20 it is −300-300.

It is 0 at p=12.5p = 12.5, where revenue peaks at 3125.

Revenue for the demand Q=500−20pQ = 500 - 20p. Where demand is inelastic, ∣E∣<1|E| < 1, revenue climbs with the price; it peaks where E=−1E = -1, at p=12.5p = 12.5; beyond, demand is elastic and revenue falls.

Elasticity is a slope

Like duration, elasticity describes small changes. For Q=1000 p−1.5Q = 1000\,p^{-1.5}, a 2% price rise cuts the quantity by about 1.5×2%=3%1.5 \times 2\% = 3\%; the curve gives 2.93%.

For a 20% rise the estimate is 30%, but the curve gives only 23.93%. For big moves, use the demand curve itself.

Try it

SymPy computes both elasticities, checks that the revenue slope equals Q(1+E)Q(1 + E), and repeats the elasticity in cents.

The last lines compare the elasticity estimate with the curve for price rises of 2% and 20%.

⌘/Ctrl + Enter runs the cell
Check yourself
Demand has elasticity −1.5-1.5. The price rises by 2%. Roughly what happens to the quantity sold?
Check yourself
At today's price, demand has elasticity −0.4-0.4. The price rises a little. What happens to revenue?
Exercise

Duration from the formula

Write exact_duration(coupon, face, years, y, freq=1), which returns the modified duration from the exact formula: no bumping. The bond pays the yearly coupon in freq equal parts, every 1freq\frac{1}{\text{freq}} of a year, and face at the end. The yield y is a decimal, compounded freq times a year.

  • Payment kk (for k=1,2,…k = 1, 2, \ldots, years * freq) comes at time kfreq\frac{k}{\text{freq}} years and is worth its amount divided by (1+yfreq)k(1 + \frac{y}{\text{freq}})^k.
  • Return the Macaulay duration divided by 1+yfreq1 + \frac{y}{\text{freq}}.
  • Raise ValueError when 1+yfreq≤01 + \frac{y}{\text{freq}} \le 0.

The tests compare it with bump estimates and known values, try a negative yield, and check the yearly and twice-a-year conventions.

See the tests
def _price(c, F, T, y, freq=1):
    return sum((c / freq) / (1 + y / freq) ** k for k in range(1, T * freq + 1)) + F / (1 + y / freq) ** (T * freq)


def _bump(c, F, T, y, freq=1, h=1e-6):
    return -(_price(c, F, T, y + h, freq) - _price(c, F, T, y - h, freq)) / (2 * h) / _price(c, F, T, y, freq)


def test_five_year_bond():
    """The 5-year 4% bond at 5% has duration about 4.40031"""
    expect_close(exact_duration(4, 100, 5, 0.05), 4.400306998058356, what="exact_duration(4, 100, 5, 0.05)", rel=1e-9)


def test_matches_bump():
    """It agrees with a bump estimate for a 10-year 6% bond at 3%"""
    expect_close(exact_duration(6, 100, 10, 0.03), _bump(6, 100, 10, 0.03), what="exact vs bump", rel=1e-6)


def test_zero_coupon():
    """A 7-year zero-coupon bond at 4% has duration 7 / 1.04"""
    expect_close(exact_duration(0, 100, 7, 0.04), 7 / 1.04, what="exact_duration(0, 100, 7, 0.04)")


def test_one_year():
    """A 1-year bond has duration 1 / (1 + y)"""
    expect_close(exact_duration(5, 100, 1, 0.05), 1 / 1.05, what="exact_duration(5, 100, 1, 0.05)")


def test_semiannual():
    """With freq=2, the 5-year 4% bond at 5% has duration about 4.45806, not 4.35191"""
    expect_close(exact_duration(4, 100, 5, 0.05, freq=2), 4.458056324352842, what="exact_duration(4, 100, 5, 0.05, freq=2)", rel=1e-9)


def test_semiannual_matches_bump():
    """With freq=2, it agrees with a bump estimate for a 30-year 5% bond at 4.5%"""
    expect_close(exact_duration(5, 100, 30, 0.045, freq=2), _bump(5, 100, 30, 0.045, freq=2), what="exact vs bump, freq=2", rel=1e-6)


def test_negative_yield():
    """A negative yield is allowed: a 5-year 1% bond at -0.5% has duration about 4.93079"""
    expect_close(exact_duration(1, 100, 5, -0.005), 4.9307916502902485, what="exact_duration(1, 100, 5, -0.005)", rel=1e-9)


def test_rejects_impossible_yield():
    """A yield of -100% or below has no discount factors: raise ValueError"""
    for y in (-1, -1.5):
        try:
            exact_duration(4, 100, 5, y)
        except ValueError:
            continue
        raise AssertionError(f"expected ValueError for y = {y}")

Summary card

  • The chain rule gives each discounted payment's slope, ddy(1+y)−t=−t(1+y)−t−1\frac{d}{dy}(1 + y)^{-t} = -t(1 + y)^{-t-1}; the sum rule gives the exact P′(y)P'(y).
  • Modified duration =DMac1+y= \frac{D_{\text{Mac}}}{1 + y}, where DMacD_{\text{Mac}} is the balance point of the payments' present values in time.
  • The factor depends on compounding: 11+y/2\frac{1}{1 + y/2} for twice a year, none for continuous discounting, where −P′P=−ddyln⁡P-\frac{P'}{P} = -\frac{d}{dy}\ln P is the average time.
  • Elasticity E=dQdppQE = \frac{dQ}{dp}\frac{p}{Q} has no units; Q=kpaQ = kp^{a} has constant elasticity aa.
  • Revenue: dRdp=Q(1+E)\frac{dR}{dp} = Q(1 + E). Revenue rises with price when ∣E∣<1|E| < 1, peaks at E=−1E = -1 and falls when ∣E∣>1|E| > 1.

Free further reading

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