Polymarket Bot Tutorial · Chapter 15 of 32
Sports microstructure bots on Polymarket: in-game edge, scoreline-driven mispricing, the NBA tag (745) and Tennis tag (864), live data sources, and execution patterns for high-frequency sports markets.
Що охоплює цей розділ
Спортивні ринки - це найстабільніше активний неполітичний сегмент на Polymarket. Боти, які працюють, діляться на дві чіткі категорії: pre-game line-catchers, що торгують після того, як лінія вже встановлена, і in-game microstructure bots, які реагують на рух order book під час матчу. У цьому розділі охоплено обидва варіанти з конкретними tag IDs, data sources та latency budgets для кожного.
Спортивні ринки - це найзавантаженіший неполітичний сегмент на Polymarket. Робоча execution pattern поєднує live-score feed (ESPN, PandaScore) з order-book microstructure signals. У цьому розділі розглянуто, що працює для NFL, NBA, soccer і tennis, а також у чому відрізняється esports.
- Чому спортивні ринки можна торгувати
- Pre-game vs in-game (різні боти)
- Перевірені tag IDs (745 NBA, 864 Tennis)
- Data sources: ESPN, official APIs, on-screen
- Latency budget для in-game
- Пастка 0.99 / 0.01
- Code: subscribe to a games book and react
Чому спортивні ринки можна торгувати
Спортивні ринки закриваються в чітких часових рамках (від годин до днів), мають публічні live data та приваблюють безперервний order flow під час матчів. Усі три умови необхідні для tradeable market - політичні ринки не мають "defined timeframe," weather markets не мають "continuous flow," а маловідомі турніри не мають "public live data."
Трейдерська аудиторія на спортивних ринках також різноманітніша, ніж, наприклад, на election markets. Звичайні sports bettors оцінюють емоційно; informed traders коригують fair value протягом матчу. Різниця між цими двома підходами і є bot edge.
Розподіл volume нерівномірний: у NFL Sunday можуть проходити сотні мільйонів доларів через Polymarket sports markets; матч Saudi Pro League у вівторок увечері може дати менше ніж $50k. Підбирайте стратегію під ті ринки, де реально є action.
Pre-game vs in-game (різні боти)
Дві принципово різні bot designs.
Pre-game line-catcher: сканує ринки, які щойно відкрилися, виявляє mis-priced lines проти вашої моделі або проти цифри на більш точному venue, виставляє FOK buy. Тримає до in-play, інколи до resolution. Швидкість: хвилини, а не секунди. Edge: model + line-shopping.
In-game microstructure: підписується на WebSocket order book live game, реагує на imbalance signals + score events за секунди. Швидкість: секунди, а не хвилини. Edge: latency + reading order flow.
У цих двох підходів майже немає спільного коду. У них різні risk profiles, різні data sources, різні exit strategies. Бот, який намагається робити обидва, зазвичай не робить добре жодного; оберіть один.
Перевірені tag IDs (745 NBA, 864 Tennis)
Production tag IDs, перевірені в травні 2026 року для основних спортивних категорій. Використовуйте їх, щоб ефективно фільтрувати виклики /events.
| Спорт / Ліга | Tag ID | Tag slug | Примітки |
|---|---|---|---|
| NBA | 745 | nba | найвищий обсяг Oct-Jun |
| NFL | 450 | nfl | пік Sun/Mon Sep-Feb |
| Tennis (all) | 864 | tennis | цілорічно, турнірний cadence |
| Soccer (general) | 1059 | soccer | поєднуйте з підтегами нижче |
| EPL | 739 | epl | |
| UCL | 2186 | uefa-champions-league | |
| Esports (all) | 702 | esports | LoL+CS2+Valorant+Dota |
| MLB | 1245 | mlb | пік Apr-Oct |
| NHL | 823 | nhl | пік Oct-Jun |
Tag IDs стабільні між роками. Нові теги додаються (Saudi Pro League, IPL), але старі теги не перенумеровують.
Data sources: ESPN, official APIs, on-screen
Для традиційних sports безкоштовний ESPN scoreboard API покриває все необхідне: scores, period/clock, win-probability, інколи shot location. Ключ не потрібен; rate limit діє лише на рівні IP. Шаблон endpoint: https://site.api.espn.com/apis/site/v2/sports/<sport>/<league>/scoreboard.
Для esports у ESPN немає покриття. Варіанти: PandaScore ($30-60/mo, industry standard), HLTV (лише CS2, можна scrape, без API), Liquipedia (підтримується спільнотою, можна scrape, повільніший cadence оновлень).
On-screen feeds (платити за TV stream і читати scorebug через OCR) працюють, але операційно складні. Рекомендовано лише якщо у вас є стратегія, якій потрібні оновлення швидше ніж за 3 секунди для спорту, який жоден API не покриває в реальному часі.
Latency budget для in-game
End-to-end latency budget для in-game reactive bot.
- Score event happens: t=0
- Source feed reflects: t+3-15s (ESPN: ~10s; PandaScore: ~3s)
- Your bot reads the feed: t+10-16s
- Bot decides action: +50ms
- FOK order placed: +200-500ms
- Matched at CLOB: +300-1000ms (network + matching)
Total: 11-17 seconds. Найшвидші professional firms досягають 3-5 секунд end-to-end завдяки платним premium feeds і co-located VPS. Retail bots, що працюють на стандартних хостингах і безкоштовному ESPN, знаходяться на повільнішому боці.
Стратегії, яким потрібні sub-5s, не є життєздатними для retail. Стратегії, що працюють у вікні 10-17s: line-catching після score, fading overreactions, late-game certainty plays.
Пастка 0.99 / 0.01
Найпоширеніша помилка in-play sports bot: купити heavy favorite за 0.99 за хвилину до кінця, очікуючи легкі +1¢. Це провалюється з трьох причин.
По-перше, 1% implied probability для underdog - це не нуль: late comebacks трапляються з відчутною частотою. Впевнена перемога на 99.5%, програна 200 разів, дає одну втрату на повний position size.
По-друге, spread на 0.99/0.01 означає, що ви платите 99c за share, заробляєте 1c у разі успіху, і втрачаєте 99c у рідкісному reversal. Risk-reward жорсткий.
По-третє, bot, який використовує GTC sell на 0.999, рідко отримає fill - покупців за такою ціною немає. Position доживає до resolution. Якщо ринок виграє, ви отримали 1c. Якщо стається reversal, ви втрачаєте 99c.
Це реальні гроші, які втрачають builder-и, що не порахували математику. Тримайтеся подалі від ринків із ціною 0.95+ , якщо ваша стратегія не створена спеціально під redemption-arbitrage profile.
Code: subscribe to a games book and react
Reference: підписка на WebSocket конкретної NBA game, логування book updates, запуск FOK на signal imbalance.
import websocket, json
THRESHOLD = 0.5 # imbalance level to trigger
def on_message(ws, message):
msg = json.loads(message)
if msg.get("event_type") != "book": return
bids = msg.get("bids", [])
asks = msg.get("asks", [])
bid_depth = sum(float(b["price"]) * float(b["size"]) for b in bids[:5])
ask_depth = sum(float(a["price"]) * float(a["size"]) for a in asks[:5])
total = bid_depth + ask_depth
if total < 100: return # too illiquid
imb = (bid_depth - ask_depth) / total
if abs(imb) > THRESHOLD:
print(f"signal imb={imb:.2f} bid={bid_depth:.0f} ask={ask_depth:.0f}")
# fire FOK here
ws = websocket.WebSocketApp(
"wss://ws-subscriptions-clob.polymarket.com/ws/market",
on_open=lambda ws: ws.send(json.dumps({"type":"Market","markets":["<CONDITION_ID>"]})),
on_message=on_message
)
ws.run_forever()
Production additions: cooldown between fires, per-token inventory cap, kill on stale book (no message in 30s).





