Bond Pricing Fundamentals: Yield to Maturity & Present Value
Covers bond pricing through present value calculations, yield to maturity analysis, and price-yield relationships. Topics include fixed income fundamentals.
Quantitative FinanceAll chapters Bond Fundamentals and Pricing › Bond Fundamentals and PricingFull chapter · Sections in this chapter1 / 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
Bond Fundamentals and Pricing
A bond can keep paying the same $30 coupon every six months while its market price rises or falls. The payment promise and the price are different objects: pricing asks what those future payments are worth today at the yield investors require.
Issuers use bonds to borrow from investors; bondholders are creditors rather than equity owners. In its 2026 Capital Markets Fact Book, SIFMA reports $160.7 trillion of global fixed-income debt outstanding and $157.8 trillion of global equity market capitalization for 2025. Outstanding debt and equity market capitalization measure different types of claims on issuers.
We will price a regular coupon bond, recover its yield from a price, and distinguish the quoted clean price from the full settlement amount. The floating-rate example then shows why a coupon reset alone does not guarantee a par price.
Anatomy of a Bond
A bond is a contractual agreement between an issuer (the borrower) and investors (the lenders). The issuer promises to make a series of payments according to a predetermined schedule in exchange for receiving capital upfront. The key parameters that define a bond are:
-
Face Value (Par Value): The principal amount promised at maturity. A $1,000 face value is common in corporate-bond examples, but actual denominations vary. A price quoted as 100 means 100% of face value, not necessarily a $100 principal amount. The face value is the basis for calculating coupons.
-
Coupon Rate: The annual interest rate applied to the face value to determine periodic payments. A 5% coupon on a $1,000 face value bond pays $50 per year in interest.
-
Maturity Date: The date when the issuer must repay the face value. Maturities range from short-term (under 1 year) to long-term (30 years or more).
-
Payment Frequency: How often coupon payments occur. Most bonds pay semiannually in the US, while European bonds often pay annually.
-
Issuer: The entity borrowing money. Issuers include sovereign governments, municipalities, corporations, and supranational organizations.
A bond trades at par when its price equals its face value, at a premium when the price exceeds face value, and at a discount when the price is below face value. These relationships depend on how the bond's coupon rate compares to prevailing market interest rates.
Government vs. Corporate Bonds
US Treasuries and German Bunds are often used as low-credit-risk benchmarks in their respective currencies. That does not make them free of interest-rate, inflation, or liquidity risk, and a benchmark must match the currency and horizon of the valuation. Government payment capacity and institutional arrangements differ: Germany, for example, does not independently issue euros to service its debt.
Corporate issuers can default on their obligations. Investors generally require compensation for that risk relative to an appropriate low-credit-risk reference. A credit spread compares a bond's yield with a stated reference yield of comparable maturity; it can reflect default and recovery expectations as well as liquidity, risk premia, and other contractual features. It is not a direct estimate of default probability alone.
Cash Flow Patterns
Start by identifying the amount and date of each promised payment. For the plain fixed-rate, noncallable bond used below, the contractual schedule is known at issuance. Actual receipt still depends on payment in full; floating coupons, embedded options, or default can change the amounts or timing. Equity dividends are different because they are not a fixed contractual coupon schedule.
Coupon Bonds
A standard coupon bond generates two types of cash flows: periodic coupon payments during its life and a final principal repayment at maturity. For a bond with face value , annual coupon rate , and semiannual payments, each coupon payment equals .
The logic behind this calculation is straightforward. If a bond promises 6% annual interest on a $1,000 face value, the total annual interest obligation is $60. When this interest is distributed across two semiannual payments, each payment becomes $30. This division of the annual coupon into smaller periodic payments has practical implications for how we measure yield and compare bonds, as we will see when discussing compounding conventions.
Consider a 10-year bond with a $1,000 face value and 6% annual coupon paid semiannually. The bondholder receives:
- 20 coupon payments of $30 each (every 6 months for 10 years)
- One principal payment of $1,000 at maturity
The final payment at maturity includes both the last coupon and the face value, totaling $1,030.
import numpy as np
# Bond parameters
face_value = 1000
coupon_rate = 0.06
years_to_maturity = 10
payments_per_year = 2
# Generate cash flows
coupon_payment = face_value * coupon_rate / payments_per_year
num_payments = years_to_maturity * payments_per_year
cash_flows = np.full(num_payments, coupon_payment)
cash_flows[-1] += face_value # Add principal to final payment
# Create time points (in years)
times = np.arange(1, num_payments + 1) / payments_per_yearBond Cash Flow Schedule (selected payments) --------------------------------------------- Period Time (yrs) Cash Flow ($) --------------------------------------------- 1 0.50 30.00 2 1.00 30.00 3 1.50 30.00 19 9.50 30.00 20 10.00 1030.00 --------------------------------------------- Total cash flows: $1,600.00

