Skip to content

PolyMomentum/polymarket-momentum-mispricing-bot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

polymarket-momentum-mispricing-bot

Production-ready TypeScript bot for trading Polymarket BTC 5-minute and 15-minute fast markets using Binance BTCUSDT momentum lag detection and market odds mispricing.


Overview

This bot watches the live Binance BTCUSDT 1-minute kline stream and, when it detects a significant price move (0.35%–0.80%) combined with an elevated volume spike, checks whether the corresponding Polymarket fast market is still mispriced. If the YES/NO odds haven't caught up to the Binance signal, it enters a trade and manages the position with take-profit, stop-loss, and force-close logic.

Keywords: Polymarket trading bot, Binance momentum bot, prediction market arbitrage, BTC fast market bot, TypeScript trading bot.


Strategy Logic

Entry β€” Buy YES

Condition Threshold
BTC 1m momentum +0.35% to +0.80%
YES price < 0.47
Volume ratio > 1.5Γ— average
Candle colour streak Last 2 green

Entry β€” Buy NO

Condition Threshold
BTC 1m momentum βˆ’0.35% to βˆ’0.80%
YES price > 0.56
Volume ratio > 1.5Γ— average
Candle colour streak Last 2 red

Skip Guards

  • Less than 60 seconds remaining
  • Spread wider than 6Β’
  • Liquidity below $50
  • Momentum outside the configured band
  • Market already priced in

Exit

Trigger Threshold
Take profit +5Β’ per share
Stop loss βˆ’3Β’ per share
Force close 15 s before expiry

Risk Controls

  • Max daily loss: $50 (configurable)
  • Max concurrent positions: 3
  • Cooldown after 3 consecutive losses: 15 minutes

Stack

Package Role
ws Binance WebSocket kline stream
axios REST calls (Binance, Polymarket CLOB, Simmer)
zod Environment config validation
pino + pino-pretty Structured logging
dotenv .env loading
typescript + tsx TypeScript compilation and dev runner

Project Structure

src/
β”œβ”€β”€ config/
β”‚   └── index.ts          Zod-validated env config
β”œβ”€β”€ exchanges/
β”‚   β”œβ”€β”€ binance.ts        WebSocket client + REST warm-up
β”‚   └── polymarket.ts     Market discovery + CLOB order books
β”œβ”€β”€ strategies/
β”‚   β”œβ”€β”€ momentum.ts       1m/3m/5m momentum + volume ratio + streak
β”‚   └── mispricing.ts     Entry/exit signal generation
β”œβ”€β”€ risk/
β”‚   └── manager.ts        Daily loss, position limits, cooldown
β”œβ”€β”€ execution/
β”‚   β”œβ”€β”€ orders.ts         Simmer SDK buy/sell + portfolio balance
β”‚   └── positions.ts      In-memory position tracker + exit loop
β”œβ”€β”€ backtest/
β”‚   └── runner.ts         Historical candle replay + P&L simulation
β”œβ”€β”€ utils/
β”‚   β”œβ”€β”€ types.ts          All shared TypeScript types
β”‚   β”œβ”€β”€ logger.ts         Pino singleton
β”‚   └── storage.ts        Append-only JSONL trade log
└── index.ts              Main bot loop + backtest entry point

Quick Start

1. Prerequisites

2. Install

cd polymarket-momentum-mispricing-bot
npm install

3. Configure

cp .env.example .env
# Edit .env and set SIMMER_API_KEY

4. Run

# Dry run (no real trades β€” default)
npm run dry-run

# Live trading
npm run live

# Backtest on historical data
npm run backtest

# Development mode with auto-restart
npm run dev

5. Build for production

npm run build
npm start

npm Scripts

Script Command Description
dev tsx src/index.ts Dev run with live TypeScript compilation
dry-run DRY_RUN=true tsx src/index.ts Signal detection only, no orders
live DRY_RUN=false tsx src/index.ts Live trading
backtest BACKTEST_MODE=true tsx src/index.ts Historical replay
build tsc Compile to dist/
start node dist/index.js Run compiled build
typecheck tsc --noEmit Type-check without output

Environment Variables

See .env.example for the full list with descriptions. Key variables:

Variable Default Description
SIMMER_API_KEY β€” Required for live trading
DRY_RUN true false to enable real orders
POSITION_SIZE_USD 10 USD per trade
MOMENTUM_MIN_PCT 0.35 Min 1m BTC move %
MOMENTUM_MAX_PCT 0.80 Max 1m BTC move %
YES_BUY_MAX_PRICE 0.47 Buy YES below this
YES_SELL_MIN_PRICE 0.56 Buy NO above this
MAX_DAILY_LOSS_USD 50 Daily loss circuit breaker
BACKTEST_START_DATE 2025-01-01 Backtest start
BACKTEST_END_DATE 2025-01-31 Backtest end

Trade Log

All trades are appended to data/trades.jsonl (configurable via TRADES_FILE). Each line is a complete JSON TradeRecord. Example:

Each line in data/trades.jsonl is one complete trade:

{"id":"3f2a1b4c-8d9e-4f5a-b6c7-d8e9f0a1b2c3","timestamp":"2026-04-30T02:43:05.000Z","market":"Will BTC be higher at 2:50PM ET? (Apr 30, 2026 - 2:45PM to 2:50PM ET)","conditionId":"0x9f3c4a2b1d0e8f7a6c5b4d3e2f1a0b9c8d7e6f5a","side":"YES","entryPrice":0.44,"exitPrice":0.492,"shares":22.7,"cost":10.0,"pnl":1.1804,"status":"closed","exitReason":"take_profit","entryTime":"2026-04-30T02:43:05.000Z","exitTime":"2026-04-30T02:44:18.000Z","secondsHeld":73,"momentum1m":0.463,"momentum3m":0.581,"momentum5m":0.744,"volumeRatio":2.34,"candleStreak":3,"window":"5m","dryRun":false}
{"id":"7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d","timestamp":"2026-04-30T02:51:44.000Z","market":"Will BTC be higher at 3:00PM ET? (Apr 30, 2026 - 2:45PM to 3:00PM ET)","conditionId":"0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b","side":"NO","entryPrice":0.41,"exitPrice":0.379,"shares":24.4,"cost":10.0,"pnl":-0.7564,"status":"closed","exitReason":"stop_loss","entryTime":"2026-04-30T02:51:44.000Z","exitTime":"2026-04-30T02:53:01.000Z","secondsHeld":77,"momentum1m":-0.512,"momentum3m":-0.643,"momentum5m":-0.791,"volumeRatio":1.87,"candleStreak":-2,"window":"15m","dryRun":false}
{"id":"b1c2d3e4-f5a6-7b8c-9d0e-1f2a3b4c5d6e","timestamp":"2026-04-30T03:05:22.000Z","market":"Will BTC be higher at 3:10PM ET? (Apr 30, 2026 - 3:05PM to 3:10PM ET)","conditionId":"0x2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c","side":"YES","entryPrice":0.43,"exitPrice":0.48,"shares":23.3,"cost":10.0,"pnl":1.163,"status":"closed","exitReason":"take_profit","entryTime":"2026-04-30T03:05:22.000Z","exitTime":"2026-04-30T03:06:47.000Z","secondsHeld":85,"momentum1m":0.391,"momentum3m":0.512,"momentum5m":0.623,"volumeRatio":1.92,"candleStreak":2,"window":"5m","dryRun":false}

Session stats after 3 trades:

# Side Entry Exit Shares Cost P&L Reason Time Held
1 YES $0.440 $0.492 22.7 $10.00 +$1.18 take_profit 73 s
2 NO $0.410 $0.379 24.4 $10.00 βˆ’$0.76 stop_loss 77 s
3 YES $0.430 $0.480 23.3 $10.00 +$1.16 take_profit 85 s
$30.00 +$1.59

Sample Output

Startup β€” dry-run mode (npm run dry-run)

02:41:08 INFO: πŸ”‘ Execution: direct CLOB (private key loaded)
    address: "0xA1b2C3d4E5f6A7b8C9d0E1f2A3b4C5d6E7f8A9b0"
    mode: "clob"

02:41:08 INFO: πŸš€ polymarket-momentum-mispricing-bot starting [DRY RUN]
    mode: "clob"
    dryRun: true
    trade5m: true
    trade15m: true
    momentumMin: 0.35
    momentumMax: 0.8
    positionSize: 10
    maxDaily: 50

