Skip to content

zostaff/poly-trading-bot

Repository files navigation

Polymarket AI Trading Bot

Python 3.12+ License: MIT

A toolkit for building automated trading strategies on Polymarket prediction markets.

Polymarket CLOB client (EIP-712 signed orders via py-clob-client), Gamma API ingestion for market discovery, USDC.e balance reads on Polygon, position tracking, SQLite telemetry, a Streamlit dashboard, and a pluggable LLM client (any model on OpenRouter). Three example strategies ship with the repo as starting points — fork them, replace them, or write your own from scratch.

Heritage note. The trading-strategy logic, risk management, dashboard, telemetry, and LLM stack are exchange-agnostic. The exchange layer (CLOB client, market discovery, USDC.e balance reads on Polygon) is purpose-built for Polymarket. Any *.legacy.bak files in src/clients/ are archived sources from a previous exchange port and will be removed once a live trading session has been verified end-to-end.


Read this before running with real money. No strategy in this repo is guaranteed to make money. The examples lose money on certain markets. Trading prediction markets is hard, the edges are small, and what worked last quarter may not work this quarter. This is a toolkit, not a turnkey bot. Read the code, understand what it does, and tune it for the markets you care about. The authors are not responsible for losses you incur using this software.


Quick Start

# 1. Clone and set up
git clone <your fork URL>
cd polymarket-trading-bot
python setup.py        # creates .venv, installs deps (incl. py-clob-client + web3)

# 2. Add your keys & wallet
cp env.template .env
# then open .env and fill in:
#   POLYMARKET_PRIVATE_KEY   — Polygon wallet that will sign orders
#   POLYMARKET_FUNDER        — proxy address (only if SIGNATURE_TYPE != 0)
#   POLYMARKET_SIGNATURE_TYPE — 0=EOA/MetaMask, 1=Magic Link, 2=Gnosis Safe
#   OPENROUTER_API_KEY       — for LLM-driven strategies

# 3. (One-time) approve USDC allowances for the Polymarket contracts
python scripts/set_allowances.py

# 4. Verify connectivity
python cli.py health

# 5. Run an example strategy in DRY-RUN mode (default)
python cli.py run --paper                  # AI directional (LLM-driven)
python cli.py run --safe-compounder        # Edge-based NO-side, no LLM

Open the dashboard in another terminal:

python cli.py dashboard

Need keys?

  • Polygon wallet → export the private key from MetaMask (or use a Magic Link wallet from polymarket.com)
  • You'll also need USDC.e on Polygon to actually trade — python cli.py status will print your balance
  • OpenRouter key → openrouter.ai

What's Included

This repo gives you the building blocks. The example strategies use them — your own strategies can too.

Component What it does Where it lives
Polymarket client Authenticated REST + WebSocket client (RSA signing, retries, rate-limit handling) src/clients/polymarket_client.py
Market ingestion Pulls the full tradeable universe via the Events API, persists to SQLite src/jobs/ingest.py
Position tracking Stop-loss, take-profit, time-based, and resolution-based exits with real Polymarket sell orders src/jobs/track.py
LLM client Single OpenRouter API key, swap models with one config line, fallback chain on errors, persistent daily-cost tracker src/clients/openrouter_client.py, src/clients/xai_client.py
SQLite telemetry Every trade, AI decision, and cost metric logged locally src/utils/database.py
Streamlit dashboard Real-time portfolio, positions, P&L, decision logs beast_mode_dashboard.py
Paper trading Log signals against settled markets without sending orders paper_trader.py
CLI run, dashboard, status, health, scores, history, close-all cli.py
Risk helpers Kelly sizing, stop-loss math, drawdown circuit breaker src/utils/, src/strategies/

The repo also ships scaffolding for things that aren't fully wired — multi-agent debate runners in src/agents/, sentiment analyzer in src/data/, etc. Treat them as starting points if you want to extend them.


Example Strategies

Three strategies ship with the repo. None of them is "the right answer." They exist so you can run something end-to-end and see how the pieces connect, then fork the one closest to what you want to build.

1. AI Directional — python cli.py run

The default. For each candidate market, it calls a single LLM via OpenRouter (with a fallback chain on errors) to score directional confidence, then sizes positions with fractional Kelly and applies category/sector guardrails.

