4、订单流重构:逐笔委托的流式处理、订单流符号化与分类、主动买盘与主动卖盘的识别

好,我们直接进入正题。这一章要聊的,是高频交易里最核心的底层能力——订单流重构

很多人做高频交易,盯着Level-2快照看,觉得已经够快了。但我告诉你,快照是「结果」,订单流才是「过程」。你想想看,行情快照每500毫秒甚至1秒才更新一次,这中间发生了多少笔委托?多少笔撤单?谁在主动吃单?这些信息,快照里全丢了。

所以,我个人习惯是:永远基于逐笔委托数据做决策。今天我们就来拆解,怎么把原始的逐笔数据,变成能直接用于策略的信号。

4.1 逐笔委托的流式处理

先看原始数据长什么样。交易所给的逐笔委托,通常包含这些字段:

字段 含义 示例
SeqNo 序列号 1000001
Time 时间戳(纳秒级) 09:30:00.123456789
Side 买卖方向 'B' 或 'S'
Price 委托价格 10.25
Qty 委托数量 1000
Type 委托类型 'ADD' / 'CANCEL' / 'TRADE'

流式处理的核心,就是按序列号顺序,逐条消费。不能跳,不能乱序。为什么?因为后面的委托可能依赖前面的状态。比如一个撤单指令,你如果先处理了,那它撤的是哪笔单子?你根本不知道。

我曾经踩过一个坑: 某次实盘,因为网络抖动导致序列号乱序,我直接按时间戳排序处理。结果撤单和成交的顺序全乱了,盘口深度直接算错,策略瞬间亏了十几个BP。从那以后,我强制要求所有数据必须按SeqNo严格排序,乱序的直接丢弃或等待重传。

流式处理的伪代码,大概长这样:

class OrderFlowProcessor:
    def __init__(self):
        self.order_book = {}  # 维护一个本地订单簿
        self.last_seq = 0

    def on_tick(self, msg):
        if msg.seq_no <= self.last_seq:
            return  # 丢弃重复或乱序
        self.last_seq = msg.seq_no

        if msg.type == 'ADD':
            self.order_book[msg.order_id] = msg
        elif msg.type == 'CANCEL':
            self.order_book.pop(msg.order_id, None)
        elif msg.type == 'TRADE':
            # 成交:从订单簿移除
            self.order_book.pop(msg.order_id, None)
            # 同时记录成交信息
            self.record_trade(msg)

嗯,这里要注意:流式处理必须是无状态的。你不能依赖数据库或外部缓存,所有状态都得在内存里维护。因为延迟就是生命,一次磁盘IO的时间,够你错过好几笔行情了。

4.2 订单流符号化与分类

原始数据太碎了。每一笔委托都是独立的,你没法直接看出「谁在主导市场」。所以我们需要符号化——把连续的委托流,抽象成有意义的「事件」。

我个人习惯把订单流分成这几类:

  • 限价单挂入:被动挂单,不立即成交。符号:LIMIT_BID / LIMIT_ASK
  • 限价单撤单:挂单被撤销。符号:CANCEL_BID / CANCEL_ASK
  • 市价单吃单:主动吃掉对手盘。符号:MARKET_BUY / MARKET_SELL
  • 部分成交:一笔大单被部分吃掉。符号:PARTIAL_FILL

为什么要分类?因为不同类型的订单,对后续价格的影响完全不同。举个例子:

  • 一个LIMIT_BID挂单,说明有人在托底,价格可能跌不下去。
  • 一个MARKET_SELL吃单,说明有人在砸盘,价格可能继续跌。
  • 一个CANCEL_ASK撤单,说明卖压减轻,价格可能反弹。

你看,同样的价格变动,背后的订单类型不同,含义天差地别。

核心观点: 不要只看价格和成交量,要看「谁在主动」。主动方才是市场的驱动力。

4.3 主动买盘与主动卖盘的识别

这是本章的重头戏。怎么判断一笔成交是主动买还是主动卖?

说白了,规则很简单:

  • 如果成交价等于卖一价,说明买方主动去吃了卖单,那就是主动买盘
  • 如果成交价等于买一价,说明卖方主动去砸了买单,那就是主动卖盘

但实际处理时,有几个细节要注意:

  1. 盘口可能已经变了。你收到成交数据时,盘口可能已经被后续的委托更新了。所以你不能拿当前的盘口去判断,得用成交发生那一刻的盘口。
  2. 大单拆分。一笔大单可能被拆成多笔成交,每一笔的主动方向可能不同。我建议按「原始委托」来判定,而不是按「成交回报」。
  3. 冰山订单。有些大单只显示一部分,你看到的卖一可能只是冰山一角。这时候主动方向的判断会失真。嗯,这个比较高级,我们后面章节再细聊。

代码实现上,我一般这样写:

def classify_trade(trade, snapshot):
    """
    trade: 当前成交
    snapshot: 成交时刻的快照(包含买一卖一)
    """
    if trade.price == snapshot.ask_price:
        return 'ACTIVE_BUY'   # 主动买
    elif trade.price == snapshot.bid_price:
        return 'ACTIVE_SELL'  # 主动卖
    else:
        # 可能是中间价成交(罕见),或者盘口已变
        return 'UNKNOWN'

你可能会问:为什么不用成交的买卖方向字段?有些交易所确实会直接告诉你「这笔成交是买方主动」还是「卖方主动」。但我不建议完全依赖这个字段。为什么?因为不同交易所的规则不一样,有的甚至不提供。自己算一遍,心里踏实。

一个小技巧: 我习惯把主动买盘和主动卖盘分别累加,计算一个「主动买卖比」。当这个比值超过2.0时,说明买方极度强势,短期大概率继续涨。反之亦然。当然,这个阈值需要根据品种和波动率动态调整。

4.4 知识体系总览

下面这张图,是我自己梳理的订单流重构核心逻辑。你可以把它当作本章的思维导图:

订单流重构核心逻辑 原始逐笔委托 流式处理(按SeqNo排序) 符号化与分类(LIMIT/MARKET/CANCEL) 主动买盘 vs 主动卖盘识别 策略信号(买卖比/动量) 数据层 处理层 抽象层 分析层 应用层

从这张图你可以看到,整个流程是层层递进的。原始数据经过流式处理,变成有序的事件流;再经过符号化,变成有意义的订单类型;最后通过主动识别,输出可以直接用的策略信号。

每一步都有坑。我见过太多人,直接拿原始成交数据算买卖比,结果方向都是反的。为什么?因为他没做流式处理,成交和盘口的时间对不上。

一句话总结: 订单流重构的本质,是从「发生了什么」到「谁在主导」的转变。你只有理解了谁在主动,才能预判价格的下一步走向。

好了,这一章的内容就到这里。下一章我们会聊怎么把这些信号组合成真正的交易策略。但在此之前,我建议你把今天讲的流式处理和主动识别,先在自己的数据上跑一遍。相信我,跑通了之后,你对市场的理解会上一个台阶。


公众号:蓝海资料掘金营,微信deep3321