第二章:数据获取与清洗——用pandas-datareader搞定多资产历史数据

做量化回测,第一步就是搞数据。没有数据,再牛的策略也是空中楼阁。

我个人习惯把数据获取比作「做饭前的买菜」。菜不新鲜,厨艺再好也白搭。今天我们就聊聊怎么用Python把股票、债券、商品的历史数据「买」回来,再「洗」干净。

2.1 为什么数据获取是回测的命门?

你想想看,回测的本质是什么?是用历史数据模拟未来。如果历史数据本身就有问题——比如缺失、错位、或者时间对不齐——那回测结果就是垃圾进垃圾出。

我在项目中遇到过最坑的一次:用某免费接口拿到的债券数据,居然有两天是重复的。回测出来的夏普比率高达3.5,我当时还兴奋了半天。后来发现是数据重复导致收益率虚高。嗯,从那以后我再也不敢轻视数据清洗了。

核心原则: 数据质量 > 策略复杂度。一个简单的策略配上干净的数据,往往比一个复杂的策略配上脏数据更靠谱。

2.2 用pandas-datareader获取多资产数据

pandas-datareader 是获取金融数据的老牌工具。虽然现在有些数据源需要API Key了,但基本的用法还是值得掌握的。

2.2.1 安装与导入

# 安装
pip install pandas-datareader

# 导入
import pandas as pd
import pandas_datareader.data as web
import datetime

2.2.2 获取股票数据(以沪深300为例)

# 获取沪深300指数历史数据
start = datetime.datetime(2020, 1, 1)
end = datetime.datetime(2023, 12, 31)

# 注意:这里用的是雅虎财经的代码
hs300 = web.DataReader('000300.SS', 'yahoo', start, end)
print(hs300.head())
小提示: 雅虎财经的代码规则是:A股代码后面加.SS(上海)或.SZ(深圳)。比如贵州茅台是600519.SS,宁德时代是300750.SZ。

2.2.3 获取债券数据(以国债为例)

债券数据相对难拿一些。我一般用FRED(美联储经济数据库)来获取国债收益率。

# 获取10年期国债收益率
# FRED代码:DGS10
treasury_10y = web.DataReader('DGS10', 'fred', start, end)
print(treasury_10y.head())

说白了,FRED的数据质量很高,但有个问题——它只提供收益率,不提供价格。如果你需要债券价格,得自己算。

2.2.4 获取商品数据(以黄金为例)

# 获取黄金期货数据
# 雅虎财经代码:GC=F
gold = web.DataReader('GC=F', 'yahoo', start, end)
print(gold.head())
注意: 商品期货数据有交割日的问题。临近交割时,主力合约会切换。如果你直接拿连续合约数据,可能会遇到价格跳空。我曾经因为这个原因,回测出来的商品策略收益虚高了20%。

2.3 数据清洗:处理缺失值与对齐时间序列

数据拿到手了,但往往不是直接能用的。三个常见问题:缺失值、时间不对齐、数据格式不一致。

2.3.1 检查缺失值

# 检查缺失值
print(hs300.isnull().sum())

# 查看缺失值的位置
missing_dates = hs300[hs300['Close'].isnull()].index
print(f"缺失日期:{missing_dates}")

2.3.2 处理缺失值的几种方式

方法 适用场景 代码示例
向前填充 非交易日导致的缺失 df.fillna(method='ffill')
向后填充 数据延迟发布 df.fillna(method='bfill')
插值法 少量缺失且趋势明显 df.interpolate()
删除缺失行 缺失比例很小 df.dropna()

我个人习惯用向前填充。为什么?因为金融数据通常是「昨天收盘价延续到今天开盘」的逻辑。非交易日没有交易,价格沿用上一个交易日的数据,这很合理。

2.3.3 对齐多资产时间序列

这是最容易被忽视的一步。股票、债券、商品的交易日历不一样。比如A股有春节长假,美国债券市场有感恩节,商品期货还有夜盘。

# 合并多资产数据
# 假设我们有三个DataFrame:hs300, treasury_10y, gold
# 只保留三个市场都有交易的日子

# 方法1:内连接(只保留共同交易日)
combined = pd.concat({
    'stock': hs300['Close'],
    'bond': treasury_10y['Close'],
    'gold': gold['Close']
}, axis=1).dropna()

print(combined.head())

# 方法2:外连接(保留所有交易日,缺失值填充)
combined_all = pd.concat({
    'stock': hs300['Close'],
    'bond': treasury_10y['Close'],
    'gold': gold['Close']
}, axis=1).fillna(method='ffill')
关键点: 多资产回测时,一定要用内连接。为什么?因为如果某一天某个资产没有交易数据,你强行用填充数据去算收益率,会引入未来信息。这在回测中是致命的。

2.4 知识体系总览

下面这张图是我自己总结的数据获取与清洗流程,你可以把它当作一个检查清单:

多资产数据获取与清洗流程 数据获取 pandas-datareader 数据检查 缺失值、异常值 数据清洗 填充/删除 时间对齐 内连接/外连接 ✅ 干净的多资产时间序列

2.5 实战小贴士

  • 数据源选择: 雅虎财经适合快速原型,但生产环境建议用Wind或Tushare。免费数据源偶尔会断,我吃过这个亏。
  • 数据缓存: 每次回测都去拉数据太慢了。我习惯把原始数据存成CSV或Parquet格式,下次直接读本地文件。
  • 交易日历: 不同市场的交易日历不一样。A股有春节、国庆,美股有感恩节、圣诞节。建议维护一个统一的交易日历表。
  • 复权处理: 股票数据一定要用后复权。前复权会改变历史价格,导致回测结果失真。这个坑我踩过不止一次。
我的习惯: 每次拿到新数据,先画个图看看。肉眼扫一遍,比任何统计检验都管用。如果看到价格曲线有奇怪的跳空或者平台,那八成是数据有问题。

好了,数据获取和清洗就聊到这儿。记住一句话:数据干净了,回测就成功了一半。下一章我们会用这些干净的数据,开始真正的回测实战。


公众号:蓝海资料掘金营,微信deep3321