MBS 提前偿付与负凸性:为什么利率下行时你的债券不涨反跌
普通债券的凸性是白送的礼物:利率跌得越多赚得越超预期。MBS 把这份礼物反过来送给了借款人。受控现金流引擎证明:WAC 6.00%、净票息 5.50%、基准价格精确 100.0000 的池子,有效久期 6.34 年、有效凸性 -313.2;拿一支久期几乎完全匹配的国债(久期 6.35、凸性 +46.5)对照,利率下行 300bp 时国债涨 +21.32%、MBS 只涨 +9.70%(上行捕获率 45.5%),上行 300bp 时国债跌 -17.12%、MBS 跌 -19.86%。根因是 WAL 从 3.47 年拉伸到 12.18 年(3.51 倍),有效久期在 +125bp 见顶 7.58 年、到 -300bp 塌缩到 3.08 年。DV01 中性对冲比率 0.9977,但四个方向的残差 P&L 全为负——这就是空 gamma。三条对抗式检验锁死因果:CPR≡0 安慰剂最小凸性 +143.8 全区间转正,CPR≡10% 恒定速度对照 +44.7 也全为正,说明负凸性来自「利率敏感的期权」而非「回款快」;S 曲线陡度 k 从 0 扫到 8,最小凸性从 +25.6 塌到 -2427.5(高阶)
核心结论:你以为你买了一只久期 6.34 年的债券,其实你买了一只久期在 3.08 到 7.58 年之间反复横跳的债券——而且它总是朝对你不利的方向跳。 本文构造一个 30 年 pass-through MBS:WAC 6.00%、投资人净票息 5.50%、贴现率取 5.50%,基准价格精确等于 100.0000(这是引擎正确性的第一道校验)。它的有效久期 6.34 年、有效凸性 -313.2、WAL 9.20 年。再拿一支久期几乎完全匹配的 7.79 年国债(久期 6.35、凸性 +46.5)做对照。结果:利率下行 300bp,国债涨 +21.32%,MBS 只涨 +9.70%,上行捕获率 45.5%;利率上行 300bp,国债跌 -17.12%,MBS 跌 -19.86%,多亏 2.74 个百分点。所有数字均由脚本真实计算,stats.json 保留全部原始值。

一、你不是债主,你是期权的卖方#
买一支普通国债,你只承担利率风险。买一支 MBS,你额外免费送给了借款人一份美式看涨期权:任何时候,只要再融资划算,他可以按面值 100 把贷款还给你,然后去别处借更便宜的钱。
这笔交易的不对称性是致命的:
- 利率跌 → 借款人行权,你收回一堆现金,只能按更低的利率再投资(收缩风险)
- 利率涨 → 借款人绝不提前还款,你手里的低息资产被锁死,错过高息(展期风险)
两个方向你都输。用凸性的语言说:普通债券的价格-利率曲线向上凸(凸性为正,利率两边跑你都占便宜);MBS 的曲线在票面附近被压平甚至向下凹。凸性为正是白送的礼物,凸性为负是你在替别人交期权费。
二、S 曲线:提前偿付对利率高度非线性#
建模的核心是把「再融资激励」映射成 CPR(年化提前偿付率)。激励定义为 refi incentive = WAC − 当前市场按揭利率,用一个有界的 arctan S 曲线拟合:
CPR_MIN, CPR_MAX = 0.04, 0.55 # 自然周转地板 / 再融资潮饱和
K_STEEP, X0_INCEN = 3.0, 0.70 # 陡度 / 触发阈值(百分点)
BURN_BETA, BURN_FLOOR = 2.2, 0.40
def scurve_cpr(incentive_pp, burnout=0.0, k=K_STEEP, x0=X0_INCEN):
s = 0.5 + np.arctan(k * (incentive_pp - x0)) / np.pi # ∈ (0,1)
cpr_fresh = CPR_MIN + (CPR_MAX - CPR_MIN) * s
# 燃尽只削弱「再融资超额部分」,不削弱自然周转地板
mult = BURN_FLOOR + (1.0 - BURN_FLOOR) * np.exp(-BURN_BETA * burnout)
return CPR_MIN + (cpr_fresh - CPR_MIN) * multpython三个细节决定了模型是否像真的:
- 地板不为零。就算利率涨到天上,仍有 4% CPR 来自换房、离婚、违约——这部分与利率无关。
- 有触发阈值。激励小于约 70bp 时借款人懒得动(手续费、时间成本),CPR 几乎不变。
- Burnout(燃尽)。该走的人早走了,剩下的是对利率不敏感的「钉子户」。这里用
burnout = 1 − 实际余额 / 计划余额度量,燃尽后再融资敏感度最低衰减到 40%。

