第一章:数据获取与清洗——打好量化分析的地基
各位同学,欢迎来到《盘口能量分布量化分析》的第一课。
做量化交易,尤其是盘口分析,最怕什么?
我个人觉得,最怕数据不准。数据不准,后面所有模型都是空中楼阁。我见过太多人,模型写得花里胡哨,结果一跑,发现是数据源出了问题,白白浪费几个星期。
所以,这一章我们先把地基打牢。说白了,就是三件事:拿到数据、洗干净数据、存好数据。
核心观点:数据质量决定了量化策略的上限。Level-2行情数据颗粒度细,但噪声也多。清洗这一步,省不了。
1.1 获取Level-2行情数据
Level-2数据,说白了就是比普通行情多一层的“内幕”。普通行情只能看到五档买卖盘,Level-2能看到十档,还能看到逐笔成交和逐笔委托。这对我们做盘口能量分布分析来说,简直是命根子。
获取方式主要有两种:
- 直接对接数据商:比如万得、聚宽、米筐、恒生等。优点是稳定,缺点是贵。我早期做私募的时候,一年光数据费就几十万。
- 使用开源库或券商API:比如
tushare、akshare、baostock。免费或低价,适合个人研究。但要注意,免费数据可能有延迟或缺失。
我个人习惯用 akshare 做快速原型验证。它接口简单,覆盖广。下面是一个获取某只股票Level-2逐笔成交数据的例子:
import akshare as ak
import pandas as pd
# 获取逐笔成交数据(示例:平安银行,2024-01-15)
df_tick = ak.stock_individual_tick_em(
symbol="000001",
start_date="2024-01-15",
end_date="2024-01-15"
)
print(df_tick.head())
print(f"数据行数: {len(df_tick)}")
小提示:Level-2数据量很大。一只股票一天可能有几万笔逐笔成交。如果你同时监控几百只股票,内存会爆炸。我建议先只关注你策略池里的核心标的。
1.2 数据清洗——去重与缺失值处理
数据拿到手,别急着分析。先看看有没有脏数据。
我遇到过最坑的一次:某数据商在行情波动剧烈时,重复推送了同一笔成交记录。结果我的策略以为成交量突然放大,开仓了……嗯,亏了不少。
去重是第一步。Level-2数据通常有唯一标识,比如 trade_id 或 seq(序列号)。直接用 drop_duplicates() 干掉重复行。
# 去重
df_tick_clean = df_tick.drop_duplicates(subset=['trade_id'])
print(f"去重后行数: {len(df_tick_clean)}")
缺失值处理是第二步。盘口数据中,缺失值通常出现在某些字段(比如 ask_price 在无挂单时为空)。处理方法要看情况:
- 直接删除:如果缺失比例很小(比如 < 1%),直接删掉最省事。
- 向前填充:对于价格类数据,用前一个有效值填充。因为价格变化是连续的,这个做法比较合理。
- 插值法:对于成交量等指标,可以用线性插值。但要注意,盘口数据波动大,插值可能引入偏差。
# 检查缺失值
print(df_tick_clean.isnull().sum())
# 向前填充价格字段
df_tick_clean['price'] = df_tick_clean['price'].ffill()
# 删除成交量缺失的行(如果很少)
df_tick_clean = df_tick_clean.dropna(subset=['volume'])
警告:千万不要对盘口数据做全局均值填充。盘口数据是非平稳的,均值填充会严重扭曲分布特征。我曾经见过有人这么干,结果策略回测曲线漂亮得不行,实盘一跑就崩。
1.3 数据标准化
为什么要标准化?你想想看,价格是几十块,成交量是几万手,量纲完全不一样。如果你直接拿原始数据做聚类或回归,价格会主导一切,成交量根本没话语权。
常用的标准化方法有两种:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score 标准化 | (x - μ) / σ | 数据近似正态分布,或需要保留异常值信息 |
| Min-Max 归一化 | (x - min) / (max - min) | 数据有明确上下界,或需要映射到 [0,1] 区间 |
我个人习惯在盘口能量分析中用 Z-score。因为盘口数据经常有极端值(比如瞬间大单),Z-score 能保留这些极端值的信息,而 Min-Max 会把它们压缩到很窄的区间。
from sklearn.preprocessing import StandardScaler
# 选择需要标准化的列
cols_to_scale = ['price', 'volume', 'bid_volume', 'ask_volume']
scaler = StandardScaler()
df_tick_clean[cols_to_scale] = scaler.fit_transform(df_tick_clean[cols_to_scale])
print(df_tick_clean[cols_to_scale].describe())
小提示:标准化时,记得用 fit_transform 在训练集上拟合,然后用 transform 在测试集上转换。不要在整个数据集上一次性做,否则会引入未来信息。
1.4 数据存储——CSV vs HDF5
数据洗干净了,得存起来。存哪里?
CSV 是最简单的格式。人类可读,Excel 能打开。但缺点也很明显:
- 文件大(文本存储,冗余多)
- 读写慢(尤其几百万行时)
- 不支持数据类型(所有数字都存成字符串)
HDF5 是二进制格式,专门为大规模科学计算设计。优点:
- 压缩率高(文件体积小很多)
- 读写快(支持索引和分块读取)
- 支持复杂数据结构(多维数组、分组)
我个人的经验是:日常探索用 CSV,正式回测和实盘用 HDF5。CSV 方便肉眼检查,HDF5 性能好。
# 存储为 CSV
df_tick_clean.to_csv('level2_tick_20240115.csv', index=False)
# 存储为 HDF5
df_tick_clean.to_hdf('level2_tick_20240115.h5', key='tick_data', mode='w', complevel=5)
关键点:HDF5 的 complevel 参数控制压缩级别(0-9)。级别越高,文件越小,但写入越慢。我一般设 5,平衡速度和体积。
1.5 避坑指南——我踩过的那些坑
做数据清洗这么多年,有些坑真的是刻骨铭心。分享几个给你:
- 时间戳对齐:不同数据源的时间戳精度可能不同(秒级 vs 毫秒级)。合并时一定要先统一精度,否则会错位。
- 除权除息:股票分红送股后,价格会跳空。如果你不做复权处理,回测结果会失真。Level-2 数据通常提供复权因子,记得用上。
- 盘口数据的时间切片:Level-2 数据是事件驱动的,不是等间隔的。如果你要做时间序列分析,需要先重采样到固定频率(比如 1 秒或 1 分钟)。
嗯,数据获取与清洗这部分,看起来基础,但真的决定了你后面所有工作的成败。别嫌麻烦,把这一步做扎实了,后面的分析会顺畅很多。
好了,这一章就到这里。记住:数据干净,策略才干净。