Tick 数据基础:数据结构与采集方式

Tick 数据是金融市场中粒度最细、信息最完整的数据形态。每一笔 Tick 记录对应交易所撮合引擎产生的单笔逐笔成交或最优报价变更。理解 Tick 数据的底层结构、字段含义以及采集链路,是后续进行标签校准与模型训练的前提。

2.1 Tick 数据结构

标准 Tick 数据通常包含四个核心字段:时间戳、成交价格、成交量以及买卖方向。不同交易所或数据供应商可能额外附加标识符、成交序号、买卖盘挂单量等信息,但上述四字段构成所有 Tick 数据的最小公共子集。

字段 符号 数据类型 说明
时间戳 t datetime / int64 交易所撮合时间,精度通常为毫秒或微秒
成交价格 p float64 该笔成交的加权平均价格
成交量 v int64 该笔成交的合约数量或股数
买卖方向 side char / int 主动成交方向:B 买、S 卖、N 未知
精度提示: 国内 A 股 Level-2 Tick 数据时间戳精度为 3 位毫秒;期货 CTP 接口提供微秒级时间戳。模型训练前必须统一时间精度,避免因精度错位导致标签偏移。

2.2 数据来源与采集方式

Tick 数据的获取渠道主要分为三类:交易所直连、第三方数据商、开源/社区数据集。下表对比了三种来源的延迟、成本与适用场景。

来源类型 延迟 成本 数据完整性 适用场景
交易所直连 微秒级 极高(硬件+席位费) 完整 高频做市、实时风控
第三方数据商 毫秒级 中等(订阅费) 较完整 策略回测、模型训练
开源/社区 秒级或离线 免费 可能缺失 教学、原型验证

2.3 采集链路与数据流

从交易所撮合引擎到本地数据库,Tick 数据需要经过行情网关、解码器、缓存队列和持久化存储四个环节。下图展示了典型的 Tick 数据采集架构。

Tick 数据采集架构 交易所撮合引擎 行情网关 协议解码器 内存缓存队列 时序数据库 关键指标 • 延迟:< 1ms • 吞吐:> 10万笔/秒 • 丢包率:< 0.01%

2.4 采集流程步骤

  1. 行情订阅:通过 TCP/UDP 多播协议向交易所或数据商网关发送订阅请求,指定合约代码与数据级别(逐笔成交 / 逐笔委托)。
  2. 协议解码:接收二进制或 FIX/FAST 编码的原始报文,按交易所规范解析出时间戳、价格、成交量、方向等字段。
  3. 时间对齐:将交易所时间戳与本地 NTP 时钟做偏差校正,消除网络传输抖动带来的时间漂移。
  4. 缓存与去重:写入内存环形缓冲区,按时间戳与成交序号去重,过滤重复或乱序报文。
  5. 持久化:批量写入时序数据库(如 InfluxDB、ClickHouse)或 Parquet 文件,按日期分区存储。
常见陷阱: 部分数据商提供的 Tick 数据会做“快照合成”,将同一毫秒内的多笔成交合并为一条记录。这种预处理会丢失微观结构信息,导致后续标签校准出现系统性偏差。建议优先使用原始逐笔成交数据。

2.5 数据质量校验

采集完成后,必须对 Tick 数据执行以下质量检查,否则模型训练将引入噪声:

  • 时间连续性:检查相邻 Tick 时间戳是否单调递增,是否存在大于 1 秒的间隔。
  • 价格合理性:价格是否在当日涨跌停板范围内,是否存在异常跳空。
  • 成交量非负:成交量必须为正整数,零值或负值视为异常。
  • 买卖方向完整性:方向字段缺失率应低于 0.1%,否则该交易日数据不可用。

以下 Python 代码片段展示了基本的 Tick 数据质量校验逻辑:

import pandas as pd

def validate_tick(df: pd.DataFrame) -> dict:
    """对 Tick DataFrame 执行基础质量校验"""
    issues = {}
    # 1. 时间单调性
    if not df['timestamp'].is_monotonic_increasing:
        issues['time_order'] = '时间戳非单调递增'
    # 2. 价格范围
    price_outliers = df[(df['price'] <= 0) | (df['price'] > 1e6)]
    if len(price_outliers) > 0:
        issues['price_range'] = f'发现 {len(price_outliers)} 笔异常价格'
    # 3. 成交量非负
    neg_vol = df[df['volume'] <= 0]
    if len(neg_vol) > 0:
        issues['volume_non_positive'] = f'发现 {len(neg_vol)} 笔非正成交量'
    # 4. 方向缺失
    missing_side = df['side'].isna().sum()
    if missing_side / len(df) > 0.001:
        issues['side_missing'] = f'方向缺失率 {missing_side/len(df):.4%}'
    return issues
实践建议: 在模型训练流水线中,将上述校验封装为独立的预处理步骤,输出 JSON 格式的质量报告。只有通过全部校验的交易日数据才允许进入标签校准环节。