scripts/polymarket.py
scripts/polymarket.pyBrowse 3 files
2,888 tokens
10,214 bytes
Token encoding: o200k_base
Snapshot 24fd22b
← Back to SKILL.md
1#!/usr/bin/env python32"""Polymarket CLI helper — query prediction market data.3 4Usage:5 python3 polymarket.py search "bitcoin"6 python3 polymarket.py trending [--limit 10]7 python3 polymarket.py market <slug>8 python3 polymarket.py event <slug>9 python3 polymarket.py price <token_id>10 python3 polymarket.py book <token_id>11 python3 polymarket.py history <condition_id> [--interval all] [--fidelity 50]12 python3 polymarket.py trades [--limit 10] [--market CONDITION_ID]13"""14 15import json16import sys17import urllib.request18import urllib.parse19import urllib.error20 21GAMMA = "https://gamma-api.polymarket.com"22CLOB = "https://clob.polymarket.com"23DATA = "https://data-api.polymarket.com"24 25 26def _get(url: str) -> dict | list:27 """GET request, return parsed JSON."""28 req = urllib.request.Request(url, headers={"User-Agent": "hermes-agent/1.0"})29 try:30 with urllib.request.urlopen(req, timeout=15) as resp:31 return json.loads(resp.read().decode())32 except urllib.error.HTTPError as e:33 print(f"HTTP {e.code}: {e.reason}", file=sys.stderr)34 sys.exit(1)35 except urllib.error.URLError as e:36 print(f"Connection error: {e.reason}", file=sys.stderr)37 sys.exit(1)38 39 40def _parse_json_field(val):41 """Parse double-encoded JSON fields (outcomePrices, outcomes, clobTokenIds)."""42 if isinstance(val, str):43 try:44 return json.loads(val)45 except (json.JSONDecodeError, TypeError):46 return val47 return val48 49 50def _fmt_pct(price_str: str) -> str:51 """Format price string as percentage."""52 try:53 return f"{float(price_str) * 100:.1f}%"54 except (ValueError, TypeError):55 return price_str56 57 58def _fmt_volume(vol) -> str:59 """Format volume as human-readable."""60 try:61 v = float(vol)62 if v >= 1_000_000:63 return f"${v / 1_000_000:.1f}M"64 if v >= 1_000:65 return f"${v / 1_000:.1f}K"66 return f"${v:.0f}"67 except (ValueError, TypeError):68 return str(vol)69 70 71def _print_market(m: dict, indent: str = ""):72 """Print a market summary."""73 question = m.get("question", "?")74 prices = _parse_json_field(m.get("outcomePrices", "[]"))75 outcomes = _parse_json_field(m.get("outcomes", "[]"))76 vol = _fmt_volume(m.get("volume", 0))77 closed = m.get("closed", False)78 status = " [CLOSED]" if closed else ""79 80 if isinstance(prices, list) and len(prices) >= 2:81 outcome_labels = outcomes if isinstance(outcomes, list) else ["Yes", "No"]82 price_str = " / ".join(83 f"{outcome_labels[i]}: {_fmt_pct(prices[i])}"84 for i in range(min(len(prices), len(outcome_labels)))85 )86 print(f"{indent}{question}{status}")87 print(f"{indent} {price_str} | Volume: {vol}")88 else:89 print(f"{indent}{question}{status} | Volume: {vol}")90 91 slug = m.get("slug", "")92 if slug:93 print(f"{indent} slug: {slug}")94 95 96def cmd_search(query: str):97 """Search for markets."""98 q = urllib.parse.quote(query)99 data = _get(f"{GAMMA}/public-search?q={q}")100 events = data.get("events", [])101 total = data.get("pagination", {}).get("totalResults", len(events))102 print(f"Found {total} results for \"{query}\":\n")103 for evt in events[:10]:104 print(f"=== {evt['title']} ===")105 print(f" Volume: {_fmt_volume(evt.get('volume', 0))} | slug: {evt.get('slug', '')}")106 markets = evt.get("markets", [])107 for m in markets[:5]:108 _print_market(m, indent=" ")109 if len(markets) > 5:110 print(f" ... and {len(markets) - 5} more markets")111 print()112 113 114def cmd_trending(limit: int = 10):115 """Show trending events by volume."""116 events = _get(f"{GAMMA}/events?limit={limit}&active=true&closed=false&order=volume&ascending=false")117 print(f"Top {len(events)} trending events:\n")118 for i, evt in enumerate(events, 1):119 print(f"{i}. {evt['title']}")120 print(f" Volume: {_fmt_volume(evt.get('volume', 0))} | Markets: {len(evt.get('markets', []))}")121 print(f" slug: {evt.get('slug', '')}")122 markets = evt.get("markets", [])123 for m in markets[:3]:124 _print_market(m, indent=" ")125 if len(markets) > 3:126 print(f" ... and {len(markets) - 3} more markets")127 print()128 129 130def cmd_market(slug: str):131 """Get market details by slug."""132 markets = _get(f"{GAMMA}/markets?slug={urllib.parse.quote(slug)}")133 if not markets:134 print(f"No market found with slug: {slug}")135 return136 m = markets[0]137 print(f"Market: {m.get('question', '?')}")138 print(f"Status: {'CLOSED' if m.get('closed') else 'ACTIVE'}")139 _print_market(m)140 print(f"\n conditionId: {m.get('conditionId', 'N/A')}")141 tokens = _parse_json_field(m.get("clobTokenIds", "[]"))142 if isinstance(tokens, list):143 outcomes = _parse_json_field(m.get("outcomes", "[]"))144 for i, t in enumerate(tokens):145 label = outcomes[i] if isinstance(outcomes, list) and i < len(outcomes) else f"Outcome {i}"146 print(f" token ({label}): {t}")147 desc = m.get("description", "")148 if desc:149 print(f"\n Description: {desc[:500]}")150 151 152def cmd_event(slug: str):153 """Get event details by slug."""154 events = _get(f"{GAMMA}/events?slug={urllib.parse.quote(slug)}")155 if not events:156 print(f"No event found with slug: {slug}")157 return158 evt = events[0]159 print(f"Event: {evt['title']}")160 print(f"Volume: {_fmt_volume(evt.get('volume', 0))}")161 print(f"Status: {'CLOSED' if evt.get('closed') else 'ACTIVE'}")162 print(f"Markets: {len(evt.get('markets', []))}\n")163 for m in evt.get("markets", []):164 _print_market(m, indent=" ")165 print()166 167 168def cmd_price(token_id: str):169 """Get current price for a token."""170 buy = _get(f"{CLOB}/price?token_id={token_id}&side=buy")171 mid = _get(f"{CLOB}/midpoint?token_id={token_id}")172 spread = _get(f"{CLOB}/spread?token_id={token_id}")173 print(f"Token: {token_id[:30]}...")174 print(f" Buy price: {_fmt_pct(buy.get('price', '?'))}")175 print(f" Midpoint: {_fmt_pct(mid.get('mid', '?'))}")176 print(f" Spread: {spread.get('spread', '?')}")177 178 179def cmd_book(token_id: str):180 """Get orderbook for a token."""181 book = _get(f"{CLOB}/book?token_id={token_id}")182 bids = book.get("bids", [])183 asks = book.get("asks", [])184 last = book.get("last_trade_price", "?")185 print(f"Orderbook for {token_id[:30]}...")186 print(f"Last trade: {_fmt_pct(last)} | Tick size: {book.get('tick_size', '?')}")187 print(f"\n Top bids ({len(bids)} total):")188 # Show bids sorted by price descending (best bids first)189 sorted_bids = sorted(bids, key=lambda x: float(x.get("price", 0)), reverse=True)190 for b in sorted_bids[:10]:191 print(f" {_fmt_pct(b['price']):>7} | Size: {float(b['size']):>10.2f}")192 print(f"\n Top asks ({len(asks)} total):")193 sorted_asks = sorted(asks, key=lambda x: float(x.get("price", 0)))194 for a in sorted_asks[:10]:195 print(f" {_fmt_pct(a['price']):>7} | Size: {float(a['size']):>10.2f}")196 197 198def cmd_history(condition_id: str, interval: str = "all", fidelity: int = 50):199 """Get price history for a market."""200 data = _get(f"{CLOB}/prices-history?market={condition_id}&interval={interval}&fidelity={fidelity}")201 history = data.get("history", [])202 if not history:203 print("No price history available for this market.")204 return205 print(f"Price history ({len(history)} points, interval={interval}):\n")206 from datetime import datetime, timezone207 for pt in history:208 ts = datetime.fromtimestamp(pt["t"], tz=timezone.utc).strftime("%Y-%m-%d %H:%M")209 price = _fmt_pct(pt["p"])210 bar = "█" * int(float(pt["p"]) * 40)211 print(f" {ts} {price:>7} {bar}")212 213 214def cmd_trades(limit: int = 10, market: str = None):215 """Get recent trades."""216 url = f"{DATA}/trades?limit={limit}"217 if market:218 url += f"&market={market}"219 trades = _get(url)220 if not isinstance(trades, list):221 print(f"Unexpected response: {trades}")222 return223 print(f"Recent trades ({len(trades)}):\n")224 for t in trades:225 side = t.get("side", "?")226 price = _fmt_pct(t.get("price", "?"))227 size = t.get("size", "?")228 outcome = t.get("outcome", "?")229 title = t.get("title", "?")[:50]230 ts = t.get("timestamp", "")231 print(f" {side:4} {price:>7} x{float(size):>8.2f} [{outcome}] {title}")232 233 234def main():235 args = sys.argv[1:]236 if not args or args[0] in {"-h", "--help", "help"}:237 print(__doc__)238 return239 240 cmd = args[0]241 242 if cmd == "search" and len(args) >= 2:243 cmd_search(" ".join(args[1:]))244 elif cmd == "trending":245 limit = 10246 if "--limit" in args:247 idx = args.index("--limit")248 limit = int(args[idx + 1]) if idx + 1 < len(args) else 10249 cmd_trending(limit)250 elif cmd == "market" and len(args) >= 2:251 cmd_market(args[1])252 elif cmd == "event" and len(args) >= 2:253 cmd_event(args[1])254 elif cmd == "price" and len(args) >= 2:255 cmd_price(args[1])256 elif cmd == "book" and len(args) >= 2:257 cmd_book(args[1])258 elif cmd == "history" and len(args) >= 2:259 interval = "all"260 fidelity = 50261 if "--interval" in args:262 idx = args.index("--interval")263 interval = args[idx + 1] if idx + 1 < len(args) else "all"264 if "--fidelity" in args:265 idx = args.index("--fidelity")266 fidelity = int(args[idx + 1]) if idx + 1 < len(args) else 50267 cmd_history(args[1], interval, fidelity)268 elif cmd == "trades":269 limit = 10270 market = None271 if "--limit" in args:272 idx = args.index("--limit")273 limit = int(args[idx + 1]) if idx + 1 < len(args) else 10274 if "--market" in args:275 idx = args.index("--market")276 market = args[idx + 1] if idx + 1 < len(args) else None277 cmd_trades(limit, market)278 else:279 print(f"Unknown command: {cmd}")280 print(__doc__)281 282 283if __name__ == "__main__":284 main()285