第 5 章 · 04 均值方差优化 本节摘要:本节讲现代投资组合理论的基石——马科维茨均值方差优化(Mean-Variance Optimization, MVO)。1952 年马科维茨证明:组合方差不仅取决于各资产方差,还取决于资产间的协方差,因此可以通过分散配置把组合风险降到加权平均以下。本节讲清三个核心:组合方差的数学形式 ω⊤Σω、有效前沿的求解(给定目标收益求最小方差,或最大化夏普)、以及 Markowitz 诅咒——协方差估计极不稳定导致权重不可靠。代码用 scipy.optimize 求解,并对照 PyPortfolioOpt 库的简化用法。 内容来源:原项目 ,汉化并套用体系化模板。
本节摘要:本节讲现代投资组合理论的基石——马科维茨均值方差优化(Mean-Variance Optimization, MVO)。1952 年马科维茨证明:组合方差不仅取决于各资产方差,还取决于资产间的协方差,因此可以通过分散配置把组合风险降到加权平均以下。本节讲清三个核心:组合方差的数学形式 ω⊤Σω、有效前沿的求解(给定目标收益求最小方差,或最大化夏普)、以及 Markowitz 诅咒——协方差估计极不稳定导致权重不可靠。代码用 scipy.optimize 求解,并对照 PyPortfolioOpt 库的简化用法。
内容来源:原项目
ch05/04_mean_variance_optimization.ipynb,汉化并套用体系化模板。
⚠️ 风险提示:均值方差优化对预期收益和协方差的估计极其敏感,「历史最优」组合往往是过拟合产物,实盘权重剧烈跳变。务必收缩估计或加稳健约束。
阅读完本节,你应当能够:
直觉上,把鸡蛋放在多个篮子里风险更小。马科维茨把这个直觉数学化了:组合收益是各资产收益的加权和,但组合方差不是各资产方差的加权和——它还含协方差项:
其中 ω 是权重向量,Σ 是协方差矩阵。展开看,组合方差由「自身方差项」和「交叉协方差项」组成。当资产间相关性 < 1 时,协方差项为负贡献,组合方差小于加权平均方差。这就是分散化的数学根源:相关越低,分散效果越好。
MVO 有两种等价的对偶形式:
实务中通常解第一个:扫一组目标收益,对每个解最小方差,得到一条「风险-收益」曲线,这就是有效前沿(efficient frontier)——前沿上的组合在任何风险水平下收益最高,或任何收益水平下风险最低。
import numpy as np from scipy.optimize import minimize periods_per_year = 52 # 周频 def portfolio_std(wt, rt=None, cov=None): """年化组合标准差""" return np.sqrt(wt @ cov @ wt * periods_per_year) def portfolio_returns(wt, rt, cov=None): """年化组合收益""" return (wt @ rt + 1) ** periods_per_year - 1 def portfolio_performance(wt, rt, cov): return portfolio_returns(wt, rt=rt), portfolio_std(wt, cov=cov)
最大化夏普 = 最小化负夏普,用 SLSQP 求解器,约束「权重绝对值之和 = 1」:
def neg_sharpe_ratio(weights, mean_ret, cov): r, sd = portfolio_performance(weights, mean_ret, cov) return -(r - rf_rate) / sd weight_constraint = {'type': 'eq', 'fun': lambda x: np.sum(np.abs(x)) - 1} def max_sharpe_ratio(mean_ret, cov, short=False): return minimize(fun=neg_sharpe_ratio, x0=x0, args=(mean_ret, cov), method='SLSQP', bounds=((-1 if short else 0, 1),) * n_assets, constraints=weight_constraint, options={'tol': 1e-10, 'maxiter': 10000})
bounds 控制能否做空:short=False 时权重 ∈ 0, 1,short=True 时 ∈ [-1, 1]。
扫一组目标收益,每个解一次最小方差:
def min_vol_target(mean_ret, cov, target, short=False): constraints = [{'type': 'eq', 'fun': lambda x: portfolio_returns(x, mean_ret) - target}, weight_constraint] bounds = ((-1 if short else 0, 1),) * n_assets return minimize(portfolio_std, x0=x0, args=(mean_ret, cov), method='SLSQP', bounds=bounds, constraints=constraints, options={'tol': 1e-10, 'maxiter': 1e4})
在求解前沿前,先用 Dirichlet 分布生成大量随机权重,画散点图直观感受风险-收益分布:
from numpy.random import dirichlet, choice def simulate_portfolios(mean_ret, cov, rf_rate, short=True): alpha = np.full(shape=n_assets, fill_value=0.05) weights = dirichlet(alpha=alpha, size=100000) # 10 万组随机权重 if short: weights *= choice([-1, 1], size=weights.shape) returns = (weights @ mean_ret.values + 1) ** periods_per_year - 1 std = (weights @ weekly_returns.T).std(1) * np.sqrt(periods_per_year) sharpe = (returns - rf_rate) / std return pd.DataFrame({'std': std, 'ret': returns, 'sharpe': sharpe}), weights
散点图上能清楚看到「最大夏普点」和「最小波动点」位于随机云的左上边界——它们就是有效前沿的端点。
⚠️ 关键陷阱:MVO 对输入(μ 和 Σ)极其敏感。预期收益 μ 极难准确估计,协方差 Σ 略好但仍不稳。当资产高度相关时,Σ 的条件数飙升,求逆数值不稳,权重会出现极端的正负跳变(例如某资产今天 +30%、明天 -25%)。相关性越高,分散越需要,但权重越不可靠——这就是「Markowitz 诅咒」。
| 输入 | 估计可靠性 | 对权重的影响 |
|---|---|---|
| 均值收益 μ | 极不可靠 | 主导权重方向,小扰动大跳变 |
| 协方差 Σ | 中等 | 高相关时数值病态 |
| 无风险利率 rf | 较可靠 | 仅影响夏普分母 |
实务对策:
PyPortfolioOpt 库把上述流程封装好,几行就能跑:
from pypfopt.efficient_frontier import EfficientFrontier from pypfopt import risk_models, expected_returns mu = expected_returns.mean_historical_return(prices, frequency=252) S = risk_models.sample_cov(prices, frequency=252) ef = EfficientFrontier(mu, S) weights = ef.max_sharpe() # 最大夏普组合 cleaned = ef.clean_weights() # 截断微小权重
risk_models 提供 Ledoit-Wolf 收缩、指数加权等多种协方差估计,expected_returns 提供 CAPM 收益率等。这套库与本系列另一份 PyPortfolioOpt 教程呼应,这里只是入门,深入用法见那篇。
下一节,我们看凯利公式——从胜率和赔率推导最优杠杆,理解「该用多大仓位」。