4. 盘口因子计算实战

盘口因子是高频与日内量化研究中最贴近微观结构的一类特征。它们不依赖财务报表,也不依赖日频复权价,而是直接从交易所推送的 Level-2 快照(Snapshot)与逐笔成交(Trade)中提取。本节的任务很明确:把两类原始数据变成三个可计算、可回测、可做样本外滚动验证的因子——买卖压力、订单簿斜率、大单净流入。在动手之前,先把数据口径和符号约定固定下来,否则后续所有计算都会在方向性上出错。

一、原始数据的字段与口径约定

快照数据通常按固定频率(如每 3 秒或每 500 毫秒)推送,每条记录包含时间戳、最新价、成交量、成交额,以及买一至买十、卖一至卖十的价与量。逐笔成交则包含逐笔时间、成交价、成交量、成交方向(主动买/主动卖,或由交易所标记的 BS 标志)。

本节统一采用如下约定,全文与图示保持一致:

  • 价格方向:以“买盘为下方、卖盘为上方”的常规盘口视角,买一价 < 卖一价;
  • 压力符号:买方力量为正、卖方力量为负,因子值越大表示买盘越强;
  • 主动方向:逐笔成交中,成交价等于卖一价视为主动买(外盘),等于买一价视为主动卖(内盘);
  • 时间对齐:逐笔数据按快照时间戳做前向填充归并,即每笔成交归属到其之前最近的一条快照区间。

这三条约定看似琐碎,但买卖压力与订单簿斜率都涉及“买量减卖量”或“价差除以量”的运算,一旦买卖档位颠倒,因子符号会整体翻转,样本外验证时 IC 会呈现稳定的负值,很容易被误判为“因子失效”而非“口径错误”。

二、买卖压力因子:从十档挂单到单一标量

最朴素的买卖压力是买一量减卖一量,但它对单档撤单极其敏感。更稳健的做法是对十档挂单做距离加权,越靠近盘口的挂单权重越高,因为近档更可能被立即成交。一个常用的加权形式是:

weight_i = 1 / (1 + i)          # i = 1..10,买一/卖一权重最高
buy_press  = Σ (bid_vol_i * weight_i)
sell_press = Σ (ask_vol_i * weight_i)
pressure   = (buy_press - sell_press) / (buy_press + sell_press)

分母做归一化后,pressure 落在 [-1, 1] 区间,便于跨股票、跨时段比较。举例来说,若某快照十档买量加权和为 12000 手、卖量加权和为 8000 手,则 pressure = (12000−8000)/(12000+8000) = 0.20,表示买方挂单占优约两成。实际实现时要注意:涨跌停板附近一侧挂单可能为空,分母趋近于零,需要设置最小量阈值或直接置为缺失值,避免产生极端噪声。

三、订单簿斜率:用回归刻画盘口“陡峭度”

买卖压力只回答了“哪边量大”,没有回答“量随价格如何分布”。订单簿斜率正是刻画这一结构的因子:把十档的“累计挂单量”对“相对盘口的价差”做线性回归,斜率越大,说明少量价差就能堆积大量挂单,盘口越“厚”、越抗冲击。

以买盘为例,设第 i 档相对买一价的价差为 Δp_i = (bid_1 − bid_i) / bid_1,累计量为 Q_i = Σ_{j≤i} bid_vol_j,对 (Δp_i, Q_i) 做最小二乘回归得到斜率 k_bid。卖盘同理,用 ask_i 相对卖一价的价差与累计卖量回归得到 k_ask。最终因子可取两者之差或比值:

slope = k_bid - k_ask      # 正值表示买盘更厚、支撑更强

需要注意,价差要用相对值而非绝对价格,否则高价股的斜率会被系统性放大。此外,回归前建议剔除累计量为零的档位,并对 Q_i 做对数变换以压缩量纲差异。斜率因子对停牌复牌、集合竞价刚结束等盘口剧烈重建的时刻非常敏感,样本外滚动验证时应把这些时段标记为异常并单独统计。

四、大单净流入:逐笔数据的方向性聚合

前两个因子来自快照的“挂单意图”,大单净流入则来自逐笔的“真实成交”。做法是设定一个单笔成交量阈值(例如按该股票近 20 日逐笔成交量分位数取 90% 分位),只保留超过阈值的成交,再按主动方向累加:

large_buy  = Σ vol_t,  t 满足 vol_t > threshold 且 方向为主动买
large_sell = Σ vol_t,  t 满足 vol_t > threshold 且 方向为主动卖
net_inflow = (large_buy - large_sell) / (large_buy + large_sell)

阈值必须用滚动窗口动态确定,而不是全样本固定值,否则会引入未来信息,直接破坏样本外验证的有效性。这也是本节与后续滚动验证章节的衔接点:因子构造阶段就要保证“任一时刻只用当时可得的数据”。

下面这张图把三类因子的数据来源、计算路径与输出方向放在同一张信号链上,便于对照理解它们各自吃的是哪类数据、输出的是什么量纲。

盘口因子信号链:原始数据 → 计算 → 因子输出 Level-2 快照 十档价 / 量 时间戳 · 最新价 逐笔成交 成交价 / 量 主动买卖方向 距离加权挂单聚合 w_i = 1/(1+i) 买量加权和 / 卖量加权和 归一化到 [-1, 1] 累计量-价差回归 Δp_i 相对买一/卖一价 Q_i 累计挂单量 最小二乘求斜率 k 大单筛选与方向累加 滚动 90% 分位阈值 主动买量 − 主动卖量 再除以总量归一化 买卖压力 pressure 正 = 买方挂单占优 订单簿斜率 slope 正 = 买盘更厚更抗冲击 大单净流入 net_inflow 正 = 大单主动买入占优 符号约定:买方力量为正、卖方力量为负;逐笔按快照时间戳前向归并;阈值与分位数均使用滚动窗口,避免未来信息

五、实现中的三个关键细节

第一,缺失与异常处理。快照在开盘集合竞价、午间休市前后常出现单侧空档,此时买卖压力分母极小。建议对分母低于该股票当日中位数 5% 的记录直接置为 NaN,而不是用 0 填充——0 会被误读为“买卖均衡”,污染因子分布。

第二,量纲与标准化。三个因子的原始量纲不同:pressure 与 net_inflow 已在 [-1,1],slope 则是“手/百分比”量级。进入滚动验证前,应对 slope 做横截面 z-score 或分位数变换,使其与另两个因子可比。标准化参数同样只能用滚动窗口估计。

第三,因子对齐与去极值。快照频率与逐笔频率不一致,归并后每条快照区间内可能包含零笔或数十笔成交。对零成交区间,net_inflow 应沿用上一有效值或置为缺失,不可简单记 0。去极值建议用 MAD 法在滚动窗口内完成,避免单笔乌龙指成交主导因子。

教学示例说明:上文中的 12000 手、8000 手、90% 分位等数值均为便于讲解的假设示例,用于演示计算过程与结果判读方式,不代表任何真实标的的实测结果。实际使用时请以自己数据源的口径重新计算。

六、结果如何观察与判断

因子算出来后,先做三件事再进入样本外验证:其一,检查因子分布是否近似对称、是否存在大量堆积在 ±1 的截断值,若有说明归一化或缺失处理有问题;其二,检查因子与未来一期收益的秩相关(Rank IC)符号是否与经济学直觉一致——pressure 与 net_inflow 应为正相关,slope 视市场结构可能为正;其三,检查因子自相关,盘口因子衰减极快,若半衰期超过数分钟,需怀疑是否混入了低频信息或时间对齐错误。

只有这三项检查通过,因子才具备进入滚动验证的价值。否则,后续无论用多复杂的滚动窗口,都只是在验证一个口径错误的特征。本节构建的三个因子,正是下一阶段样本外滚动验证的输入对象。