数据准备:高频数据获取与清洗
做流动性分析,数据是地基。地基不稳,楼盖得再漂亮也得塌。
我个人习惯把数据准备分成三大块:获取、清洗、存储。这三块环环相扣,哪一步出问题,后面的分析全白搭。今天咱们就聊聊,怎么把这套流程跑通。
核心观点:高频数据的质量,直接决定流动性指标的可靠性。宁可花80%的时间在数据准备上,也别急着跑模型。
一、高频数据获取:Tick级与分钟级
先说说数据源。国内常用的有Wind、聚宽、Tushare,国外有Quandl、Polygon。我建议你至少准备两个数据源做交叉验证。
1. Tick级数据
Tick数据就是每一笔成交的记录。包含时间戳、价格、成交量、买卖方向。这东西有多大?
举个例子:沪深300成分股,一天下来大概产生500万到1000万条Tick记录。一个月就是2个亿的量级。嗯,存储压力不小。
# 获取Tick数据的伪代码示例
def fetch_tick_data(symbol, date):
"""
从交易所获取Tick级数据
"""
# 连接数据源
conn = create_connection(source='wind')
# 获取原始Tick数据
raw_ticks = conn.query(f"""
SELECT time, price, volume, side
FROM tick_table
WHERE symbol = '{symbol}'
AND trade_date = '{date}'
ORDER BY time
""")
return raw_ticks
我的经验:获取Tick数据时,一定要注意时间戳的精度。有些数据源给的是毫秒级,有些是微秒级。我遇到过数据源混用精度的情况,结果算出来的流动性指标全是错的。
2. 分钟级数据
分钟级数据其实是Tick数据的聚合。把1分钟内的所有Tick按时间、价格、成交量汇总。常用的有1分钟、5分钟、15分钟、30分钟。
# Tick转分钟数据的聚合逻辑
def aggregate_to_minute(ticks):
"""
将Tick数据聚合成分钟级数据
"""
# 按分钟分组
ticks['minute'] = ticks['time'].dt.floor('1min')
# 聚合计算
minute_data = ticks.groupby('minute').agg({
'price': ['first', 'last', 'max', 'min', 'mean'],
'volume': 'sum',
'side': lambda x: (x == 'buy').sum() # 买方成交笔数
})
return minute_data
为什么要两种粒度都保留?说白了,不同场景用不同数据。做日内高频策略,Tick级是必须的。做日间分析,分钟级就够了。你想想看,如果每次跑回测都用Tick数据,那计算量得多大?
二、数据清洗与对齐
数据拿到手,别急着用。先洗一洗。我在项目中遇到过太多坑了——空值、异常值、时间戳错位,哪个都能让你白忙活半天。
1. 常见的数据问题
| 问题类型 | 表现 | 处理方法 |
|---|---|---|
| 缺失值 | 某段时间没有交易记录 | 前向填充或插值 |
| 异常值 | 价格突然跳变(比如从10块跳到100块) | 设置阈值过滤(比如超过3个标准差) |
| 时间戳错位 | 不同数据源的时间基准不一致 | 统一转换为UTC时间 |
| 重复记录 | 同一笔交易被记录了两次 | 按时间戳去重 |
避坑指南:我曾经处理过一批数据,发现某只股票在下午3点后还有大量交易记录。查了半天才发现,是数据源把盘后大宗交易也混进来了。记住:A股收盘后15分钟内的大宗交易,要单独处理。
2. 数据对齐
做流动性分析,经常需要把不同频率的数据对齐。比如Tick数据和分钟数据,怎么对齐?
# 数据对齐示例
def align_data(tick_data, minute_data):
"""
将Tick数据对齐到分钟数据的时间轴上
"""
# 将Tick数据的时间戳向下取整到分钟
tick_data['aligned_time'] = tick_data['time'].dt.floor('1min')
# 按对齐后的时间合并
aligned = pd.merge_asof(
minute_data.sort_values('time'),
tick_data.sort_values('aligned_time'),
left_on='time',
right_on='aligned_time',
direction='nearest'
)
return aligned
这里有个细节要注意:对齐方向。你是用前向对齐还是后向对齐?我个人习惯用前向对齐——用上一个有效数据填充当前缺失值。为什么?因为交易数据天然有滞后性,用未来的数据填充当前,会引入未来信息。
三、数据存储方案
高频数据量太大,普通的关系型数据库扛不住。我建议用列式存储或者时序数据库。
1. 存储方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Parquet + HDF5 | 压缩率高,读取快 | 不支持实时写入 | 离线分析 |
| InfluxDB | 支持实时写入,查询快 | 存储成本高 | 实时监控 |
| ClickHouse | 列式存储,聚合查询极快 | 运维复杂 | 大规模分析 |
| MongoDB | 灵活,支持文档结构 | 查询性能一般 | 原型开发 |
我的建议:如果你刚开始做,先用Parquet格式存本地。等数据量上来了,再迁移到ClickHouse。别一上来就搞分布式,运维成本太高。
2. 存储结构设计
我习惯按日期+股票代码分片存储。这样查询某只股票某天的数据,直接定位到对应文件,不用全表扫描。
# 存储目录结构示例
/data/
/ticks/
/2024-01-01/
000001.SZ.parquet
000002.SZ.parquet
...
/2024-01-02/
...
/minutes/
/2024-01-01/
000001.SZ.parquet
...
/2024-01-02/
...
为什么要按日期分?因为流动性分析通常按天做。你想想看,如果所有数据都塞在一个文件里,查某一天的数据得读整个文件,那效率得多低?
四、知识体系总览
下面这张图,把整个数据准备的流程串起来了。你可以把它当作一个检查清单——做每一步之前,先看看自己走到哪了。
这张图把整个流程分成了三个阶段。每个阶段都有对应的技术选型和注意事项。你照着这个框架走,基本不会出大问题。
最后说一句:数据准备这件事,没有捷径。我见过太多人急着跑模型,结果数据没洗干净,出来的结论全是错的。记住:垃圾进,垃圾出。把数据准备这一步做扎实了,后面的分析才能站得住脚。