Polymarket adapter
CLOB prediction markets on Polygon. EIP-712 signed orders with proxy-wallet support.
horizon.venues.polymarket.Polymarket wraps the official py_clob_client. Matching runs off-chain; settlement is on-chain ERC-1155 conditional tokens on Polygon.
Tier 1 to 3 code is unchanged. Only construct Polymarket(...) when you have an account funded with USDC on Polygon.
What you get
- EIP-712 signing. The SDK signs each order with an EOA private key. First-run API credentials derive from the signer via
create_or_derive_api_creds(). - Three signature types.
0raw EOA (most programmatic users),1email / magic-link proxy,2browser-wallet proxy. For proxy setups, setfunder=<proxy_address>; for EOA, funder defaults to the signer’s address. - Token-id market model. Each binary market has a YES
token_idand a NOtoken_id. Horizon uses the token id directly asmarket_id. To buy NO, target the NO token id. - Limit-only CLOB. No market orders native; Polymarket’s
create_market_orderis an aggressive IoC limit wrapped by the SDK. Horizon routesorder_type="market"through that path. - Status map.
LIVE-> Accepted,MATCHED-> Filled,CANCELLED-> Canceled,EXPIRED-> Expired.PartiallyFilledderived fromsize_matchedvs.original_size. - USDC 6-decimal. Balance normalizes to dollars.
Quickstart
from horizon.venues.polymarket import Polymarket
pm = Polymarket(
private_key="0x...",
funder="0xabc...", # EOA proxy or signer address
signature_type=0, # 0 EOA, 1 email-magic, 2 browser
)
pm.connect() # probes via get_orders()
print(pm.balance()) # USDC
Credentials resolve via Secrets: polymarket.private_key, polymarket.funder, and optionally polymarket.api_key, polymarket.api_secret, polymarket.api_passphrase (when you already derived CLOB creds and want to reuse them).
Market ids
market_id is always the token_id (hex string). Each question has:
condition_id: identifies the binary market.token_id (YES): buy to go long YES shares.token_id (NO): buy to go long NO shares.
Use list_markets() / market_info(condition_id) to map between them.
Order types
| Type | OrderAction.order_type | Notes |
|---|---|---|
| Limit | "limit" | Requires price in 0.01..0.99. |
| Market | "market" | Routed through create_market_order. Aggressive limit under the hood. |
TIF options accepted by the SDK: GTC, GTD, FOK, FAK. post_only and reduce_only pass through where the SDK supports them.
Amend
Polymarket does not expose in-place amend. Polymarket.amend(...) raises NotImplementedError. Cancel and resubmit.
Positions
Holdings are ERC-1155 balances on Polygon. The adapter does not currently query Polygon RPC for balances; positions() returns []. Use fill history + external Polygon indexing for an accurate snapshot, or extend the class with a web3 balance reader.
WebSocket feed
from horizon.data.live import PolymarketLiveFeed, SubscriptionKind
feed = PolymarketLiveFeed(fill_hook=pm.push_fill)
feed.subscribe(["<token_id>"], SubscriptionKind.OrderBook)
Channels: book, price_change, trade, order. The user channel authenticates with the same API credentials as REST; pass them via Secrets or explicit constructor args.
Fees
Polymarket fees are expressed as fee_rate_bps on fills. The adapter computes the actual fee cost as quantity * price * bps / 10000 in USDC for every fill.
Status by phase
| Capability | Status |
|---|---|
| submit, cancel, cancel_all (per-market + global) | Shipped |
| open_orders with PartiallyFilled derivation | Shipped |
| balance (USDC allowance check) | Shipped |
| drain_fills via get_trades + push_fill WS injection | Shipped |
| market_info, list_markets, orderbook | Shipped |
| positions (Polygon RPC read) | Out of scope; add via web3.py subclass if needed. |
| amend | Not supported; cancel + resubmit. |
Regulatory notes
Polymarket is offshore from a US perspective. Operators outside the allowed jurisdictions should not use the production adapter. The adapter itself carries no jurisdiction logic; that lives in the firm’s compliance program.