数据源与数据清洗:Tick级数据与Level 2数据

做高频交易,说白了就是跟数据打交道。我入行那会儿,第一个项目就是处理纳秒级的行情数据。当时觉得不就是个时间戳嘛,结果踩的坑能写一本《十万个为什么》。今天咱们就聊聊数据源和数据清洗,尤其是那些让我半夜爬起来改代码的教训。

Tick级数据与Level 2数据

先说说数据源。高频交易里,最基础的是Tick级数据。每一笔成交、每一次报价变动,都算一个Tick。我习惯把Tick数据想象成市场的「心跳」——每跳一下,价格就动一下。

Level 2数据呢?它比Tick数据更细。你想想看,Tick数据只告诉你「成交价是多少」,但Level 2数据会告诉你「买一价、卖一价、买一量、卖一量」,甚至能看到前10档的挂单情况。说白了,Level 2就是市场的「内脏」,你能看到里面在怎么蠕动。

我在项目中遇到过一个问题:某交易所的Level 2数据,买一价和卖一价之间居然有0.01元的价差,但Tick数据里同一时刻的成交价却落在中间。嗯,这里要注意——数据源不一致,是高频交易里最常见的坑。

关键点:Tick数据适合做回测和策略验证,Level 2数据适合做订单流分析和微观结构研究。两者缺一不可,但必须对齐。

数据对齐与时间戳处理

数据对齐,听起来简单,做起来要命。尤其是纳秒级数据,时间戳稍微差一点,策略就全错了。

我刚开始做的时候,从两个交易所拿数据,一个用UTC时间,一个用本地时间。结果对齐后,发现买卖信号总是慢半拍。后来一查,原来是时区没统一。你说气不气人?

时间戳处理,我建议统一用Unix纳秒时间戳。为什么?因为不同交易所的时间格式五花八门——有的用字符串"2024-01-15 09:30:00.123456789",有的用整数。你想想看,字符串解析一次就要几十纳秒,高频交易里这几十纳秒可能就是几百万的盈亏。

# 我常用的时间戳标准化函数
def normalize_timestamp(ts, source_format='exchange_a'):
    if source_format == 'exchange_a':
        # 字符串转纳秒
        dt = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f')
        return int(dt.timestamp() * 1e9)
    elif source_format == 'exchange_b':
        # 微秒转纳秒
        return ts * 1000
    else:
        raise ValueError(f"未知格式: {source_format}")
避坑指南:我曾经因为时间戳精度不一致,导致回测结果比实盘好20%。后来发现,一个数据源是微秒级,另一个是纳秒级,对齐时直接截断了。记住:永远不要假设数据源的精度一致。

异常值检测与缺失值填充

异常值检测,说白了就是找「不正常的点」。高频数据里,异常值可能是交易所的bug,也可能是网络延迟导致的。我见过最离谱的:某只股票的价格突然从100元跳到10000元,然后又跳回来。嗯,这明显是数据错误。

我的做法是:用滚动窗口的Z-score检测。窗口大小选100个Tick,Z-score阈值设3。超过3倍标准差,就标记为异常。

def detect_outliers_zscore(price_series, window=100, threshold=3):
    rolling_mean = price_series.rolling(window=window).mean()
    rolling_std = price_series.rolling(window=window).std()
    z_scores = (price_series - rolling_mean) / rolling_std
    return abs(z_scores) > threshold

缺失值填充呢?高频数据里,缺失值很少是「空值」,更多是「时间戳跳变」。比如,正常每秒有100个Tick,突然某秒只有10个。这时候,我建议用前向填充(forward fill),但要注意:如果连续缺失超过5个Tick,就别填了,直接丢弃。为什么?因为市场状态可能已经变了,填进去的数据反而会误导策略。

个人经验:我处理纳秒级数据时,发现缺失值往往伴随着异常波动。所以我的流程是:先检测异常值,再处理缺失值。顺序不能反,否则异常值会被当成正常数据填充进去。

我在处理纳秒级数据时踩过的坑

最后,分享几个我踩过的坑,希望能帮你少走弯路。

  • 坑一:时间戳溢出。 32位整数存纳秒时间戳,会溢出。我有个同事用int存,结果数据跑到2038年就炸了。现在我都用64位整数。
  • 坑二:数据排序。 纳秒级数据,有时候时间戳是乱序的。因为网络延迟,后发的数据可能先到。我建议每次读取数据后,先按时间戳排序,再处理。
  • 坑三:内存爆炸。 一天的高频Tick数据,可能有几亿条。如果全部加载到内存,32GB都不够。我习惯用分块读取(chunk),或者用数据库存储。
  • 坑四:浮点数精度。 价格用浮点数存,加减几次后精度就丢了。我建议用整数存,比如把价格乘以10000,变成整数再计算。
总结一下:数据源要统一,时间戳要标准化,异常值要检测,缺失值要谨慎填充。每一步都马虎不得,否则策略就是「垃圾进,垃圾出」。
高频交易数据清洗流程 数据源 Tick数据 / Level 2数据 时间戳处理 统一为纳秒 / 对齐 异常值检测 Z-score / 滚动窗口 缺失值填充 前向填充 / 丢弃 数据对齐 多源数据合并 清洗后数据 可用于策略回测 注意:异常值检测和缺失值填充的顺序不能颠倒 时间戳处理必须在数据对齐之前完成

好了,数据源和数据清洗就聊到这儿。记住,高频交易里,数据质量决定了策略的生死。别嫌麻烦,把数据搞干净了,后面的工作才能顺利。

专注资料整理