skills/alsk1992/cloddsbot/trading-polymarket

trading-polymarket

SKILL.md

Polymarket Trading - Complete API Reference

Full access to Polymarket's CLOB (Central Limit Order Book) via the official py_clob_client library.

60+ methods documented. This is the complete reference.

Required Environment Variables

PRIVATE_KEY=0x...           # Ethereum private key for signing
POLY_FUNDER_ADDRESS=0x...   # Your wallet address on Polygon
POLY_API_KEY=...            # From Polymarket API
POLY_API_SECRET=...         # Base64 encoded secret
POLY_API_PASSPHRASE=...     # API passphrase

Installation

pip install py-clob-client requests

Authentication Levels

Level Requirements Capabilities
L0 None Read-only: orderbooks, prices, markets
L1 Private key Create & sign orders (not post)
L2 Private key + API creds Full trading: post orders, cancel, query

Signature Types

Type Use Case
0 Standard EOA (MetaMask, hardware wallets)
1 Magic/email wallets (delegated signing)
2 Proxy wallets (Gnosis Safe, browser proxy)

ClobClient - Complete API (60+ Methods)

Initialization

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import (
    OrderArgs, MarketOrderArgs, ApiCreds, OrderType,
    BookParams, TradeParams, OpenOrderParams, BalanceAllowanceParams,
    AssetType, OrderScoringParams, OrdersScoringParams, DropNotificationParams
)
from py_clob_client.order_builder.constants import BUY, SELL
from py_clob_client.constants import POLYGON  # 137

# Level 2 Auth (full trading access)
client = ClobClient(
    host="https://clob.polymarket.com",
    key=os.getenv("PRIVATE_KEY"),           # Private key for signing
    chain_id=POLYGON,                        # 137 for mainnet, 80002 for Amoy testnet
    funder=os.getenv("POLY_FUNDER_ADDRESS"), # Wallet address (for proxy wallets)
    signature_type=2                         # 0=EOA, 1=MagicLink, 2=Proxy
)

# Set API credentials for authenticated endpoints
client.set_api_creds(ApiCreds(
    api_key=os.getenv("POLY_API_KEY"),
    api_secret=os.getenv("POLY_API_SECRET"),
    api_passphrase=os.getenv("POLY_API_PASSPHRASE")
))

Health & Configuration

client.get_ok()                    # Check if server is up
client.get_server_time()           # Get server timestamp
client.get_address()               # Your signer's public address
client.get_collateral_address()    # USDC contract address
client.get_conditional_address()   # CTF contract address
client.get_exchange_address()      # Exchange contract (neg_risk=False default)

Market Data - Single Token

# Get prices and spreads
client.get_midpoint(token_id)              # Mid market price
client.get_price(token_id, side="BUY")     # Best price for side
client.get_spread(token_id)                # Current spread
client.get_last_trade_price(token_id)      # Last executed trade price

# Get full orderbook
orderbook = client.get_order_book(token_id)
# Returns: OrderBookSummary with bids, asks, tick_size, neg_risk, timestamp, hash

Market Data - Batch (Multiple Tokens)

params = [
    BookParams(token_id="TOKEN1", side="BUY"),
    BookParams(token_id="TOKEN2", side="SELL")
]

client.get_midpoints(params)           # Multiple midpoints
client.get_prices(params)              # Multiple prices
client.get_spreads(params)             # Multiple spreads
client.get_order_books(params)         # Multiple orderbooks
client.get_last_trades_prices(params)  # Multiple last prices

Market Metadata

client.get_tick_size(token_id)      # Returns: "0.1", "0.01", "0.001", or "0.0001"
client.get_neg_risk(token_id)       # Returns: True/False (negative risk market)
client.get_fee_rate_bps(token_id)   # Returns: fee rate in basis points (0 or 1000)

Order Types

from py_clob_client.clob_types import OrderType