It is not a "5-model ensemble" despite earlier README claims. One model is called per decision. The fallback chain only triggers on errors. The agents/ directory contains scaffolding for real parallel multi-model voting, but it's not wired into the live trading path. If you want a real ensemble, fork src/jobs/decide.py and build it.

python cli.py run --paper          # paper trading
python cli.py run --live           # live trading (real money)

Defaults: 15% max drawdown, 45% min confidence, 3% max position size, 30% max sector concentration, quarter-Kelly. All configurable in src/config/settings.py.

2. Safe Compounder — python cli.py run --safe-compounder

Pure edge-based math, no LLM required. Scans every active Polymarket market for NO-side asks above a price threshold with a positive expected-value edge, then places resting maker orders one cent below the ask.

python cli.py run --safe-compounder              # dry-run preview
python cli.py run --safe-compounder --live       # live execution

# Run continuously instead of one cycle and exit:
python cli.py run --safe-compounder --live --loop --interval 300

Rules: NO side only, YES last ≤ 20¢, NO ask > 80¢, edge > 5¢, max 10%/position, skips sports/entertainment/"mention" markets.

3. Beast Mode — python cli.py run --beast

Aggressive settings with no category guardrails. Available for comparison and experimentation — not recommended for live trading. Running this with real money historically led to significant losses on this repo.


Stopping Cleanly

Ctrl-C sends SIGINT and triggers graceful shutdown — the bot finishes the in-flight cycle, logs, and exits. Open positions remain on Polymarket until they resolve.

If you want to liquidate everything before stepping away:

# 1. Stop the bot
Ctrl-C

# 2. Place limit sells at the current best bid for every open position
python cli.py close-all              # dry-run preview
python cli.py close-all --live       # actually send orders

# 3. Verify
python cli.py status

close-all queries Polymarket directly (not the local DB), so it works even when local state is stale. Sells are limit-priced, so they may rest unfilled on thin books — check Polymarket or cli.py status after a minute.


Installation

Prerequisites

Automated Setup

git clone https://github.com/zostaff/polymarket-ai-trading-bot.git
cd polymarket-ai-trading-bot
python setup.py

Creates a virtual env, installs dependencies, and prints next steps.

Manual Setup

git clone https://github.com/zostaff/polymarket-ai-trading-bot.git
cd polymarket-ai-trading-bot

python -m venv .venv
source .venv/bin/activate        # macOS / Linux
# .venv\Scripts\activate          # Windows

pip install -r requirements.txt

Configuration

cp env.template .env
# then edit .env with your keys
Variable Description
POLYMARKET_PRIVATE_KEY Polygon wallet private key that signs orders (0x-prefixed 64-hex)
POLYMARKET_FUNDER Optional. Proxy address that holds USDC (only set when SIGNATURE_TYPE != 0)
POLYMARKET_SIGNATURE_TYPE 0 = EOA / MetaMask (default), 1 = Magic Link, 2 = Gnosis Safe
POLYMARKET_HOST Defaults to https://clob.polymarket.com
POLYMARKET_CHAIN_ID Defaults to 137 (Polygon mainnet)
POLYGON_RPC_URL Optional custom Polygon RPC; defaults to the public endpoint
OPENROUTER_API_KEY OpenRouter key (only for the AI directional strategy)
DRY_RUN / LIVE_TRADING_ENABLED DRY_RUN=true (default) keeps the bot in paper mode. LIVE_TRADING_ENABLED=true is the legacy flag, equivalent to DRY_RUN=false.

The wallet's private key lives in .env only — never on disk. .env is git-ignored.

Verify everything is wired:

python cli.py health

Configuration

All trading parameters live in src/config/settings.py. The most useful knobs:

# Position sizing
max_position_size_pct  = 3.0     # Max 3% of balance per position
max_positions          = 10      # Max concurrent positions
kelly_fraction         = 0.25    # Quarter-Kelly (conservative)

# Market filtering
min_volume             = 500     # Minimum contract volume
max_time_to_expiry_days = 14     # How far out to trade
min_confidence_to_trade = 0.45   # Minimum AI confidence to enter

