Skip to content

12.1 收益率曲线建模 ​

即期利率、远期利率与到期收益率 ​

理解三者之间的区别是固定收益分析的基础。

名词解释:即期利率(Spot Rate)

当前时刻投资于零息债券至未来某一时刻 T 可以获得的年化收益率,记为 r(0,T)。零息债券价格与即期利率的关系为 P(0,T)=e−r(0,T)⋅T。

名词解释:远期利率(Forward Rate)

在当前时刻约定的,未来某个时间段 [T1,T2] 内的借贷利率,记为 f(0,T1,T2)。它与即期利率的关系为 f(0,T1,T2)=r(0,T2)T2−r(0,T1)T1T2−T1。

到期收益率(Yield to Maturity, YTM)是使债券未来现金流的现值恰好等于其当前市场价格的单一折现率:

$$P = \sum_{t=1}^n \frac{C_t}{(1+YTM)^t}$$

三者的核心区别在于:即期利率是零息债券的收益率;YTM 是附息债券的内部收益率(假设所有息票以 YTM 再投资);远期利率是隐含在未来时间段内的利率预期。

PYTHON48 行 · 1.7 KB
📄此处有展示代码48 行 · 1.7 KB展开 ▼
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)$$

三个因子的经济学解释:

  • β0:水平因子(Level),长期利率水平(τ→∞ 时 r→β0)
  • β1:斜率因子(Slope),短期与长期利差(τ→0 时 r→β0+β1,短期-长期 =−β1)
  • β2:曲率因子(Curvature),中端利率的隆起程度
  • λ:衰减参数,控制各因子的衰减速度
PYTHON22 行 · 765 B
📄此处有展示代码22 行 · 765 B展开 ▼
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)$$
PYTHON12 行 · 323 B
📄此处有展示代码12 行 · 323 B展开 ▼
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)中端与两端反向变动
PYTHON27 行 · 870 B
📄此处有展示代码27 行 · 870 B展开 ▼
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) ​

所有期限的利率同时变化相同的幅度 Δr。这是最简单的利率风险情景,由久期对冲。债券组合对平移的敏感度:

$$\Delta P \approx -D_{\text{mod}} \cdot P \cdot \Delta r$$

斜率变化(Twist / Steepening-Flattening) ​

短端和长端反向变动。例如<降息交易>通常导致短端利率下降但长端稳定甚至上升("陡峭化")。对冲斜率风险需要结合不同期限的债券。

蝶式变化(Butterfly) ​

中端利率变动而短端和长端基本不变(或反向),体现为曲率的改变。

PYTHON15 行 · 597 B
📄此处有展示代码15 行 · 597 B展开 ▼
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`(利率情景分析)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

实战要点:在实践中,收益率曲线建模的最大挑战在于数据质量。市场报价存在买卖价差、流动性差异、以及不同债券的特殊性(如老券/新券溢价、含权特征等)。在构建曲线之前,必须对原始数据执行严格的数据清洗流程,剔除异常值和流动性不足的报价点。

常见误区 ​

误区1:到期收益率是"未来的收益保证"。 YTM 隐含"票息按同一利率再投资"的假设,利率变动时实际收益会偏离 YTM。

误区2:远期利率是对未来的预测。 远期利率是无套利框架下从即期曲线推演的市场定价,包含期限溢价,不是预测值。

误区3:收益率曲线三主成分的解释力放之四海皆准。 水平/斜率/曲率三因子在美国国债解释力超 95%,但在新兴市场或信用债中占比结构不同。

误区4:Nelson-Siegel 参数可以直接跨市场比较。 NS/Svensson 的四参数(水平/斜率/曲率/曲率衰减)依赖于样本与估计方法,数值本身不具可比性。

本章要点 ​

  • 即期利率是贴现率之锚,远期利率由即期曲线无套利推出,YTM 是两者的加权近似
  • Nelson-Siegel 四参数对应曲线的水平、斜率、曲率与衰减速度
  • 曲线变动可分解为平移、扭转、蝶式三个正交主成分
  • 曲线策略(骑乘、杠铃 vs 子弹)对关键期限的敏感度不同,需用关键利率久期管理

跨模块延伸:资产定价与因子模型、定价数值方法