4、订单簿动态特征:订单到达率、订单撤销率、订单簿斜率(Slope)、加权平均价格(WAP)
做市策略的核心,说白了就是跟订单簿打交道。你盯盘的时候,看到的那些跳动的数字,背后其实藏着很多「微结构」信息。今天我们来聊聊四个我平时最常用的动态特征:订单到达率、撤销率、斜率,还有加权平均价格。
嗯,先别急着看公式。咱们先想想:为什么这些特征重要?
因为订单簿不是静止的。它每时每刻都在变化。有人挂单,有人撤单,价格在动,深度在变。如果你只看静态的快照,就像看一张照片去理解一部电影——信息损失太大了。
核心观点:动态特征能帮你捕捉市场参与者的「意图」。挂单快还是撤单快?买单厚还是卖单厚?这些信号直接决定了你的做市策略该怎么调。
4.1 订单到达率(Order Arrival Rate)
订单到达率,就是单位时间内新挂单的数量。我习惯用每秒或每分钟的到达笔数来衡量。
公式很简单:
到达率 = 新挂单数量 / 时间窗口(秒)
我在项目中遇到过一种情况:某个币种平时到达率只有 5 笔/秒,突然飙到 50 笔/秒。这时候如果你还按原来的参数做市,很容易被「冲」到。为什么?因为高到达率往往意味着信息驱动的事件——可能是大新闻,也可能是大单拆单。
实战技巧:我个人习惯用指数加权移动平均(EWMA)来计算到达率,而不是简单平均。这样能更快响应市场变化。
代码实现也很直接:
def calc_arrival_rate(order_events, window_ms=1000):
"""
计算订单到达率
:param order_events: 订单事件列表,每个事件包含时间戳
:param window_ms: 时间窗口,单位毫秒
"""
import pandas as pd
df = pd.DataFrame(order_events, columns=['timestamp'])
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)
# 每秒重采样,统计订单数量
rate = df.resample(f'{window_ms}ms').size()
# 转换为每秒到达率
rate_per_sec = rate / (window_ms / 1000.0)
return rate_per_sec
你想想看,如果到达率突然下降,说明什么?可能是市场在「休息」,也可能是暴风雨前的宁静。这时候我通常会降低报价频率,减少被「钓鱼」的风险。
4.2 订单撤销率(Order Cancellation Rate)
撤销率,这个特征我特别看重。因为它直接反映了「虚假流动性」的程度。
撤销率 = 单位时间内撤销的订单数量 / 总挂单数量
我曾经踩过一个坑:某个交易对看起来买盘很厚,有 100 个 BTC 的买单挂在那边。我放心地挂了卖单,结果下一秒那些买单全撤了,价格直接砸穿。嗯,从那以后,我再也不敢只看深度绝对值了。
避坑指南:高撤销率(比如超过 30%)通常意味着「幌骗」行为。做市商在利用虚假订单诱导你进场。遇到这种情况,我建议你暂时离场,或者把报价放到更远的位置。
撤销率的计算:
def calc_cancel_rate(snapshot, prev_snapshot):
"""
计算订单撤销率
:param snapshot: 当前订单簿快照
:param prev_snapshot: 上一个快照
"""
# 找出消失的订单(被撤销的)
current_ids = set(snapshot['order_id'])
prev_ids = set(prev_snapshot['order_id'])
cancelled = prev_ids - current_ids
# 撤销率 = 撤销数量 / 上一时刻总订单数
cancel_rate = len(cancelled) / len(prev_ids) if len(prev_ids) > 0 else 0
return cancel_rate
我个人习惯把撤销率和到达率放在一起看。如果到达率高、撤销率也高,说明市场在「洗牌」——有人在频繁挂单撤单,可能是做市商在调整仓位,也可能是算法在试探。
4.3 订单簿斜率(Order Book Slope)
斜率这个概念,我第一次接触时觉得有点抽象。说白了,它就是订单簿的「陡峭程度」。
想象一下:买一价 100,买二价 99,买三价 98……如果每个价位的量都差不多,那斜率就平缓。如果买一价有 1000 个币,买二价只有 10 个,那斜率就很陡。
斜率的计算公式:
Slope = (P_bid_i - P_bid_j) / (Q_bid_i - Q_bid_j)
更常用的做法是拟合一条线,用线性回归的系数来表示斜率。
为什么斜率重要?陡峭的订单簿意味着流动性集中在少数价位,价格容易被推动。平缓的订单簿则意味着深度均匀,市场更「抗揍」。
代码实现:
import numpy as np
def calc_slope(levels, side='bid'):
"""
计算订单簿斜率
:param levels: 订单簿档位,[(price, qty), ...]
:param side: 'bid' 或 'ask'
"""
prices = np.array([l[0] for l in levels])
qtys = np.array([l[1] for l in levels])
# 用最小二乘法拟合价格-数量关系
# 斜率 = 价格变化 / 数量变化
if side == 'bid':
# 买单:价格越高,量通常越大(靠近买一)
coeffs = np.polyfit(qtys, prices, 1)
else:
# 卖单:价格越低,量通常越大(靠近卖一)
coeffs = np.polyfit(qtys, prices, 1)
return coeffs[0] # 斜率
我记得有一次做回测,发现斜率这个特征对短期价格反转有很强的预测能力。当买单斜率突然变陡(买一堆积了大量订单),而卖单斜率平缓,价格往往会在几秒内反弹。这其实就是「支撑位」的微观体现。
4.4 加权平均价格(WAP)
WAP 这个概念,很多人以为就是简单的均价。其实不然。WAP 考虑了每个价位的「权重」——也就是成交量。
公式:
WAP = Σ(Price_i * Qty_i) / Σ(Qty_i)
通常我们会分别计算买盘 WAP 和卖盘 WAP:
- 买盘 WAP: 所有买单的加权平均价格,反映买方愿意支付的平均成本
- 卖盘 WAP: 所有卖单的加权平均价格,反映卖方愿意接受的平均价格
实战用法:我个人习惯用 WAP 的差值(卖盘 WAP - 买盘 WAP)作为「价差」的替代指标。这个差值比简单的买卖一价差更稳健,因为它考虑了整个订单簿的深度。
代码实现:
def calc_wap(levels):
"""
计算加权平均价格
:param levels: [(price, qty), ...]
:return: WAP
"""
total_qty = sum(qty for _, qty in levels)
if total_qty == 0:
return 0
wap = sum(price * qty for price, qty in levels) / total_qty
return wap
# 示例:计算买盘和卖盘的 WAP
bid_wap = calc_wap(bid_levels)
ask_wap = calc_wap(ask_levels)
wap_spread = ask_wap - bid_wap
你想想看,如果买盘 WAP 在上升,而卖盘 WAP 不动,说明买方在「抢筹」——他们愿意出更高的价格来买入。这时候作为做市商,你应该上调卖单价格,赚取更多利润。
4.5 四个特征的综合运用
单独看每个特征都有用,但把它们组合起来,威力更大。我画了一张图来说明它们之间的关系:
在实际策略中,我通常这样组合使用:
| 市场状态 | 到达率 | 撤销率 | 斜率 | WAP差值 | 做市策略建议 |
|---|---|---|---|---|---|
| 平静市场 | 低 | 低 | 平缓 | 稳定 | 正常做市,赚取价差 |
| 信息驱动 | 高 | 中 | 变陡 | 扩大 | 扩大报价价差,降低仓位 |
| 幌骗攻击 | 高 | 高 | 异常 | 波动 | 暂停做市,观察为主 |
| 大单拆单 | 中高 | 低 | 单边变陡 | 单边移动 | 跟随趋势,但控制风险 |
重要提醒:这些特征不是孤立的。我见过很多新手只看一个指标就做决策,结果被市场教训。记住,做市是概率游戏,多个特征共同指向同一个方向时,信号的可靠性才高。
好了,这四个特征就讲到这里。我个人觉得,订单簿动态特征就像市场的「心电图」——读懂它,你就能感知市场的呼吸节奏。下次你盯盘的时候,不妨试试用这些指标去分析,你会发现很多以前忽略的细节。
最后一个小技巧:把这些特征做成实时监控面板,用颜色标记异常值。比如到达率超过 3 倍标准差时标红,撤销率超过 40% 时标黄。这样一眼就能看出市场有没有「异常」。
公众号:蓝海资料掘金营,微信deep3321