/ / /
第35讲:风险约束的投资组合优化 (Risk-Constrained Portfolio Optimization)
🔴
入学要求
💯
能力测试
🛣️
课程安排
🕹️
研究资源

第35讲:风险约束的投资组合优化 (Risk-Constrained Portfolio Optimization)

💡

查看全集:💎Quantopia量化分析56讲

一、核心概念与工具准备

关键公式

R=BF+ϵ\vec{R} = B\vec{F} + \epsilon

其中:

R\vec{R} 表示资产收益率向量

BB 表示因子暴露度矩阵(因子载荷)

F\vec{F} 表示因子收益率向量

ϵ\epsilon 表示特质收益向量(随机误差项)

这个公式体现了投资组合收益可以分解为:

  1. 系统性部分:因子暴露与因子收益的乘积(BFB\vec{F})
  1. 非系统性部分:特质收益(ϵ\epsilon)

工具包配置

import numpy as np
import pandas as pd
import yfinance as yf
import statsmodels.api as sm
import matplotlib.pyplot as plt
from tqdm import tqdm

二、数据获取与预处理

步骤详解

  1. 获取标普500成分股(示例替代QTradableStocksUS):
sp500 = pd.read_html('https://en.wikipedia.org/wiki/List_of_S%26P_500_companies')[0]
tickers = sp500.Symbol.tolist()[:50]  # 取前50支简化计算
  1. 下载历史价格数据
def download_prices(tickers, start, end):
    data = yf.download(tickers, start=start, end=end)['Adj Close']
    return data.dropna(axis=1)

prices = download_prices(tickers, '2009-01-01', '2011-01-01')
returns = prices.pct_change().dropna()  # 日收益率矩阵

数据结构示例

Date        AAPL      MSFT      AMZN      ...
2009-01-02  0.012     -0.005    0.018
2009-01-03  -0.007     0.015    0.002

三、因子模型构建

Fama-French三因子数据获取

ff_url = "https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_daily_CSV.zip"
ff_data = pd.read_csv(ff_url, skiprows=3, index_col=0, parse_dates=True)
ff_data = ff_data.iloc[:, :3] / 100  # 转换为小数形式

因子对齐处理

common_dates = returns.index.intersection(ff_data.index)
F = ff_data.loc[common_dates]  # 因子收益矩阵
R = returns.loc[common_dates]  # 对齐后的资产收益

四、因子暴露计算

滚动回归示例

betas = pd.DataFrame(index=R.columns, columns=F.columns)

for stock in tqdm(R.columns):
    model = sm.OLS(R[stock], sm.add_constant(F))
    results = model.fit()
    betas.loc[stock] = results.params.drop('const')

暴露矩阵结构

           Mkt-RF     SMB     HML
AAPL        1.05   -0.09   -0.34
MSFT        0.92    0.12    0.21
...

五、风险分解实战

方差分解实现

# 协方差矩阵计算
factor_cov = F.cov()
specific_var = R.var(axis=0)

# 组合权重(等权示例)
weights = np.ones(len(R.columns)) / len(R.columns)

# 风险分解
common_risk = weights @ betas @ factor_cov @ betas.T @ weights
specific_risk = weights @ np.diag(specific_var) @ weights
total_risk = common_risk + specific_risk

风险占比计算

print(f"系统性风险占比: {common_risk/total_risk:.2%}")

六、优化模型构建

CVXPY优化示例

import cvxpy as cp

# 定义优化变量
w = cp.Variable(len(weights))
risk_target = 0.05  # 最大因子暴露

# 构建约束
constraints = [
    cp.sum(w) == 1,
    w >= 0,
    cp.norm(betas.T @ w, 'inf') <= risk_target
]

# 定义目标函数(最大化收益)
portfolio_return = R.mean().values @ w
problem = cp.Problem(cp.Maximize(portfolio_return), constraints)
problem.solve()

优化结果分析

print("最优权重:", w.value.round(3))
print("预期收益:", portfolio_return.value)

七、绩效归因分析

使用PyFolio进行归因

import pyfolio as pf

# 假设已有组合收益序列
returns_portfolio = ...

# 生成归因报告
pf.create_returns_tear_sheet(returns_portfolio, factor_loadings=betas)

八、关键演进对比

方法类型优点局限性
均值-方差优化理论完备对输入参数敏感
风险平价模型风险均衡配置忽略收益预测
因子风险约束模型明确控制风险来源依赖因子模型准确性

小练习

尝试将市场因子暴露限制在0.8以下,比较优化前后组合的收益波动比变化

# 参考答案
risk_target = 0.8
constraints.append(betas['Mkt-RF'] @ w <= risk_target)
problem.solve()
print(f"夏普比率变化: {portfolio_return.value/problem.constraints[0].dual_value}")

附:练习合集