尾部风险平价:用 CVaR 替代波动率,把每个资产的『崩盘贡献』拉平
波动率平价的隐藏杀机:它用标准差衡量风险,而标准差对『平时波澜不惊、崩盘时血流成河』的资产(短波动信用、卖期权、carry)严重低估——于是波动率平价会把最高权重堆给这类资产,账面看似最『安全』,实则把组合的命门交给了最肥的尾巴。本文用纯 numpy 造 4 资产(股票/债券/商品/短波动信用),故意让『短波动信用』日常波动仅 3.5%(比债券还低)却带灾难性左尾(偏度 -10.2、超峰度 113)。实测暴露陷阱:波动率平价只给它 17.7% 权重,它却贡献 39.2% 的 CVaR 尾部风险——低波动是伪装。尾部风险平价改用 5% CVaR 度量风险、Euler 分解算边际尾部贡献、迭代把每个资产的尾部贡献拉到 25%:短波动信用权重被砍到 11.5%,样本内 Sharpe 从 0.34 升到 0.57、最大回撤从 -14.6% 收到 -12.2%、组合 CVaR 从 0.96% 降到 0.83%。全文打穿波动率≠风险、CVaR 次可加性为何让分解成立、尾部估计的样本量诅咒、样本内最优必然乐观四类陷阱(中高阶)。
先说结论:波动率平价把风险等同于标准差,而标准差是个近视眼——它看得见日常的颠簸,看不见那条平时藏起来、崩盘时吞掉你半个组合的肥尾。传统风险平价(risk parity)让每个资产对组合波动的贡献相等,逻辑优雅。但它有个致命盲点:对那些「平时收租、偶尔爆仓」的资产(卖期权、carry 交易、短久期高收益信用),标准差系统性低估其真实风险,于是波动率平价反而给它们最高权重。尾部风险平价(Tail Risk Parity)用 CVaR(条件在险价值)替代标准差,把「谁在崩盘时贡献最大」拉平。本文用纯 numpy 完整实现,并用一个精心设计的陷阱资产把差距演给你看。
一、波动率平价的盲点:低波动 ≠ 低风险#
风险平价的思想很干净:不要让任何单一资产主导组合风险。等权组合的问题是,股票波动是债券的三倍,60/40 组合里 90% 的风险其实都来自股票——「资产等权」不等于「风险等权」。波动率平价的解法是按风险反比配权,让每个资产贡献相同的组合波动。
但「风险 = 波动率」这个等式,藏着一个巨大的假设:收益分布是对称的、薄尾的(理想情况是正态)。对股票、债券这类资产,标准差还算能代表风险。可金融市场里有一类资产专门违反这个假设:
- 卖期权 / 短波动策略:平时稳定收取权利金,波动极低;但市场一崩,亏损是断崖式的
- carry 交易:高息货币平时慢慢升值收息,风险事件一来汇率瞬间崩塌
- 短久期高收益信用:违约前一直付息、价格平稳,违约时本金蒸发
这些资产的共同特征是负偏度 + 高峰度:日常波动率(标准差)很低,但左尾极肥。用标准差衡量,它们看起来是「低风险高收益」的圣杯;实际上它们是把风险从「日常波动」挪到了「尾部」,标准差这个近视眼根本看不见。
我们造一组资产,故意埋进这样一个陷阱:
import numpy as np
rng = np.random.default_rng(42)
N, T = 4, 3000
names = ["股票", "债券", "商品", "短波动信用"]
# 日常波动率:短波动信用最低(3.5%),比债券(6%)还低——这正是 VP 会跳进去的陷阱
vols = np.array([0.16, 0.06, 0.20, 0.035]) / np.sqrt(252)
corr = np.array([[1.00,-0.20,0.35,0.45],
[-0.20,1.00,-0.10,0.05],
[0.35,-0.10,1.00,0.25],
[0.45,0.05,0.25,1.00]])
L = np.linalg.cholesky(corr)
z = rng.standard_normal((T, N)) @ L.T
mu = np.array([0.0006, 0.00015, 0.0005, 0.00055])
rets = mu + z * vols
# 共同崩盘日(股票/商品中等左尾)
crash = rng.random(T) < 0.010
jump = np.where(crash, -np.abs(rng.normal(0.022, 0.012, T)), 0.0)
rets[:, 0] += jump * 0.9
rets[:, 2] += jump * 0.6
# 短波动信用:独立、更稀、灾难性左尾——平时收租,尾部巨亏
credit_crash = rng.random(T) < 0.008
rets[:, 3] += np.where(credit_crash, -np.abs(rng.normal(0.09, 0.04, T)), 0.0)python
关键数字:短波动信用的年化波动率只有 16.8%(和股票相当,但其中大部分来自罕见崩盘,日常波动极低),偏度 -10.2、超峰度 113——这是一条灾难性的肥左尾。而股票的偏度只有 -0.11、超峰度 0.5,接近正态。
二、CVaR:不只问「会亏多少」,而是「崩了之后平均亏多少」#
要看见尾部,得换一把尺子。VaR(Value at Risk,在险价值)是第一直觉:5% VaR 就是「95% 的日子里亏损不超过这个数」。但 VaR 有两个众所周知的毛病:它只告诉你尾部的门槛,不告诉你门槛之外有多惨;而且它不满足次可加性(两个资产合并后 VaR 可能大于各自之和),这让它无法用于风险分解。
CVaR(Conditional VaR,又叫 Expected Shortfall)修好了这两点。5% CVaR 是「最坏 5% 的日子里,平均亏多少」:
def cvar(x, alpha=0.05):
var = np.quantile(x, alpha)
tail = x[x <= var]
return -tail.mean()python用这两把尺子分别量四个资产,排序就变了:

| 资产 | 年化波动率 | 日 CVaR(5%) |
|---|---|---|
| 股票 | 16.2% | 2.15% |
| 债券 | 6.0% | 0.78% |
| 商品 | 19.9% | 2.55% |
| 短波动信用 | 16.8% | 2.33% |
用日常眼光(如果只看未含崩盘的平静期波动),短波动信用像是低风险资产。但 CVaR 把它的真面目揭出来了:它的尾部风险(2.33%)逼近股票、远超债券。CVaR 之所以能做到,是因为它满足次可加性——这是 Artzner 等人「一致性风险度量」四公理之一,也是它能替代标准差做风险分解的数学前提。VaR 不满足这条,所以下面的风险贡献分解只能建立在 CVaR 上。
三、CVaR 风险贡献:谁在崩盘时真正主导组合#
风险平价的核心是「风险贡献」的概念。对波动率,各资产的边际贡献能用协方差算出来。对 CVaR,我们用 Euler 分配(Euler allocation):由于 CVaR 是权重的一阶齐次函数,它可以被精确拆分成各资产的贡献之和,且第 个资产的 CVaR 贡献是:
直觉极其清晰:在组合整体落入最坏 5% 的那些日子里,资产 平均亏了多少,乘以它的权重,就是它对组合尾部风险的贡献。注意条件是「组合在尾部」而非「资产自己在尾部」——这抓住了「谁在组合最需要时掉链子」。
def cvar_risk_contrib(w, R, alpha=0.05):
pr = R @ w
var = np.quantile(pr, alpha)
tail_mask = pr <= var # 组合落入尾部的那些日子
cond_mean = R[tail_mask].mean(axis=0) # 各资产在这些日子的平均收益
return w * (-cond_mean) # Euler 贡献python现在见证陷阱。先按波动率平价(按波动率反比配权)建组合,看看 CVaR 贡献怎么分布:
iv = 1.0 / (np.array([rets[:, i].std() for i in range(N)]) * np.sqrt(252))
w_vp = iv / iv.sum() # 波动率平价权重python结果触目惊心:
| 资产 | VP 权重 | CVaR 贡献占比 |
|---|---|---|
| 股票 | 18.3% | 25.8% |
| 债券 | 49.1% | 9.9% |
| 商品 | 14.9% | 25.1% |
| 短波动信用 | 17.7% | 39.2% |
波动率平价只给了短波动信用 17.7% 的权重,它却贡献了 39.2% 的组合尾部风险。波动率平价自以为把风险摊平了,实际上把组合的命门——那条 -10.2 偏度的肥尾——悄悄堆到了最危险的资产上。这就是短波动策略在 2008、2018Vol、2020 一次次团灭的结构性原因:所有基于波动率的风控,在崩盘前都给它们开了绿灯。
四、尾部风险平价:把崩盘贡献迭代拉平到 25%#
目标明确了:不让任何资产主导组合的尾部风险。即让每个资产的 CVaR 贡献占比都等于 。CVaR 组合优化本可以用 Rockafellar-Uryasev 的线性规划精确求解,但为了让原理透明、代码自足,这里用一个迭代的投影梯度法——哪个资产尾部贡献超标就降它的权重,不足就升,反复迭代直到拉平:
def tail_risk_parity(R, alpha=0.05, iters=6000, lr=0.03):
n = R.shape[1]
w = np.ones(n) / n
for _ in range(iters):
rc = cvar_risk_contrib(w, R, alpha)
target = rc.mean() # 目标:贡献均等
grad = rc - target # 超标的往下调,不足的往上调
w = w - lr * grad
w = np.clip(w, 1e-4, None) # 只做多
w = w / w.sum() # 归一化
return w
w_trp = tail_risk_parity(rets)python收敛后,CVaR 贡献被精确拉平:

尾部风险平价把四个资产的 CVaR 贡献全部拉到 ~25%。代价体现在权重上:

短波动信用的权重从波动率平价的 17.7% 被砍到 11.5%,腾出来的仓位主要加到了尾部温和的债券上。这不是尾部风险平价『保守』,而是它终于看见了波动率平价看不见的东西——短波动信用那 39.2% 的隐藏尾部贡献。
五、回测:拉平尾部贡献,换来更平的净值#
在同一段含崩盘的市场上跑三个组合(等权 / 波动率平价 / 尾部风险平价):

| 策略 | 年化收益 | 年化波动 | Sharpe | 最大回撤 | 日 CVaR(5%) |
|---|---|---|---|---|---|
| 等权 | 0.4% | 8.9% | 0.05 | -22.8% | 1.37% |
| 波动率平价 | 2.1% | 6.4% | 0.34 | -14.6% | 0.96% |
| 尾部风险平价 | 3.3% | 5.9% | 0.57 | -12.2% | 0.83% |
尾部风险平价在所有维度上都赢了波动率平价:Sharpe 从 0.34 升到 0.57,最大回撤从 -14.6% 收到 -12.2%,组合 CVaR 从 0.96% 降到 0.83%。收益还更高——因为它在崩盘日少扛了短波动信用的巨亏,净值曲线的坑更浅,复利基数保得更好。
要诚实说明:这段样本里,正是因为短波动信用真的爆了几次,才让「提前减配它」显得英明。如果这段样本里它一次都没崩,波动率平价的高权重反而会赚更多——尾部风险平价买的是保险,保险在没出事时永远显得多余。它的价值不在提高平均收益,而在削掉左尾、让你活到下一轮。
六、结果解读与实战边界#
这个方法的真正价值。 它把风控的焦点从「日常波动」搬到「崩盘贡献」,专治波动率类风控对肥尾资产的系统性盲区。任何组合里若含有短波动、carry、信用、卖保护这类「负偏度收租」资产,波动率平价几乎必然低配了它们的真实风险——尾部风险平价是对这个盲区的直接修正。
四个必须知道的陷阱:
-
尾部估计的样本量诅咒。 CVaR 只用最坏 5% 的数据点。3000 天里,5% 尾部只有 150 个点;如果你想看 1% 尾部,就只剩 30 个点。尾部越极端,可用样本越少,估计越不稳。短波动信用那条 -10.2 偏度的尾巴,可能整个样本里就靠几十次崩盘定义——换个样本期,CVaR 估计可能大幅漂移。实战中必须配合参数化尾部模型(EVT 广义帕累托分布)或更长历史来稳住尾部估计,纯经验 CVaR 在小样本上会骗你。
-
样本内最优必然乐观。 上面的回测是样本内的:我用同一段数据既估 CVaR 又跑回测。真实的尾部风险平价必须样本外验证——用滚动窗口估 CVaR、下一期持有,再滚动。样本内 Sharpe 0.57 里,有一部分是「我知道短波动信用会崩所以提前减配」的后视镜红利,样本外会打折。
-
CVaR 分解依赖历史尾部会重演。 Euler 分解算的是「历史上组合落尾时谁亏得多」。但下一次崩盘的相关性结构可能和历史完全不同——2008 是信用崩、2020 是流动性崩、2022 是股债双杀。用历史尾部相关性配的权,在新型崩盘里可能失效。尾部相关性在危机中往往趋近 1(一起崩),这会让分散化承诺落空。
-
迭代解 vs 精确解。 本文的投影梯度法直观但不保证全局最优,且对学习率敏感。生产环境应该用 Rockafellar-Uryasev 的 CVaR 线性规划公式(把 CVaR 最小化转成可求解的 LP),既严格又快。这里用迭代法是为了让「拉平尾部贡献」这个机制看得见摸得着。
一句话总结:波动率平价问「谁平时抖得厉害」,尾部风险平价问「谁崩起来最要命」——后者才是真正会杀死你组合的问题。当你的资产篮子里有『平时收租、偶尔爆仓』的东西时,换尺子这件事,可能比调任何参数都值钱。