OrderType.GTC   # Good Till Cancelled - stays open until filled/cancelled
OrderType.FOK   # Fill Or Kill - fill entirely immediately or cancel
OrderType.GTD   # Good Till Date - expires at timestamp (min 60 seconds)
OrderType.FAK   # Fill And Kill - fill what's possible, cancel rest

When to Use Each Order Type

Type Use Case Example
GTC Entries - wait for fill Place buy at 0.45, wait for dip
FOK Exits - need immediate fill Market sell entire position NOW
GTD Time-limited orders Offer expires in 5 minutes
FAK Partial fills OK Get as much as possible now

OrderArgs - Limit Orders

OrderArgs(
    token_id: str,           # Token ID (outcome to trade)
    price: float,            # Price 0.01-0.99
    size: float,             # Number of shares
    side: str,               # "BUY" or "SELL" (or use BUY/SELL constants)
    fee_rate_bps: int = 0,   # Optional: fee rate in bps (0 or check market)
    nonce: int = 0,          # Optional: unique nonce for cancellation
    expiration: int = 0,     # Optional: expiry timestamp (0 = GTC, use timestamp for GTD)
    taker: str = ZERO_ADDRESS  # Optional: specific taker (ZERO_ADDRESS = anyone)
)

MarketOrderArgs - Market Orders

MarketOrderArgs(
    token_id: str,           # Token ID
    amount: float,           # Total USDC amount to spend (BUY) or shares (SELL)
    side: str,               # "BUY" or "SELL"
    price: float = 0,        # Optional: worst acceptable price (slippage protection)
    fee_rate_bps: int = 0,   # Optional: fee rate
    nonce: int = 0,          # Optional: nonce
    taker: str = ZERO_ADDRESS,  # Optional: taker address
    order_type: OrderType = FOK  # Optional: FOK (default) or FAK
)

Complete Order Examples

from py_clob_client.order_builder.constants import BUY, SELL

# 1. LIMIT BUY (GTC) - sits on book until filled
order = client.create_and_post_order(
    OrderArgs(token_id=TOKEN_ID, price=0.45, size=100.0, side=BUY)
)

# 2. LIMIT SELL (GTC)
order = client.create_and_post_order(
    OrderArgs(token_id=TOKEN_ID, price=0.55, size=50.0, side=SELL)
)

# 3. MARKET BUY - spend $100 USDC at current prices (FOK)
signed = client.create_market_order(
    MarketOrderArgs(token_id=TOKEN_ID, amount=100.0, side=BUY)
)
result = client.post_order(signed, orderType=OrderType.FOK)

# 4. MARKET SELL - sell all shares immediately (FOK)
signed = client.create_market_order(
    MarketOrderArgs(token_id=TOKEN_ID, amount=my_shares, side=SELL)
)
result = client.post_order(signed, orderType=OrderType.FOK)

# 5. POST-ONLY MAKER ORDER (avoid taker fees, earn rebates)
signed = client.create_order(
    OrderArgs(token_id=TOKEN_ID, price=0.44, size=100.0, side=BUY)
)
result = client.post_order(signed, orderType=OrderType.GTC, post_only=True)
# If order would cross spread, it gets REJECTED instead of taking

# 6. GOOD TIL DATE (GTD) - expires after timestamp
import time
expiry = int(time.time()) + 300  # 5 minutes from now
signed = client.create_order(
    OrderArgs(token_id=TOKEN_ID, price=0.50, size=100.0, side=BUY, expiration=expiry)
)
result = client.post_order(signed, orderType=OrderType.GTD)

# 7. FILL AND KILL (FAK) - fill what you can, cancel rest
signed = client.create_market_order(
    MarketOrderArgs(token_id=TOKEN_ID, amount=1000.0, side=BUY)
)
result = client.post_order(signed, orderType=OrderType.FAK)

Order Operations

Create and Post Orders

# SIMPLE: Create and post in one call (recommended)
result = client.create_and_post_order(
    OrderArgs(
        token_id="123456789012345678901234567890",
        price=0.45,
        size=10.0,
        side="BUY"
    )
)
# Returns: {"orderID": "...", "status": "...", ...}