# LLM (OpenRouter)
primary_model          = "anthropic/claude-sonnet-4.5"
ai_temperature         = 0       # Deterministic
ai_max_tokens          = 8000

# Risk management
max_daily_loss_pct     = 10.0    # Daily loss circuit breaker
max_drawdown           = 0.15    # Portfolio drawdown halt
daily_ai_cost_limit    = 10.0    # Max daily LLM spend in USD

Swapping models: change primary_model to any slug from openrouter.ai/models. The fallback chain in src/clients/openrouter_client.py controls what happens when the primary errors.

Controlling LLM spend: the bot checks the daily limit before every API call and skips trading until the next calendar day once exhausted. Set DAILY_AI_COST_LIMIT in .env to override.


Project Structure

polymarket-ai-trading-bot/
├── beast_mode_bot.py          # Example AI directional bot — main loop orchestration
├── cli.py                     # Unified CLI: run, dashboard, status, health, close-all, scores, history
├── paper_trader.py            # Paper-trading signal logger + static dashboard
├── setup.py                   # Bootstrap script
├── env.template               # Environment variable template
│
├── src/
│   ├── agents/                # UNWIRED scaffolding for multi-agent debate (fork to use)
│   ├── clients/               # Polymarket, OpenRouter, WebSocket clients
│   ├── config/                # Settings and trading parameters
│   ├── data/                  # News + sentiment helpers (optional)
│   ├── events/                # Async event bus
│   ├── jobs/                  # ingest, decide, execute, track, evaluate
│   ├── strategies/            # Safe compounder, category scorer, portfolio enforcer
│   └── utils/                 # Database, logging, prompts, risk helpers
│
├── scripts/                   # Diagnostic and utility scripts
├── docs/                      # Additional docs + paper-trading dashboard HTML
└── tests/                     # Pytest suite

Paper Trading

Simulate trades without sending real orders. Every signal is logged to SQLite and a static HTML dashboard renders cumulative P&L after markets settle.

python paper_trader.py                            # one scan
python paper_trader.py --loop --interval 900      # continuous, every 15m
python paper_trader.py --settle                   # update outcomes for resolved markets
python paper_trader.py --dashboard                # regenerate HTML
python paper_trader.py --stats                    # print stats

Output goes to docs/paper_dashboard.html.


Category Scoring (used by AI Directional)

The category scorer evaluates each Polymarket market category on a 0-100 scale based on historical ROI, win rate, recent trend, and sample size. Allocation per category is gated by score.

Score Max Position Status
80–100 20% STRONG
60–79 10% GOOD
40–59 5% WEAK
20–39 2% POOR
0–19 0 BLOCKED
python cli.py scores

This is one heuristic for category-level risk control. If it doesn't fit your strategy, ignore it — it's only used by the AI directional path.


Performance Tracking

Every trade, AI decision, and cost metric is recorded to trading_system.db (local SQLite). Inspect via the dashboard or:

python cli.py history                # Last 50 trades
python cli.py history --limit 100    # Last 100
python cli.py status                 # Live balance + open positions from Polymarket

Development

Running Tests

pytest tests/          # full suite
pytest tests/ -v       # verbose
pytest --cov=src       # with coverage

Code Quality

black src/ tests/ cli.py beast_mode_bot.py
isort src/ tests/ cli.py beast_mode_bot.py
mypy src/

Adding a New Strategy

  1. Create a module under src/strategies/
  2. Wire it into a CLI flag in cli.py (or invoke it directly)
  3. Use the PolymarketClient for orders/positions and DatabaseManager for state
  4. Add tests under tests/

Troubleshooting

Health check fails with auth / signing error

Polymarket auth = an EIP-712 signature derived from your wallet's private key. Auth failures usually mean one of:

  1. POLYMARKET_PRIVATE_KEY in .env is malformed (must be 0x + 64 hex chars).
  2. POLYMARKET_SIGNATURE_TYPE doesn't match the wallet kind: 0 for an EOA you exported from MetaMask, 1 for a Magic-Link / email-login wallet, 2 for Gnosis Safe / browser proxy. With 1 or 2, you also need to set POLYMARKET_FUNDER to the proxy address that holds USDC.
  3. python cli.py health --full is the path that actually signs — the default health command skips it to save Polymarket rate-limit slots.
