Fully automated arbitrage bot that captures guaranteed profit from Polymarket's 15-minute Up/Down prediction markets. It buys both YES and NO sides at a combined cost below $1.00, then collects $1.00 when the market resolves — pocketing the spread every 15 minutes.
Every 15 minutes, Polymarket opens binary markets like "Will BTC go Up or Down?". Each market has two tokens — YES and NO — that each resolve to exactly $1.00 or $0.00.
The bot exploits a simple math fact: if you buy YES at $0.47 and NO at $0.47, you spend $0.94 total. One side always wins and pays $1.00. That's $0.06 profit per share, risk-free.
Buy 5 YES shares @ $0.47 = $2.35
Buy 5 NO shares @ $0.47 = $2.35
Total = $4.70
Market resolves → winning side pays $1.00 × 5 = $5.00
Profit = $5.00 − $4.70 = $0.30 per cycle (6.4% return in 15 min)
The bot runs 24/7, executing this strategy every 15 minutes across multiple markets (BTC, ETH, SOL, XRP).
This version of the bot is configured for BTC, ETH, and SOL 15-minute Up/Down markets on Polymarket.
- Guaranteed Profit — Both sides of a binary market are purchased; one always wins
- Sub-second Execution — 50ms polling loop with fire-and-forget order placement
- Smart Entry Timing — Three independent buy triggers (reversal, depth discount, time-based)
- Adaptive Polling — Speeds up to 100ms when opportunities are detected, slows to 2s when idle
- Dynamic Thresholds — Automatically adjusts second-side entry based on first-side fill price
- State Persistence — Resumes mid-hedge after restarts without losing position tracking
- Risk Guards — Max cost cap (sumAvg < $0.99), minimum balance check, stale order cleanup
- Full Logging — Every trade, price tick, and decision logged to daily rotating files
- Node.js 18+ with
ts-node - A Polygon wallet funded with USDC
- Polymarket API credentials (auto-generated on first run)
git clone <repository-url>
cd polymarket-trading-bot
npm installCopy the example environment file and set your private key:
cp .env.example .envPRIVATE_KEY=your_polygon_wallet_private_key
TRADE_MARKETS=btc,eth,sol
TRADE_THRESHOLD=0.47
TRADE_SHARES=5
MAX_BUYS_PER_SIDE=1npm startThe bot will:
- Generate API credentials (first run only)
- Approve USDC allowances on Polymarket contracts
- Wait until your wallet has at least $1 USDC available
- Begin the arbitrage loop
| Variable | Default | Description |
|---|---|---|
PRIVATE_KEY |
required | Polygon wallet private key |
TRADE_MARKETS |
btc,eth,sol |
Markets to trade (comma-separated: btc,eth,sol) |
TRADE_THRESHOLD |
0.47 |
Entry price — buy when a token drops below this |
TRADE_SHARES |
5 |
Number of shares per buy |
MAX_BUYS_PER_SIDE |
1 |
Maximum buys per side per 15m cycle |
TRADE_MAX_SUM_AVG |
0.99 |
Max combined avg price (above this = no profit) |
TRADE_TICK_SIZE |
0.01 |
Price precision for orders |
| Variable | Default | Description |
|---|---|---|
TRADE_POLL_MS |
50 |
Main loop interval (milliseconds) |
TRADE_MIN_POLL_MS |
100 |
Fastest adaptive poll rate |
TRADE_MAX_POLL_MS |
2000 |
Slowest adaptive poll rate |
TRADE_FIRE_AND_FORGET |
true |
Place orders without waiting for confirmation |
TRADE_PRICE_BUFFER |
0.05 |
Cents above midpoint to ensure fill |
TRADE_USE_FAK |
true |
Fill-and-Kill orders for instant execution |
| Variable | Default | Description |
|---|---|---|
REVERSAL_DELTA |
0.02 |
Price bounce from bottom to trigger buy |
TRADE_DEPTH_BUY_DISCOUNT_PERCENT |
0.02 |
Buy if price drops this % below tracked low |
TRADE_SECOND_SIDE_BUFFER |
0.003 |
Buffer for opposite side entry |
TRADE_DYNAMIC_THRESHOLD_BOOST |
0.04 |
Extra cents added to second-side threshold |
| Variable | Default | Description |
|---|---|---|
TRADE_WAIT_FOR_NEXT_MARKET_START |
false |
Wait for next 15m boundary before starting |
TRADE_ORDER_CHECK_DELAY_MS |
100 |
Delay before first order status check |
TRADE_ORDER_RETRY_DELAY_MS |
300 |
Delay between order status retries |
TRADE_ORDER_MAX_ATTEMPTS |
2 |
Max retries for order confirmation |
:00 ──── Market opens ─────────────────────── :15
│ │
│ Poll midpoint prices every 50ms │
│ Wait for YES or NO to drop below $0.47 │
│ │
│ Token drops → start tracking lowest price │
│ │
│ Trigger fires → BUY first side │
│ Switch to opposite side │
│ Trigger fires → BUY second side │
│ │
│ ✓ Hedge complete — wait for resolution │
│ │
:15 ── Market resolves → $1.00 payout ────────
The bot uses three independent triggers. Whichever fires first executes the buy:
1. Reversal Detection
Price dropped to a local minimum, then bounced back up by REVERSAL_DELTA ($0.02). This confirms the bottom and buys on the way back up.
2. Depth Discount
Price fell more than DEPTH_BUY_DISCOUNT_PERCENT (2%) below the tracked low. This catches fast crashes where waiting for a reversal would miss the opportunity.
3. Time Threshold (second side only) After buying one side, if the opposite side stays below its dynamic threshold for 200ms, buy immediately. Prevents missing the second side during slow price movement.
After the first buy, the bot calculates a dynamic threshold for the opposite side:
secondSideThreshold = (1 - firstBuyPrice) + dynamicThresholdBoost
Example: Bought YES at $0.47
→ Buy NO when price ≤ 1 - 0.47 + 0.04 = $0.57
This ensures the combined cost stays well below $1.00.
Before every buy, the bot checks:
avgPriceYES + avgPriceNO < TRADE_MAX_SUM_AVG ($0.99)
If the combined average would exceed $0.99, the buy is skipped — there's no profit margin left.
├── src/
│ ├── index.ts # Entry point — startup sequence
│ ├── config/index.ts # Environment config loader
│ ├── order-builder/
│ │ └── copytrade.ts # Core arbitrage engine (CopytradeArbBot)
│ ├── providers/
│ │ └── clobclient.ts # Polymarket CLOB API client
│ ├── security/
│ │ ├── allowance.ts # USDC approval management
│ │ └── createCredential.ts
│ ├── utils/
│ │ ├── balance.ts # Wallet balance polling
│ │ ├── holdings.ts # Token position tracking
│ │ ├── logger.ts # Colored console logger
│ │ └── console-file.ts # File logging (daily rotation)
│ └── data/
│ ├── copytrade-state.json # Persistent hedge state
│ └── token-holding.json # Token position database
├── .env
├── package.json
└── tsconfig.json
The bot logs every action to both console and daily log files in logs/:
[INFO] Starting the bot...
[INFO] Credentials ready
[INFO] Approving USDC allowances to Polymarket contracts...
[OK] Wallet is funded
[INFO] btc | YES=$0.52 NO=$0.48 — tracking NO (below $0.47)
[INFO] btc | NO dropped to $0.44 (new low)
[INFO] btc | REVERSAL triggered: NO bounced $0.02 from low → BUY
[OK] btc | Bought 5 NO @ $0.46 — switching to YES side
[INFO] btc | YES=$0.50 — below dynamic threshold $0.58
[OK] btc | Bought 5 YES @ $0.50 — HEDGE COMPLETE
[INFO] btc | Cost: $0.96/share — Guaranteed profit: $0.04 × 5 = $0.20
Sample arbitrage outcome from a live trading cycle:
| Command | Description |
|---|---|
npm start |
Start the arbitrage bot |
npm run redeem |
Manually redeem a resolved market |
npm run redeem:holdings |
Auto-redeem all resolved positions |
npm run balance:log |
Log current wallet balances |
Want a more profitable, private arbitrage trading bot? My private projects deliver higher returns with advanced strategies. Reach out for details.
Disclaimer: Trading prediction markets carries risk. Past performance does not guarantee future results. Always trade with funds you can afford to lose.

