1. 涨停封板瞬间大单撤单的识别与统计

任务边界与数据前提

本例只做一件事:拿到某只股票涨停封板瞬间的逐笔委托流水,识别“封板之后短时间内的大额撤单”,并输出三个基础异常指标——撤单笔数、撤单量、撤单金额。不计算撤单率与封单量的联动,也不做阈值敏感性分析,那是后续案例的任务。

逐笔委托数据通常包含这些字段(不同数据源命名略有差异,这里统一为教学用字段名):

  • ts:委托时间,精确到毫秒或秒;
  • order_id:委托编号,撤单记录通过它关联原委托;
  • side:买卖方向,B 为买、S 为卖;
  • price:委托价格;
  • qty:委托数量(股);
  • type:记录类型,order 为新增委托,cancel 为撤单;
  • cancel_qty:撤单记录中的撤单数量。

关键前提是封板时刻的确定。本例采用可复现的判定规则:把买一价首次达到当日涨停价、且该价位上买盘累计委托量首次超过设定封单门槛(示例取 100 万股)的最早时间,记为 seal_ts。封板后窗口取 seal_ts 起 60 秒,即 [seal_ts, seal_ts + 60s)。所有时间比较都在同一时间基准下进行,避免跨时区或单位混用。

识别逻辑:从流水到异常指标

识别分三步,核心是“先定位封板、再筛窗口内的撤单、最后按金额阈值过滤大单”。

  1. 定位封板时刻:遍历新增委托,找出价格等于涨停价、方向为买、且该价位累计买量首次达到封单门槛的记录时间。
  2. 筛窗口内撤单:只保留 type == cancel、方向为买、时间落在封板后 60 秒内的记录。方向限定为买,是因为涨停封板时真正影响封单的是买方撤单;卖方撤单不构成封单流失。
  3. 大额过滤:用撤单数量乘以该委托的委托价得到撤单金额,金额不低于阈值(示例取 100 万元)的才计入“大额撤单”。

三个指标的定义必须写死,否则不同人算出的数不可比:

  • 撤单笔数:满足上述条件的撤单记录条数;
  • 撤单量:这些记录的 cancel_qty 之和(股);
  • 撤单金额:逐笔 cancel_qty × price 后求和(元),而不是“总撤单量 × 某一价格”。
撤单记录里的 price 应取原委托价。若数据源在撤单记录中不回填价格,需要先用 order_id 关联新增委托补齐价格,再做金额计算,否则金额会算错。

封板窗口内的撤单识别路径

逐笔委托流水 → 封板定位 → 窗口筛选 → 大额判定 → 三项指标 时间 → 封板前 买盘未达涨停价 / 封单不足 seal_ts 封板 买一价=涨停价 封单量≥100万股 封板后 60 秒窗口 [seal_ts, seal_ts+60s) 窗口外 不计入本例统计 窗口内撤单记录 type=cancel 且 side=B 撤单金额 ≥ 100 万元? 否 小额撤单,不计入 是 大额撤单集合 → 撤单笔数 / 撤单量 / 撤单金额 逐笔 cancel_qty×price 求和,而非总量乘单价

可运行脚本

下面脚本用纯 Python 标准库实现,不依赖 pandas,便于直接验证。示例数据是构造的教学样本,用于演示识别过程,不代表任何真实股票行情。

from datetime import datetime, timedelta

FMT = "%H:%M:%S.%f"

# 教学示例:逐笔委托流水(order=新增委托,cancel=撤单)
ticks = [
    # ts, order_id, side, price, qty, type, cancel_qty
    ("09:30:00.000", "A001", "B", 10.00, 200000, "order", 0),
    ("09:30:05.200", "A002", "B", 10.00, 300000, "order", 0),
    ("09:30:08.100", "A003", "B", 10.00, 500000, "order", 0),   # 封单累计达 100 万股
    ("09:30:12.400", "A004", "B", 10.00, 800000, "order", 0),
    ("09:30:15.000", "A005", "B", 10.00, 600000, "order", 0),
    ("09:30:20.300", "A004", "B", 10.00, 0, "cancel", 800000),  # 大额撤单
    ("09:30:31.700", "A002", "B", 10.00, 0, "cancel", 300000),  # 大额撤单
    ("09:30:45.900", "A006", "B", 10.00, 50000, "order", 0),
    ("09:30:50.100", "A006", "B", 10.00, 0, "cancel", 50000),   # 小额撤单
    ("09:31:10.000", "A005", "B", 10.00, 0, "cancel", 600000),  # 窗口外
]

