引言:现代投资组合理论的基石
资产配置是投资管理中最核心的决策环节,它决定了投资组合90%以上的收益表现。在众多资产配置模型中,均值方差模型(Mean-Variance Model) 由诺贝尔经济学奖得主哈里·马科维茨(Harry Markowitz)于1952年提出,是现代投资组合理论(Modern Portfolio Theory, MPT)的基石。该模型通过量化资产的预期收益与风险,为投资者提供了科学构建最优投资组合的数学框架。
均值方差模型的核心思想是:投资者不应仅关注单个资产的预期收益,而应关注整个投资组合的收益与风险特征。通过分散化投资,可以在不降低预期收益的前提下降低风险,或在相同风险水平下提升收益。本文将从数学推导、实战计算、Python代码实现和实际应用四个维度,深入解析均值方差模型的完整应用流程。
一、均值方差模型的数学基础与推导
1.1 核心假设与变量定义
均值方差模型建立在以下关键假设之上:
- 投资者是风险厌恶的,追求效用最大化
- 抄资者只关心资产的预期收益和风险(方差)
- 市场是完全的,无摩擦成本
- 资产收益服从正态分布
关键变量定义:
- \(n\):资产数量
- \(\mu = [\mu_1, \μ_2, ..., \mu_n]^T\):预期收益向量
- \(\Sigma\):协方差矩阵(反映资产间风险相关性)
- \(w = [w_1, w_2, ..., w_n]^T\):权重向量,满足 \(\sum w_i = 1\) 且 \(w_i \geq 0\)(不允许卖空)
1.2 组合收益与风险的数学表达
组合预期收益: $\( E(R_p) = \sum_{i=1}^n w_i \mu_i = w^T \mu \)$
组合方差(风险): $\( Var(R_p) = \sum_{i=1}^n \sum_{j=1}^n w_i w_j \sigma_{ij} = w^T \Sigma w \)$
其中 \(\sigma_{ij}\) 是资产 \(i\) 和资产 \(j\) 的协方差,\(\sigma_{ii} = \sigma_i^2\) 是资产 \(i\) 的方差。
1.3 最优化问题构建
均值方差模型的目标是寻找最优权重 \(w^*\),使得在给定预期收益水平下风险最小化,或在给定风险水平下收益最大化。这转化为一个二次规划问题:
目标函数: $\( \min_{w} w^T \Sigma w \quad \text{subject to} \quad w^T \mu = \mu_p, \quad w^T \mathbf{1} = 1 \)$
其中 \(\mu_p\) 是目标预期收益,\(\mathbf{1}\) 是全1向量。
1.4 拉格朗日乘数法求解
引入拉格朗日乘子 \(\lambda\) 和 \(\gamma\),构建拉格朗日函数: $\( L(w, \lambda, \gamma) = w^T \Sigma w + \lambda(\mu_p - w^T \mu) + \gamma(1 - w^T \mathbf{1}) \)$
对 \(w\) 求导并令导数为0: $\( \frac{\partial L}{\partial w} = 2\Sigma w - \lambda \mu - \gamma \mathbf{1} = 0 \)$
解得: $\( w = \frac{1}{2} \Sigma^{-1} (\lambda \mu + \gamma \math1) \)$
通过约束条件 \(\mu_p = w^T \mu\) 和 \(1 = w^T \mathbf{1}\) 可解出 \(\lambda\) 和 \(\gamma\),最终得到最优权重表达式: $\( w^* = \Sigma^{-1} \begin{bmatrix} \mu & \math1 \end{bmatrix} \begin{bmatrix} \mu^T \Sigma^{-1} \mu & \mu^T \Sigma^{-1} \math1 \\ \math1^T \Sigma^{-1} \mu & \math1^T \Sigma^{-1} \math1 \end{**bmatrix}^{-1} \begin{bmatrix} \mu_p \\ 1 \end{bmatrix} \)$
2. 实战案例:构建股票-债券投资组合
2.1 数据准备与预处理
假设我们有3类资产:美国大盘股(SPY)、美国小盘股(IWM)和美国国债(TLT)。我们获取2020-22023年的日度价格数据,计算预期收益和协方差矩阵。
数据预处理步骤:
- 获取价格数据
- 计算日度对数收益率:\(r_t = \ln(P_t/P_{t-1})\)
- 年化处理:预期收益 \(\mu = \text{mean}(r) \times 252\),协方差 \(\Sigma = \text{cov}(r) \times 252\)
2.2 Python代码实现:数据获取与计算
import numpy as np
import pandas as pd
import yfinance as yf
from scipy.optimize import minimize
import matplotlib.pyplot as plt
# 1. 数据获取
tickers = ['SPY', 'IWM', 'TLT']
start_date = '2020-01-01'
end_date = '2023-12-31'
# 下载价格数据
prices = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
returns = np.log(prices / prices.shift(1)).dropna()
# 2. 计算年化预期收益和协方差矩阵
mu = returns.mean() * 252 # 年化预期收益
cov = returns.cov() * 252 # 年化协方差矩阵
print("年化预期收益:\n", mu)
print("\n年化协方差矩阵:\n", cov)
代码说明:
- 使用
yfinance库获取股票和债券的调整后价格 - 计算对数收益率并年化处理(252个交易日)
mu和cov是模型的核心输入参数
2.3 最优化求解:构建有效前沿
有效前沿(Efficient Frontier)是所有最优投资组合的集合,即在给定风险水平下提供最大收益的组合。我们通过遍历目标收益范围来计算有效前沿:
# 3. 定义投资组合统计函数
def portfolio_stats(w, mu, cov):
"""计算组合收益、风险和夏普比率"""
port_return = w @ mu
port_vol = np.sqrt(w @ cov @ w)
sharpe = port_return / port_vol
return port_return, port_vol, sharpe
# 4. 定义最优化函数
def optimize_portfolio(target_return, mu, cov):
"""在给定目标收益下最小化风险"""
n = len(mu)
# 目标函数:最小化方差
def objective(w):
return w @ cov @ w
# 约束条件
constraints = (
{'type': 'eq', 'fun': lambda w: w @ mu - target_return}, # 收益约束
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}, # 权重和为1
{'type': 'ineq', 'fun': lambda w: w}, # 权重非负(不允许卖空)
)
# 初始猜测
w0 = np.ones(n) / n
# 求解
result = minimize(objective, w0, method='SLSQP', constraints=constraints)
return result.x if result.success else None
# 5. 计算有效前沿
target_returns = np.linspace(mu.min(), mu.max(), 50)
efficient_weights = []
efficient_volatilities = []
efficient_returns = []
for r in target_returns:
w_opt = optimize_portfolio(r, mu, cov)
if w_opt is not:
vol = np.sqrt(w_opt @ cov @ w_opt)
efficient_weights.append(w_opt)
efficient_volatilities.append(vol)
efficient_returns.append(r)
# 6. 计算全局最小方差组合(GMV)
def global_min_variance(cov):
"""计算全局最小方差组合"""
n = cov.shape[0]
def objective(w):
return w @ cov @ w
constraints = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1})
w0 = np.ones(n) / n
result = minimize(objective, w0, method='SLSQP', constraints=constraints)
return result.x
w_gmv = global_min_variance(cov)
gmv_return, gmv_vol, _ = portfolio_stats(w_gmv, mu, cov)
代码解析:
portfolio_stats函数计算组合的三个核心指标optimize_portfolio使用scipy.optimize.minimize进行二次规划求解- 通过遍历目标收益范围构建有效前沿
- 全局最小方差组合是有效前沿的左端点(风险最小点)
2.4 可视化有效前沿与最优组合
# 7. 可视化
plt.figure(figsize=(12, 8))
# 绘制有效前沿
plt.plot(efficient_volatilities, efficient_returns, 'b-', linewidth=2, label='Efficient Frontier')
# 绘制全局最小方差组合
plt.scatter(gmv_vol, gmv_return, color='red', s=100, marker='*', label='Global Min Variance')
# 绘制单个资产
for i, ticker in enumerate(tickers):
vol = np.sqrt(cov.iloc[i, i])
plt.scatter(vol, mu[i], s=100, label=ticker)
# 绘制资本市场线(CML)- 假设无风险利率为2%
rf_rate = 0.02
# 找到切线组合(最大夏普比率)
sharpe_ratios = (np.array(efficient_returns) - rf_rate) / np.array(efficient_volatilities)
max_sharpe_idx = np.argmax(sharpe_ratios)
max_sharpe_vol = efficient_volatilities[max_sharpe_idx]
max_sharpe_return = efficient_returns[max_sharpe_idx]
# 绘制CML
cml_x = [0, max_sharpe_vol * 2]
cml_y = [rf_rate, max_sharpe_return + (max_sharpe_return - rf_rate) * (cml_x[1] / max_sharpe_vol - 1)]
plt.plot(cml_x, cml_y, 'g--', linewidth=2, label='Capital Market Line')
plt.xlabel('Volatility (Standard Deviation)')
plt_ylabel('Expected Return')
plt.title('Efficient Frontier with Capital Market Line')
plt.legend()
plt.grid(True)
plt.show()
# 8. 输出最优组合权重
print("\n全局最小方差组合权重:")
for t, w in zip(tickers, w_gmv):
print(f"{t}: {w:.2%}")
print("\n最大夏普比率组合权重:")
w_max_sharpe = efficient_weights[max_sharpe_idx]
for t, w in zip(tickers, w_max_sharpe):
print(f"{t}: {w:.2%}")
可视化解读:
- 蓝色曲线:有效前沿,所有最优组合的集合
- 红色星号:全局最小方差组合(风险最小点)
- 绿色虚线:资本市场线(CML),引入无风险资产后的最优组合边界
- 单个资产点:显示分散化投资的优势(组合点位于资产点左侧,表示风险更低)
3. 实战扩展:带约束条件的均值方差模型
3.1 实际投资中的常见约束
真实投资场景中,纯数学最优解往往不符合实际要求,需要添加以下约束:
- 行业/资产类别上限:单一资产不超过组合的30%
- 禁止卖空:权重必须非负
- 最小持仓:任何资产权重不低于5%
- 换手率限制:限制调仓频率
- 跟踪误差约束:相对于基准的偏离度限制
3.2 带约束的Python实现
def constrained_optimization(mu, cov, constraints_config):
"""
带约束的均值方差优化
参数:
mu: 预期收益向量
cov: 协方差矩阵
constraints_config: 约束配置字典
"""
n = len(mu)
# 目标函数:最小化方差
def objective(w):
return w @ cov @ w
# 基础约束
constraints = [
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}, # 权重和为1
{'type': 'ineq', 'fun': lambda w: w}, # 非负约束
]
# 自定义约束
if 'max_weight' in constraints_config:
max_w = constraints_config['max_weight']
constraints.append({'type': 'ineq', 'fun': lambda w: max_w - w})
if 'min_weight' in constraints_config:
min_w = constraints_config['min_weight']
constraints.append({'type': 'ineq', 'fun': lambda w: w - min_w})
if 'target_return' in constraints_config:
target_r = constraints_config['target_return']
constraints.append({'type': 'eq', 'fun': lambda w: w @ mu - target_r})
# 求解
w0 = np.ones(n) / n
result = minimize(objective, w0, method='SLSQP', constraints=constraints)
if result.success:
return result.x
else:
raise ValueError("优化失败,请检查约束条件是否可行")
# 使用示例
constraints = {
'max_weight': 0.30, # 单一资产不超过30%
'min_weight': 0.05, # 最小持仓5%
'target_return': 0.10 # 目标收益10%
}
w_constrained = constrained_optimization(mu, cov, constraints)
print("\n带约束的最优权重:")
for t, w in zip(tickers, w_constrained):
print(f"{t}: {w:.2%}")
约束条件说明:
max_weight: 防止过度集中风险min_weight: 避免持仓过小导致交易成本不划算target_return: 确保组合达到特定收益目标
4. 模型局限性分析与改进方向
4.1 均值方差模型的固有缺陷
- 对输入参数极度敏感:预期收益和协方差矩阵的微小变化会导致权重剧烈波动
- 未考虑高阶矩:忽略了偏度和峰度(极端事件风险)
- 静态假设:假设参数在投资期内恒定,忽略了时变性
- 未考虑交易成本:实际调仓会产生摩擦成本
- 正态分布假设:金融资产收益率往往呈现尖峰厚尾特征
4.2 改进模型与替代方案
| 模型 | 核心改进 | 适用场景 |
|---|---|---|
| Black-Litterman模型 | 结合市场均衡收益与主观观点 | 需要融入投资者观点 |
| 风险平价模型 | 按风险贡献度分配权重 | 追求极端风险分散 |
| BLUP模型 | 引入贝叶斯收缩估计 | 小样本数据稳定估计 |
| CVaR模型 | 关注尾部风险 | 极端风险控制 |
4.3 实战建议:模型组合使用
在实际应用中,建议采用混合策略:
- 长期配置:使用均值方差模型确定战略资产配置比例
- 中期调整:结合Black-Litterman模型融入市场观点
- 短期优化:使用风险平价模型进行战术调整
- 风险控制:使用CVaR模型监控尾部风险
5. 完整实战案例:从数据到决策
5.1 案例背景
假设一位45岁投资者,有50万美元投资资金,风险偏好中等,目标是在5-10年内实现资产增值,同时控制下行风险。
5.2 完整代码实现
class MeanVariancePortfolio:
"""均值方差投资组合管理类"""
def __init__(self, tickers, start_date, end_date):
self.tickers = ticki
self.start_date = start_date
self.end_date = end_date
self.mu = None
self.cov = None
self.weights = None
def load_data(self):
"""加载并处理数据"""
prices = yf.download(self.tickers, start=self.start_date, end=self.end_date)['Adj Close']
returns = np.log(prices / prices.shift(1)).dropna()
self.mu = returns.mean() * 252
self.cov = returns.cov() * 252
return self.mu, self.cov
def optimize(self, target_return=None, constraints=None):
"""优化投资组合"""
if constraints is None:
constraints = {}
if target_return is not None:
constraints['target_return'] = target_return
self.weights = constrained_optimization(self.mu, self.cov, constraints)
return self.weights
def backtest(self, test_start='2024-01-01', test_end='2024-12-31'):
"""回测验证"""
# 获取测试期数据
prices = yf.download(self.tickers, start=test_start, end=test_end)['Adj Close']
returns = np.log(prices / prices.shift(1)).dropna()
# 计算组合表现
port_returns = returns @ self.weights
cumulative = (1 + port_returns).cumprod()
# 计算指标
total_return = cumulative.iloc[-1] - 1
annual_return = (1 + total_return) ** (1 / (len(returns) / 252)) - 1
annual_vol = port_returns.std() * np.sqrt(252)
sharpe = annual_return / annual_vol
max_dd = (cumulative / cumulative.cummax() - 1).min()
print(f"回测结果 ({test_start} to {test_end})")
print(f"总收益: {total_return:.2%}")
print(f"年化收益: {annual_return:.2%}")
print(f"年化波动: {annual_vol:.2%}")
print(f"夏普比率: {sharpe:.2f}")
print(f"最大回撤: {max_dd:.2%}")
return cumulative
# 完整使用流程
portfolio = MeanVariancePortfolio(['SPY', 'IWM', 'TLT'], '2020-01-01', '2023-12-31')
portfolio.load_data()
# 案例1:保守型配置(目标收益8%,单一资产上限25%)
w_conservative = portfolio.optimize(
target_return=0.08,
constraints={'max_weight': 0.25, 'min_weight': 0.05}
)
# 案例2:平衡型配置(目标收益12%,单一资产上限35%)
w_balanced = portfolio.optimize(
target_return=0.12,
constraints={'max_weight': 0.35, 'min_weight': 0.05}
)
# 回测验证
print("\n=== 保守型配置回测 ===")
portfolio.weights = w_conservative
portfolio.backtest()
print("\n=== 平衡型配置回测 ===")
portfolio.weights = w_balanced
portfolio.backtest()
案例结果解读:
- 保守型:债券配置比例较高,波动率低,适合风险厌恶型投资者
- 平衡型:股票配置比例提升,预期收益更高,但需承担更大波动
- 通过回测验证模型在样本外的表现,检验模型稳健性
6. 总结与最佳实践
6.1 核心要点回顾
- 数学基础:均值方差模型通过二次规划求解最优权重,核心是权衡收益与风险
- 数据驱动:预期收益和协方差矩阵的估计质量直接影响模型效果
- 约束条件:实际应用必须添加现实约束,避免理论最优解脱离实际
- 可视化:有效前沿是理解模型价值的核心工具
- 回测验证:样本外测试是检验模型有效性的必要步骤
6.2 实战最佳实践
- 参数估计:使用至少3年历史数据,采用指数加权移动平均(EWMA)降低噪声
- 再平衡频率:建议季度再平衡,避免过度交易
- 风险预算:设置最大回撤阈值(如-15%),触发风控机制
- 模型融合:将均值方差模型与风险平价、宏观因子模型结合使用
- 持续监控:定期评估模型假设是否成立,及时调整参数
6.3 进一步学习方向
- 高级优化技术:随机优化、鲁棒优化
- 因子投资:将均值方差与Fama-French因子模型结合
- 机器学习应用:使用LSTM预测预期收益,提升输入参数质量
- 另类数据:整合卫星图像、社交媒体数据改进预测
均值方差模型虽然诞生于上世纪50年代,但其核心思想——通过分散化优化风险收益比——至今仍是投资管理的黄金法则。掌握其数学原理、代码实现和实际约束,是构建科学投资体系的关键一步。# 资产配置模型均值方差模型实战推导与应用解析
引言:现代投资组合理论的基石
资产配置是投资管理中最核心的决策环节,它决定了投资组合90%以上的收益表现。在众多资产配置模型中,均值方差模型(Mean-Variance Model) 由诺贝尔经济学奖得主哈里·马科维茨(Harry Markowitz)于1952年提出,是现代投资组合理论(Modern Portfolio Theory, MPT)的基石。该模型通过量化资产的预期收益与风险,为投资者提供了科学构建最优投资组合的数学框架。
均值方差模型的核心思想是:投资者不应仅关注单个资产的预期收益,而应关注整个投资组合的收益与风险特征。通过分散化投资,可以在不降低预期收益的前提下降低风险,或在相同风险水平下提升收益。本文将从数学推导、实战计算、Python代码实现和实际应用四个维度,深入解析均值方差模型的完整应用流程。
一、均值方差模型的数学基础与推导
1.1 核心假设与变量定义
均值方差模型建立在以下关键假设之上:
- 投资者是风险厌恶的,追求效用最大化
- 抄资者只关心资产的预期收益和风险(方差)
- 市场是完全的,无摩擦成本
- 资产收益服从正态分布
关键变量定义:
- \(n\):资产数量
- \(\mu = [\mu_1, \mu_2, ..., \mu_n]^T\):预期收益向量
- \(\Sigma\):协方差矩阵(反映资产间风险相关性)
- \(w = [w_1, w_2, ..., w_n]^T\):权重向量,满足 \(\sum w_i = 1\) 且 \(w_i \geq 0\)(不允许卖空)
1.2 组合收益与风险的数学表达
组合预期收益: $\( E(R_p) = \sum_{i=1}^n w_i \mu_i = w^T \mu \)$
组合方差(风险): $\( Var(R_p) = \sum_{i=1}^n \sum_{j=1}^n w_i w_j \sigma_{ij} = w^T \Sigma w \)$
其中 \(\sigma_{ij}\) 是资产 \(i\) 和资产 \(j\) 的协方差,\(\sigma_{ii} = \sigma_i^2\) 是资产 \(i\) 的方差。
1.3 最优化问题构建
均值方差模型的目标是寻找最优权重 \(w^*\),使得在给定预期收益水平下风险最小化,或在给定风险水平下收益最大化。这转化为一个二次规划问题:
目标函数: $\( \min_{w} w^T \Sigma w \quad \text{subject to} \quad w^T \mu = \mu_p, \quad w^T \mathbf{1} = 1 \)$
其中 \(\mu_p\) 是目标预期收益,\(\mathbf{1}\) 是全1向量。
1.4 拉格朗日乘数法求解
引入拉格朗日乘子 \(\lambda\) 和 \(\gamma\),构建拉格朗日函数: $\( L(w, \lambda, \gamma) = w^T \Sigma w + \lambda(\mu_p - w^T \mu) + \gamma(1 - w^T \mathbf{1}) \)$
对 \(w\) 求导并令导数为0: $\( \frac{\partial L}{\partial w} = 2\Sigma w - \lambda \mu - \gamma \mathbf{1} = 0 \)$
解得: $\( w = \frac{1}{2} \Sigma^{-1} (\lambda \mu + \gamma \mathbf{1}) \)$
通过约束条件 \(\mu_p = w^T \mu\) 和 \(1 = w^T \mathbf{1}\) 可解出 \(\lambda\) 和 \(\gamma\),最终得到最优权重表达式: $\( w^* = \Sigma^{-1} \begin{bmatrix} \mu & \mathbf{1} \end{bmatrix} \begin{bmatrix} \mu^T \Sigma^{-1} \mu & \mu^T \Sigma^{-1} \mathbf{1} \\ \mathbf{1}^T \Sigma^{-1} \mu & \mathbf{1}^T \Sigma^{-1} \mathbf{1} \end{bmatrix}^{-1} \begin{bmatrix} \mu_p \\ 1 \end{bmatrix} \)$
2. 实战案例:构建股票-债券投资组合
2.1 数据准备与预处理
假设我们有3类资产:美国大盘股(SPY)、美国小盘股(IWM)和美国国债(TLT)。我们获取2020-2023年的日度价格数据,计算预期收益和协方差矩阵。
数据预处理步骤:
- 获取价格数据
- 计算日度对数收益率:\(r_t = \ln(P_t/P_{t-1})\)
- 年化处理:预期收益 \(\mu = \text{mean}(r) \times 252\),协方差 \(\Sigma = \text{cov}(r) \times 252\)
2.2 Python代码实现:数据获取与计算
import numpy as np
import pandas as pd
import yfinance as yf
from scipy.optimize import minimize
import matplotlib.pyplot as plt
# 1. 数据获取
tickers = ['SPY', 'IWM', 'TLT']
start_date = '2020-01-01'
end_date = '2023-12-31'
# 下载价格数据
prices = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
returns = np.log(prices / prices.shift(1)).dropna()
# 2. 计算年化预期收益和协方差矩阵
mu = returns.mean() * 252 # 年化预期收益
cov = returns.cov() * 252 # 年化协方差矩阵
print("年化预期收益:\n", mu)
print("\n年化协方差矩阵:\n", cov)
代码说明:
- 使用
yfinance库获取股票和债券的调整后价格 - 计算对数收益率并年化处理(252个交易日)
mu和cov是模型的核心输入参数
2.3 最优化求解:构建有效前沿
有效前沿(Efficient Frontier)是所有最优投资组合的集合,即在给定风险水平下提供最大收益的组合。我们通过遍历目标收益范围来计算有效前沿:
# 3. 定义投资组合统计函数
def portfolio_stats(w, mu, cov):
"""计算组合收益、风险和夏普比率"""
port_return = w @ mu
port_vol = np.sqrt(w @ cov @ w)
sharpe = port_return / port_vol
return port_return, port_vol, sharpe
# 4. 定义最优化函数
def optimize_portfolio(target_return, mu, cov):
"""在给定目标收益下最小化风险"""
n = len(mu)
# 目标函数:最小化方差
def objective(w):
return w @ cov @ w
# 约束条件
constraints = (
{'type': 'eq', 'fun': lambda w: w @ mu - target_return}, # 收益约束
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}, # 权重和为1
{'type': 'ineq', 'fun': lambda w: w}, # 权重非负(不允许卖空)
)
# 初始猜测
w0 = np.ones(n) / n
# 求解
result = minimize(objective, w0, method='SLSQP', constraints=constraints)
return result.x if result.success else None
# 5. 计算有效前沿
target_returns = np.linspace(mu.min(), mu.max(), 50)
efficient_weights = []
efficient_volatilities = []
efficient_returns = []
for r in target_returns:
w_opt = optimize_portfolio(r, mu, cov)
if w_opt is not None:
vol = np.sqrt(w_opt @ cov @ w_opt)
efficient_weights.append(w_opt)
efficient_volatilities.append(vol)
efficient_returns.append(r)
# 6. 计算全局最小方差组合(GMV)
def global_min_variance(cov):
"""计算全局最小方差组合"""
n = cov.shape[0]
def objective(w):
return w @ cov @ w
constraints = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1})
w0 = np.ones(n) / n
result = minimize(objective, w0, method='SLSQP', constraints=constraints)
return result.x
w_gmv = global_min_variance(cov)
gmv_return, gmv_vol, _ = portfolio_stats(w_gmv, mu, cov)
代码解析:
portfolio_stats函数计算组合的三个核心指标optimize_portfolio使用scipy.optimize.minimize进行二次规划求解- 通过遍历目标收益范围构建有效前沿
- 全局最小方差组合是有效前沿的左端点(风险最小点)
2.4 可视化有效前沿与最优组合
# 7. 可视化
plt.figure(figsize=(12, 8))
# 绘制有效前沿
plt.plot(efficient_volatilities, efficient_returns, 'b-', linewidth=2, label='Efficient Frontier')
# 绘制全局最小方差组合
plt.scatter(gmv_vol, gmv_return, color='red', s=100, marker='*', label='Global Min Variance')
# 绘制单个资产
for i, ticker in enumerate(tickers):
vol = np.sqrt(cov.iloc[i, i])
plt.scatter(vol, mu[i], s=100, label=ticker)
# 绘制资本市场线(CML)- 假设无风险利率为2%
rf_rate = 0.02
# 找到切线组合(最大夏普比率)
sharpe_ratios = (np.array(efficient_returns) - rf_rate) / np.array(efficient_volatilities)
max_sharpe_idx = np.argmax(sharpe_ratios)
max_sharpe_vol = efficient_volatilities[max_sharpe_idx]
max_sharpe_return = efficient_returns[max_sharpe_idx]
# 绘制CML
cml_x = [0, max_sharpe_vol * 2]
cml_y = [rf_rate, max_sharpe_return + (max_sharpe_return - rf_rate) * (cml_x[1] / max_sharpe_vol - 1)]
plt.plot(cml_x, cml_y, 'g--', linewidth=2, label='Capital Market Line')
plt.xlabel('Volatility (Standard Deviation)')
plt.ylabel('Expected Return')
plt.title('Efficient Frontier with Capital Market Line')
plt.legend()
plt.grid(True)
plt.show()
# 8. 输出最优组合权重
print("\n全局最小方差组合权重:")
for t, w in zip(tickers, w_gmv):
print(f"{t}: {w:.2%}")
print("\n最大夏普比率组合权重:")
w_max_sharpe = efficient_weights[max_sharpe_idx]
for t, w in zip(tickers, w_max_sharpe):
print(f"{t}: {w:.2%}")
可视化解读:
- 蓝色曲线:有效前沿,所有最优组合的集合
- 红色星号:全局最小方差组合(风险最小点)
- 绿色虚线:资本市场线(CML),引入无风险资产后的最优组合边界
- 单个资产点:显示分散化投资的优势(组合点位于资产点左侧,表示风险更低)
3. 实战扩展:带约束条件的均值方差模型
3.1 实际投资中的常见约束
真实投资场景中,纯数学最优解往往不符合实际要求,需要添加以下约束:
- 行业/资产类别上限:单一资产不超过组合的30%
- 禁止卖空:权重必须非负
- 最小持仓:任何资产权重不低于5%
- 换手率限制:限制调仓频率
- 跟踪误差约束:相对于基准的偏离度限制
3.2 带约束的Python实现
def constrained_optimization(mu, cov, constraints_config):
"""
带约束的均值方差优化
参数:
mu: 预期收益向量
cov: 协方差矩阵
constraints_config: 约束配置字典
"""
n = len(mu)
# 目标函数:最小化方差
def objective(w):
return w @ cov @ w
# 基础约束
constraints = [
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}, # 权重和为1
{'type': 'ineq', 'fun': lambda w: w}, # 非负约束
]
# 自定义约束
if 'max_weight' in constraints_config:
max_w = constraints_config['max_weight']
constraints.append({'type': 'ineq', 'fun': lambda w: max_w - w})
if 'min_weight' in constraints_config:
min_w = constraints_config['min_weight']
constraints.append({'type': 'ineq', 'fun': lambda w: w - min_w})
if 'target_return' in constraints_config:
target_r = constraints_config['target_return']
constraints.append({'type': 'eq', 'fun': lambda w: w @ mu - target_r})
# 求解
w0 = np.ones(n) / n
result = minimize(objective, w0, method='SLSQP', constraints=constraints)
if result.success:
return result.x
else:
raise ValueError("优化失败,请检查约束条件是否可行")
# 使用示例
constraints = {
'max_weight': 0.30, # 单一资产不超过30%
'min_weight': 0.05, # 最小持仓5%
'target_return': 0.10 # 目标收益10%
}
w_constrained = constrained_optimization(mu, cov, constraints)
print("\n带约束的最优权重:")
for t, w in zip(tickers, w_constrained):
print(f"{t}: {w:.2%}")
约束条件说明:
max_weight: 防止过度集中风险min_weight: 避免持仓过小导致交易成本不划算target_return: 确保组合达到特定收益目标
4. 模型局限性分析与改进方向
4.1 均值方差模型的固有缺陷
- 对输入参数极度敏感:预期收益和协方差矩阵的微小变化会导致权重剧烈波动
- 未考虑高阶矩:忽略了偏度和峰度(极端事件风险)
- 静态假设:假设参数在投资期内恒定,忽略了时变性
- 未考虑交易成本:实际调仓会产生摩擦成本
- 正态分布假设:金融资产收益率往往呈现尖峰厚尾特征
4.2 改进模型与替代方案
| 模型 | 核心改进 | 适用场景 |
|---|---|---|
| Black-Litterman模型 | 结合市场均衡收益与主观观点 | 需要融入投资者观点 |
| 风险平价模型 | 按风险贡献度分配权重 | 追求极端风险分散 |
| BLUP模型 | 引入贝叶斯收缩估计 | 小样本数据稳定估计 |
| CVaR模型 | 关注尾部风险 | 极端风险控制 |
4.3 实战建议:模型组合使用
在实际应用中,建议采用混合策略:
- 长期配置:使用均值方差模型确定战略资产配置比例
- 中期调整:结合Black-Litterman模型融入市场观点
- 短期优化:使用风险平价模型进行战术调整
- 风险控制:使用CVaR模型监控尾部风险
5. 完整实战案例:从数据到决策
5.1 案例背景
假设一位45岁投资者,有50万美元投资资金,风险偏好中等,目标是在5-10年内实现资产增值,同时控制下行风险。
5.2 完整代码实现
class MeanVariancePortfolio:
"""均值方差投资组合管理类"""
def __init__(self, tickers, start_date, end_date):
self.tickers = tickers
self.start_date = start_date
self.end_date = end_date
self.mu = None
self.cov = None
self.weights = None
def load_data(self):
"""加载并处理数据"""
prices = yf.download(self.tickers, start=self.start_date, end=self.end_date)['Adj Close']
returns = np.log(prices / prices.shift(1)).dropna()
self.mu = returns.mean() * 252
self.cov = returns.cov() * 252
return self.mu, self.cov
def optimize(self, target_return=None, constraints=None):
"""优化投资组合"""
if constraints is None:
constraints = {}
if target_return is not None:
constraints['target_return'] = target_return
self.weights = constrained_optimization(self.mu, self.cov, constraints)
return self.weights
def backtest(self, test_start='2024-01-01', test_end='2024-12-31'):
"""回测验证"""
# 获取测试期数据
prices = yf.download(self.tickers, start=test_start, end=test_end)['Adj Close']
returns = np.log(prices / prices.shift(1)).dropna()
# 计算组合表现
port_returns = returns @ self.weights
cumulative = (1 + port_returns).cumprod()
# 计算指标
total_return = cumulative.iloc[-1] - 1
annual_return = (1 + total_return) ** (1 / (len(returns) / 252)) - 1
annual_vol = port_returns.std() * np.sqrt(252)
sharpe = annual_return / annual_vol
max_dd = (cumulative / cumulative.cummax() - 1).min()
print(f"回测结果 ({test_start} to {test_end})")
print(f"总收益: {total_return:.2%}")
print(f"年化收益: {annual_return:.2%}")
print(f"年化波动: {annual_vol:.2%}")
print(f"夏普比率: {sharpe:.2f}")
print(f"最大回撤: {max_dd:.2%}")
return cumulative
# 完整使用流程
portfolio = MeanVariancePortfolio(['SPY', 'IWM', 'TLT'], '2020-01-01', '2023-12-31')
portfolio.load_data()
# 案例1:保守型配置(目标收益8%,单一资产上限25%)
w_conservative = portfolio.optimize(
target_return=0.08,
constraints={'max_weight': 0.25, 'min_weight': 0.05}
)
# 案例2:平衡型配置(目标收益12%,单一资产上限35%)
w_balanced = portfolio.optimize(
target_return=0.12,
constraints={'max_weight': 0.35, 'min_weight': 0.05}
)
# 回测验证
print("\n=== 保守型配置回测 ===")
portfolio.weights = w_conservative
portfolio.backtest()
print("\n=== 平衡型配置回测 ===")
portfolio.weights = w_balanced
portfolio.backtest()
案例结果解读:
- 保守型:债券配置比例较高,波动率低,适合风险厌恶型投资者
- 平衡型:股票配置比例提升,预期收益更高,但需承担更大波动
- 通过回测验证模型在样本外的表现,检验模型稳健性
6. 总结与最佳实践
6.1 核心要点回顾
- 数学基础:均值方差模型通过二次规划求解最优权重,核心是权衡收益与风险
- 数据驱动:预期收益和协方差矩阵的估计质量直接影响模型效果
- 约束条件:实际应用必须添加现实约束,避免理论最优解脱离实际
- 可视化:有效前沿是理解模型价值的核心工具
- 回测验证:样本外测试是检验模型有效性的必要步骤
6.2 实战最佳实践
- 参数估计:使用至少3年历史数据,采用指数加权移动平均(EWMA)降低噪声
- 再平衡频率:建议季度再平衡,避免过度交易
- 风险预算:设置最大回撤阈值(如-15%),触发风控机制
- 模型融合:将均值方差模型与风险平价、宏观因子模型结合使用
- 持续监控:定期评估模型假设是否成立,及时调整参数
6.3 进一步学习方向
- 高级优化技术:随机优化、鲁棒优化
- 因子投资:将均值方差与Fama-French因子模型结合
- 机器学习应用:使用LSTM预测预期收益,提升输入参数质量
- 另类数据:整合卫星图像、社交媒体数据改进预测
均值方差模型虽然诞生于上世纪50年代,但其核心思想——通过分散化优化风险收益比——至今仍是投资管理的黄金法则。掌握其数学原理、代码实现和实际约束,是构建科学投资体系的关键一步。
