Skip to main content

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)

Source distribution for sfmm 0.1.0
File Size Uploaded
sfmm-0.1.0.tar.gz 21.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sfmm 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page