3. 逐笔数据清洗与异常处理
逐笔成交数据是分钟聚合的原料,原料不干净,聚合结果就不可信。真实行情落地到本地时,常见的问题并不复杂,但足够隐蔽:同一笔成交被重复推送、相邻记录时间戳倒退、价格为 0 或明显偏离、成交量出现负数或异常巨量。这些问题如果直接进入分钟聚合,会导致成交量虚增、分钟 K 线出现假跳空、最高最低价失真,甚至让后续的指标计算全部偏移。本节围绕四类高频脏数据,给出可落地的识别规则、清洗动作与容错策略。
先明确判定口径,再谈清洗
清洗的前提是有一致的判定标准。逐笔成交记录通常包含证券代码、成交时间、成交价格、成交数量、买卖方向、成交编号等字段。我们约定:时间戳统一为交易所撮合时间,精确到毫秒;成交数量单位为股;价格单位为元;成交编号在同一交易日内唯一。所有规则都基于这套口径设计,若数据源字段含义不同,需先做字段映射再进入清洗环节。
另一个关键约定是“清洗不改变有效数据”。清洗的目标是剔除或修正脏记录,而不是重算行情。对于无法确认真实值的记录,宁可标记后隔离,也不要随意填补,否则会引入新的偏差。
四类脏数据的识别与处理
重复记录。重复通常来自行情网关重推或落库重试。识别依据优先用成交编号:同一交易日内成交编号相同的记录视为重复,保留首条、丢弃后续。若数据源没有成交编号,则退化为组合键判断——证券代码、成交时间、价格、数量四项完全一致且时间间隔在极短窗口内(例如 1 秒),可判为疑似重复。注意,真实市场中同一毫秒内同价同量的两笔成交是可能存在的,因此组合键判重必须配合时间窗口,不能简单全局去重。
时间戳乱序。乱序表现为后到的记录时间早于前一条。处理策略不是简单丢弃,而是先做局部排序:以证券代码为分组,按时间戳升序重排,再检查排序后是否仍存在时间倒挂。若某条记录时间戳明显偏离(例如早于当日开盘或晚于收盘),应判为异常时间并隔离。对于跨日数据,必须先按交易日切分再排序,避免把不同交易日的数据混在一起。
价格为零或异常。价格为 0 的记录几乎不可能是真实成交,直接剔除。价格明显偏离的判定可结合当日涨跌停价:超出涨跌停范围的记录判为异常。此外,价格与数量同时为 0 的记录、价格为负的记录,一律隔离。对于价格缺失但数量正常的记录,不建议用前一笔价格填补,因为逐笔成交的价格具有即时性,填补会制造虚假成交。
数量异常。数量为负、数量为 0、数量超过单笔合理上限(例如超过流通股本的某个比例)都应标记。数量异常往往与拆单、撤单回报混入有关,需要结合数据源说明判断。若确认是撤单类记录混入成交表,应在清洗阶段按记录类型过滤,而不是在聚合阶段补救。
清洗规则的可执行写法
把规则写成代码时,建议按“先分组、再排序、后校验”的顺序组织,避免在乱序数据上做重复判断。下面是一段示意性的处理逻辑,参数为示例值,实际使用时需按数据源调整:
# 示例参数,非实测结果
MAX_QTY = 10_000_000 # 单笔数量上限(股)
PRICE_LIMIT = 0.10 # 涨跌停幅度示例
WINDOW_MS = 1000 # 组合键判重时间窗口
def clean_tick(df):
# 1. 按交易日与代码分组
df = df.sort_values(["trade_date", "symbol", "ts"])
# 2. 成交编号判重,保留首条
df = df.drop_duplicates(subset=["trade_date", "symbol", "trade_id"], keep="first")
# 3. 价格与数量校验
bad_price = (df["price"] <= 0) | (df["price"] > df["pre_close"] * (1 + PRICE_LIMIT))
bad_qty = (df["qty"] <= 0) | (df["qty"] > MAX_QTY)
# 4. 异常记录隔离,不进入聚合
abnormal = df[bad_price | bad_qty].copy()
valid = df[~(bad_price | bad_qty)]
return valid, abnormal
这段逻辑的关键点在于:判重放在排序之后、校验之前,保证重复记录不会因为乱序而漏判;异常记录单独返回而不是直接丢弃,便于后续统计异常率和回溯数据源。
容错策略:隔离、告警与降级
清洗不是一次性动作,而是需要容错机制兜底。建议采用三级策略。第一级是隔离:所有被判为异常的记录写入隔离表,附带原因码(重复、乱序、零价、超限等),保留原始字段,便于复查。第二级是告警:按证券统计异常记录占比,当单只证券异常率超过阈值(示例取 0.5%)时触发告警,提示数据源可能存在问题。第三级是降级:若某只证券异常率持续偏高,暂停该证券的分钟聚合,避免污染下游结果,待人工核查后再恢复。
需要强调的是,容错不等于放任。对于重复记录和零价记录,可以直接剔除;对于时间戳乱序,排序后仍倒挂的记录应隔离而非强行修正;对于数量异常,必须先确认记录类型,排除撤单回报混入的可能,再决定剔除还是保留。所有处理动作都应留下日志,形成可追溯的清洗链路。
清洗后的验证
清洗完成后,至少做三项检查:一是记录数守恒,原始记录数应等于有效记录数加隔离记录数;二是时间单调性,同一证券的有效记录时间戳应严格不减;三是聚合前抽样,随机抽取若干分钟,核对有效记录的成交量之和与原始数据中对应时段的成交量是否一致(差异应仅来自被剔除的异常记录)。三项检查通过后,数据才可进入分钟聚合环节。