Sports market making bot for Polymarket liquidity rewards
Project description
polymarket-sports-mm
Sports market making bot for Polymarket liquidity rewards. Optimized for the quadratic scoring function.
Polymarket pays $5M+/month to market makers who post resting limit orders on sports markets. This bot quotes both sides of the book — pre-game and live — to capture those rewards.
The math
Every minute, Polymarket samples your resting orders and scores them:
S(v, s) = ((v - s) / v)²
v = max qualifying spread (set per market, typically 3-5 cents)
s = your order's distance from midpoint
This is quadratic. Being 2x tighter = 4x the score:
Distance from mid Score
0 cents (at mid) 1.000
1 cent 0.444
2 cents 0.111
3 cents (at max) 0.000
Two-sided quoting (bid + ask) scores 3x vs single-sided. The bot always quotes both sides.
Install
git clone https://github.com/spfunctions/polymarket-sports-mm.git
cd polymarket-sports-mm
pip install -e .
Usage
Discover markets
No credentials needed. Scans Polymarket for active sports events:
$ sfmm discover
Sports Events (376 found)
┌────────────┬──────────────────────────────────┬─────┬──────────┐
│ Sport │ Event │ Mid │ Volume │
├────────────┼──────────────────────────────────┼─────┼──────────┤
│ soccer │ 2026 FIFA World Cup Winner │0.16 │ $467,000 │
│ basketball │ 2026 NBA Champion │0.36 │ $33,200 │
│ hockey │ 2026 NHL Stanley Cup Champion │0.12 │ $2,745 │
│ soccer │ UEFA Champions League Winner │0.10 │ $5,557 │
│ basketball │ NBA MVP │0.27 │ $4,700 │
│ ... │ │ │ │
└────────────┴──────────────────────────────────┴─────┴──────────┘
Dry run
No credentials needed. Fetches real orderbooks, computes real quotes, logs what it would place:
$ sfmm run --dry-run
DRY RUN — no orders will be placed
PRE 2026 FIFA World Cup Winner (58 markets)
PRE 2026 NBA Champion (20 markets)
[DRY] place BUY 100 @ 0.15 on Spain token
[DRY] place SELL 100 @ 0.17 on Spain token
Will Spain win the 2026 FIFA W: bid 0.15x100 ask 0.17x100 (mid=0.157)
[DRY] place BUY 100 @ 0.36 on OKC Thunder token
[DRY] place SELL 100 @ 0.39 on OKC Thunder token
Will the Oklahoma City Thunder: bid 0.36x100 ask 0.39x100 (mid=0.375)
Real quoting
Requires Polymarket CLOB credentials (register here):
export POLYMARKET_PRIVATE_KEY=0x...
export POLYMARKET_API_KEY=...
export POLYMARKET_API_SECRET=...
export POLYMARKET_API_PASSPHRASE=...
# Pre-game only
sfmm run --mode pre
# Pre-game + live (auto-transitions at kickoff)
sfmm run
# Specific market
sfmm run --market <condition_id>
How the bot quotes
The core insight: prices for score, sizes for view.
Bid price = midpoint - 1 tick (maximizes quadratic score)
Ask price = midpoint + 1 tick (symmetric = balanced Q_min)
Bid size = base × (1 + skew) (skew > 0 if bullish)
Ask size = base × (1 - skew) (skew blends view + inventory mean-reversion)
Moving prices away from midpoint to express a view kills your score (quadratic penalty). Instead, keep prices tight and adjust the size ratio.
Architecture
sfmm discover
│
│ Polymarket Gamma API
│ → find sports events, extract token IDs
▼
sfmm run
│
├─ Pre-game engine (poll every 5s)
│ │
│ ├─ Fetch orderbook (CLOB API)
│ ├─ Compute adjusted midpoint
│ ├─ Fair value = midpoint (Level 0) or external odds blend (Level 1)
│ ├─ Quote engine → optimal bid/ask price & size
│ └─ Cancel-and-replace via py-clob-client
│
├─ Live engine (poll every 1s)
│ │
│ ├─ Same as pre-game but faster
│ ├─ Circuit breaker: midpoint jump > threshold → pull all quotes
│ ├─ Cooldown (10s) → re-read mid → requote
│ └─ Symmetric only (no view skew during live — survival mode)
│
└─ Risk manager
├─ Position limit per market (default 1000 contracts)
├─ Total exposure cap ($5,000)
├─ Daily loss limit ($500) → halt
└─ Inventory mean-reversion via size skew
Reward pools (April 2026)
| League | Total/game | Pre | Live |
|---|---|---|---|
| Champions League QFs | $24,000 | $6,750 | $17,250 |
| English Premier League | $10,000 | $2,800 | $7,200 |
| NBA | $7,700 | $2,150 | $5,550 |
| CS2 A-tier | $5,500 | $1,550 | $3,950 |
| IPL Cricket | $4,500 | $1,250 | $3,250 |
| La Liga / Serie A | $3,300 | $900 | $2,400 |
| MLB | $1,650 | $465 | $1,185 |
| NHL | $1,500 | $400 | $1,100 |
Full list: Polymarket docs
Configuration
Copy config.example.toml:
[quoting]
spread_ticks = 1 # ticks from midpoint
min_size_multiplier = 2.0 # quote min_incentive_size * this
max_position_per_market = 1000
max_total_exposure_usd = 5000
[risk]
daily_loss_limit_usd = 500
circuit_cooldown_sec = 10
inventory_half_life = 500
[feeds]
poll_interval_pre = 5.0
poll_interval_live = 1.0
Environment variables (.env.example):
POLYMARKET_PRIVATE_KEY=0x...
POLYMARKET_API_KEY=
POLYMARKET_API_SECRET=
POLYMARKET_API_PASSPHRASE=
SF_API_KEY= # optional — SimpleFunctions intent reporting
ODDS_API_KEY= # optional — external odds for fair value
Tests
pip install -e ".[dev]"
pytest tests/ -v -p no:asyncio
47 tests covering scoring (verified against Polymarket docs worked example), quote engine, and risk logic.
Roadmap
- Core scoring (S(v,s), Q_min, adjusted midpoint)
- Quote engine (prices for score, sizes for view)
- Pre-game engine
- Live engine with circuit breaker
- Market discovery (376 events found)
- Dry-run mode
- External odds feed (Pinnacle/Betfair fair value anchor)
- Sport-specific probability models (Dixon-Coles for soccer)
- Rich terminal dashboard
- Backtest against historical orderbook data
- WebSocket feeds (replace polling)
License
MIT
Built with SimpleFunctions — prediction market intelligence for AI agents.
Project details
Release history Release notifications | RSS feed
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 sfmm-0.1.0.tar.gz.
File metadata
- Download URL: sfmm-0.1.0.tar.gz
- Upload date:
- Size: 21.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
268263eba4e92164b7e41c357b75816d75a6ff650be58aa811b36749d893fdd7
|
|
| MD5 |
b6c56edbd033316b14eab39cb5d5411e
|
|
| BLAKE2b-256 |
3797d38152b3d38e2cca3ed362b57fa32ba8f33d45b71e0a8a1fcf3feb2df7f5
|
File details
Details for the file sfmm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sfmm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a33115d142e6e671ed62b02b52f03f2fe838a9e545c43edcd3c950e85deba61
|
|
| MD5 |
d5181d92fceeb4525be275e232edb742
|
|
| BLAKE2b-256 |
ed22526b708573756e58a7a424534ef4dff0505dd37fa19c1d7d0e0779e42915
|