Short-Rate Interest Rate Models › Bond Pricing with Short-Rate Models
Quantitative FinanceAll chapters Short-Rate Interest Rate Models › Bond Pricing with Short-Rate ModelsOne section · Sections in this chapter7 / 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
Vasicek and CIR models provide closed-form bond pricing formulas. Let's implement these and construct yield curves.
def vasicek_bond_price(r, t, T, kappa, theta, sigma):
"""
Calculate zero-coupon bond price under Vasicek model.
Parameters:
-----------
r : float or array
Current short rate
t : float
Current time
T : float
Maturity time
kappa, theta, sigma : float
Vasicek model parameters
Returns:
--------
float or array : Bond price(s)
"""
tau = T - t
if tau <= 0:
return 1.0
B = (1 - np.exp(-kappa * tau)) / kappa
A = np.exp(
(theta - sigma**2 / (2 * kappa**2)) * (B - tau)
- sigma**2 * B**2 / (4 * kappa)
)
return A * np.exp(-B * r)
def cir_bond_price(r, t, T, kappa, theta, sigma):
"""
Calculate zero-coupon bond price under CIR model.
Parameters:
-----------
r : float or array
Current short rate
t : float
Current time
T : float
Maturity time
kappa, theta, sigma : float
CIR model parameters
Returns:
--------
float or array : Bond price(s)
"""
tau = T - t
if tau <= 0:
return 1.0
gamma = np.sqrt(kappa**2 + 2 * sigma**2)
exp_gamma = np.exp(gamma * tau)
denominator = (gamma + kappa) * (exp_gamma - 1) + 2 * gamma
B = 2 * (exp_gamma - 1) / denominator
A = ((2 * gamma * np.exp((kappa + gamma) * tau / 2)) / denominator) ** (
2 * kappa * theta / sigma**2
)
return A * np.exp(-B * r)Yield Curve Construction
Let's generate yield curves for different initial short rates.
# Maturities from 0.25 to 30 years
maturities = np.array([0.25, 0.5, 1, 2, 3, 5, 7, 10, 15, 20, 30])
r_scenarios = [0.02, 0.05, 0.08] # Different starting rates
# Calculate yields for each scenario
def calculate_yields(r, maturities, kappa, theta, sigma, model="vasicek"):
"""Calculate continuously compounded yields for given maturities."""
yields = np.zeros(len(maturities))
for i, T in enumerate(maturities):
if model == "vasicek":
P = vasicek_bond_price(r, 0, T, kappa, theta, sigma)
else:
P = cir_bond_price(r, 0, T, kappa, theta, sigma)
yields[i] = -np.log(P) / T
return yields
# Compute yield curves for scenarios
vasicek_yields = np.array(
[
calculate_yields(r, maturities, kappa, theta, sigma, "vasicek")
for r in r_scenarios
]
)
cir_yields = np.array(
[
calculate_yields(r, maturities, kappa, theta, sigma, "cir")
for r in r_scenarios
]
)

The yield curves demonstrate several important features of mean-reverting short-rate models:
-
Convergence: All curves converge toward a common long-term yield regardless of the initial rate. This reflects the mean-reversion property, where expected rates eventually gravitate toward .
-
Curve shape flexibility: When , the yield curve is upward-sloping (normal). When , the curve is downward-sloping (inverted). When , the curve is relatively flat but shows a slight convexity effect.
-
Risk premium implicit in long yields: Long-term yields don't equal exactly due to the interaction between mean reversion and interest rate volatility, which creates a convexity adjustment in bond prices.
Monte Carlo Bond Pricing
We can also price bonds using Monte Carlo simulation by averaging the discount factor across paths. This approach is more general than the closed-form solutions and extends to models without analytical formulas.
The Monte Carlo estimator for the zero-coupon bond price is:
This formula averages the realized discount factors across all simulated paths:
- number of simulation paths
- : number of time steps
- : short rate on path at step
- : time step size
The inner sum approximates the integral for path using a Riemann sum. The exponential of the negative of this integral gives the discount factor for that path. Averaging across all paths produces an unbiased estimator of the bond price, with standard error decreasing as .
This approach generalizes to complex interest rate derivatives. For any derivative with payoff depending on rates at multiple times, we can estimate the price by simulating paths, computing the payoff on each path, discounting at the realized short-rate integral, and averaging.
def monte_carlo_bond_price(rates, dt, T):
"""
Calculate bond price using Monte Carlo simulation.
Parameters:
-----------
rates : ndarray
Simulated rate paths, shape (n_paths, n_steps+1)
dt : float
Time step size
T : float
Bond maturity
Returns:
--------
float : Estimated bond price
"""
# Number of steps to maturity
steps_to_T = int(T / dt)
# Calculate integral of rates using trapezoidal rule
rate_integral = np.trapezoid(rates[:, : steps_to_T + 1], dx=dt, axis=1)
# Discount factor for each path
discount_factors = np.exp(-rate_integral)
# Average across paths
return discount_factors.mean(), discount_factors.std() / np.sqrt(
len(discount_factors)
)
# Compare analytical and Monte Carlo prices
test_maturities = [1, 2, 5, 10]
mc_results = []
for T_mat in test_maturities:
# Analytical prices
v_ana = vasicek_bond_price(r0, 0, T_mat, kappa, theta, sigma)
c_ana = cir_bond_price(r0, 0, T_mat, kappa, theta, sigma)
# Monte Carlo prices
v_mc, v_se = monte_carlo_bond_price(vasicek_paths, dt, T_mat)
c_mc, c_se = monte_carlo_bond_price(cir_paths, dt, T_mat)
mc_results.append((T_mat, v_ana, v_mc, c_ana, c_mc))Bond Price Comparison: Analytical vs Monte Carlo ================================================================= Maturity Vasicek MC Vasicek CIR MC CIR ----------------------------------------------------------------- 1 0.973999 0.973864 0.973954 0.973928 2 0.940067 0.940146 0.939821 0.939827 5 0.824426 0.826258 0.822950 0.823289 10 0.647405 0.650255 0.643928 0.644419
The Monte Carlo estimates closely match the analytical formulas, validating our simulation implementation. The small differences arise from simulation error, which decreases with more paths or finer time steps.
