Polymarket Bot 教程 · 第 17/32 章

将 Polymarket 订单簿失衡作为短期价格信号:买卖盘量比、microprice 计算、信号半衰期,以及失衡 bot 何时会胜过随机执行。

本章内容概览

订单簿失衡是限价订单簿中买方深度与卖方深度的比率。在 Polymarket 上,它确实存在,但持续时间很短的预测优势-通常在中间价变动前 5-30 秒。本章讲的是计算方式,以及信号何时会失真。

  • 什么是订单簿失衡
  • Microprice 计算
  • 将失衡作为方向性信号
  • Polymarket 上的信号半衰期
  • 失衡信号何时会失真
  • 代码:每个 WS tick 计算一次失衡

什么是订单簿失衡

订单簿失衡是限价订单簿中买方总深度与卖方总深度的比率。按前 N 层计算(通常 N=5),它捕捉的是在中间价尚未反映之前的整体 trader 压力。

公式:imbalance = (Σ bid[i].price × bid[i].size for i in [0..N]) − (Σ ask[i].price × ask[i].size) / (Σ both)。范围从 -1 到 +1;正值表示更强的买入压力,负值表示更强的卖出压力。

从经验上看,这个信号在 Polymarket 上是真实存在的,但噪声很大。单个 whale 可以在 30-60 秒内伪造失衡,随后再被 arbed 掉。它适合作为多个特征之一,但危险在于把它当成唯一触发条件。

Microprice 计算

Microprice 是对简单 mid 的改进:按最优买价和最优卖价各自的数量进行加权平均。

microprice = (best_bid × ask_size + best_ask × bid_size) / (bid_size + ask_size)

当买盘队列远大于卖盘时,microprice 会更靠近 ask。直觉是:排队等待买入的人更多,下一笔成交更可能把价格推向 ask,因此 fair value 更接近 ask。

Microprice 是实际中间价在 5-30 秒内的领先指标。生产环境中的 bots 会把它作为 take-profit 决策的参考价格,而不是天真的 mid。

将失衡作为方向性信号

来自生产观察:当失衡在 10 秒内从 -0.3 翻转到 +0.5,且没有伴随新闻事件时,接下来 30-60 秒内中间价上行 1-2 美分的概率约为 65%。

这确实是一个优势,但在小仓位下,扣除费用后会消失。想把它变现,bot 必须下足够大的量来覆盖价格波动减去费用,但又不能大到自己把订单簿推动起来。Polymarket 的订单簿通常很薄,超过 50 shares 的单子就可能移动市场。

应将失衡与其他特征结合使用:成交速度(成交越多 = 越像真实信号)、best-bid 是否真的上移(而不只是深度变动)、市场是否处于新闻驱动模式。

Polymarket 上的信号半衰期

失衡信号会衰减。来自我们 trader 的生产数据:imbalance > 0.6 → 预期在 60 秒内产生 1.2c 的中间价变动,半衰期约为 30 秒。90 秒后,预测价值已经归零。

对 bot 设计的含义是:要么快速响应,要么直接跳过。一个需要 15 秒才能决策的 bot,在下单前已经消耗掉一半优势。失衡策略的延迟预算应控制在从信号到触发 FOK 不超过 5 秒。

如果策略持仓时间长于半衰期(1-2 分钟),那本质上是在赌下一个信号,而不是当前信号。请明确这一点;不要不小心把基于失衡的仓位一直持有到结算。

失衡信号何时会失真

当以下三种情况之一成立时,信号会误导你。

  • 新闻驱动的波动:失衡是你尚未看到的新闻的结果。逆着它交易会亏;顺着它交易则是 news arbitrage,属于另一种策略。
  • Whale spoofing:一笔大单被挂出并迅速撤销,会在其存在期间制造虚假的失衡。通过检查失衡是否持续 10 秒以上再触发来过滤。
  • 周期末再平衡:market makers 出于库存原因而不是信息原因撤 quote。几分钟后,当 MM 重新报价时,失衡会反转。

组合过滤条件是:imbalance > threshold AND trade velocity > baseline AND 最近 5 分钟内没有新闻事件。任何单一过滤条件都会产生太多误报。

代码:每个 WS tick 计算一次失衡

参考:订阅 WebSocket book 更新,在每个 tick 上重新计算失衡。

def on_book_message(msg):
    bids = msg.get("bids", [])[:5]
    asks = msg.get("asks", [])[:5]
    bid_usd = sum(float(b["price"]) * float(b["size"]) for b in bids)
    ask_usd = sum(float(a["price"]) * float(a["size"]) for a in asks)
    total = bid_usd + ask_usd
    if total < 100: return  # illiquid
    imb = (bid_usd - ask_usd) / total
    state[msg["asset_id"]] = {
        "imb": imb,
        "best_bid": float(bids[0]["price"]) if bids else 0,
        "best_ask": float(asks[0]["price"]) if asks else 1,
        "ts": time.time()
    }
    # decision logic with cooldown + filters
    if imb > 0.6 and time.time() - last_fired.get(msg["asset_id"], 0) > 60:
        check_filters_and_maybe_fire(msg["asset_id"])

状态是按 token 维护的。冷却时间可防止对同一信号过度触发。过滤器(news 检查、trade velocity)负责放行实际交易。

常见问题

什么是订单簿失衡?
它是订单簿顶部买盘量与卖盘量的比率。一个明显偏向买方的订单簿(接近 touch 的位置买家多于卖家)意味着短期内价格有上行压力。这个信号在 crypto 和 equities 中很常见;在 Polymarket 上,它在流动性好的市场有效,但在薄市场中会消失。
我该如何计算 microprice?
microprice = (best_ask * bid_size + best_bid * ask_size) / (bid_size + ask_size)。它是 mid-price 的按成交量加权版本,会向成交量更少的一侧倾斜-也就是“更先耗尽”的那一侧。bots 会把它作为包含失衡因素的 fair-value 估计。
Polymarket 上失衡信号的半衰期是多少?
在活跃市场中是 5-30 秒。在薄市场中会更长(因为新订单需要更久才能压过失衡)。如果你的 bot 能在 1 秒内响应,就能捕获其中一部分;如果需要 5 秒以上,通常就太晚了。
失衡什么时候会失真?
当一笔大单单独挂在一侧等待被成交时(比如一个很大的 resting bid,但没有其他活动)。失衡是真实的,但它并不预测价格-它只是显示有一个非常坚定的买家。过滤时要数订单数量,而不只是看成交量:N 笔订单对 1 笔订单的失衡,比 1 笔订单各自 5 倍成交量更有信息量。
只靠订单簿失衡就足够交易吗?
通常不够。作为单独信号,它很弱,而且会被 arbed。把它和另一个信号结合(例如 macroprice 均值回归、news 标记或 sports state),才能获得更持久的优势。只看失衡的策略,在扣除费用后往往会跑输随机执行。
哪个 Python 库可以从 Polymarket WS 计算失衡?
据我们所知,没有现成的库-这只需要几十行代码。通过 py-clob-client 订阅 market book,在每个 price_change 事件上重新计算 top-of-book 的 bid/ask size,并输出你的失衡指标。缓存上一个值,只在有意义的变化时才重新触发。