Python SDK for Polymarket — market discovery, real-time price streaming, and paper trading
Project description
polyalpha
Python SDK for Polymarket — market discovery, real-time price streaming, and paper trading.
pip install polyalpha
Quick start
import polyalpha
client = polyalpha.Client()
# Fetch the active BTC 5-minute market
market = client.markets.latest("BTC", "5m")
market.show()
Market discovery
client = polyalpha.Client()
# Latest market for any asset / timeframe
market = client.markets.latest("BTC", "5m")
market = client.markets.latest("ETH", "15m")
market = client.markets.latest("SOL", "1h")
# Direct slug lookup
market = client.markets.get("btc-updown-5m-1751234700")
# Keyword search
markets = client.markets.search("ETH 15m") # → list[Market]
# All active markets at a timeframe
markets = client.markets.available("5m") # → list[Market]
Supported assets: BTC, ETH, SOL, XRP, DOGE
Supported timeframes: 5m, 15m, 1h, 4h, 24h
Market object
market.id # Gamma condition / event ID
market.slug # "btc-updown-5m-1751234700"
market.question # "Will BTC be higher in 5 minutes?"
market.end_time # ISO-8601 window close time
market.volume # float (USDC)
market.liquidity # float (USDC)
market.up_price # float — current UP mid-price
market.down_price # float — current DOWN mid-price
market.up_token # CLOB token ID for the UP leg
market.down_token # CLOB token ID for the DOWN leg
market.url # https://polymarket.com/event/…
market.active # bool
market.closed # bool
market.show() # print formatted summary
market.dump() # → dict (raw excluded)
market.json() # → JSON string
Price streaming
stream = client.stream(market)
@stream.on("price")
def on_price(up: float, down: float):
print(f"UP={up:.4f} DOWN={down:.4f}")
@stream.on("book")
def on_book(data: dict):
print(data["bids"][0], data["asks"][0])
@stream.on("trade")
def on_trade(data: dict):
print(data["price"], data["size"])
@stream.on("close")
def on_close():
print("Market resolved")
@stream.on("error")
def on_error(exc: Exception):
print(f"Error: {exc}")
@stream.on("connect")
def on_connect():
print("Connected")
stream.start() # blocking
stream.start(background=True) # daemon thread
stream.stop() # clean shutdown
The stream auto-reconnects on drops using exponential back-off.
A text PING keepalive is sent every 10 seconds to prevent silent server-side disconnects.
Latest prices are always available without a handler:
print(stream.up, stream.down)
Paper trading
client = polyalpha.Client(balance=500.0)
# Market fill — executes immediately at the current price
order = client.paper.buy(market, side="UP", amount=10.0)
# Limit order — queued until the streamed price crosses the threshold
order = client.paper.limit(market, side="UP", price=0.92, amount=25.0)
# Cancel a pending limit and refund the reserved balance
client.paper.cancel(order.id)
# Inspect
client.paper.open() # → list[PaperOrder] (pending limits)
client.paper.orders() # → list[PaperOrder] (all orders)
client.paper.positions() # → list[PaperPosition] (live)
client.paper.all_positions() # → list[PaperPosition] (all, incl. resolved)
client.paper.balance # float
# Wire a stream for auto-fill and live P&L updates
stream = client.stream(market)
client.paper.attach_stream(stream, market)
stream.start(background=True)
# Resolve after market settles
client.paper.resolve(market, outcome="UP")
# Print P&L table
client.paper.summary()
PaperOrder
order.id # UUID string
order.side # "UP" | "DOWN"
order.price # fill price (or limit threshold if still open)
order.amount # USDC spent
order.shares # shares received after 2% taker fee
order.fee # USDC fee paid
order.status # "open" | "filled" | "cancelled"
order.is_limit # bool
order.filled_at # datetime (UTC) or None
order.dump() # → dict
PaperPosition
pos.side # "UP" | "DOWN"
pos.shares # float
pos.avg_price # volume-weighted average entry price
pos.current_price # live price (updated from stream)
pos.cost_basis # shares × avg_price
pos.current_value # shares × current_price (or 0/shares if resolved)
pos.pnl # current_value − cost_basis
pos.pnl_pct # pnl / cost_basis × 100
pos.resolved # bool
pos.outcome # "WON" | "LOST" | None
pos.dump() # → dict
Auto-Redeem
Automatically redeem resolved positions based on configurable triggers (time intervals, market count, or value thresholds).
import polyalpha
from polyalpha import AutoRedeemConfig
client = polyalpha.Client(balance=1000.0)
# Simple daily auto-redeem
config = AutoRedeemConfig(
time_interval="1d", # Redeem daily
min_value_usd=100.0, # Only when value >= $100
)
client.paper.set_auto_redeem_config(config)
client.paper.auto_redeem.start_scheduler()
Configuration Options
AutoRedeemConfig(
# Trigger modes
trigger_on_time=True, # Enable time-based triggers
trigger_on_count=True, # Enable count-based triggers
trigger_on_value=False, # Enable value-based triggers
# Time-based
time_interval="1d", # "1h", "6h", "1d", "1w"
redeem_at_time=None, # Specific time "14:00" UTC
# Count-based
min_markets=10, # Redeem after N markets
max_markets=100, # Force redeem at N (safety)
# Value-based
min_value_usd=100.0, # Redeem when value >= $100
max_value_usd=10000.0, # Force redeem at $10k (safety)
# Safety
require_confirmation=False, # Confirm before redeeming
dry_run=False, # Simulate without executing
only_winning=False, # Only redeem winning positions
min_age_hours=1, # Wait N hours after resolution
)
Manual Redemption
# Check for redeemable positions
positions = client.paper.auto_redeem.check_positions()
print(f"Found {len(positions)} positions to redeem")
# Manually redeem
result = client.paper.auto_redeem.redeem(positions)
print(f"Redeemed {result.redeemed_count} positions")
# View history
history = client.paper.auto_redeem.get_redeem_history()
Configuration
client = polyalpha.Client(
balance = 100.0, # paper USDC balance
timeout = 10, # HTTP timeout (seconds)
retries = 3, # retries on 5xx errors
log_level = "WARNING", # "DEBUG" | "INFO" | "WARNING" | "ERROR"
rate_limit = None, # max API requests per second (default: unlimited)
)
Rate limiting: Optional token-bucket rate limiter to prevent API abuse. Set to an integer (e.g., 10 for 10 requests/second) or None for unlimited.
Error handling
from polyalpha import (
MarketNotFound, # no active market for that slug / asset+timeframe
MarketClosed, # market exists but window has closed
StreamDisconnected, # WS dropped and retry budget exhausted
InsufficientBalance, # paper balance too low
OrderNotFound, # cancel/lookup of unknown order ID
)
try:
market = client.markets.latest("BTC", "5m")
except polyalpha.MarketNotFound as exc:
print(f"Not found: {exc}")
Examples
python examples/market.py --asset BTC --timeframe 5m
python examples/market.py --rate-limit 10
python examples/stream.py --asset ETH --timeframe 15m --log DEBUG
python examples/paper.py --side UP --amount 25 --limit 0.92
python examples/auto_redeem.py
Project layout
src/polyalpha/
├── __init__.py Public API surface
├── client.py Client — main entry point
├── markets.py MarketClient — Gamma API + slug resolution
├── stream.py Stream — WebSocket price feed
├── core/
│ ├── __init__.py
│ ├── constants.py Endpoints, timeframes, assets, tuning knobs
│ ├── errors.py Typed exceptions
│ └── market.py Market dataclass
└── trading/
├── __init__.py
└── paper.py PaperEngine, PaperOrder, PaperPosition
examples/
├── market.py
├── stream.py
└── paper.py
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file polyalpha-0.2.1.tar.gz.
File metadata
- Download URL: polyalpha-0.2.1.tar.gz
- Upload date:
- Size: 300.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5e4df1b7a78fe98ea451ce47034e940cfa5f63647d5a2560d1253c1fe89d874
|
|
| MD5 |
8b7f65feabb9501ab3cd626cc22a9a73
|
|
| BLAKE2b-256 |
e3dea0f0de5e02b33b4e74d653663e37f581a33d5f0da1b27eda0e610a6fd348
|
File details
Details for the file polyalpha-0.2.1-py3-none-any.whl.
File metadata
- Download URL: polyalpha-0.2.1-py3-none-any.whl
- Upload date:
- Size: 266.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0543d66a83a0ee521e57aa87cab90c29df56e38b33abe47e9d350bec2358b97c
|
|
| MD5 |
e586f1fc48692ac0fabcbe343194e247
|
|
| BLAKE2b-256 |
feeff6ec2e9299123aa91d93d9be0dc5f9f7029a5d58332015c14bb2646d60d3
|