第三章:数据获取与清洗——如何获取宏观数据?

做宏观因子投资,说白了就是跟数据打交道。我见过太多人,策略模型写得天花乱坠,结果一跑回测,数据对不齐,频率不一致,直接崩盘。嗯,今天我们就来聊聊这个最基础、也最容易被忽视的环节——数据获取与清洗。

我个人习惯把数据工作分成三步:拿到数据 → 洗成标准格式 → 对齐到统一频率。每一步都有坑,我们一个一个踩过去。

核心观点:数据质量决定了策略的天花板。模型再牛,也救不了脏数据。

3.1 宏观数据源:FRED、Wind、Tushare

宏观数据从哪里来?我常用的有三个渠道,各有各的脾气。

3.1.1 FRED(美联储经济数据库)

FRED 是全球宏观研究者的首选。它覆盖了美国、欧洲、日本等主要经济体的利率、GDP、CPI、失业率等数据。我个人觉得,做全球宏观策略,FRED 是绕不开的。

获取方式:通过 fredapi 库,或者直接下载 CSV。我建议用 API,方便自动化。

# 安装 fredapi
# pip install fredapi

from fredapi import Fred
fred = Fred(api_key='你的API_KEY')

# 获取美国10年期国债收益率(系列ID: DGS10)
data = fred.get_series('DGS10', start='2000-01-01', end='2023-12-31')
print(data.head())

小技巧:FRED 的系列 ID 可以在官网搜索。比如 GDP 是 'GDPC1',CPI 是 'CPIAUCSL'。我一般会建一个 ID 映射表,方便批量拉取。

3.1.2 Wind(万得)

做中国市场的朋友,Wind 是标配。它覆盖了国内宏观、行业、个股数据。不过 Wind 是收费的,而且 API 接口有点老。

获取方式:通过 Wind 的 Python 接口 WindPy。注意,需要先安装 Wind 客户端。

from WindPy import w
w.start()

# 获取中国CPI月度数据
data = w.edb("M0001396", "2000-01-01", "2023-12-31")
print(data.Data)

注意:Wind 的 EDB 代码(经济数据库代码)需要提前查好。我刚开始用的时候,经常因为代码写错拉不到数据,后来养成了先查代码再写脚本的习惯。

3.1.3 Tushare(开源数据平台)

Tushare 是国内比较良心的开源数据平台,适合个人研究。它提供了宏观经济、股票、基金等数据。不过免费版有调用频率限制。

获取方式:通过 tushare 库,需要注册获取 token。

import tushare as ts
ts.set_token('你的TOKEN')
pro = ts.pro_api()

# 获取中国GDP季度数据
df = pro.cn_gdp(start_q='2000Q1', end_q='2023Q4')
print(df.head())

避坑指南:我曾经因为 Tushare 的免费版调用次数限制,导致回测脚本跑一半报错。后来我改用本地缓存策略,每天只拉一次数据,存成 CSV 或 Parquet 格式,后续直接读取本地文件。

3.2 数据清洗:脏数据怎么处理?

数据拉下来之后,你会发现各种问题:缺失值、异常值、重复值、格式不统一。我总结了一套清洗流程,分享给你。

3.2.1 缺失值处理

宏观数据经常有缺失,比如节假日没有交易数据,或者某些指标发布频率不一致。处理方法有三种:

  • 向前填充(ffill):用上一个有效值填充。适合利率、汇率等连续数据。
  • 向后填充(bfill):用下一个有效值填充。适合某些滞后发布的指标。
  • 插值法:用线性插值或样条插值。适合 GDP、CPI 等平滑数据。
import pandas as pd

# 假设 df 是包含缺失值的 DataFrame
# 向前填充
df.fillna(method='ffill', inplace=True)

# 线性插值
df.interpolate(method='linear', inplace=True)

我的经验:对于宏观因子,我一般优先用向前填充。因为宏观数据发布有滞后性,比如 GDP 数据是季度末之后才公布,用前值填充更符合实际投资场景。

3.2.2 异常值检测

异常值会严重干扰因子计算。我常用的方法是 Z-score 法IQR 法

from scipy import stats
import numpy as np

# Z-score 法:剔除超过3个标准差的点
z_scores = np.abs(stats.zscore(df['value']))
df_clean = df[(z_scores < 3)]

# IQR 法:剔除超出1.5倍四分位距的点
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
df_clean = df[(df['value'] >= Q1 - 1.5*IQR) & (df['value'] <= Q3 + 1.5*IQR)]

注意:不要机械地剔除所有异常值。比如金融危机期间,某些指标会出现极端值,但这些极端值恰恰是策略需要捕捉的信号。我一般会先标记异常值,再结合业务逻辑判断是否剔除。

3.3 数据对齐与频率转换

宏观数据频率五花八门:GDP 是季度数据,CPI 是月度数据,利率是日度数据。做因子模型时,必须把它们对齐到统一频率。

3.3.1 频率转换:从低频到高频

把季度数据转成月度数据,或者把月度数据转成日度数据。常用的方法:

  • 线性插值:假设数据在两个时间点之间线性变化。
  • 填充法:用上一个值填充到下一个值出现之前。
  • 多项式插值:适合更平滑的曲线。
# 将季度数据转换为月度数据(线性插值)
df_quarterly = pd.DataFrame({
    'date': pd.date_range('2020-01-01', periods=4, freq='Q'),
    'gdp': [100, 102, 104, 106]
})
df_quarterly.set_index('date', inplace=True)

# 重采样到月度频率,线性插值
df_monthly = df_quarterly.resample('M').interpolate(method='linear')
print(df_monthly)

3.3.2 频率转换:从高频到低频

把日度数据转成月度或季度数据。常用方法:

  • 取平均值:比如把日度利率转成月度平均利率。
  • 取期末值:比如把日度汇率转成月末汇率。
  • 取累计值:比如把日度收益率转成月度累计收益率。
# 将日度数据转换为月度数据(取月末值)
df_daily = pd.DataFrame({
    'date': pd.date_range('2020-01-01', periods=90, freq='D'),
    'rate': np.random.randn(90)
})
df_daily.set_index('date', inplace=True)

# 取月末值
df_monthly = df_daily.resample('M').last()
print(df_monthly)

避坑指南:我曾经在做因子对齐时,把日度利率直接取平均值转成月度数据,结果发现回测结果跟实际偏差很大。后来我意识到,对于利率这种价格型指标,应该取期末值而不是平均值。你想想看,投资决策是基于最新的利率水平,而不是过去一个月的平均利率。

3.4 实战:构建一个宏观因子数据集

好了,理论讲完了,我们来实战一把。假设我们要构建一个包含美国 GDP、CPI、利率的月度因子数据集。

import pandas as pd
import numpy as np
from fredapi import Fred

# 初始化 FRED
fred = Fred(api_key='你的API_KEY')

# 拉取数据
gdp = fred.get_series('GDPC1', start='2000-01-01', end='2023-12-31')  # 季度数据
cpi = fred.get_series('CPIAUCSL', start='2000-01-01', end='2023-12-31')  # 月度数据
rate = fred.get_series('DGS10', start='2000-01-01', end='2023-12-31')  # 日度数据

# 转换为 DataFrame
df_gdp = pd.DataFrame({'gdp': gdp})
df_cpi = pd.DataFrame({'cpi': cpi})
df_rate = pd.DataFrame({'rate': rate})

# 对齐到月度频率
# GDP:季度转月度,线性插值
df_gdp_monthly = df_gdp.resample('M').interpolate(method='linear')

# CPI:本身就是月度数据,直接使用
df_cpi_monthly = df_cpi.resample('M').last()

# 利率:日度转月度,取月末值
df_rate_monthly = df_rate.resample('M').last()

# 合并
df_factors = pd.concat([df_gdp_monthly, df_cpi_monthly, df_rate_monthly], axis=1)
df_factors.dropna(inplace=True)

print(df_factors.head())

输出结果:

日期GDPCPI利率
2000-01-31100.0168.86.58
2000-02-29100.5169.16.42
2000-03-31101.0169.76.23

嗯,到这里,一个干净的宏观因子数据集就准备好了。你可以直接用它来跑因子模型了。

我的习惯:每次做完数据清洗,我都会画一张图看看数据分布。如果发现某个时间点出现异常跳变,我会回头检查原始数据。这个习惯帮我避免了好几次因为数据错误导致的策略回测偏差。

3.5 本章知识体系图

下面这张图概括了本章的核心逻辑:从数据源到清洗,再到对齐,最后形成可用的因子数据集。

宏观因子数据获取与清洗流程 数据源 FRED / Wind / Tushare 数据清洗 缺失值 / 异常值 / 重复值 频率对齐 日/月/季度 → 统一频率 因子数据集 GDP / CPI / 利率等 检查与验证 可视化 / 统计检验 图:宏观因子数据获取与清洗流程 虚线表示可选的回流检查步骤

这张图展示了从数据源到最终因子数据集的完整路径。我个人觉得,最关键的步骤是频率对齐,因为这一步最容易出错。你想想看,如果 GDP 和利率的频率没对齐,因子模型算出来的 beta 系数就是错的。

总结一下:

  • 数据源:FRED 做全球,Wind 做中国,Tushare 做个人研究。
  • 清洗:缺失值用 ffill,异常值用 Z-score 或 IQR,但要结合业务逻辑。
  • 对齐:低频转高频用插值,高频转低频用期末值或平均值。
  • 验证:每次清洗后画图检查,避免数据错误。

好了,数据准备好了。下一章我们会聊因子计算与回测框架搭建。不过在那之前,我建议你先把今天的内容动手跑一遍。数据工作虽然枯燥,但它是整个策略的基石。嗯,就到这里。


蓝海数据掘金营,专注资料整理