# ADVANCED: Separate create and post
order = client.create_order(OrderArgs(...))  # Returns SignedOrder
result = client.post_order(order, orderType=OrderType.GTC, post_only=False)

# Market order (calculates price automatically)
result = client.create_market_order(
    MarketOrderArgs(
        token_id="...",
        amount=100.0,    # Spend $100 USDC
        side="BUY"
    )
)

# Calculate expected fill price before market order
price = client.calculate_market_price(
    token_id="...",
    side="BUY",
    amount=100.0,
    order_type=OrderType.FOK
)

Cancel Orders

client.cancel(order_id="ORDER_ID")           # Cancel specific order
client.cancel_orders(["ID1", "ID2", "ID3"])  # Cancel multiple
client.cancel_all()                          # Cancel ALL open orders
client.cancel_market_orders(                 # Cancel by market/asset
    market="CONDITION_ID",
    asset_id="TOKEN_ID"
)

Query Orders

# Get all open orders
orders = client.get_orders(
    params=OpenOrderParams(
        id="ORDER_ID",      # Optional: specific order
        market="COND_ID",   # Optional: filter by market
        asset_id="TOKEN"    # Optional: filter by token
    ),
    next_cursor="MA=="      # For pagination
)

# Get specific order
order = client.get_order(order_id="ORDER_ID")

Trade History

trades = client.get_trades(
    params=TradeParams(
        id="TRADE_ID",           # Optional: specific trade
        maker_address="0x...",   # Optional: filter by maker
        market="CONDITION_ID",   # Optional: filter by market
        asset_id="TOKEN_ID",     # Optional: filter by token
        before="2024-01-01",     # Optional: before date
        after="2023-01-01"       # Optional: after date
    ),
    next_cursor="MA=="
)

Balance & Allowance

# Check balance and allowances
balance = client.get_balance_allowance(
    params=BalanceAllowanceParams(
        asset_type=AssetType.COLLATERAL,  # USDC balance
        # or AssetType.CONDITIONAL         # Token balance
        token_id="TOKEN_ID"                # For conditional tokens
    )
)

# Update/refresh allowance cache
client.update_balance_allowance(params=...)

Market Discovery

Get Markets from CLOB

# All active markets
markets = client.get_markets(next_cursor="MA==")
simplified = client.get_simplified_markets()

# Specific market
market = client.get_market(condition_id="CONDITION_ID")

# Market trade events
events = client.get_market_trades_events(condition_id="CONDITION_ID")

# Sampling/featured markets
client.get_sampling_markets()
client.get_sampling_simplified_markets()

Get Markets from Gamma API (More Details)

import requests

def search_markets(query: str, limit: int = 10):
    """Search Polymarket markets by keyword"""
    url = "https://gamma-api.polymarket.com/markets"
    params = {
        "_q": query,
        "active": "true",
        "closed": "false",
        "_limit": limit
    }
    r = requests.get(url, params=params)
    return r.json()

# Get market details
markets = search_markets("bitcoin")
for m in markets:
    print(f"Question: {m['question']}")
    print(f"Condition ID: {m['condition_id']}")
    print(f"Volume: ${m.get('volume', 0):,.2f}")
    for token in m.get('tokens', []):
        print(f"  {token['outcome']}: {token['token_id']}")
        print(f"    Price: {float(token['price']):.2f}")

On-Chain Operations

Check Token Balance (Position Size)

import requests

CTF_CONTRACT = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"
RPC_URL = "https://polygon-rpc.com/"

def get_token_balance(wallet: str, token_id: str) -> float:
    """Get balance of a specific outcome token in shares"""
    token_int = int(token_id)
    # ERC-1155 balanceOf(address,uint256)
    data = f"0x00fdd58e000000000000000000000000{wallet[2:].lower()}{token_int:064x}"

    r = requests.post(RPC_URL, json={
        "jsonrpc": "2.0",
        "method": "eth_call",
        "params": [{"to": CTF_CONTRACT, "data": data}, "latest"],
        "id": 1
    })

    result = r.json().get("result", "0x0")
    balance = int(result, 16) / 1e6  # Convert from raw to shares
    return balance

