第二章:数据获取与清洗——用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 知识体系总览
下面这张图是我自己总结的数据获取与清洗流程,你可以把它当作一个检查清单:
2.5 实战小贴士
- 数据源选择: 雅虎财经适合快速原型,但生产环境建议用Wind或Tushare。免费数据源偶尔会断,我吃过这个亏。
- 数据缓存: 每次回测都去拉数据太慢了。我习惯把原始数据存成CSV或Parquet格式,下次直接读本地文件。
- 交易日历: 不同市场的交易日历不一样。A股有春节、国庆,美股有感恩节、圣诞节。建议维护一个统一的交易日历表。
- 复权处理: 股票数据一定要用后复权。前复权会改变历史价格,导致回测结果失真。这个坑我踩过不止一次。
我的习惯: 每次拿到新数据,先画个图看看。肉眼扫一遍,比任何统计检验都管用。如果看到价格曲线有奇怪的跳空或者平台,那八成是数据有问题。
好了,数据获取和清洗就聊到这儿。记住一句话:数据干净了,回测就成功了一半。下一章我们会用这些干净的数据,开始真正的回测实战。
公众号:蓝海资料掘金营,微信deep3321