Trending-skills polymarket-copy-trading-bot
TypeScript bot that monitors a Polymarket wallet and mirrors BUY trades to your own account via the Polymarket CLOB API on Polygon.
git clone https://github.com/Aradotso/trending-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/Aradotso/trending-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/polymarket-copy-trading-bot" ~/.claude/skills/aradotso-trending-skills-polymarket-copy-trading-bot && rm -rf "$T"
skills/polymarket-copy-trading-bot/SKILL.mdPolymarket Copy Trading Bot
Skill by ara.so — Daily 2026 Skills collection.
A TypeScript bot that continuously monitors a target Polymarket wallet, detects trades in real time via REST polling and/or WebSocket, and mirrors BUY orders to your own account using the Polymarket CLOB SDK on Polygon mainnet.
What It Does
- Monitors a target wallet via Polymarket Data API (REST polling every ~2s) and optionally WebSocket
- Mirrors BUY trades only — SELL trades are detected but skipped by default
- Sizes copies using a configurable
(default 10%) with min/max capsPOSITION_MULTIPLIER - Submits orders as FOK, FAK, or LIMIT via the Polymarket CLOB client
- Enforces risk limits — per-session and per-market notional caps
- Supports three auth modes — EOA (
), Poly Proxy (SIG_TYPE=0
), Poly Polymorphic (SIG_TYPE=1
)SIG_TYPE=2
Installation
git clone https://github.com/Neron888/Polymarket-copy-trading-bot.git cd Polymarket-copy-trading-bot npm install
Requires Node.js v18+. The CLOB SDK requires ethers v5 (already pinned in
package.json).
Configuration
cp .env.example .env
Edit
.env:
# Required TARGET_WALLET=0xTargetWalletAddressToMonitor WALLET_PRIVATE_KEY=0xYourPrivateKey RPC_URL=https://your-quicknode-polygon-endpoint.quiknode.pro/your-key/ # Auth mode (0=EOA default, 1=Poly Proxy, 2=Poly Polymorphic) SIG_TYPE=0 # Required only for SIG_TYPE=1 or 2 PROXY_WALLET_ADDRESS= # Sizing POSITION_MULTIPLIER=0.1 # 10% of target's trade size MAX_TRADE_SIZE=100 # Max USDC per copied trade MIN_TRADE_SIZE=1 # Min USDC per copied trade # Order behavior ORDER_TYPE=FOK # FOK | FAK | LIMIT SLIPPAGE_TOLERANCE=0.02 # 2% # Risk caps (0 = disabled) MAX_SESSION_NOTIONAL=500 # Total USDC for entire session MAX_PER_MARKET_NOTIONAL=100 # Per market USDC cap # Monitoring USE_WEBSOCKET=true POLL_INTERVAL=2000 # ms between REST polls USE_USER_CHANNEL=false # true = user WS channel, false = market channel # Optional POLYMARKET_GEO_TOKEN= WS_ASSET_IDS= # comma-separated asset IDs for market WS WS_MARKET_IDS= # comma-separated condition IDs for user channel MIN_PRIORITY_FEE_GWEI=30 MIN_MAX_FEE_GWEI=60
Key Commands
# Start the bot (development mode with ts-node) npm start # Generate and persist API credentials to .polymarket-api-creds npm run generate-api-creds # Validate existing API credentials npm run test-api-creds # Compile TypeScript to dist/ npm run build # Run compiled production build npm run start:prod
Architecture Overview
index.ts └── TradeMonitor — REST polls Polymarket Data API for new target trades └── WebSocketMonitor — Optional low-latency WS subscription (market or user channel) └── TradeExecutor — Sizes trade, checks balance/allowance, submits CLOB order └── PositionTracker — In-memory positions updated on fills └── RiskManager — Session + per-market notional enforcement
Execution flow:
detect trade → BUY? → subscribe WS if needed → compute copy size → risk check → execute order (FOK/FAK/LIMIT) → record fill → update stats
Code Examples
Starting the bot programmatically
import { startBot } from './src/index'; // The bot reads all config from process.env / .env startBot();
TradeMonitor — polling pattern
import { TradeMonitor } from './src/TradeMonitor'; const monitor = new TradeMonitor({ targetWallet: process.env.TARGET_WALLET!, pollInterval: Number(process.env.POLL_INTERVAL ?? 2000), }); monitor.on('trade', (trade) => { console.log('New trade detected:', trade); // trade.side: 'BUY' | 'SELL' // trade.asset: token ID (outcome token address) // trade.size: USDC size // trade.price: fill price (0–1) }); monitor.start();
TradeExecutor — placing a copy order
import { TradeExecutor } from './src/TradeExecutor'; import { ClobClient } from '@polymarket/clob-client'; import { ethers } from 'ethers'; const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL); const signer = new ethers.Wallet(process.env.WALLET_PRIVATE_KEY!, provider); const clobClient = new ClobClient( 'https://clob.polymarket.com', 137, // Polygon chainId signer, apiCreds, // loaded from .polymarket-api-creds Number(process.env.SIG_TYPE ?? 0), process.env.PROXY_WALLET_ADDRESS || undefined, ); const executor = new TradeExecutor({ client: clobClient, positionMultiplier: Number(process.env.POSITION_MULTIPLIER ?? 0.1), maxTradeSize: Number(process.env.MAX_TRADE_SIZE ?? 100), minTradeSize: Number(process.env.MIN_TRADE_SIZE ?? 1), orderType: (process.env.ORDER_TYPE ?? 'FOK') as 'FOK' | 'FAK' | 'LIMIT', slippageTolerance: Number(process.env.SLIPPAGE_TOLERANCE ?? 0.02), }); // Copy a detected trade await executor.copyTrade({ side: 'BUY', tokenId: '0xAssetId...', originalSize: 12.5, // USDC from target's trade price: 0.62, });
RiskManager — checking before execution
import { RiskManager } from './src/RiskManager'; const riskManager = new RiskManager({ maxSessionNotional: Number(process.env.MAX_SESSION_NOTIONAL ?? 0), maxPerMarketNotional: Number(process.env.MAX_PER_MARKET_NOTIONAL ?? 0), }); const allowed = riskManager.checkTrade({ marketId: '0xConditionId...', notional: copySize, }); if (!allowed) { console.log('Trade blocked by risk limits'); }
WebSocket monitor — low-latency subscription
import { WebSocketMonitor } from './src/WebSocketMonitor'; const wsMonitor = new WebSocketMonitor({ useUserChannel: process.env.USE_USER_CHANNEL === 'true', assetIds: process.env.WS_ASSET_IDS?.split(',').filter(Boolean) ?? [], marketIds: process.env.WS_MARKET_IDS?.split(',').filter(Boolean) ?? [], }); wsMonitor.on('orderFilled', (fill) => { console.log('Fill received via WS:', fill); }); wsMonitor.connect();
Authentication Modes
EOA (default — SIG_TYPE=0
)
SIG_TYPE=0SIG_TYPE=0 WALLET_PRIVATE_KEY=0xYourKey # PROXY_WALLET_ADDRESS — leave empty
On first run, the bot auto-submits USDC.e/CTF approval transactions. Wallet needs POL for gas.
Poly Proxy (SIG_TYPE=1
)
SIG_TYPE=1SIG_TYPE=1 WALLET_PRIVATE_KEY=0xSignerKey PROXY_WALLET_ADDRESS=0xYourPolymarketProxyAddress
Poly Polymorphic (SIG_TYPE=2
)
SIG_TYPE=2SIG_TYPE=2 WALLET_PRIVATE_KEY=0xSignerKey PROXY_WALLET_ADDRESS=0xYourPolymorphicSafeAddress
Generating API Credentials
npm run generate-api-creds
This derives API keys from your wallet signature and writes them to
.polymarket-api-creds. Run once before npm start if you want to pre-generate credentials. The bot also auto-generates them on first start in EOA mode.
Validate credentials:
npm run test-api-creds
Common Patterns
Conservative testing setup
POSITION_MULTIPLIER=0.05 MAX_TRADE_SIZE=5 MIN_TRADE_SIZE=1 MAX_SESSION_NOTIONAL=20 ORDER_TYPE=FOK USE_WEBSOCKET=false
WebSocket-only market channel (lower latency)
USE_WEBSOCKET=true USE_USER_CHANNEL=false WS_ASSET_IDS=0xTokenId1,0xTokenId2
LIMIT orders with slippage buffer
ORDER_TYPE=LIMIT SLIPPAGE_TOLERANCE=0.03
Troubleshooting
Bot starts but no trades detected
- Verify
is a valid Polymarket wallet with recent activityTARGET_WALLET - Check
— default 2000ms; lower means more API callsPOLL_INTERVAL - Confirm the target wallet trades on Polymarket (not just holds positions)
version conflictsethers
- The project pins ethers v5. Do not upgrade to v6 — the CLOB SDK requires v5
- Run
to check for duplicate versionsnpm ls ethers
Approval transactions failing
- Ensure the wallet has sufficient POL (MATIC) for gas on Polygon mainnet
- Try increasing
andMIN_PRIORITY_FEE_GWEI
if transactions stallMIN_MAX_FEE_GWEI
failsgenerate-api-creds
- Confirm
is correct and 0x-prefixedWALLET_PRIVATE_KEY - For
, ensureSIG_TYPE=1/2
matches your Polymarket accountPROXY_WALLET_ADDRESS
Orders rejected by CLOB
- FOK orders fail if insufficient liquidity — try
orORDER_TYPE=FAKLIMIT - Check
isn't too tight for illiquid marketsSLIPPAGE_TOLERANCE
Session notional cap hit immediately
resets per process run; restart the bot to resetMAX_SESSION_NOTIONAL- Set
to disable the cap entirelyMAX_SESSION_NOTIONAL=0
Wallet Requirements
- USDC.e on Polygon mainnet (collateral for trades)
- POL (formerly MATIC) for gas fees
- Approve USDC.e for Polymarket CTF Exchange and CLOB contracts (bot does this automatically on first EOA run)