Polymarket Bot Tutorial · Chapter 22 of 32
NegRisk multi-outcome bots на Polymarket: механіка sum-to-1, leg arbitrage, коли YES legs не дають суму 1, hedging між legs і типові помилки виконання, специфічні для multi-outcome markets.
Що охоплює цей розділ
Multi-outcome NegRisk markets є взаємовиключними - рівно один результат закривається як YES. Цей розділ - це стратегічний шар поверх механіки виконання з розділу 11: як hedging між legs, коли sum-to-1 arb є реальним, і які баги найчастіше ловлять NegRisk bots під час першого запуску.
- Короткий огляд NegRisk vs binary
- Інваріант sum-to-1 і arbitrage
- Побудова hedge по legs
- Execution: neg_risk flag в orders
- Поширені bugs у NegRisk bots
- Code: snapshot усіх legs і виявлення суми нижче 1.00
NegRisk vs binary: короткий огляд
Binary: один yes/no market, два tokens, сума дорівнює 1.0. NegRisk: N взаємовиключних outcome, N tokens, усі YES legs разом дають приблизно 1.0 по всій події.
З погляду execution, NegRisk вимагає negRisk: true для кожного order (розділ 11) і проходить через окремий exchange contract. З погляду strategy, NegRisk дає дві унікальні можливості, яких немає в binary: cross-leg arb, коли сума відхиляється від 1.0, і побудову hedge через купівлю кількох YES legs.
Витрати, унікальні для NegRisk: більше legs = більше spread tax (кожен leg, яким ви торгуєте, коштує приблизно 0.5–1c spread), ширші відхилення sum-to-1 на illiquid events (arb частіше доступний, але менший).
Інваріант sum-to-1 і arbitrage
Ідея arb: якщо купівля всіх N YES legs коштує менше ніж $1.00, ви фіксуєте гарантований profit на момент розв'язання (один leg має виплатити $1.00; інші стануть $0).
На практиці arb gap зазвичай становить 0–3c і з'їдається spread + fees на кожному leg, а також зникає протягом кількох хвилин після відкриття. Capacity обмежена найтоншим leg за liquidity.
Arb також підпадає під специфічні режими failure при resolution: outcome "none of the above", який явно закривається як YES, коли жоден названий candidate не підходить. Якщо у події є такий leg, а ви його не купили, ваш "complete hedge" не покриває фактичну виплату.
Побудова hedge по leg-ах
Якщо ви тримаєте позицію в одному NegRisk leg, ви можете hedge-ити її, купуючи YES на конкуруючих legs у відповідній пропорції. Якщо ви тримаєте Trump-YES на 0.50 і хочете захиститися від Trump loss, ви купуєте портфель інших названих legs.
Вага hedge на один leg ≈ поточна implied probability цього leg за умови, що Trump програє. Наближення: weight_i = price_i / (1 - trump_price).
Hedge не є ідеальним, бо ціни, які використовуються, є point-in-time, а conditional probabilities змінюються зі зміною новинного фону. Перераховуйте hedge щотижня або під час важливих новин. Не ускладнюйте надмірно; мета hedge - зменшити variance, а не прибрати її повністю.
Execution: neg_risk flag в orders
Найпоширеніший баг, специфічний для NegRisk: забути negRisk: true у payload для розміщення order. Order приймається 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
);
Source of truth: market.negRisk з Gamma API. Читайте його і передавайте далі. Ніколи не hardcode-ьте цей flag на основі здогадок.
Поширені bugs у NegRisk bots
Із production debug logs по кількох bots.
- Missing negRisk flag: orders приймаються, settlement fails. Виправлення: примусово встановлюйте цей flag у кожному wrapper.
- Hedging без "Other" leg: у подіях з outcome "None of the above" hedge portfolio, який його виключає, є неповним. Виправлення: завжди перевіряйте Other leg під час побудови hedges.
- Недостатній розмір sum-to-1 arb: arber знаходить 1c edge, але торгує по 5 shares на leg; загальний profit становить 5 cents до spread, а після цього net стає негативним. Виправлення: масштабуйте arb так, щоб витягувати суттєві absolute dollars, а не ганятися за headline percentages.
- Застаріле ціноутворення leg-ів: bot отримує 3 leg prices, витрачає 200ms сумарно, і ціна останнього leg змінюється під час fetch. Виправлення: отримуйте всі legs паралельно + розглядайте snapshot як одне спостереження.
Code: snapshot усіх legs і виявлення суми нижче 1.00
Reference: паралельний snapshot усіх YES legs NegRisk події, виявлення arb.
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
Atomic execution усіх legs - складніша задача, і вона потребує per-leg FOK + rollback у разі partial fill (схожий патерн на stat-arb code з розділу 16).





