第三章 流动性分层:L1/L2/L3订单簿结构、深度图解读、流动性聚合

做市这件事,说白了就是跟流动性打交道。

你想想看,我们赚的是买卖价差,但前提是——市场得有足够的单子让你吃。如果盘口空空如也,你挂单也没人理,那还做什么市?

所以这一章,我们来聊聊流动性的底层结构。我个人习惯把流动性分成三个层次:L1、L2、L3。别被这些术语吓到,其实没那么复杂。

3.1 订单簿的骨架:L1/L2/L3 到底指什么?

先给个直观的定义:

  • L1(Level 1):最优买卖价。也就是你看到的那一档——买一价、卖一价,加上对应的挂单量。
  • L2(Level 2):多档深度。从买一到买N,从卖一到卖N,每一档的价格和数量都给你。
  • L3(Level 3):逐笔委托。每一笔挂单的完整信息,包括订单ID、价格、数量、时间戳,甚至能追踪到订单的修改和撤单。

我在做高频做市的时候,L3数据是必需品。为什么?因为L2只能告诉你「现在有多少单子」,但L3能告诉你「这些单子是谁挂的、挂了多久、有没有撤单倾向」。说白了,L3让你看到市场的「呼吸」。

核心观点:L1是表象,L2是骨架,L3是肌肉和神经。做市算法越精细,对数据层级的要求就越高。

3.2 深度图解读:别只看价格,要看形状

深度图(Depth Chart)是L2数据的可视化。横轴是价格,纵轴是累计挂单量。买盘在左,卖盘在右,中间那个缺口就是当前价。

我见过很多新手做市商,只看深度图上的「厚度」——觉得买盘厚就安全,卖盘厚就危险。其实没那么简单。

真正有价值的是深度图的形状。我总结了几种常见形态:

形态 特征 做市策略含义
陡峭型 靠近最优价时深度极厚,远离时迅速变薄 市场流动性集中,适合窄价差做市
平缓型 深度随价格变化缓慢,没有明显断层 市场深度分散,价差可以适当放宽
断层型 某个价格区间突然出现深度缺失 可能存在大单埋伏或流动性黑洞,需警惕
单边倾斜型 买盘明显厚于卖盘,或反之 市场情绪偏向一方,做市需调整库存方向

举个例子。我曾经在某个山寨币上做市,深度图看起来买盘很厚,但仔细一看——买一到买五之间有个明显的断层。也就是说,如果价格跌穿买一,会直接砸到买五的位置。这种结构下,我果断把止损线设在了断层上方。结果第二天,果然有人砸盘,我的止损刚好触发,躲过一劫。

我的习惯:每次启动做市算法前,先拉出深度图看5分钟。不是看价格涨跌,而是看形状有没有异常。形状变了,说明有人在搞事情。

3.3 流动性聚合:把碎片拼成地图

现实中的流动性是分散的。同一个币种,可能在币安、OKX、Bybit、Deribit上都有交易。每个交易所的深度、费率、延迟都不一样。

流动性聚合(Liquidity Aggregation),就是把多个交易所的订单簿合并成一个虚拟订单簿。做市算法根据这个虚拟订单簿来决策,然后路由到具体交易所执行。

这里有个关键问题:怎么合并?

最简单的做法是「价格对齐,深度累加」。比如币安卖一价100.1,深度100个;OKX卖一价100.2,深度200个。那虚拟订单簿的卖一就是100.1,深度100+200=300?

不对。因为两个交易所的订单不能同时成交,你只能选一个。所以更合理的做法是:

  • 按价格排序,保留每个价格点的最优深度
  • 或者按「可成交总量」来加权,考虑滑点成本

我个人倾向于第二种。我在项目中用过一种方法:

def aggregate_l2(orderbooks, max_levels=10):
    """
    聚合多个L2订单簿
    orderbooks: list of (exchange_name, bids, asks)
    bids/asks: list of (price, size)
    """
    # 按价格合并,相同价格取深度之和
    agg_bids = {}
    agg_asks = {}
    
    for name, bids, asks in orderbooks:
        for price, size in bids:
            agg_bids[price] = agg_bids.get(price, 0) + size
        for price, size in asks:
            agg_asks[price] = agg_asks.get(price, 0) + size
    
    # 排序并截取前N档
    sorted_bids = sorted(agg_bids.items(), reverse=True)[:max_levels]
    sorted_asks = sorted(agg_asks.items())[:max_levels]
    
    return sorted_bids, sorted_asks

这段代码很简单,但实际生产环境要复杂得多。比如要考虑交易所之间的延迟差异——如果A交易所的数据比B交易所慢了200毫秒,那聚合出来的深度可能已经过时了。

避坑指南:我曾经在聚合时忽略了交易所的「最小交易量」限制。结果虚拟订单簿显示某个价位有500个币可以吃,但实际交易所的最小挂单是1000个币,导致算法一直无法成交。嗯,从那以后,我每次聚合都会把交易所的规则参数一起带进来。

3.4 知识体系:流动性分层与做市算法的关系

下面这张图是我自己画的,把L1/L2/L3、深度图、流动性聚合串在了一起。你可以看到它们如何影响做市算法的核心决策:

流动性分层与做市算法决策流程 L1 最优买卖价 L2 多档深度 L3 逐笔委托 深度图生成 & 流动性聚合 形状识别 | 跨交易所合并 | 延迟对齐 | 规则过滤 做市算法核心决策 价差设定 | 挂单深度 | 库存管理 | 撤单频率 滑点控制 | 成本模型 | 风险阈值 订单路由 & 执行 反馈循环

这张图想表达的核心是:数据层越深,决策层越精准。但代价是计算复杂度和延迟也会上升。所以实际做市时,你需要根据策略类型来取舍。

比如做高频做市(HFT),L3数据是必须的,但聚合可能只做1-2个交易所,因为延迟敏感。做中低频做市,L2就够了,但聚合的交易所可以更多,追求的是深度覆盖。

3.5 实战中的流动性陷阱

最后聊几个我踩过的坑:

  • 虚假深度:有些交易所的L2数据里,挂单量是「名义深度」,不是「可成交深度」。比如某个价位挂了1000个币,但其中800个是冰山订单,实际只能吃到200个。我曾经因为这个吃过亏,后来改用L3数据来验证。
  • 聚合延迟:跨交易所聚合时,如果某个交易所的数据延迟了,聚合结果会「偏老」。我习惯给每个交易所加一个「新鲜度标签」,超过一定延迟的数据直接丢弃。
  • 深度突变:有时候深度图看起来很正常,但下一秒某个价位突然被抽走几千个币。这通常是大户在撤单。我建议在聚合时加入「深度变化率」监控,如果某个价位的深度在1秒内变化超过30%,触发警报。

一个小技巧:做市算法启动前,先跑一段「回放模式」——用历史L2/L3数据模拟当前市场,看看你的聚合逻辑能不能正确识别深度变化。我每次换交易所或者改参数,都会先跑一遍回放,省得上线后出幺蛾子。

好了,流动性分层这部分就聊到这儿。记住一句话:你看到的深度,不一定是你能吃到的深度。做市的核心能力之一,就是透过L1/L2的表象,看到L3背后的真实流动性。

专注资料整理