4. 数据预处理与瞬态事件对齐

开盘集合竞价阶段的成交数据具有“时间集中、笔数稀疏、价格跳变剧烈”的特点。原始逐笔数据往往来自不同通道:交易所逐笔成交、快照行情、委托队列,甚至还有本地采集的行情回放日志。这些数据在进入瞬态特征提取之前,必须先完成三件事:把时间轴统一到同一参照系、把明显失真的记录剔除或修正、把连续行情切成“集合竞价时段”并标出真正值得分析的瞬态事件。本节围绕这三件事展开,并给出可直接套用的处理流程与判定规则。

一、时间戳对齐:先统一参照系,再谈对齐

对齐的第一步不是写代码,而是明确时间参照。交易所逐笔数据通常带两个时间字段:交易所撮合时间(如 TradeTime,精度到毫秒或微秒)和本地接收时间(RecvTime)。两者含义不同:前者是事件在撮合引擎中发生的时刻,后者是数据到达本地网卡的时刻。做瞬态特征提取时,应以交易所时间为准,本地接收时间只用于评估链路延迟和检测丢包。

实际对齐中常见三类偏差:其一是精度不一致,快照是 3 秒一笔,逐笔是毫秒级,直接按时间戳合并会产生大量空值;其二是时钟漂移,不同采集节点之间存在几十毫秒的系统偏差;其三是时区与日期边界,跨日回放时容易把 09:25 的数据错配到前一日。处理办法是:统一转换为带时区的标准时间戳(如 epoch 微秒),以逐笔成交为主表,快照按“最近邻且不晚于事件时刻”的规则做前向填充,并对每个采集源计算相对主时钟的中位偏移量,偏移超过阈值(示例取 50 ms)的源标记为可疑、单独复核。

# 示例:以逐笔成交为主表,快照前向对齐(参数为教学示例)
trades = load_trades(date)          # 字段: ts_exch(us), price, volume, bs_flag
snaps  = load_snapshots(date)       # 字段: ts_exch(us), bid1, ask1, last, vol_total

trades = trades.sort_values("ts_exch")
snaps  = snaps.sort_values("ts_exch")

# 最近邻且不晚于事件时刻的前向对齐
aligned = pd.merge_asof(
    trades, snaps,
    on="ts_exch", direction="backward",
    tolerance=3_000_000)            # 容忍 3 秒,超过则视为无快照
aligned["snap_gap_us"] = aligned["ts_exch"] - aligned["snap_ts_exch"]

对齐完成后要检查两个指标:snap_gap_us 的分布是否集中在快照周期附近,以及是否存在大量空值。若空值比例超过 5%,说明快照覆盖不足或时间基准错位,应回到采集环节排查,而不是在特征层“硬补”。

二、异常值处理:区分“脏数据”与“真实瞬态”

集合竞价阶段的异常值有两类,处理方式完全不同。第一类是数据错误:价格为 0、成交量为负、同一毫秒内出现方向矛盾的多笔成交、时间戳倒退。这类记录应直接剔除或按规则修正。第二类是真实的市场瞬态:例如 09:20 前可撤单阶段的大额撤单引发的价格回撤、09:25 集中撮合瞬间的巨量成交。这类“异常”恰恰是特征提取的目标,绝不能当噪声清掉。

区分二者的实用判据是“是否可被市场机制解释”。价格落在当日涨跌停区间内、成交量与委托队列变化方向一致、时间戳单调,则倾向保留;价格越界、量价为负、时间倒退,则判为错误。对可疑但无法立即判定的记录,采用“标记不删除”策略:新增 quality_flag 字段,取值为 ok、suspect、bad,后续特征计算时对 suspect 记录降权或单独统计,保证可追溯。

经验规则:集合竞价时段的“异常”记录中,真正属于采集错误的通常不足 1%,但如果不加区分地做 3σ 截断,会把 09:25 撮合瞬间的正常巨量成交误删,直接破坏瞬态特征。因此本节推荐“规则判定 + 标记保留”,而不是无差别统计剔除。

三、集合竞价时段切片与瞬态事件标记

A 股开盘集合竞价的完整时段为 09:15–09:25,其中 09:15–09:20 可委托可撤单,09:20–09:25 可委托不可撤单,09:25 集中撮合产生开盘价。切片时应按“阶段”而非等长窗口切分,因为三个阶段的市场行为机制不同:前段撤单频繁、价格噪声大;后段委托锁定、价格趋于收敛;撮合瞬间则是单点脉冲。建议切片字段包括 phase(pre_cancel / locked / match)、phase_start、phase_end,并对 09:25 撮合点单独打标 is_open_auction_match。

瞬态事件标记的核心是定义“什么算一次瞬态”。在本课程语境下,瞬态指在极短时间窗内出现的、显著偏离局部基线的量价突变。可操作的定义为:以 500 ms 为滑动窗,计算窗口内成交量 vol_win 与价格变动 dpx_win,当 vol_win 超过前 20 个窗口成交量中位数的 5 倍,且 |dpx_win| 超过 0.3% 时,标记为一个瞬态事件,事件时间取窗口内成交量最大的那笔成交时刻。阈值均为教学示例,实盘需按标的流动性和历史分布重新标定。

集合竞价时段切片与瞬态事件标记 09:15 09:20 09:25 时间 可撤单阶段 pre_cancel · 撤单频繁 锁定阶段 locked · 委托不可撤 撮合点 match · 单点脉冲 成交量基线(前20窗中位数) 瞬态事件 A 瞬态事件 B(撮合脉冲) 瞬态事件判定(示例阈值,需按标的标定) ① 滑动窗 500 ms,窗口成交量 vol_win > 5 × 前20窗中位数 ② 窗口价格变动 |dpx_win| > 0.3% ③ 事件时刻取窗口内成交量最大那笔成交;quality_flag ≠ bad 才参与判定

上图把三个阶段、成交量分布与两类瞬态事件放在同一时间轴上:事件 A 出现在可撤单阶段,属于撤单引发的量价突变;事件 B 落在撮合点,是集中撮合产生的单点脉冲。两者都满足判定条件,但成因不同,后续特征提取时应分别统计,不能混为一类。

四、处理结果的自检

预处理完成后,建议至少核对四项:时间戳是否单调不减、quality_flag 为 bad 的比例是否在合理范围(示例参考值小于 0.5%)、每个阶段是否都有数据覆盖、瞬态事件数量是否与当日市场活跃度大致相符。若某日事件数为 0,先怀疑阈值过严或切片错误,而不是直接认定“当日无瞬态”。把上述检查固化为每日批处理脚本的输出摘要,可以让数据问题在进入特征计算前就暴露出来,避免污染后续的开盘瞬态分析。