4. 封板前撤单行为的时序特征提取
撤单事件的时间轴建模
封板前的撤单行为不是孤立事件,而是一串带时间戳、档位、数量的离散动作。要判断撤单是否“集中在最后阶段”,需要先把逐笔委托明细还原成一条可计算的时间轴:每条撤单记录包含撤单时刻 t、原挂单档位 L(买一至买五)、撤单量 q。以封板时刻 T 为基准,把 t 映射为相对时间 τ = T − t(单位秒),则 τ 越小表示越接近封板。
本节采用三个统计量刻画撤单结构:
- 撤单量:某时间窗口内买一至买五撤单量之和 Qw,用于衡量撤单规模。
- 撤单频率:窗口内撤单笔数 nw 除以窗口时长,得到笔/分钟,用于衡量撤单密集程度。
- 撤单档位分布:各档撤单量占该窗口总撤单量的比例 pL,用于判断撤单是集中在买一(贴近成交价)还是分散到买五。
把封板前 5 分钟切成 5 个 60 秒窗口:W1 为 τ∈(240,300],W2 为 (180,240],W3 为 (120,180],W4 为 (60,120],W5 为 (0,60]。若 W5 的撤单量占比与撤单频率显著高于前四个窗口,且撤单档位向买一集中,则判定撤单行为集中在封板前最后阶段。
撤单档位分布的判读
仅看总量还不够。若 W5 的撤单量虽大,但主要发生在买四、买五,说明撤单是“远端挂单撤离”,对封板稳定性的冲击有限;若撤单集中在买一、买二,则意味着贴近成交价的承接盘在封板前被抽走,封板质量更脆弱。因此需要按档位拆分:
| 档位 | W1–W3 撤单量(手) | W4 撤单量(手) | W5 撤单量(手) | W5 占该档总量 |
|---|---|---|---|---|
| 买一 | 1,500 | 3,400 | 9,600 | 66.2% |
| 买二 | 2,100 | 2,000 | 3,200 | 43.8% |
| 买三 | 1,800 | 1,300 | 1,500 | 32.6% |
| 买四 | 1,400 | 700 | 600 | 22.2% |
| 买五 | 1,100 | 400 | 400 | 21.1% |
示例数据下,买一档在 W5 的撤单量占该档全窗口撤单量的 66.2%,且 W5 买一撤单量占 W5 总撤单量的 62.7%,说明撤单不仅集中在最后 60 秒,而且集中在最贴近成交价的买一档。这是“封板前最后阶段撤单”的典型形态。
可执行的提取与判定流程
下面给出一个可直接运行的 Python 片段,输入为逐笔委托明细(含撤单记录),输出各窗口的撤单量、频率、档位分布与集中度判定。字段约定:ts 为撤单时间(秒级时间戳),side 为买卖方向,level 为档位 1–5,qty 为撤单量(手),seal_ts 为封板时刻。
import pandas as pd
def extract_cancel_windows(cancel_df, seal_ts, windows=(300, 240, 180, 120, 60, 0)):
df = cancel_df[cancel_df["side"] == "B"].copy()
df["tau"] = seal_ts - df["ts"]
df = df[(df["tau"] > 0) & (df["tau"] <= 300)]
df["window"] = pd.cut(df["tau"], bins=windows, labels=["W1","W2","W3","W4","W5"], right=True)
agg = df.groupby("window", observed=True).agg(
n=("qty", "size"), qty=("qty", "sum")
).reindex(["W1","W2","W3","W4","W5"]).fillna(0)
agg["freq_per_min"] = agg["n"] / 1.0
agg["qty_share"] = agg["qty"] / agg["qty"].sum()
level_dist = df.pivot_table(index="window", columns="level", values="qty",
aggfunc="sum", fill_value=0).reindex(["W1","W2","W3","W4","W5"]).fillna(0)
level_share = level_dist.div(level_dist.sum(axis=1), axis=0)
return agg, level_dist, level_share
def judge_concentration(agg, level_share, qty_thr=0.40, freq_ratio_thr=2.0, l1_thr=0.50):
w5 = agg.loc["W5"]
base_freq = agg.loc[["W1","W2","W3","W4"], "freq_per_min"].mean()
cond_qty = w5["qty_share"] >= qty_thr
cond_freq = base_freq > 0 and w5["freq_per_min"] / base_freq >= freq_ratio_thr
cond_level = level_share.loc["W5", 1] >= l1_thr
return {
"W5撤单量占比": round(w5["qty_share"], 3),
"W5频率倍数": round(w5["freq_per_min"] / base_freq, 2) if base_freq else None,
"W5买一占比": round(level_share.loc["W5", 1], 3),
"集中判定": cond_qty and cond_freq and cond_level,
}
判定阈值可按样本池校准:qty_thr=0.40 表示最后 60 秒撤单量占 5 分钟总撤单量 40% 以上;freq_ratio_thr=2.0 表示 W5 频率至少为前四窗口均值的 2 倍;l1_thr=0.50 表示 W5 撤单中买一档占比过半。三个条件同时满足才判为“集中在最后阶段”,避免单一指标误判。
结果判读与记录表
对每个涨停样本输出一行记录,便于横向比较与复盘。示例数据(假设值)如下:
| 样本 | W5 撤单量占比 | W5 频率倍数 | W5 买一占比 | 集中判定 | 备注 |
|---|---|---|---|---|---|
| A | 49.2% | 5.0 | 62.7% | 是 | 买一撤单主导 |
| B | 41.5% | 2.3 | 38.0% | 否 | 远端撤离为主 |
| C | 28.7% | 1.4 | 45.2% | 否 | 撤单分散 |
样本 A 三项条件均满足,判为集中;样本 B 虽 W5 量占比达标,但买一占比不足 50%,说明撤单主要发生在买三至买五,属于远端撤离;样本 C 频率倍数不足 2,撤单在 5 分钟内分布较均匀。通过这张表可以把“撤单是否集中在最后阶段”从主观印象转为可复核的量化结论。