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,说明订单堆积速度超过了成交速度。这时候价格往往会快速变动,因为流动性在恶化。
知识体系图
下面这张图展示了订单簿排队论模型的核心逻辑:
实战中的坑
我踩过不少坑,说几个典型的:
- 订单到达率不是常数——用固定λ去模拟,结果会严重偏离真实市场。我建议用GARCH模型或者HMM来动态估计λ。
- 撤单行为被低估——很多人只关注到达和成交,忽略了撤单。其实在流动性好的品种上,撤单率可能高达60%。
- 价格影响是非线性的——小单子冲击小,大单子冲击大。而且这个关系不是线性的,有个“拐点”。我习惯用幂律函数去拟合。
一个小技巧:你可以用订单簿的“斜率”来判断短期方向。如果买单队列比卖单队列陡峭(即深度集中在靠近中间价的位置),说明买方更急切,价格可能上涨。反之亦然。
好了,关于订单簿的排队论模型就聊这么多。代码已经给了,你可以自己跑跑看。记住,模型永远是模型的,真实市场比它复杂得多。但有了这个基础,你至少能看懂订单簿在说什么。