polymarket-arbitrage-trading-bot
Automated dump-and-hedge arbitrage trading bot for Polymarket's 15-minute crypto Up/Down markets, supporting BTC, ETH, SOL, and XRP.
Best use case
polymarket-arbitrage-trading-bot is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Automated dump-and-hedge arbitrage trading bot for Polymarket's 15-minute crypto Up/Down markets, supporting BTC, ETH, SOL, and XRP.
Teams using polymarket-arbitrage-trading-bot should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/polymarket-arbitrage-trading-bot/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How polymarket-arbitrage-trading-bot Compares
| Feature / Agent | polymarket-arbitrage-trading-bot | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Automated dump-and-hedge arbitrage trading bot for Polymarket's 15-minute crypto Up/Down markets, supporting BTC, ETH, SOL, and XRP.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
AI Agent for Product Research
Browse AI agent skills for product research, competitive analysis, customer discovery, and structured product decision support.
AI Agent for SaaS Idea Validation
Use AI agent skills for SaaS idea validation, market research, customer discovery, competitor analysis, and documenting startup hypotheses.
AI Agents for Marketing
Discover AI agents for marketing workflows, from SEO and content production to campaign research, outreach, and analytics.
SKILL.md Source
# Polymarket Arbitrage Trading Bot
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.
Automated dump-and-hedge arbitrage bot for Polymarket's 15-minute crypto Up/Down prediction markets. Written in TypeScript using the official `@polymarket/clob-client`. Watches BTC, ETH, SOL, and XRP markets for sharp price drops on one leg, then buys both legs when combined cost falls below a target threshold to lock in a structural edge before resolution.
---
## Installation
```bash
git clone https://github.com/apechurch/polymarket-arbitrage-trading-bot.git
cd polymarket-arbitrage-trading-bot
npm install
cp .env.example .env
# Configure .env — see Configuration section
npm run build
```
**Requirements:** Node.js 16+, USDC on Polygon (for live trading), a Polymarket-compatible wallet.
---
## Project Structure
```text
src/
main.ts # Entry point: market discovery, monitors, period rollover
monitor.ts # Price polling & snapshots
dumpHedgeTrader.ts # Core strategy: dump → hedge → stop-loss → settlement
api.ts # Gamma API, CLOB API, order placement, redemption
config.ts # Environment variable loading
models.ts # Shared TypeScript types
logger.ts # History file (history.toml) + stderr logging
```
---
## Key Commands
| Command | Purpose |
|---------|---------|
| `npm run dev` | Run via `ts-node` (development, no build needed) |
| `npm run build` | Compile TypeScript to `dist/` |
| `npm run typecheck` | Type-check without emitting output |
| `npm run clean` | Remove `dist/` directory |
| `npm run sim` | **Simulation mode** — logs trades, no real orders |
| `npm run prod` | **Production mode** — places real CLOB orders |
| `npm start` | Run compiled output (defaults to simulation unless `--production` passed) |
---
## Configuration (`.env`)
```bash
# Wallet / Auth
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE
PROXY_WALLET_ADDRESS=0xYOUR_PROXY_WALLET
SIGNATURE_TYPE=2 # 0=EOA, 1=Proxy, 2=Gnosis Safe
# Markets to trade (comma-separated)
MARKETS=btc,eth,sol,xrp
# Polling
CHECK_INTERVAL_MS=1000
# Strategy thresholds
DUMP_HEDGE_SHARES=10 # Shares per leg
DUMP_HEDGE_SUM_TARGET=0.95 # Max combined price for both legs
DUMP_HEDGE_MOVE_THRESHOLD=0.15 # Min fractional drop to trigger (15%)
DUMP_HEDGE_WINDOW_MINUTES=5 # Only detect dumps in first N minutes of round
DUMP_HEDGE_STOP_LOSS_MAX_WAIT_MINUTES=8 # Force stop-loss hedge after N minutes
# Mode flag (use --production CLI flag for live trading)
PRODUCTION=false
# Optional API overrides
GAMMA_API_URL=https://gamma-api.polymarket.com
CLOB_API_URL=https://clob.polymarket.com
API_KEY=
API_SECRET=
API_PASSPHRASE=
```
---
## Strategy Overview
```text
New 15m round starts
│
▼
Watch first DUMP_HEDGE_WINDOW_MINUTES minutes
│
├── Up or Down leg drops ≥ DUMP_HEDGE_MOVE_THRESHOLD?
│ │
│ ▼
│ Buy dumped leg (Leg 1)
│ │
│ ├── Opposite ask cheap enough?
│ │ (leg1_entry + opposite_ask ≤ DUMP_HEDGE_SUM_TARGET)
│ │ │
│ │ ▼
│ │ Buy hedge leg (Leg 2) → locked-in edge
│ │
│ └── Timeout (DUMP_HEDGE_STOP_LOSS_MAX_WAIT_MINUTES)?
│ │
│ ▼
│ Execute stop-loss hedge
│
└── Round ends → settle winners, redeem on-chain (production)
```
---
## Code Examples
### Loading Config (`src/config.ts` pattern)
```typescript
import * as dotenv from 'dotenv';
dotenv.config();
export const config = {
privateKey: process.env.PRIVATE_KEY!,
proxyWalletAddress: process.env.PROXY_WALLET_ADDRESS ?? '',
signatureType: parseInt(process.env.SIGNATURE_TYPE ?? '2', 10),
markets: (process.env.MARKETS ?? 'btc').split(',').map(m => m.trim()),
checkIntervalMs: parseInt(process.env.CHECK_INTERVAL_MS ?? '1000', 10),
dumpHedgeShares: parseFloat(process.env.DUMP_HEDGE_SHARES ?? '10'),
dumpHedgeSumTarget: parseFloat(process.env.DUMP_HEDGE_SUM_TARGET ?? '0.95'),
dumpHedgeMoveThreshold: parseFloat(process.env.DUMP_HEDGE_MOVE_THRESHOLD ?? '0.15'),
dumpHedgeWindowMinutes: parseInt(process.env.DUMP_HEDGE_WINDOW_MINUTES ?? '5', 10),
dumpHedgeStopLossMaxWaitMinutes: parseInt(
process.env.DUMP_HEDGE_STOP_LOSS_MAX_WAIT_MINUTES ?? '8', 10
),
production: process.env.PRODUCTION === 'true',
};
```
### Initializing the CLOB Client
```typescript
import { ClobClient } from '@polymarket/clob-client';
import { ethers } from 'ethers';
import { config } from './config';
function createClobClient(): ClobClient {
const wallet = new ethers.Wallet(config.privateKey);
return new ClobClient(
config.clobApiUrl, // e.g. 'https://clob.polymarket.com'
137, // Polygon chain ID
wallet,
undefined, // credentials (set after key derivation if needed)
config.signatureType,
config.proxyWalletAddress
);
}
```
### Discovering the Active 15-Minute Market
```typescript
import axios from 'axios';
interface GammaMarket {
conditionId: string;
question: string;
endDateIso: string;
active: boolean;
tokens: Array<{ outcome: string; token_id: string }>;
}
async function findActive15mMarket(asset: string): Promise<GammaMarket | null> {
const tag = `${asset.toUpperCase()}-15m`;
const resp = await axios.get(`${config.gammaApiUrl}/markets`, {
params: { tag, active: true, limit: 5 }
});
const markets: GammaMarket[] = resp.data;
// Return the earliest-closing active market
return markets.sort(
(a, b) => new Date(a.endDateIso).getTime() - new Date(b.endDateIso).getTime()
)[0] ?? null;
}
```
### Fetching Best Ask Price from CLOB
```typescript
async function getBestAsk(tokenId: string): Promise<number | null> {
try {
const resp = await axios.get(`${config.clobApiUrl}/book`, {
params: { token_id: tokenId }
});
const asks: Array<{ price: string; size: string }> = resp.data.asks ?? [];
if (asks.length === 0) return null;
// Best ask = lowest price
return Math.min(...asks.map(a => parseFloat(a.price)));
} catch {
return null;
}
}
```
### Dump Detection Logic
```typescript
interface PriceSnapshot {
timestamp: number;
ask: number;
}
function detectDump(
history: PriceSnapshot[],
currentAsk: number,
threshold: number,
windowMs: number
): boolean {
const cutoff = Date.now() - windowMs;
const recent = history.filter(s => s.timestamp >= cutoff);
if (recent.length === 0) return false;
const highestRecentAsk = Math.max(...recent.map(s => s.ask));
const drop = (highestRecentAsk - currentAsk) / highestRecentAsk;
return drop >= threshold;
}
// Usage:
const windowMs = config.dumpHedgeWindowMinutes * 60 * 1000;
const isDump = detectDump(
priceHistory,
currentAsk,
config.dumpHedgeMoveThreshold,
windowMs
);
```
### Placing a Market Buy Order (Production)
```typescript
import { ClobClient, OrderType, Side } from '@polymarket/clob-client';
async function buyShares(
client: ClobClient,
tokenId: string,
price: number,
shares: number,
simulate: boolean
): Promise<string | null> {
if (simulate) {
console.error(`[SIM] BUY ${shares} shares @ ${price} token=${tokenId}`);
return 'sim-order-id';
}
const order = await client.createOrder({
tokenID: tokenId,
price,
size: shares,
side: Side.BUY,
orderType: OrderType.FOK, // Fill-or-Kill for immediate execution
});
const resp = await client.postOrder(order);
return resp.orderID ?? null;
}
```
### Core Dump-Hedge Cycle
```typescript
interface LegState {
filled: boolean;
tokenId: string;
entryPrice: number | null;
orderId: string | null;
}
async function runDumpHedgeCycle(
client: ClobClient,
upTokenId: string,
downTokenId: string,
simulate: boolean
): Promise<void> {
const leg1: LegState = { filled: false, tokenId: '', entryPrice: null, orderId: null };
const leg2: LegState = { filled: false, tokenId: '', entryPrice: null, orderId: null };
const startTime = Date.now();
const windowMs = config.dumpHedgeWindowMinutes * 60 * 1000;
const stopLossMs = config.dumpHedgeStopLossMaxWaitMinutes * 60 * 1000;
const priceHistory: Record<string, PriceSnapshot[]> = {
[upTokenId]: [], [downTokenId]: []
};
const interval = setInterval(async () => {
const elapsed = Date.now() - startTime;
const upAsk = await getBestAsk(upTokenId);
const downAsk = await getBestAsk(downTokenId);
if (upAsk == null || downAsk == null) return;
// Record history
const now = Date.now();
priceHistory[upTokenId].push({ timestamp: now, ask: upAsk });
priceHistory[downTokenId].push({ timestamp: now, ask: downAsk });
// === LEG 1: Detect dump, buy dumped leg ===
if (!leg1.filled && elapsed <= windowMs) {
const upDumped = detectDump(
priceHistory[upTokenId], upAsk, config.dumpHedgeMoveThreshold, windowMs
);
const downDumped = detectDump(
priceHistory[downTokenId], downAsk, config.dumpHedgeMoveThreshold, windowMs
);
if (upDumped || downDumped) {
const dumpedToken = upDumped ? upTokenId : downTokenId;
const dumpedAsk = upDumped ? upAsk : downAsk;
leg1.tokenId = dumpedToken;
leg1.entryPrice = dumpedAsk;
leg1.orderId = await buyShares(
client, dumpedToken, dumpedAsk, config.dumpHedgeShares, simulate
);
leg1.filled = true;
console.error(`[LEG1] Bought dumped leg @ ${dumpedAsk}`);
}
}
// === LEG 2: Hedge when sum is favorable ===
if (leg1.filled && !leg2.filled) {
const hedgeToken = leg1.tokenId === upTokenId ? downTokenId : upTokenId;
const hedgeAsk = leg1.tokenId === upTokenId ? downAsk : upAsk;
const combinedCost = leg1.entryPrice! + hedgeAsk;
const shouldHedge =
combinedCost <= config.dumpHedgeSumTarget ||
elapsed >= stopLossMs; // Stop-loss: force hedge on timeout
if (shouldHedge) {
const label = combinedCost <= config.dumpHedgeSumTarget ? 'HEDGE' : 'STOP-LOSS';
leg2.tokenId = hedgeToken;
leg2.entryPrice = hedgeAsk;
leg2.orderId = await buyShares(
client, hedgeToken, hedgeAsk, config.dumpHedgeShares, simulate
);
leg2.filled = true;
console.error(`[LEG2:${label}] Bought hedge @ ${hedgeAsk}, combined=${combinedCost}`);
clearInterval(interval);
}
}
}, config.checkIntervalMs);
}
```
### Settlement and Redemption
```typescript
async function settleRound(
client: ClobClient,
conditionId: string,
winningTokenId: string,
simulate: boolean
): Promise<void> {
if (simulate) {
console.error(`[SIM] Would redeem winning token ${winningTokenId}`);
return;
}
// Redeem via CLOB client (CTF redemption on Polygon)
await client.redeemPositions({
conditionId,
amounts: [{ tokenId: winningTokenId, amount: config.dumpHedgeShares }]
});
console.error(`[SETTLE] Redeemed ${config.dumpHedgeShares} shares for ${winningTokenId}`);
}
```
---
## Running Modes
### Simulation (Recommended First)
```bash
# Via npm script
npm run sim
# Or directly with flag
node dist/main.js --simulation
# Monitor output
tail -f history.toml
```
### Production (Live Trading)
```bash
# Ensure .env has correct PRIVATE_KEY, PROXY_WALLET_ADDRESS, SIGNATURE_TYPE
npm run prod
# Or:
PRODUCTION=true node dist/main.js --production
```
### Single Asset, Custom Thresholds
```bash
MARKETS=btc \
DUMP_HEDGE_MOVE_THRESHOLD=0.12 \
DUMP_HEDGE_SUM_TARGET=0.93 \
DUMP_HEDGE_SHARES=5 \
npm run prod
```
---
## Common Patterns
### Multi-Asset Parallel Monitoring
```typescript
// main.ts pattern: spin up one monitor per asset
import { config } from './config';
async function main() {
const isProduction = process.argv.includes('--production') || config.production;
await Promise.all(
config.markets.map(asset =>
runAssetMonitor(asset, isProduction)
)
);
}
async function runAssetMonitor(asset: string, production: boolean) {
while (true) {
const market = await findActive15mMarket(asset);
if (!market) {
console.error(`[${asset}] No active market, retrying in 30s`);
await sleep(30_000);
continue;
}
const [upToken, downToken] = market.tokens;
const client = createClobClient();
await runDumpHedgeCycle(client, upToken.token_id, downToken.token_id, !production);
// Wait for round end, then loop for next round
const roundEnd = new Date(market.endDateIso).getTime();
await sleep(Math.max(0, roundEnd - Date.now() + 5_000));
}
}
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
main().catch(console.error);
```
### Logging to history.toml
```typescript
import * as fs from 'fs';
interface TradeRecord {
asset: string;
roundEnd: string;
leg1Price: number;
leg2Price: number;
combined: number;
target: number;
mode: 'hedge' | 'stop-loss';
timestamp: string;
}
function appendHistory(record: TradeRecord): void {
const entry = `
[[trade]]
asset = "${record.asset}"
round_end = "${record.roundEnd}"
leg1_price = ${record.leg1Price}
leg2_price = ${record.leg2Price}
combined = ${record.combined}
target = ${record.target}
mode = "${record.mode}"
timestamp = "${record.timestamp}"
`;
fs.appendFileSync('history.toml', entry, 'utf8');
}
```
---
## Troubleshooting
| Issue | Cause | Fix |
|-------|-------|-----|
| `Failed to fetch market/orderbook` | API/network error | Temporary; check `GAMMA_API_URL` / `CLOB_API_URL` connectivity, retries are built in |
| Orders fail in production | Wrong auth config | Verify `PRIVATE_KEY`, `SIGNATURE_TYPE`, and `PROXY_WALLET_ADDRESS` match your Polymarket account |
| No market found for asset | Round gap or unsupported asset | Only use `btc`, `eth`, `sol`, `xrp`; wait for next 15m round to start |
| Bot never triggers leg 1 | Threshold too high or quiet market | Lower `DUMP_HEDGE_MOVE_THRESHOLD` or increase `DUMP_HEDGE_WINDOW_MINUTES` |
| Combined cost always above target | Market conditions | Lower `DUMP_HEDGE_SUM_TARGET` or adjust `DUMP_HEDGE_STOP_LOSS_MAX_WAIT_MINUTES` |
| `Cannot find module` errors | Missing build step | Run `npm run build` before `npm start` / `npm run prod` |
| Simulation not placing orders | Expected behavior | Simulation mode logs only; switch to `--production` for real orders |
---
## Safety Checklist
1. **Always simulate first** — run `npm run sim` across multiple rounds and inspect `history.toml`
2. **Start small** — use low `DUMP_HEDGE_SHARES` (e.g. `1`) in first production runs
3. **Secure credentials** — never commit `.env` to version control; add it to `.gitignore`
4. **Monitor stop-loss behavior** — tune `DUMP_HEDGE_STOP_LOSS_MAX_WAIT_MINUTES` carefully; forced hedges at bad prices reduce edge
5. **Polygon USDC** — ensure sufficient USDC balance on Polygon before running production
6. **Round timing** — the bot auto-rolls to the next round; verify rollover logs look correct in simulation firstRelated Skills
universal-trading
Execute cross-chain token trading on EVM and Solana with Particle Network Universal Account SDK. Use when users ask to set up universal-account-example, buy or sell tokens, run convert/swap flows, transfer assets, call custom transactions, query balances/history, or monitor transaction status via WebSocket.
polymarket-sports-edge
Find odds divergence between sportsbook consensus and Polymarket sports markets, then trade the gap.
polymarket-openclaw-trader
Reusable Polymarket + OpenClaw trading operations skill for any workspace. Use when the user needs to set up, run, tune, monitor, and deploy an automated Polymarket trading project (paper/live), including env configuration, risk controls, reporting, and dashboard operations.
paper-trading-plan
Generates structured paper trading plans with entry, stop loss, take profit, position size, and failure conditions for SPX, indices, and US equity options.
Trading
Trading analysis and education. Technical analysis, chart patterns, risk management, and position sizing for stocks, forex, and crypto.
trading-coach
🏆 AI交易复盘教练 — 把你的券商CSV变成可执行的改进洞察! 自动FIFO配对持仓,8维度质量评分(入场/出场/趋势/风险...),10维度AI洞察。 支持富途(中/英)、老虎、中信、华泰等主流券商。 触发条件: 用户提供交易CSV、要求分析交易表现、评估交易质量、生成复盘报告、 计算盈亏统计、识别交易模式问题、"帮我复盘"、"分析我的交易"。
convertible-bond-trading-bot
可转债自动交易机器人,止跌企稳智能选股,上涨趋势自动高抛低吸,自带回测功能,年化收益30%+。每次调用0.01USDT。
trading-analyzer
Multi-source trading analyzer (`/drunk-trading-analyzer`) combining crypto data (TradingView), stock data (Alpha Vantage), and market intelligence (Yahoo Finance) into unified analysis reports with price trends, technical indicators, and sentiment analysis.
trading-signal
Subscribe and retrieve on-chain Smart Money signals. Monitor trading activities of smart money addresses, including buy/sell signals, trigger price, current price, max gain, and exit rate. Use this skill when users are looking for investment opportunities — smart money signals can serve as valuable references for potential trades.
DEX Agent — Direct DeFi Trading Skill
**Zero-fee DeFi trading for OpenClaw agents. Bankr alternative.**
defi-trading-engine
DeFi Trading Engine - Autonomous DeFi trading bot with self-improving review system for OpenClaw agents. Use when setting up DeFi trading, crypto trading bot, automated trading, Base chain trading, Bankr integration, trading engine, self-improving bot, or trading strategy execution.
polymarket-trade
Trade on Polymarket prediction markets on Polygon. Supports browsing markets, checking wallet/CLOB balance, and buying or selling YES/NO shares with safety gates. Wallet: WDK vault (~/.aurehub/.wdk_vault). Config: ~/.aurehub/polymarket.yaml. Triggers: buy YES, buy NO, sell shares, browse markets, check Polymarket balance, Polymarket trade, prediction market, what are the odds, redeem winnings, claim resolved positions.