TypeScript Polymarket Arbitrage Bot
Arbitrage on Polymarket is not one strategy - it is a family of edge types with different risk profiles, speed requirements, and capital efficiency. I built a TypeScript Polymarket arbitrage bot that implements multiple strategies behind a single execution engine, with dry-run, scan-only, and live modes.
Live overview: casatrick.github.io/polymarket-arbitrage-bot
Strategy comparison
| Strategy | Edge type | Risk | Speed needed |
|---|---|---|---|
| Intra-market | YES + NO < $1.00 | Very low | Medium |
| Multi-outcome / bundle | Outcomes sum < 100% | Low | Medium |
| Latency / temporal | Spot vs implied lag | Medium | Very high |
| Correlation / logical | Related market mismatch | Medium | High |
| Tail-end / resolution | Near-certain outcomes | Very low | Low |
| Market making | Bid-ask spread | Low-medium | High |
The bot routes each market through enabled strategies in src/strategies/,
then executes via a shared CLOB trader module.
Project structure
src/
├── bot.ts
├── strategies/
│ ├── intraMarket.ts
│ ├── latency.ts
│ ├── correlation.ts
│ └── tailEnd.ts
├── arbEngine.ts # strategy selector
├── trader.ts # CLOB execution
├── market.ts # market data + WebSocket
└── config.ts
Adding a new strategy means one file + a config flag - the engine handles discovery, sizing, and logging consistently.
Intra-market: the core loop
if (yesPrice + noPrice < 1.00 - CONFIG.minEdge) {
const edge = 1.00 - (yesPrice + noPrice);
const size = calculatePositionSize(edge, market.liquidity);
await executeArbitrage(market.id, size, yesPrice, noPrice);
}Default minEdge: 2%. Position sizing supports Kelly criterion or fixed
fraction modes via config.ts.
Latency arbitrage on crypto markets
For 5m/15m BTC and ETH up/down markets, Polymarket prices can lag spot feeds. The latency strategy compares implied probability against external price data and trades the convergence gap:
const impliedProb = getImpliedProbability(market);
const diff = Math.abs(impliedProb - externalPrice);
if (diff > CONFIG.minLatencyEdge) {
await trader.placeOrder(market.id, determineSide(externalPrice), size);
}This is the highest-frequency strategy - WebSocket latency and server placement matter. For production, host near Polymarket CLOB infrastructure (Dublin/Amsterdam region on a non-geoblocked VPS).
Tail-end / resolution arbitrage
When a market resolves within 24 hours and the leading outcome trades above 0.96, buying certainty captures small returns with minimal hold time:
if (market.timeToResolution < 24 * 60 * 60 && market.highestPrice > 0.96) {
await trader.buyCertainty(market.id, market.highestOutcome);
}Low volatility, capital-efficient - good complement to faster strategies.
Run modes
npm run bot:dry # simulation - start here
npm run scan # scanner only, no execution
npm run bot # live tradingConfigure via .env:
PRIVATE_KEY=0x...
POLYMARKET_API_KEY=...
POLYMARKET_API_SECRET=...
POLYMARKET_API_PASSPHRASE=...
CHAIN_ID=137
Risk controls
Every strategy shares the same risk layer:
maxExposureUSDC- portfolio capminEdge- minimum edge before entry- Kelly or fixed bet sizing
- Strategy enable/disable flags per deployment
Never run live without validating in dry-run first.
Python vs TypeScript versions
I maintain two open-source Polymarket arbitrage bots:
| Repo | Language | Focus |
|---|---|---|
| polymarket-arbitrage-bot-python | Python | 5 crypto strategies, signal ranking |
| polymarket-arbitrage-bot | TypeScript | Modular strategies, CLOB + WebSocket |
For late-entry probability capture on BTC 5/15-minute markets specifically, see the dedicated Polymarket trading bot.
Related reading
- Python Polymarket arbitrage bot guide
- Production Polymarket bot architecture
- Prediction markets development
Building custom arbitrage infrastructure? See about for contact details.