The total undiscounted cash flows exceed face value by the cumulative coupon payments. At a positive discount rate, each future payment has a smaller present value than its future dollar amount. Zero or negative rates require different comparisons, as the discounting formula makes explicit.
Zero-Coupon Bonds
Zero-coupon bonds pay no periodic coupons and promise a single payment at maturity. With a positive yield and positive time to maturity, price is below face value; at zero yield it is at par, and a negative yield can put it above par. An investor who holds to maturity and is paid in full earns the difference between the redemption amount and purchase price before costs and taxes.
US Treasury bills (T-bills) with maturities up to one year are zero-coupon instruments. A T-bill with a face value of $10,000 might sell for $9,800, with the $200 difference representing the investor's interest income.
A deterministic coupon schedule can be valued as a collection of individual dated payments. This valuation decomposition is not a claim that every bond can be legally or economically stripped into separately tradeable instruments. The Treasury STRIPS program, Separate Trading of Registered Interest and Principal of Securities, does permit eligible Treasury principal and coupon components to be held and traded separately.
Bond Pricing by Discounting Cash Flows
The price of a bond equals the present value of all its future cash flows, discounted at an appropriate rate. This principle connects bond valuation to the time value of money. Bond pricing answers this simple question. What would a rational investor pay today in exchange for a known stream of future payments? The answer requires us to account for the opportunity cost of capital, the return an investor could earn by deploying funds elsewhere.
Present Value Framework
Discounting compares money available at different dates under a stated rate convention. If $100 can earn 5% for one year, it grows to $105, so $105 due next year has a present value of $100 at that rate. A positive rate makes present value smaller than the future amount; the statement is not unconditional when rates can be zero or negative.
For known cash flows at times in years, and a single effective annual discount rate , the price in this simplified model is:
where:
- : current price of the bond
- : cash flow received at time (coupon or principal)
- : time in years until the -th cash flow
- : annual discount rate (yield)
- : total number of cash flows
The factor is the growth of one dollar over years under the effective annual rate convention. Dividing a future payment by it reverses that growth. At , a later otherwise identical payment has a lower present value; at the values are equal, while a negative rate reverses the ordering.