# Usage
balance = get_token_balance(
    wallet="0x7c2211103e7Fbb257Ac6fa59f972cfd8bc9D4795",
    token_id="12345678901234567890"
)
print(f"Position: {balance} shares")

Check USDC Balance

USDC_CONTRACT = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"

def get_usdc_balance(wallet: str) -> float:
    """Get USDC balance on Polygon"""
    # ERC-20 balanceOf(address)
    data = f"0x70a08231000000000000000000000000{wallet[2:].lower()}"

    r = requests.post(RPC_URL, json={
        "jsonrpc": "2.0",
        "method": "eth_call",
        "params": [{"to": USDC_CONTRACT, "data": data}, "latest"],
        "id": 1
    })

    result = r.json().get("result", "0x0")
    return int(result, 16) / 1e6  # USDC has 6 decimals

API Key Management

# Create new API key
creds = client.create_api_key(nonce=0)

# Derive existing API key (if you lost creds but have private key)
creds = client.derive_api_key(nonce=0)

# Create or derive (tries both)
creds = client.create_or_derive_api_creds(nonce=0)

# Get all your API keys
keys = client.get_api_keys()

# Delete current API key
client.delete_api_key()

# Readonly API keys (for monitoring only)
readonly = client.create_readonly_api_key()
client.get_readonly_api_keys()
client.delete_readonly_api_key(key="...")
client.validate_readonly_api_key(address="0x...", key="...")

Advanced Features

Order Heartbeat (Keep Orders Alive)

# Start heartbeat - if not sent within 10s, all orders cancelled
heartbeat_id = client.post_heartbeat(heartbeat_id=None)

# Continue sending heartbeats
while trading:
    client.post_heartbeat(heartbeat_id=heartbeat_id)
    time.sleep(5)

Order Scoring

# Check if order is scoring (earning rewards)
is_scoring = client.is_order_scoring(
    params=OrderScoringParams(order_id="...")
)

# Check multiple orders
scores = client.are_orders_scoring(
    params=OrdersScoringParams(order_ids=["ID1", "ID2"])
)

Notifications

notifications = client.get_notifications()
client.drop_notifications(params=DropNotificationParams(...))

Fee Structure

IMPORTANT: Most Polymarket markets have ZERO fees (0% maker, 0% taker).

15-min Crypto Markets (Exception)

Only 15-minute BTC/ETH/SOL/XRP price prediction markets have fees:

fee = shares × 0.25 × (price × (1 - price))²
Entry Price Fee % (per side)
0.50 ~1.56%
0.60 or 0.40 ~1.44%
0.70 or 0.30 ~1.10%
0.80 or 0.20 ~0.64%
0.90 or 0.10 ~0.20%

TAKER = crosses spread = PAYS fee MAKER = adds liquidity = NO fee + earns rebates

To be a maker: Post orders that don't immediately fill (inside the spread).


Decimal Precision Rules

Order Side Price Decimals Size Decimals
BUY 2 4
SELL 2 2

Min order size: $1 per side


Complete Trading Example

#!/usr/bin/env python3
"""
Production-ready Polymarket trading script
"""

import os
import time
import requests
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, ApiCreds, OrderType

# Initialize
client = ClobClient(
    "https://clob.polymarket.com",
    key=os.getenv("PRIVATE_KEY"),
    chain_id=137,
    funder=os.getenv("POLY_FUNDER_ADDRESS"),
    signature_type=2
)
client.set_api_creds(ApiCreds(
    api_key=os.getenv("POLY_API_KEY"),
    api_secret=os.getenv("POLY_API_SECRET"),
    api_passphrase=os.getenv("POLY_API_PASSPHRASE")
))

