第1章:均值-方差优化——马科维茨模型、有效前沿、最大夏普比率组合、约束条件处理
1.1 马科维茨模型:现代投资组合理论的基石
说起资产配置,绕不开一个人——马科维茨。他老人家在1952年提出的均值-方差模型,说白了就是一句话:别把所有鸡蛋放在一个篮子里,但怎么放,得算清楚。
我刚开始做量化那会儿,觉得这模型太简单了。不就是算个期望收益和协方差矩阵嘛。后来在实盘中吃了亏,才明白它的精妙之处。
马科维茨模型的核心逻辑是这样的:
- 收益用期望收益率衡量,越高越好
- 风险用收益率的方差(或标准差)衡量,越低越好
- 目标:在给定风险下最大化收益,或在给定收益下最小化风险
数学上,我们要求解这样一个优化问题:
min w^T Σ w
s.t. w^T μ = μ_target
sum(w) = 1
w_i ≥ 0 (可选,不允许做空)
其中w是权重向量,Σ是协方差矩阵,μ是预期收益向量。嗯,这里要注意,协方差矩阵的估计质量直接决定了模型的好坏。我在项目中遇到过,用过去3年的数据算协方差,结果市场风格一变,模型立马失效。
关键点:马科维茨模型是静态的,它假设收益和风险是稳定的。但现实世界哪有这么简单?所以后面我们会讲动态调整。
1.2 有效前沿:那条最美的曲线
有效前沿是什么?你想想看,把所有可能的投资组合画在风险-收益平面上,那些在相同风险下收益最高、或相同收益下风险最低的点连起来,就是有效前沿。
它长什么样?是一条向左上方凸起的曲线。为什么?因为资产之间的相关性不是完美的,组合能带来分散化收益。
我习惯用Python来画这条曲线。代码其实不复杂:
import numpy as np
import matplotlib.pyplot as plt
def efficient_frontier(mu, sigma, num_portfolios=10000):
n_assets = len(mu)
results = np.zeros((3, num_portfolios))
for i in range(num_portfolios):
weights = np.random.random(n_assets)
weights /= np.sum(weights)
port_return = np.dot(weights, mu)
port_risk = np.sqrt(np.dot(weights.T, np.dot(sigma, weights)))
results[0, i] = port_return
results[1, i] = port_risk
results[2, i] = port_return / port_risk # 夏普比率
return results
这段代码随机生成权重,计算每个组合的收益和风险。跑个一万次,你就能看到有效前沿的轮廓了。不过说实话,随机采样效率不高,真正做研究时我会用凸优化直接求解。
实战技巧:有效前沿的左侧区域是无效的——同样的风险下,你明明可以拿到更高的收益。所以,我们只关注前沿上的点。
1.3 最大夏普比率组合:最优的那个点
有效前沿上有无数个点,哪个最好?这取决于你的风险偏好。但有一个点很特殊——最大夏普比率组合。
夏普比率 = (组合收益 - 无风险利率) / 组合风险。它衡量的是每单位风险能带来多少超额收益。最大夏普比率组合,就是有效前沿上斜率最大的那个点。
怎么找?数学上,它是有效前沿与从无风险利率出发的射线的切点。代码实现也很直接:
def max_sharpe_portfolio(mu, sigma, rf=0.02):
n = len(mu)
# 用优化求解
def neg_sharpe(weights):
port_return = np.dot(weights, mu)
port_risk = np.sqrt(np.dot(weights.T, np.dot(sigma, weights)))
return -(port_return - rf) / port_risk
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
bounds = tuple((0, 1) for _ in range(n))
result = minimize(neg_sharpe,
n * [1./n],
method='SLSQP',
bounds=bounds,
constraints=constraints)
return result.x
我曾经用这个模型给一个FOF产品做配置,回测效果不错。但实盘时发现,最大夏普比率组合对输入参数特别敏感。预期收益稍微变一点,权重就天翻地覆。这就是所谓的“估计误差最大化”问题。
避坑指南:我曾经因为过度依赖最大夏普比率组合,导致换手率极高,交易成本吃掉了一大半收益。后来我学会了加约束条件,比如限制单个资产权重的上下限,或者限制行业集中度。
1.4 约束条件处理:让模型更接地气
纯数学上的马科维茨模型,允许你做空、允许你全仓一只股票。但现实中呢?
- 公募基金通常不允许做空
- 单个股票持仓不能超过10%
- 行业配置不能偏离基准太多
- 换手率要控制在合理范围
这些约束条件,说白了就是让模型从“理想”走向“现实”。处理起来也不复杂,在优化时加上bounds和constraints就行。
举个例子,假设我们要求:
- 每个资产权重在5%~30%之间
- 总权重为1
- 不做空
bounds = tuple((0.05, 0.30) for _ in range(n))
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
你想想看,加了这些约束后,有效前沿会怎样?它会向内收缩——因为可行域变小了。但换来的是更稳健、更可执行的组合。
我个人习惯,在实盘前至少加三类约束:
- 权重上下限:防止过度集中
- 行业暴露:控制风格漂移
- 换手率约束:降低交易成本
核心总结:均值-方差优化不是万能药,但它提供了一个清晰的框架。理解它,你就能看懂市面上80%的资产配置模型。后面的章节,我们会在这个基础上加入动态调整、风险平价、Black-Litterman等进阶方法。
知识体系图:本章核心逻辑
我的建议:刚开始学均值-方差优化,别急着上复杂模型。先用Excel或Python跑一个简单的两资产案例,把有效前沿画出来。亲眼看到那条曲线,比看一百页理论都管用。
公众号:蓝海资料掘金营,微信deep3321