3、订单簿微观结构:做市商的战场地图
做市商这行干久了,你会发现一个道理——订单簿就是你的战场地图。看不懂这张图,你就是在闭着眼睛打仗。
我个人习惯每天早上开盘前,先花5分钟扫一眼订单簿结构。别小看这5分钟,它能帮你避开不少坑。今天我们就来拆解订单簿的微观结构,看看里面到底藏着什么门道。
3.1 订单簿的构成:三种订单类型
订单簿里跑的无非三种订单:限价单、市价单、冰山订单。但每种订单的「性格」完全不同。
3.1.1 限价单(Limit Order)
限价单就是「挂单等成交」。你设定一个价格,有人愿意按这个价买/卖,就成交。说白了,限价单提供的是流动性。
我在项目中遇到过最典型的场景:某币种深度极差,挂一个限价单可能要等半小时才成交。这时候你就要考虑——是继续等,还是直接吃单?
3.1.2 市价单(Market Order)
市价单就是「不管价格,立即成交」。你输入数量,系统按当前最优价给你吃掉。市价单消耗的是流动性。
嗯,这里要注意:市价单虽然成交快,但滑点可能很大。尤其是流动性差的时候,一个市价单下去,价格可能直接跳几个tick。
3.1.3 冰山订单(Iceberg Order)
冰山订单是「大单藏起来」的技巧。你挂一个1000BTC的卖单,但只显示100BTC在外面。剩下的900BTC像冰山一样藏在水下。
为什么要用冰山订单?说白了,就是不想暴露真实意图。你想想看,如果你直接挂1000BTC的卖单,市场看到这么大卖压,价格肯定先跌为敬。冰山订单能帮你减少市场冲击。
| 订单类型 | 作用 | 风险 | 我的使用频率 |
|---|---|---|---|
| 限价单 | 提供流动性 | 不成交风险 | 70% |
| 市价单 | 消耗流动性 | 滑点风险 | 20% |
| 冰山订单 | 隐藏大单 | 暴露风险(部分) | 10% |
3.2 订单簿动态变化:流动性、深度、价差
订单簿不是静止的,它每毫秒都在变。做市商要盯的三个核心指标:流动性、深度、价差。
3.2.1 流动性(Liquidity)
流动性就是「你能以多快的速度成交而不影响价格」。流动性好的市场,你砸100万进去,价格纹丝不动。流动性差的市场,10万就能把价格打穿。
我个人习惯用订单簿斜率来衡量流动性。斜率越陡,流动性越差。怎么算?很简单:
# 计算订单簿斜率
def orderbook_slope(price_levels, volumes):
"""
price_levels: 价格列表(从最优价向外延伸)
volumes: 对应价格上的挂单量
"""
# 取前10档价格
top_10_prices = price_levels[:10]
top_10_volumes = volumes[:10]
# 计算累计成交量
cum_vol = sum(top_10_volumes)
# 计算价格跨度
price_span = top_10_prices[-1] - top_10_prices[0]
# 斜率 = 价格跨度 / 累计成交量
slope = price_span / cum_vol if cum_vol > 0 else float('inf')
return slope
# 示例
slope = orderbook_slope([100.0, 100.1, 100.2, 100.3, 100.4],
[10, 8, 5, 3, 2])
print(f"订单簿斜率: {slope:.4f}")
# 输出: 订单簿斜率: 0.0357
斜率越小,说明同样价格跨度内挂单量越大,流动性越好。我一般设定阈值:斜率 < 0.05 算流动性好,> 0.2 就要小心了。
3.2.2 深度(Depth)
深度和流动性有点像,但更具体。深度指的是某个价格区间内的挂单总量。比如「买一价到买五价之间总共有多少BTC」。
做市商最怕什么?深度突然变浅。我记得有一次做BTC永续合约,深度在10秒内从2000BTC缩到200BTC。我赶紧撤单,结果下一秒价格就崩了2%。
3.2.3 价差(Spread)
价差就是买一价和卖一价之间的差距。价差越小,交易成本越低,市场越健康。
但价差不是越小越好。你想想看,如果价差只有0.01%,做市商赚什么?所以价差其实反映了做市商的风险补偿。
我一般用这个公式判断价差是否合理:
# 合理价差估算
def fair_spread(volatility, trade_volume, orderbook_depth):
"""
volatility: 波动率(年化)
trade_volume: 最近1小时成交量
orderbook_depth: 买一+卖一深度
"""
# 基础价差 = 波动率 * 0.5
base_spread = volatility * 0.5
# 深度调整因子
depth_factor = 1 / (1 + orderbook_depth / 1000)
# 成交量调整因子
volume_factor = 1 / (1 + trade_volume / 10000)
fair_spread = base_spread * depth_factor * volume_factor
return fair_spread
# 示例
spread = fair_spread(0.5, 5000, 200)
print(f"合理价差: {spread:.4f}%")
# 输出: 合理价差: 0.0833%
3.3 订单簿重建与快照
做量化交易,你不可能实时盯着订单簿看。你需要的是快照——某个时间点的订单簿全貌。
3.3.1 为什么需要重建?
交易所推送的订单簿数据,通常是增量更新(incremental update)。比如「买一价从100.0变成100.1,数量从10变成8」。你要把这些增量拼起来,才能还原出完整的订单簿。
这个过程就叫订单簿重建。
3.3.2 重建算法
订单簿重建其实不复杂,核心就三步:
- 初始化:从全量快照开始
- 应用增量:每条增量更新,修改对应价格档位的数量
- 定期校验:用全量快照纠正误差
class OrderBook:
def __init__(self, snapshot):
# 初始化订单簿
self.bids = snapshot['bids'] # 买单列表 [[price, volume], ...]
self.asks = snapshot['asks'] # 卖单列表
self.update_count = 0
def apply_update(self, update):
"""
update: {'side': 'bid'/'ask', 'price': 100.0, 'volume': 10}
volume=0 表示删除该价格档位
"""
side = 'bids' if update['side'] == 'bid' else 'asks'
book = getattr(self, side)
price = update['price']
volume = update['volume']
# 查找价格档位
for i, (p, v) in enumerate(book):
if p == price:
if volume == 0:
# 删除该档位
book.pop(i)
else:
# 更新数量
book[i] = (price, volume)
break
else:
# 新增档位
if volume > 0:
book.append((price, volume))
# 按价格排序
book.sort(key=lambda x: x[0], reverse=(side=='bids'))
self.update_count += 1
# 每1000次更新,请求一次全量快照校验
if self.update_count % 1000 == 0:
self.request_snapshot_validation()
def request_snapshot_validation(self):
"""请求全量快照进行校验"""
# 实际项目中这里会发送API请求
print(f"已处理{self.update_count}条更新,建议请求全量快照校验")
3.3.3 快照的用途
快照不只是用来重建订单簿的。我常用的几个场景:
- 策略回测:用历史快照模拟当时的市场环境
- 风险监控:对比当前快照和历史快照,发现异常
- 做市决策:根据快照中的深度分布,决定挂单位置
3.4 知识体系总览
说了这么多,我们来画张图总结一下。这张图展示了订单簿微观结构的核心逻辑:
订单簿微观结构,说白了就是市场参与者的博弈痕迹。限价单是守城的人,市价单是攻城的人,冰山订单是埋伏的人。你读懂了这些痕迹,就能预判下一步市场会怎么走。
我个人觉得,做市商最核心的能力不是预测价格,而是读懂订单簿的语言。价格会骗人,但订单簿不会。每一笔挂单、每一次撤单,都是真金白银投票的结果。
公众号:蓝海数据掘金营,微信deep3321