2.1 订单簿动态与流动性建模 限价订单簿是一个离散事件驱动的排队系统:买卖双方的委托按价格分层聚集,同一价位内部按提交时间排队,交易所的撮合引擎按价格优先、时间优先的规则逐笔配对。这句话是本节的全部对象。高频交易里的一切微观决策——报价挂哪一档、吃单吃到几层、撤单什么时候发——都建立在对这套排队力学的心算能力上。本节承接第一章的生态视角,把"做市商与套利者都在盯着什么"这个问题落到具体的盘口结构上,也为 2.2 的行情重建提供目标物:你要重建的正是这个东西。 读完你应当能 画出十档盘口的结构,手算一笔市价单穿透多档后的成交明细与滑点; 区分报单、撤单、成交三类事件对盘口的不同影响,说明每类事件改变哪些状态; 解释队列深度的时间维度:同样的挂单总量,不同的队列结构意味着完全不同的流动性;
限价订单簿是一个离散事件驱动的排队系统:买卖双方的委托按价格分层聚集,同一价位内部按提交时间排队,交易所的撮合引擎按价格优先、时间优先的规则逐笔配对。这句话是本节的全部对象。高频交易里的一切微观决策——报价挂哪一档、吃单吃到几层、撤单什么时候发——都建立在对这套排队力学的心算能力上。本节承接第一章的生态视角,把"做市商与套利者都在盯着什么"这个问题落到具体的盘口结构上,也为 2.2 的行情重建提供目标物:你要重建的正是这个东西。
订单簿只认三类原子事件:报单(新委托进入某价位的队列尾部)、撤单(委托从队列中消失)、成交(市价单或市价化的限价单吃掉对方队列头部)。每一档价位是一个先进先出的队列,队列长度等于该价位可见挂单总量。撮合规则决定了两个推论:同一价位的队列位置就是成交优先级,所以"排在队首"本身有价值;穿价成交会依次清空所经价位的队列,清空后最优价跳到下一档,这就是滑点的来源。
把推演做成一张表,是分析盘口的基本功。假设某合约当前卖方三档如下:卖一 100.05 挂八百手、卖二 100.10 挂五百手、卖三 100.15 挂六百手。此时一笔一千二百手的市价买单进场:
| 步骤 | 动作 | 卖一队列 | 卖二队列 | 卖三队列 | 成交记录 |
|---|---|---|---|---|---|
| 初始 | —— | 800 手 | 500 手 | 600 手 | —— |
| 一 | 吃卖一 | 0 | 500 | 600 | 800 手 @ 100.05 |
| 二 | 吃卖二 | 0 | 0 | 600 | 400 手 @ 100.10,剩余 100 手继续 |
| 三 | 吃卖三 | 0 | 0 | 200 | 100 手 @ 100.15 |
三个价位、三段成交,均价被推高了——这笔单的加权成交价约 100.075,比卖一价高出两档半。这就是"吃穿三档"的真实成本。注意末档:市价单在流动性枯竭时的行为由交易所规则决定,多数市场会成交到深度耗尽为止,部分市场支持"立即或取消"属性让它主动止损。建模时必须读清所在市场的撮合细则,这类细节在回测与实盘之间制造的偏差,比任何模型误差都刺眼。
新手看盘口只看每档的总手数,行家看的是队列的构成。同样显示五千手的买一档,可能是五笔千手级机构单(驻留时间长,撤单概率低),也可能是几百笔散户或小算法的碎片单(平均驻留毫秒级,随时蒸发),还可能是一笔冰山单只露出五千手的冰山一角。三种结构的"真流动性"天差地别:前者的五千手大概率还在,后者的五千手在你订单到达前可能只剩零头。
这就是流动性的时间维度:队列深度不是标量,而是一个带驻留时间的分布。行业里常见的量化刻画是队列年龄——每档队列里各笔委托已等待的时长分布。老单占比高的档位更"实",新单占比高的档位更"虚"。更激进的玩家直接研究队列位置:估一笔新挂单前面有多少手、按当前消耗速率要等多久才轮到自己成交,这个等待时间的倒数就是报价队列的"成交概率密度",是做市策略的核心输入。

静态结构之外,盘口还有呼吸节律。观察一段高频数据会发现:报价档位上的挂单像沙漏里的沙子,不断地有新单落进来、旧单被撤走或成交,队列处于持续的流入流出之中。学界把报单与撤单建模为强度随状态变化的随机点过程,得出的经验规律很有意思:队列越长,单位时间新增挂单的速率往往越高,而撤单率相对温和——厚档位有自我强化的倾向,这解释了为什么盘口深度常呈聚集分布,而不是均匀铺开。
对交易者更实际的提醒是"假单层"与"幌骗"的边界。挂出明显超出真实意愿的大单再迅速撤回以诱导对手,多数市场已明确定性为操纵(幌骗,spoofing),是红线;但合法的"探测性挂撤"与它之间的灰区确实存在,区分标准在于挂单是否附随真实的成交意图。工程上,策略系统需要具备对这些行为的鲁棒性:报价逻辑不能把任何一档的静态深度当作可信输入,而应结合队列年龄、撤单速率做衰减处理。这段逻辑通常长成下面的样子:
def effective_depth(level): """衰减后的有效深度:老单权重高,新单权重低""" weighted = 0.0 for order in level.queue: # 按提交时间升序 age = now - order.submit_ts w = 1.0 - math.exp(-age / TAU) # TAU 为该标的经验驻留常数 weighted += order.qty * w return weighted # 用于报价与风险计算
这短短几行在实盘中的意义是:把"看起来有的流动性"折算成"信得过的流动性"。TAU 取多少、要不要按时段区分(开盘集合竞价后与尾盘的队列行为差异很大),属于第六章策略参数化的内容,但它的结构性位置在订单簿建模这里就已经定了。
背景:某团队的做法策略在一只新纳入观察的合约上运行,盘口显示该合约买一档长期有一万多手的厚单,团队据此认为下行风险有垫,报价偏激。
操作:某日开盘后一分钟内,那笔厚买单连续撤去九成,紧接着一波市价卖单击穿买一、买二,策略的存货在两档内被套。事后调取逐笔数据复盘:厚单是一台做市算法的"深度维持"行为,每被吃掉一批就撤旧挂新,队列年龄中位数不足一百毫秒。
结果:团队把有效深度算法上线(即上面的衰减逻辑),并把"队列年龄突变"加进撤单预警信号。同类事件此后再未造成超预期损失,代价是报价带收窄了约一档,日成交量下降。
解读:这笔学费买到的认知是:盘口是对手行为的投影,不是客观库存。你看到的一万手,可能是一万手承诺,也可能是一台机器每百毫秒重写一次的橱窗。订单簿建模的进阶方向,就是把对手的挂撤行为本身当作被建模对象。
变式:在集中竞价品种上,队列结构以机构大单为主,衰减常数可以放长;在碎片化严重的品种上,任何单档深度都不可长信,报价应更多参考跨档斜率与近期消耗速率。把这套参数化思路带进第六章,你会发现做市策略的很多"参数玄学",根子都在微观结构的认知上。