「我想赚上涨的钱,但最多只亏 20%。」这句话听起来像一句愿望,但在组合保险(Portfolio Insurance)的框架里,它可以被写成一个可执行的仓位公式。最经典的实现之一就是 CPPI(Constant Proportion Portfolio Insurance,恒定比例投资组合保险)。
它的核心思想朴素到一句话就能说清:先划出一条绝不让组合跌破的「底线」(floor),把「当前资产 − 底线」当成缓冲垫,只拿缓冲垫的一个倍数去买风险资产。 市场涨,资产离底线远,缓冲垫厚,自动加仓;市场跌,缓冲垫被吃掉,自动减仓。下行约束就这样被「焊」进了每一天的仓位里。
本文用一段带两次崩盘的 6 年日度行情演示 CPPI 的真实表现,并诚实指出它最大的几个陷阱——尤其是一个教科书经常轻描淡写、实盘却要命的 gap risk(跳空风险)。
一、CPPI 的数学:把约束变成仓位#
设第 日组合净值为 ,你设定的保护底线(floor)为 。CPPI 定义三个量:
- 缓冲垫(cushion): —— 你「亏得起」的空间。
- 风险敞口(exposure): —— 拿缓冲垫的 倍去买股票/风险资产。 叫乘数(multiplier),通常取 3~5。
- 安全资产(bond): —— 剩下的钱买债券(无风险或低波动)。
下一日的净值更新为:
其中 必须 clamp 到 :当缓冲垫耗尽(),风险敞口直接归零,全部转债券,组合停在底线附近。
直觉上:乘数 越大,越激进——同样的缓冲垫撬动更多股票,上涨时弹性更大,但下跌时 cushion 被吞噬得更快、减仓更剧烈。

二、Python:一个可直接跑的 CPPI 引擎#
下面这段代码就是生成上方图表的同一套逻辑,去掉了绘图、只留核心。注意 clamp——它保证崩盘时不会出现「负敞口」这种无意义状态。
import numpy as np
def run_cppi(r_risky, r_safe, m, floor_level, rebalance=True):
"""
r_risky, r_safe : 长度 T 的日收益序列
m : 乘数
floor_level : 保护底线(占初始财富比例,如 0.80 = 最多亏 20%)
返回 A(净值), E(风险敞口), C(缓冲垫),长度 T+1
"""
A = np.empty(len(r_risky) + 1)
E = np.empty(len(r_risky) + 1)
C = np.empty(len(r_risky) + 1)
A[0] = 1.0
for t in range(len(r_risky)):
C[t] = A[t] - floor_level
E_t = m * C[t]
E_t = min(max(E_t, 0.0), A[t]) # clamp 到 [0, A_t]
bond = A[t] - E_t
A[t + 1] = E_t * (1 + r_risky[t]) + bond * (1 + r_safe[t])
E[t], C[t] = E_t, C[t]
E[-1], C[-1] = E[-2], C[-2]
return A, E, C
# ---- 示例:带一次 -38% 崩盘 + 一次 -18% 熊市的 GBM 市场 ----
def simulate_market(T=252*6, seed=2026):
rng = np.random.default_rng(seed)
mu_d, sig_d = 0.09/252, 0.17/np.sqrt(252)
r = rng.normal(mu_d, sig_d, size=T)
crash = int(T*0.60); r[crash] += -0.38
for k in range(1,6): r[crash+k] += -0.05*np.exp(-k/2.0)
bear = int(T*0.85); r[bear] += -0.18
return r
r_risky = simulate_market()
r_safe = np.random.default_rng(11).normal(0.025/252, 0.01/np.sqrt(252), len(r_risky))
A3, E3, C3 = run_cppi(r_risky, r_safe, m=3.0, floor_level=0.80)
print("CPPI(m=3) 终值=%.3f" % A3[-1])python三、缓冲垫如何变成「自动止损」#
把资产、底线、缓冲垫画在同一张图上,CPPI 的机制就一目了然:

崩盘那天(图中红点),风险资产暴跌,资产净值 下坠;但因为它已经把一部分钱放在债券里,下坠的幅度被缓冲垫吸收了一大部分。更关键的是第二天:新的 cushion 变薄,公式自动把风险敞口 调小,相当于「被动止损减仓」。你不需要手动设止损线——仓位公式替你做了。
这里有一个和「固定比例仓位」的本质区别:普通「永远 60% 股票」在崩盘里照跌 60% 的比例;CPPI 的仓位随 cushion 缩放,跌得越多、仓位越轻,天然带一个加速去杠杆的机制。
四、真实计算结果(6 年,含两次崩盘)#
我跑了一段 1512 个交易日(约 6 年)的行情:风险资产年化 9%、波动 17%,债券年化 2.5%,在第 60% 处注入 −38% 崩盘、第 85% 处注入 −18% 熊市。floor 固定为 0.80。结果:
| 策略 | 终值 | 最大回撤 |
|---|---|---|
| 买入持有(全仓风险) | 0.539 | −72.3% |
| CPPI m=2 | 0.853 | −45.3% |
| CPPI m=3 | 0.834 | −53.8% |
| CPPI m=5 | 0.812 | −58.3% |
CPPI 确实把回撤从 −72% 拉到了 −45%~−58% 区间——但注意:它没有把回撤锁死在 20%。这就是下一节的陷阱。

五、乘数 的权衡:收益与 cushion 侵蚀#
固定 floor=0.80,扫描乘数 :
- 终值:
- 最大回撤:

规律很清楚: 越大,上行参与度越高,但回撤也越深、cushion 被吞噬得越快。而且这个市场里 超过 2 之后终值反而下降——因为崩盘太多,更大的敞口只是多吃了跌幅。这告诉我们一个朴素的道理:CPPI 不是「无脑加 就能多赚」,乘数是对「我愿意为上行放弃多少下行保护」的定价。
六、真实陷阱(务必知道)#
-
Gap risk(跳空风险)——最致命的坑。 CPPI 是「今天看 cushion、明天调仓位」,中间隔着一个交易日。如果风险资产单日暴跌(比如 −38%),当天你还是满敞口,这一天的亏损会直接穿透 floor。这就是为什么上面 、floor=0.80 的理论最大单日亏损约 ,实测 −45.3%——它必然会阶段性跌破底线。教科书说「CPPI 保证不破 floor」,前提是你盘中能连续再平衡且资产不跳空;真实股票/期货做不到。
-
乘数是双刃剑,且上限受监管/券商约束。 机构 CPPI 的 往往被合规上限卡住(如 ≤5),否则 cushion 归零时一次反弹也救不回来。
-
floor 的设置是信仰问题。 floor 设太高(如 0.95),cushion 永远薄,几乎全仓债券,等于放弃了大部分上涨;设太低(如 0.5),回撤保护形同虚设。floor 该多高,取决于你的「最大可忍亏损」,不是拍脑袋。
-
再平衡摩擦与税费。 每天调仓、且 cushion 薄时频繁加减仓,交易成本会啃掉本就不多的超额。实盘要用阈值带(cushion 变化超 x% 才调)压低换手。
-
债券端并非真·无风险。 上面用 2.5% 短债近似安全资产;若 floor 用长债或信用债对冲,安全资产自己也会跌,floor 保护被打折扣。
七、小结#
CPPI 的价值不在于「保证不亏」,而在于把一句模糊的「我不想亏太多」翻译成每天自动执行的仓位纪律:缓冲垫厚就进攻、薄就防守,下行约束内嵌在公式里,不靠人盯盘、不靠意志力。但它有三个必须正视的前提——跳空会击穿 floor、乘数要匹配你的风险预算、再平衡有成本。把它当「带保护的可变仓位策略」而非「保本神器」,才算用对了。
本文行情为带崩盘的 GBM 合成数据,仅用于演示 CPPI 机制;实盘落地请用真实标的收益序列,并把 gap risk、交易成本、floor 路径(通常用现值折现而非常数)一并建模。CPPI 仅做多头研究示例,不构成任何投资建议。