第二讲:数据准备——获取历史行情数据(OHLCV),数据清洗与对齐
做量化交易,第一步就是搞数据。
很多人觉得这事儿简单,不就是从交易所拉个CSV嘛。我刚开始也这么想,结果第一次跑回测,发现策略收益高得离谱——后来一查,原来是数据里混进了未来信息。嗯,那感觉就像考试抄到了答案,结果发现答案是下一场的。
所以这一讲,咱们把数据准备这件事彻底聊透。
2.1 OHLCV数据到底是什么
OHLCV是金融市场的“通用语言”。
- O(Open):开盘价
- H(High):最高价
- L(Low):最低价
- C(Close):收盘价
- V(Volume):成交量
说白了,每一根K线就是这五个数字。我习惯把OHLCV叫做“五元组”,因为所有技术指标、信号生成,最终都从这五个数里算出来。
核心原则: 数据质量决定策略上限。再牛的算法,喂进去垃圾数据,出来的也是垃圾信号。
2.2 获取数据的常见渠道
我个人常用的数据源有这几个:
| 数据源 | 适用市场 | 特点 |
|---|---|---|
| Yahoo Finance (yfinance) | 美股、港股 | 免费、易用,但精度一般 |
| Binance API | 加密货币 | 毫秒级精度,适合高频 |
| TuShare | A股 | 国内最全,需注册 |
| 本地CSV/数据库 | 任意 | 自己维护,最可控 |
举个例子,用yfinance拉取苹果股票数据:
import yfinance as yf
# 获取苹果2023年全年日线数据
aapl = yf.download('AAPL', start='2023-01-01', end='2023-12-31')
print(aapl.head())
输出长这样:
Open High Low Close Volume
Date
2023-01-03 129.93 131.25 124.17 125.07 74629500
2023-01-04 126.89 128.66 125.08 126.36 58366400
...
小技巧: 我建议第一次拉数据时,先拉最近10条看看格式。很多坑在数据格式上——比如日期列是不是datetime类型,成交量是不是整数。
2.3 数据清洗——别让脏数据毁了你的策略
数据拉下来,不等于能用。我遇到过最离谱的一次,某交易所的BTC数据里,某天最高价比最低价还低——这明显是数据录入错误。
清洗步骤一般包括:
- 检查缺失值:用
isnull().sum()看看有没有NaN - 处理异常值:比如价格突然跳变100倍,多半是数据错误
- 去除重复行:同一时间戳出现两次,取最后一次
- 排序:按时间升序排列,这是基本要求
代码示例:
# 清洗流程
df = df.sort_index() # 按时间排序
df = df[~df.index.duplicated(keep='last')] # 去重
df = df.dropna() # 删除缺失值
# 检查异常:价格不能为负
assert (df['Close'] > 0).all(), "存在负价格!"
注意: 千万不要用
fillna(method='ffill')填充价格数据!我曾经这么干过,结果把停牌期间的数据全用前一天的填充了,回测时策略在停牌期间“神奇地”赚了钱——其实是数据造假。
2.4 数据对齐——多品种交易的命门
如果你只交易一个品种,对齐问题不大。但做多品种策略时,对齐就是噩梦。
举个例子:你想同时交易BTC和ETH,它们的时间戳可能不完全一致。BTC在12:00:01有数据,ETH在12:00:02才有。直接合并的话,会出现错位。
对齐的核心思路:统一时间轴。
我常用的方法:
# 假设btc_df和eth_df都是按时间索引的DataFrame
# 使用merge,以BTC的时间轴为准
merged = pd.merge(btc_df, eth_df, left_index=True, right_index=True, how='inner', suffixes=('_btc', '_eth'))
# 或者用reindex统一到整点分钟
time_index = pd.date_range(start='2023-01-01', end='2023-12-31', freq='1min')
btc_aligned = btc_df.reindex(time_index, method='ffill')
eth_aligned = eth_df.reindex(time_index, method='ffill')
关键点: 对齐时用
how='inner'还是how='outer'?我建议用inner,只保留所有品种都有数据的时刻。用outer的话,某个品种缺失数据时,你可能会用前向填充引入偏差。
2.5 知识体系总览
下面这张图,把数据准备的整个流程串起来了:
2.6 避坑指南
最后,分享几个我踩过的坑:
- 时间戳时区问题:交易所给的是UTC,你本地是UTC+8,直接合并会错位。我习惯统一转成UTC再处理。
- 除权除息:股票数据有复权问题。用前复权还是后复权?我建议用前复权,因为回测时更接近真实交易场景。
- 数据量太大:分钟级数据一年就有几十万行。别一次性全加载到内存,用
chunksize分批读取。
我的习惯: 每次拉完数据,先画个收盘价折线图。肉眼扫一遍,有没有异常跳变、有没有缺失区间,一眼就能看出来。这比任何自动化检查都靠谱。
好了,数据准备这块就聊到这儿。数据干净了,后面算信号才有底气。下一讲咱们开始真正搞信号生成算法——到时候你会发现,数据准备花的时间,绝对值得。