3. 成交分布统计的数据准备

开盘集合竞价的成交分布统计,本质上是对“9:15—9:25 这十分钟里,每一笔委托如何进入、如何被撮合、最终以什么价格成交多少”的一次结构化还原。统计结果是否可信,八成取决于数据准备阶段。本节围绕三个环节展开:获取(拿到逐笔委托与逐笔成交原始数据)、清洗(剔除无效、重复、错位记录)、标准化(统一字段、单位、时间口径与代码映射),最终形成一张可直接用于分布统计的“干净明细表”。

一、需要获取哪些数据:两类明细 + 一份参考

集合竞价统计至少需要三类输入。第一类是逐笔委托明细,核心字段包括:证券代码、委托时间(精确到毫秒或秒)、委托方向(买/卖)、委托价格、委托数量、订单编号、委托类型(限价/市价/本方最优等)。第二类是逐笔成交明细,核心字段包括:成交时间、成交价格、成交数量、买方订单号、卖方订单号、成交编号。第三类是参考数据,包括当日开盘参考价、涨跌停价格、证券代码与简称对照、交易日历,用于后续校验和分组。

获取渠道通常有两类:交易所行情文件(如 Level-2 逐笔委托与逐笔成交的二进制或 CSV 落地文件)、以及行情商/数据商的接口回放。无论哪种渠道,都要在获取时记录数据版本、采集时间、文件校验值,否则后续出现“同一只股票两次统计结果不一致”时无法追溯。示例场景中,我们假设某交易日全市场逐笔委托约 2000 万条、逐笔成交约 800 万条,单日原始数据量在数 GB 级别,这决定了清洗必须用批处理脚本而非手工表格。

二、清洗:先解决“脏数据”的四种典型形态

原始逐笔数据常见的质量问题有四类,处理顺序建议按“去重 → 时间对齐 → 异常值处理 → 一致性校验”推进。

  • 重复记录:同一订单编号在委托表中出现多次,多因文件分片重叠或断点续传导致。处理方式是按“订单编号 + 委托时间”去重,保留首条。
  • 时间越界:集合竞价统计只关心 9:15:00—9:25:00 的委托,但原始文件常包含 9:25 之后的连续竞价数据,甚至包含盘后固定价格交易。需按交易所时区(北京时间)截取窗口,并注意 9:25 的撮合成交时间戳可能落在 9:25:00.000 附近,边界应取闭区间。
  • 字段缺失与类型错误:价格为空、数量为 0 或负、方向字段为未知编码。此类记录不能简单删除,应先标记为“待核”,再判断是否影响统计口径——例如数量为 0 的撤单记录应保留用于计算撤单率,但不计入成交分布。
  • 成交与委托对不上:成交明细中的买方/卖方订单号在委托表中找不到对应记录。这通常意味着委托表被截断或代码映射错误,必须做双向关联校验,统计“孤儿成交”比例,超过阈值(如 0.1%)就应回退检查数据源。
注意:集合竞价阶段存在大量“撤单”记录,撤单在逐笔委托中通常表现为独立记录或状态变更。清洗时不要把撤单直接删除,否则会低估委托总量、扭曲撤单率与成交率的计算。

三、标准化:让字段“可计算”

清洗解决“对不对”,标准化解决“能不能算”。关键动作包括:

  1. 统一时间口径:全部转换为“当日 00:00:00 起的毫秒偏移”,避免跨文件时区与格式(HH:MM:SS vs 毫秒时间戳)混用。
  2. 统一价格与数量单位:价格统一为“元”,保留两位或三位小数(视品种最小价位而定);数量统一为“股”,注意部分数据源以“手”为单位,需乘以 100。
  3. 统一方向编码:将 B/S、1/2、买/卖等不同编码映射为统一的 buy/sell 枚举。
  4. 代码映射:把带市场前缀的代码(如 SH600000、600000.SH)统一为六位数字代码,并关联板块(主板/创业板/科创板)与涨跌停幅度,供后续分组统计。
  5. 派生字段:计算委托金额 = 价格 × 数量,标记“是否在 9:20 前可撤单窗口”“是否参与开盘撮合”等布尔字段,为分布统计预置维度。

下面这张图展示了从原始文件到可统计明细表的完整数据流,以及每一步的关键校验点:

集合竞价逐笔数据准备流程与校验点 原始数据获取 逐笔委托文件 逐笔成交文件 参考数据 开盘参考价 / 涨跌停 / 代码表 清洗 ① 按订单号去重 ② 截取 9:15—9:25 窗口 ③ 标记缺失/异常记录 ④ 成交-委托双向关联 ⑤ 保留撤单,单独标记 校验:孤儿成交率 < 0.1% 标准化 时间→毫秒偏移 价格→元 / 数量→股 方向→buy/sell 枚举 代码→六位 + 板块映射 派生:金额、可撤窗口标记 校验:字段非空率 100% 可统计 明细表 委托表 成交表 质量闸门(不通过则回退重取) 记录数守恒:委托数 ≥ 成交数 × 2;时间单调性;价格在涨跌停区间内 同一订单号在委托表唯一;成交买卖双方订单号均可回查 统计口径约定:时间窗口取 [09:15:00, 09:25:00] 闭区间;价格单位元;数量单位股;撤单记录保留并标记 is_cancel=1; 成交分布按“成交价相对开盘参考价的偏离档位”分组,档位宽度由品种最小价位决定,全文与图示保持一致。

四、一个可执行的处理示例

假设我们拿到某日委托文件 order_20240612.csv,字段为 code,time,side,price,qty,order_id,type。用 Python 的 pandas 做最小闭环处理,关键代码如下(示例参数,非实测结果):

import pandas as pd

df = pd.read_csv("order_20240612.csv", dtype={"code": str})
# 1) 时间标准化:HH:MM:SS.mmm → 当日毫秒偏移
df["t_ms"] = pd.to_timedelta(df["time"]).dt.total_seconds().mul(1000).astype("int64")
# 2) 截取集合竞价窗口(闭区间)
df = df[(df["t_ms"] >= 9*3600*1000 + 15*60*1000) &
        (df["t_ms"] <= 9*3600*1000 + 25*60*1000)]
# 3) 去重:订单号 + 时间保留首条
df = df.drop_duplicates(subset=["order_id", "t_ms"], keep="first")
# 4) 方向与单位标准化
df["side"] = df["side"].map({"B": "buy", "S": "sell", "1": "buy", "2": "sell"})
df["qty"] = df["qty"].astype("int64")          # 统一为股
df["amount"] = df["price"] * df["qty"]          # 派生委托金额
# 5) 标记撤单(假设 type=C 表示撤单)
df["is_cancel"] = (df["type"] == "C").astype("int8")
print(df.shape, df["is_cancel"].mean())

运行后应重点观察三个数:清洗后记录数相对原始记录数的保留率(正常应在 95% 以上,过低说明窗口或去重逻辑有误)、撤单标记占比(集合竞价阶段通常不低,需与交易所公开统计口径交叉验证)、以及 amount 是否存在负值或异常大值。若保留率骤降,优先检查时间字段是否被误解析为 UTC。

五、数据质量检查清单

在进入分布统计前,建议逐项确认以下要点,任一项不通过都应先修复数据而非继续计算:

检查项判断标准(示例)不通过时的处理
时间窗口完整性9:15—9:25 每分钟均有记录检查文件分片是否缺失
订单号唯一性委托表内无重复 order_id按订单号+时间去重
成交可回查率孤儿成交占比 < 0.1%回退核对代码映射与文件版本
价格合法性全部落在当日涨跌停区间内标记异常并单独核查
单位一致性数量均为 100 的整数倍(A 股)排查“手/股”混用
撤单标记覆盖撤单记录均有 is_cancel 标记补充委托类型映射表
把上述清洗与标准化脚本固化为每日批处理任务,并将“保留率、孤儿率、撤单占比”三个指标写入日志。这样当某天统计结果异常时,可以先看日志定位是数据问题还是市场行为本身的变化,避免把数据缺陷误读为交易特征。

完成本节的数据准备后,我们得到的是一张字段统一、时间对齐、可双向追溯的逐笔明细表。它是后续“按价格档位统计成交量分布”“按委托金额区间统计委托笔数”“计算撤单率与成交率”等所有指标的唯一数据底座。数据准备做得扎实,后面的分布统计才有讨论价值。