Skip to content

6.1 均值-方差优化

概念详解

均值-方差优化(Mean-Variance Optimization, MVO)是现代投资组合理论的基石,由Harry Markowitz于1952年提出,并因此获得1990年诺贝尔经济学奖。MVO的核心思想极为简洁:在给定期望收益下最小化风险,或在给定风险下最大化期望收益

MVO将投资问题转化为一个数学优化问题——用均值衡量收益,用方差(或标准差)衡量风险,然后在两者之间寻找最优权衡。这个权衡的边界就是有效前沿(Efficient Frontier)。

名词解释:有效前沿

在给定风险水平下提供最高期望收益,或在给定期望收益下具有最小风险的投资组合集合的曲线。有效前沿上的任何组合都是"帕累托最优"的——不可能在不增加风险的情况下提高收益,也不可能在不降低收益的情况下减少风险。

名词解释:切线组合(Tangency Portfolio)

从无风险利率出发向有效前沿做切线,切点即为切线组合(也称市场组合)。切线组合在所有与无风险资产组合的组合中,具有最高的夏普比率。夏普比率最大化的结果就是切线组合。

名词解释:最小方差组合(GMV)

有效前沿上最左端的点——在所有可能的组合中具有最小方差的组合。GMV组合不依赖预期收益的估计,因此在实际中比切线组合更稳健(预期收益的估计误差远大于协方差的估计误差)。

MVO的基本假设

  1. 投资者是理性的,追求在风险和收益之间做最优权衡
  2. 资产收益率服从多元正态分布(或投资者只关心前两阶矩)
  3. 市场无摩擦(无交易成本、税收等)
  4. 可以无限细分的资产投资
  5. 存在无风险资产,可以自由借贷

数学原理

最优化问题

给定 N 个风险资产,期望收益向量 μ,协方差矩阵 Σ,权重向量 w

最小方差优化:minwwTΣws.t.wTμ=μtargetwi=1最大夏普优化:maxwwTμrfwTΣws.t.wi=1

二次规划求解

引入拉格朗日乘子,MVO的解析解为:

w=Σ111TΣ11(最小方差组合)w=Σ1(μrf1)1TΣ1(μrf1)(切线组合)

有效前沿的构建

对于不同的目标收益 μtarget,求解对应的最优权重 w(μtarget),得到组合的期望收益 μp=wTμ 和标准差 σp=wTΣw,所有 (σp,μp) 点构成有效前沿。

📈 两资产有效前沿模拟器

📈 两资产有效前沿模拟器

Python实战

📌 案例:用scipy做MVO

PYTHON56 行 · 1.6 KB
📄此处有展示代码56 行 · 1.6 KB展开 ▼
python
import numpy as np
from scipy.optimize import minimize

def mvo_min_variance(mu, Sigma, target_return=None):
    """
    均值-方差优化

    Parameters:
        mu: 期望收益向量 (N,)
        Sigma: 协方差矩阵 (N, N)
        target_return: 目标收益;None时输出最小方差组合
    Returns:
        weights: 最优权重向量
    """
    n = len(mu)

    def portfolio_variance(w):
        return w @ Sigma @ w

    constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]  # 权重和为1

    if target_return is not None:
        constraints.append({'type': 'eq', 'fun': lambda w: w @ mu - target_return})

    bounds = [(0, 1) for _ in range(n)]  # 不允许卖空
    w0 = np.ones(n) / n

    result = minimize(portfolio_variance, w0, method='SLSQP',
                      bounds=bounds, constraints=constraints)
    return result.x

