组合优化入门:均值方差与风险平价
本课目标:理解均值方差框架的直觉与它在实践中的脆弱性,认识风险平价的思想,并学会给优化问题加上实盘必需的约束。
上一课我们用分位数等权把因子分变成了持仓。等权简单可靠,但它对风险不闻不问:两只总分一样的股票,一只波动 15%、一只波动 60%,等权买入意味着组合风险被高波动的那只主导。组合优化要解决的就是”在预期收益和风险之间做系统性权衡”。本文不构成投资建议。
均值方差:优雅的框架
均值方差框架的直觉一句话就能说完:在给定风险水平下最大化预期收益,或在给定预期收益下最小化风险。它需要两个输入——每只股票的预期收益向量 μ,和股票间的协方差矩阵 Σ;输出是一组最优权重 w。目标函数常写成:
最大化 wᵀμ − (λ/2)·wᵀΣw,其中 λ 是风险厌恶系数。
这个框架的漂亮之处在于它把”分散化”数学化了:只要资产不完全相关,组合波动就小于成分波动的加权平均——这是组合管理里少有的近似免费的午餐。它还给出了一个重要视角:一只股票值不值得加仓,不取决于它自身的风险收益,而取决于它对组合整体的边际贡献。在多因子语境下,μ 通常直接用上一课合成的因子分(经过缩放)充当。
输入敏感:优雅框架的阿喀琉斯之踵
均值方差在教科书里完美,在实践中却出名地难用,核心原因是对输入误差极度敏感:
- 预期收益 μ 的微小扰动,会导致最优权重的剧烈翻转。优化器像一台”误差放大器”——它会最大化地押注那些被你高估了预期收益的资产;
- 输出的组合经常极端:满仓押少数几只股票、大量权重贴在约束边界上;
- 每期输入更新后权重大幅跳动,带来不可接受的换手。
而 μ 恰恰是我们估得最不准的东西——因子对收益的预测本来就只有微弱的相关性。所以实务共识是:对 μ 的精度保持悲观,用约束和正则化驯服优化器,而不是指望喂给它精确的预期收益。常见的驯服手段包括:只用 μ 的排序信息而弱化数值大小、把权重向基准或等权收缩、给目标函数加上惩罚换手的项。这些手段的共同精神是一句话——当输入不可靠时,宁可要一个”大致正确”的钝解,也不要一个”精确错误”的锐解。
协方差估计同样困难
Σ 看起来比 μ 好估,其实也有自己的麻烦:
- 维度灾难:N 只股票的协方差矩阵有 N(N+1)/2 个参数。500 只股票就有超过 12 万个参数,而一年只有约 240 个交易日的数据,样本协方差矩阵噪声极大甚至不可逆;
- 时变性:相关性结构在危机中会集体抬升,“平时分散、跌时抱团”;
- 常见对策:压缩估计(把样本协方差向结构化目标收缩)、因子风险模型(假设股票收益由少数风格/行业因子驱动,把估计对象从 12 万个参数降到几十个因子的协方差)、以及拉长半衰期的指数加权估计。
进阶阶段不必自己实现因子风险模型,但要理解一点:直接把样本协方差矩阵塞进优化器,是新手最常见的翻车方式之一。
风险平价:不猜收益,只配风险
既然 μ 最不可靠,一个激进的思路是干脆不用它。风险平价(risk parity)的思想是:让每个资产(或每类资产)对组合总风险的贡献相等。
对比三种配置逻辑:
| 方案 | 均衡的对象 | 需要的输入 |
|---|---|---|
| 等权 | 资金 | 无 |
| 风险平价 | 风险贡献 | 协方差(主要是波动率) |
| 均值方差 | 边际收益/边际风险 | 预期收益 + 协方差 |
最简化的版本是波动率倒数加权(忽略相关性):
vol = ret.rolling(60).std() * np.sqrt(250) # 年化波动raw_w = 1.0 / vol.iloc[-1]weights = raw_w / raw_w.sum() # 归一化完整的风险平价需要数值求解,使每只资产的风险贡献 wᵢ·(Σw)ᵢ 相等。风险平价常用于大类资产配置(股、债、商品),在股票组合内部则常以”波动率倒数”或”风险预算”的简化形态出现。它的代价也很直白:完全放弃了收益观点,低波动资产会拿到很高的权重。
在多因子选股的语境下,一个折中方案很自然:用因子分决定”选哪些股票”,用风险平价或波动率倒数决定”每只买多少”。选股环节保留收益观点,配权环节只做风险均衡——这比把因子分直接塞进均值方差优化器要稳健得多,实现成本也低,很适合作为进阶阶段的默认方案。
实盘必需的约束
无论用哪个框架,裸的优化解都不能直接交易。常见约束及其动机:
- 个股上限(如单票不超过 5%):防止优化器把误差押成重仓,控制个股黑天鹅暴露;
- 行业上限/行业偏离约束(如相对基准行业偏离不超过 ±3%):把收益来源约束在选股而非行业押注上;
- 换手约束(如单次调仓换手不超过 20%):直接控制交易成本,也间接抑制了权重对输入噪声的敏感;
- 禁买/剔除清单:停牌、ST、流动性不足的股票直接排除,避免优化解落在无法执行的标的上。
带约束的优化本质是一个二次规划问题,Python 生态里可用 scipy.optimize.minimize 或凸优化库求解。示意(最小化跟踪风险的骨架):
from scipy.optimize import minimize
def objective(w, mu, cov, lam=10.0): return -(w @ mu) + 0.5 * lam * w @ cov @ w
cons = [{"type": "eq", "fun": lambda w: w.sum() - 1.0}]bounds = [(0.0, 0.05)] * n_assets # 不做空,单票上限 5%res = minimize(objective, x0, args=(mu, cov), bounds=bounds, constraints=cons)一个务实的经验:约束收紧到一定程度后,不同优化方法的结果会趋同——约束本身就是最强的正则化。先把约束设计好,再谈优化器的高级技巧。
最后别忘了评价基准:任何优化组合都应与等权组合放在同一回测框架下对比,看年化波动、最大回撤、换手率和成本后收益。如果精心调校的优化器在成本后跑不过等权,说明当前输入质量还撑不起这套复杂度,退回简单方案不丢人。
本课小结
- 均值方差框架直觉优雅,但对预期收益输入极度敏感,优化器天然是”误差放大器”;
- 协方差矩阵参数量巨大,直接用样本协方差是常见翻车点,压缩估计与因子风险模型是主流对策;
- 风险平价放弃收益预测、只均衡风险贡献,是对输入不确定性的一种务实回应;
- 个股、行业、换手三类约束是实盘组合的标配,约束本身就是最有效的正则化;
- 复杂优化应先与等权、波动率倒数加权这类朴素基准对比,胜出了才有存在的理由。
动手练习:取上一课多因子组合的前 50 只股票,分别用等权和 60 日波动率倒数加权构建两个组合,按月调仓回测,对比两者的年化波动、最大回撤与换手率,体会”只动风险、不动收益观点”能带来多大差异。