For the next examples, value a fixed-rate, noncallable bond immediately after a coupon date, with equally spaced payments, nonnegative coupons, positive face value, and a positive integer number of remaining periods. Let be the positive integer payment frequency and the nominal annual yield compounded times per year. Its periodic yield is , and the price is:
where:
- : bond price
- : cash flow at period
- : annual yield to maturity
- : number of payment periods per year
- : total number of payment periods
- : period counter (1, 2, ..., )
Here, counts payment periods rather than years, and we divide the annual yield by to get the per-period rate. This adjustment accounts for the compounding frequency. A bond paying semiannually compounds twice per year, so we use half the annual rate for each six-month period. The distinction matters. A 6% annual yield compounded semiannually is not the same as a 6% yield compounded annually. With semiannual compounding, you earn 3% in the first half of the year, then 3% on that larger balance in the second half, resulting in an effective annual yield slightly above 6%.
Pricing a Coupon Bond
Now that we understand how to discount individual cash flows, we can develop a simpler formula for pricing standard coupon bonds. Rather than discounting each payment individually, we can exploit the regular structure of coupon payments to derive a closed-form expression that separates the bond's value into two intuitive components.
For a standard coupon bond, we can separate the cash flows into an annuity (the coupons) and a lump sum (the face value). The coupon payments form what mathematicians call an ordinary annuity, a sequence of equal payments occurring at regular intervals. A compact formula exists for the present value of such an annuity, which we can combine with the discounted principal to get the total bond price.
Let be the coupon rate, the face value, the yield, the payment frequency, and the total number of periods. The price is:
where:
- : bond price
- : annual coupon rate (as a decimal)
- : face value (par value)
- : number of coupon payments per year
- : annual yield to maturity
- : total number of coupon periods ()
- : the periodic coupon payment amount
- : the present value annuity factor for ; at its continuous limit is .
The formula separates bond value into two components:
-
Coupon annuity (first term): The stream of periodic coupon payments forms an ordinary annuity. The annuity factor represents how much a dollar paid each period for periods is worth today. It's the sum of all the individual discount factors. To understand this factor, imagine receiving $1 at the end of each period for periods. The present value of the first dollar is , the second is , and so on. The annuity factor is simply the sum of this geometric series, expressed in a single formula. As the number of periods increases or the yield decreases, this factor grows. You're receiving more dollars, or each dollar is worth more in present value terms.
-
Principal repayment (second term): The face value received at maturity, discounted back to today by the factor . This term captures the return of the investor's principal. For long-maturity bonds, this discount factor can be quite small. At a 6% yield compounded semiannually over 30 years, the discount factor is approximately 0.17, meaning the present value of the $1,000 face value is only about $170.
For this fixed positive-cash-flow schedule, increasing the admissible yield decreases each discounted payment and therefore the total price. At the same maturity, yield, and compounding convention, reducing the coupon shifts more of the value toward the final principal payment and increases percentage price sensitivity. A zero-coupon bond is the limiting case. This comparison concerns percentage sensitivity, not necessarily the dollar price change of bonds with the same face value.
def _finite_number(value, name):
"""Convert one finite scalar; reject booleans and nonscalar inputs."""
if isinstance(value, (bool, np.bool_)) or np.ndim(value) != 0:
raise ValueError(f"{name} must be one finite number")
try:
result = float(value)
except (TypeError, ValueError, OverflowError) as exc:
raise ValueError(f"{name} must be one finite number") from exc
if not np.isfinite(result):
raise ValueError(f"{name} must be finite")
return result
def _regular_bond_inputs(face_value, coupon_rate, years_to_maturity, payments_per_year):
"""Validate the regular coupon-date model, not a dated settlement schedule."""
face = _finite_number(face_value, "face_value")
coupon = _finite_number(coupon_rate, "coupon_rate")
years = _finite_number(years_to_maturity, "years_to_maturity")
frequency = _finite_number(payments_per_year, "payments_per_year")
if face <= 0 or coupon < 0 or years < 0:
raise ValueError("Require positive face value, nonnegative coupon and maturity")
if frequency <= 0 or not frequency.is_integer():
raise ValueError("payments_per_year must be a positive integer")
periods = years * frequency
if not np.isfinite(periods) or abs(periods - round(periods)) > 1e-10:
raise ValueError(
"Maturity times frequency must be a finite integer period count"
)
return face, coupon, years, int(frequency), int(round(periods))
def price_bond(
face_value, coupon_rate, years_to_maturity, yield_rate, payments_per_year=2
):
"""Price regular, nonnegative fixed coupons on a coupon-date boundary.
Yield is a nominal annual quote compounded payments_per_year times.
Require yield > -payments_per_year and a whole remaining period count.
At zero remaining maturity, face value is due immediately. Irregular
schedules and between-coupon settlement need a dated cash-flow model.
"""
face, coupon, _, frequency, periods = _regular_bond_inputs(
face_value, coupon_rate, years_to_maturity, payments_per_year
)
annual_yield = _finite_number(yield_rate, "yield_rate")
periodic_yield = annual_yield / frequency
if periodic_yield <= -1:
raise ValueError("yield_rate must exceed -payments_per_year")
periodic_coupon = coupon * face / frequency
with np.errstate(over="ignore", invalid="ignore", under="ignore"):
log_growth = np.log1p(periodic_yield)
principal_factor = np.exp(-periods * log_growth)
annuity_factor = (
float(periods)
if periodic_yield == 0
else -np.expm1(-periods * log_growth) / periodic_yield
)
value = periodic_coupon * annuity_factor + face * principal_factor
if not np.isfinite(value) or value <= 0:
raise ValueError("Price is outside the supported floating-point range")
return float(value)# Price a 10-year bond with 6% coupon at different yield levels
face_value = 1000
coupon_rate = 0.06
years = 10
yields = [0.04, 0.05, 0.06, 0.07, 0.08]
prices = [price_bond(face_value, coupon_rate, years, y) for y in yields]Bond Prices at Different Yield Levels --------------------------------------------- Yield Price ($) Price/Par --------------------------------------------- 4.0% 1163.51 116.35% 5.0% 1077.95 107.79% 6.0% 1000.00 100.00% 7.0% 928.94 92.89% 8.0% 864.10 86.41%
This demonstrates the inverse relationship between yields and prices. When the yield equals the coupon rate (6%), the bond trades at par. When yields fall below the coupon rate, the bond trades at a premium because its fixed coupons are more attractive than market rates. When yields rise above the coupon rate, the bond trades at a discount.
Suppose new otherwise comparable bonds offer 7% coupons. A 6% coupon bond pays less interest per year for the same face value, so a lower purchase price can make its promised cash flows yield the required return. Comparisons need matching currency, credit, options, liquidity, and discounting assumptions. Equal promised-payment yields do not guarantee equal realized returns.

Pricing a Zero-Coupon Bond
Zero-coupon bonds strip away the complexity of multiple cash flows, leaving us with the purest expression of present value. With only a single payment at maturity, the pricing formula reduces to its most elemental form, making zero-coupon bonds ideal pedagogical tools for understanding how time and yield interact to determine value.
Zero-coupon bonds have a simpler pricing formula since there's only one cash flow:
where:
- : current price of the zero-coupon bond
- : face value paid at maturity
- : annual yield (discount rate)
- : time to maturity in years
The simplicity of this formula makes zero-coupon bonds ideal building blocks for understanding fixed income. The price represents exactly how much an investor must pay today to receive dollars in years, given the market's required return of . Notice that the price is always less than the face value (assuming a positive yield), and the discount increases with both yield and time to maturity. A 30-year zero-coupon bond at a 5% yield trades at roughly 23% of its face value, which reflects the substantial time value of money over such a long horizon.
def price_zero_coupon(face_value, years_to_maturity, yield_rate):
"""Price one future principal payment at an effective annual yield > -1."""
face = _finite_number(face_value, "face_value")
years = _finite_number(years_to_maturity, "years_to_maturity")
annual_yield = _finite_number(yield_rate, "yield_rate")
if face <= 0 or years < 0 or annual_yield <= -1:
raise ValueError("Require positive face, nonnegative maturity and yield > -1")
with np.errstate(over="ignore", under="ignore", invalid="ignore"):
value = face * np.exp(-years * np.log1p(annual_yield))
if not np.isfinite(value) or value <= 0:
raise ValueError("Price is outside the supported floating-point range")
return float(value)
# Price 5-year zero-coupon bonds at different yields
zcb_prices = [price_zero_coupon(1000, 5, y) for y in yields]Zero-Coupon Bond Prices (5-year maturity) ----------------------------------- Yield Price ($) ----------------------------------- 4.0% 821.93 5.0% 783.53 6.0% 747.26 7.0% 712.99 8.0% 680.58
At the same maturity and yield convention, a zero-coupon bond has greater percentage yield sensitivity than an otherwise comparable positive-coupon bond. Its value is concentrated in the terminal payment. Dollar price sensitivity also depends on the amount invested or face value, so it need not follow the same ranking.
Yield to Maturity
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.
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 in the bond pricing equation:
where:
- : observed market price (known)
- : yield to maturity (unknown, to be solved)
- , , : as defined previously
For a multi-period coupon bond, it is generally more practical to solve numerically than to derive an algebraic expression for . 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.
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]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.
Fixed Rate vs. Floating Rate Bonds
The bonds we've discussed so far have fixed coupon rates set at issuance. Floating rate bonds (or floating rate notes, FRNs) have coupon rates that reset periodically based on a reference rate plus a spread. This fundamental structural difference creates very different risk profiles and pricing dynamics.
Floating Rate Mechanics
As an illustrative advance-reset contract, suppose a quarterly floater uses a specified three-month reference rate plus 100 basis points. A 4.5% reference fixing would produce a 5.5% annualized coupon. Do not equate that term fixing with overnight SOFR: SOFR-linked notes may instead compound daily overnight rates in arrears, so the period's final coupon is not known at its start.
The coupon for each period is calculated as:
where:
- : the interest rate for the upcoming coupon period
- : the contractually specified fixing or period benchmark; its observation timing depends on the advance-reset or in-arrears convention
- : the contractual coupon margin, distinct from the current market-required discount margin
The contract determines the observation dates, lookback, and whether the rate is fixed in advance or compounded in arrears. Its quoted margin may be fixed at issuance, but the market-required margin can change with credit and liquidity conditions. Resets reduce some benchmark-rate exposure relative to an otherwise comparable fixed-rate bond; they do not remove credit, spread, basis, reset-lag, or liquidity risk.
Pricing Floating Rate Bonds
Future coupons depend on the contract's reference-rate observations. For a one-period valuation, assume the note's ex-coupon value will be immediately after its next coupon/reset. This is a pricing assumption, not a consequence of unchanged issuer credit quality alone.
Let be the current annualized fixed coupon, the full current coupon-period accrual fraction, the remaining time to payment under the same simple-accrual basis, and the current reference discount rate plus required discount margin. Between coupon dates the next full coupon is , not merely interest on the remaining time.
Here is face value, is the coupon fixed for the current period, is that full coupon's accrual fraction, is remaining time, is the chosen reference discount rate, and is the market-required discount margin. On a coupon date just after payment, for the new period, and this model is at par only if . Between dates, dirty price also includes accrued interest.
As the next payment approaches, while the full coupon remains due: dirty value tends to before payment and drops by that coupon after payment. Under the matching simple accrual convention, clean price is dirty price minus . A coupon reset does not erase an already accrued coupon.
def price_frn_between_resets(
face_value,
current_coupon_rate,
current_reference_rate,
time_to_reset,
*,
coupon_accrual_fraction,
spread=0,
):
"""Return dirty value under an explicit next-reset ex-coupon-par model.
coupon_accrual_fraction is the FULL current coupon period, while
time_to_reset is its remaining fraction on the same simple basis.
Credit/basis/curve effects beyond the stated flat discount margin are
not modeled. The coupon and principal are assumed to be paid in full.
"""
face = _finite_number(face_value, "face_value")
coupon_rate = _finite_number(current_coupon_rate, "current_coupon_rate")
reference = _finite_number(current_reference_rate, "current_reference_rate")
remaining = _finite_number(time_to_reset, "time_to_reset")
alpha = _finite_number(coupon_accrual_fraction, "coupon_accrual_fraction")
margin = _finite_number(spread, "spread")
if face <= 0 or alpha <= 0 or not 0 <= remaining <= alpha:
raise ValueError(
"Require positive face and full accrual fraction, with 0 <= remaining <= full fraction"
)
payment = face * (1 + coupon_rate * alpha)
denominator = 1 + (reference + margin) * remaining
if payment <= 0 or denominator <= 0:
raise ValueError(
"Require positive promised next payment and discount denominator"
)
value = payment / denominator
if not np.isfinite(value) or value <= 0:
raise ValueError("Price is outside the supported floating-point range")
return float(value)
# Example at the start of a quarter: fixed coupon 5.5%, reference 5%,
# full and remaining accrual fractions both 0.25, required margin 1%.
frn_price = price_frn_between_resets(
face_value=1000,
current_coupon_rate=0.055,
current_reference_rate=0.05,
time_to_reset=0.25,
coupon_accrual_fraction=0.25,
spread=0.01, # 100 bps required discount margin
)
# Halfway through the same coupon period, the full $13.75 coupon remains due.
mid_period_dirty = price_frn_between_resets(
1000, 0.055, 0.05, 0.125, coupon_accrual_fraction=0.25, spread=0.01
)
mid_period_accrued = 0.055 * 1000 * (0.25 - 0.125)
mid_period_clean = mid_period_dirty - mid_period_accruedFloating Rate Note Price: $998.77 Price deviation from par: $-1.23 Mid-period dirty price: $1006.20 Mid-period accrued: $6.88 Mid-period clean price: $999.33
The function returns about $998.77, or $1.23 below par. The fixed 5.5% coupon is below the 6% discount rate formed from the 5% reference rate and 1% required margin. This conditional example illustrates a small price deviation; it does not establish that FRN prices are stable under changes in credit spreads or other risks.
Clean Price, Dirty Price, and Accrued Interest
In many conventional bond markets, the quoted clean price excludes accrued interest, while the full or dirty price includes it. Express both prices and accrued interest on the same face-value basis before adding them. Flat-trading, defaulted, and ex-coupon securities can follow different rules.
For a standard cum-coupon settlement, the buyer receives the upcoming coupon and compensates the seller for the accrued portion through the settlement price. Entitlement follows the security's record-date and ex-coupon rules, not simply ownership on payment day. Special settlement adjustments can apply when a trade falls between the record date and payment date.
For a regular fixed coupon period using the stated actual-days coupon-period fraction, accrued interest is calculated as:
where:
- : the full periodic coupon amount (e.g., )
- : calendar days from the last payment date to settlement
- : total days in the current coupon period
The fraction represents the portion of the coupon period that has elapsed. This is the seller's "fair share" of the upcoming coupon. If exactly half the period has elapsed, the seller is entitled to half the coupon.
The dirty price is then:
where:
- : the full bond price on the stated face-value basis, before transaction charges
- : the quoted market price (excludes accrued interest)
Many markets quote clean prices because dirty prices jump discontinuously at coupon payment dates. The clean price provides a smoother series that better reflects changes in interest rates and credit conditions rather than the mechanical passage of time through a coupon period.
def calculate_accrued_interest(
face_value,
coupon_rate,
days_since_coupon,
days_in_period,
payments_per_year=2,
):
"""Regular nonnegative fixed coupon, using the stated coupon-day ratio.
Apply actual contractual dates and ex-coupon rules outside this example.
"""
face, coupon_rate, _, frequency, _ = _regular_bond_inputs(
face_value, coupon_rate, 1, payments_per_year
)
elapsed = _finite_number(days_since_coupon, "days_since_coupon")
period = _finite_number(days_in_period, "days_in_period")
if period <= 0 or not 0 <= elapsed <= period:
raise ValueError("Require positive period days and 0 <= elapsed <= period")
return (face * coupon_rate / frequency) * (elapsed / period)
# Example: 6% coupon bond, 45 days into a 182-day period
face_value = 1000
coupon_rate = 0.06
days_since_coupon = 45
days_in_period = 182
accrued = calculate_accrued_interest(
face_value, coupon_rate, days_since_coupon, days_in_period
)
clean_price = 1025.50 # Quoted market price
dirty_price = clean_price + accruedBond Settlement Calculation ---------------------------------------- Clean Price (quoted): $1,025.50 Accrued Interest: $7.42 Dirty Price (paid): $1,032.92 Days since last coupon: 45 Days in coupon period: 182 Fraction of period: 24.73%
When the buyer receives the next coupon payment, it will compensate them for both the accrued interest they paid and the interest earned during the remaining portion of the period they held the bond.

