3. 数据获取与预处理:宏观经济数据源与清洗对齐
做宏观因子归因,说白了就是跟数据打交道。我入行那会儿,最头疼的不是模型怎么搭,而是数据从哪来、怎么洗干净。你想想看,模型再漂亮,数据是脏的,结果就是一堆垃圾。
这一章,咱们就聊聊宏观经济数据的获取和预处理。我会把常用的数据源、清洗技巧、频率转换这些硬骨头,一个一个啃下来。
3.1 宏观经济数据源:Wind、CEIC、FRED
宏观数据源,我主要用三个:Wind、CEIC、FRED。各有各的脾气,也各有各的拿手好戏。
3.1.1 Wind(万得)
Wind 是国内做量化绕不开的坎。我个人习惯用它来拉A股相关的宏观数据,比如CPI、PPI、工业增加值、社融数据。它的API接口挺全的,Python 和 R 都能直接调。
举个例子,用 Python 从 Wind 拉数据:
from WindPy import w
w.start()
# 拉取CPI月度数据
cpi_data = w.edb("M0001396", "2010-01-01", "2024-12-31", "Fill=Previous")
print(cpi_data.Data)
这里有个坑:Wind 的代码体系比较乱,同一个指标可能有多个代码。我建议你建一个自己的代码映射表,省得每次都要查。
3.1.2 CEIC
CEIC 的优势在于全球宏观数据覆盖广。我做跨境策略时,经常用它拉新兴市场的GDP、汇率、利率数据。它的数据质量很高,但价格也不便宜。
CEIC 的数据通常以 Excel 或 CSV 格式导出。我一般会写个脚本批量导入,避免手动操作出错。
3.1.3 FRED(Federal Reserve Economic Data)
FRED 是美联储的免费数据源,做海外宏观必备。它的API是开放的,用起来很方便。
import pandas as pd
import pandas_datareader.data as web
from datetime import datetime
# 拉取美国GDP数据
gdp = web.DataReader('GDP', 'fred', start='2010-01-01', end='2024-12-31')
print(gdp.head())
FRED 的数据频率很杂,有日度的、周度的、月度的、季度的。嗯,这里要注意,不同频率的数据对齐是个大问题,后面我会细说。
3.2 数据清洗与对齐
数据拿到手,第一件事不是建模,而是清洗。我见过太多人跳过这步,结果模型跑出来全是噪音。
3.2.1 常见数据问题
- 缺失值: 宏观数据常有缺失,比如节假日、统计口径调整。
- 异常值: 比如某个月CPI突然跳升,可能是数据录入错误。
- 单位不一致: 有的数据是百分比,有的是绝对值,得统一。
- 时间戳错位: 比如数据发布时间和实际时间对不上。
3.2.2 清洗实战
我一般用 pandas 做清洗,流程如下:
import pandas as pd
import numpy as np
# 假设df是原始数据
df = pd.read_csv('macro_data.csv', index_col='date', parse_dates=True)
# 1. 处理缺失值:前向填充
df.fillna(method='ffill', inplace=True)
# 2. 检测异常值:用3倍标准差法
for col in df.columns:
mean = df[col].mean()
std = df[col].std()
df[col] = np.where(np.abs(df[col] - mean) > 3*std, np.nan, df[col])
# 3. 再次填充异常值
df.fillna(method='ffill', inplace=True)
# 4. 统一单位:假设所有数据转为百分比
df = df / 100
3.2.3 数据对齐
不同数据源的时间戳可能不一样。比如Wind的数据是月末,FRED的数据是月初。对齐的方法很简单:统一到一个时间基准。
我常用的对齐策略:
- 确定基准频率(比如月度)。
- 将所有数据重采样到该频率。
- 用最接近的时间点填充。
# 对齐到月末
df_aligned = df.resample('M').last()
3.3 频率转换:日/周/月/季
频率转换是宏观因子归因里最绕的一环。你想想看,GDP是季度数据,CPI是月度数据,利率可能是日度数据。怎么把它们放到一个模型里?
3.3.1 降频(高频转低频)
降频相对简单。日度转月度,可以用月末值、平均值或中位数。我个人习惯用月末值,因为宏观数据通常反映的是时点状态。
# 日度转月度:取月末值
monthly = daily.resample('M').last()
# 日度转季度:取季末值
quarterly = daily.resample('Q').last()
3.3.2 升频(低频转高频)
升频就麻烦多了。季度数据转月度,不能简单填充,否则会引入伪模式。
我常用的方法:
- 线性插值: 假设数据在季度内线性变化。
- 样条插值: 更平滑,适合趋势明显的指标。
- 前向填充: 简单粗暴,但会丢失信息。
# 季度转月度:线性插值
monthly = quarterly.resample('M').interpolate(method='linear')
# 季度转月度:样条插值
monthly_spline = quarterly.resample('M').interpolate(method='spline', order=2)
3.3.3 频率转换的避坑指南
我曾经在做一个多因子模型时,把季度GDP数据直接填充到月度,然后用月度数据跑回归。结果R²高得离谱,但回测一塌糊涂。后来发现,填充后的数据引入了严重的自相关,模型过拟合了。
所以,频率转换时记住三点:
- 不要过度填充: 低频数据转高频,信息量不会增加。
- 保留原始频率: 建模时尽量用原始频率,避免人为制造数据。
- 使用混合频率模型: 如果必须用不同频率,考虑MIDAS(混合数据抽样)模型。
3.4 本章知识体系
下面这张图,是我自己梳理的数据获取与预处理流程。你看一眼,心里就有谱了。
这张图把整个流程串起来了。从数据源到清洗,再到频率转换,最后输出干净的数据集。每一步都不能省,省了就是给自己挖坑。
好了,数据获取和预处理就聊到这儿。下一章咱们会深入因子构建,到时候这些数据就能派上用场了。