4、宏观因子的数据获取与清洗:数据源选择、频率对齐与异常处理
做宏观因子驱动策略,最头疼的往往不是模型本身,而是数据。我见过太多人花三个月搭了个漂亮的模型,结果因为数据源选错了、频率没对齐,回测曲线漂亮得不像话,一上实盘就崩。说白了,数据是地基,地基不稳,楼盖得再高也是危房。
这一章,咱们就聊聊数据获取与清洗的那些坑。我会把我在实战中踩过的雷、总结的经验,一股脑倒出来。
4.1 数据源选择:Wind、Bloomberg、CEIC,怎么选?
先说说三大主流数据源。我个人习惯是:国内用Wind,跨境用Bloomberg,宏观长序列用CEIC。但这只是大原则,具体还得看场景。
| 数据源 | 优势 | 劣势 | 我的使用场景 |
|---|---|---|---|
| Wind | 国内数据最全,API友好,更新快 | 跨境数据少,历史数据有时不连续 | A股因子、国内宏观指标 |
| Bloomberg | 全球数据标准,衍生品数据强 | 贵,学习曲线陡 | 跨境宏观、外汇、商品 |
| CEIC | 宏观长序列王者,历史数据完整 | 微观数据弱,更新频率低 | GDP、CPI等长周期研究 |
4.2 数据频率对齐:日频、周频、月频,怎么统一?
数据频率对齐,是宏观因子建模里最容易被忽视的环节。你想想看,GDP是季度数据,CPI是月度数据,利率是日频数据,股票收益率是日频数据。这些数据频率不一样,怎么放到一个模型里?
我的做法是:统一降频到最低频率。比如你要做月度再平衡,那就把所有数据都转成月频。
具体怎么转?我总结了一套规则:
- 流量数据(如GDP、工业增加值): 取季度内最后一个月的数据,或者取季度平均值。我个人习惯用最后一个月,因为反应更快。
- 存量数据(如货币供应量M2): 直接取月末值。
- 价格数据(如CPI、PPI): 取月度值,不需要额外处理。
- 收益率数据(如股票指数): 取月内日收益率的累乘,或者直接取月末值计算月收益率。
代码实现上,我一般用pandas的resample方法。举个例子:
import pandas as pd
# 假设df是日频数据,index是日期
# 转成月频,取月末值
monthly_df = df.resample('M').last()
# 如果是收益率,需要累乘
monthly_return = (1 + df).resample('M').prod() - 1
# 如果是季度数据,转成月频(取季度末月)
quarterly_to_monthly = df.resample('M').ffill() # 前向填充
4.3 异常值处理:别让一个极端值毁了你的模型
宏观数据里,异常值太常见了。比如2020年3月的VIX指数,比如2008年10月的信用利差。这些极端值,到底是该保留还是该剔除?
我的原则是:先判断异常值的性质,再决定处理方法。
- 数据录入错误: 比如某个数据点突然跳了10个标准差,明显是录入错误。这种直接剔除或用前后值填充。
- 真实极端事件: 比如金融危机、疫情。这种要保留,但可以做缩尾处理(Winsorization)。
- 结构性变化: 比如某个国家改了统计口径。这种需要分段处理,或者用断点回归。
具体操作上,我常用两种方法:
- MAD法(中位数绝对偏差): 比标准差法更稳健,不受极端值影响。
- 分位数法: 比如把超过99%分位数的值替换成99%分位数。
import numpy as np
def winsorize_series(series, limits=[0.01, 0.01]):
"""
缩尾处理:把超出上下限的值替换为分位数
"""
lower = series.quantile(limits[0])
upper = series.quantile(1 - limits[1])
return series.clip(lower, upper)
def mad_outlier(series, threshold=3):
"""
MAD法检测异常值
"""
median = series.median()
mad = np.median(np.abs(series - median))
modified_z_scores = 0.6745 * (series - median) / mad
return np.abs(modified_z_scores) > threshold
4.4 缺失值填充:能不用插值就不用插值
缺失值处理,是数据清洗里最考验功力的环节。宏观数据缺失的原因五花八门:节假日、数据发布延迟、统计口径变更、数据源缺失……
我的处理优先级是:
- 前向填充(ffill): 适用于低频数据,比如月度数据缺失,用上个月的值填充。这是最保守的方法。
- 后向填充(bfill): 适用于数据发布延迟的情况。比如某个月的数据晚了两周发布,可以用下个月的值回填。
- 线性插值: 只适用于缺失值较少且数据趋势明显的情况。比如日频数据缺失一两天。
- 模型预测填充: 用其他相关因子来预测缺失值。比如用PMI预测GDP。这个方法最复杂,但效果最好。
代码实现上,pandas的fillna方法基本够用:
# 前向填充
df.fillna(method='ffill', inplace=True)
# 后向填充
df.fillna(method='bfill', inplace=True)
# 线性插值
df.interpolate(method='linear', inplace=True)
# 更高级的:用其他因子做回归预测
from sklearn.linear_model import LinearRegression
# 假设X是其他因子,y是缺失的因子
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
4.5 知识体系总览
说了这么多,咱们用一张图来总结一下本章的核心逻辑。这张图是我在做项目时自己画的,后来成了团队的标准流程。
这张图看起来简单,但每一步都有坑。数据源选错了,后面再怎么清洗都是白搭。频率没对齐,模型跑出来全是伪相关。异常值处理不当,一个极端值就能把整个模型带偏。缺失值填充太随意,等于给模型喂了毒药。
嗯,这一章的内容就到这里。数据清洗是个细致活,急不得。下一章咱们聊聊因子计算的具体方法,到时候会用到今天清洗好的数据。
公众号:蓝海资料掘金营,微信deep3321