Day Count Conventions
The calculation of accrued interest depends on how days are counted, which varies by market and instrument type. These day count conventions affect both the numerator (days elapsed) and denominator (days in the period or year) of interest calculations. Though this seems like a minor technicality, day count conventions can significantly affect payments and valuations, particularly for large positions or over extended periods.
Common Conventions
A day-count convention is part of the contract, not an accuracy ranking. Different named conventions can produce different accrual fractions from the same calendar dates. Use the convention specified for the actual instrument, including any month-end, leap-year, or irregular-coupon rules.
Common day-count conventions include:
-
Actual/Actual: A family of conventions using actual calendar days. For the regular US Treasury coupon-period example here, accrued coupon interest uses elapsed actual days divided by actual days in the coupon period. Other Actual/Actual variants and irregular coupon periods need their specified rules.
-
30/360: A family of conventions using a stylized 360-day year and defined month-end adjustments. Variants differ in how they treat the 31st and February month ends. The simplified helper below is only the explicitly stated date-adjustment rule, not a complete implementation of every US corporate or municipal convention.
-
Actual/360: Actual elapsed days divided by 360. It is used in a range of money-market and floating-rate contracts. At the same quoted annual rate and elapsed-day count, it produces more accrued interest than Actual/365 Fixed because 360 is the smaller denominator; the elapsed interval need not exceed 360 days.
-
Actual/365 Fixed: Actual elapsed days divided by 365, including intervals containing leap days. Distinguish this named fixed-denominator convention from other Actual/365 variants.
from datetime import date
def day_count_30_360(start_date, end_date):
"""
Illustrative 30/360 rule: cap start day at 30, then conditionally cap end.
This is not a complete named US convention: February ends are not adjusted.
"""
d1 = min(start_date.day, 30)
d2 = end_date.day if d1 < 30 else min(end_date.day, 30)
days = (
360 * (end_date.year - start_date.year)
+ 30 * (end_date.month - start_date.month)
+ (d2 - d1)
)
return days
def day_count_actual(start_date, end_date):
"""Calculate actual days between two dates."""
return (end_date - start_date).days
# Compare conventions for a corporate bond
last_coupon = date(2024, 6, 15)
settlement = date(2024, 8, 20)
next_coupon = date(2024, 12, 15)
actual_days = day_count_actual(last_coupon, settlement)
thirty_360_days = day_count_30_360(last_coupon, settlement)
period_actual = day_count_actual(last_coupon, next_coupon)
period_30_360 = day_count_30_360(last_coupon, next_coupon)Day Count Convention Comparison -------------------------------------------------- Last coupon date: 2024-06-15 Settlement date: 2024-08-20 Next coupon date: 2024-12-15 Convention Days Elapsed Period Days -------------------------------------------------- Actual/Actual 66 183 30/360 65 180
The choice of convention affects the accrued interest calculation and therefore the dirty price. When comparing bonds or calculating returns, ensure consistent day count treatment.
The Price-Yield Relationship
The relationship between bond prices and yields is fundamental to fixed income analysis. Understanding this relationship helps explain interest rate risk and informs trading strategies. Every bond investor needs to understand how yield movements translate into price changes, since this relationship determines both risks and opportunities in fixed income markets.
Inverse Relationship
For the option-free bond with fixed positive cash flows used here, price decreases as its quoted yield increases within the model domain. That price-yield identity should not be read as saying every observed market-rate increase moves every bond price down: floating payments, embedded options, credit changes, and other drivers can change the comparison.
For an otherwise comparable bond paying a 5% coupon when the required nominal yield is 6%, the regular coupon-date price is below par. With yield and coupon held fixed, extending the payment schedule can change the price further. Duration measures the local percentage response to yield changes; maturity alone does not determine every dollar price-change comparison.

