每个期限一个利率
到目前为止,一个收益率就折现了债券的所有付款。实际上,借出 6 个月、2 年和 10 年的资金,利率各不相同,随期限变化的形态就是收益率曲线。零息利率是在某个日期的单笔付款对应的年利率,中间没有票息。曲线通常随期限上升(等得越久,投资者要求越多),但也可能走平或倒挂(短期利率高于长期利率),市场把倒挂解读为对增长的警告。
import numpy as np
maturities = np.array([0.5, 1, 2, 5, 10, 30]) # years where the market quotes a rate
rates = np.array([0.040, 0.042, 0.045, 0.048, 0.050, 0.052]) # an upward-sloping curve
# The rate at 3 years is not quoted: interpolate linearly between 2 years (4.5%) and 5 years (4.8%)
print(float(np.interp(3, maturities, rates))) # 0.046
print(np.interp([0.25, 7, 40], maturities, rates).tolist()) # [0.04, 0.0488, 0.052] flat outside the quoted range折现因子与远期利率
期限 t 的零息利率 r 给出折现因子 (1 + r) ** -t:在 t 时支付的一个单位今天值多少。曲线中还藏着预期。借出 2 年、利率 4.5%,应当等于先借 1 年、利率 4.2%,再按约定利率 f 续借一年:(1 + 4.5%)² = (1 + 4.2%)(1 + f),所以 f = 4.80%。这个远期利率就是第二年的盈亏平衡利率,今天就锁定。一般地,f = ((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1。
def discount_factor(rate, t):
return (1 + rate) ** -t
print(round(float(discount_factor(0.045, 2)), 5)) # 0.91573 one unit paid in 2 years
r1, t1, r2, t2 = 0.042, 1, 0.045, 2
forward = ((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1
print(round(forward, 5)) # 0.04801 the rate for year 2, implied by the curve
print(round((1 + r1) ** t1 * (1 + forward) ** (t2 - t1), 5), round((1 + r2) ** t2, 5)) # 1.09 1.09 both routes agree在整条曲线上定价
用单一收益率定价,是把 1 年后的付款和 10 年后的付款一视同仁。曲线法更忠实:对每笔付款读取其自身日期的零息利率(必要时插值),转成折现因子,再对 现金流 * 因子 求和。现金流相同的两只债券,不论报价收益率如何,价格必须相同,银行就是这样对债券按市价估值的。在我们的曲线上,3 年期 5% 债券价格是 1011.25,而按平坦的 4.6% 是 1010.98:曲线平缓时二者相近,但当曲线扭曲时,不同债券的反应各不相同。
times = np.array([1.0, 2.0, 3.0])
flows = np.array([50.0, 50.0, 1050.0]) # a 3-year 5% bond with annual coupons
zero = np.interp(times, maturities, rates)
print(zero.tolist()) # [0.042, 0.045, 0.046]
print((discount_factor(zero, times)).round(5).tolist()) # [0.95969, 0.91573, 0.87379]
print(round(float((flows * discount_factor(zero, times)).sum()), 4)) # 1011.2462 on the curve
print(round(float((flows / 1.046 ** times).sum()), 4)) # 1010.9752 with one flat 4.6% instead# The forward rates between the quoted maturities, in one array expression
t = np.array([1.0, 2.0, 5.0])
r = np.array([0.042, 0.045, 0.048])
forwards = ((1 + r[1:]) ** t[1:] / (1 + r[:-1]) ** t[:-1]) ** (1 / np.diff(t)) - 1
print(forwards.round(5).tolist()) # [0.04801, 0.05] year 2 and years 3 to 5: an upward-sloping curve implies rising forwards插值是一种建模选择对零息利率做线性插值是最简单的规则,但不是唯一的:从业者也会对折现因子的对数插值或使用样条,这种选择会改变报价点之间的远期利率和价格。在最后一个报价期限之外,
np.interp 让利率保持不变,这是一种约定而非事实。发布曲线时务必说明所用方法。- 收益率曲线是各期限的零息利率;缺失的期限用插值读取(
np.interp,范围外保持不变)。 - 折现因子
(1 + r) ** -t;远期利率((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1。 - 价格 = 各现金流按其自身日期的零息利率折现后的
现金流 * 折现因子之和;说明插值方法。
下一节:期权收益,看涨或看跌期权到期时的支付,这是期权定价的起点。
实验:评分内容
- 用
interpolate_rate读取报价期限之间的利率25 分 - 用
discount_factor把利率转换为折现因子25 分 - 用
forward_rate读出曲线中隐含的远期利率25 分 - 用
price_on_curve在整条曲线上为现金流定价25 分