风险平价:你以为的「平均分」,其实一点都不平均
你有十万块,挑了五个标的,每个投两万。分得够均匀了吧。
然后你开始每天看账户。涨的时候,你发现整个账户的涨幅几乎和其中某一个的走势一模一样;跌的时候也是。剩下四个像是没买过——它们确实在动,只是动的幅度小到完全被那一个盖住了。
钱是平均分的,可账户的脾气,被其中一个说了算。
这件事不是你运气不好,它几乎必然会发生。而「风险平价」这四个字,说的就是有人认真对付过这个问题:你以为你在分配钱,实际上被分配掉的是风险,而这两样东西从来不是一回事。
先说人话:插排上的三样电器
家里一个插排上插了三样东西:一台空调、一盏台灯、一个手机充电器。有人问你这个插排的负载怎么分配,你说「三样电器,一样一件,挺均匀的」。
随便哪个电工听了都会笑。空调一台就可能顶掉整个插排的容量,台灯加充电器合起来还不到零头。按「件数」看是均匀的,按「瓦数」看极度不均——插排真跳闸的那一刻,九成九是空调干的,另外两样只是恰好插在同一个孔上,白背了个名分。
组合里的钱,就是件数;风险,就是瓦数。
每样投一样多钱,分的是件数。风险平价想分的是瓦数:让每样东西对「整个插排跳闸」这件事贡献一样多。
翻回投资的说法:不是每个资产买一样多钱,而是让每个资产贡献一样多的波动——波动就是「这东西平常上下晃多大」,晃得凶的少买点,晃得温吞的多买点,最后每个人在账户总晃动里占的份额差不多相等。
注意这里有一个偷偷发生的替换:把「风险」换成了「波动」。这个替换是整套思路的地基,也是它后面所有麻烦的源头。先记住这一笔,等会儿回来算账。
投资层:一个你能拿计算器核对的例子
术语讲一百遍,不如手算一遍。
假设两个资产。A 的年化波动是 20%(意思是它一年上下晃动的典型幅度大约二十个点),B 的年化波动是 5%。为了先把事情算清楚,我们假设两者之间毫无关系——A 涨的时候 B 涨还是跌完全看它自己心情。
先看等额分配。 各投一半,也就是各 0.5。
组合的波动怎么算?在两者互不相干的前提下,各自的「波动的平方」按权重的平方加起来:
- A 贡献:0.5² × 0.20² = 0.25 × 0.04 = 0.010
- B 贡献:0.5² × 0.05² = 0.25 × 0.0025 = 0.000625
- 合计:0.010625
A 占了 0.010 / 0.010625 ≈ 94%。
你分了一半一半的钱,A 却包办了组合九成四的波动。B 那两万块在账户的起伏里,几乎没有存在感。这就是开头那个场景的数学解释——它不是玄学,是一个平方关系带来的必然结果:波动在组合里的影响力,是按平方放大的,20 比 5 只是四倍差距,平方之后就是十六倍。
再看风险平价怎么分。 目标是让两边贡献相等。在互不相干这个简化前提下,答案有个特别干净的形式:按波动的倒数分配。
- A 分 1/20 = 0.05,B 分 1/5 = 0.20
- 归一化(让两个数加起来等于 1):A 得 0.05/0.25 = 0.2,B 得 0.20/0.25 = 0.8
验算一下:
- A 贡献:0.2² × 0.04 = 0.04 × 0.04 = 0.0016
- B 贡献:0.8² × 0.0025 = 0.64 × 0.0025 = 0.0016
一模一样。两边各占一半,成了。
看清楚这个结果意味着什么:为了让两个资产「平起平坐」,你得往那个不起眼的 B 上砸八成的钱,而那个看起来很有戏、天天有行情的 A,只配拿两成。风险平价在直觉上是反人性的——它逼你重仓最无聊的那个东西。很多人第一次看到这个权重表的反应是「这也太保守了吧」,然后手一抖就把 A 的比例调回去了,调回去的那一刻,风险平价就不存在了。
还要说清一件常被弄混的事:这套算法只回答「怎么分」,完全不回答「买不买」。它假设你已经选好了要持有哪几样东西,剩下的只是分配问题。选错了标的,风险平价只会帮你把错误分得更均匀一点。
系统层:在真实的组合优化器里长什么样
qlib 的组合优化模块把四种分配方式并列放在一个类里,摆在一起看特别清楚——它们的区别不在于谁更高级,而在于各自要求你事先知道多少东西。
| 方法 | 中文 | 你必须先提供 | 它在追求什么 |
|---|---|---|---|
inv |
波动率倒数 | 每个资产自己的波动 | 按波动倒数分,忽略资产之间的关系 |
gmv |
全局最小方差 | 完整的协方差矩阵 | 组合总波动最小,不管钱怎么分布 |
mvo |
均值方差 | 协方差矩阵 + 收益预测 | 收益与风险的加权取舍 |
rp |
风险平价 | 完整的协方差矩阵 | 每个资产的风险贡献相等 |
这里说的协方差矩阵,是一张记录「谁和谁一起动、各自动多大」的表:对角线上是每个资产自己的波动,对角线之外是两两之间的联动程度。它是这一整层的输入,也是这一整层的命门。
inv 的实现短得让人意外,核心只有三行:
vola = np.diag(S) ** 0.5 # 取协方差矩阵对角线,开方得到各自波动
w = 1 / vola # 波动的倒数
w /= w.sum() # 归一化,加起来等于 1
这正是我们刚才手算的那步。注意它只用了 np.diag(S)——矩阵对角线,也就是每个资产自己的波动,对角线之外那些「谁和谁一起动」的信息,它一个都没看。所以 inv 严格来说不是风险平价,它是风险平价在「资产之间互不相干」这个前提下的偷懒解。前提不成立的时候,两者会分道扬镳。
真正的 rp 得去解一个优化问题。它的目标函数是这样一句话:
def func(x):
N = len(x)
Sx = S @ x
xSx = x @ Sx
return np.sum((x - xSx / Sx / N) ** 2)
不用逐符号啃,读它在干什么就够了:算出每个资产当前的风险贡献,跟「总风险除以资产个数」这个理想值比一比,把所有偏差平方加起来,然后找一组权重让这个偏差总和最小。它不是一个公式,是一场搜索。 约束条件也很朴素:权重非负(不做空)、加起来等于 1(钱全投出去)。
Vibe-Trading 的实现补上了工程上最有意思的一环。它同样用数值求解,但求解之前先给了一个起点:
inv_vol = 1.0 / vols
seed = inv_vol / inv_vol.sum()
用波动倒数的结果当初始猜测,再让优化器从这里往真解走。这个安排本身就是一句注解:波动倒数是风险平价的近似,相关性是从近似走到精确的那段距离。 更值得看的是它的兜底——求解失败的时候,直接返回这个初始猜测,而不是抛错停机。生产环境里的优化器必须永远给得出一个答案,哪怕是个粗糙的。
还有一处守卫,藏着一个非常真实的坑:
if not np.isfinite(cov).all() or np.any(vols < 1e-12):
return self._equal_weight(n)
波动几乎为零就退回等权。为什么要特意防这个?因为分配公式里波动在分母上——一个长期停牌、刚上市、或者数据缺失导致「算出来几乎不动」的资产,会被算出一个趋近于无穷大的权重。 现实里它不是没风险,它是没数据。没有这道守卫,你的组合会一头扎进最不该重仓的地方,而且过程中不会报任何错。
它悄悄假设了什么
回到前面那笔账:把「风险」替换成了「波动」。这个替换带出三层假设,每一层都能单独把整套思路掀翻。
假设一:风险就是波动,而且波动是对称的。 涨 10% 和跌 10% 在这套算法眼里权重完全一样,因为它只看幅度不看方向。可你的真实感受、你的账户承受力、你被迫离场的那条线,从来只跟往下那一半有关。一个天天小涨小跌、偶尔一次深跌的资产,和一个每天上下大幅震荡但从不深跌的资产,波动数字可能差不多,你的实际处境天差地别。这层缺口,正是下行风险和 尾部风险 这两个概念各自要补的。
假设二:风险可以被估准。 协方差矩阵不是天上掉下来的,它是从一段历史里算出来的。Vibe-Trading 的优化器基类用一个滚动窗口来做这件事,回看长度是构造时传进去的参数(当前默认值 60 个交易日,可配置)——也就是说,明天的分配比例,取决于过去这一段时间的表现。窗口取短了,噪音大、权重乱跳;取长了,反应迟钝、危机来了还在用平静期的数字。没有一个「正确」的窗口长度,只有你选择在哪种错误上吃亏。
而且这个估计问题会随资产数量迅速恶化。资产两两之间的关系是组合数,十个资产要估五十几个数,三十个资产要估四百多个——你的历史数据长度往往撑不起这么多待估参数。qlib 专门开了一整个「风险模型」层来对付这件事,其中的压缩估计器做的事就是把样本算出来的协方差矩阵,往一个更简单、更稳定的假设矩阵上拉一把:
S_hat = (1 - alpha) * S + alpha * F
用一点点系统性偏差,换掉一大块估计噪音。当一件事需要专门建一层来救,说明它在实践中真的会出事。
假设三:估出来的东西在未来还成立。 这是最脆的一环。波动确实有点惯性——平静的日子倾向于跟着平静的日子,所以短期外推不算太离谱。但资产之间的相关性没有这种惯性,它在危机里会跳变。平时各走各路的东西,在恐慌抛售的那几天会一起跌,因为大家卖的不是这个资产,是所有能卖的东西。相关性一升,你精心算出来的分散效果当场蒸发。这一层展开在 相关性 里,也是 分散化的数学 那篇讲的东西在压力状态下的反面。
它什么时候翻车
低波动被当成了低风险。 算法只知道 B 晃得小,不知道 B 为什么晃得小。有些东西波动低是因为真的稳,有些是因为流动性差、没人交易,价格根本没被认真定过——后者一旦被认真定价,就是一次跳空。给这类资产分八成仓位,是在往一个看不见的坑里加码。
为了收益去加杠杆,然后杠杆遇上波动跳升。 风险平价的组合天然偏向低波动资产,整体收益也就跟着低。于是一个几乎必然出现的下一步是:借钱放大它。问题在于,杠杆放大的不只是收益,它把「波动跳升」这件事从「难受」变成了「出局」——被强制平仓的那一刻,你多正确都没有意义。这条线的数学在 仓位与破产风险 里说得很透,值得对着看一遍。
波动一变权重就得跟着变,换手成本会吃掉一切。 这是最容易被回测掩盖的一条。波动率天天在变,理论上你的权重也该天天跟着调,可每一次调整都要付出买卖的代价。Vibe-Trading 里有一个专门的换手感知优化器,做法是在优化目标上直接挂一个惩罚项,你想调仓可以,但要为「调多少」付出代价——目标里那个惩罚系数越大,优化器越倾向于按兵不动。它的文档里还特意提醒:这个系数是有量纲的,跟你输入数据的频率有关,换个频率就得重新拿捏。翻译成人话:「多久调一次、调多狠」不是个技术细节,它是这套方法能不能落地的关键,展开见 调仓频率。
回测里的窗口偷看了未来。 这是工程上最隐蔽的一处。计算今天权重用的历史窗口,能不能包含今天这根 K 线?Vibe-Trading 的基类在这里写得很死:只取严格早于决策时点的行,注释里也讲明了理由——交易在当根 K 线开盘时发生,而收盘价那时还不存在,把它算进去就是把未来的信息塞进了当下的权重。这类错误不会报错,只会让你的历史成绩变好看。
「风险贡献相等」不等于「风险变小了」。 风险平价重新分配的是风险的归属,不是风险的总量。一个各资产贡献均等、但每样都在下跌的组合,跌起来一点都不会手软。把它当成一种保护,是这个概念被误用得最多的方式——它是个分配规则,不是个防护罩。
失效边界与常见误用
把 inv 当成风险平价。 波动倒数只在资产间关系可以忽略时才等于风险平价。真实市场里同一板块的东西高度联动,用波动倒数分,会在不知不觉中把几个「其实是同一个东西」的资产各给一份,看着分散,实则重仓。
在两三个资产上用它。 资产太少的时候,估计误差占比高,优化出来的权重对输入的微小变化极其敏感——今天这个八成,明天可能变成六成。这类方法需要一定数量的资产才谈得上稳定。
用它给单一市场里的一篮子股票分配。 同一个市场的股票之间本来就高度联动,风险平价能压掉的只是其中很小一部分。它真正发挥作用的场景,是把性质差别很大的东西放在一起分配。至于「跟大盘一起动的那部分」到底占多少、能不能被分掉,那是 beta 与 alpha 的话题,不归这套方法管。
忘了它对输入的依赖是单向的。 这套方法自己不产生任何判断,它把协方差矩阵原样翻译成权重。输入的估计有多脏,输出的权重就有多脏,而输出看起来永远是一串工工整整的小数,没有任何地方会提示你「这次的输入不太可靠」。
在实盘里频繁地手动微调。 这套方法的价值来自纪律——它给出的权重经常反直觉,而你每一次「这个我觉得该多买点」的干预,都在把它拉回等额分配。要么信它,要么别用它,半信半疑地用是最差的一种。
小结
- 每样投一样多钱,分的是钱;风险平价想分的是风险——让每个资产对组合的总晃动贡献相等。
- 波动在组合里的影响力是按平方放大的:20% 波动和 5% 波动各投一半,前者能占掉九成四的风险。想让它俩平起平坐,得给低波动那个八成仓位——这套方法在直觉上是反人性的。
- 系统里它跟另外三种分配方式并列:波动倒数是它在「互不相干」前提下的简化解,最小方差不管分散只压总波动,均值方差还额外要你交出收益预测。风险平价的立足点在于它只用你相对估得准的那部分信息。
- 三层假设依次变脆:风险等于波动(忽略了下跌与上涨的不对称)→ 波动能被估准(协方差矩阵是估计量,资产一多就散架,所以才有专门的压缩估计层)→ 估出来的还能外推(波动尚有惯性,相关性在危机中会跳变)。
- 最常见的四种翻车:把低波动当低风险、为补收益而加杠杆、换手成本吃掉全部优势、回测窗口偷看了当天数据。
- 它是个分配规则,不是防护罩。风险贡献均等,跟组合会不会亏钱,是两个问题。
一句话记住这篇:别数你买了几样,看清楚哪一样在替你决定今天的心情。
本文所引源码均来自上述快照版本,是阅读代码得出的机制说明,不是运行结果;文中算例为方便手算取的整齐数字,只用于演示关系,不代表任何真实资产。回 量化与 AI 投研概念库 看其它概念各自在说什么;本站内容为投资教育,不构成任何投资建议,边界见 免责声明。
本文为投资教育整理,关键数据与结论请结合下列权威来源验证。