CPPI 回撤控制:用动态乘数把组合下行锁进保险箱
止损单只能保护一个仓位,CPPI 保护的是整个组合的底线。恒定比例组合保险的核心是一条自动化纪律:风险敞口 = 乘数 m × 缓冲垫(净值 − 保本地板),净值离地板越近仓位越轻,跌到地板自动清零——数学上保证(连续交易时)永不击穿。本文用纯 numpy 完整实现 CPPI,在植入 40 天熊市 + 单日 -8% 跳空的 5 年路径上实测:买入持有最大回撤 -45%,CPPI m=4 只有 -16.8%,代价是牛市收益从 +57% 降到 +28%。然后打穿两个教科书不讲的死穴:gap risk——周频再平衡 + -12% 跳空下,m=8 的击穿地板概率高达 58.9%(m×单期最大跌幅 > 100% 就是死刑判决书);cash-lock——高地板 + 大乘数在崩盘后缓冲垫归零,风险资产随后反弹 141%,CPPI 却抱着债券几乎纹丝不动。乘数选择的铁律:m < 1/单期最大可能跌幅,日频对应 m≤5、周频只敢 m≤3。附四类真实陷阱(中阶)。
先说结论:CPPI 是一条把「亏到多少必须停」写成自动化纪律的公式——它数学上保证(在连续交易假设下)组合永不跌破你预设的保本地板,代价是牛市里少赚、跳空时可能被击穿、深跌后可能永远趴在债券里错过反弹。这三个代价教科书通常一笔带过,本文用蒙特卡洛把它们一个个量化出来。
一、为什么止损单不够:你需要的是组合级的地板#
大多数人控制回撤的工具是止损单:单个仓位跌 8% 就砍。但止损单有三个结构性缺陷:
- 它是仓位级的,不是组合级的。十个仓位各自止损 8%,组合仍可能在相关性趋同的崩盘里同时触发,总损失远超预期;
- 它是一次性的。止损触发后你空仓了,何时回来靠拍脑袋;
- 它是离散的。跌 7.9% 不触发、跌 8.1% 全砍,风险敞口对亏损不连续。
CPPI(Constant Proportion Portfolio Insurance,恒定比例组合保险)由 Black 和 Jones 在 1987 年提出,解决的正是这三个问题。它的思想可以用一句话说完:
你愿意亏的钱决定你能冒的险。离底线越远,仓位越重;离底线越近,仓位越轻;碰到底线,仓位归零。
这是一个连续的、组合级的、自动重新入场的风控状态机。
二、机制拆解:三个变量的舞蹈#
CPPI 只有三个核心变量:
- 地板 F(floor):你发誓不跌破的净值底线。比如起始资金 100 万、地板 85 万。地板本身按无风险利率缓慢增长(因为 85 万现在买成债券,到期就是保本额);
- 缓冲垫 C(cushion):当前净值 V 减去地板,
C = max(V − F, 0)。这是你「输得起」的钱; - 乘数 m(multiplier):胆量参数。风险资产敞口
E = m × C。
每期再平衡逻辑就三行:
cushion = max(V - F, 0.0) # 还能输多少
exposure = min(m * cushion, V) # 风险敞口 = 乘数 × 缓冲垫(不加杠杆则封顶净值)
bond = V - exposure # 其余买无风险资产python为什么这样就能保本?考察最坏情况:假设风险资产单期最多跌 d(比如 20%),你的损失最多是 E × d = m × C × d。只要 m × d ≤ 1,损失就不会超过缓冲垫 C,净值就不会跌破地板。这就是乘数选择的铁律:m < 1 / 单期最大可能跌幅。风险资产单日可能跌 20%,日频再平衡就只敢用 m ≤ 5;周频再平衡时单周可能跌 30%,m 就只剩 3。
完整实现(纯 numpy,日频再平衡):
import numpy as np
def run_cppi(risky_ret, m=4, floor_pct=0.85, rf_daily=0.02/252, max_lev=1.0):
"""CPPI:每日再平衡,地板按无风险利率增长"""
n = len(risky_ret)
V = np.empty(n + 1); V[0] = 1.0 # 净值
F = np.empty(n + 1); F[0] = floor_pct # 地板
w_hist = np.empty(n) # 风险资产权重轨迹
for t in range(n):
cushion = max(V[t] - F[t], 0.0)
w = min(m * cushion / V[t], max_lev) # 不加杠杆版本
w_hist[t] = w
V[t+1] = V[t] * (1 + w * risky_ret[t] + (1 - w) * rf_daily)
F[t+1] = F[t] * (1 + rf_daily) # 地板随 rf 复利抬升
return V, F, w_histpython注意一个细节:w = m × cushion / V 是把敞口表达成权重。当净值远高于地板时 m × C > V,权重被 max_lev=1 封顶——不加杠杆的 CPPI 在顺风时就是满仓,和买入持有无异;它的差异全部体现在逆风时。
三、实测:植入一场崩盘,看地板如何兜底#
模拟 5 年日线(1260 天):基础参数年化收益 9%、波动 16%,然后故意植入一段 40 天的熊市(日均 -1.1%)、一次单日 -8% 跳空,崩盘后接一段 200 天的反弹。这条路径的设计意图是同时测试 CPPI 的三个侧面:崩盘保护、反弹参与、以及后面要讲的 cash-lock。

