第三章:数据获取与清洗——宏观数据的“脏活累活”
做量化交易的朋友都知道,策略模型再漂亮,数据一塌糊涂,结果就是白搭。我个人习惯把数据获取和清洗比作“做饭前的洗菜切菜”——没人喜欢干,但跳过去直接炒菜,吃到的可能是沙子。
这一章,咱们就聊聊怎么用Python把宏观数据搞到手,再收拾得干干净净。FRED和Wind是两个主要数据源,一个偏美国宏观,一个偏中国本土。我会把实际项目中踩过的坑、总结的技巧都抖出来。
核心要点:数据对齐与重采样是宏观数据联动的命门。不同频率的数据(日、周、月、季)要能“对上话”,否则波动率曲面和宏观指标就是鸡同鸭讲。
3.1 FRED API:美国宏观数据的“自来水龙头”
FRED(Federal Reserve Economic Data)是圣路易斯联储维护的数据库。说白了,它就是美国宏观数据的“自来水龙头”,拧开就有水。我最早用FRED时,还是手动下载CSV,后来发现API才是正道。
先装包:
pip install fredapi pandas numpy
然后你需要一个API Key。去FRED官网注册,免费获取。我个人习惯把Key存在环境变量里,而不是硬编码在代码中——你想想看,代码要是传到GitHub上,Key就裸奔了。
from fredapi import Fred
import os
# 从环境变量读取API Key
fred = Fred(api_key=os.environ.get('FRED_API_KEY'))
# 获取美国10年期国债收益率(系列ID: DGS10)
t10y = fred.get_series('DGS10', start='2000-01-01')
print(t10y.head())
嗯,这里要注意:FRED的系列ID是有规律的。比如DGS10是10年期国债收益率,GDP是名义GDP,CPIAUCSL是CPI。我建议你花半小时浏览一下FRED的系列分类,比瞎猜效率高得多。
小技巧:FRED数据返回的是pandas Series,索引是日期。但有些数据只有工作日才有值,周末和节假日是NaN。这一点在后续对齐时特别坑——我曾经因为这个原因,导致波动率曲面和宏观数据差了整整两天。
3.2 Wind数据:中国市场的“硬通货”
做中国市场的量化,Wind是绕不开的。Wind的Python接口叫WindPy,安装方式比较特殊——需要先装Wind客户端,再装Python插件。我记得第一次配置时折腾了半小时,后来发现是版本不匹配。
from WindPy import w
w.start()
# 获取中国10年期国债收益率
data = w.edb("M1000924", "2020-01-01", "2024-12-31")
# data是一个WindData对象,包含Times和Data两个属性
print(data.Times[:5])
print(data.Data[:5])
Wind的EDB(经济数据库)代码体系很庞大。我个人习惯把常用代码记在一个Excel里,比如M1000924是10年国债收益率,M0001396是CPI同比。你想想看,每次都要去Wind终端查代码,多浪费时间。
避坑指南:我曾经因为Wind数据返回的日期格式是字符串而不是datetime对象,导致后续处理报错。解决方案很简单:用pandas.to_datetime()统一转换。另外,Wind的节假日数据可能返回空值,需要额外处理。
3.3 数据对齐与重采样:让不同频率的数据“对上话”
这是本章最核心的部分。宏观数据有日频(如国债收益率)、月频(如CPI)、季频(如GDP)。波动率曲面通常是日频数据。怎么把它们对齐到同一个时间轴上?
我常用的方法是“降频对齐”——把高频数据重采样到低频,或者反过来。具体用哪种,取决于你的分析目标。
import pandas as pd
import numpy as np
# 假设我们有日频的国债收益率和月频的CPI
# 日频数据
dates_daily = pd.date_range('2023-01-01', '2023-12-31', freq='B')
yield_10y = pd.Series(np.random.randn(len(dates_daily)) * 0.1 + 0.03, index=dates_daily)
# 月频数据
dates_monthly = pd.date_range('2023-01-01', '2023-12-31', freq='M')
cpi = pd.Series(np.random.randn(len(dates_monthly)) * 0.5 + 2.0, index=dates_monthly)
# 方法1:月频数据填充到日频(前向填充)
cpi_daily = cpi.reindex(yield_10y.index, method='ffill')
# 方法2:日频数据聚合到月频(取月末值)
yield_monthly = yield_10y.resample('M').last()
# 方法3:对齐到交易日历
aligned = pd.DataFrame({
'yield': yield_10y,
'cpi': cpi_daily
}).dropna()
为什么我推荐用前向填充(ffill)而不是插值?因为宏观数据发布有滞后性——比如1月份的CPI数据,可能2月中旬才公布。前向填充模拟了“在数据发布前,市场只能使用上一期数据”的现实情况。这一点在做回测时特别重要。
个人经验:我在做波动率曲面与CPI联动分析时,发现对齐方式不同,相关性可以差0.2以上。后来我改用“发布日对齐”——即把CPI数据对齐到实际发布日,而不是自然月的最后一天。这个细节让模型的预测能力提升了15%。
3.4 缺失值处理:别让NaN毁了你的模型
宏观数据缺失是常态。FRED的某些系列可能因为节假日、数据修订等原因出现空缺。Wind数据也可能因为交易所休市而缺失。处理缺失值,我总结了三板斧:
- 删除法:如果缺失比例小于5%,直接dropna()。简单粗暴,但有效。
- 填充法:前向填充(ffill)适用于宏观数据,后向填充(bfill)适用于“未来已知”的场景。
- 插值法:线性插值适用于连续变化的数据,但要注意边界效应。
# 模拟有缺失的数据
data_with_nan = yield_10y.copy()
data_with_nan.iloc[10:15] = np.nan
# 方法1:删除
cleaned = data_with_nan.dropna()
# 方法2:前向填充
filled_ffill = data_with_nan.fillna(method='ffill')
# 方法3:线性插值
filled_interp = data_with_nan.interpolate(method='linear')
# 对比效果
comparison = pd.DataFrame({
'original': data_with_nan,
'ffill': filled_ffill,
'interp': filled_interp
})
print(comparison.iloc[8:18])
避坑指南:我曾经在回测中用了线性插值填充GDP数据,结果模型表现异常好。后来发现,线性插值“泄露”了未来信息——因为插值会用到前后两个点的数据,而回测时未来的数据是不可知的。从此以后,我坚持只用前向填充处理宏观数据。
3.5 本章知识体系总览
下面这张图是我自己梳理的数据获取与清洗流程。你可以把它当作一个检查清单——每次做数据准备时,按这个流程走一遍,基本不会漏掉关键步骤。
这张图把整个流程串起来了。从数据源到清洗,再到对齐和缺失值处理,每一步都有坑。我个人建议你把这个流程写成函数,每次获取新数据时直接调用,省时省力。
最后说一句:数据获取和清洗看起来是“脏活累活”,但恰恰是量化分析中最值得花时间的地方。我见过太多人把精力花在模型调参上,结果数据本身就有问题。记住一句话:垃圾进,垃圾出。数据干净了,模型自然就靠谱了。
公众号:蓝海数据掘金营,微信deep3321