Polymarket 트레이딩 봇 만드는 방법
대부분의 Polymarket 봇 튜토리얼은 더 이상 존재하지 않는 SDK를 기반으로 합니다. 이 글은 처음부터 끝까지 현재 기준의 방법을 다룹니다. 2026년에 무엇을 설치해야 하는지, 인증이 실제로 어떻게 동작하는지, 데이터 공백이 어디에 있는지, 그리고 첫 주에 봇을 죽이는 실패 유형까지.
대부분의 Polymarket 봇 튜토리얼은 낡았습니다
Polymarket은 통합 SDK로 전환했습니다. Python은 polymarket-client, TypeScript는 @polymarket/client입니다. 이 SDK들은 py-clob-client, @polymarket/clob-client-v2, @polymarket/builder-relayer-client, @polymarket/builder-signing-sdk를 대체합니다. 가이드가 pip install py-clob-client를 하라고 하거나 AI 어시스턴트가 그 import를 생성한다면, 오래된 자료를 기반으로 하고 있는 것입니다. 아래 내용은 모두 현재 패키지를 사용합니다.
Polymarket 봇은 여섯 부분으로 이루어집니다. 마켓 찾기, 가격 읽기, 결정하기, 주문 넣기, 포지션 관리하기, 살아남기. 처음 두 가지는 쉽고, 네 번째는 손이 많이 가며, 여섯 번째에서 대부분의 봇이 실제로 죽습니다. 이 가이드는 검증된 코드로 전체 과정을 따라가며, 정말 어려운 부분에 대해서는 솔직하게 말합니다.
0단계: 봇이 무엇을 거래할지 정하기
코드 한 줄을 쓰기 전에 해 둘 가치가 있습니다. 이후의 모든 것을 결정하기 때문입니다. Polymarket 마켓은 공통점이 거의 없는 두 계열로 나뉩니다:
| 이벤트 마켓 | 암호화폐 Up/Down 마켓 | |
|---|---|---|
| 예시 | 선거, 스포츠, 시상식 | btc-updown-5m, eth-updown-15m |
| 수명 | 수주에서 수개월 | 5분에서 24시간 |
| 가격을 움직이는 것 | 뉴스, 정보, 심리 | 시장 미시구조와 주문 흐름 |
| 우위의 원천 | 더 많은 정보 | 더 빠른 속도 또는 더 나은 가격 책정 |
| 하루 거래 수 | 몇 건 | 수백 건 |
| 지연 시간 민감도 | 낮음 | 높음 |
자동화를 한다면 거의 확실히 두 번째 계열에 관심이 있을 겁니다. 빈도가 높고, 연속적이며, 코드로 옮길 수 있을 만큼 기계적입니다. 자세한 내용은 Up/Down 마켓 설명에서 다룹니다.
1단계: 최신 SDK 설치하기
두 SDK 모두 Polymarket의 모든 API에 대한 하나의 클라이언트 인터페이스, 타입이 지정된 모델, 일관된 페이지네이션, 내장 서명을 제공합니다. 직접 다시 구현하고 싶지 않은 번거로운 작업이 상당히 많이 해결됩니다.
pip install polymarket-client
# or: uv add polymarket-client / poetry add polymarket-clientPython 클라이언트는 네 가지가 있으며, 올바른 것을 고르는 일이 보기보다 중요합니다:
| 클라이언트 | 공개 데이터 | 거래 | 실시간 |
|---|---|---|---|
AsyncPublicClient | 예 | 아니요 | 예 |
AsyncSecureClient | 예 | 예 | 예 |
PublicClient | 예 | 아니요 | 아니요 |
SecureClient | 예 | 예 | 아니요 |
subscribe()를 구현하지 않습니다. 봇에는 비동기 버전이 필요하고, 동기 버전은 스크립트와 노트북용입니다.공개 클라이언트로 시작하세요
마켓 탐색, 가격 읽기, 신호 생성, 전체 데이터 파이프라인을 AsyncPublicClient로 만들고 테스트할 수 있습니다. 키도, 지갑도, 위험도 없습니다. 주문을 넣을 준비가 되었을 때만 AsyncSecureClient로 바꾸세요. 대부분의 버그는 그보다 훨씬 전에 드러납니다.
2단계: 마켓 찾기
공개 마켓 탐색에는 자격 증명이 전혀 필요 없습니다. SDK의 페이지네이터는 모든 목록 메서드에서 일관되므로 이 패턴은 그대로 응용할 수 있습니다.
import asyncio
from polymarket import AsyncPublicClient
async def main() -> None:
async with AsyncPublicClient() as client:
pages = client.list_markets(closed=False, page_size=50)
# Iterate items directly when page boundaries do not matter.
async for market in pages.iter_items():
if "updown" in (market.slug or ""):
print(market.slug, market.condition_id)
asyncio.run(main())여기서 두 가지를 꼭 기억하세요. 첫째, page.next_cursor는 불투명한 커서입니다. 나중에 스캔을 재개하려면 그대로 저장하고, 파싱하거나 직접 만들려고 하지 마세요. 둘째, /markets는 플랫폼 전체에서 가장 엄격한 요청 한도(10초에 300회)를 가지므로, 반복 루프에서 다시 목록을 조회하지 말고 마켓 메타데이터를 캐시하세요. 전체 수치는 요청 한도 레퍼런스에 있습니다.
3단계: 인증하기
거래에는 지갑이 필요합니다. Python SDK는 로컬 개인 키로 서명자를 만듭니다:
import os
from polymarket import AsyncSecureClient
client = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
)내부적으로 Polymarket은 두 개의 인증 계층을 사용합니다. L1은 주소를 통제하고 있음을 증명하고 API 자격 증명을 생성하거나 파생할 수 있게 해주는 EIP-712 지갑 서명이고, L2는 그 자격 증명으로 각 비공개 요청을 HMAC-SHA256 서명합니다. SDK가 두 가지를 모두 처리하며, 이것이 SDK를 쓰는 주된 이유입니다.
마주치게 될 두 가지 인증 실패
불완전한 자격 증명: L2에는 키, 시크릿, 그리고 패스프레이즈가 필요합니다. 셋 중 둘만 있으면 형식은 올바르지만 검증할 수 없는 서명이 만들어집니다. 시계 오차: 서명에는 타임스탬프가 포함되므로, 시계가 몇 초 어긋난 컨테이너는 거부됩니다. 두 경우 모두 똑같이 도움이 안 되는 Unauthorized/Invalid api key로 나타납니다. 자세한 해석은 오류 레퍼런스에 있습니다.
키 관리
그 개인 키로 자금을 옮길 수 있습니다. 환경 변수나 시크릿 매니저에 보관하세요. 소스 코드, 컨테이너 이미지, 곧 푸시할 저장소에는 절대 넣지 마세요. 봇이 잃어도 되는 만큼만 넣어 둔 전용 지갑을 사용해, 버그로 인한 손실이 새벽 3시의 판단력이 아니라 잔액으로 제한되도록 하세요.
4단계: 실시간 가격 받기
첫 봇에서 가장 흔한 아키텍처 실수는 타이머로 /book을 폴링하는 것입니다. 요청 한도를 소모하고, 지연 시간을 늘리며, 확장성이 나쁩니다. 대신 구독하세요. SDK가 여러 피드를 하나의 이벤트 스트림으로 합쳐 줍니다.
from polymarket.streams import MarketSpec
async with await client.subscribe([MarketSpec(token_ids=[token_id])]) as stream:
async for event in stream:
# MarketBookEvent | MarketPriceChangeEvent | MarketLastTradePriceEvent
# | MarketTickSizeChangeEvent | MarketBestBidAskEvent
# | NewMarketEvent | MarketResolvedEvent
handle(event)SDK 대신 원시 WebSocket에 연결한다면 10초마다 PING을 보내야 연결이 끊기지 않으며, best_bid_ask, new_market, market_resolved 이벤트를 받으려면 custom_feature_enabled: true가 필요합니다. 이 내용과 재연결 패턴은 WebSocket 가이드에서 다룹니다. 별도로, 기준 가격은 2026년 9월 15일에 PolyBolt로 이전되었습니다. 봇이 현물 가격을 스트리밍한다면 마이그레이션 가이드를 참고하세요.
5단계: 주문 넣기
시장가 주문이 동작하는 가장 간단한 방법이며, amount는 주식 수가 아니라 지출할 pUSD 금액 단위입니다:
market = await client.get_market(slug="<market-slug>")
token_id = market.outcomes.yes.token_id
assert token_id is not None
response = await client.place_market_order(
token_id=token_id,
side="BUY",
amount="10", # spend up to 10 pUSD
)
if not response.ok:
raise RuntimeError(response.message)
# Settlement is on-chain and asynchronous — this waits for it.
hashes = await client.wait_for_order_fill_settlement(response)지정가 주문을 걸어 둘 때는 다른 무엇보다 두 가지 규칙에 먼저 걸리게 됩니다. 가격은 마켓의 틱 그리드에 맞아야 합니다. 틱 크기는 마켓마다 다르고 마켓이 성숙하면서 좁아질 수 있으므로, 0.01이라고 가정하지 말고 마켓마다 GET /tick-size를 호출하세요. 그리고 마켓의 최소 규모보다 작은 주문은 즉시 거부됩니다. 두 경우 모두 구체적인 메시지와 함께 400으로 반환되며, 오류 레퍼런스에 정리되어 있습니다.
배치 요청의 200이 성공을 뜻하지는 않습니다
여러 주문을 제출하면 주문별 실패는 200 응답 배열 안에 개별 errorMsg 필드로 반환됩니다. 배열을 순회하며 모든 항목을 확인하세요. HTTP 상태만 확인하는 봇은 실제로는 없는 포지션을 가지고 있다고 조용히 믿게 됩니다. 정말 위험한 종류의 버그입니다.
6단계: 전략이 통하는지 확인하기
지금까지의 내용은 모두 배관 작업이며, 배관은 쉬운 절반입니다. 어려운 질문은 전략이 돈을 버는지이고, 여기서 Polymarket 봇 개발은 암호화폐 현물 봇 개발과 크게 갈라집니다.
테스트넷이 없습니다. 샌드박스도, 데모 API도, 모의 거래 엔드포인트도 없습니다. Polymarket 문서 어디에도 이런 단어가 나오지 않습니다. 첫 실제 주문은 실제 거래 상대방과의 실제 돈 거래입니다. 네 가지 우회 방법은 Polymarket에 테스트넷이 있나요를 참고하세요.
과거 오더북도 없습니다. 마켓 결과가 확정되면 오더북은 404를 반환합니다. 약 1분에 한 포인트의 가격 시계열만 남으며, 그마저도 시작과 끝을 명시적으로 지정해 요청해야 합니다:
TOK=<token_id_of_a_resolved_market>
START=<epoch seconds the window opened>
END=$((START + 300))
curl -s "https://clob.polymarket.com/book?token_id=$TOK"
# {"error":"No orderbook exists for the requested token id"} HTTP 404
curl -s "https://clob.polymarket.com/prices-history?market=$TOK&startTs=$START&endTs=$END&fidelity=1"
# about 6 points for a 5-minute market; interval=max returns [] for older ones따라서 Polymarket으로는 백테스트할 수 없습니다. 마켓이 진행되는 동안 누군가 기록한 특정 시점 스냅샷이 필요합니다. PolyTest가 암호화폐 Up/Down 마켓에 대해 제공하는 것이 바로 그것입니다. 240M+개 스냅샷, 한쪽당 8단계 오더북 깊이, 1초 미만 단위 타임스탬프, 결과가 확정된 마켓 보존:
import os, requests
BASE = "https://api.polytest.io/api/v1"
H = {"X-API-Key": os.environ["POLYTEST_KEY"]}
markets = requests.get(f"{BASE}/markets",
params={"coin": "btc", "market_type": "5m", "limit": 50},
headers=H).json()["markets"]
snaps = requests.get(f"{BASE}/markets/{markets[0]['id']}/snapshots",
params={"include_orderbook": "true"},
headers=H).json()["snapshots"]
def simulate_fill(levels, qty):
"""Walk the ladder. Mid-price is not a price you can trade at."""
filled = cost = 0.0
for lvl in levels:
take = min(lvl["size"], qty - filled)
cost += take * lvl["price"]
filled += take
if filled >= qty:
break
return cost / filled if filled >= qty else None
print(simulate_fill(snaps[0]["orderbook"]["asks"], 500))백테스트를 무의미하게 만드는 실수
중간가로 백테스트하면 전략이 아니라 산술 항등식을 테스트하는 것입니다. Up/Down 스프레드는 $1에 정산되는 계약에서 보통 2–5센트이며, 주장되는 우위보다 넓은 경우가 많습니다. 오더북을 따라가며 실제로 받았을 체결을 반영하세요. 이 하나의 변화로 대부분의 전략이 탈락합니다. 자금을 넣기 전에 저렴하게 탈락시키는 것, 바로 그것이 원하는 바입니다.
7단계: 프로덕션에서 살아남기
한 시간 동안 올바르게 거래하다가 멈춰 버리는 봇은 봇이 없는 것보다 나쁩니다. 실제로 발생하는 실패 유형을 대략 자주 발생하는 순서로 정리했습니다:
| 실패 | 보이는 현상 | 대처 |
|---|---|---|
| 요청 한도 초과 | 오류가 아니라 지연 시간 증가 | Cloudflare는 거부하지 않고 속도를 늦춥니다. 오류율이 아니라 p99를 지켜보세요 |
| 매칭 엔진 재시작 | 425 Too Early | 백오프로 재시도하세요. 단발성 오류에는 알림을 보내지 마세요 |
| 거래소 일시 중지 | 503 | 신규 진입을 멈추세요. 허용되면 취소하세요 |
| Cancel-only 모드 | 신규 주문에 503 | 취소 경로는 진입 경로와 독립적으로 동작해야 합니다 |
| Post-only 모드 | 503 + retry_after_seconds | 정확히 그 시간만큼 기다린 뒤 재시도하세요 |
| 서명자 불일치 | 400 주문 서명자 오류 | 다른 지갑의 오래된 자격 증명입니다. 다시 파생하세요 |
| WebSocket 끊김 | 무응답 | 데이터가 없다는 것은 변화가 없다는 뜻이 아닙니다. 마지막 메시지 이후 경과 시간을 추적하세요 |
| 시계 오차 | 어제까지 동작하던 봇에서 401 | NTP를 실행하세요. 시간을 가장 많이 잡아먹는 문제입니다 |
무응답이 위험한 상태입니다
끊긴 WebSocket은 예외를 발생시키지 않습니다. 그냥 메시지 전달을 멈출 뿐입니다. 포지션을 보유한 채 업데이트를 받지 못하는 봇은 시장이 마지막으로 본 가격에 멈춰 있다고 태연히 믿습니다. 마지막 메시지의 경과 시간을 추적하고, 데이터가 오래된 상태를 평온함이 아니라 오류 조건으로 취급하세요.
- 진입 경로보다 취소 경로를 먼저 테스트하세요. 빠져나오지 못하는 것이 들어가지 못하는 것보다 훨씬 나쁩니다.
- 노출 한도는 머릿속이 아니라 코드로 제한하세요. 엄격한 포지션 한도는 로직 버그를 무한한 손실이 아니라 작은 손실로 바꿉니다.
- 킬 스위치를 만들고 실제로 테스트하세요. 테스트하지 않은 킬 스위치는 주석에 불과합니다.
- 예약 작업에 지터를 추가하세요. 정확히 같은 초에 깨어나는 워커들은 평균 요청률을 훨씬 넘는 동시 급증을 만듭니다.
- 첫 주에는 모든 요청과 응답을 로그로 남기세요. 반드시 필요하게 되며, 나중에는 재구성할 수 없습니다.
- 메시지가 아니라 범주로 알림을 보내세요. Polymarket은 오류 문자열에 주문 ID와 가격을 넣으므로, 원시 문자열 기반 알림은 잡음일 뿐입니다.
현실적인 개발 순서
시간과 돈을 가장 적게 낭비하는 순서입니다:
- 읽기 전용 연동을 공개 클라이언트로, 키 없이 진행합니다. 탐색, 파싱, 페이지네이션, 백오프를 검증합니다. 하루, 무료.
- 전략을 백테스트합니다. 과거 오더북과 정직한 체결 시뮬레이션으로 진행합니다. 대부분의 아이디어가 여기서 탈락합니다. 그것이 목적입니다.
- 실시간 모의 거래를 최소 일주일 진행합니다. 주문을 보내는 대신 의도한 주문을 로그로 남깁니다. 가동 시간, 재연결, 타이밍을 검증합니다.
- 최소 규모로 실거래를 시작하며, 엄격한 노출 상한과 테스트된 킬 스위치를 갖춥니다.
- 실거래 수치가 모의 거래 수치와 일치할 때만 규모를 키우세요. 차이가 난다면 체결 모델이 틀린 것입니다. 2단계로 돌아가세요.
1, 3, 4단계는 며칠이면 만들 수 있습니다. 2단계는 Polymarket이 보관하지 않는 데이터가 필요하며, 가장 확실하게 돈을 아껴주는 단계이기도 합니다. PolyTest 무료 체험을 시작하세요. 아이디어가 스프레드를 견디는지 알아보기에 충분합니다.
자주 묻는 질문
- Polymarket 트레이딩 봇은 어떻게 만드나요?
- 최신 통합 SDK를 설치하세요. Python은 polymarket-client, TypeScript는 @polymarket/client입니다. AsyncPublicClient로 자격 증명 없이 마켓을 찾고 가격을 읽은 다음, 거래할 때는 개인 키와 함께 AsyncSecureClient를 사용하세요. 가격을 폴링하지 말고 WebSocket market 채널을 구독하고, place_market_order로 주문을 넣으며, 425, 429, 503 응답은 백오프로 처리하세요. Polymarket에는 테스트넷이 없고 오더북 기록도 보관하지 않으므로, 실거래 전에 현실적인 체결 시뮬레이션과 함께 과거 오더북 스냅샷으로 백테스트하세요.
- 2026년에 Polymarket 봇에는 어떤 SDK를 써야 하나요?
- 통합 SDK를 사용하세요. Python은 polymarket-client, TypeScript는 @polymarket/client입니다. 이 SDK들은 이전의 py-clob-client, @polymarket/clob-client-v2, @polymarket/builder-relayer-client, @polymarket/builder-signing-sdk 패키지를 대체합니다. 통합 Rust SDK도 개발 중입니다. 대부분의 튜토리얼과 AI가 생성한 코드는 여전히 지원 중단된 패키지를 참조하므로, 코드 조각을 믿기 전에 import를 확인하세요.
- 실제 돈을 걸기 전에 Polymarket에서 모의 거래를 할 수 있나요?
- Polymarket을 통해서는 불가능합니다. 테스트넷, 샌드박스, 데모 API가 없습니다. 실전 전략 루프를 프로덕션 마켓 데이터에 대해 실행하면서, 주문을 제출하는 대신 의도한 주문을 그 순간의 오더북 상태와 함께 로그로 남기는 방식으로 모의 거래 장치를 직접 만들 수 있습니다. 이렇게 하면 봇이 운영상 견고하다는 것을 증명할 수 있지만, 체결을 가정하므로 대기열 순서나 시장 충격은 알 수 없습니다.
- Polymarket 트레이딩 봇은 어떻게 백테스트하나요?
- 서드파티의 과거 오더북 스냅샷이 필요합니다. Polymarket API는 결과가 확정된 마켓의 오더북에 대해 404를 반환하고 가격 기록은 약 1분에 한 포인트만 보관하기 때문입니다. 전체 깊이를 갖춘 특정 시점 스냅샷을 가져와 미래를 미리 보지 않고 시간순으로 재생하고, 중간가를 가정하지 말고 오더북 단계를 따라가며 체결을 시뮬레이션하세요. $1 계약에서 2-5센트에 달하는 Up/Down 스프레드는 보통 주장되는 우위보다 크므로, 중간가 기반 백테스트는 오해를 낳습니다.
- Polymarket 봇에서 Unauthorized/Invalid api key 오류가 나는 이유는 무엇인가요?
- 대부분 둘 중 하나입니다. 자격 증명 세트가 불완전하거나(L2 인증에는 API 키, 시크릿, 패스프레이즈가 함께 필요합니다), 시스템 시계가 어긋난 경우입니다. 요청 서명에는 타임스탬프가 포함되어 몇 초의 오차만으로도 무효가 됩니다. 드물게는 거래하는 지갑과 다른 지갑으로 자격 증명을 파생한 경우도 있습니다.
- Polymarket 봇에 WebSocket이 필요한가요, 아니면 폴링해도 되나요?
- WebSocket을 사용하세요. 타이머로 /book을 폴링하면 요청 한도를 소모하고, 지연 시간이 늘며, 확장성이 나쁩니다. 공개 오더북과 가격 업데이트는 CLOB market 채널을, 자신의 주문과 거래 업데이트는 user 채널을 구독하세요. SDK가 아닌 원시 WebSocket에 연결한다면 연결을 유지하기 위해 10초마다 PING을 보내세요.
Polymarket이 보관하지 않는 과거 데이터를 확보하세요
PolyTest는 Polymarket 암호화폐 Up/Down 마켓을 실시간으로 기록합니다. 8단계 오더북 깊이와 1초 미만 단위 타임스탬프를 갖춘 240M+개의 스냅샷을 제공하며, 결과가 확정된 마켓도 보존합니다. 3일 무료 체험으로 시작하세요.
더 읽어보기
Polymarket 현물 지연 우위는 진짜입니다. 깊이는 약 $100입니다.
Polymarket이 가격을 다시 매기기 전에, 현물이 이미 확인해 준 쪽을 삽니다. 사람들이 주장하는 대부분의 전략과 달리 이 전략은 데이터 앞에서도 살아남습니다. z-점수 15로요. 이것으로 모두가 부자가 되지 못한 이유는 오더북에 있습니다.
Polymarket WebSocket: 끊기지 않는 실시간 데이터
오더북을 폴링하는 것은 아무 이득 없이 요청 한도를 소모하는 가장 흔한 방법입니다. 네 가지 스트림, 아무도 문서화하지 않은 구독 옵션, 그리고 봇이 무응답을 받고도 평온하다고 여기는 실패 유형을 설명합니다.
2026년 Polymarket Python SDK (그리고 py-clob-client에서 옮겨 가기)
코드가 아직 py_clob_client에서 ClobClient를 import한다면, 대체된 패키지 위에 만들어진 것입니다. AI 어시스턴트가 작성해 주는 Polymarket 코드도 대부분 마찬가지입니다. 현재 SDK와 옮겨 가는 방법을 소개합니다.