2. 数据获取与初步探查
逐笔成交表与逐笔委托表是跨表关联分析的两块基石。在动手写任何关联逻辑之前,必须先完成一次扎实的“数据体检”:确认两张表各自长什么样、有多少行、覆盖哪段时间、哪些字段有缺失。这一步做不扎实,后面所有关联结果都可能是空中楼阁——比如把委托表的时间精度误当成秒级,或者成交表里混入了盘后固定价格交易,关联出来的买卖方向就会系统性错位。
一、先明确两张表各自承载什么
逐笔成交表(常见命名如 trade、tick_trade、transaction)记录的是“已经发生的撮合结果”,核心字段通常包括:证券代码、成交时间(精确到毫秒或微秒)、成交价格、成交数量、成交编号、买方委托序号、卖方委托序号,以及买卖方向标志(部分数据源以 B/S 或 1/2 表示主动方)。逐笔委托表(常见命名如 order、tick_order)记录的是“进入撮合队列的指令”,核心字段包括:证券代码、委托时间、委托价格、委托数量、委托序号、买卖方向、订单类型(限价/市价/本方最优等)。
两张表通过“委托序号”建立血缘关系:成交表中的买方委托序号和卖方委托序号,分别指向委托表中对应的两条委托记录。这正是跨表关联的锚点。因此探查阶段要特别关注委托序号字段的类型(整数还是字符串)、是否唯一、是否存在跨交易日复用。
二、字段结构检查:不只看列名,要看语义
拿到数据后第一步是打印表结构。以常见的 CSV 或 Parquet 数据为例,用 Python 的 pandas 读取后查看 dtypes 与 head():
import pandas as pd
trade = pd.read_parquet("trade_20240603.parquet")
order = pd.read_parquet("order_20240603.parquet")
print(trade.dtypes)
print(order.dtypes)
print(trade.head(3).to_string())
print(order.head(3).to_string())
重点核对四类问题。其一是时间字段:是字符串还是整型?精度是秒、毫秒还是微秒?上交所与深交所的逐笔数据时间戳精度不同,部分数据商还会把时间统一转成“HHMMSSmmm”整型,关联前必须统一成可比较的时间类型。其二是委托序号:检查是否为 64 位整数,若被读成 float 会丢失精度,导致关联键对不上。其三是方向字段:确认 B/S 的含义是“主动买卖”还是“委托买卖”,两者在成交表与委托表中的语义可能相反。其四是价格与数量:确认单位是元还是分、是股还是手,避免后续计算量纲混乱。
三、数据量级与时间范围:建立基线预期
数据量级要结合标的和交易日判断。以单只活跃股票单日为例,逐笔委托通常在数万到数十万条,逐笔成交在数万条量级;若拿到的是全市场全交易日文件,行数会达到千万甚至上亿级。探查时应记录:总行数、去重后的证券代码数、每个代码的行数分布、时间字段的最小值与最大值。
print(len(trade), len(order))
print(trade["symbol"].nunique(), order["symbol"].nunique())
print(trade["time"].min(), trade["time"].max())
print(order["time"].min(), order["time"].max())
时间范围要特别留意集合竞价时段。逐笔委托表通常包含 9:15–9:25 的开盘集合竞价委托,而逐笔成交表在 9:25 才产生第一笔集中撮合成交。如果两张表的时间下界差异明显,属于正常现象,但要在关联逻辑中明确:集合竞价阶段的成交,其对应委托可能早于成交时间数分钟。此外要检查是否存在盘后固定价格交易(15:05–15:30)记录,这类成交的撮合机制与连续竞价不同,是否纳入分析需提前决策。
四、缺失情况:分字段统计,定位结构性缺口
缺失检查不能只算一个总体缺失率,要按字段分别统计,并区分“真空缺”与“业务性空值”。例如成交表中的买方委托序号在部分数据源中对集合竞价成交可能为空;委托表中的订单类型字段对某些市场可能整列为空。建议输出每列的缺失数量与缺失比例,并对关键关联字段单独设阈值告警。
for col in ["buy_order_id", "sell_order_id", "price", "volume"]:
miss = trade[col].isna().sum()
print(col, miss, f"{miss/len(trade):.4%}")
若委托序号缺失比例超过千分之一,就要回溯数据源说明,确认是否该批次数据本身不完整,而不是急于用价格和时间做模糊匹配——模糊匹配会引入大量伪关联,污染后续分析。
五、把探查结果落成一份可复用的基线记录
探查不是看一眼就结束,而应产出一份可对照的基线记录,供后续每个交易日复用。建议至少记录:数据源与版本、文件行数、证券代码数、时间字段类型与精度、时间最小/最大值、关键关联字段缺失率、方向字段取值集合。下面是一份针对单只股票单日数据的示例记录(数值为教学假设,非实测):
| 检查项 | 成交表 | 委托表 | 判断 |
|---|---|---|---|
| 总行数 | 约 4.2 万 | 约 9.6 万 | 委托多于成交,符合预期 |
| 时间精度 | 毫秒 | 毫秒 | 一致,可直接比较 |
| 时间范围 | 09:25:00–14:57:00 | 09:15:00–14:57:00 | 委托含集合竞价,正常 |
| 委托序号类型 | int64 | int64 | 一致,可作关联键 |
| 委托序号缺失率 | 0.02% | — | 低于千分之一阈值,可接受 |
| 方向字段取值 | {B, S} | {1, 2} | 编码不同,需映射统一 |
这张表的价值在于:它把“数据能不能用、怎么用”的判断显性化了。比如方向字段编码不一致这一条,直接决定了关联前要先做一次映射转换;委托序号缺失率虽低,但要在关联结果中把这些成交单独标记为“无法回溯委托”,而不是静默丢弃。
六、常见陷阱与处理原则
第一个陷阱是时间字段被自动推断成字符串后按字典序比较,导致“9:5:0”排在“10:0:0”之后,必须显式转换为时间类型。第二个陷阱是委托序号在跨交易日文件中重复,若把多日数据拼在一起再关联,会出现一对多错配,正确做法是按“交易日+委托序号”联合键关联。第三个陷阱是成交表与委托表的证券代码格式不一致(如带后缀与不带后缀),关联前统一格式。第四个陷阱是忽略撤单:委托表中被撤销的委托不会出现在成交表中,关联时成交表是左表、委托表是右表,未匹配上的委托属于正常撤单或未成交,不应视为数据错误。
完成上述检查后,两张表才真正具备“可关联”的前提:字段语义清楚、时间可比、关联键类型一致且缺失可控、时间范围与业务阶段明确。接下来的工作,才是围绕委托序号构建关联逻辑,并处理一对多、撤单、集合竞价等边界情形。