LIMIT_PRICE = 10.00      # 涨停价
SEAL_QTY = 1_000_000     # 封单门槛(股)
WINDOW_SEC = 60          # 封板后观察窗口
BIG_AMOUNT = 1_000_000   # 大额撤单金额阈值(元)

def parse(ts):
    return datetime.strptime(ts, FMT)

# 1) 定位封板时刻:买一价达涨停价且累计买量首次达到门槛
seal_ts, cum = None, 0
for ts, oid, side, price, qty, typ, cq in ticks:
    if typ == "order" and side == "B" and abs(price - LIMIT_PRICE) < 1e-9:
        cum += qty
        if cum >= SEAL_QTY:
            seal_ts = parse(ts)
            break

assert seal_ts is not None, "样本未出现封板"
end_ts = seal_ts + timedelta(seconds=WINDOW_SEC)

# 2) 建立 order_id -> 委托价 映射,用于撤单金额计算
price_of = {oid: price for ts, oid, side, price, qty, typ, cq in ticks if typ == "order"}

# 3) 窗口内买方撤单 + 大额过滤
big_cancels = []
for ts, oid, side, price, qty, typ, cq in ticks:
    if typ != "cancel" or side != "B":
        continue
    t = parse(ts)
    if not (seal_ts <= t < end_ts):
        continue
    p = price_of.get(oid, price)
    amount = cq * p
    if amount >= BIG_AMOUNT:
        big_cancels.append((ts, oid, cq, p, amount))

count = len(big_cancels)
vol = sum(x[2] for x in big_cancels)
amt = sum(x[4] for x in big_cancels)

print("seal_ts =", seal_ts.strftime(FMT))
print("大额撤单笔数 =", count)
print("大额撤单量(股) =", vol)
print("大额撤单金额(元) =", amt)
for x in big_cancels:
    print("  ", x)

按示例数据运行,预期输出为:seal_ts = 09:30:08.100000,大额撤单笔数 2,撤单量 1,100,000 股,撤单金额 11,000,000 元。其中 A004 的 80 万股撤单发生在封板后约 12 秒,A002 的 30 万股发生在约 23 秒,均在窗口内且金额达标;A006 的 5 万股金额仅 50 万元,被阈值过滤;A005 的 60 万股撤单发生在 09:31:10,已超出 60 秒窗口。

结果判读与常见偏差

现象可能原因处理方式
撤单金额明显偏小撤单记录未回填价格,用了 0 或错误价用 order_id 关联新增委托补齐价格
封板时刻定位不到封单门槛设得过高,或涨停价判断用了浮点相等降低门槛;价格比较改用容差 abs(p-limit)<1e-9
笔数偏多但金额很小未做金额阈值过滤,把碎单也算进来先按金额过滤再统计,或分档输出
把卖方撤单算进来未限定 side == "B"涨停封板场景只统计买方撤单
窗口边界重复或漏算区间开闭不一致统一用左闭右开 [seal_ts, seal_ts+60s)
本例输出的是“基础异常指标”,只回答“封板后 60 秒内发生了多少笔、多少量、多少金额的大额撤单”。它不判断这些撤单是否构成操纵,也不与封单量变化做联动分析。指标数值必须与所用阈值(封单门槛、窗口长度、大额金额)一起记录,脱离参数谈“异常”没有意义。

快速执行清单

  • 确认数据含 order_id、type、cancel_qty,且撤单能关联到原委托价;
  • 固定涨停价、封单门槛、窗口长度、大额阈值四个参数并写入输出;
  • 封板时刻取“首次满足”而非“最后一次满足”;
  • 金额按逐笔 cancel_qty × price 求和;
  • 输出三项指标后,抽查 1–2 笔明细与原始流水核对。