TOKEN_ID = "YOUR_TOKEN_ID"
WALLET = os.getenv("POLY_FUNDER_ADDRESS")
CTF = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"

def get_balance(token_id):
    """Get position size"""
    token_int = int(token_id)
    data = f"0x00fdd58e000000000000000000000000{WALLET[2:].lower()}{token_int:064x}"
    r = requests.post("https://polygon-rpc.com/", json={
        "jsonrpc": "2.0", "method": "eth_call",
        "params": [{"to": CTF, "data": data}, "latest"], "id": 1
    })
    return int(r.json().get("result", "0x0"), 16) / 1e6

def get_orderbook(token_id):
    """Get current bid/ask"""
    book = client.get_order_book(token_id)
    return {
        "best_bid": float(book.bids[0].price) if book.bids else 0,
        "best_ask": float(book.asks[0].price) if book.asks else 1
    }

# Check position
position = get_balance(TOKEN_ID)
print(f"Current position: {position} shares")

# Get market
book = get_orderbook(TOKEN_ID)
print(f"Bid: {book['best_bid']:.2f}, Ask: {book['best_ask']:.2f}")

# Place a buy order (maker - inside spread)
buy_price = book['best_bid'] + 0.01  # 1 cent above bid
if buy_price < book['best_ask']:  # Ensure we're maker
    result = client.create_and_post_order(OrderArgs(
        token_id=TOKEN_ID,
        price=buy_price,
        size=10.0,
        side="BUY"
    ))
    print(f"Buy order placed: {result}")

# Place a sell order (market sell via FOK)
if position > 0:
    result = client.create_and_post_order(OrderArgs(
        token_id=TOKEN_ID,
        price=0.01,  # Lowest price = immediate fill
        size=position,
        side="SELL"
    ))
    print(f"Sold position: {result}")

Error Handling

from py_clob_client.exceptions import PolyApiException

try:
    result = client.create_and_post_order(OrderArgs(...))
except PolyApiException as e:
    print(f"API Error: {e}")
except Exception as e:
    print(f"Error: {e}")

Common errors:

  • "insufficient balance" - Not enough USDC/tokens
  • "invalid price" - Price outside 0.01-0.99 or wrong decimals
  • "order too small" - Below minimum order size
  • "market closed" - Market not accepting orders

CLI Commands (/poly)

Access Polymarket trading directly from Claude Code:

Market Data

/poly search <query>                     # Search markets
/poly market <condition-id>              # Market details
/poly book <token-id>                    # View orderbook

Trading

/poly buy <token-id> <size> <price>      # Buy shares (limit)
/poly sell <token-id> <size> <price>     # Sell shares (limit)
/poly orders                             # Open orders
/poly cancel <order-id>                  # Cancel order
/poly cancel all                         # Cancel all orders
/poly trades [limit]                     # Recent trade history
/poly balance                            # USDC + positions

Advanced Orders

/poly twap <buy|sell> <token> <total> <price> [slices] [interval-sec]
/poly bracket <token> <size> <tp> <sl>   # TP + SL bracket
/poly trigger buy <token> <size> <price> # Buy when price drops

Note: TWAP and bracket orders are persisted to the database and will automatically resume after restarts.

Auto-Redeem (Resolved Positions)

/poly redeem                           # One-time redeem all resolved positions
/poly redeem start                     # Start auto-polling (default: every 60s)
/poly redeem stop                      # Stop auto-polling
/poly redeem status                    # Check auto-redeemer status
/poly redeem pending                   # List positions pending redemption
/poly redeem <conditionId> <tokenId>   # Redeem specific position

Env vars:

  • POLY_REDEEM_INTERVAL_MS - Polling interval in ms (default: 60000 = 1 minute)

Real-Time Fills (WebSocket)

/poly fills                              # Connect fills WebSocket
/poly fills status                       # Show connection + recent fills
/poly fills stop                         # Disconnect fills WebSocket
/poly fills clear                        # Clear tracked fills

