Bond Risk Measures and Immunization › Macaulay Duration

Michael BrenndoerferNovember 8, 20257 min read
Quantitative FinanceAll chapters
Part 2 of 15Equity and Fixed Income Markets
Chapter 13Bond Risk Measures and Immunization › Macaulay Duration
Reading section 3 of 13 · Macaulay Duration · 7 minSection 3 of 13 · 7 min

Book contents

Section 3 of 13 · 7 min read

Bond Risk Measures and Immunization › Macaulay DurationOne section · Sections in this chapter3 / 13 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Macaulay Duration

Macaulay duration is the weighted average time until a bond's cash flows are received. The weights are the present values of each cash flow as a proportion of the bond's total price.

Frederick Macaulay introduced duration in 1938 as a measure of the effective maturity of a bond. His insight was that a bond's stated maturity date tells only part of the story. A 10-year bond that pays substantial coupons behaves quite differently from a 10-year zero-coupon bond, even though both mature on the same date. Macaulay sought a single number that would capture the economic timing of all a bond's cash flows.

Rather than using only the final maturity date, duration accounts for all cash flows and their timing. The concept is analogous to finding the center of gravity of the cash flow stream. Just as a physical object balances at its center of mass, a bond's duration represents the "balance point" of its payments over time. For a bond paying cash flows CtC_t at times t=1,2,…,nt = 1, 2, \ldots, n:

DMac=∑t=1nt⋅Ct(1+y)tP=1P∑t=1nt⋅PV(Ct)D_{Mac} = \frac{\sum_{t=1}^{n} t \cdot \frac{C_t}{(1+y)^t}}{P} = \frac{1}{P} \sum_{t=1}^{n} t \cdot PV(C_t)

where:

  • DMacD_{Mac}: Macaulay duration in payment periods in this formula
  • tt: the integer payment-period index; divide by the annual payment frequency to express duration in years
  • CtC_t: the cash flow at time tt
  • yy: the yield to maturity per period (expressed as a decimal)
  • PP: the bond price (sum of all present values)
  • PV(Ct)PV(C_t): the present value of cash flow CtC_t, equal to Ct/(1+y)tC_t/(1+y)^t
  • nn: the total number of cash flow periods

Each term t⋅PV(Ct)t \cdot PV(C_t) weights the time period by the present value of the cash flow received at that time. Dividing by the total price PP normalizes these weights to sum to one, so the result is a proper weighted average.

Macaulay duration summarizes the timing of present-value weights; it is not a payback date or a promise that accumulated coupons have recovered the purchase price. A zero-coupon bond has one receipt time, so its duration equals maturity. With positive earlier coupons, some present-value weight arrives sooner and the weighted mean is shorter than maturity.

For a zero-coupon bond, the calculation is trivial. The only cash flow is the face value at maturity, so Macaulay duration equals the time to maturity. With a positive coupon payment strictly before maturity, Macaulay duration is less than maturity because that earlier payment receives positive present-value weight. If all remaining cash flow arrives at maturity, duration equals maturity.

In[6]:
Code
def macaulay_duration(
    face_value, coupon_rate, years_to_maturity, yield_rate, frequency=2
):
    """Calculate Macaulay duration of a bond."""
    periods, coupon_payment, periodic_yield = _bond_terms(
        face_value, coupon_rate, years_to_maturity, yield_rate, frequency
    )

    # Calculate price
    price = bond_price(
        face_value, coupon_rate, years_to_maturity, yield_rate, frequency
    )

    # Calculate weighted average time
    weighted_time = 0
    for t in range(1, periods + 1):
        if t < periods:
            cf = coupon_payment
        else:
            cf = coupon_payment + face_value

        pv_cf = cf / (1 + periodic_yield) ** t
        time_in_years = t / frequency
        weighted_time += time_in_years * pv_cf

    return weighted_time / price

Let's calculate duration for several bonds to see how bond characteristics affect it:

In[7]:
Code
# Compare durations for different bonds
bonds = [
    {"name": "5-year, 5% coupon", "maturity": 5, "coupon": 0.05},
    {"name": "10-year, 5% coupon", "maturity": 10, "coupon": 0.05},
    {"name": "10-year, 2% coupon", "maturity": 10, "coupon": 0.02},
    {"name": "10-year, 8% coupon", "maturity": 10, "coupon": 0.08},
    {"name": "30-year, 5% coupon", "maturity": 30, "coupon": 0.05},
]

yield_rate = 0.05  # 5% yield

print("Bond Characteristics and Macaulay Duration")
print("=" * 55)
print(f"{'Bond':<25} {'Maturity':>10} {'Duration':>12}")
print("-" * 55)

for bond in bonds:
    mac_dur = macaulay_duration(1000, bond["coupon"], bond["maturity"], yield_rate)
    print(f"{bond['name']:<25} {bond['maturity']:>10} {mac_dur:>12.2f} years")
Out[7]:
Console
Bond Characteristics and Macaulay Duration
=======================================================
Bond                        Maturity     Duration
-------------------------------------------------------
5-year, 5% coupon                  5         4.49 years
10-year, 5% coupon                10         7.99 years
10-year, 2% coupon                10         8.95 years
10-year, 8% coupon                10         7.39 years
30-year, 5% coupon                30        15.84 years

The results confirm our intuition: longer maturity increases duration, and lower coupons increase duration because less of the bond's value comes from near-term cash flows. Notice that the 10-year, 2% coupon bond has a duration of nearly 9 years, while the 10-year, 8% coupon bond has a duration closer to 7 years. The higher coupon payments shift the center of gravity of the cash flows earlier in time.

Out[8]:
Visualization
Line chart showing duration versus maturity for bonds with different coupon rates.
Macaulay duration as a function of maturity for bonds with different coupon rates, all calculated at a 5% yield. Zero-coupon bonds have duration exactly equal to maturity (the 45-degree reference line), representing the theoretical maximum duration for any given maturity. Coupon-paying bonds have shorter durations because earlier cash flows pull the weighted average time forward, with higher coupon rates creating larger gaps between duration and maturity. The 8% coupon bond shows the shortest duration at each maturity point, while the 2% coupon bond approaches zero-coupon duration levels.
Out[9]:
Visualization
Bar chart showing present values of cash flows over time with duration marked as center of gravity.
Present value of cash flows for a 10-year, 5% coupon bond at 5% yield, displayed as bars over the bond's 10-year life. Semi-annual coupon payments (blue bars) contribute modest present values throughout the period, while the final payment including principal (green bar) dominates the total value. The Macaulay duration of 7.99 years, marked by the red vertical line, represents the balance point of these weighted cash flows, similar to a physical center of gravity. This visualization shows duration below maturity because this bond has positive coupon payments strictly before maturity, pulling its present-value-weighted receipt time forward.