主路径结果(起始净值 1.0,地板 0.85):
| 策略 | 总收益 | 最大回撤 | Sharpe |
|---|---|---|---|
| 买入持有 | +56.8% | -45.0% | 0.50 |
| CPPI m=3 | +25.0% | -15.1% | 0.31 |
| CPPI m=4 | +28.4% | -16.8% | 0.32 |
| CPPI m=6 | +25.0% | -17.9% | 0.26 |
两个数字值得盯着看。第一,最大回撤从 -45% 压到 -16.8%(m=4),回撤幅度砍掉近三分之二——这正是地板承诺的兑现:净值逼近地板时权重自动趋零,跌势被切断。第二,总收益从 +57% 降到 +28%,几乎腰斩。这不是实现 bug,是 CPPI 的定价:崩盘中减仓的同一套机制,在反弹初期也让你只有很轻的仓位,反弹的头一段肉是吃不到的,要等缓冲垫重新长厚、权重慢慢爬回来。
权重轨迹把这个机制看得更清楚:

崩盘段里三条线全部俯冲向零——乘数越大俯冲越陡(m=6 对缓冲垫变化最敏感)。崩盘结束后,m 越大权重回升越快,但也意味着它在下跌途中每一天都维持着相对更高的敞口,所以 m=6 的最大回撤(-17.9%)反而比 m=3(-15.1%)更深。乘数是缓冲垫的放大器,双向放大。

四、死穴一:Gap risk——跳空是保本承诺的粉碎机#
CPPI 的保本证明依赖一个假设:你能在净值滑向地板的过程中连续减仓。现实市场会跳空:隔夜跌 12% 开盘,你的「连续减仓」在这 12% 里一次都没有机会执行。
跳空何时击穿地板?回到铁律:单期损失 = w × d ≈ m × (C/V) × d。击穿条件是 m × d > 1。d = 12% 时,m > 8.3 的组合只要在满缓冲状态挨一记跳空就直接穿底。更隐蔽的是再平衡频率:周频再平衡等于把「单期」从一天拉长到一周,d 变成单周累计跌幅,铁律里的分母变大、安全乘数骤降。
用 800 条蒙特卡洛路径实测(3 年、周频再平衡、日跳空概率 0.4%、跳空幅度 -12%):

| 乘数 m | 击穿地板概率 | 3 年中位终值 |
|---|---|---|
| 2 | 0.0% | 0.995 |
| 4 | 0.8% | 0.930 |
| 6 | 7.1% | 0.907 |
| 8 | 58.9% | 0.903 |
| 10 | 71.1% | 0.901 |
m 从 6 到 8,击穿概率从 7% 跳到 59%——这不是渐变,是相变。m=8 意味着只要某周累计跌超 12.5% 就必穿,而含跳空的路径三年里大概率会出现至少一次这样的周。更讽刺的是中位终值几乎不随 m 增长(0.93 → 0.90):在这个参数区间,大乘数没有换来更高收益,只换来了更高的死亡概率。「乘数越大越激进越赚」是对 CPPI 最常见的误读——激进的上限被 max_lev 封顶,激进的下场却没有底。
五、死穴二:Cash-lock——保住了本金,錯過了整个反弹#
第二个死穴更心理层面,也更常见。把地板抬到 90%、乘数放到 6、周频调仓,跑同一条主路径:

崩盘结束时(第 560 天),净值 0.942、地板 0.941——缓冲垫只剩 0.001,风险资产权重被公式压到 0.6%。随后风险资产从低点反弹 141%,而这个 CPPI 组合呢?缓冲垫太薄,权重爬升极其缓慢,5 年终值 1.031——只比保本线多 3%,眼睁睁看着 141% 的反弹从头顶飞过。
这就是 cash-lock(现金锁死):缓冲垫一旦被崩盘吃光,E = m × C ≈ 0,组合实质上变成一只纯债基金。数学上它没有违约——地板确实保住了;体验上它是灾难——你买 CPPI 是为了「有保护地参与市场」,锁死后你只剩保护、没有参与。而且缓冲垫的重建只能靠无风险利率,年化 2% 的利息要把缓冲垫从 0.1% 养回 5%,需要漫长的岁月。
缓解 cash-lock 的工程手段有三类,各有代价:
- 降低地板 / 定期重置地板:每年把地板重设为当前净值的 85%,代价是保本承诺变成「滚动保本」,跨年累计仍可能大幅亏损;
- 给权重设下限(如永远至少 5% 风险资产):代价是极端行情下地板可能被击穿,保本从「保证」降级为「大概率」;
- 用期权替代动态复制:直接买保护性认沽,gap risk 和 cash-lock 全部消失,代价是持续支付期权费——CPPI 本质上就是在用交易复制一个 put,复制版省了权利金、但把跳空风险和锁死风险留给了自己。天下没有免费的保险。
六、四类真实陷阱#
陷阱一:用日频回测的乘数上实盘的周频调仓。 回测里 m=6 日频安全(单日很少跌 17%),实盘图省事改周频,单周跌 17% 并不罕见——同一个 m,再平衡频率一变,铁律 m < 1/d 里的 d 翻了倍。乘数必须和再平衡频率绑定校准,二者不可分开调参。
陷阱二:忽略交易成本对高乘数的惩罚。 m 越大,权重对净值波动越敏感,每日调仓量越大。本文模拟未计成本;实盘中 m=6 的年换手可以轻松达到数倍于 m=3,双边千一的成本一年就能吃掉 1-2 个点。高乘数策略加上成本后,收益优势进一步消失,只剩风险劣势。
陷阱三:把「地板没破」当成「策略成功」。 cash-lock 案例里地板完好无损,但投资者在第 560 天之后实质上持有的是一只货币基金,机会成本 141%。评估 CPPI 不能只看击穿概率,必须同时看锁死概率和锁死后的机会成本——三者合起来才是这份「保险」的真实保费。
陷阱四:在均值回归市场里高估 CPPI。 CPPI 是趋势跟随型风控:跌了减仓、涨了加仓。在单边市里这是美德,在震荡市里这是「高买低卖」的自动化执行器——每次假摔都减仓在低点、每次假涨都加仓在高点,磨损收益。本文主路径是单边崩盘 + 单边反弹,对 CPPI 相对友好;若换成来回震荡的路径,CPPI 相对买入持有的差距会进一步拉大。买保险的前提是你真的怕那场火灾,而不是隔壁的烟味。
七、收尾#
CPPI 把「最多亏多少」从愿望变成公式:敞口 = m × (净值 − 地板),三行代码就是一台自动化的风控状态机。实测它把 -45% 的回撤压到 -17%,代价是收益腰斩。它的两个死穴都源于同一个根:保本证明依赖连续交易假设——跳空撕碎这个假设时是 gap risk(m=8 周频击穿率 59%),缓冲垫归零时是 cash-lock(错过 141% 反弹)。乘数的铁律 m < 1/单期最大跌幅 必须和再平衡频率一起校准,而如果你无法接受这两个死穴,那你要的其实不是 CPPI,是一张真的认沽期权——记得付权利金。
下一篇我们讲 CPPI 的直系升级版 TIPP:把地板从「起始资金的 85%」改成「历史最高净值的 85%」,让保本线跟着利润一起上抬——它解决了 CPPI「只保本金不保利润」的痛点,也带来了新的代价。
本文所有模拟数据与图表由纯 numpy 生成,参数与代码可复现。CPPI 不构成投资建议,任何保本机制的「保证」都有其数学前提,跳空面前没有铁地板。