第一章:数据获取与清洗——量化交易的基石
做统计套利,说白了就是找两个品种之间的价差规律。但有个前提——你得有干净的数据。我见过太多人策略写得漂亮,一上实盘就崩,十有八九是数据出了问题。这一章,咱们就把数据获取和清洗这件事彻底聊透。
1.1 数据源选择:你的原材料从哪里来?
数据源选不对,后面全白费。我个人习惯把数据源分成两类:交易所直连和第三方数据商。各有各的脾气。
交易所API
直接连交易所,数据最原始、延迟最低。适合做高频或者T+0策略。但坑也不少——
- 限流问题:交易所不是你家开的,每秒请求次数有限。我曾经在币安上同时跑5个策略,结果被限流了半小时,那叫一个惨。
- 数据格式不统一:不同交易所的K线字段名都不一样。有的叫
open,有的叫o,你得自己映射。 - 断线重连:WebSocket断了怎么办?我建议写个心跳检测,5秒没收到数据就自动重连。
我的经验:做统计套利,至少要有两个交易所的数据。因为价差策略本质上是吃两个市场之间的定价偏差,单市场数据不够用。
第三方数据商
如果你不想折腾交易所API,找第三方数据商是省心选择。国内常用的有万得、聚宽、Tushare,国外有Quandl、Alpha Vantage。
| 数据商 | 优点 | 缺点 |
|---|---|---|
| 万得 | 数据全,A股覆盖好 | 贵,一年几万块 |
| 聚宽 | 免费,有回测平台 | 数据延迟高,不适合高频 |
| Tushare | 开源,社区活跃 | 需要积分,部分数据收费 |
| Quandl | 全球数据,质量高 | 部分数据要付费 |
小建议:刚开始做策略,先用免费数据跑通逻辑。等要实盘了,再考虑付费数据。别一上来就花冤枉钱。
1.2 数据清洗流程:脏数据是策略的杀手
数据拿到手,第一件事不是分析,是清洗。我见过有人用未清洗的数据跑回测,年化收益率50%,实盘直接亏20%。为什么?因为数据里有未来函数和幸存者偏差。
去重
同一个时间戳出现两条数据,怎么办?我的做法是:
- 先按时间戳排序
- 用
drop_duplicates()去重 - 保留最后一条(因为可能是修正数据)
import pandas as pd
# 假设df是原始数据
df = df.sort_values('timestamp')
df = df.drop_duplicates(subset='timestamp', keep='last')
对齐
统计套利最怕时间戳对不上。比如A品种是1分钟K线,B品种是5分钟K线,你怎么算价差?
我习惯的做法是:
- 统一用高频数据的时间戳作为基准
- 低频数据向前填充(forward fill)
- 或者用
resample重采样到同一频率
# 将两个品种对齐到1分钟
df_a = df_a.resample('1T').last()
df_b = df_b.resample('1T').last()
# 合并
df = pd.concat([df_a, df_b], axis=1)
注意:对齐时千万别用向后填充(backfill),那会引入未来数据。我曾经因为这个坑,回测曲线漂亮得不像话,实盘直接打脸。
填充缺失值
数据缺失是常态。比如节假日、交易所故障、网络波动。怎么处理?
- 线性插值:适合短期缺失,比如缺了1-2根K线
- 向前填充:适合非交易时段,比如夜盘休市
- 直接删除:如果缺失太多,比如连续缺了10根K线,我建议直接删掉那一段
# 线性插值
df = df.interpolate(method='linear')
# 向前填充
df = df.fillna(method='ffill')
# 删除连续缺失超过5行的数据
df = df.dropna(thresh=5)
1.3 数据存储方案:存得好才能用得爽
数据清洗完了,得存起来。选什么格式?我根据场景来定。
CSV:简单但慢
适合小数据量,比如几千条K线。优点是通用,Excel都能打开。缺点是:
- 读写慢,尤其是几百万条数据
- 不支持索引,每次都要重新加载
- 容易损坏,断电就完蛋
HDF5:快但复杂
适合大数据量,比如全市场Tick数据。我常用pandas.HDFStore来存:
import pandas as pd
# 写入
store = pd.HDFStore('data.h5')
store['btc_usdt'] = df
store.close()
# 读取
store = pd.HDFStore('data.h5')
df = store['btc_usdt']
store.close()
我的习惯:HDF5支持压缩,能省不少磁盘空间。我存1年的1分钟K线数据,CSV要2GB,HDF5只要500MB。
数据库:最靠谱的选择
如果你要实盘,我强烈建议用数据库。MySQL、PostgreSQL、InfluxDB都行。好处是:
- 支持并发读写
- 数据不会损坏
- 可以按时间范围查询
import sqlalchemy as sa
# 连接数据库
engine = sa.create_engine('postgresql://user:pass@localhost:5432/trading')
# 写入
df.to_sql('btc_usdt', engine, if_exists='replace')
# 读取
df = pd.read_sql('SELECT * FROM btc_usdt WHERE timestamp > "2024-01-01"', engine)
避坑指南:我曾经用SQLite存数据,结果并发写入时直接锁死。实盘系统千万别用SQLite,至少上PostgreSQL。
知识体系总览
下面这张图,把数据获取与清洗的整个流程串起来了。你照着这个框架走,基本不会出大问题。
嗯,数据获取与清洗这部分,说白了就是打地基。地基不稳,楼盖得再高也得塌。我见过太多人在这上面栽跟头,所以这一章我讲得特别细。你照着做,至少能避开80%的坑。
记住一句话:数据质量决定策略上限。你的策略再牛,数据是脏的,结果就是垃圾进垃圾出。