Order Heartbeat

/poly heartbeat                          # Start heartbeat (keeps orders alive)
/poly heartbeat status                   # Check heartbeat status
/poly heartbeat stop                     # Stop heartbeat (orders cancelled in 10s)

Account & Settlements

/poly settlements                        # View pending settlements from resolved markets
/poly allowance                          # Check USDC approval status
/poly orderbooks <token1> [token2] ...   # Batch fetch orderbooks

Complete ClobClient Method Reference

Health & Config (L0 - No Auth)

Method Parameters Returns Description
get_ok() - dict Health check
get_server_time() - dict Server timestamp
get_address() - str Your signer address
get_collateral_address() - str USDC contract
get_conditional_address() - str CTF contract
get_exchange_address(neg_risk) bool str Exchange contract

Market Data (L0 - No Auth)

Method Parameters Returns Description
get_midpoint(token_id) str dict Mid price
get_midpoints(params) list[BookParams] dict Multiple mid prices
get_price(token_id, side) str, str dict Best price for side
get_prices(params) list[BookParams] dict Multiple prices
get_spread(token_id) str dict Bid-ask spread
get_spreads(params) list[BookParams] dict Multiple spreads
get_order_book(token_id) str OrderBookSummary Full orderbook
get_order_books(params) list[BookParams] list Multiple orderbooks
get_last_trade_price(token_id) str dict Last trade
get_last_trades_prices(params) list[BookParams] dict Multiple last trades
get_tick_size(token_id) str TickSize "0.1"/"0.01"/"0.001"/"0.0001"
get_neg_risk(token_id) str bool Is neg_risk market
get_fee_rate_bps(token_id) str int Fee in basis points

Market Discovery (L0 - No Auth)

Method Parameters Returns Description
get_markets(next_cursor) str dict Paginated markets
get_simplified_markets(next_cursor) str dict Simplified markets
get_sampling_markets(next_cursor) str dict Featured markets
get_market(condition_id) str dict Single market
get_market_trades_events(condition_id) str dict Trade events

Order Creation (L1 - Needs Private Key)

Method Parameters Returns Description
create_order(order_args, options) OrderArgs, CreateOrderOptions dict Sign limit order
create_market_order(order_args, options) MarketOrderArgs, CreateOrderOptions dict Sign market order
calculate_market_price(token_id, side, amount, order_type) str, str, float, OrderType float Expected fill price

Order Posting (L2 - Needs API Creds)

Method Parameters Returns Description
post_order(order, orderType, post_only) SignedOrder, OrderType, bool dict Post single order
post_orders(args) list[PostOrdersArgs] dict Post batch orders
create_and_post_order(order_args, options) OrderArgs, PartialCreateOrderOptions dict Create + post (recommended)

Order Cancellation (L2 - Needs API Creds)

Method Parameters Returns Description
cancel(order_id) str dict Cancel one order
cancel_orders(order_ids) list[str] dict Cancel multiple
cancel_all() - dict Cancel ALL orders
cancel_market_orders(market, asset_id) str, str dict Cancel by market

Order Queries (L2 - Needs API Creds)

Method Parameters Returns Description
get_orders(params, next_cursor) OpenOrderParams, str list Get open orders
get_order(order_id) str dict Get specific order
get_trades(params, next_cursor) TradeParams, str list Trade history

API Key Management (L2 - Needs API Creds)

Method Parameters Returns Description
create_api_key(nonce) int ApiCreds Create new key
derive_api_key(nonce) int ApiCreds Derive existing key
create_or_derive_api_creds(nonce) int ApiCreds Create or derive
set_api_creds(creds) ApiCreds - Set credentials
get_api_keys() - dict List your keys
delete_api_key() - dict Delete current key
create_readonly_api_key() - ReadonlyApiKeyResponse Readonly key
get_readonly_api_keys() - list[str] List readonly keys
delete_readonly_api_key(key) str bool Delete readonly key

