Production-style toolkit for sports market analytics and paper auto-trading. This project compares Polymarket sports prices with sportsbook implied probabilities, finds directional opportunities, and runs a guarded paper trading loop with audit logs.
- Overview
- Core Features
- Architecture
- Prerequisites
- Installation
- Configuration
- Pipeline Commands
- Auto-Trading Commands
- Risk Controls
- Outputs and Journals
- Recommended Workflow
- Troubleshooting
- Testing
- Project Structure
- Documentation
- License
The repository has two layers:
- Analytics layer
- fetches Polymarket sports markets,
- fetches sportsbook odds,
- matches events/outcomes,
- computes directional opportunities.
- Trading layer (paper-first)
- normalizes opportunities into signals,
- runs deterministic risk gates,
- simulates fills in paper mode,
- manages exits and positions,
- stores append-only JSONL journals. This is directional value trading, not guaranteed risk-free arbitrage.
- Polymarket Gamma API ingestion
- Optional CLOB read-only enrichment
- Sportsbook odds ingestion and normalization
- Fuzzy event matching with confidence scores
- Directional edge ranking by expected value
- Paper execution adapter with deterministic behavior
- Position lifecycle rules (TP/SL/max-hold)
- JSON/CSV exports and JSONL audit logs
Packages:
poly_sports/data_fetching/(fetch and compare)poly_sports/processing/(matching and edge logic)poly_sports/trading/(models, config, decision, risk, execution, journal, positions)scripts/(operational command entrypoints) Core flow:
- Fetch markets ->
data/arbitrage_data*.json - Compare with sportsbook odds ->
data/arbitrage_comparison.json - Detect opportunities ->
data/directional_arbitrage.json - Run trader ->
data/trading/*.jsonlandstate.json
- Python 3.9+ (3.12 recommended)
venvsupportODDS_API_KEYNo private key is required for paper mode.
From repo root:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .Create local env file:
cp .env.example .envMinimum .env:
ODDS_API_KEY=your_odds_api_key_here
TRADING_MODE=paper
ENABLE_LIVE_TRADING=false
TRADING_DRY_RUN=true| Variable | Default | Description |
|---|---|---|
ODDS_API_KEY |
- | Required sportsbook API key |
GAMMA_API_URL |
https://gamma-api.polymarket.com |
Polymarket source |
CLOB_HOST |
https://clob.polymarket.com |
CLOB endpoint |
ENRICH_WITH_CLOB |
false |
Include CLOB market metadata |
USE_STORED_EVENTS |
false |
Reuse cached sportsbook events |
OUTPUT_DIR |
data |
Output directory |
| Variable | Default | Description |
|---|---|---|
TRADING_MODE |
paper |
Trading mode (paper / live) |
ENABLE_LIVE_TRADING |
false |
Hard guard for live mode |
TRADING_DRY_RUN |
true |
Log-only execution mode |
TRADING_STAKE_USD |
25 |
Stake per entry |
TRADING_MAX_POSITIONS |
5 |
Max concurrent positions |
TRADING_MAX_DAILY_LOSS_USD |
100 |
Daily kill-switch |
TRADING_MIN_PROFIT |
0.02 |
Minimum expected edge |
TRADING_MIN_CONFIDENCE |
0.75 |
Minimum match confidence |
TRADING_MIN_LIQUIDITY_USD |
2000 |
Liquidity gate |
TRADING_MAX_SPREAD |
0.03 |
Spread gate |
TRADING_CYCLE_SECONDS |
60 |
Loop interval |
TRADING_COOLDOWN_SECONDS |
300 |
Post-close cooldown |
TRADING_COMPARISON_PATH |
data/arbitrage_comparison.json |
Signal input file |
TRADING_JOURNAL_DIR |
data/trading |
Journal output path |
Run the full data pipeline:
source .venv/bin/activate
python -m poly_sports.data_fetching.fetch_sports_markets
python -m poly_sports.data_fetching.fetch_sports_markets filter data/arbitrage_data.json data
python -m poly_sports.data_fetching.fetch_odds_comparison
python scripts/run_arbitrage_detection.py --sort-by profit_marginKey artifacts:
data/arbitrage_data_filtered.jsondata/arbitrage_comparison.jsondata/directional_arbitrage.json
One-cycle dry run:
python scripts/run_auto_trader.py --cycles 1 --dry-runContinuous paper run:
python scripts/run_auto_trader.pySession summary:
python scripts/summarize_trading_session.py --journal-dir data/tradingLive execution is intentionally guarded by config and adapter checks.
Signals can be denied for:
- low confidence,
- low liquidity,
- high spread,
- stale signal age,
- max position cap reached,
- duplicate market exposure,
- daily loss limit reached,
- cooldown not elapsed.
All decisions are written to
risk_events.jsonlfor audit and tuning.
Standard output files:
data/sports_markets.jsondata/arbitrage_data.jsondata/arbitrage_data_filtered.jsondata/arbitrage_comparison.jsondata/directional_arbitrage.jsonTrading journals indata/trading/:signals.jsonlrisk_events.jsonlorders.jsonlfills.jsonlpositions.jsonlstate.jsonUse journals as authoritative lifecycle history.
- Refresh comparison data.
- Run one-cycle dry run.
- Inspect
risk_events.jsonldeny reasons. - Tune thresholds gradually.
- Run continuous paper mode.
- Validate consistency across multiple days before any live path work.
Use python3 or activate venv:
source .venv/bin/activate
python ...Usually the shell is outside .venv; activate it again.
Usually a matching-quality issue (naming/time/coverage mismatch), not a crash.
Common causes:
TRADING_DRY_RUN=true,- all signals denied by risk filters,
- duplicate/idempotent suppression.
Run full tests:
pytest tests/ -vRun trading-focused tests:
pytest tests/test_trading_*.py -qPolymarket-Sports-Arbitrage-Bot/
├── poly_sports/
│ ├── data_fetching/
│ ├── processing/
│ ├── trading/
│ │ ├── execution/
│ │ ├── config.py
│ │ ├── decision_engine.py
│ │ ├── engine.py
│ │ ├── journal.py
│ │ ├── models.py
│ │ ├── position_manager.py
│ │ └── risk_engine.py
│ └── utils/
├── scripts/
├── tests/
├── docs/
└── data/
docs/ARBITRAGE_CALCULATION.md(edge and delta formulas)docs/TRADING_ENGINE.md(engine lifecycle and journal schema)
Add your preferred license (for example MIT) and include a LICENSE file in the repository root.