三、现金流引擎:一个循环讲完 30 年#
pass-through 的月度现金流只有三块:净票息利息、计划本金、提前偿付本金。计划本金用沉基金公式(剩余期数 rem 会随时间递减):
for i in range(TERM_M): # TERM_M = 360
rem = TERM_M - i
factor = wac_m / ((1.0 + wac_m) ** rem - 1.0) # 当期计划本金占比
sched_prin = min(bal * factor, bal)
burn = max(0.0, 1.0 - bal / sched_bal) # 燃尽程度
cpr = scurve_cpr(incentive, burnout=burn) * min(1.0, (i + 1) / SEASON_M)
smm = 1.0 - (1.0 - cpr) ** (1.0 / 12.0) # 年化 CPR → 单月死亡率
pp = (bal - sched_prin) * smm # 提前偿付本金
cf[i] = bal * NET_CPN / 12.0 + sched_prin + pp # 投资人拿净票息
bal -= (sched_prin + pp)pythonsched_bal 是一条平行跑的「无提前偿付」轨道,专门用来度量燃尽。注意 SMM 作用在扣除计划本金之后的余额上——顺序搞反会系统性高估提前偿付。
正确性校验:因为贴现率取得与净票息相同(5.50%),而所有本金都按面值 100 收回,基准价格必须精确等于 100。脚本实际输出 100.0000,引擎没有漏钱也没有多算。
四、现金流被利率来回撕扯:3.47 年 vs 12.18 年#
同一个池子,在不同利率情景下的本金回收时间分布完全是两个东西:
| 情景 | 首年平均 CPR | WAL | 有效久期 |
|---|---|---|---|
| -300bp | 10.76% | 3.47 年 | 3.08 年 |
| 0bp | 2.41% | 9.20 年 | 6.34 年 |
| +300bp | 1.18% | 12.18 年 | 7.19 年 |
WAL 被拉伸了 3.51 倍。你以为自己持有的是一个久期 6.34 年的资产,实际上它在 3.47 年和 12.18 年这两个完全不同的资产之间摇摆——而切换开关握在别人手里。

五、久期反转:你的对冲比率一直在错#
有效久期和有效凸性用中心差分算——关键是按揭利率和贴现率必须同步冲击,只动贴现率算出来的是错的:
def eff_dur_conv(shift_bp, h=25.0, **kw):
p0 = price_pool(shift_bp, **kw)
pu = price_pool(shift_bp + h, **kw) # 按揭利率与贴现率一起 +h
pd = price_pool(shift_bp - h, **kw)
dy = h / 10000.0
return (pd - pu) / (2 * p0 * dy), (pd + pu - 2 * p0) / (p0 * dy * dy), p0python结果非常反直觉:MBS 的有效久期在 +125bp 处见顶 7.58 年,然后随着利率下行一路塌缩,到 -300bp 只剩 3.08 年(缩短 59.4%)。也就是说,利率跌得越多,你能赚钱的「杠杆」自动变得越小;利率涨得越多,你亏钱的杠杆自动变大。
局部有效凸性最深处是 -839.5(出现在 -50bp,即期权最贴价的位置),基准点 -313.2。有意思的是负凸性只在 [-75bp, +50bp] 这个窄带里成立(25 个格点里 6 个),因为深度实值/虚值时 S 曲线两端都趋平、期权 gamma 消失。

六、对冲残差:两个方向都亏,这就是空 gamma#
用国债做 DV01 中性对冲,比率算出来是 0.9977(几乎 1:1,因为久期和价格都匹配)。理论上如果两者凸性相同,任何平行移动都该完美对冲。实际残差:
| 情景 | -300bp | -100bp | +100bp | +300bp |
|---|---|---|---|---|
| 残差 P&L(每 100 面值) | -11.57 | -3.02 | -0.88 | -2.78 |
24 个非零格点,残差全部为负。 这不是运气不好,这是空 gamma 的定义:无论利率往哪边走、走多远,你都在向对手方支付时间价值。绝对残差合计 86.12。
还有一个更该被警惕的发现:用基准点凸性做二阶泰勒展开去预测残差,全区间 R² = -0.90(比直接用均值预测还差),只有在 ±100bp 以内 R² 才有 0.54。原因是 MBS 的凸性本身极度局部——反解出的「路径平均凸性」在 -100bp 是 -555.9,到 -300bp 却只有 -206.9,在 +300bp 更是只剩 -18.8。拿单点凸性去做大幅移动的风险预算,会系统性高估亏损、给你虚假的安全感。

