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.
Metadata
Release files for sfmm 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sfmm-0.1.0.tar.gz | 21.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sfmm-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.0 kB
Release files / sfmm-0.1.0.tar.gz
| Download URL | sfmm-0.1.0.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
268263eba4e92164b7e41c357b75816d75a6ff650be58aa811b36749d893fdd7
|
|
BLAKE2b-256 checksum How to use checksums |
3797d38152b3d38e2cca3ed362b57fa32ba8f33d45b71e0a8a1fcf3feb2df7f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / sfmm-0.1.0-py3-none-any.whl
| Download URL | sfmm-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7a33115d142e6e671ed62b02b52f03f2fe838a9e545c43edcd3c950e85deba61
|
|
BLAKE2b-256 checksum How to use checksums |
ed22526b708573756e58a7a424534ef4dff0505dd37fa19c1d7d0e0779e42915
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|