跳到主内容
研究主线资产定价与组合构建 · 第30–36讲5/7 · 配套练习模块导览 →
练习

第34讲:因子风险暴露 (Factor Risk Exposure) · 练习

比较不同窗口的滚动因子暴露,验证多因子中性约束是否可行,并检查求解器状态与暴露残差。

← 返回本讲讲义
完成状态保存在当前浏览器,可随时取消。

实践:计算因子风险暴露#

滚动回归对比分析#

请完成以下任务:

  1. 获取阿里巴巴(BABA)和英伟达(NVDA)2020-2023年日度收益率数据

  2. 获取同期Fama-French五因子数据(代码:F-F_Research_Data_5_Factors_2x3)

  3. 分别计算90日和180日滚动窗口下的市场因子暴露

  4. 可视化对比不同窗口长度对暴露系数稳定性的影响

code
import yfinance as yf
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
from pandas_datareader import data as web

# 因子数据来自 Ken French Data Library,不是 Yahoo 行情代码
ff5 = web.DataReader(
    'F-F_Research_Data_5_Factors_2x3_daily', 'famafrench',
    start='2020-01-01', end='2023-12-31',
)[0] / 100

# 个股数据获取
tech_stocks = yf.download(
    ['BABA', 'NVDA'], start='2020-01-01', end='2023-12-31',
    auto_adjust=True, group_by='column', multi_level_index=True,
)['Close']
tech_returns = tech_stocks.pct_change().dropna()

# 数据对齐,并把个股收益转换为相对无风险利率的超额收益
combined_data = tech_returns.join(ff5, how='inner').dropna()
combined_data[tech_returns.columns] = combined_data[tech_returns.columns].sub(
    combined_data['RF'], axis=0
)

构建追踪组合#

多因子中性组合优化#

请实现以下要求:

  1. 选取苹果、微软、特斯拉三只股票

  2. 判断仅使用这三只股票、且不允许做空时,能否同时对市场因子和动量因子(Mom)中性

  3. 组合需满足个股权重不超过40%的约束

  4. 检查求解器状态和约束残差;若不可行,说明需要增加何种对冲工具或放宽哪些约束

code
from scipy.optimize import minimize
import numpy as np

# 获取因子暴露数据示例
factor_exposures = pd.DataFrame({
    'AAPL': [1.2, 0.8],
    'MSFT': [0.9, 0.6],
    'TSLA': [1.5, 1.1]
}, index=['Mkt-RF', 'Mom']).T

def multi_factor_neutral(benchmark_exposures):
    n_assets = len(factor_exposures)
    init_weights = np.ones(n_assets)/n_assets

    def objective(w):
        return np.sum(w**2)  # 在可行解中降低权重集中度;这不是方差模型

    constraints = (
        {'type': 'eq', 'fun': lambda w: w @ factor_exposures.values - benchmark_exposures},
        {'type': 'eq', 'fun': lambda w: np.sum(w)-1}
    )
    bounds = [(0,0.4) for _ in range(n_assets)]

    res = minimize(objective, init_weights,
                  constraints=constraints, bounds=bounds)
    if not res.success:
        raise ValueError(f'优化不可行:{res.message}')
    residual = res.x @ factor_exposures.values - benchmark_exposures
    if np.max(np.abs(residual)) > 1e-8:
        raise ValueError(f'因子约束未满足:{residual}')
    return res.x

# 目标因子暴露(全市场中性)
target_exposures = [0, 0]  # 市场因子和动量因子均为中性

这里给出的三只股票对两个因子的暴露都为正,同时又要求权重非负且总和为 1,因此 target_exposures = [0, 0] 不可行。这个例子应当保留失败检查:要得到真正的因子中性组合,需要加入可做空的股票或因子对冲工具,并重新设置权重/总敞口约束;不能忽略求解器状态后直接使用 res.x

完成练习后,自评一次

建议先独立作答,再展开参考答案进行核对。