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
校准效果验证: 校准完成后,应检查成交编号是否连续(无缺失)、时间戳是否严格递增(无乱序)、以及买卖盘索引与对应档位价格是否一致。建议输出校准前后的统计对比表,记录去重数量、排序后逆序比例、时间对齐偏差等指标。