第四章:数据获取与清洗——暗池交易的数据基石

做量化交易,尤其是暗池策略,最怕什么?

不是策略亏钱,而是数据本身就有问题。我见过太多团队,模型跑得飞起,回测曲线漂亮得不像话,结果一上实盘就崩。为什么?数据源没搞对,或者清洗不到位。

这一章,我们就来聊聊暗池交易的数据获取与清洗。说白了,就是搞清楚数据从哪来、长什么样、怎么把它收拾干净。

4.1 暗池交易数据源介绍

暗池交易的数据,跟普通交易所的Level 1数据完全是两码事。普通行情只给你买卖十档,暗池数据则要复杂得多。

我个人习惯把数据源分成三类:

  • 交易所官方数据:比如NYSE的暗池交易报告、NASDAQ的暗池流动性数据。这些最权威,但通常有延迟,而且收费不菲。
  • 第三方数据供应商:像Bloomberg、Reuters、Quandl这些。它们会把多个暗池的数据整合起来,方便你一站式获取。但要注意,整合过程中可能会有数据丢失或对齐问题。
  • 直接市场接入(DMA)数据:如果你有DMA权限,可以直接从暗池拿到最原始的订单流数据。这是最理想的,但门槛也最高。

核心要点:暗池数据的关键字段包括——订单ID、时间戳(精确到纳秒)、价格、数量、方向(买/卖)、暗池标识符、订单类型(IOC、PEG等)。

我在项目中遇到过一个问题:某个第三方数据商提供的暗池数据,时间戳只精确到毫秒。对于高频策略来说,这简直就是灾难。毫秒级别的误差,足以让你的回测结果完全失真。

4.2 Level 2 / Level 3 数据解析

Level 2和Level 3数据,是暗池交易的核心。很多人分不清它们的区别,我简单解释一下。

Level 2数据:显示的是每个价格水平上的总订单量。比如,在某个暗池里,买一价上有5000股,卖一价上有3000股。你只能看到总量,看不到单个订单。

Level 3数据:这就更细了。你能看到每个单独的订单,包括它的ID、大小、时间戳,甚至能追踪到它是否被部分成交或取消。说白了,Level 3就是订单簿的“显微镜”。

解析这些数据,我建议用Python的pandas库。下面是一个简单的解析示例:

import pandas as pd
import numpy as np

# 假设我们有一个暗池Level 3数据的CSV文件
# 字段:order_id, timestamp, price, quantity, side, pool_id, order_type

df = pd.read_csv('dark_pool_l3.csv', parse_dates=['timestamp'])

# 按暗池和时间排序
df = df.sort_values(['pool_id', 'timestamp'])

# 计算每个暗池的累计买卖压力
df['cum_buy_volume'] = df[df['side'] == 'buy']['quantity'].cumsum()
df['cum_sell_volume'] = df[df['side'] == 'sell']['quantity'].cumsum()

print(df.head())

嗯,这里要注意:Level 3数据量非常大。一个活跃的暗池,一天可能产生几百万条订单事件。所以解析时一定要考虑内存和性能问题。我一般会用dask或者vaex来处理超大数据集。

个人经验:解析Level 3数据时,别忘了处理订单取消事件。很多新手只关注成交,忽略了取消订单中包含的信息。其实,频繁的取消往往意味着有人在试探市场。

4.3 数据清洗与对齐

数据拿到手了,解析完了,接下来就是最磨人的一步——清洗与对齐。

为什么需要清洗?因为原始数据里什么妖魔鬼怪都有:

  • 时间戳错乱(比如某条数据的时间比前一条还早)
  • 价格或数量为负数
  • 重复的订单记录
  • 缺失的关键字段

我曾经遇到过一个案例:某个暗池的数据,在特定时间段内,所有买单的价格都少了一个小数点。原因是数据供应商的ETL脚本出了bug。如果不仔细清洗,你的策略就会基于错误的价格信号做决策。

下面是我常用的清洗流程:

def clean_dark_pool_data(df):
    # 1. 移除时间戳异常的数据
    df = df[df['timestamp'].notna()]
    df = df.sort_values('timestamp')
    df = df[df['timestamp'] >= df['timestamp'].shift()]  # 确保时间递增
    
    # 2. 过滤无效价格和数量
    df = df[(df['price'] > 0) & (df['quantity'] > 0)]
    
    # 3. 去重
    df = df.drop_duplicates(subset=['order_id', 'timestamp'])
    
    # 4. 填充缺失的暗池标识符(如果有)
    df['pool_id'] = df['pool_id'].fillna('UNKNOWN')
    
    return df

cleaned_df = clean_dark_pool_data(df)

数据对齐,则是另一个头疼的问题。不同暗池的数据,时间戳可能基于不同的时钟源。A暗池用NTP同步,B暗池用GPS时钟,两者之间可能有几微秒的偏差。

我的做法是:先选定一个参考时钟(比如交易所的主时钟),然后对所有暗池的数据进行时间戳校准。具体来说,就是计算每个暗池与参考时钟之间的平均延迟,然后统一调整。

避坑指南:千万不要直接合并不同暗池的原始数据。我曾经这么干过,结果回测出来的策略在实盘时完全失效。原因就是数据没有对齐,导致信号出现的时间点全错了。

4.4 知识体系框架

为了让你更直观地理解本章的内容,我画了一张流程图。它展示了从数据源到清洗对齐的完整链路。

暗池交易数据获取与清洗流程 数据源 交易所官方 / 第三方 / DMA 数据解析 Level 2 / Level 3 数据清洗 去重 / 过滤 / 填充 数据对齐 时间戳校准 / 跨暗池合并 回测与验证 策略测试 / 绩效评估 数据质量反馈 异常检测 / 数据修复 虚线表示数据质量反馈回路,确保清洗和解析过程持续优化

这张图展示了整个数据处理的闭环。从数据源开始,经过解析、清洗、对齐,最终进入回测。同时,回测过程中发现的数据问题,会反馈回清洗环节,形成一个持续优化的循环。

你想想看,如果没有这个反馈回路,你可能会一直用着有问题的数据而不自知。我见过太多团队,回测做了几百次,结果数据源从一开始就是错的。

总结一下:数据获取与清洗,是暗池交易策略的基石。数据源选对了,解析做细了,清洗对齐到位了,你的策略才有可信度。否则,再漂亮的回测曲线也只是海市蜃楼。

公众号:蓝海数据掘金营,微信deep3321