def build_efficient_frontier(mu, Sigma, n_points=50, rf=0.02):
    """构建有效前沿"""
    gmv = mvo_min_variance(mu, Sigma)  # 最小方差组合
    min_ret = gmv @ mu
    max_ret = mu.max()

    target_returns = np.linspace(min_ret, max_ret, n_points)
    frontier = []

    for target in target_returns:
        try:
            w = mvo_min_variance(mu, Sigma, target_return=target)
            port_ret = w @ mu
            port_vol = np.sqrt(w @ Sigma @ w)
            sharpe = (port_ret - rf) / port_vol
            frontier.append({
                'return': port_ret,
                'volatility': port_vol,
                'sharpe': sharpe,
                'weights': w
            })
        except Exception:
            continue

    return pd.DataFrame(frontier)
点击展开可浏览运行结果
最优权重:
  资产1: 23.9%
  资产2: 8.2%
  资产3: 16.8%
  资产4: 16.8%
  资产5: 34.3%

年化收益: 24.65%
年化波动: 7.62%
夏普比率: 3.23

📌 案例:带约束的MVO(含个股上限、行业上限等)

PYTHON60 行 · 2.2 KB
📄此处有展示代码60 行 · 2.2 KB展开 ▼
python
def constrained_mvo(mu, Sigma, constraints_config):
    """
    带复杂约束的MVO

    constraints_config example:
    {
        'max_weight': 0.1,          # 单只股票上限10%
        'min_weight': 0.0,          # 不允许卖空
        'industry_exposure': {       # 行业暴露上限
            'tech': 0.3,
            'finance': 0.2,
        },
        'turnover_limit': 0.3,      # 换手率上限(需要当前持仓)
        'current_weights': None,    # 当前持仓权重
    }
    """
    n = len(mu)

    def objective(w):
        return w @ Sigma @ w

    # 约束条件
    constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]

    # 如有目标收益
    if 'target_return' in constraints_config:
        constraints.append(
            {'type': 'eq', 'fun': lambda w: w @ mu - constraints_config['target_return']}
        )

    # 权重边界
    bounds = [(constraints_config.get('min_weight', 0),
               constraints_config.get('max_weight', 1)) for _ in range(n)]

    # 行业暴露约束(需要行业映射表)
    if 'industry_mapping' in constraints_config and 'industry_exposure' in constraints_config:
        industry_mapping = constraints_config['industry_mapping']
        for ind, max_exp in constraints_config['industry_exposure'].items():
            ind_mask = np.array([industry_mapping.get(i) == ind for i in range(n)], dtype=float)
            constraints.append(
                {'type': 'ineq', 'fun': lambda w, m=ind_mask, e=max_exp: e - w @ m}
            )

    # 换手率约束
    if 'current_weights' in constraints_config and 'turnover_limit' in constraints_config:
        current_w = np.array(constraints_config['current_weights'])
        max_turnover = constraints_config['turnover_limit']

        def turnover_constraint(w):
            return max_turnover - np.sum(np.abs(w - current_w)) / 2
        constraints.append({'type': 'ineq', 'fun': turnover_constraint})

    w0 = np.ones(n) / n
    result = minimize(objective, w0, method='SLSQP',
                      bounds=bounds, constraints=constraints)

    if result.success:
        return result.x
    else:
        raise ValueError(f"优化失败: {result.message}")
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `constrained_mvo`(带复杂约束的MVO)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

📌 案例:MVO的敏感性分析——协方差估计误差的影响

PYTHON34 行 · 1.1 KB
📄此处有展示代码34 行 · 1.1 KB展开 ▼
python
def mvo_sensitivity_cov(returns, n_simulations=100, noise_level=0.1):
    """
    分析协方差矩阵估计误差对MVO权重的影响

    对协方差矩阵加入随机噪声,观察权重的分布
    """
    mu = returns.mean() * 252
    Sigma = returns.cov() * 252
    n = len(mu)

    weight_distribution = np.zeros((n_simulations, n))

    for i in range(n_simulations):
        # 对协方差矩阵加噪声
        noise = np.random.normal(0, noise_level, Sigma.shape)
        noise = (noise + noise.T) / 2  # 保持对称
        np.fill_diagonal(noise, np.diag(noise) * 2)  # 对角线加更多噪声

        Sigma_noisy = Sigma * (1 + noise)

        try:
            w = mvo_min_variance(mu, Sigma_noisy)
            weight_distribution[i] = w
        except Exception:
            continue

    # 分析权重的不确定性
    mean_weights = weight_distribution.mean(axis=0)
    std_weights = weight_distribution.std(axis=0)

    print(f"权重不确定性的均值: {std_weights.mean():.4f}")
    print(f"最不确定的资产: std={std_weights.max():.4f}")

    return mean_weights, std_weights
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `mvo_sensitivity_cov`(分析协方差矩阵估计误差对MVO权重的影响)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