七、三个安慰剂:负凸性 100% 来自提前偿付期权#
光看到负凸性不够,得证明它只来自提前偿付的利率敏感性,而不是别的什么。
安慰剂 1:关掉提前偿付(CPR≡0)。 只剩纯计划摊销。有效凸性全区间为正,最小值 +143.8、基准 +192.2(久期 11.19 年、WAL 19.31 年)。负凸性精确消失。
对照 2:恒定 CPR=10%(有提前偿付,但与利率无关)。 最小凸性 +44.7、基准 +57.7、久期缩到 5.55 年。回款确实变快了,但凸性依然全为正。这一条最关键:它排除了「因为回款快所以凸性负」这个错误归因——负凸性来自 CPR 对利率的导数,不是 CPR 的水平。
敏感性 3:S 曲线陡度 k 扫描。 让借款人的「行权效率」从零变到极高:
| k | 0.0 | 0.25 | 1.0 | 3.0(基准) | 8.0 |
|---|---|---|---|---|---|
| 最小有效凸性 | +25.6 | -32.9 | -245.6 | -839.5 | -2427.5 |
| -300bp 上行捕获率 | 66.1% | 56.4% | 48.2% | 45.5% | 44.7% |
k=0(CPR 完全不随利率变)时凸性转正,单调、连续、无跳跃。负凸性的强度就是借款人行权效率的单调函数。

检验 4:差分步长收敛性。 凸性会不会是数值噪声?h 从 5bp 扫到 100bp,凸性分别是 -298.1 / -300.0 / -313.2 / -355.6 / -343.6,极差仅 57.4 且恒为负。结论不靠调参。(但要注意久期本身对步长敏感得多:从 6.44 年掉到 5.28 年——这本身就是负凸性的另一个表现。)
检验 5:Burnout 开关。 关掉燃尽后基准凸性从 -313.2 恶化到 -415.7,-300bp 捕获率从 45.5% 掉到 34.0%。燃尽是负凸性的安全气囊:老池子没那么毒,新发行的当期票面池最危险。(一个诚实的反例:关掉燃尽后的最小凸性是 -808.2,反而比含燃尽的 -839.5 略浅——因为燃尽把期权 gamma 从贴价区推开了。看负凸性不能只看谷底,要看你实际持仓的那个点。)

八、陷阱与局限(诚实版)#
1. 标题里的「不涨反跌」在本模型里没有字面发生。 -300bp 时 MBS 仍然是 +9.70%,只是相对久期匹配国债少赚一半多。真正出现绝对下跌,需要叠加 OAS 走阔(危机中 MBS 利差与利率往往同向恶化)或负凸性驱动的凸性对冲踩踏——本模型用的是固定静态利差,没有建模 OAS 动态,因此低估了真实世界的下行。
2. 平行移动是最温和的假设。 现实中曲线会陡峭化/平坦化,而提前偿付看的是长端按揭利率、贴现看的是整条曲线。单因子平行冲击既算不出关键利率久期,也算不出曲线形变带来的损益。
3. 确定性路径 ≠ OAS。 真正的 MBS 定价要在几百条蒙特卡洛利率路径上跑现金流再取均值,才能捕捉期权的路径依赖(尤其 burnout 依赖历史最低利率,而非当期利率)。本文的单路径静态贴现会低估期权价值、从而低估负凸性绝对值。
4. S 曲线参数是构造的,不是拟合的。 k=3.0、x0=0.70、CPR_MAX=0.55 都是我设的。好消息是敏感性扫描显示:结论的符号在 k>0.25 的整个区间稳健,只有量级依赖参数。
5. 没有建模违约、修改、政策干预。 大规模再融资项目、贷款服务商行为、信贷标准收紧都会让 S 曲线整体平移或变形。
九、实践 Checklist#
- 永远不要用票面久期(coupon duration)给 MBS 做对冲,用有效久期,且必须让按揭利率与贴现率同步冲击。
- 对冲比率要动态重算。本例久期从 7.58 年到 3.08 年跨度 59.4%,静态对冲在大幅移动中必然失效。
- 别用单点凸性做大幅位移的风险预算。二阶泰勒在 ±100bp 内 R² 只有 0.54,全区间是 -0.90;要压力测试就直接重定价,别外推。
- 区分「速度」与「敏感度」。CPR 高不等于负凸性;CPR 对利率的斜率才是。恒定 CPR=10% 的对照凸性是 +44.7。
- 优先看池子的燃尽状态。深度折价/深度燃尽的老池子负凸性弱得多;当期票面(at-the-money)的新池子凸性谷底 -839.5,最危险。
- 在风险报告里同时列出局部凸性和路径平均凸性。-839.5 和 -206.9 讲的是完全不同的故事,只报一个都是误导。
- 把负凸性当成一个已卖出的期权仓位来管理,不是当成一个债券的二阶修正项——它的对冲成本(本例绝对残差合计 86.12/100 面值)应该被明确定价,而不是被摊进「跟踪误差」。