Order placement fails with `not allowed for this market`

Polymarket has two exchange contracts: the standard CTF Exchange and the newer neg_risk_exchange for negative-risk markets (most multi-outcome event markets). The bot reads each market's negRisk flag from Gamma at ingestion time and routes orders accordingly. If you see this error, verify the market was registered through the ingestion job (not constructed by hand) — polymarket_client.register_market(condition_id, yes, no, neg_risk=...) must be called with the right flag.

"Shutdown signal received" without pressing Ctrl-C

The bot now logs which signal arrived (SIGINT, SIGTERM, or SIGHUP). If you see SIGTERM or SIGHUP without sending it yourself, common causes:

  • Parent shell closed (run inside tmux, screen, or with nohup)
  • A cloud platform / systemd / launchd timeout
  • Another shell sent kill <pid>
  • An OOM killer warning before SIGKILL

The bot did not kill itself — something external ended the process.

Bot ran for weeks but placed no positions

This is expected behavior, not a bug. The example strategies are conservative by design:

  • AI Directional requires confidence ≥ 45%, category score ≥ 30, and is gated by drawdown / sector caps. On many days, no markets clear all four filters.
  • Safe Compounder requires NO ask > 80¢ AND edge > 5¢. Most NO-side markets don't meet both.

If you want more activity, lower the thresholds in src/config/settings.py (or the relevant strategy file) — but that means taking lower-edge bets. Or write your own strategy that targets the markets you actually have an edge on. This repo is a toolkit; the example thresholds are starting points.

"no such table: positions" error on fresh install

The DB file isn't committed; it's created at runtime. The bot auto-initializes on startup, but you can do it manually:

python -m src.utils.database

Use -m — running python src/utils/database.py directly fails with an import error.

AdGuard (macOS) blocks dependency downloads

If AdGuard is running as a system-level proxy, pip install may time out during setup. Disable AdGuard at the system level for the install, then re-enable it. AdGuard as a browser extension is fine.

Bot not placing live trades despite --live
grep -i "live trading\|paper trading\|LIVE ORDER" logs/trading_system.log | tail -20

If you see "Paper trading mode" the flag isn't taking effect. Verify the API key has trading permissions in Polymarket Settings.

Model not found / OpenRouter API errors

Model names on OpenRouter change. Update primary_model in src/config/settings.py with a current slug from openrouter.ai/models, or set PRIMARY_MODEL in .env.

Bot only seeing "KXMVE" tickers

The Polymarket /markets endpoint returns parlay tickers; real markets live under the Events API. The ingestion pipeline already uses Events with nested markets. If you see only KXMVE*, check API permissions and run python cli.py health.

Python 3.14 PyO3 compatibility error
export PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1
pip install -r requirements.txt

Or use Python 3.13:

pyenv install 3.13.1 && pyenv local 3.13.1
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

Lessons From Live Trading

These are observations from running the example strategies with real money on Polymarket. They informed the defaults shipped here. They are not universal trading wisdom — they're notes from one set of experiments.

1. Category discipline mattered more than AI confidence. The LLM could be 80% confident on a CPI trade and still be wrong. Market-implied probabilities on highly-watched economic releases are already efficient.

2. Kelly fraction matters enormously. Three-quarter Kelly compounds losses catastrophically on a 45% win-rate strategy. Quarter-Kelly is what the example strategies use.

3. A 50% drawdown limit isn't a limit. The default is 15% with the circuit breaker actually halting trades, not just logging.

4. Sector concentration creates correlated losses. When 90% of capital is in economic categories on a Fed day, everything moves together. The default cap is 30% per category.

5. More trades without edge is faster path to zero. The default scan interval is 60 seconds, not 30, and trades are gated by both confidence and category score.

Your edge is probably somewhere else. Use the toolkit to find it.


Contributing

Contributions welcome. See CONTRIBUTING.md for full guidelines.

# 1. Fork
# 2. Create a feature branch
git checkout -b feature/your-feature
# 3. Make changes, add tests, run pytest and black
# 4. Commit using conventional commits (feat:, fix:, refactor:)
# 5. Open a PR

Resources


License

MIT. See LICENSE.


If this is useful to you, a star helps others find it.

About

poly-trading-bot

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages