4. 逐笔成交数据校准

逐笔成交数据是 Level2 行情中最细粒度的交易记录,每一笔都对应交易所撮合主机的一次成交确认。该数据的质量直接决定回测与实盘信号的可信度。本节将详解逐笔成交的核心字段、常见异常模式以及标准校准流程。

4.1 逐笔成交数据字段详解

逐笔成交记录通常包含以下五个关键字段,它们共同构成一笔成交的完整描述:

字段名 类型 说明 校准要点
成交编号 int64 交易所为每笔成交分配的唯一序号,通常按时间递增 去重与排序的核心依据
成交价格 float64 该笔成交的撮合价格,单位:元 需在涨跌停范围内,且为最小变动价位整数倍
成交数量 int64 该笔成交的股数(沪市)/ 份数(深市) 必须为正整数,且不超过单笔最大申报量
成交时间 datetime / int64 交易所主机记录的时间戳,精度通常为毫秒或微秒 时间对齐与乱序修复的基准
买卖盘索引 int32 标识该笔成交由哪一档买卖盘触发(如 1=买一,-1=卖一) 用于还原主动方向,需与盘口快照联动校验
字段联动校验提示: 买卖盘索引字段常被忽略,但它对判断主动买/主动卖至关重要。校准时应将索引值与同时刻的十档盘口快照进行交叉比对,确保索引指向的档位价格与成交价格一致。

4.2 常见数据异常

在实际生产环境中,逐笔成交数据常出现以下三类异常,若不处理将导致回测结果失真:

  • 重复(Duplicate):同一笔成交被推送两次或多次,表现为成交编号完全相同,但时间戳可能因网络延迟而略有差异。
  • 缺失(Missing):因网络丢包或行情网关故障,部分成交编号未被记录,导致序列中出现断层。
  • 乱序(Out-of-Order):由于多路传输或缓存抖动,后发生的成交先到达,表现为成交编号与时间戳均不单调递增。
避坑提醒: 不要仅依赖时间戳去重。同一笔成交的重复包可能携带不同的微秒级时间戳,必须使用成交编号作为唯一标识。时间戳仅用于排序与对齐,不应用于去重。

4.3 校准方法

针对上述异常,标准校准流程分为三步:去重、排序、时间对齐。以下为各步骤的详细操作与代码示例。

4.3.1 去重

以成交编号为键,保留首次出现的记录,丢弃后续重复项。若数据量极大,可使用布隆过滤器或哈希集合进行内存优化。

# Python 示例:基于成交编号去重
import pandas as pd

def dedup_trades(df: pd.DataFrame) -> pd.DataFrame:
    """
    对逐笔成交数据按成交编号去重,保留首次出现。
    """
    # 按成交编号排序后去重,确保保留最早到达的记录
    df_sorted = df.sort_values('trade_id')
    df_deduped = df_sorted.drop_duplicates(subset='trade_id', keep='first')
    return df_deduped.reset_index(drop=True)

4.3.2 排序

去重后,按成交编号与时间戳进行双重排序。若成交编号本身已保证单调递增,则仅按编号排序即可;否则需以时间戳为主键、编号为次键排序。

# Python 示例:双重排序
def sort_trades(df: pd.DataFrame) -> pd.DataFrame:
    """
    按成交编号与时间戳排序,修复乱序。
    """
    df_sorted = df.sort_values(['trade_time', 'trade_id'])
    return df_sorted.reset_index(drop=True)

4.3.3 时间对齐

时间对齐用于修正因网络延迟导致的时间戳偏差,以及将成交时间与盘口快照时间统一到同一时间基准。常用方法包括:

  • 线性插值:对缺失的微秒级时间戳,根据前后成交编号与时间戳进行线性插值。
  • 快照锚定:以每秒或每 3 秒的快照时间为锚点,将成交时间对齐到最近的快照时间戳。
  • 交易所基准校正:若已知交易所主机的时钟偏差,可整体平移时间戳。
# Python 示例:基于快照锚定的时间对齐
def align_trades_to_snapshot(trades: pd.DataFrame,
                             snapshots: pd.DataFrame) -> pd.DataFrame:
    """
    将逐笔成交时间对齐到最近的盘口快照时间。
    """
    # 使用 merge_asof 将成交时间匹配到最近的快照时间
    aligned = pd.merge_asof(
        trades.sort_values('trade_time'),
        snapshots[['snap_time']].sort_values('snap_time'),
        left_on='trade_time',
        right_on='snap_time',
        direction='nearest'
    )
    # 用快照时间覆盖原始成交时间
    aligned['trade_time'] = aligned['snap_time']
    aligned.drop(columns='snap_time', inplace=True)
    return aligned
校准效果验证: 校准完成后,应检查成交编号是否连续(无缺失)、时间戳是否严格递增(无乱序)、以及买卖盘索引与对应档位价格是否一致。建议输出校准前后的统计对比表,记录去重数量、排序后逆序比例、时间对齐偏差等指标。
逐笔成交数据校准流程 原始逐笔成交数据 步骤1:去重 以成交编号为键,保留首次出现 步骤2:排序 按成交编号 + 时间戳双重排序 步骤3:时间对齐 线性插值 / 快照锚定 / 交易所基准校正 校准后逐笔成交数据