Polymarket Bot 教程 · 第 22 章,共 32 章
Polymarket 上的 NegRisk 多结果 bots:sum-to-1 机制、当 YES legs 之和不为 1 时的 leg arbitrage、跨 legs 对冲,以及多结果市场特有的执行陷阱。
本章内容概览
多结果 NegRisk 市场是互斥的-最终只会有一个结果为 YES。本章是在第 11 章执行机制之上的策略层:如何跨 legs 对冲、何时 sum-to-1 arbitrage 真的存在,以及 NegRisk bots 在首次部署时最常踩的 bug。
- NegRisk vs binary 回顾
- Sum-to-1 不变量与套利
- 逐 leg 对冲构建
- 执行:订单中的 neg_risk 标志
- NegRisk bots 的常见 bug
- 代码:并行快照所有 legs 并检测低于 1.00 的总和
NegRisk vs binary 回顾
Binary:一个 yes/no 市场,两个 token,总和为 1.0。NegRisk:N 个互斥结果,N 个 token,所有 YES legs 的总和在事件中约等于 1.0。
在执行层面,NegRisk 要求每一笔订单都带上 negRisk: true(第 11 章),并通过一个独立的 exchange contract 路由。从策略层面看,NegRisk 提供了 binary 没有的两个独特机会:当总和偏离 1.0 时进行跨 leg arbitrage,以及通过购买多个 YES legs 来构建对冲。
NegRisk 独有的成本:legs 越多,spread tax 越高(你交易的每个 leg 大约要承担 0.5-1c 的 spread 成本),低流动性事件的 sum-to-1 偏离通常更大(套利更常出现,但规模也更小)。
Sum-to-1 不变量与套利
套利前提:如果买入所有 N 个 YES legs 的总成本低于 1.00 美元,那么在结算时你就锁定了确定性利润(必有一个 leg 赔付 1.00 美元;其他 legs 归零)。
在实践中,套利缺口通常只有 0-3c,会被每个 leg 的 spread + fees 吃掉,而且往往在开盘后几分钟内就消失。容量受限于流动性最薄的 leg。
这种套利还受特定结算失败模式影响:例如“none of the above”结果会在没有任何已命名候选人符合条件时明确结算为 YES。如果事件有这样的 leg,而你没有买入它,那么你的“完整对冲”其实漏掉了实际赔付来源。
逐 leg 对冲构建
如果你持有 NegRisk 某个 leg 的仓位,可以按比例买入其他竞争 legs 来对冲。比如你持有 Trump-YES,价格 0.50,并希望对冲 Trump 失败的风险,你可以买入其他已命名 legs 的组合。
每个 leg 的对冲权重 ≈ 该 leg 当前的隐含概率,条件是 Trump 失败。近似公式:weight_i = price_i / (1 - trump_price)。
这种对冲并不完美,因为使用的是某一时点的价格,而随着新闻出现,条件概率会变化。每周或在重大新闻后重新平衡对冲即可。不要过度设计;对冲的目标是降低方差,而不是消除它。
执行:订单中的 neg_risk 标志
NegRisk 特有的最常见 bug:在下单 payload 里忘记了 negRisk: true。订单会被 API 接受,但由于它被路由到了标准 CTF exchange,而不是 NegRisk exchange,结算会出错。
// CORRECT for NegRisk markets:
await client.createAndPostOrder(
{ tokenID, price, size, side: Side.BUY },
{ tickSize: '0.01', negRisk: true }, // <-- REQUIRED
OrderType.FOK
);
权威来源:Gamma API 中的 market.negRisk。读取它,并直接传下去。不要靠猜测硬编码这个标志。
NegRisk bots 的常见 bug
来自多个 bots 的生产环境调试日志。
- 缺少 negRisk 标志:订单被接受,但结算失败。解决:在每一层封装里都强制设置该标志。
- 对冲时遗漏 “Other” leg:在包含 “None of the above” 结果的事件里,排除它的对冲组合是不完整的。解决:构建对冲时始终检查是否存在 Other leg。
- sum-to-1 arbitrage 仓位过小:arber 发现了 1c 的边际优势,但每个 leg 只交易 5 股;总利润在 spread 前只有 5 美分,净值为负。解决:把套利仓位做得足以产生有意义的绝对美元收益,而不是追逐表面百分比。
- leg 定价过时:bot 获取 3 个 leg 的价格,总共花了 200ms,最后一个 leg 的价格在抓取期间已经变化。解决:并行获取所有 legs + 将快照视为一次整体观测。
代码:并行快照所有 legs 并检测低于 1.00 的总和
参考:并行快照一个 NegRisk 事件的所有 YES legs,并检测套利。
import asyncio, aiohttp
async def fetch_leg_ask(session, token_id):
async with session.get(f"https://clob.polymarket.com/book?token_id={token_id}") as r:
d = await r.json()
asks = d.get("asks", [])
return float(asks[0]["price"]) if asks else None
async def check_arb(event_slug):
event = await fetch_event(event_slug)
if not event["markets"][0]["negRisk"]: return None
legs = []
for m in event["markets"]:
toks = json.loads(m["clobTokenIds"])
yes_token = toks[0]
legs.append(yes_token)
async with aiohttp.ClientSession() as s:
asks = await asyncio.gather(*[fetch_leg_ask(s, t) for t in legs])
if any(a is None for a in asks): return None
total = sum(asks)
if total < 0.97:
return {"edge": 1 - total, "legs": list(zip(legs, asks))}
return None
所有 legs 的原子执行才是更难的问题,这需要对每个 leg 使用 FOK,并在部分成交时回滚(类似第 16 章 stat-arb 代码中的模式)。