02:41:08 INFO: Connecting to Binance WebSocket...
02:41:09 INFO: Binance WebSocket connected
    symbol: "BTCUSDT"
    stream: "btcusdt@kline_1m"

02:41:09 INFO: Warming up candle buffer
    symbol: "BTCUSDT"
    limit: 60

02:41:10 INFO: Candle buffer ready
    count: 60
    priceNow: 94821.32

02:41:10 INFO: Bot running β€” press Ctrl+C to stop

Live strategy tick β€” skip signals

02:41:10 DEBUG: Skip: 1m momentum 0.112% < min 0.35% (too weak)
    window: "5m"
    yes: "0.510"
    no: "0.490"
    secs: "247"

02:41:15 DEBUG: Skip: Volume ratio 1.21x < min 1.5x
    window: "5m"
    yes: "0.480"
    no: "0.520"
    secs: "242"

02:41:20 DEBUG: Skip: 1m momentum 0.093% < min 0.35% (too weak)
    window: "15m"
    yes: "0.503"
    no: "0.497"
    secs: "731"

Live trade β€” BUY YES signal detected

02:43:05 INFO: 🎯 Entry signal: YES on 5m market
    window: "5m"
    side: "YES"
    confidence: "0.71"
    reason: "BTC β–²0.463% | YES @ 0.440 | vol 2.34x | streak +3"
    yes: "0.440"
    no: "0.560"
    secs: "187"
    momentum: "β–² 1m:+0.463% 3m:+0.581% 5m:+0.744% vol:2.34x streak:+3 $94,971.84"

02:43:05 INFO: [DRY RUN] BUY simulated
    marketId: "0x9f3c4a2b1d0e8f7a6c5b4d3e2f1a0b9c8d7e6f5a"
    side: "YES"
    amountUsd: 10
    fillPrice: 0.44
    dryShares: "22.7"
    mode: "clob"

02:43:05 INFO: Position opened
    id: "3f2a1b4c-8d9e-4f5a-b6c7-d8e9f0a1b2c3"
    side: "YES"
    question: "Will BTC be higher at 2:50PM ET? (Apr 30..."
    entryPrice: "0.440"
    shares: "22.7"
    cost: "10.00"

Position exit β€” take profit

02:44:18 INFO: βœ… Position exit β€” take_profit
    id: "3f2a1b4c-8d9e-4f5a-b6c7-d8e9f0a1b2c3"
    side: "YES"
    reason: "take_profit"
    entryPrice: "0.440"
    exitPrice: "0.492"
    pnlPerShare: "0.052"
    pnl: "1.1804"
    shares: "22.7"

02:44:18 INFO: [DRY RUN] SELL simulated
    marketId: "0x9f3c4a2b1d0e8f7a6c5b4d3e2f1a0b9c8d7e6f5a"
    side: "YES"
    shares: 22.7
    currentPrice: 0.492
    mode: "clob"

02:44:18 INFO: RiskManager: position closed
    pnl: "1.1804"
    dailyPnl: "1.1804"
    openCount: 0
    consecutiveLosses: 0

BUY NO signal β€” BTC drops

02:51:44 INFO: 🎯 Entry signal: NO on 15m market
    window: "15m"
    side: "NO"
    confidence: "0.68"
    reason: "BTC β–Ό0.512% | YES @ 0.590 (HIGH) | vol 1.87x | streak -2"
    yes: "0.590"
    no: "0.410"
    secs: "634"
    momentum: "β–Ό 1m:-0.512% 3m:-0.643% 5m:-0.791% vol:1.87x streak:-2 $94,487.22"

02:51:44 INFO: [DRY RUN] BUY simulated
    side: "NO"
    amountUsd: 10
    fillPrice: 0.41
    dryShares: "24.4"

02:51:44 INFO: Position opened
    id: "7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"
    side: "NO"
    entryPrice: "0.410"
    shares: "24.4"
    cost: "10.00"

Stop loss

02:53:01 INFO: ❌ Position exit β€” stop_loss
    id: "7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"
    side: "NO"
    entryPrice: "0.410"
    exitPrice: "0.379"
    pnlPerShare: "-0.031"
    pnl: "-0.7564"

02:53:01 INFO: RiskManager: position closed
    pnl: "-0.7564"
    dailyPnl: "0.4240"
    consecutiveLosses: 1

60-second status log

02:55:00 INFO: πŸ“Š Status
    risk: "Daily P&L: $0.42 | Open: 0 | Trades: 2 (1W/1L 50.0%) | Streak-losses: 1"
    btcPrice: "94,512.80"
    storedTrades: 2
    storedPnl: "0.4240"
    tradeFile: "data/trades.jsonl"

Loss-streak cooldown (after 3 losses)

03:18:22 WARN: Loss streak threshold reached β€” entering cooldown
    consecutiveLosses: 3
    cooldownMinutes: 15
    cooldownUntil: "2026-04-30T03:33:22.000Z"

03:18:30 WARN: β›” Risk gate blocked trade
    reason: "Cooldown active β€” 831s remaining"

Backtest

The backtest mode fetches real Binance BTCUSDT 1-minute candles and replays them through the strategy. Since historical Polymarket market data is not publicly available, synthetic YES/NO prices are used for entry conditions (configurable via BACKTEST_ENTRY_PRICE). Market resolution is exact β€” it uses the actual BTC price direction during each window.

# Backtest January 2025
BACKTEST_START_DATE=2025-01-01 BACKTEST_END_DATE=2025-01-31 npm run backtest

Sample backtest console output

════════════════════════════════════════════════════════
  Starting backtest
    start: "2025-01-01"
    end:   "2025-01-31"
    startingBalance: 1000

  Candle data loaded
    totalCandles: 44640

════════════════════════════════════════════════════════
  πŸ“Š  BACKTEST SUMMARY  β€”  polymarket-momentum-mispricing-bot
════════════════════════════════════════════════════════
  Period:          2025-01-01 β†’ 2025-01-31
  Starting capital $1000.00
  ────────────────────────────────────────────────────
  Signals scanned: 4176
  Trades executed: 312
  Wins:            181   Losses: 131
  Win rate:        58.0%
  Avg P&L/trade:   $0.0623
  ────────────────────────────────────────────────────
  Net P&L:         $19.44
  Final balance:   $1019.44
  ROI:             +1.94%
  Max drawdown:    3.21%
════════════════════════════════════════════════════════

Execution Architecture

Two execution backends β€” selected by EXECUTION_MODE in .env:

CLOB mode (default β€” recommended)

Your private key signs EIP-712 orders directly on the Polymarket CTF Exchange contract (Polygon). No intermediary needed.

Bot β†’ EIP-712 sign (ethers v6) β†’ Polymarket CLOB API β†’ Order filled
                                  (clob.polymarket.com)
  • POST /auth/api-key β€” obtain session credentials (signed once, cached)
  • POST /order β€” submit FOK (Fill-or-Kill) buy or sell order
  • GET /balance-allowance β€” USDC balance

Simmer mode (no private key)

Simmer acts as an execution proxy and handles CLOB signing for you.

Bot β†’ Simmer API β†’ Polymarket CLOB β†’ Order filled
      (api.simmer.markets)
  • POST /api/sdk/markets/import β€” register a market
  • POST /api/sdk/trade β€” buy/sell an outcome
  • GET /api/sdk/portfolio β€” account balance

Safety Notes

  1. Always test with DRY_RUN=true first. Watch the logs for signal quality before enabling live trading.
  2. Start with a small POSITION_SIZE_USD (e.g., $5–$10).
  3. The bot does not guarantee profits. Polymarket fast markets are highly competitive.
  4. Keep MAX_DAILY_LOSS_USD set to a comfortable level you can afford to lose.
  5. The Simmer API is a third-party service β€” review their terms before use.

License

MIT

About

Polymarket trading bot, Binance momentum bot, prediction market arbitrage, BTC fast market bot, Polymarket trading bot, Binance momentum bot, prediction market arbitrage, BTC fast market bot, Polymarket trading bot, Binance momentum bot, prediction market arbitrage, BTC fast market bot, Polymarket trading bot, Binance momentum bot

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors