A Python toolkit for automated market-making on Polymarket, a decentralised binary-outcome prediction market built on Polygon.
| Module | Description |
|---|---|
order_book |
Local CLOB mirror with bid/ask depth, spread, and own-order tracking |
pricing |
Avellaneda-Stoikov inventory-adjusted quotes + simple fixed-spread fallback |
inventory |
Position accounting, average entry price, realised/unrealised P&L |
risk |
Pre-trade and post-trade risk checks (position limits, daily loss cap) |
client |
Async facade over the Polymarket CLOB REST API |
market_maker |
Main event loop that ties all modules together |
┌──────────────────────────────────────────────────────────────────┐
│ MarketMaker │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────────┐ │
│ │ OrderBook │ │ Pricing │ │ InventoryTracker │ │
│ │ (per token) │ │ (A-S model) │ │ (all positions) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ └──────────────────▼───────────────────────▼─────────┐ │
│ RiskManager │ │
│ (pre/post-trade checks) │ │
│ │ │ │
│ ▼ │ │
│ PolymarketClient │ │
│ (async CLOB API wrapper) │ │
└──────────────────────────────────────────────────────────────────┘
The bot uses the Avellaneda-Stoikov (2008) stochastic-control model:
reservation_price = mid − q · γ · σ² · T
optimal_spread = γ · σ² · T + (2/γ) · ln(1 + γ/κ)
where:
q— signed inventory (shares long)γ— risk-aversion coefficient (controls how aggressively inventory is unwound)σ— price volatilityT— time horizonκ— order-arrival intensity
As inventory grows long the reservation price falls, widening the ask and tightening the bid to attract sellers. The reverse holds for a short inventory.
Requirements: Python 3.10+
git clone <this-repo>
cd Prediction_Market_MarketMaker_Tools
pip install -e ".[dev]"cp .env.example .env
# fill in POLYMARKET_API_KEY, POLYMARKET_API_SECRET,
# POLYMARKET_API_PASSPHRASE, and POLYMARKET_PRIVATE_KEYObtain API credentials from Polymarket's API portal and generate a CLOB API key using your EVM wallet's private key.
# Dry run (no real orders placed)
python examples/basic_mm.py \
--token-id 0xYOUR_TOKEN_ID \
--condition-id 0xYOUR_CONDITION_ID \
--order-size 10 \
--interval 15 \
--dry-run
# Live trading
python examples/basic_mm.py \
--token-id 0xYOUR_TOKEN_ID \
--condition-id 0xYOUR_CONDITION_ID \
--order-size 10 \
--interval 15 \
--gamma 0.1 \
--sigma 0.05 \
--max-position 200 \
--max-daily-loss 100import asyncio
from pm_mm import MarketMaker
from pm_mm.client import PolymarketClient, PolymarketCredentials
from pm_mm.market_maker import MarketConfig, MarketMakerConfig
from pm_mm.risk import RiskLimits
config = MarketMakerConfig(
markets=[
MarketConfig(
token_id="0x...",
condition_id="0x...",
order_size=10.0,
quote_interval_secs=15.0,
)
],
risk_limits=RiskLimits(
max_position_shares=500,
max_position_usd=500,
max_daily_loss_usd=200,
),
pricing_gamma=0.1,
pricing_sigma=0.05,
dry_run=False,
)
async def main():
creds = PolymarketCredentials.from_env()
async with PolymarketClient(creds) as client:
mm = MarketMaker(config, client)
await mm.run()
asyncio.run(main())from pm_mm.order_book import OrderBook, Side
from pm_mm.pricing import AvellanedaStoikov, FixedSpread
from pm_mm.inventory import InventoryTracker, Fill
from pm_mm.risk import RiskManager, RiskLimits
# Order book
book = OrderBook(token_id="0x...")
book.update_snapshot(bids=[(0.50, 100)], asks=[(0.52, 100)])
print(book.mid_price) # 0.51
print(book.spread_pct) # ~0.039
# Pricing
model = AvellanedaStoikov(gamma=0.1, sigma=0.05)
quote = model.quote(mid=0.51, inventory=50)
print(quote.bid, quote.ask)
# Fixed-spread fallback
fs = FixedSpread(half_spread=0.02)
quote = fs.quote(mid=0.51)
# Inventory tracking
tracker = InventoryTracker()
tracker.record_fill(Fill("order-1", "0x...", "BUY", price=0.50, shares=100))
pos = tracker.position("0x...")
print(pos.unrealised_pnl(mark_price=0.60)) # 10.0
# Risk management
rm = RiskManager(limits=RiskLimits(max_daily_loss_usd=50), inventory=tracker)
rm.check_daily_loss() # raises RiskLimitBreached if over limitAll parameters can be set via .env or passed programmatically.
| Variable | Default | Description |
|---|---|---|
POLYMARKET_API_KEY |
— | CLOB API key |
POLYMARKET_API_SECRET |
— | CLOB API secret |
POLYMARKET_API_PASSPHRASE |
— | CLOB API passphrase |
POLYMARKET_PRIVATE_KEY |
— | EVM wallet private key (no 0x prefix) |
CLOB_HOST |
https://clob.polymarket.com |
CLOB endpoint |
MAX_POSITION_SHARES |
1000 |
Max shares per side per market |
MAX_POSITION_USD |
500 |
Max USD notional per market |
MAX_DAILY_LOSS_USD |
200 |
Daily loss hard stop |
MIN_SPREAD_PCT |
0.02 |
Minimum bid-ask spread (2%) |
RISK_AVERSION |
0.1 |
A-S gamma parameter |
VOLATILITY |
0.05 |
A-S sigma parameter |
ORDER_SIZE_SHARES |
10 |
Default order size |
The RiskManager enforces the following checks before and after each quote cycle:
- Position size — won't place an order that would push shares beyond
max_position_shares - Notional exposure — won't exceed
max_position_usdper market - Daily loss cap — halts the bot and cancels all orders if
max_daily_loss_usdis breached - Spread floor — won't quote spreads narrower than
min_spread_pct - Order size cap — single-order size is capped at
max_order_size_shares
On a hard halt, the bot issues cancel_all_orders() before exiting.
Warning: This is experimental software. Use
--dry-runto test your configuration before committing real capital. Prediction markets carry unique risks including resolution uncertainty and illiquidity.
# Run tests
pytest
# Lint
ruff check src tests
# Type check
mypy srcPrediction_Market_MarketMaker_Tools/
├── src/pm_mm/
│ ├── __init__.py
│ ├── order_book.py # local CLOB mirror
│ ├── pricing.py # Avellaneda-Stoikov + fixed-spread models
│ ├── inventory.py # position & P&L tracking
│ ├── risk.py # risk checks and limits
│ ├── client.py # async Polymarket CLOB client
│ └── market_maker.py # main loop
├── tests/
│ ├── test_order_book.py
│ ├── test_pricing.py
│ ├── test_inventory.py
│ └── test_risk.py
├── examples/
│ └── basic_mm.py
├── .env.example
├── pyproject.toml
└── README.md
- Avellaneda, M. & Stoikov, S. (2008). High-frequency trading in a limit order book. Quantitative Finance, 8(3), 217–224.
- Polymarket CLOB API docs
- py-clob-client