主题切换
15.1 宏观因子模型
名词解释:宏观因子(Macroeconomic Factors)
宏观因子是可观测或可估计的宏观经济驱动变量,对多种资产类别的收益率具有系统性解释力。与风格因子(如价值、动量)不同,宏观因子基于经济基本面,如GDP增长、通胀、利率和汇率,能够跨越资产类别解释风险和收益的来源。
一、主要宏观因子体系
1.1 五大核心宏观因子
现代宏观量化投资通常围绕五个核心因子构建框架:
| 因子 | 代理变量 | 经济含义 | 影响资产 |
|---|---|---|---|
| 增长(Growth) | PMI、工业增加值、GDP增速 | 经济扩张/收缩 | 权益、信用债、商品 |
| 通胀(Inflation) | CPI、PCE、BEI | 物价水平变化 | 名义国债、TIPS、黄金 |
| 利率(Rates) | 国债收益率、收益率曲线斜率 | 货币政策和融资条件 | 债券、REITs、成长股 |
| 汇率(FX) | DXY、贸易加权汇率 | 国际竞争力与资本流动 | 出口企业股、新兴市场 |
| 风险偏好(Risk Appetite) | VIX、信用利差、EMB利差 | 投资者情绪与风险承受 | 高收益债、权益、避险资产 |
1.2 因子的构建方法
宏观因子不能直接观测,需要通过统计方法从可观测的经济指标中提取。三种主流方法:
- 主成分分析(PCA):从多维经济数据中提取正交的主成分作为因子
- 卡尔曼滤波:将可观测经济变量与不可观测的潜在因子连接
- 动态因子模型(DFM):假设少量潜在因子驱动大量经济序列
此处有展示代码展开 ▼
python
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
def build_macro_factors_pca(economic_data: pd.DataFrame,
n_factors: int = 5) -> tuple:
"""
通过PCA从经济数据中提取宏观因子。
参数:
economic_data: 经济指标DataFrame,行=时间,列=指标
n_factors: 提取的主成分数量
返回:
(factors, loadings, explained_variance)
"""
# 标准化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(economic_data)
# PCA
pca = PCA(n_components=n_factors)
factors = pca.fit_transform(data_scaled)
# 因子载荷
loadings = pd.DataFrame(
pca.components_.T,
index=economic_data.columns,
columns=[f'Factor{i+1}' for i in range(n_factors)]
)
# 解释方差
explained_variance = pca.explained_variance_ratio_
# 命名因子(基于最大载荷的经济指标)
factor_names = []
for i in range(n_factors):
top_indicator = loadings[f'Factor{i+1}'].abs().idxmax()
factor_names.append(f'PC{i+1} ({top_indicator})')
factors_df = pd.DataFrame(
factors,
index=economic_data.index,
columns=factor_names
)
# 可视化
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 解释方差
axes[0, 0].bar(range(1, n_factors + 1),
explained_variance * 100, color='steelblue')
axes[0, 0].plot(range(1, n_factors + 1),
np.cumsum(explained_variance) * 100,
'ro-', markersize=8)
axes[0, 0].set_xlabel('主成分')
axes[0, 0].set_ylabel('解释方差 (%)')
axes[0, 0].set_title('各主成分解释方差')
axes[0, 0].grid(True, alpha=0.3)
# 载荷热力图(前两个因子)
load_top = loadings.iloc[:, :2].copy()
load_top = load_top.loc[load_top.abs().sum(axis=1).sort_values(
ascending=False).index[:15]]
im = axes[0, 1].imshow(load_top.values, cmap='RdBu_r', aspect='auto',
vmin=-1, vmax=1)
axes[0, 1].set_xticks(range(2))
axes[0, 1].set_xticklabels(load_top.columns)
axes[0, 1].set_yticks(range(len(load_top)))
axes[0, 1].set_yticklabels(load_top.index, fontsize=8)
axes[0, 1].set_title('前两个因子的载荷')
plt.colorbar(im, ax=axes[0, 1])
# 因子时序
for i, col in enumerate(factors_df.columns[:2]):
axes[1, i].plot(factors_df.index, factors_df[col],
linewidth=1, color=['darkblue', 'darkred'][i])
axes[1, i].axhline(y=0, color='gray', linestyle='--', alpha=0.5)
axes[1, i].set_title(col)
axes[1, i].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
return factors_df, loadings, explained_variance点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `build_macro_factors_pca`(通过PCA从经济数据中提取宏观因子)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
二、因子暴露与资产收益回归
2.1 宏观因子模型的回归设定
将资产收益率对宏观因子进行时间序列回归:
其中
2.2 两阶段估计法(Fama-MacBeth)
此处有展示代码展开 ▼
python
def fama_macbeth_macro(asset_returns: pd.DataFrame,
macro_factors: pd.DataFrame,
n_lags: int = 0) -> dict:
"""
Fama-MacBeth 两阶段回归估计宏观因子风险溢价。
第一阶段:对每个资产做时间序列回归得到 β
第二阶段:每期横截面回归得到因子风险溢价 λ
参数:
asset_returns: (T, N) 资产超额收益率
macro_factors: (T, K) 宏观因子时间序列
n_lags: 滞后因子数(用于考虑延迟反应)
返回:
包含 betas, risk_premia, t_stats 的字典
"""
T, N = asset_returns.shape
K = macro_factors.shape[1]
# 第一阶段:时间序列回归 (每个资产)
betas = np.zeros((N, K))
for i in range(N):
y = asset_returns.iloc[:, i].values
X = macro_factors.values
X = np.column_stack([np.ones(len(X)), X]) # 添加截距
beta = np.linalg.lstsq(X, y, rcond=None)[0]
betas[i] = beta[1:] # 斜率为因子暴露
# 第二阶段:横截面回归 (每期)
risk_premia = np.zeros((T, K))
for t in range(T):
y = asset_returns.iloc[t].values # 横截面收益率
X = betas # 因子暴露作为解释变量
X = np.column_stack([np.ones(N), X])
premia = np.linalg.lstsq(X, y, rcond=None)[0]
risk_premia[t] = premia[1:]
# 风险溢价统计
mean_premia = np.mean(risk_premia, axis=0)
se_premia = np.std(risk_premia, axis=0) / np.sqrt(T)
t_stats = mean_premia / se_premia
# 显著性检验 (Newey-West adjusted)
from statsmodels.tsa.stattools import adfuller
return {
'betas': pd.DataFrame(betas,
index=asset_returns.columns,
columns=[f'Beta_F{i+1}' for i in range(K)]),
'risk_premia_annual': mean_premia * 12,
't_stats': t_stats,
'significant': np.abs(t_stats) > 1.96,
'premia_time_series': pd.DataFrame(
risk_premia,
index=asset_returns.index,
columns=[f'Lambda_F{i+1}' for i in range(K)]
)
}点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `fama_macbeth_macro`(Fama-MacBeth 两阶段回归估计宏观因子风险溢价)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
三、宏观因子与风格因子的交互
3.1 因子轮动的宏观驱动
风格因子(价值、动量、质量、规模、低波动)的表现与宏观经济环境密切相关:
| 宏观环境 | 表现优异的风格因子 | 表现较差的风格因子 |
|---|---|---|
| 高增长 + 低通胀(Goldilocks) | 动量、成长 | 低波动、价值 |
| 衰退 + 通缩 | 质量、低波动 | 价值、小盘 |
| 滞胀 | 价值、商品因子 | 成长、动量 |
| 复苏 | 小盘、价值 | 低波动、质量 |
3.2 双因子暴露分析
此处有展示代码展开 ▼
python
def factor_interaction_analysis(asset_returns: pd.DataFrame,
macro_factors: pd.DataFrame,
style_factors: pd.DataFrame) -> pd.DataFrame:
"""
分析宏观因子与风格因子的交互效应。
回归: r_i = α + β_macro * F_macro + β_style * F_style
+ γ * (F_macro * F_style) + ε
参数:
asset_returns: 资产收益率
macro_factors: 宏观因子
style_factors: 风格因子
返回:
交互项效应的DataFrame
"""
results = []
for macro_name in macro_factors.columns:
for style_name in style_factors.columns:
# 构造回归变量
F_macro = macro_factors[macro_name].values
F_style = style_factors[style_name].values
F_interact = F_macro * F_style
# 对每个资产做回归
interaction_betas = []
for asset in asset_returns.columns:
y = asset_returns[asset].values
X = np.column_stack([
np.ones(len(y)), F_macro, F_style, F_interact
])
# 移除包含NaN的行
mask = ~np.isnan(y) & ~np.isnan(X).any(axis=1)
if mask.sum() < 10:
continue
beta = np.linalg.lstsq(X[mask], y[mask], rcond=None)[0]
interaction_betas.append(beta[3]) # 交互项系数
if interaction_betas:
mean_beta = np.mean(interaction_betas)
t_stat = mean_beta / (np.std(interaction_betas) /
np.sqrt(len(interaction_betas)))
results.append({
'Macro_Factor': macro_name,
'Style_Factor': style_name,
'Interaction_Beta_mean': mean_beta,
't_stat': t_stat,
'Significant': abs(t_stat) > 1.96
})
return pd.DataFrame(results)点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `factor_interaction_analysis`(分析宏观因子与风格因子的交互效应)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
四、因子择时框架
4.1 因子择时的逻辑
因子择时(Factor Timing)是基于宏观经济状态动态调整因子配置的策略。其基本逻辑是:不同因子在不同宏观环境下表现不同,通过预测宏观状态可以间接预测因子收益。
4.2 基于宏观状态的因子轮动
此处有展示代码展开 ▼
python
def macro_regime_factor_allocation(macro_state: str,
factor_universe: dict) -> dict:
"""
基于宏观状态的因子配置权重。
参数:
macro_state: 宏观状态标签
- 'expansion': 扩张期
- 'contraction': 收缩期
- 'stagflation': 滞胀期
- 'recovery': 复苏期
factor_universe: 因子名称到中性权重的映射
返回:
调整后的因子权重
"""
# 宏观状态与因子表现的关系矩阵
regime_tilts = {
'expansion': {'momentum': 1.5, 'growth': 1.5, 'quality': 1.0,
'value': 0.5, 'low_vol': 0.5, 'size': 1.0},
'contraction': {'momentum': 0.5, 'growth': 0.3, 'quality': 2.0,
'value': 0.8, 'low_vol': 2.0, 'size': 0.2},
'stagflation': {'momentum': 0.5, 'growth': 0.2, 'quality': 1.0,
'value': 1.5, 'low_vol': 1.0, 'size': 0.5},
'recovery': {'momentum': 1.0, 'growth': 1.2, 'quality': 1.0,
'value': 1.5, 'low_vol': 0.5, 'size': 1.8}
}
if macro_state not in regime_tilts:
return factor_universe # 未知状态,保持中性
tilts = regime_tilts[macro_state]
# 计算调整后的权重
adjusted_weights = {}
for factor, neutral_weight in factor_universe.items():
tilt = tilts.get(factor, 1.0)
adjusted_weights[factor] = neutral_weight * tilt
# 归一化
total = sum(adjusted_weights.values())
adjusted_weights = {k: v / total for k, v in adjusted_weights.items()}
return adjusted_weights点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `macro_regime_factor_allocation`(基于宏观状态的因子配置权重)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
宏观因子模型为量化投资提供了一个自上而下的风险归因和资产配置框架。在掌握了宏观因子的构建与应用之后,我们将深入探讨一个最为重要的宏观经济变量——美联储货币政策——如何影响全球资产定价。