Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sfmm-0.1.0.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sfmm-0.1.0-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

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

Hashes for sfmm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 268263eba4e92164b7e41c357b75816d75a6ff650be58aa811b36749d893fdd7
MD5 b6c56edbd033316b14eab39cb5d5411e
BLAKE2b-256 3797d38152b3d38e2cca3ed362b57fa32ba8f33d45b71e0a8a1fcf3feb2df7f5

See more details on using hashes here.

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

Hashes for sfmm-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7a33115d142e6e671ed62b02b52f03f2fe838a9e545c43edcd3c950e85deba61
MD5 d5181d92fceeb4525be275e232edb742
BLAKE2b-256 ed22526b708573756e58a7a424534ef4dff0505dd37fa19c1d7d0e0779e42915

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page