21、订单流与市场微观结构:订单簿的排队论模型

做量化这些年,我越来越觉得订单簿就像个活物。它每时每刻都在呼吸、在变化。你盯着它看久了,会发现一些规律——订单的到达、排队、成交、撤单,其实背后有一套数学逻辑在支撑。今天我们就聊聊这个:订单簿的排队论模型。

为什么是排队论?

说白了,订单簿就是一个排队系统。买单排一队,卖单排一队。新来的订单要么插队(市价单),要么老老实实排到队尾(限价单)。这跟银行柜台、超市结账本质上没区别。

我刚开始做高频策略时,总觉得订单簿的微观结构太复杂,没法建模。后来发现,用排队论去理解,很多现象就说得通了。比如为什么某个价位突然堆了很多单子?为什么价格突破时流动性瞬间消失?

核心思想:订单簿的动态变化,可以看作多个独立泊松过程的叠加。每个价位上的订单到达、撤单、成交,都服从一定的概率分布。

订单到达率与价格影响

先问个问题:订单到达率是恒定的吗?

当然不是。我观察过实盘数据,订单到达率有明显的日内模式。开盘和收盘时,订单流最密集。中午时段相对稀疏。而且,当价格快速变动时,订单到达率会急剧上升——这就是所谓的“事件驱动”。

价格影响怎么量化?我习惯用这个公式:

价格冲击 = α × (订单流不平衡) + β × (订单到达率变化)

其中α和β需要根据历史数据拟合。我在一个项目中试过,用过去30天的tick数据做回归,R²能到0.6左右。虽然不是特别高,但作为实时风控的参考已经够用了。

我的经验:订单流不平衡比单纯的成交量更能预测短期价格方向。你可以试试用过去N笔交易的买卖方向统计,做成一个“订单流指标”。

Python模拟订单簿动态

纸上谈兵没意思,我们直接上代码。下面这个例子模拟了一个简单的订单簿,包含限价单到达、市价单成交、以及撤单行为。

import numpy as np
import pandas as pd
from collections import defaultdict

class OrderBookSimulator:
    def __init__(self, tick_size=0.01, spread=0.02):
        self.tick_size = tick_size
        self.spread = spread
        self.bids = defaultdict(list)  # 买单队列
        self.asks = defaultdict(list)  # 卖单队列
        self.mid_price = 100.0
        self.time = 0
        
    def limit_order_arrival(self, side, price, volume):
        """限价单到达"""
        if side == 'buy':
            self.bids[price].append(volume)
        else:
            self.asks[price].append(volume)
            
    def market_order(self, side, volume):
        """市价单成交"""
        if side == 'buy':
            # 吃掉卖单
            for price in sorted(self.asks.keys()):
                while volume > 0 and self.asks[price]:
                    trade_vol = min(volume, self.asks[price][0])
                    volume -= trade_vol
                    self.asks[price][0] -= trade_vol
                    if self.asks[price][0] <= 0:
                        self.asks[price].pop(0)
                if not self.asks[price]:
                    del self.asks[price]
        else:
            # 吃掉买单
            for price in sorted(self.bids.keys(), reverse=True):
                while volume > 0 and self.bids[price]:
                    trade_vol = min(volume, self.bids[price][0])
                    volume -= trade_vol
                    self.bids[price][0] -= trade_vol
                    if self.bids[price][0] <= 0:
                        self.bids[price].pop(0)
                if not self.bids[price]:
                    del self.bids[price]
                    
    def cancel_order(self, side, price, volume):
        """撤单"""
        if side == 'buy' and price in self.bids:
            if self.bids[price]:
                self.bids[price][0] -= volume
                if self.bids[price][0] <= 0:
                    self.bids[price].pop(0)
        elif side == 'sell' and price in self.asks:
            if self.asks[price]:
                self.asks[price][0] -= volume
                if self.asks[price][0] <= 0:
                    self.asks[price].pop(0)
                    
    def simulate_step(self, lambda_arrival=10, lambda_cancel=2):
        """模拟一个时间步"""
        self.time += 1
        
        # 限价单到达(泊松过程)
        n_buy = np.random.poisson(lambda_arrival * 0.5)
        n_sell = np.random.poisson(lambda_arrival * 0.5)
        
        for _ in range(n_buy):
            price = self.mid_price - self.spread/2 + np.random.normal(0, 0.01)
            price = round(price / self.tick_size) * self.tick_size
            volume = np.random.randint(1, 10)
            self.limit_order_arrival('buy', price, volume)
            
        for _ in range(n_sell):
            price = self.mid_price + self.spread/2 + np.random.normal(0, 0.01)
            price = round(price / self.tick_size) * self.tick_size
            volume = np.random.randint(1, 10)
            self.limit_order_arrival('sell', price, volume)
            
        # 市价单(概率性出现)
        if np.random.random() < 0.3:
            side = 'buy' if np.random.random() < 0.5 else 'sell'
            volume = np.random.randint(1, 5)
            self.market_order(side, volume)
            
        # 撤单
        for side, book in [('buy', self.bids), ('sell', self.asks)]:
            for price in list(book.keys()):
                if np.random.random() < 0.1:  # 10%概率撤单
                    self.cancel_order(side, price, book[price][0] if book[price] else 0)
                    
        # 更新中间价
        best_bid = max(self.bids.keys()) if self.bids else self.mid_price - 0.01
        best_ask = min(self.asks.keys()) if self.asks else self.mid_price + 0.01
        self.mid_price = (best_bid + best_ask) / 2
        
        return {
            'time': self.time,
            'mid_price': self.mid_price,
            'bid_depth': sum(sum(v) for v in self.bids.values()),
            'ask_depth': sum(sum(v) for v in self.asks.values()),
            'spread': best_ask - best_bid
        }

# 运行模拟
sim = OrderBookSimulator()
results = []
for _ in range(1000):
    results.append(sim.simulate_step())

df = pd.DataFrame(results)
print(df.tail())

注意:这个模型做了很多简化。真实订单簿还有冰山订单、隐藏订单、以及各种算法交易策略的干扰。我曾经用这个模型做回测,发现它低估了极端行情下的流动性枯竭风险。所以,用它做策略开发可以,但实盘前一定要用真实数据验证。

订单簿的排队论核心参数

我整理了几个关键参数,你在分析订单簿时一定要关注:

参数 含义 典型值(A股)
λ 订单到达率(单/秒) 50-200
μ 订单服务率(成交/秒) 30-100
ρ = λ/μ 系统负载 0.5-0.8
W 平均等待时间(秒) 0.1-2.0
L 队列平均长度 5-50手

嗯,这里要注意:ρ如果超过1,说明订单堆积速度超过了成交速度。这时候价格往往会快速变动,因为流动性在恶化。

知识体系图

下面这张图展示了订单簿排队论模型的核心逻辑:

订单簿排队论模型知识体系 订单流输入 限价单 / 市价单 / 撤单 排队系统 泊松到达 + 指数服务 价格影响输出 冲击成本 / 滑点 关键参数 λ: 到达率 μ: 服务率 ρ: 系统负载 微观特征 订单簿形状 流动性聚集 价差动态 实战应用 订单流策略 做市商模型 风险监控 核心逻辑:订单流 → 排队系统 → 价格发现 Python模拟:泊松过程 + 队列管理 + 价格更新

实战中的坑

我踩过不少坑,说几个典型的:

  • 订单到达率不是常数——用固定λ去模拟,结果会严重偏离真实市场。我建议用GARCH模型或者HMM来动态估计λ。
  • 撤单行为被低估——很多人只关注到达和成交,忽略了撤单。其实在流动性好的品种上,撤单率可能高达60%。
  • 价格影响是非线性的——小单子冲击小,大单子冲击大。而且这个关系不是线性的,有个“拐点”。我习惯用幂律函数去拟合。

一个小技巧:你可以用订单簿的“斜率”来判断短期方向。如果买单队列比卖单队列陡峭(即深度集中在靠近中间价的位置),说明买方更急切,价格可能上涨。反之亦然。

好了,关于订单簿的排队论模型就聊这么多。代码已经给了,你可以自己跑跑看。记住,模型永远是模型的,真实市场比它复杂得多。但有了这个基础,你至少能看懂订单簿在说什么。

交易系统化学习资料 微信Strategy888888