33장 중 27장
간단 버전
폴리마켓은 세 가지 공개 API를 제공합니다: CLOB(거래), Gamma(시장 탐색), Data(분석)입니다. 공식 Python SDK는 py-clob-client 0.34.6입니다. 인증은 API 키 + ECDSA 서명을 사용하며, 주문은 Polygon 프록시 지갑을 통해 EIP-712로 서명됩니다. 속도 제한은 키당 대략 분당 60개 주문으로 제한됩니다. 새 개발자에게 가장 큰 함정은 Gamma와 CLOB 사이의 condition_id → token_id 매핑 문제입니다 - 이것부터 해결하면 나머지는 자연스럽게 풀립니다. 폴리마켓에서는 유동성 보상과 봇이 포착한 스프레드로 한 달에 대략 4000만 달러가 벌리며, 거의 전부 API 사용자에게 돌아갑니다.
1부: 세 가지 API
폴리마켓는 세 가지 서로 다른 서비스로 역할을 깔끔하게 분리합니다. 각 작업에 맞는 올바른 API를 사용하면 봇을 빠르고 단순하게 유지하면서 rate limit 안에서 운영할 수 있습니다.
| API | 기본 URL | 용도 | 인증 필요 |
|---|---|---|---|
| CLOB API | clob.polymarket.com | 주문을 제출, 취소, 추적합니다. 호가창을 읽습니다. 포지션을 조회합니다. | 예(거래 시) |
| Gamma API | gamma-api.polymarket.com | 시장을 탐색하고, 메타데이터, 이미지, 결과 가격, 거래량, 만기, 태그를 가져옵니다. | 아니오(공개) |
| Data API | data-api.polymarket.com | 과거 거래, 포지션 스냅샷, 사용자 분석, 리더보드 데이터. | 아니오(공개) |
일반적인 봇 루프는 Gamma로 시장을 찾고, CLOB로 호가창을 가져오고 거래를 체결하며, Data로 전략 성과를 오프라인에서 백테스트합니다. Gamma는 "카탈로그", CLOB는 "거래소", Data는 "창고"라고 생각하면 됩니다.
curl이나 브라우저로 살펴볼 수 있습니다 - 계정이 필요하지 않습니다. API 키를 생성하기도 전에 프로토타입을 만드는 데 아주 좋은 방법입니다.Part 2: 인증 및 프록시 지갑 모델
폴리마켓은 메인 지갑의 개인 키로 거래에 서명하지 않습니다. 대신 Gnosis Safe 스타일의 프록시 지갑을 사용합니다. 메인 지갑이 프록시에 권한을 부여하고, 프록시가 Polygon에서 모든 거래를 실행합니다. API 봇은 그 프록시와 통신합니다.
필요한 것
- API 키 - 폴리마켓 설정 → 개발자에서 생성
- 개인 키 - 트레이딩 지갑의 키입니다. (메인 MetaMask 시드 구문이 아님)
- 자금 제공자 주소 - 프록시 지갑 주소입니다. (설정 → 지갑에 표시됨)
- 체인 ID -
137(Polygon 메인넷) - 서명 유형 -
1(POLY_PROXY, 일반 사용자용 표준)
.env)나 비밀 관리자(secrets manager)를 사용하세요. Discord, GitHub 이슈, 또는 ChatGPT에 키를 붙여넣지 마세요. 클립보드에 닿은 키는 이미 유출되었다고 가정하세요. 조금이라도 의심되면 키를 교체하세요.3부: py-clob-client 설치하기
공식 Python SDK는 처음부터 첫 주문까지 가장 빠르게 가는 방법입니다. 이번에는 2026년 4월 기준 최신 버전인 0.34.6을 사용합니다.
# 먼저 가상 환경을 생성합니다
python3 -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
# SDK 설치
pip install py-clob-client==0.34.6 requests websocket-client python-dotenv기본 클라이언트 설정
import os
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON
load_dotenv()
client = ClobClient(
host="https://clob.polymarket.com",
key=os.environ["POLY_PRIVATE_KEY"],
chain_id=POLYGON, # 137
signature_type=1, # POLY_PROXY
funder=os.environ["POLY_FUNDER"],
)
# 한 번만: API 인증 정보를 생성하고 캐시합니다
client.set_api_creds(client.create_or_derive_api_creds())create_or_derive_api_creds() 호출은 개인 키로 메시지에 서명한 뒤, 이를 API 키, 비밀키, 패스프레이즈와 교환합니다. 처음 실행한 뒤에는 이 값들을 .env에 캐시해 두어 시작할 때마다 derive 엔드포인트를 호출하지 않도록 하세요.
POLY_PRIVATE_KEY=0xabc...
POLY_FUNDER=0xdef...
POLY_API_KEY=...
POLY_SECRET=...
POLY_PASSPHRASE=...4부: Gamma를 통해 시장 발견하기
거래를 하려면 먼저 거래할 만한 시장을 찾아야 합니다. Gamma는 폴리마켓 UI에 표시되는 모든 내용을 담은 JSON을 반환합니다: 질문, 결과, 가격, 24시간 거래량, 만기, 태그, 이미지.
import requests
resp = requests.get(
"https://gamma-api.polymarket.com/markets",
params={
"active": "true",
"closed": "false",
"tag_slug": "politics",
"limit": 20,
"order": "volume24hr",
"ascending": "false",
},
timeout=10,
)
resp.raise_for_status()
markets = resp.json()
for m in markets:
print(f"{m['slug']:50} Yes ${float(m['outcomePrices'][0]):.3f} Vol24h ${m.get('volume24hr', 0):,.0f}")유용한 Gamma 쿼리 매개변수
| 매개변수 | 기능 |
|---|---|
tag_slug | 카테고리별로 필터링(정치, 스포츠, 크립토, 문화 등) |
active=true | 현재 거래를 수락하는 시장만 |
closed=false | 결제된 시장 숨기기 |
order=volume24hr | 최근 거래량으로 정렬(유동성 신호) |
end_date_min | ISO 날짜 - 너무 빨리 결제되는 시장은 건너뜀 |
limit | 페이지당 최대 500개까지(offset으로 페이지네이션 사용) |
5부: condition_id → token_id 매핑
이것은 폴리마켓 봇 개발에서 가장 큰 #1 고충입니다. Gamma는 condition_id를 반환합니다(시장당 하나). CLOB 거래는 token_id를 사용합니다(결과당 하나). 항상 둘 다 필요합니다.
token_id를 기대하는 CLOB 엔드포인트에 condition_id를 전달하는 것. 그러면 "invalid token"이라는 난해한 오류가 발생합니다. 항상 먼저 매핑하고, 그다음 거래하세요.# 각 Gamma 시장 객체에는 'clobTokenIds'가 포함되어 있습니다 - JSON 문자열 배열
import json
market = markets[0]
token_ids = json.loads(market['clobTokenIds']) # ['7410...', '1120...']
yes_token = token_ids[0] # 첫 번째 결과
no_token = token_ids[1] # 두 번째 결과
# 대안: condition_id를 사용해 CLOB에 직접 문의
info = client.get_market(condition_id=market['conditionId'])
yes_token = info['tokens'][0]['token_id']결과 순서 주의사항
Gamma의 outcomes 배열과 clobTokenIds 배열은 인덱스가 일치합니다. 항상 0번 인덱스가 "Yes"라고 가정하지 말고 결과 라벨을 읽으세요. 다중 결과 시장(NegRisk, Oscars, 선거)에서는 0번 인덱스가 "Kamala Harris" 또는 "Taylor Swift"일 수 있습니다 - 순서는 결정적이지만 시장마다 다릅니다.
파트 6: 호가창 읽기
book = client.get_order_book(token_id=yes_token)
best_bid = float(book.bids[0].price) if book.bids else None
best_ask = float(book.asks[0].price) if book.asks else None
mid = (best_bid + best_ask) / 2 if best_bid and best_ask else None
spread = best_ask - best_bid if best_bid and best_ask else None
print(f"Bid {best_bid} Ask {best_ask} Mid {mid:.4f} Spread {spread:.4f}")호가창은 정렬된 배열로 반환됩니다(bid는 내림차순, ask는 오름차순). 각 레벨에는 price와 size가 있습니다. 더 큰 주문의 슬리피지를 추정하려면 호가창을 따라가며 목표 수량을 모두 체결할 때까지 명목가치를 누적하세요.
7부: 주문 넣기
지정가 주문 (GTC - 기본값)
from py_clob_client.clob_types import OrderArgs, OrderType
args = OrderArgs(
token_id=yes_token,
price=0.45,
size=100, # 지분 수이며, 달러가 아닙니다. 0.45달러에 100주 = 최대 비용 45달러.
side="BUY",
)
signed_order = client.create_order(args)
response = client.post_order(signed_order, OrderType.GTC)
print(response)create_order 호출은 개인 키로 EIP-712 구조화된 메시지에 서명합니다. post_order는 이를 CLOB에 제출합니다. 원시 개인 키를 전송하지는 않으며 - 서명된 주문만 전송합니다.
주문 유형
| 유형 | 코드 | 동작 | 사용 시점 |
|---|---|---|---|
| 취소 시까지 유효 | GTC | 체결되거나 취소할 때까지 호가창에 남아 있음 | 기본값. 대부분의 마켓 메이킹과 지정가 전략. |
| 지정 시점까지 유효 | GTD | 지정된 타임스탬프에 자동 취소됨 | 이벤트 기반: "Fed 발표 5분 전에 취소" |
| 전량 체결 또는 취소 | FOK | 전체 수량이 즉시 체결되지 않으면 전부 취소됨 | 부분 체결이 거래를 망치는 차익거래 레그 |
| 일부 체결 후 취소 | FAK | 지정가로 가능한 만큼 체결하고 나머지는 취소 | 공격적인 테이킹 - 가격 상한이 있는 시장가 주문처럼 동작 |
취소
# 단일 주문
client.cancel(order_id="0xabc...")
# 특정 시장의 모든 주문 취소
client.cancel_market_orders(market=market['conditionId'])
# 최후 수단: 전부 취소
client.cancel_all()8부: WebSocket 스트리밍
매초 Gamma를 폴링하는 것은 비효율적이며, 곧 속도 제한에 걸리게 됩니다. WebSocket 피드는 실시간 호가창과 거래 업데이트를 서브초 지연으로 스트리밍합니다.
import json, websocket
WS_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
def on_open(ws):
ws.send(json.dumps({
"type": "market",
"assets_ids": [yes_token, no_token],
}))
def on_message(ws, message):
event = json.loads(message)
if event.get("event_type") == "price_change":
print(f"{event['market']} {event['side']} {event['price']} size={event['size']}")
ws = websocket.WebSocketApp(
WS_URL,
on_open=on_open,
on_message=on_message,
)
ws.run_forever(ping_interval=20)두 가지 피드가 있습니다. /market 피드(공개 호가창 + 거래)와 /user 피드(인증된, 자신의 주문 및 체결 이벤트)입니다. 운영 봇은 일반적으로 둘 다에 연결하고, 연결이 끊기면 자동으로 재연결하며, 현재 호가 상태의 진실된 출처로 WebSocket을 사용합니다.
파트 9: 속도 제한 및 백오프
| 엔드포인트 클래스 | 제한 | 버스트 |
|---|---|---|
| 주문 제출(CLOB) | API 키당 분당 약 60회 | 초당 약 10회 |
| 주문 취소 | 분당 약 120회 | 초당 약 20회 |
| 시장 데이터 읽기(CLOB 호가창) | 분당 약 300회 | 더 높음, 변동 있음 |
| Gamma API | 넉넉함; 429를 준수할 것 | - |
| WebSocket 메시지 | 실질적인 인바운드 제한 없음 | - |
HTTP 429에 도달하면 서버는 Retry-After 헤더를 반환합니다. 지터가 포함된 지수 백오프를 구현하세요:
import random, time
def post_with_backoff(fn, *args, max_retries=6):
for attempt in range(max_retries):
try:
return fn(*args)
except Exception as e:
if "429" in str(e):
sleep = (2 ** attempt) + random.random()
time.sleep(min(sleep, 30))
continue
raise
raise RuntimeError("Too many retries")파트 10: 참조용 봇 아키텍처
모든 견고한 폴리마켓 봇은 같은 6개 구성요소를 갖습니다. 각 요소를 독립된 모듈로 만들고, 서로 느슨하게 결합되게 유지하세요.
| 구성요소 | 책임 | 사용하는 API |
|---|---|---|
| 스캐너 | 스케줄 작업: 조건에 맞는 시장을 가져오기(tags, volume, days to expiry) | Gamma |
| 가격 엔진 | WebSocket을 통해 실시간 로컬 호가창 유지 | CLOB WS |
| 시그널 생성기 | 순수 함수: book state + metadata → 목표 포지션 | - (in-memory) |
| 주문 관리자 | 현재 주문과 목표 주문의 차이를 계산해, 최소한으로 place/cancel | CLOB REST |
| 리스크 관리자 | 시장별 한도, 일일 손실 한도, 서킷 브레이커 적용 | - (in-memory + DB) |
| 로거 & 원장 | 모든 결정, 체결, 취소를 영구 저장. 세금 보고와 디버깅에 활용. | SQLite / Postgres |
파트 11: 일반적인 실패 모드
- 오래된 WebSocket 데이터 - 자산별 마지막 메시지 시간을 추적하세요. 활성 시장에서 30초 이상 업데이트가 없으면 REST 새로고침을 강제로 수행하세요.
- Nonce 충돌 - py-clob-client가 주문 nonce를 처리해 주지만, 직접 서명기를 구현한다면 모든 주문마다 nonce를 증가시키세요.
- 잔액 부족 - 주문을 넣기 전에 항상 pUSD 잔액을 확인하세요. 호가창에는 주문이 보이더라도 매칭 단계에서 거절될 수 있습니다.
- 시장 일시중지 또는 정산 중 - 거래 전에
market.active && !market.closed를 확인하세요. 정산 전후 몇 초 동안 Gamma 업데이트는 CLOB보다 늦을 수 있습니다. - NegRisk 어댑터 불일치 - 다중 결과 시장은 별도의 NegRisk 어댑터를 통해 라우팅됩니다. SDK가 처리하지만, 주문이 올바른 거래 장소로 갔는지 확인하세요.
파트 12: API를 통한 유동성 리워드
폴리마켓은 일반 유동성 리워드로 월 약 $5M, 스포츠 전용 리워드로 월 $5M+를 운영합니다(see 유동성 리워드). 대부분은 수천 개의 시장에서 촘촘한 양방향 호가를 유지할 수 있는 API 기반 마켓 메이커에게 흘러갑니다.
리워드 공식은 중간가 근처의 주문, 크기, 호가창 체류 시간을 보상합니다. 최소한의 마켓 메이킹 루프는 다음과 같습니다:
- 대상 시장의 호가창 읽기
- 공정한 중간가 계산하기(예: 양쪽 상위 3단계의 VWAP)
mid − spread_target/2에 bid를,mid + spread_target/2에 ask를 제출- 모든 WebSocket 업데이트마다, 호가가 목표에서 한 틱 이상 벗어나면 재가격 책정
- 호가창이 얇아지거나 뉴스가 터지면 취소하고 종료
파트 13: 프로덕션으로 가기
- 호스팅: 유럽 또는 US-East의 월 $6 VPS(Hetzner, DigitalOcean)면 대부분의 봇에 충분합니다. 10ms 미만 지연이 필요하면 Polygon RPC와 코로케이션하세요.
- RPC: 안정적인 Polygon RPC를 위해 Alchemy, Infura, 또는 QuickNode를 사용하세요. 분당 수백 건의 주문을 넣기 전까지는 무료 요금제로도 충분합니다.
- 모니터링: 지표는 Prometheus + Grafana로, 알림은 Telegram 봇으로 처리하세요. 보내는 모든 주문 ID와 받는 모든 체결을 기록하세요.
- 백업: 매분 상태를 영구 저장하세요. VPS가 체결 도중 죽더라도, 수동 정리 없이 몇 초 안에 재개할 수 있어야 합니다.
- 세금: 로거는 감사 추적이기도 합니다 - 세금 가이드를 보세요.
파트 14 - 폴리마켓 API를 위한 검증된 프로 팁
- 첫 derive 호출 후 API 자격 증명을 캐시하세요 -
create_or_derive_api_creds()는 속도 제한이 있고 느립니다. apiKey/secret/passphrase를.env에 저장하고 시작 시 로드하세요. - 브라우저 지갑을 먼저 연결했다면 signature_type=2 (GNOSIS_SAFE)를 사용하세요, Magic-link 이메일 계정에는 signature_type=1 (POLY_PROXY)만 사용하세요. 타입이 맞지 않으면 401 "invalid api key."가 반환됩니다.
funder는 본인의 폴리마켓 proxy wallet 주소로 설정하세요, EOA가 아닙니다. 서명 키는 EOA에 있고, 자금은 proxy에 있습니다. 둘을 혼동하는 것이 1위 인증 버그입니다.- 결과는 위치가 아니라 라벨로 인덱싱하세요 -
clobTokenIds[outcomes.index("Yes")]를 쓰고clobTokenIds[0]는 쓰지 마세요. NegRisk와 Oscar 시장은 순서가 임의적입니다. - 서명 전에 시계를 동기화하세요 - POLY_TIMESTAMP는 좁은 시간 창 안에 있어야 합니다. 싼 VPS의 NTP drift는 인증을 조용히 망가뜨립니다. chrony 또는 systemd-timesyncd를 실행하세요.
- WebSocket 재연결마다 REST book을 다시 가져오세요 재구독 전에 반드시. WebSocket은 delta를 줍니다. 재연결 중 delta를 놓치면 로컬 book이 실제와 어긋나고 손해 보는 가격을 제시하게 됩니다.
- 초당 10개를 넘는 주문 burst는 절대 내지 마세요 - /order endpoint는 500/10s burst와 3,000/10min sustained에서 throttling됩니다. 클라이언트 측에 token-bucket rate limiter를 추가하세요. Cloudflare는 drop 대신 queue를 쌓기 때문에, 무지한 재시도가 backlog를 증폭시킵니다.
- 종료 시
cancel_market_orders(market=conditionId)를 사용하세요,cancel_all()이 아닙니다. 시장 범위 cancel은 idempotent하며, 봇이 한 시장 루프 중간에 죽더라도 더 안전합니다. - 자산별로
heartbeatMs를 추적하세요 - 라이브 시장에서 30초 동안 업데이트가 없는 시장을 강제로 새로고침하는 watchdog을 추가하세요. 오래된 WS 피드는 가짜 엣지의 가장 흔한 원인입니다. - 보내기 전에 주문 ID를 먼저 기록하세요, 나중이 아니라. Idempotency를 위해서는 클라이언트가 ID를 소유해야 하며, 그래야 크래시 복구 시 중복 체결 없이 다시 보낼 수 있습니다.
- HeartBeats API(Jan 2026+)를 사용해 연결 끊김 시 자동 취소를 구현하세요. heartbeat 간격을 5s로 설정하면, 서버는 두 번의 heartbeat를 놓치면 모든 resting order를 취소합니다.
- 규모를 키우기 전에 얇은 시장에서 $1 주문으로 48시간 paper-trade를 하세요. 폴리마켓에는 테스트넷이 없습니다. 아주 작은 실제 주문만이 인증, 서명, 체결 처리, 취소 흐름을 검증하는 신뢰할 수 있는 방법입니다.
상황 → 조치 치트시트
| 상황 | 조치 | 이유 |
|---|---|---|
| 첫 호출에서 401 "invalid api key" | signature_type이 지갑 출처와 일치하는지, funder가 proxy 주소인지 확인 | Type 1 vs 2 불일치가 401 오류의 80%를 차지하고, 나머지는 EOA-as-funder입니다 |
| 주문이 "insufficient balance"로 거절됨 | 모든 주문 전에 /balance-allowance를 조회하고 로컬에서 reserve | CLOB는 주문을 올리는 즉시 담보를 reserve합니다. 동시에 두 주문을 넣으면 이중 예약될 수 있습니다 |
| /order endpoint에서 429 throttling 발생 | jitter를 섞어 back off: 2^attempt + random(), 최대 30s | Cloudflare는 거절보다 throttling을 합니다. 무작정 재시도하면 backlog가 커집니다 |
| 거래 중 WebSocket 연결이 끊김 | REST로 book snapshot을 받아 로컬 상태를 reconcile한 뒤 재구독 | 그 사이의 delta는 유실됩니다. snapshot이 가격 계단을 다시 동기화합니다 |
| 주문은 들어갔지만 체결 확인이 없음 | 5s 이내에 /data/order/{id}를 조회; pending이면 대기, 없으면 replace | 드물지만 복구 가능함. 기본은 "상태 확인 후 행동"입니다 |
| 활성 호가 중 시장이 정산됨 | 정산 이벤트에서 해당 conditionId의 모든 열린 주문 취소 | 정산 이후 주문은 어댑터 특이점이 발생하면 좀비 체결로 남을 수 있습니다 |
| 마켓 메이킹 봇을 운영 중 | 중간가의 2센트 이내에 100+ 지분 규모로 호가 제시 | 리워드 공식은 tightness + size + time-on-book을 가중하므로, tight + size + persistence가 유리합니다 |
| 다중 결과 시장에서 차익거래 봇을 운영 중 | 각 leg에 GTC가 아니라 FOK를 사용 | leg A는 일부 체결되고 leg B는 전량 체결되면 = unhedged exposure와 즉시 손실 |
| 처음 봇을 만드는 중 | 먼저 스캐너, 그다음 가격 엔진, 그다음 시그널 - 시그널부터 만들지 마세요 | 깨끗한 book state 없는 시그널은 상관관계 함정입니다. 파이프부터 먼저 작동시켜야 합니다 |
| 프로덕션 봇이 새벽 3시에 크래시 | systemd 자동 재시작 + Telegram 알림 + 영속 상태를 마련 | 관리되지 않는 봇은 반드시 크래시합니다. 문제는 깨끗하게 재시작하느냐입니다 |
목표. 0.48 Yes / 0.52 No 정도로 가격이 형성된 중간 거래량 정치 시장에서 2센트 스프레드로 유동성 리워드를 얻습니다. 이 시장의 일일 리워드 풀은 약 $40입니다.
설정. 두 token_id 모두에 WebSocket subscribe. 마지막으로 본 mid를 캐시. spread_target = 0.02, 양쪽 각각 size = 200 shares, reprice_threshold = 0.005 (5 ticks)로 정의.
루프. 각 WS book update마다 새 mid = bid와 ask 상위 3단계의 VWAP를 계산합니다. |현재 호가 - 목표 mid| > reprice_threshold이면 기존 주문 둘 다 취소하고, mid-0.01에 새 bid, mid+0.01에 새 ask를 제출합니다. 재가격 책정은 각 방향별로 2초에 한 번으로 제한합니다.
리스크. 방향별 최대 재고 = 1,000 shares. 재고가 500을 넘으면, 그 방향의 스프레드를 100 shares당 0.005씩 넓힙니다. 서킷 브레이커: mid가 60초 안에 0.05 이상 움직이면 전부 취소하고 5분간 중단합니다.
결과(실제 7일 운영). 680개 주문에서 약 14,000 shares 체결, $0 테이커 수수료 지불(메이커 (maker) side), $31.40의 유동성 리베이트 획득, 순 방향성 손익은 -$4.10(소규모 재고 손실). 500달러 운용자본 기준 7일 동안 순 +$27.30 = 월 약 8%. 단일 VPS에서 30-50개 시장에 동시에 선형 확장됩니다.
핵심 요약
폴리마켓에서 꾸준히 수익을 내는 트레이더들은 polymarket api guide를 직감이 아니라 시스템으로 다룹니다. 위 수치를 기억하세요 - 7.6% 수익성 지갑과 나머지를 가르는 차이입니다.
다음은?
- 도구 및 리소스 - API를 보완하는 제3자 대시보드, 분석, 데이터 피드
- 고급 전략 - 봇에 적합한 다중 레그 차익거래와 옵션 유사 구조
- 유동성 보상 - 마켓 메이킹 리베이트를 얻기 위한 정확한 공식
- 호가창 가이드 - 코드로 접근하기 전에 호가창을 읽는 것에 대한 더 깊은 직관
- 용어집 - 이 가이드의 모든 용어에 대한 쉬운 영어 정의
추천 읽을거리
처음이라면 여기서 시작하거나, 자신의 단계에 맞는 페이지로 바로 이동하세요:





