Skip to content
🐍Bond Duration in Python: Macaulay and Modified

Bond Duration in Python: Macaulay and Modified

intermediate Python 14 minbond durationMacaulay durationmodified duration
What you'll learn
  • bond duration
  • Macaulay duration
  • modified duration
  • present value
  • time value of money
  • convexity
  • yield to maturity

Duration - One Number for Interest-Rate Risk

Scene: the Monday risk meeting. Last lesson you built a Bond class that reprices itself at any yield. Now the head of rates asks a sharper question: "If yields jump one full point, roughly how much does this bond lose? And I need it for every position in the book, not just this one." Repricing everything under every scenario is slow. The number every fixed-income desk reaches for first is a single figure per position: its duration.

What it is: Macaulay duration is the average time, in years, until you receive a bond's cash flows, where each year is weighted by the present value of what it pays. Modified duration turns that time into a sensitivity: the approximate percentage fall in price for a one percentage point rise in yield.

Frederick Macaulay introduced the measure in 1938 (you met him in the last lesson's timeline), and desks still compute it in exactly this form.

🧠 The mental model: a see-saw of discounted cash flows

Lay the bond's cash flows along a timeline from year 1 to year 10 and stack each one's present value on it, like weights on a plank. Macaulay duration is the point where the plank balances.

  • Nine small coupons sit on years 1 to 9; one big block sits on year 10 (the last coupon plus the face value). In this lesson's bond that final block carries roughly two thirds of the price, so the balance point lands late, but not at 10.
  • Coupons pull the balance point earlier. Cash that arrives sooner shortens the average wait, so a coupon bond's duration is always shorter than its maturity. A zero-coupon bond has a single block, so its duration equals its maturity.
  • The weights must be present values, not raw cash. A 45 coupon received in year 1 is worth more today than the same 45 received in year 9. Weight by undiscounted amounts and the distant face value gets far too much pull.

The three formulas

text
Price P      =  Σ  CFₜ / (1 + y)ᵗ                     for t = 1 .. T
Macaulay D   =  Σ  t × CFₜ / (1 + y)ᵗ   ÷   P         in years
Modified D   =  Macaulay D / (1 + y)

The top of the Macaulay fraction is the PV-weighted time sum: each year t multiplied by the present value of the cash flow paid in that year. Dividing by the price, which is itself the sum of those same present values, turns it into a weighted average: the weights PVₜ / P add up to exactly 100%.

From duration to a price change

Modified duration is the slope you need for a quick estimate:

text
ΔP  ≈  - Modified D  ×  Δy  ×  P

With Δy = 0.01 (one percentage point, or 100 basis points), a modified duration near 8 says "the price falls by about 8%". The minus sign is Malkiel's first theorem from the last lesson: yields up, prices down.

Why the estimate is not exact: convexity

The duration estimate is a straight line laid against a curve. The true price-yield relationship bends (it is convex), so the straight line sits below the curve on both sides:

Yield moveDuration saysExact repricing
yields risea lossa slightly smaller loss
yields falla gaina slightly bigger gain

That gap comes from convexity, the same curvature behind the Malkiel theorem you met in the last lesson: a fall in yield lifts the price more than an equal rise cuts it. For a few basis points the gap is negligible; for a full point on a 10-year bond it is worth measuring, which is why the report below prints both numbers side by side.

Your Task

The bond is the one from the last lesson: 1000 face value, a 4.5% annual coupon, 10 years to maturity, priced at a 4.0% market yield. The starter builds the cash-flow list, prices the bond and runs the whole risk report. What is broken sits at the 👉 YOUR TURN marker: the PV-weighted time sum weights each year by the raw cash flow instead of its present value.

  1. Run the starter as-is. The report claims a Macaulay duration of 11.99 years for a bond whose last payment arrives in year 10, which is impossible: an average of payment dates can never land after the final one.
  2. Fix that one line so each year t is weighted by cf / (1 + YIELD) ** t.

Predict before you run: the corrected duration must land below 10 years, but how far below? And for the +1 point move, will the straight-line duration estimate show a bigger loss or a smaller one than the exact repricing (which you already know from the last lesson: 1040.55 falls to 961.39)? Write both answers down, then check the Macaulay duration and Convexity gap lines.