Convexity of the Price-Yield Curve
For an option-free bond with positive fixed payments and admissible yields, the price-yield curve is convex. Around the same base yield, a symmetric yield decline produces a larger price gain than the loss from an equal yield rise, with other inputs fixed. This does not guarantee a profitable holding period or describe bonds whose embedded options change their cash flows.
The curvature arises from the present value formula. Since price is a sum of terms like , the second derivative with respect to is positive (). To understand this intuitively, consider what happens as yields change. When yields fall, each basis point decrease has a larger dollar impact because you're multiplying the cash flows by increasingly larger discount factors. The discount factors are growing at an accelerating rate as yields decline. Conversely, as yields rise, the discount factors shrink, so each additional basis point increase has a diminishing effect on price because you're taking away smaller and smaller pieces of value. This asymmetry, where gains accelerate as yields fall and losses decelerate as yields rise, is precisely what convexity measures. Convexity quantifies how much the price-yield relationship curves away from a straight line.
# Demonstrate convexity with price changes
base_yield = 0.06
yield_change = 0.01 # 100 basis points
price_base = price_bond(1000, 0.06, 10, base_yield)
price_up = price_bond(1000, 0.06, 10, base_yield + yield_change)
price_down = price_bond(1000, 0.06, 10, base_yield - yield_change)
change_up = price_up - price_base
change_down = price_down - price_baseConvexity Demonstration: 10-Year 6% Bond -------------------------------------------------- Price at 6% yield: $1,000.00 Price at 7% yield: $928.94 Price at 5% yield: $1,077.95 Price change (+100bp): $-71.06 (-7.11%) Price change (-100bp): $77.95 (7.79%) Asymmetry: Gain $6.88 more than loss

For otherwise matched fixed positive-cash-flow bonds, longer maturity and lower coupon generally increase normalized yield convexity. The comparison concerns the same yield and compounding convention; it does not establish that every longer bond has a larger dollar gain or that an option-bearing bond is positively convex.
Practical Example: Comparing Bond Investments
Consider two bonds with the same face value, maturity, and semiannual payment convention. We will compare their promised-payment yields, rather than infer a complete relative-value recommendation from coupon and price alone:
- Bond A: 5-year maturity, 4% coupon, priced at $950
- Bond B: 5-year maturity, 6% coupon, priced at $1,040
# Bond A: 5-year, 4% coupon at $950
ytm_a = yield_to_maturity(950, 1000, 0.04, 5)
# Bond B: 5-year, 6% coupon at $1,040
ytm_b = yield_to_maturity(1040, 1000, 0.06, 5)
# Calculate total cash flows
total_cf_a = 40 * 5 + 1000 # 5 years of $40 coupons + face value
total_cf_b = 60 * 5 + 1000 # 5 years of $60 coupons + face value
# Current yields (annual coupon / price)
current_yield_a = 40 / 950
current_yield_b = 60 / 1040Bond Comparison Analysis ======================================================= Metric Bond A Bond B ------------------------------------------------------- Coupon Rate 4.00% 6.00% Price $950.00 $1,040.00 Current Yield 4.21 % 5.77 % Yield to Maturity 5.15 % 5.08 % Total Cash Flows $1,200 $1,300 -------------------------------------------------------
Bond A's YTM is about 5.15%, compared with 5.08% for Bond B under the same semiannual quote convention. The discount on Bond A contributes to its yield, while Bond B pays larger coupons but is purchased above par. These figures compare promised-payment IRRs; deciding which is better value also requires comparable credit, options, liquidity, taxes, costs, and investment objectives.
The current yield (annual coupon divided by price) can be misleading because it ignores price appreciation for discount bonds and price depreciation for premium bonds.
Limitations and Practical Considerations
Bond pricing in practice involves complexities beyond this simplified framework. Understanding these limitations helps practitioners apply theoretical models appropriately.
The assumption of a single discount rate (the YTM) for all cash flows is a significant simplification. In reality, interest rates vary by maturity, forming a yield curve. Short-term rates may differ substantially from long-term rates, and using a single rate ignores this term structure. More sophisticated approaches discount each cash flow at its own appropriate rate, a topic we'll address when discussing yield curve construction.
Credit risk affects corporate debt and can affect sovereign debt as well. Default probabilities and recovery expectations can change with issuer finances and economic conditions. A constant-spread model does not represent those changing exposures. Embedded options, such as an issuer call or holder put, can change payment timing and require valuation consistent with the contractual exercise rights.
Liquidity also influences prices. A bond that trades infrequently may have a wider bid-ask spread and a price that doesn't fully reflect current market conditions. Two bonds with identical cash flows may trade at different prices simply because one is more liquid than the other.
Despite these limitations, the discounted cash flow framework remains the foundation of bond analysis. Understanding this framework is essential before tackling more advanced topics like duration, convexity, and term structure models.
Summary
This chapter established the fundamental concepts for understanding and pricing bonds:
Bond Characteristics: Face value, coupon terms, maturity, payment frequency, and issuer define the payment contract. Low-credit-risk sovereign yields can serve as currency-specific benchmarks; corporate spreads can reflect credit, liquidity, and risk premia.
Pricing Framework: The regular coupon-date model discounts a fixed nonnegative coupon annuity and terminal principal under one stated yield convention. Zero-coupon bonds have one promised principal payment. Dated settlement, credit, and options need additional treatment.
Yield to Maturity: YTM is the price-equating IRR of the promised schedule. It is distinct from realized return, which depends on actual payments, holding period, reinvestment, and costs.
Fixed vs. Floating Rate: Fixed coupons create exposure to discount-yield changes. Floating resets reduce some benchmark exposure, but par at reset requires matching coupon and market valuation assumptions; changes in credit or discount margins still affect price.
Clean and Dirty Prices: In standard cum-coupon settlement, dirty price adds accrued interest to clean price on the same quotation basis. Contractual day-count, record-date, and ex-coupon rules determine the treatment.
Price-Yield Relationship: For positive fixed cash flows without embedded options, price decreases with yield and is convex. Symmetric yield declines and rises then produce asymmetric price changes, with other inputs fixed; this is not a guaranteed investment profit.
Key Parameters
The key parameters for bond pricing and analysis are:
-
Face Value (F): The principal amount repaid at maturity. Sets the baseline for calculating coupon payments and determines the bond's par value.
-
Coupon Rate (c): Annual fixed coupon divided by face value. At matched maturity, yield, and convention, higher coupons reduce percentage yield sensitivity; dollar sensitivity need not decline.
-
Years to Maturity (T): Time until final principal is due. In the regular schedule, is an integer period count. Maturity is one determinant of sensitivity, together with coupon, yield, and contractual terms.
-
Yield to Maturity (y): The rate equating the promised schedule's present value to price. Its compounding and settlement convention must be specified; the quote is not a guaranteed realized return.
-
Payment Frequency (m): Number of coupon payments per year. Affects compounding calculations. Semiannual payments use y/2 as the periodic rate.
-
Reference Rate: For floating rate bonds, the benchmark rate (e.g., SOFR) that determines coupon resets. Changes in this rate directly affect future coupon payments.
-
Spread: Distinguish the contractual margin used in the floating coupon from the current market-required discount margin. A fixed contractual margin need not equal the margin investors require later.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about bond fundamentals and pricing.
Bond Fundamentals and Pricing
Continue reading
Keep exploring
Join the discussionAsk a question or share what you learned.
Reference
Citation details
Cite or share this article.


Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!