Balance & Allowance (L2 - Needs API Creds)

Method Parameters Returns Description
get_balance_allowance(params) BalanceAllowanceParams dict Check balance
update_balance_allowance(params) BalanceAllowanceParams dict Refresh allowance

Advanced Features (L2 - Needs API Creds)

Method Parameters Returns Description
post_heartbeat(heartbeat_id) str dict Keep orders alive (10s timeout)
is_order_scoring(params) OrderScoringParams dict Check if earning rewards
are_orders_scoring(params) OrdersScoringParams dict Check multiple orders
get_notifications() - dict Get notifications
drop_notifications(params) DropNotificationParams dict Delete notifications
get_closed_only_mode() - dict Check closed-only status

Contract Addresses (Polygon Mainnet)

USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"           # Collateral token
CTF  = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"           # Conditional tokens (ERC-1155)
EXCHANGE = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"       # Regular exchange
NEG_RISK = "0xC5d563A36AE78145C45a50134d48A1215220f80a"       # Neg risk exchange (crypto)

Quick Reference: Common Patterns

Check Position → Sell All

balance = get_token_balance(wallet, token_id)
if balance > 0:
    client.create_and_post_order(OrderArgs(
        token_id=token_id, price=0.01, size=balance, side="SELL"
    ))

Maker Entry (No Fees)

book = client.get_order_book(token_id)
best_bid = float(book.bids[0].price) if book.bids else 0
maker_price = best_bid + 0.01  # 1 cent above bid
signed = client.create_order(OrderArgs(token_id=token_id, price=maker_price, size=100, side="BUY"))
client.post_order(signed, orderType=OrderType.GTC, post_only=True)

Market Buy $50 Worth

signed = client.create_market_order(MarketOrderArgs(token_id=token_id, amount=50.0, side="BUY"))
client.post_order(signed, orderType=OrderType.FOK)

Cancel Everything

client.cancel_all()

WebSocket Channels (Real-Time Updates)

Polymarket provides WebSocket channels for real-time updates. No RPC needed - everything goes through CLOB.

WebSocket URLs

MARKET_WS = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
USER_WS = "wss://ws-subscriptions-clob.polymarket.com/ws/user"

Market Channel (Public - No Auth)

Subscribe to orderbook updates, price changes, trades for any market.

import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    event_type = data.get("event_type")

    if event_type == "book":
        # Full orderbook snapshot (on subscribe + after trades)
        print(f"Book update: {data['bids']}, {data['asks']}")

    elif event_type == "price_change":
        # Order placed/cancelled affecting price level
        print(f"Price change: {data}")

    elif event_type == "last_trade_price":
        # Trade executed
        print(f"Trade: {data['price']} x {data['size']}")

    elif event_type == "tick_size_change":
        # Tick size changed (price went extreme)
        print(f"Tick size: {data['old_tick_size']} -> {data['new_tick_size']}")

def on_open(ws):
    # Subscribe to specific token
    ws.send(json.dumps({
        "type": "subscribe",
        "channel": "market",
        "assets_ids": [TOKEN_ID]  # List of token IDs
    }))

ws = websocket.WebSocketApp(
    "wss://ws-subscriptions-clob.polymarket.com/ws/market",
    on_message=on_message,
    on_open=on_open
)
ws.run_forever()

User Channel (Authenticated - For Fills)

Subscribe to YOUR order updates, fills, trades. This is how you get fill notifications.

import websocket
import json
import hmac
import hashlib
import time
import base64

API_KEY = os.getenv("POLY_API_KEY")
API_SECRET = os.getenv("POLY_API_SECRET")
API_PASSPHRASE = os.getenv("POLY_API_PASSPHRASE")

