An automated trading system for Polymarket's binary Up/Down prediction markets. The bot trades 5-minute, 15-minute, and 30-minute BTC/ETH/SOL/XRP Up/Down markets using zone-based price analysis, Kelly criterion position sizing, and oracle-aware exit logic.
Polymarket's Up/Down markets resolve at 1.00 (win) or 0.00 (loss) when a settlement oracle publishes the final price. The bot exploits mean-reversion opportunities when market probabilities deviate significantly from fair value, particularly during flash-crash events where one side's price drops sharply within a short window.
Core logic:
- Monitors live orderbook data via WebSocket.
- Classifies each outcome token into one of five price zones (DEAD, LOW, MID, HIGH, PREMIUM) and gates entries accordingly.
- Sizes each position using the Kelly criterion — a mathematically optimal bet-sizing formula that balances growth rate against ruin risk.
- Protects against oracle settlement risk by blocking new entries and force-closing open positions as the market approaches its resolution time.
- Paper-trades by default (no real orders) to allow strategy validation without risk.
polymarket-combined/
├── apps/ # Runnable strategy applications
│ ├── paper_trader.py # Paper trading simulator (no real orders)
│ ├── compounder.py # Live compounding strategy with capital protection
│ ├── flash_crash_strategy.py # Flash crash detection strategy (base)
│ ├── flash_crash_runner.py # Runner for flash_crash_strategy
│ ├── base_strategy.py # Abstract base class for all strategies
│ ├── db_alert_watcher.py # Database-backed price alert watcher
│ ├── orderbook_viewer.py # Real-time orderbook display
│ └── event_orderbook_viewer.py # Event-specific orderbook viewer
│
├── lib/ # Reusable trading library
│ ├── market_manager.py # WebSocket market connection + orderbook management
│ ├── market_selector.py # Market selection (by coin, interval, or DB prefix)
│ ├── price_tracker.py # Price history + flash crash detection
│ ├── position_manager.py # Position state tracking
│ ├── btc_oracle.py # BTC spot price feed (used for value arbitrage)
│ ├── db.py # PostgreSQL database helpers
│ ├── terminal_utils.py # ANSI color output utilities
│ └── alerts/
│ ├── swing_detector.py # Swing high/low reversal detector
│ ├── momentum_detector.py # Momentum signal detector
│ └── pump_detector.py # Pump-and-dump pattern detector
│
├── src/ # Polymarket API client layer
│ ├── websocket_client.py # CLOB WebSocket client (orderbook snapshots)
│ ├── gamma_client.py # Gamma API client (market metadata)
│ ├── bot.py # TradingBot core (order placement)
│ ├── client.py # CLOB REST API client
│ ├── signer.py # EIP-712 / Gnosis Safe signing
│ ├── crypto.py # Cryptographic utilities
│ ├── http.py # HTTP helpers
│ ├── config.py # Configuration loader
│ └── utils.py # Shared utilities
│
├── config.example.yaml # Configuration template
├── requirements.txt # Python dependencies
└── setup.bat / run.bat # Windows helper scripts
Each market token is assigned to a zone based on its current mid price:
| Zone | Price Range | Action | Notes |
|---|---|---|---|
| DEAD | < 0.10 | Block | Outcome nearly certain — no edge |
| LOW | 0.10–0.30 | Block (paper) / Small (compounder) | Unfavorable odds |
| MID | 0.30–0.70 | Normal entry | Mean reversion target |
| HIGH | 0.70–0.90 | Good entry | Flash crash opportunity |
| PREMIUM | 0.90–0.99 | Best entry | Hold until settlement |
Position sizes are calculated using the fractional Kelly formula:
f* = (W * R - (1 - W)) / R * kelly_fraction
Where W is the estimated win rate and R is the payoff ratio for each zone. Fractional Kelly (0.15x–0.50x depending on zone) is applied for risk control.
Polymarket markets settle when an external oracle publishes the final price. The bot enforces strict time-based guards:
- No new entries within the last 30–45 seconds before settlement.
- Force-close all open positions within the last 25–30 seconds.
- Neutral zone block: when the market is close to 50/50 and near expiry, entries are suppressed to avoid getting caught by random noise at settlement.
Simulates trades against live Polymarket data without sending any real orders. Tracks balance, fees, unrealized P&L, zone statistics, and win rates across a configurable session duration.
Additional logic:
- Momentum reversal: after a stop loss, immediately evaluates a reversal trade in the opposite direction if the price is still in a valid zone.
- Death spiral protection: if the same direction loses twice in a row, a 2-minute reversal cooldown is enforced.
- Volatility filter: minimum price volatility required before entering.
A live compounding strategy with multi-layered capital protection:
- Protected base: a configurable floor (e.g. $10) that is never risked regardless of market conditions.
- Trading capital: only the amount above the protected base is used for trades.
- Target multiplier: the session ends automatically when the trading capital reaches the configured multiple (e.g. 1.5x, 2.0x).
Entry signals (in priority order):
- Dynamic Order Flow Event (T+60s): analyses 60 seconds of live orderbook data for bid/ask imbalance and price velocity before committing to an entry.
- Market Open Rush (first 45s): aggressive entries at market open when prices are volatile and mid-zone.
- Swing Bounce/Reject: enters on detected swing lows (bounce) or swing highs (reject) using a configurable window and minimum move threshold.
- Oracle Value: BTC spot price vs. market implied probability arbitrage.
Detects when one side's probability drops by a configurable threshold (default 30pp) within a short lookback window, then buys the crashed side expecting mean reversion. Uses configurable take-profit and stop-loss dollar amounts.
- Python 3.10+
- PostgreSQL (optional, only required for
db_alert_watcher.pyand DB-prefix market selection) - A Polymarket account with a funded Gnosis Safe wallet (required only for live trading; paper trading works without credentials)
# 1. Clone the repository
git clone <repo-url>
cd polymarket-combined
# 2. Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # Linux/macOS
venv\Scripts\activate # Windows
# 3. Install dependencies
pip install -r requirements.txt
# 4. Configure credentials (live trading only)
cp config.example.yaml config.yaml
# Edit config.yaml and fill in your safe_address, RPC URL, and builder API keys
# 5. Set environment variables (or use a .env file)
# POLYMARKET_PRIVATE_KEY=<your-private-key>Run a 30-minute simulation with a $100 starting balance on BTC 5-minute markets:
python apps/paper_trader.py --db-prefix btc-updown-5m --balance 100 --duration 30Run using the Gamma API to auto-discover the current BTC market:
python apps/paper_trader.py --coin BTC --balance 100 --duration 30Customise entry thresholds:
python apps/paper_trader.py \
--db-prefix btc-updown-5m \
--balance 200 \
--duration 60 \
--drop 0.05 \ # 5% drop triggers entry (default: 7%)
--lookback 20 \ # 20s detection window (default: 15s)
--tp 0.25 \ # 25% take profit (default: 20%)
--sl 0.08 # 8% stop loss (default: 10%)Start with $12 total balance, protect $10, target 1.5x on the $2 trading capital:
python apps/compounder.py --balance 12 --protected 10 --target-mult 1.5 --db-prefix btc-updown-5mMore aggressive: $15 total, $10 protected, target 2x, write a session log:
python apps/compounder.py \
--balance 15 \
--protected 10 \
--target-mult 2.0 \
--db-prefix btc-updown-5m \
--log autoUse the Gamma API with a specific coin and interval instead of a DB prefix:
python apps/compounder.py --coin ETH --interval 15m --balance 12 --protected 10paper_trader.py:
| Flag | Default | Description |
|---|---|---|
--coin |
BTC | Coin (BTC, ETH, SOL, XRP) — used if --db-prefix not set |
--db-prefix |
btc-updown-5m | Market slug prefix; overrides --coin |
--balance |
100 | Starting balance in USD |
--duration |
30 | Simulation duration in minutes |
--drop |
0.07 | Flash crash drop threshold (7%) |
--lookback |
15 | Drop detection window in seconds |
--tp |
0.20 | Take profit (20%) |
--sl |
0.10 | Stop loss (10%) |
--no-trade-secs |
45 | Block new entries this many seconds before settlement |
--force-exit-secs |
30 | Force-close this many seconds before settlement |
--min-vol |
0.03 | Minimum volatility required to enter |
compounder.py:
| Flag | Default | Description |
|---|---|---|
--balance |
12 | Total starting balance in USD |
--protected |
10 | Protected base amount (never risked) |
--target-mult |
1.5 | Exit multiplier (1.5 = 50% gain on trading capital) |
--coin |
BTC | Coin symbol |
--interval |
5m | Market interval (5m, 15m, 30m) |
--db-prefix |
— | DB slug prefix; overrides --coin/--interval |
--tp |
0.10 | Take profit (10%) |
--sl |
0.05 | Stop loss (5%) |
--duration |
60 | Max session duration in minutes |
--log |
— | Log file path (auto for timestamped name) |
The PriceTracker maintains a rolling price history per token. A flash crash event is
triggered when the price drops by at least drop_threshold within the last
lookback_secs seconds. On detection, the bot evaluates a long entry on the crashed
token.
The compounder fetches the current BTC spot price via BTCOracle and compares it to
the "price to beat" captured at market open. If BTC is above the opening price, the
market should resolve UP — if the UP token is trading below fair value (edge >= 3pp),
the bot enters long UP. The reverse applies for BTC below opening price.
For PREMIUM zone positions (probability > 0.90), the bot holds through settlement rather than taking profit early, since the expected value of a $0.92 token reaching $1.00 at settlement exceeds the benefit of an early exit. For HIGH zone positions, profit-taking is suspended during the final 90 seconds if the trade is in profit.
This software is experimental. Prediction markets are highly volatile and can move to zero. The strategies implemented here do not guarantee profit. Always:
- Test with paper trading before using real funds.
- Keep protected capital well above the minimum threshold.
- Monitor sessions actively, especially near settlement.
- Never risk funds you cannot afford to lose.
See LICENSE for details.