主题切换
12.1 收益率曲线建模
即期利率、远期利率与到期收益率
理解三者之间的区别是固定收益分析的基础。
名词解释:即期利率(Spot Rate)
当前时刻投资于零息债券至未来某一时刻
名词解释:远期利率(Forward Rate)
在当前时刻约定的,未来某个时间段
到期收益率(Yield to Maturity, YTM)是使债券未来现金流的现值恰好等于其当前市场价格的单一折现率:
$$P = \sum_{t=1}^n \frac{C_t}{(1+YTM)^t}$$
三者的核心区别在于:即期利率是零息债券的收益率;YTM 是附息债券的内部收益率(假设所有息票以 YTM 再投资);远期利率是隐含在未来时间段内的利率预期。
此处有展示代码展开 ▼
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import CubicSpline
# 示例:从市场上观测到的债券价格 bootstrapping 即期曲线
def bootstrap_spot_curve(bonds):
"""
bonds: list of dicts with keys 'maturity', 'coupon', 'price'
使用 bootstrapping 方法从附息债券价格中提取即期利率曲线
"""
bonds = sorted(bonds, key=lambda x: x['maturity'])
spot_rates = []
maturities = []
for i, bond in enumerate(bonds):
T = bond['maturity']
coupon = bond['coupon'] / 2 # 半年付息
price = bond['price']
n_payments = int(T * 2)
if T <= 0.5:
# 短端直接用零息公式
r = -np.log(price / (100 + coupon)) / T
else:
# 使用已求出的即期利率折现前面的现金流
pv_coupons = 0
for j in range(1, n_payments):
t_j = j * 0.5
if t_j <= maturities[-1]:
# 有对应的即期利率
idx = np.searchsorted(maturities, t_j)
if idx < len(spot_rates):
r_spot = spot_rates[idx]
else:
r_spot = spot_rates[-1]
pv_coupons += coupon * np.exp(-r_spot * t_j)
else:
# 需要插值
cs = CubicSpline(maturities, spot_rates)
r_interp = cs(t_j)
pv_coupons += coupon * np.exp(-r_interp * t_j)
r = -np.log((price - pv_coupons) / (100 + coupon)) / T
spot_rates.append(r)
maturities.append(T)
return np.array(maturities), np.array(spot_rates)点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `bootstrap_spot_curve`(bonds: list of dicts with keys 'maturity', 'coupon', 'price')。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
Nelson-Siegel 和 Svensson 模型
Nelson-Siegel 模型
Nelson-Siegel 模型用三个参数来描述整个收益率曲线:
$$r(\tau) = \beta_0 + \beta_1\left(\frac{1 - e^{-\tau/\lambda}}{\tau/\lambda}\right) + \beta_2\left(\frac{1 - e^{-\tau/\lambda}}{\tau/\lambda} - e^{-\tau/\lambda}\right)$$
三个因子的经济学解释:
:水平因子(Level),长期利率水平( 时 ) :斜率因子(Slope),短期与长期利差( 时 ,短期-长期 ) :曲率因子(Curvature),中端利率的隆起程度 :衰减参数,控制各因子的衰减速度
此处有展示代码展开 ▼
python
from scipy.optimize import minimize
def nelson_siegel(tau, beta0, beta1, beta2, lam):
"""Nelson-Siegel 模型 —— 即期利率关于期限的函数"""
x = tau / lam
factor_loading = (1 - np.exp(-x)) / x
return (
beta0
+ beta1 * factor_loading
+ beta2 * (factor_loading - np.exp(-x))
)
def fit_nelson_siegel(maturities, yields):
"""拟合 Nelson-Siegel 参数"""
def objective(params):
beta0, beta1, beta2, lam = params
predicted = nelson_siegel(maturities, beta0, beta1, beta2, lam)
return np.sum((predicted - yields)**2)
result = minimize(objective, [0.03, -0.02, 0.01, 2.0],
bounds=[(-0.1, 0.2), (-0.3, 0.3), (-0.3, 0.3), (0.1, 30.0)])
return result.x点击展开可浏览运行结果
拟合参数: beta0=0.0399, beta1=-0.0195, beta2=0.0137, tau=2.22 期限 原始 拟合 0.2年 2.227% 2.214% 0.5年 2.323% 2.374% 1.0年 2.671% 2.648% 2.0年 3.152% 3.051% 3.0年 3.236% 3.317% 5.0年 3.527% 3.616% 7.0年 3.851% 3.756% 10.0年 3.871% 3.848% 20.0年 3.853% 3.926% 30.0年 3.988% 3.948%
Svensson 模型
Svensson 是 NS 的扩展,增加了第四个因子来捕捉收益率曲线上更复杂的形态(如双峰):
$$r(\tau) = \beta_0 + \beta_1\left(\frac{1 - e^{-\tau/\lambda_1}}{\tau/\lambda_1}\right) + \beta_2\left(\frac{1 - e^{-\tau/\lambda_1}}{\tau/\lambda_1} - e^{-\tau/\lambda_1}\right) + \beta_3\left(\frac{1 - e^{-\tau/\lambda_2}}{\tau/\lambda_2} - e^{-\tau/\lambda_2}\right)$$
此处有展示代码展开 ▼
python
def svensson(tau, beta0, beta1, beta2, beta3, lam1, lam2):
"""Svensson 扩展模型"""
x1 = tau / lam1
x2 = tau / lam2
f1 = (1 - np.exp(-x1)) / x1
f2 = (1 - np.exp(-x2)) / x2
return (
beta0
+ beta1 * f1
+ beta2 * (f1 - np.exp(-x1))
+ beta3 * (f2 - np.exp(-x2))
)点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `svensson`(Svensson 扩展模型)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
主成分分析(PCA)对收益率曲线的解释
主成分分析是理解收益率曲线动态变化的最强大工具之一。对收益率变动的协方差矩阵进行特征分解,前三个主成分通常可以解释 95%以上的方差:
| 主成分 | 解释比例 | 经济学解释 | 曲线变化形态 |
|---|---|---|---|
| PC1 | ~80-85% | 水平移动(Level/Shift) | 各期限利率同向变动 |
| PC2 | ~10-15% | 斜率变化(Slope/Twist) | 短端和长端反向变动 |
| PC3 | ~3-5% | 曲率变化(Curvature/Butterfly) | 中端与两端反向变动 |
此处有展示代码展开 ▼
python
def yield_curve_pca(yield_changes):
"""
收益率曲线变动的主成分分析
yield_changes: DataFrame, shape (n_dates, n_maturities)
每行是每天各期限的收益率变动
"""
# 协方差矩阵
cov_matrix = np.cov(yield_changes.T)
# 特征分解
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
# 按特征值降序排列
idx = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]
# 解释比例
total_var = eigenvalues.sum()
explained_ratio = eigenvalues / total_var
print("PCA 结果:")
for i in range(min(3, len(eigenvalues))):
print(f" PC{i+1}: {explained_ratio[i]*100:.1f}% 方差解释")
print(f" 特征向量: {eigenvectors[:, i].round(3)}")
return eigenvalues, eigenvectors, explained_ratio点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `yield_curve_pca`(收益率曲线变动的主成分分析)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
曲线平移/扭转/蝶式变化
在实际的风险管理中,交易员和风险经理关注的不是单个期限的利率变化,而是三种标准化情景:
平行移动(Parallel Shift)
所有期限的利率同时变化相同的幅度
$$\Delta P \approx -D_{\text{mod}} \cdot P \cdot \Delta r$$
斜率变化(Twist / Steepening-Flattening)
短端和长端反向变动。例如<降息交易>通常导致短端利率下降但长端稳定甚至上升("陡峭化")。对冲斜率风险需要结合不同期限的债券。
蝶式变化(Butterfly)
中端利率变动而短端和长端基本不变(或反向),体现为曲率的改变。
此处有展示代码展开 ▼
python
def scenario_analysis(portfolio_durations, scenario_shifts):
"""
利率情景分析
portfolio_durations: dict {maturity: modified_duration * position_value}
scenario_shifts: dict {maturity: rate_change_bps}
"""
total_pnl = 0
for maturity, dv01 in portfolio_durations.items():
# 线性近似:PnL = -DV01 * rate_change_bps / 10000 * notional
shift = scenario_shifts.get(maturity, 0)
pnl = -dv01 * shift
total_pnl += pnl
print(f" 期限 {maturity}y: DV01={dv01:.0f}, 变动 {shift:+d}bp -> PnL={pnl:+.0f}")
return total_pnl点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `scenario_analysis`(利率情景分析)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
实战要点:在实践中,收益率曲线建模的最大挑战在于数据质量。市场报价存在买卖价差、流动性差异、以及不同债券的特殊性(如老券/新券溢价、含权特征等)。在构建曲线之前,必须对原始数据执行严格的数据清洗流程,剔除异常值和流动性不足的报价点。