def get_auth_headers():
    """Generate HMAC auth for WebSocket"""
    timestamp = str(int(time.time()))
    message = f"GET\n{timestamp}\n/ws/user"
    signature = hmac.new(
        base64.b64decode(API_SECRET),
        message.encode(),
        hashlib.sha256
    ).digest()
    return {
        "POLY-ADDRESS": WALLET_ADDRESS,
        "POLY-SIGNATURE": base64.b64encode(signature).decode(),
        "POLY-TIMESTAMP": timestamp,
        "POLY-API-KEY": API_KEY,
        "POLY-PASSPHRASE": API_PASSPHRASE
    }

def on_message(ws, message):
    data = json.loads(message)
    event_type = data.get("event_type")

    if event_type == "trade":
        # YOUR FILL - order matched!
        status = data.get("status")  # MATCHED, MINED, CONFIRMED, FAILED
        print(f"FILL: {data['side']} {data['size']} @ {data['price']}")
        print(f"  Status: {status}")
        print(f"  Trade ID: {data['id']}")
        print(f"  Market: {data['market']}")

        if status == "CONFIRMED":
            print("  ✓ Trade confirmed on-chain!")
        elif status == "FAILED":
            print("  ✗ Trade failed - check logs")

    elif event_type == "order":
        order_type = data.get("type")  # PLACEMENT, UPDATE, CANCELLATION
        print(f"ORDER {order_type}: {data['side']} {data['original_size']} @ {data['price']}")
        print(f"  Filled: {data.get('size_matched', 0)}")

        if order_type == "CANCELLATION":
            print("  Order cancelled")

def on_open(ws):
    # Subscribe with auth
    ws.send(json.dumps({
        "type": "subscribe",
        "channel": "user",
        "auth": get_auth_headers(),
        # Optional: filter to specific market
        # "markets": [CONDITION_ID]
    }))

ws = websocket.WebSocketApp(
    "wss://ws-subscriptions-clob.polymarket.com/ws/user",
    on_message=on_message,
    on_open=on_open
)
ws.run_forever()

Message Types Summary

Market Channel:

Event Trigger Key Fields
book Subscribe, trades affect book bids, asks, timestamp
price_change Order placed/cancelled price, size, side
last_trade_price Trade executed price, size, side
tick_size_change Price extreme (>0.96 or <0.04) old_tick_size, new_tick_size

User Channel:

Event Trigger Key Fields
trade Your order filled status, side, size, price, market
order Order placed/updated/cancelled type (PLACEMENT/UPDATE/CANCELLATION), size_matched

Trade Status Flow

MATCHED → MINED → CONFIRMED (success)
                → RETRYING → CONFIRMED/FAILED

Keepalive

Send PING every 10 seconds to keep connection alive:

import threading

def send_ping():
    while True:
        ws.send(json.dumps({"type": "ping"}))
        time.sleep(10)

threading.Thread(target=send_ping, daemon=True).start()

Real-Time Data Client (Alternative)

Polymarket also provides @polymarket/real-time-data-client for TypeScript:

import { RealTimeDataClient } from "@polymarket/real-time-data-client";

const client = new RealTimeDataClient({
    onMessage: (msg) => console.log(msg),
    onConnect: (c) => {
        // Subscribe to user fills with auth
        c.subscribe({
            subscriptions: [{
                topic: "clob_user",
                type: "*",
                clob_auth: {
                    key: API_KEY,
                    secret: API_SECRET,
                    passphrase: API_PASSPHRASE
                }
            }]
        });
    }
});

client.connect();

No RPC Needed - CLOB Handles Everything

Operation Method
Get positions GET /data/positions?user={address} via Gamma API
Get balance client.get_balance_allowance()
Place orders client.create_and_post_order()
Get fills User WebSocket channel
Get prices client.get_order_book() or Market WebSocket
Cancel orders client.cancel()

The only time you might use RPC is to check on-chain token balances independently, but even that's available via Gamma API positions endpoint.

Weekly Installs
13
GitHub Stars
62
First Seen
Feb 20, 2026
Installed on
opencode13
gemini-cli13
github-copilot13
codex13
amp13
kimi-cli13