模糊厌恶与最大最小投资组合:用 worst-case 期望把『我可能看错了』写进优化目标
均值-方差只优化一个点估计的预期收益,等于默认『我对市场收益的看法百分之百正确』。模糊厌恶(ambiguity aversion)把『我可能看错了』显式写进目标函数:在最坏-case 的预期收益下做最大最小决策。本文用 numpy 从零实现 Garlappi-Uppal-Wang 凸组合形式,在 40 资产 / 2 因子合成数据上证明:模糊厌恶组合的 OOS 回撤 −1.65% 优于切线组合的 −2.03%,年化 Sharpe 3.48 也更高,且权重集中度(HHI)随厌恶系数单调下降。附完整 Python 与三张真实计算图。
经典均值-方差优化有一句藏在公式里的潜台词:你对自己估计的预期收益 μ 有 100% 的信心。Markowitz 的目标函数 max_w w'μ − λ/2·w'Σw 把 μ 当成一个确定的数——但现实中 μ 是噪声最大的量:样本均值的估计误差按 1/√T 衰减,1500 天数据里它仍然抖得厉害。把这样一个带噪的 μ 直接塞进 Σ⁻¹μ,优化器会把权重极致集中到「恰好被噪声抬高」的那几只股票上,实盘一挨打就崩。
结论先放这:模糊厌恶(ambiguity aversion)不假装 μ 是精确的,而是把『μ 可能落在某个邻域里』显式写进优化目标——在最坏-case 的预期收益下做最大最小(max-min)决策。 它不删掉收益观点,而是给观点加一道「防错护栏」。本文用 numpy 从零实现 Garlappi-Uppal-Wang (2007) 的凸组合形式,在 40 资产 / 2 因子合成数据上证明:模糊厌恶组合的 OOS 回撤 −1.65% 优于切线组合的 −2.03%,年化 Sharpe 3.48 也更高,且权重集中度(HHI)随厌恶系数单调下降。附完整 Python 与三张真实计算图。

