第4章:现代投资组合理论(MPT)——马科维茨模型的核心思想
各位同学,今天我们来聊聊现代投资组合理论。说实话,这个理论在金融圈的地位,就跟牛顿力学在物理界的地位差不多。我当年刚入行时,老板扔给我一堆股票数据,说「你给我做个组合优化」。我当时一脸懵,后来啃完马科维茨的论文,才恍然大悟——原来投资不是选最好的股票,而是选最合适的组合。
4.1 马科维茨模型的核心思想
马科维茨在1952年提出了一个颠覆性的观点:不要把所有鸡蛋放在一个篮子里。但这句话谁都会说,关键是怎么科学地放?
他的核心洞察其实很简单:
- 收益可以衡量:用期望收益率(均值)
- 风险可以量化:用收益率的波动率(方差或标准差)
- 组合的效果:不是简单的加权平均,因为资产之间有相关性
说白了,就是通过分散化,在不降低预期收益的情况下降低风险,或者在相同风险下获得更高收益。嗯,这里要注意:分散化不是随便买几只股票就完事了,你得考虑它们之间的相关性。
核心公式:组合的方差 = w₁²σ₁² + w₂²σ₂² + 2w₁w₂σ₁σ₂ρ₁₂
其中ρ是相关系数。当ρ < 1时,组合风险就小于单个资产风险的加权平均。这就是分散化的数学本质。
我在项目中遇到过一位客户,他买了20只科技股,以为分散了。结果一算相关系数,全在0.8以上。这哪是分散?这是把鸡蛋放在20个相邻的篮子里,一摔全碎。
4.2 有效前沿与最优组合
有效前沿是什么?我习惯这么理解:给定一组资产,你能达到的「最佳风险-收益边界」。
你想想看,如果你把所有可能的权重组合都画出来,会得到一个散点图。这些点中,有一部分是「占优」的——在相同风险下收益最高,或者在相同收益下风险最低。这些点连成的曲线,就是有效前沿。
避坑指南:我曾经以为有效前沿是一条平滑的曲线,结果用真实数据一跑,发现全是锯齿。后来才明白,样本数据有限,估计误差会导致前沿「毛躁」。所以实际中我们通常用「收缩估计」或「贝叶斯方法」来平滑。
最优组合怎么选?这取决于你的风险偏好。有效前沿上的每个点都是「最优」的,但哪个最适合你?
- 风险厌恶型:选靠近左端的点,波动小但收益也低
- 风险偏好型:选靠近右端的点,收益高但波动大
- 最理性选择:引入无风险资产,找到资本市场线(CML)与有效前沿的切点——那就是「市场组合」
我个人习惯用夏普比率来选。夏普比率最高的那个组合,就是每单位风险能带来最多超额收益的点。说白了,就是性价比最高的组合。
4.3 均值-方差优化的数学原理
数学上,这是个二次规划问题:
目标函数:最小化组合方差 σ² = wᵀΣw
约束条件:
- 预期收益 = wᵀμ = μ₀(目标收益)
- 权重之和 = 1(满仓投资)
- 允许做空时:wᵢ ∈ ℝ
- 不允许做空时:wᵢ ≥ 0
其中Σ是协方差矩阵,μ是预期收益向量。为什么用协方差矩阵?因为它捕捉了所有资产两两之间的相关性。我刚开始做优化时,总想着用相关系数矩阵,后来发现协方差矩阵才是正确的——它同时包含了波动率和相关性信息。
求解这个优化问题,可以用拉格朗日乘子法。对于不允许做空的情况,则需要用数值优化算法(比如scipy.optimize)。
4.4 Python实现:从理论到代码
光说不练假把式。下面我给出一个完整的Python实现,你直接复制就能跑。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import minimize
# 假设我们有4只股票的历史收益率数据
# 实际中你可以从雅虎财经或本地CSV读取
returns = pd.DataFrame({
'AAPL': [0.02, -0.01, 0.03, 0.01, -0.02],
'GOOG': [0.01, 0.02, -0.01, 0.03, 0.00],
'MSFT': [0.03, 0.00, 0.02, -0.01, 0.01],
'AMZN': [-0.01, 0.03, 0.01, 0.02, -0.01]
})
# 计算预期收益和协方差矩阵
mu = returns.mean() * 252 # 年化收益率
Sigma = returns.cov() * 252 # 年化协方差矩阵
def portfolio_stats(weights):
"""计算组合的预期收益、方差和标准差"""
port_return = np.dot(weights, mu)
port_variance = np.dot(weights.T, np.dot(Sigma, weights))
port_std = np.sqrt(port_variance)
return port_return, port_variance, port_std
def min_variance_portfolio(target_return=None):
"""求解最小方差组合或给定收益下的最小方差组合"""
n = len(mu)
# 约束条件:权重和为1
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
# 如果有目标收益,添加收益约束
if target_return is not None:
constraints = (
{'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
{'type': 'eq', 'fun': lambda x: np.dot(x, mu) - target_return}
)
# 不允许做空(权重非负)
bounds = tuple((0, 1) for _ in range(n))
# 初始权重:等权重
init_guess = np.array([1/n] * n)
# 目标函数:最小化方差
def objective(weights):
_, var, _ = portfolio_stats(weights)
return var
result = minimize(objective, init_guess,
method='SLSQP',
bounds=bounds,
constraints=constraints)
return result.x
# 生成有效前沿
target_returns = np.linspace(mu.min(), mu.max(), 50)
efficient_portfolios = []
for tr in target_returns:
weights = min_variance_portfolio(tr)
ret, var, std = portfolio_stats(weights)
efficient_portfolios.append({'return': ret, 'risk': std, 'weights': weights})
# 绘制有效前沿
risks = [p['risk'] for p in efficient_portfolios]
returns = [p['return'] for p in efficient_portfolios]
plt.figure(figsize=(10, 6))
plt.plot(risks, returns, 'b-', linewidth=2, label='有效前沿')
plt.scatter(risks, returns, c='blue', s=10)
# 标记最小方差组合
min_var_idx = np.argmin(risks)
plt.scatter(risks[min_var_idx], returns[min_var_idx],
c='red', s=100, marker='*', label='最小方差组合')
plt.xlabel('风险(标准差)')
plt.ylabel('预期收益')
plt.title('马科维茨有效前沿')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
重要提醒:这段代码用的是样本数据,实际中你需要注意:
- 预期收益的估计误差很大,建议用CAPM或因子模型来平滑
- 协方差矩阵对异常值敏感,可以考虑用「收缩估计」或「稳健协方差」
- 不允许做空的约束会让有效前沿「缩水」,但更符合实际
4.5 知识体系总览
下面这张图帮你理清本章的核心逻辑:
这张图展示了MPT的完整流程:从输入数据开始,经过均值-方差优化,最终得到有效前沿和最优组合。中间的关键概念和注意事项,都是我在实际项目中踩过的坑。
4.6 小结与避坑指南
最后,我总结几个实战要点:
- 数据质量决定一切:垃圾进,垃圾出。我见过有人用3个月的数据做优化,结果前沿完全变形。建议至少用3-5年的日频数据。
- 不要迷信最优解:数学上的最优组合,在实际中可能因为交易成本、流动性等问题无法实现。我习惯在最优解附近取一个「稳健区域」。
- 定期再平衡:组合的权重会随着市场变化而漂移。我一般每季度做一次再平衡,同时检查协方差矩阵是否发生了结构性变化。
- 黑天鹅事件:MPT假设收益率服从正态分布,但现实中肥尾效应明显。建议结合压力测试和VaR来补充。
我的个人习惯:在做均值-方差优化之前,我会先用主成分分析(PCA)对资产进行降维。这样既能减少估计误差,又能抓住主要的风险因子。你试试看,效果往往比直接用原始数据好。
好了,这一章的内容就到这里。MPT是量化投资的基石,虽然它有很多假设限制,但理解它的核心思想——风险与收益的权衡、分散化的数学本质、有效前沿的构建——对你后续学习因子模型、风险平价等高级内容会非常有帮助。
公众号:蓝海资料掘金营,微信deep3321