常见误区

  1. 误差最大化问题:MVO对输入参数(尤其是预期收益)极其敏感。预期收益估计的微小误差会导致权重配置的巨大改变——MVO往往会给估计误差最大的资产分配极端权重。这是MVO在实践中最大的痛点。

  2. 协方差矩阵不可逆:当资产数量 N 大于时间样本数 TN>T)时,样本协方差矩阵是奇异的(不可逆),无法直接求解MVO。需要使用收缩估计(Ledoit-Wolf)、因子模型或正则化方法。

  3. 正态分布假设:MVO假设收益率服从正态分布,但实际的金融收益率存在肥尾和偏度。在极端市场事件中,基于正态假设的MVO会严重低估组合的真实风险。

  4. 忽略了参数不确定性:MVO将估计的参数(均值和协方差)当作已知的确定值,忽略了估计误差。贝叶斯方法和稳健优化可以部分解决这个问题。

  5. 静态优化 vs 动态决策:MVO是静态的单期优化,但实际投资是多期决策。Black-Litterman模型和随机规划可以处理多期问题。

实战练习

  1. 选取5只股票,计算它们的年化收益、波动率和协方差矩阵。绘制有效前沿曲线,标记最小方差组合和切线组合的位置。观察切线组合的夏普比率。

  2. 权重敏感性实验:对预期收益向量中的每个元素分别加一个小扰动(如±0.5%),观察最小方差组合和切线组合的权重变化。记录变化最大的资产。

  3. 用滚动窗口实现动态MVO:每季度用过去3年的数据估计参数并计算MVO最优权重,持有一个季度后重新优化。比较动态MVO与等权基准的长期表现差异。

延伸阅读

  • Markowitz, H. (1952). "Portfolio Selection." Journal of Finance. MVO的开创性论文。
  • Michaud, R. O. (1989). "The Markowitz Optimization Enigma: Is 'Optimized' Optimal?" 提出MVO的误差最大化问题。
  • Ledoit, O. & Wolf, M. (2004). "A Well-Conditioned Estimator for Large-Dimensional Covariance Matrices." 提出协方差矩阵的收缩估计方法。
  • Black, F. & Litterman, R. (1992). "Global Portfolio Optimization." 提出Black-Litterman模型,解决了MVO对输入参数过于敏感的问题。
  • DeMiguel, V., Garlappi, L., & Uppal, R. (2009). "Optimal Versus Naive Diversification: How Inefficient is the 1/N Portfolio Strategy?" 发现1/N等权在样本外经常优于MVO。

本章要点

  • MVO是现代投资组合理论的数学核心,通过权衡期望收益与方差来找到最优组合配置。
  • 有效前沿是在风险-收益平面上所有帕累托最优组合的集合;切线组合在前沿上具有最高夏普比率。
  • MVO在理论上完美,在实践中有严重的误差最大化问题:预期收益估计的小误差会被放大为权重的极端配置。
  • 协方差矩阵的估计同样关键:当 N>T 时矩阵奇异,需使用收缩估计、因子模型或正则化。
  • 最小方差组合(GMV)不依赖预期收益估计,在实践中比切线组合更稳健。
  • MVO的改进方向:Black-Litterman模型(融合主观观点)、贝叶斯方法(考虑参数不确定性)、稳健优化(最差情形优化)。