数据模块构建:历史数据获取、数据清洗与对齐、Tick与K线数据、数据存储方案

做市商策略的回测,说白了就是一场「数据游戏」。

我见过太多人,策略逻辑写得天花乱坠,结果一跑回测,数据源就有问题。嗯,那基本等于白干。今天这一章,咱们就把数据模块的底裤扒干净。

1. 历史数据获取:从哪里来,怎么拿?

做市商策略对数据的要求,比普通趋势策略高得多。你想想看,我们赚的是买卖价差和返佣,每一笔订单的成交细节都至关重要。

数据源的选择,我个人习惯分三类:

  • 交易所官方API:最可靠,但有限流。适合小规模回测。
  • 第三方数据服务商:比如彭博、路透、或者一些加密货币数据平台。数据干净,但贵。
  • 自建数据节点:自己搭节点,全量同步链上数据。最费劲,但最灵活。
我的建议:初期先用交易所API,跑通流程再说。别一上来就搞自建节点,容易把自己搞崩。

获取数据时,有几个坑你得避开:

  • 时间戳问题:交易所返回的时间戳,是毫秒还是微秒?是UTC还是本地时间?我曾经因为没注意这个,回测结果差了十万八千里。
  • 数据完整性:网络波动可能导致数据缺失。我习惯在获取时加个重试机制,最多重试3次。
  • 限流策略:别一股脑儿全请求,会被封IP。加个sleep,或者用异步请求。
# 一个简单的数据获取示例(伪代码)
def fetch_trades(symbol, start_time, end_time):
    trades = []
    cursor = start_time
    while cursor < end_time:
        try:
            resp = api.get_trades(symbol, since=cursor, limit=1000)
            trades.extend(resp['data'])
            cursor = resp['next_cursor']
            time.sleep(0.1)  # 限流
        except Exception as e:
            print(f"获取失败,重试中... {e}")
            time.sleep(1)
    return trades

2. 数据清洗与对齐:脏数据是回测的毒药

数据拿到手,别急着用。先洗一洗。

常见的数据脏问题:

  • 重复数据:同一个tick出现了两次。直接去重,保留第一条。
  • 异常值:价格突然跳了100倍,明显是数据错误。我一般用3倍标准差法剔除。
  • 缺失值:某几秒没有数据。做市商策略对连续性要求高,缺失会导致订单簿重建失败。我习惯用前值填充。
注意:千万别用插值法填充tick数据!做市商策略对价格敏感,插值会引入虚假信息。前值填充是最安全的。

数据对齐,这是个技术活。

不同交易所的数据,时间戳可能不同步。比如A交易所用毫秒,B交易所用微秒。对齐时,我习惯统一转成毫秒时间戳,然后按时间排序。

还有一个更隐蔽的问题:订单簿的快照和增量数据对齐。快照是某一时刻的完整订单簿,增量是后续的变动。对齐时,得先应用快照,再按时间顺序应用增量。顺序错了,订单簿就乱了。

# 数据对齐示例
def align_orderbook(snapshot, deltas):
    # 先应用快照
    ob = snapshot.copy()
    # 按时间排序增量
    deltas_sorted = sorted(deltas, key=lambda x: x['timestamp'])
    for delta in deltas_sorted:
        # 应用增量更新
        ob.update(delta)
    return ob

3. Tick与K线数据:两种视角,两种用途

Tick数据,就是每一笔成交的原始记录。谁买了,谁卖了,价格多少,数量多少。

做市商策略回测,必须用Tick数据。为什么?因为K线数据是聚合后的,丢失了微观结构信息。你想想看,如果只用1分钟K线,你根本不知道这一分钟里价格是怎么波动的,是平滑上升还是剧烈震荡。这对做市商策略来说,信息量完全不够。

K线数据,适合做宏观分析,比如判断市场趋势、波动率等。但在回测中,K线只能作为辅助。

数据类型 粒度 用途 数据量
Tick数据 每笔成交 做市商策略回测 极大
K线数据 1分钟/5分钟等 趋势分析、波动率计算 中等
核心原则:做市商回测,Tick数据是主食,K线数据是配菜。别搞反了。

我个人习惯,在回测时同时加载两种数据。Tick数据用于模拟订单簿和成交,K线数据用于计算一些辅助指标,比如短期波动率、买卖压力等。

4. 数据存储方案:别让数据成为瓶颈

数据量大了,存储就成了问题。一天几千万条Tick数据,普通数据库根本扛不住。

我推荐几种方案:

  • Parquet格式:列式存储,压缩率高,读取快。适合做离线回测。
  • InfluxDB:时序数据库,专门处理时间序列数据。查询效率高,但部署稍复杂。
  • HDF5:老牌科学计算格式,支持多维数据。适合存储订单簿快照。

我自己的项目里,用的是Parquet + 内存映射。回测时,把数据映射到内存里,读取速度接近内存访问,比数据库快一个数量级。

# 使用Parquet存储Tick数据
import pandas as pd

# 假设trades是一个DataFrame
trades.to_parquet('trades_2024.parquet', compression='snappy')

# 读取时
trades = pd.read_parquet('trades_2024.parquet')
一个小技巧:按日期分片存储。比如每天一个Parquet文件。这样回测时只需要加载对应日期的数据,节省内存。

还有一个容易被忽略的点:数据版本管理。数据源可能会更新,比如交易所修正了历史数据。我习惯在文件名里加上数据获取日期,比如 trades_2024_20250101.parquet,这样能追溯数据来源。

知识体系总览

下面这张图,是我对数据模块构建的整体理解。你可以把它当作一个检查清单,看看自己有没有遗漏。

数据模块构建知识体系 历史数据获取 数据清洗与对齐 Tick与K线数据 数据存储方案 交易所API 第三方服务商 去重/异常值处理 时间戳对齐 Tick数据 K线数据 Parquet InfluxDB HDF5 数据是回测的基石,花再多时间准备都不为过

数据模块构建,说白了就是四个步骤:获取、清洗、理解、存储。每一步都有坑,但踩过了,后面就顺了。

我个人习惯,在开始写策略代码之前,先花一周时间把数据模块搭好。磨刀不误砍柴工,这话一点不假。


公众号:蓝海资料掘金营,微信deep3321