4. 涨停开板前撤单行为的时序特征提取

任务定位:把“撤单”从事件计数变成时序特征

本例要解决的问题是:在涨停样本中,锁定首次开板时刻 T_open,回看其前 W 分钟(本例取 W = 10 分钟)的逐笔撤单流水,只保留大单撤单(本例阈值 Q_big = 500 手),把散落的撤单记录整理成一条可计算的时序序列,并输出三个特征:撤单间隔、撤单集中度、撤单量占比。这三个特征共同刻画“开板前撤单是零星发生还是密集爆发、撤掉的是不是封单主力”。

与单纯统计撤单率不同,本例的输入是带时间戳的撤单明细,输出是时间轴上的形态指标,因此必须先做时间对齐,再做序列化,最后才谈特征计算。

数据口径与时间参照约定

  • 时间统一用“距开板秒数”表示:t = T_open - T_cancel,单位为秒,t > 0 表示开板前,t 越小越接近开板。全例正文、公式、SVG 均采用这一参照,不再切换。
  • 撤单记录字段:order_id、cancel_time、cancel_vol(手)、side(本例只取买入方向封单撤单)。
  • 封单量基准 V_seal:取 T_open - W 时刻的买一封单量,作为撤单量占比的分母基准。
  • 大单阈值 Q_big = 500 手,仅保留 cancel_vol >= Q_big 的记录进入序列。

时序特征提取流程

下图给出从原始撤单流水到三个特征的完整处理链,重点标出时间对齐、阈值过滤、序列排序与特征计算之间的数据依赖关系。

逐笔撤单流水 order_id / cancel_time cancel_vol / side 开板时刻 T_open 窗口 W = 10 分钟 封单量基准 V_seal 取 T_open − W 时刻 ① 时间对齐 t = T_open − cancel_time 保留 0 < t ≤ 600 秒 ② 大单过滤 cancel_vol ≥ 500 手 仅买入方向封单撤单 ③ 序列排序 按 t 升序排列 得到 t₁ < t₂ < … < tₙ 特征 A:撤单间隔 Δᵢ = tᵢ₊₁ − tᵢ(秒) 取中位数 Δ_med 与最小间隔 Δ_min 特征 B:撤单集中度 C = 最后 120 秒撤单笔数 / 总笔数 C 越接近 1,撤单越向开板时刻聚集 特征 C:撤单量占比 R = Σ cancel_vol / V_seal 衡量撤掉的是否为封单主力 时间轴(距开板秒数,向右为更接近开板) 600s 300s 120s 0s 开板 最后 120 秒密集区

用一段可运行代码完成提取

下面用 Python 实现上述流程。输入是一份教学示例撤单明细(时间用距开板秒数直接给出,避免时区与格式干扰),输出三个特征值。示例数据为构造数据,用于演示计算过程,不代表任何真实标的。

from statistics import median

# 教学示例:距开板秒数 t、撤单手数 vol(买入方向封单撤单)
cancels = [
    {"t": 588, "vol": 800},
    {"t": 540, "vol": 1200},
    {"t": 470, "vol": 600},
    {"t": 300, "vol": 1500},
    {"t": 210, "vol": 900},
    {"t": 150, "vol": 2000},
    {"t": 95,  "vol": 1800},
    {"t": 60,  "vol": 2500},
    {"t": 35,  "vol": 3000},
    {"t": 12,  "vol": 2200},
    {"t": 8,   "vol": 400},   # 低于大单阈值,应被过滤
]

W = 600          # 回看窗口(秒)
Q_BIG = 500      # 大单阈值(手)
V_SEAL = 20000   # T_open - W 时刻封单量(手)
CONC_WIN = 120   # 集中度统计窗口(秒)

# ① 时间对齐 + ② 大单过滤
seq = [c for c in cancels if 0 < c["t"] <= W and c["vol"] >= Q_BIG]

# ③ 按 t 升序排序
seq.sort(key=lambda c: c["t"])
ts = [c["t"] for c in seq]
vols = [c["vol"] for c in seq]

# 特征 A:撤单间隔
gaps = [ts[i+1] - ts[i] for i in range(len(ts)-1)]
gap_med = median(gaps) if gaps else None
gap_min = min(gaps) if gaps else None

# 特征 B:撤单集中度(最后 120 秒内笔数占比)
n = len(seq)
n_conc = sum(1 for t in ts if t <= CONC_WIN)
concentration = n_conc / n if n else 0.0

# 特征 C:撤单量占比
vol_sum = sum(vols)
vol_ratio = vol_sum / V_SEAL

print("有效大单撤单笔数:", n)
print("撤单间隔中位数(秒):", gap_med)
print("最小撤单间隔(秒):", gap_min)
print("撤单集中度:", round(concentration, 3))
print("撤单量占比:", round(vol_ratio, 3))

按示例数据运行,可得到:有效大单撤单 10 笔(t=8, vol=400 被阈值过滤);间隔序列为 48、70、170、90、60、55、35、25、23 秒,中位数 55 秒、最小 23 秒;最后 120 秒内有 4 笔(t=95, 60, 35, 12),集中度 0.4;撤单量合计 16300 手,占封单基准 20000 手的 0.815。这些数值是给定输入的直接计算结果,用于说明特征口径。

特征如何判读

特征计算口径本例取值判读含义
撤单间隔中位数相邻大单撤单时间差的中位数55 秒间隔越小,撤单节奏越急促
最小撤单间隔相邻撤单时间差的最小值23 秒反映是否出现短时连续撤单
撤单集中度最后 120 秒笔数 / 总笔数0.40越接近 1,撤单越贴近开板时刻
撤单量占比大单撤单量合计 / 封单基准0.815越高说明撤掉的是封单主力
集中度窗口 CONC_WIN 与回看窗口 W 是两个独立参数:前者刻画“贴近开板”的聚集程度,后者决定纳入统计的时间范围。调整 CONC_WIN 会直接改变集中度取值,因此同一批样本比较时必须固定该参数。
若某样本在窗口内没有满足阈值的大单撤单,间隔与集中度均无定义,应显式标记为“无大单撤单样本”,不要用 0 填充后参与横向比较,否则会把“没有异常”误判为“异常程度最低”。

可直接套用的特征提取清单

  • 确认 T_open 取首次开板时刻,而非回封后再开板时刻。
  • 确认 V_seal 与撤单窗口使用同一时间基准,避免分母取错时点。
  • 过滤条件同时包含方向(买入封单撤单)与金额阈值,缺一不可。
  • 间隔特征同时输出中位数与最小值,单看均值会被极端长间隔拉偏。
  • 集中度窗口固定后,再对多样本做横向排序。
  • 无有效撤单的样本单独归类,不参与特征均值统计。