Bond Fundamentals and Pricing › Yield to Maturity

Michael BrenndoerferUpdated December 28, 20254 min read
Quantitative FinanceAll chapters
Part 2 of 15Equity and Fixed Income Markets
Chapter 11Bond Fundamentals and Pricing › Yield to Maturity
Reading section 5 of 14 · Yield to Maturity · 4 minSection 5 of 14 · 4 min

Book contents

Section 5 of 14 · 4 min read

Bond Fundamentals and Pricing › Yield to MaturityOne section · Sections in this chapter5 / 14 ↓
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

Traders can observe a market price and solve for its implied yield to maturity (YTM). It is the single rate equating that price to the present value of the promised payment schedule under the specified convention. The quote describes those promised payments rather than predicting the investor's realized return.

Yield to Maturity

YTM is the internal rate of return of the promised payment schedule at the observed full price, under a specified compounding and settlement convention. The calculation itself does not require an actual reinvestment strategy. Realizing the corresponding compound holding-to-maturity return additionally requires payment in full and reinvestment of coupons at that yield, before costs and taxes.

Calculating YTM

The calculation of yield to maturity reverses the pricing problem we solved earlier. Instead of taking a yield and computing a price, we observe a market price and work backward to find the yield that makes our pricing formula produce that exact price. For the multi-period coupon examples here, we solve this inverse problem by numerical iteration.

Finding the YTM requires solving for yy in the bond pricing equation:

P=∑i=1nCi(1+ym)iP = \sum_{i=1}^{n} \frac{C_i}{\left(1 + \frac{y}{m}\right)^i}

where:

  • PP: observed market price (known)
  • yy: yield to maturity (unknown, to be solved)
  • CiC_i, mm, nn: as defined previously

For a multi-period coupon bond, it is generally more practical to solve numerically than to derive an algebraic expression for yy. Special cases do have closed-form solutions, including a single remaining payment. Newton-Raphson or Brent's bracketed method can find a yield numerically; a Brent bracket must lie in the model domain and contain a sign change in the continuous price residual.

In[13]:
Code
from scipy.optimize import brentq


def yield_to_maturity(
    price,
    face_value,
    coupon_rate,
    years_to_maturity,
    payments_per_year=2,
    *,
    yield_bracket=(0.0001, 0.5),
):
    """Solve a nominal annual YTM only within an explicit finite bracket.

    The default covers 0.01% through 50%, matching these examples. Supply
    another admissible bracket for zero or negative yields; this helper
    does not automatically search every possible financial yield.
    """
    target = _finite_number(price, "price")
    face, coupon, years, frequency, periods = _regular_bond_inputs(
        face_value, coupon_rate, years_to_maturity, payments_per_year
    )
    if target <= 0 or periods == 0:
        raise ValueError("Require positive price and positive remaining maturity")
    try:
        lower, upper = yield_bracket
    except (TypeError, ValueError) as exc:
        raise ValueError("yield_bracket must have exactly two endpoints") from exc
    lower = _finite_number(lower, "lower yield endpoint")
    upper = _finite_number(upper, "upper yield endpoint")
    if lower <= -frequency or upper <= lower:
        raise ValueError("Require -frequency < lower < upper for yield_bracket")

    def price_difference(y):
        return price_bond(face, coupon, years, y, frequency) - target

    lower_value, upper_value = price_difference(lower), price_difference(upper)
    if lower_value == 0:
        return lower
    if upper_value == 0:
        return upper
    if np.signbit(lower_value) == np.signbit(upper_value):
        raise ValueError(
            "yield_bracket does not bracket this price; choose endpoints with opposite residual signs"
        )
    return float(brentq(price_difference, lower, upper))


# Calculate YTM for bonds at various prices
test_prices = [1100, 1050, 1000, 950, 900]
ytms = [yield_to_maturity(p, 1000, 0.06, 10) for p in test_prices]
Out[14]:
Console
Yield to Maturity Calculation
---------------------------------------------
Price ($)       YTM             vs Coupon Rate 
---------------------------------------------
1100            4.733         % Premium        
1050            5.348         % Premium        
1000            6.000         % Par            
950             6.694         % Discount       
900             7.435         % Discount

The results confirm our earlier observation: premium bonds (price > par) have yields below the coupon rate, while discount bonds have yields above the coupon rate.

YTM Assumptions and Limitations

Separate the quoted IRR from a realized investment return:

  • Payment and holding horizon: YTM uses the promised payments through maturity. Default or a sale before maturity changes the realized cash flows.
  • Coupon reinvestment: Realizing the YTM as a compound return through maturity requires reinvesting intermediate coupons at that rate. Available rates can differ, especially across many coupon dates.

YTM is a useful comparison quote, not a guaranteed total return or a complete measure of relative value. Credit, options, liquidity, taxes, costs, and the timing of cash flows still matter.