Polymarket Bot Tutorial · 32개 중 15장
Polymarket의 sports microstructure bot: in-game edge, scoreline-driven mispricing, NBA tag (745)와 Tennis tag (864), live data sources, 그리고 고빈도 sports market을 위한 execution pattern.
이 장에서 다루는 내용
Sports market은 Polymarket에서 정치가 아닌 섹션 중 가장 꾸준히 활발한 영역입니다. 잘 작동하는 bot은 크게 두 가지로 나뉩니다. line이 설정된 뒤 한 번만 거래하는 pre-game line-catcher와, 경기 중 order book의 움직임에 반응하는 in-game microstructure bot입니다. 이 장에서는 각각에 적용되는 specific tag ID, data source, latency budget까지 함께 다룹니다.
Sports market은 Polymarket에서 정치가 아닌 섹션 중 가장 바쁜 영역입니다. 잘 작동하는 execution pattern은 live-score feed(ESPN, PandaScore)와 order-book microstructure signal을 결합합니다. 이 장에서는 특히 NFL, NBA, soccer, tennis에서 무엇이 작동하는지, 그리고 esports는 어떻게 다른지 다룹니다.
- 왜 sports market은 tradeable한가
- Pre-game vs in-game (서로 다른 bot)
- 검증된 tag ID (745 NBA, 864 Tennis)
- Data source: ESPN, official APIs, on-screen
- In-game을 위한 latency budget
- 0.99 / 0.01 trap
- Code: games book에 subscribe하고 반응하기
왜 sports market은 tradeable한가
Sports market은 정의된 시간 범위(몇 시간에서 며칠) 안에 청산되고, public live data가 있으며, 경기 동안 지속적인 order flow를 끌어옵니다. 이 세 가지는 tradeable market에 모두 필요합니다. 정치 market에는 "defined timeframe"이 부족하고, weather market에는 "continuous flow"가 부족하며, 잘 알려지지 않은 tournament에는 "public live data"가 부족합니다.
Sports market의 trader 구성도 election market보다 더 다양합니다. 일반 sports bettor는 감정적으로 price를 매기고, informed trader는 경기 진행에 따라 fair value로 교정합니다. 이 둘 사이의 gap이 bot edge입니다.
Volume 분포는 고르지 않습니다. NFL Sunday에는 Polymarket sports market 전반에 걸쳐 수억 달러가 움직이지만, 화요일 밤 Saudi Pro League 경기는 5만 달러도 되지 않을 수 있습니다. 실제로 action이 있는 곳에 맞춰 strategy 규모를 정해야 합니다.
Pre-game vs in-game (서로 다른 bot)
근본적으로 서로 다른 두 가지 bot design이 있습니다.
Pre-game line-catcher: 방금 열린 market을 스캔하고, 모델이나 더 sharp한 venue의 숫자에 비해 잘못 price된 line을 찾아 FOK buy를 넣습니다. in-play까지, 때로는 resolution까지 보유합니다. Speed: 초가 아니라 분 단위. Edge: model + line-shopping.
In-game microstructure: live game의 order book WebSocket에 subscribe하고, imbalance signal + score event에 몇 초 안에 반응합니다. Speed: 분이 아니라 초 단위. Edge: latency + order flow 읽기.
두 방식은 거의 code를 공유하지 않습니다. risk profile도 다르고, data source도 다르고, exit strategy도 다릅니다. 둘 다 하려는 bot은 결국 둘 다 제대로 못합니다. 하나를 고르세요.
검증된 tag ID (745 NBA, 864 Tennis)
주요 sports category에 대해 2026년 5월 기준 production tag ID가 검증되었습니다. 이를 사용해 /events 호출을 효율적으로 필터링하세요.
| Sport / League | Tag ID | Tag slug | Notes |
|---|---|---|---|
| NBA | 745 | nba | 10월~6월 최고 volume |
| NFL | 450 | nfl | 9월~2월 일요일/월요일 피크 |
| Tennis (all) | 864 | tennis | 연중 운영, tournament cadence |
| Soccer (general) | 1059 | soccer | 아래 sub-tag와 함께 사용 |
| EPL | 739 | epl | |
| UCL | 2186 | uefa-champions-league | |
| Esports (all) | 702 | esports | LoL+CS2+Valorant+Dota |
| MLB | 1245 | mlb | 4월~10월 피크 |
| NHL | 823 | nhl | 10월~6월 피크 |
tag ID는 연도에 걸쳐 안정적입니다. 새로운 tag(Saudi Pro League, IPL)는 추가되지만, 기존 tag는 번호가 바뀌지 않습니다.
Data source: ESPN, official APIs, on-screen
전통적인 sports의 경우 무료 ESPN scoreboard API가 필요한 모든 것을 제공합니다: score, period/clock, win-probability, 때로는 shot location까지. key는 필요 없고 IP 수준에서만 rate limit이 있습니다. Endpoint pattern: https://site.api.espn.com/apis/site/v2/sports/<sport>/<league>/scoreboard.
Esports에는 ESPN coverage가 없습니다. 선택지는 다음과 같습니다: PandaScore($30-60/mo, 업계 표준), HLTV(CS2 전용, scrape 가능, API 없음), Liquipedia(커뮤니티 유지, scrape 가능, update cadence가 더 느림).
On-screen feed(TV stream을 구매하고 OCR로 scorebug를 읽는 방식)는 가능하지만 운영 부담이 큽니다. 실시간으로 API가 제공되지 않는 종목에서 3초 미만 update가 필요한 strategy가 있을 때만 권장합니다.
In-game latency budget
In-game reactive bot의 end-to-end latency budget입니다.
- Score event 발생: t=0
- Source feed에 반영: t+3-15초 (ESPN: 약 10초; PandaScore: 약 3초)
- Bot이 feed를 읽음: t+10-16초
- Bot이 action 결정: +50ms
- FOK order 제출: +200-500ms
- CLOB에서 체결: +300-1000ms (network + matching)
합계: 11-17초. 가장 빠른 professional firm은 premium feed 유료 구독과 co-located VPS를 통해 end-to-end 3-5초를 달성합니다. 표준 host와 무료 ESPN을 사용하는 retail bot은 더 느린 구간에 있습니다.
5초 미만이 필요한 strategy는 retail에게는 현실적이지 않습니다. 10-17초 창에서 작동하는 strategy는 다음과 같습니다: score 이후 line-catching, overreaction fading, late-game certainty play.
0.99 / 0.01 trap
가장 흔한 in-play sports bot 실패는 종료 1분 전 heavy favorite를 0.99에 사서 손쉽게 +1¢를 기대하는 것입니다. 실패하는 이유는 세 가지입니다.
첫째, underdog의 1% implied probability가 0은 아닙니다. 늦은 역전은 무시할 수 없는 빈도로 발생합니다. 99.5% 승리가 200번 반복되면, 풀 포지션 크기 기준으로 한 번은 손실이 납니다.
둘째, 0.99/0.01의 spread는 주당 99센트를 내고, 성공 시 1센트를 벌며, 드문 역전 시 99센트를 잃는 구조입니다. risk-reward가 매우 나쁩니다.
셋째, GTC sell을 0.999에 넣는 bot은 거의 체결되지 않습니다. 그 가격에는 buyer가 없습니다. 포지션은 resolution까지 이어집니다. 이기면 1센트를 벌고, 역전이 일어나면 99센트를 잃습니다.
이 trap은 수학을 제대로 돌리지 않은 builder가 실제 돈을 잃는 사례입니다. redemption-arbitrage profile에 맞게 설계된 strategy가 아니라면 0.95 이상의 price market에는 들어가지 마세요.
Code: games book에 subscribe하고 반응하기
Reference: 특정 NBA 경기의 WebSocket에 subscribe하고, book update를 기록하며, imbalance signal에서 FOK를 실행합니다.
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 추가 사항: 발동 사이의 cooldown, token별 inventory cap, stale book(30초 동안 메시지 없음)일 때 kill.





