4. 到期收益率求解:牛顿法与二分法实现

任务说明

给定债券的现金流与当前市场价格,反解到期收益率(YTM)。本案例用两种方法实现:二分法(稳健但慢)与牛顿法(快但依赖导数)。最后比较两者的迭代次数与收敛速度。

环境与依赖

仅需 Python 3,无第三方依赖。债券定价公式:

P = Σ C/(1+y)^t + F/(1+y)^n

核心实现

def bond_price(face, coupon_rate, years, ytm, freq=1):
    """按年付息债券定价。freq=1 表示每年付息一次。"""
    n = int(years * freq)
    c = face * coupon_rate / freq
    r = ytm / freq
    pv = sum(c / (1 + r) ** t for t in range(1, n + 1))
    pv += face / (1 + r) ** n
    return pv

def ytm_bisection(price, face, coupon_rate, years, freq=1,
                  lo=1e-6, hi=1.0, tol=1e-10, max_iter=200):
    """二分法:价格随 ytm 单调递减,用区间收缩逼近。"""
    for i in range(1, max_iter + 1):
        mid = (lo + hi) / 2
        p = bond_price(face, coupon_rate, years, mid, freq)
        if abs(p - price) < tol:
            return mid, i
        if p > price:      # 价格偏高 → ytm 偏低 → 抬高下界
            lo = mid
        else:
            hi = mid
    return (lo + hi) / 2, max_iter

def ytm_newton(price, face, coupon_rate, years, freq=1,
               y0=0.05, tol=1e-10, max_iter=100):
    """牛顿法:f(y)=P(y)-price,f'(y) 用解析导数。"""
    n = int(years * freq)
    c = face * coupon_rate / freq
    y = y0
    for i in range(1, max_iter + 1):
        r = y / freq
        p = bond_price(face, coupon_rate, years, y, freq)
        # dP/dy = Σ -t*C/(1+r)^(t+1)/freq - n*F/(1+r)^(n+1)/freq
        dp = sum(-t * c / (1 + r) ** (t + 1) for t in range(1, n + 1))
        dp += -n * face / (1 + r) ** (n + 1)
        dp /= freq
        diff = p - price
        if abs(diff) < tol:
            return y, i
        y = y - diff / dp
    return y, max_iter

if __name__ == "__main__":
    face, coupon_rate, years = 1000.0, 0.05, 10
    true_y = 0.06
    market_price = bond_price(face, coupon_rate, years, true_y)
    print(f"市场价 = {market_price:.6f}")

    yb, ib = ytm_bisection(market_price, face, coupon_rate, years)
    yn, in_ = ytm_newton(market_price, face, coupon_rate, years)
    print(f"二分法: ytm={yb:.10f}, 迭代={ib}")
    print(f"牛顿法: ytm={yn:.10f}, 迭代={in_}")

运行与验证

直接执行 python ytm.py。判定标准:

  • 两种方法求出的 ytm 都应接近 0.06(误差 < 1e-8);
  • 牛顿法迭代次数应明显少于二分法(通常 4~6 次 vs 30+ 次);
  • 若把 true_y 改成其他值(如 0.03、0.10),结果应随之改变且两法一致。

收敛过程示意

YTM 求解流程 市场价格 P 二分法 区间 [lo,hi] 收缩 牛顿法 y ← y - f(y)/f'(y) 线性收敛 迭代次数多 二次收敛 迭代次数少 输出 YTM 与迭代次数

关键点

  • 二分法要求价格关于 ytm 单调递减,因此区间收缩方向由 p > price 决定。
  • 牛顿法需要解析导数;若导数接近 0 会发散,实际工程中常加步长限制或回退到二分。
  • 两种方法结果一致即可交叉验证实现正确性。