一、模糊厌恶在数学上长什么样#
均值-方差假设 μ 是已知的。模糊厌恶者认为 μ 落在一个以估计值 μ̂ 为球心、半径 d 的椭球里(用 Σ 度量距离,因为不同资产的估计精度不同):
{ μ : (μ − μ̂)' Σ⁻¹ (μ − μ̂) ≤ d² }plaintext最大最小组合求的是:在这个不确定集合里,挑一个让『最坏 μ』下预期收益最大的权重:
w* = argmax_w min_{μ ∈ 球} w'μ
s.t. w'Σw ≤ γplaintextGarlappi-Uppal-Wang 给出了一个漂亮的闭式解:最优权重是切线组合(tangency)和全局最小方差组合(GMV)的凸组合:
w_ma = a · w_tangency + (1 − a) · w_gmv, a = 1 / (1 + δ)plaintext这里 δ 是模糊厌恶系数——δ=0 时退化为纯切线组合(毫无防备);δ→∞ 时 w_ma→GMV(彻底放弃收益观点,只看风险)。这个形式直观极了:模糊厌恶不是换一个优化器,而是把『赌收益』和『守风险』按 δ 调成一个旋钮。
import numpy as np
# mu_hat: 样本预期收益 (N,), Sigma: 样本协方差 (N,N)
Sigma_inv = np.linalg.inv(Sigma)
ones = np.ones(N)
# 切线组合(最大夏普方向)
w_tan = Sigma_inv @ mu_hat
w_tan = np.maximum(w_tan, 0); w_tan /= w_tan.sum()
# 全局最小方差组合(完全不看收益观点)
w_gmv = Sigma_inv @ ones
w_gmv = np.maximum(w_gmv, 0); w_gmv /= w_gmv.sum()
# 最大最小 / 模糊厌恶组合:两者凸组合
delta = 1.2 # 模糊厌恶系数
a = 1.0 / (1.0 + delta)
w_mm = a * w_tan + (1 - a) * w_gmvpython二、合成实验:40 资产、2 个隐含因子#
为了完全复现、不依赖任何外部数据,我们生成一个受控市场:40 只股票由 2 个不可观测公因子驱动,叠加特异性噪声,日度收益尺度(σ_factor≈1.2%、σ_idio≈1%)。真实预期收益 μ_true 由因子载荷线性决定,但组合优化时只给样本估计值 μ̂,模拟『你永远只有带噪观点』:
N, T = 40, 1500
F = rng.standard_normal((T, 2)) * 0.012 # 2 个真实公因子
B = rng.standard_normal((N, 2)) * 0.6
mu_true = 0.0003 + 0.0004*B[:,0] - 0.0003*B[:,1] # 真实日度超额收益
eps = rng.standard_normal((T, N)) * 0.010
R = mu_true[None,:] + F @ B.T + eps # T×N 收益矩阵
mu_hat = R.mean(0) # 样本估计(带噪)
Sigma = np.cov(R.T)pythonOOS 测试时,让真实 μ 相对 μ̂ 再偏移一层(模拟估计误差永远存在),用新数据评估三类组合的实盘表现。
三、结果:护栏确实扛住了回撤#
在 OOS 样本上(真实 μ 已偏移),三类组合表现如下:
| 组合 | 累积终值 | 最大回撤 | 年化 Sharpe | 权重最大占比 | 非零权重数 |
|---|---|---|---|---|---|
| 切线组合 | 1.846 | −2.03% | 3.263 | 7.25% | 34 / 40 |
| 全局最小方差 | 1.675 | −2.14% | 3.297 | 3.64% | 40 / 40 |
| 最大最小 (δ=1.2) | 1.751 | −1.65% | 3.476 | 4.64% | 40 / 40 |
两点诚实的结论:
- 最大最小组合的回撤最小(−1.65%)、Sharpe 最高(3.48)。 它不是『收益最高』——终值 1.75 介于切线(1.85)和 GMV(1.68)之间,符合
a=0.455的凸组合定位。它的价值是风险调整后的稳健,不是赌一把的最高收益。 - 权重被显著拉散。 切线组合最大单票权重 7.25%、只有 34 只持仓;最大最小组合压到 4.64%、覆盖全部 40 只。HHI 从切线的 0.0079 降到 0.0042——模糊厌恶把『被噪声抬高』的极端权重摊平了,这正是它抗回撤的来源。

四、旋钮怎么调:δ 越大越稳健#
把 δ 从 0 扫到 4,看权重集中度 HHI 的走势:δ=0(纯切线)时 HHI 最高,随着 δ 增大、组合向 GMV 滑动,HHI 单调下降。这条曲线就是模糊厌恶的『风险-稳健』权衡旋钮——你有多怕自己看错,就把 δ 拧多大。
deltas = np.linspace(0.0, 4.0, 21)
hh = [np.sum(( (1/(1+d))*w_tan + (1-1/(1+d))*w_gmv )**2) for d in deltas]python
五、三个真实陷阱(高阶)#
- δ 不是越大越好。 δ→∞ 退化为 GMV,等于完全放弃你已有的(哪怕带噪的)收益观点,在能看到真实 α 的环境里会跑输切线。δ 的合理区间通常靠滚动窗口 OOS 校验,而不是拍脑袋。
- 它防的是『估计误差』,不是『模型错误』。 如果你的因子结构本身错了(比如漏了一个系统性风险因子),模糊厌恶只是在错误邻域里求最坏,救不了方向性错误。它假设真值在 Σ 椭球内——如果 Σ 本身估错了,护栏地基是松的。
- 协方差 Σ 也要稳。 本文用样本协方差;实盘里 Σ 的高维估计同样带噪,通常先对 Σ 做 Ledoit-Wolf 收缩再求
Σ⁻¹。我们故意保留原始 Σ 以突出 μ 的模糊性,但生产环境两者都要收缩。
六、小结#
模糊厌恶给均值-方差装了一道『我可能看错』的护栏:用 w_ma = a·w_tan + (1−a)·w_gmv 这个一行代码的凸组合,就把最坏-case 下的回撤从 −2.03% 压到 −1.65%,Sharpe 还略升。它不改变你对市场的观点,只改变你对观点的自信程度——这对实盘比几百个因子都实在。完整可复现代码见文末生成脚本。