自1952年Harry Markowitz提出现代投资组合理论以来,均值-方差框架一直在投资组合构建中占据主导地位。然而,传统方差作为风险度量存在明显缺陷:它同等惩罚收益和损失,忽视极端尾部事件,且假设收益呈正态分布。本文将介绍如何使用Python实现更先进的风险度量方法,包括半方差(Semivariance)、条件风险价值(CVaR)和最大回撤(Maximum Drawdown),帮助构建能够有效抵御灾难性损失的投资组合。
传统方差的局限性
传统均值-方差优化存在四个核心问题:
-
对称性问题:方差同等对待上涨和下跌,10%的下跌和10%的上涨在计算中权重相同,这与投资者的实际风险感受不符
-
聚焦中间值:方差衡量的是围绕均值的离散程度,而投资者真正关心的是可能抹去多年收益的尾部风险
-
分布假设:方差假设收益呈正态分布,但真实市场收益具有肥尾特征和偏斜性
-
路径无关性:方差不关心收益的顺序,但稳步下跌和突然崩盘给投资者的体验截然不同
方法一:下行风险组合优化
下偏矩(Lower Partial Moment,LPM)是下行风险的广义形式,其数学定义为:
LPM_α = E[(τ - R)₊^α]
其中τ是灾难阈值,α是风险态度参数。当α=2时,即为半方差(Semivariance)。
import cvxpy as cp
import numpy as np
def optimize_semivariance_portfolio(returns, tau=0, alpha=2, lambda_risk=1.0):
"""
使用下行风险度量优化投资组合
参数说明:
returns: 历史收益率矩阵,形状为 (T, N),T为时间周期数,N为资产数量
tau: 灾难阈值(最低可接受收益率)
alpha: 风险态度参数(1=线性,2=二次,3=三次)
lambda_risk: 风险厌恶系数
返回:
最优投资组合权重
"""
T, N = returns.shape
mu = returns.mean(axis=0) # 计算预期收益率
# 定义决策变量
w = cp.Variable(N) # 投资组合权重
s = cp.Variable(T) # 下行偏差的辅助变量
# 目标函数:最大化收益 - λ * 下行风险
expected_return = mu @ w
downside_risk = (1/T) * cp.sum(cp.power(s, alpha))
objective = cp.Maximize(expected_return - lambda_risk * downside_risk)
# 约束条件
constraints = [
cp.sum(w) == 1, # 满仓约束
w >= 0, # 仅做多约束
s >= 0, # 非负偏差
s >= tau - returns @ w # 捕捉下行偏差
]
# 求解优化问题
problem = cp.Problem(objective, constraints)
problem.solve(solver=cp.ECOS)
return w.value
# 示例:模拟252个交易日、10种资产的收益率数据
np.random.seed(42)
returns = np.random.randn(252, 10) * 0.01 + 0.0003
# 使用不同的α值进行优化
weights_linear = optimize_semivariance_portfolio(returns, alpha=1) # 线性惩罚
weights_semi = optimize_semivariance_portfolio(returns, alpha=2) # 半方差
weights_cubic = optimize_semivariance_portfolio(returns, alpha=3) # 三次惩罚
print("半方差优化权重:")
print(np.round(weights_semi, 4))
方法二:尾部风险组合优化(CVaR)
条件风险价值(CVaR)也称为预期损失(Expected Shortfall),它衡量的是超过VaR阈值后的平均损失。CVaR的优势在于它是凸函数,可以通过优化算法高效求解。
def optimize_cvar_portfolio(returns, alpha=0.95, lambda_risk=1.0):
"""
使用条件风险价值(CVaR)优化投资组合
CVaR表示在最差的(1-α)%情况下的平均损失
例如α=0.95时,我们最小化最差5%情景下的平均损失
参数说明:
returns: 历史收益率矩阵,形状为 (T, N)
alpha: 置信水平(常用0.90、0.95或0.99)
lambda_risk: 风险厌恶系数
返回:
最优权重、VaR值、CVaR值
"""
T, N = returns.shape
mu = returns.mean(axis=0)
# 决策变量
w = cp.Variable(N) # 投资组合权重
tau = cp.Variable() # VaR阈值
u = cp.Variable(T) # 超额损失变量
# CVaR目标函数
expected_return = mu @ w
cvar = tau + (1/(1-alpha)) * (1/T) * cp.sum(u)
objective = cp.Maximize(expected_return - lambda_risk * cvar)
# 约束条件
constraints = [
cp.sum(w) == 1, # 预算约束
w >= 0, # 仅做多
u >= 0, # 非负超额
u >= -returns @ w - tau # 捕捉超过VaR的损失
]
# 求解
problem = cp.Problem(objective, constraints)
problem.solve(solver=cp.ECOS)
# 提取结果
optimal_w = w.value
optimal_tau = tau.value
optimal_cvar = tau.value + (1/(1-alpha)) * (1/T) * np.sum(u.value)
return optimal_w, optimal_tau, optimal_cvar
# 示例:比较不同置信水平
np.random.seed(42)
returns = np.random.randn(252, 20) * 0.015 + 0.0004
w_90, var_90, cvar_90 = optimize_cvar_portfolio(returns, alpha=0.90)
w_95, var_95, cvar_95 = optimize_cvar_portfolio(returns, alpha=0.95)
w_99, var_99, cvar_99 = optimize_cvar_portfolio(returns, alpha=0.99)
print(f"CVaR 90%: {cvar_90:.4f}")
print(f"CVaR 95%: {cvar_95:.4f}") # 最常用的置信水平
print(f"CVaR 99%: {cvar_99:.4f}")
方法三:回撤风险组合优化
回撤(Drawdown)捕捉的是投资者真正经历的痛苦:看着财富从峰值下跌。一个关键的心理洞察是:50%的损失需要100%的收益才能恢复。
def optimize_cdar_portfolio(returns, alpha=0.95, lambda_risk=1.0):
"""
最小化条件回撤风险(CDaR)
CDaR是最差回撤的平均值,比最大回撤更稳定
因为它考虑多个糟糕情景,而不仅仅是单一最差情况
参数说明:
returns: 历史收益率矩阵
alpha: 置信水平
lambda_risk: 风险厌恶系数
"""
T, N = returns.shape
mu = returns.mean(axis=0)
cum_returns = np.cumsum(returns, axis=0) # 计算累计收益
# 决策变量
w = cp.Variable(N) # 投资组合权重
u = cp.Variable(T) # 历史最高水位标记
tau = cp.Variable() # 回撤VaR
z = cp.Variable(T) # 超额回撤
# CDaR目标函数
expected_return = mu @ w
cdar = tau + (1/(1-alpha)) * (1/T) * cp.sum(z)
objective = cp.Maximize(expected_return - lambda_risk * cdar)
# 约束条件
constraints = [
cp.sum(w) == 1,
w >= 0,
z >= 0,
]
# 回撤计算约束
for t in range(T):
drawdown_t = u[t] - cum_returns[t, :] @ w
constraints.append(u[t] >= cum_returns[t, :] @ w)
if t > 0:
constraints.append(u[t] >= u[t-1]) # 单调递增
constraints.append(z[t] >= drawdown_t - tau)
problem = cp.Problem(objective, constraints)
problem.solve(solver=cp.ECOS)
return w.value, tau.value
# 示例
np.random.seed(42)
returns = np.random.randn(252, 15) * 0.012 + 0.0003
w_cdar, dd_var = optimize_cdar_portfolio(returns, alpha=0.95, lambda_risk=5.0)
print(f"CDaR 95%组合的回撤VaR: {dd_var:.2%}")
print("优化权重:")
print(np.round(w_cdar, 4))
实测对比与选择建议
根据对S&P 500成分股的回测结果(2015-2020年,每月再平衡,1年回望期),不同风险度量的表现如下:
- 半方差(α=2)组合表现最佳,夏普比率达到1.24,同时最大回撤比传统均值-方差组合降低了近10%
- 熵风险价值(EVaR)在相同置信水平下持续优于CVaR
- 极端度量如最大回撤组合表现较差,因为它们由单个数据点决定,导致不稳定
对于量化交易实践,建议采用以下策略:
- 将半方差作为均值-方差的默认升级方案
- 在核心配置之上叠加CVaR或EVaR作为尾部风险覆盖层
- 即使回撤不在优化目标中,也应将其作为健康指标进行监控
选择风险度量时可参考以下决策流程:
- 如果收益分布对称,使用均值-方差即可
- 如果主要担心尾部风险,选择CVaR或EVaR
- 如果需要每日监控投资组合,选择CDaR
- 其他情况下,半方差是最稳妥的选择
总结
均值-方差优化仍是有用的基准方法,但在肥尾、结构性断裂和杠杆约束主导的市场中,它已不再足够。替代风险度量让你能够表达真正关心的内容:避免深度水下期、在尾部事件中存活、以及提供投资者能够坚持的更平滑的权益曲线。
本文介绍的三类方法——下行风险(半方差)、尾部风险(CVaR/EVaR)和路径相关风险(回撤)——都可以通过凸优化高效求解。如果你已经熟悉凸优化,现在就拥有了替换方差作为默认风险度量的所有要素,将风险管理从事后补救变成首要的设计选择。