
Polymarket Arbitrage Trading Bot
- 945 installs
- 66 repo stars
- Updated July 9, 2026
- aradotso/trending-skills
polymarket-arbitrage-trading-bot is an agent skill that configures automated dump-and-hedge arbitrage against Polymarket 15-minute crypto Up/Down markets for developers building prediction-market trading automation.
About
polymarket-arbitrage-trading-bot is an ara.so skill from the Daily 2026 Skills collection that walks developers through building an automated dump-and-hedge arbitrage bot on Polymarket's 15-minute crypto Up/Down prediction markets. The skill covers CLOB API integration, hedge logic, and simulation mode so orders can be tested before live execution. Supported assets include BTC, ETH, SOL, and XRP across short-horizon Up/Down contracts. Trigger phrases span bot setup, dump-hedge strategy configuration, TypeScript CLOB arbitrage, and simulation runs. Developers reach for this skill when they need a structured agent playbook for Polymarket automation rather than hand-rolling order routing, position hedging, and market-selection logic from scratch. It targets engineers comfortable with trading APIs who want repeatable arbitrage workflows inside Claude Code or Cursor.
- Monitors BTC, ETH, SOL, and XRP 15-minute markets in real time
- Executes automated dump-and-hedge strategy when combined leg cost falls below threshold
- Built with official @polymarket/clob-client in TypeScript
- Supports live trading, simulation mode, and period rollover
- Includes market discovery, price polling, and snapshot capabilities
Polymarket Arbitrage Trading Bot by the numbers
- 945 all-time installs (skills.sh)
- +11 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #150 of 1,136 Finance & Trading skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aradotso/trending-skills --skill polymarket-arbitrage-trading-botAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 945 |
|---|---|
| repo stars | ★ 66 |
| Security audit | 0 / 3 scanners passed |
| Last updated | July 9, 2026 |
| Repository | aradotso/trending-skills ↗ |
How do you automate Polymarket dump-hedge arbitrage?
Automatically execute dump-and-hedge arbitrage on Polymarket's 15-minute Up/Down crypto prediction markets.
Who is it for?
Developers building TypeScript trading automation against Polymarket 15-minute crypto prediction markets with hedged arbitrage logic.
Skip if: Teams seeking general-purpose exchange bots, long-horizon investing strategies, or markets outside Polymarket's 15-minute Up/Down contracts.
When should I use this skill?
The user asks to set up, configure, simulate, or run a Polymarket arbitrage or dump-hedge trading bot.
What you get
Configured Polymarket CLOB trading bot, hedge strategy parameters, and simulation-mode execution scripts.
- arbitrage bot configuration
- hedge strategy scripts
- simulation-mode trading workflow
By the numbers
- Supports 4 crypto assets: BTC, ETH, SOL, and XRP
- Targets Polymarket 15-minute Up/Down prediction markets
Files
Polymarket Arbitrage Trading Bot
Skill by 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
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 buildRequirements: Node.js 16+, USDC on Polygon (for live trading), a Polymarket-compatible wallet.
---
Project Structure
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)
# 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
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)
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
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
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
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
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)
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
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
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)
# Via npm script
npm run sim
# Or directly with flag
node dist/main.js --simulation
# Monitor output
tail -f history.tomlProduction (Live Trading)
# Ensure .env has correct PRIVATE_KEY, PROXY_WALLET_ADDRESS, SIGNATURE_TYPE
npm run prod
# Or:
PRODUCTION=true node dist/main.js --productionSingle Asset, Custom Thresholds
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
// 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
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 first
Related skills
How it compares
Use this skill for Polymarket-specific short-horizon arbitrage; pick general trading-bot skills for multi-exchange or non-prediction-market strategies.
FAQ
Which crypto assets does the Polymarket bot skill support?
polymarket-arbitrage-trading-bot targets Polymarket 15-minute Up/Down prediction markets for BTC, ETH, SOL, and XRP. The skill focuses on short-horizon contracts rather than general spot or perpetual markets.
Does polymarket-arbitrage-trading-bot support dry runs?
polymarket-arbitrage-trading-bot includes simulation mode so developers can test dump-and-hedge logic and CLOB order flow before sending live trades on Polymarket prediction markets.
Is Polymarket Arbitrage Trading Bot safe to install?
skills.sh reports 0 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.