Polymarket Bot Tutorial · Chapter 18 of 32
Боти прогнозування UMA dispute на Polymarket: виявляйте proposals Optimistic Oracle, прогнозуйте ймовірність dispute, використовуйте цінову асиметрію до й після dispute та уникайте death spirals на disputed markets.
Що охоплює цей розділ
Optimistic Oracle (OO) від UMA розв’язує markets на Polymarket, а disputes створюють цінові аномалії до й після їх виникнення. Tradeable patterns існують по обидва боки dispute, але стратегія операційно складна й “спалила” більше bot'ів, ніж прогодувала. Цей розділ - чесний playbook.
- Як працює UMA Optimistic Oracle
- Як виявити proposal on-chain
- Dispute predictors (volume, ambiguity, history)
- Pre-dispute price asymmetry
- Post-dispute trade setups
- Коли НЕ торгувати disputed markets
- Code: підписка на UMA proposed/disputed events
Як працює UMA Optimistic Oracle
Optimistic Oracle (OO) від UMA - це рівень розв’язання disputes для Polymarket. Кожне resolution market проходить через OO; більшість не оскаржується й автоматично завершується. Спірні випадки - disputes - запускають 24-72-годинний voting period, під час якого власники UMA token'ів визначають результат.
Життєвий цикл: resolver від Polymarket пропонує ціну (0 = NO переміг, 1 = YES переміг). Після 2-годинного challenge window, якщо ніхто не подає dispute, ціна фіксується остаточно, а контракт CTF розподіляє виплати. Якщо хтось подає dispute, market переходить у voting window; UMA holders голосують, перемагає більшість.
Для bot'а релевантні events - ProposePrice (proposal подано, challenge window відкрито) та DisputePrice (dispute подано, voting period починається). Підписуйтеся на них, щоб відстежувати стан market resolution у реальному часі.
Виявлення proposal on-chain
Контракт UMA OO на Polygon генерує event ProposePrice з параметрами (requester, identifier, timestamp, ancillaryData, proposer, proposedPrice). Відфільтровуйте за відомою Polymarket requester address, щоб обмежитися релевантними proposals.
POLY_REQUESTER = "0x..." # Polymarket Adjudicator
filt = oo_contract.events.ProposePrice.create_filter(
fromBlock="latest",
argument_filters={"requester": POLY_REQUESTER}
)
for event in filt.get_new_entries():
market_id = decode_ancillary(event.args.ancillaryData)
proposed = "YES" if event.args.proposedPrice == 1e18 else "NO"
print(f"PROPOSE: market {market_id} → {proposed}")
Поле ancillaryData - це JSON у hex-encoded форматі, що описує питання market'а. Його декодування дає вам ідентифікатор market'а, який можна звірити з вашими open positions.
Dispute predictors (volume, ambiguity, history)
Три сигнали до dispute корелюють із майбутніми реальними disputes.
- Total volume: markets із lifetime volume понад $1M оскаржуються у 4 рази частіше, ніж малі markets. Більше капіталу на кону = більше стимулів оскаржувати.
- Ambiguous wording: будь-який market з формулюванням "or similar," "officially confirmed," або складеними умовами (дата AND конкретний outcome) має підвищену dispute rate.
- Past disputes on the same event: якщо ранній proposal уже був disputed і поданий повторно, другий proposal оскаржується у 2-3 рази частіше за норму.
Bot може обчислювати score "dispute probability" на основі цих ознак і уникати входу в markets вище певного порога поблизу resolution.
Цінова асиметрія до dispute
За кілька годин до ймовірного dispute ціна market'а часто рухається асиметрично: сторона, яку proposer назвав як YES, просідає вниз (бо trader'и бояться, що dispute змінить результат), а інша сторона зростає.
Якщо у вас є спрямований погляд на те, як саме dispute буде вирішено, це tradeable window. Ризик: якщо dispute не відбудеться, асиметрія розвертається, коли challenge window закривається без події, і ціни різко повертаються до напрямку proposal.
Чесно: більшість pre-dispute asymmetry trades є збитковими, бо більшість challenges завершується на користь original proposal. Стратегія працює лише тоді, коли у вас є конкретна інформація про те, чому цей dispute, ймовірно, буде sustained.
Post-dispute trade setups
Після подання dispute market торгується 24-72 години в стані "limbo" - відомо, що він disputed, але outcome ще потрібно проголосувати. Існує два setups.
Convergence to UMA consensus: якщо resolution dispute сигналізується рано (наприклад, відомий UMA voter публічно стає на одну сторону), ціна рухається до цього resolution. Bot, який стежить за сигналами UMA Discord / Twitter + price action, може зловити це в 30-60% випадків.
Volatility farming: періоди limbo мають широкі spreads. Терплячий market maker може заробляти spread tax у багатьох trader'ів, які по черзі входять і виходять протягом voting window. Inventory risk високий; розмір позиції має відповідати цьому.
Обидва підходи вимагають комфорту з реальною можливістю, що resolution піде проти вашої позиції. Ставте inventory у dispute-period не більше ніж удвічі меншим.
Коли НЕ торгувати disputed markets
Три ситуації, коли dispute trade за замовчуванням неправильний.
- У вас немає view, специфічного для UMA. Якщо ваша єдина перевага - "original proposal здається мені правильним", у вас немає edge над original proposer - а той, хто подав dispute, думає протилежне. Результат голосування - це coin flip, який ви не можете передбачити.
- Dispute стосується неоднозначного формулювання. Виборці UMA зазвичай стають на бік strict-reading-of-the-question. Якщо market казав "by January 31", а подія сталася 1 лютого, UMA розв’яже NO, незалежно від інтуїції trader'ів.
- Ви тримаєте inventory ще до dispute. Додавати до вже наявної позиції, щоб "average down" через limbo, - класичний патерн знищення капіталу. Або тримайте, або виходьте, ніколи не додавайте.
Code: підписка на UMA proposed/disputed events
Reference: WebSocket subscription до UMA OO events, відфільтрованих за Polymarket requester.
from web3 import Web3
w3 = Web3(Web3.WebsocketProvider(POLYGON_WSS))
oo = w3.eth.contract(address=UMA_OO_ADDR, abi=UMA_OO_ABI)
POLY = "0x...".lower()
dispute_filter = oo.events.DisputePrice.create_filter(fromBlock="latest")
propose_filter = oo.events.ProposePrice.create_filter(fromBlock="latest")
while True:
for event in dispute_filter.get_new_entries():
if event.args.requester.lower() == POLY:
on_dispute(event)
for event in propose_filter.get_new_entries():
if event.args.requester.lower() == POLY:
on_propose(event)
time.sleep(2)
def on_dispute(event):
market_q = decode_ancillary_to_question(event.args.ancillaryData)
send_telegram(f"DISPUTE: {market_q}")
# If we hold a position in this market, alert + consider exit
if market_q in our_positions:
flag_position_for_review(market_q)
Патерн: підписатися, декодувати, сповіщати. Діяти на disputes алгоритмічно - це high-risk; завдання bot'а зазвичай полягає в тому, щоб показати подію людському reviewer'у.





