How to Build a Polymarket Arbitrage Bot (Step by Step)

By Merunas Grincalaitis (X · GitHub · DEV · Hashnode · Telegram), Founder & Lead MEV Engineer at MEV DAO — Ethereum smart contract, Flashbots & MEV builder since 2017. Last updated: October 2026.

TL;DR: A Polymarket arbitrage bot profits from mathematical mispricings across Polymarket's hybrid Central Limit Order Book (CLOB) and Gnosis Conditional Token Framework (CTFExchange on Polygon): (1) Single-market YES + NO < $1.00 (mint/merge arbitrage), where buying both sides costs less than $1.00 and guarantees $1.00 payout at resolution (or instant on-chain mergePositions redemption for USDC); (2) Multi-outcome NegRisk (NegRiskCTFExchange) combinatorial arbitrage, where the sum of mutually exclusive candidate asks (Σ Ask_i < $1.00) or bids (Σ Bid_i > $1.00) drifts out of parity; and (3) Cross-venue arbitrage between Polymarket, Kalshi, and sportsbook/perp feeds.

Part of the Custom MEV Bot Architecture, Arbitrage, Liquidations & Hiring Topic Cluster: Start with our Pillar Guide, Custom MEV Bot Development: What to Know Before You Hire, for the complete architectural blueprint. This guide focuses specifically on polymarket arbitrage bot with production Ethereum mainnet parameters and runnable verification steps.

While our core guides cover Polymarket Trading Bot Strategies: Arbitrage, Market Making, News Latency, the single most requested engineering tutorial from quants reaching out to our Custom MEV & Trading Bot Development team is: how do you actually build a Polymarket arbitrage bot step by step?

Unlike Uniswap AMM arbitrage (which executes inside a single atomic Ethereum transaction), Polymarket uses a hybrid off-chain CLOB + on-chain Polygon settlement (CTFExchange) architecture. Here is the exact 5-step technical architecture to build a low-latency Polymarket arbitrage bot in Rust or Python in 2026.

Architecture diagram of polymarket arbitrage bot workflow on Ethereum showing Reth / Mempool Feed, State Graph Solver, Solidity Executor, and Builder Bribe Split
Figure 1: End-to-end Ethereum execution pipeline for polymarket arbitrage bot (Reth / Mempool Feed → State Graph Solver → Solidity Executor → Builder Bribe Split).

The 3 types of Polymarket arbitrage (Comparison Table)

Before writing code, decide which of the three arbitrage loops your Polymarket arbitrage bot will target:

Arbitrage Strategy Exact Mathematical Condition How Profit Is Locked In Capital Lockup Duration Typical Net Spread & Competition
1. Binary Complement Arb (YES + NO) Ask(YES) + Ask(NO) < $1.00 (Buy both) OR Bid(YES) + Bid(NO) > $1.00 (Mint & sell both) Call CTF.mergePositions() on-chain to burn 1 YES + 1 NO back into $1.00 USDC immediately! < 10 seconds (No waiting for market resolution!) 0.3% – 1.8% per fill; sub-50ms CLOB latency required
2. Multi-Outcome NegRisk Rebalancing In an $N$-outcome mutually exclusive market, $\sum_{i=1}^N \text{Ask}(\text{YES}_i) < 1.00$ or $\text{Ask}(\text{NO}_i)$ conversion Convert $N-1$ NO shares or merge full outcome sets via NegRiskAdapter into $1.00 USDC Seconds to minutes 0.8% – 3.5% during volatile breaking news spikes
3. Cross-Exchange Event Arb (Polymarket vs. Kalshi / Perps) Ask_Polymarket(YES) + Ask_Kalshi(NO) + Fees < $1.00 Hold opposing hedged positions across both venues until event resolution (or convergence) Days to weeks (until event settles or spread mean-reverts) 2.0% – 7.5% net spread; requires split working capital

Step 1: Understand Polymarket's Hybrid CLOB + `CTFExchange` Architecture

Every market on Polymarket is backed by Gnosis Conditional Tokens (ERC-1155) collateralized by USDC.e / native USDC on Polygon:

  • Minting (splitPosition): Anyone can deposit $1.00 USDC into the Conditional Tokens contract and mint 1.00 YES token + 1.00 NO token.
  • Redeeming (mergePositions): Anyone holding 1.00 YES + 1.00 NO of the same conditionId can burn them at any time—even months before the market resolves—and immediately receive $1.00 USDC back!
  • Off-Chain Order Matching (clob.polymarket.com): Traders sign EIP-712 limit/market orders (Order struct signed by an EOA or Gnosis Safe proxy wallet) and submit them to Polymarket's REST/WebSocket CLOB operator, which matches crossing orders and submits the atomic match batch to CTFExchange (0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E) or NegRiskCTFExchange (0xC5d563A36AE78145C45a50134d48A1215220f80a).

That splitPosition / mergePositions invariant is why a Polymarket arbitrage bot does not have to lock up capital until Election Day or Super Bowl Sunday!

Step 2: Stream the live L2 orderbook over WebSockets (`wss://ws-subscriptions-clob.polymarket.com`)

Polling REST endpoints (GET /book) every second is far too slow—by the time you see a crossed spread via REST, a WebSocket bot has already swept it.

Your Polymarket arbitrage bot must maintain a persistent WebSocket connection to wss://ws-subscriptions-clob.polymarket.com/ws/market and maintain a local in-memory L2 orderbook (BTreeMap<PriceTicks, Size> in Rust or sortedcontainers.SortedDict in Python) for every asset_id (token_id) in a market group:

import asyncio, json, websockets

CLOB_WS = "wss://ws-subscriptions-clob.polymarket.com/ws/market"

async def watch_binary_market(yes_token_id: str, no_token_id: str):
    books = {yes_token_id: {"asks": {}, "bids": {}}, no_token_id: {"asks": {}, "bids": {}}}
    async with websockets.connect(CLOB_WS, ping_interval=10) as ws:
        await ws.send(json.dumps({
            "assets_ids": [yes_token_id, no_token_id],
            "type": "market"
        }))
        async for raw_msg in ws:
            events = json.loads(raw_msg)
            for ev in (events if isinstance(events, list) else [events]):
                # Update local L2 book from "book" or "price_change" events
                asset = ev.get("asset_id")
                if asset in books and ev.get("event_type") == "book":
                    books[asset]["asks"] = {float(a["price"]): float(a["size"]) for a in ev.get("asks", [])}
                    books[asset]["bids"] = {float(b["price"]): float(b["size"]) for b in ev.get("bids", [])}

                # Check Binary Complement Arbitrage: Best Ask(YES) + Best Ask(NO) < 1.00
                if books[yes_token_id]["asks"] and books[no_token_id]["asks"]:
                    best_ask_yes = min(books[yes_token_id]["asks"].keys())
                    best_ask_no  = min(books[no_token_id]["asks"].keys())
                    total_cost   = best_ask_yes + best_ask_no
                    if total_cost < 0.996: # 40 bps safety margin
                        max_shares = min(
                            books[yes_token_id]["asks"][best_ask_yes],
                            books[no_token_id]["asks"][best_ask_no]
                        )
                        print(f"[ARB] Buy YES@{best_ask_yes} + NO@{best_ask_no} = {total_cost:.4f} | Size: {max_shares}")

Step 3: Multi-outcome `NegRisk` combinatorial arbitrage math

Where do the largest spreads live in 2026? In multi-outcome NegRisk markets (for example: "Who will win the Champions League?" or "What will the Fed funds rate be in December?" with 5 to 20 mutually exclusive brackets).

In a complete mutually exclusive NegRisk event with $N$ outcomes, exactly one outcome will resolve to $1.00 and the other $N-1$ outcomes will resolve to $0.00:

  1. Underpriced Sum of Asks ($\sum_{i=1}^N \text{Ask}(\text{YES}_i) < 1.00$): If the best asks across all $N$ outcomes sum to $0.975, your Polymarket arbitrage bot fires parallel Fill-or-Kill (FOK) buy orders across all $N$ legs for $S$ shares (where $S = \min_i \text{Size}(\text{Ask}_i)$) and merges them via NegRiskAdapter for $1.00 \times S USDC—locking in a risk-free 2.5% instant return!
  2. NO-Token Conversion Arbitrage: Because NegRiskAdapter allows converting 1 share of $\text{NO}_A$ + 1 share of $\text{NO}_B$ into $\text{YES}$ shares of the remaining outcomes plus USDC collateral, your bot can continuously arbitrage price discrepancies between any single outcome's NO book and the other outcomes' YES books.

Step 4: Handling execution risk ("Legging Risk") with pre-signed EIP-712 orders

Unlike an Ethereum Flashbots bundle where all legs execute atomically or revert together, submitting 2 to 6 separate CLOB orders to https://clob.polymarket.com/orders introduces legging risk: what if Leg 1 fills, and a rival bot sweeps Leg 2 a millisecond before your second HTTP request arrives?

To minimize legging risk in a production Polymarket trading bot:

  1. Host in AWS eu-west-1 (Dublin) or us-east-1 with persistent HTTP/2 connections pre-warmed to clob.polymarket.com.
  2. Pre-compute EIP-712 Domain Separators & Sign in Rust (alloy-signer): Signing EIP-712 Order structs in Python eth_account takes 3–8 ms per leg (30 ms for a 5-leg NegRisk basket). Moving order signing to Rust reduces signing latency to <0.15 ms for all 5 legs combined!
  3. Use FOK (Fill-Or-Kill) or FAK (Fill-And-Kill) Order Types + Instant Delta Hedge: Always submit aggressive arb legs as FOK batch orders (POST /orders) and code an immediate unwind/hedge loop that dumps any partial fill back at breakeven if one leg of a multi-outcome basket fails to fill.

Step 5: Automated on-chain `mergePositions` recycling

Once your bot holds equal balances of YES and NO tokens (or a complete NegRisk set), a background worker calls mergePositions on the Polygon Conditional Tokens contract (0x4D97DCd97eC945f40cF65F87097ACe5EA0476045) to burn the ERC-1155 outcome tokens and return liquid USDC right back into your trading proxy wallet so you can compound the same capital 20+ times a day.

Frequently asked questions

How does a Polymarket arbitrage bot make risk-free profit without waiting for the market to resolve?

On Polymarket, 1 YES share + 1 NO share of the same binary market is always redeemable on-chain for $1.00 USDC via the Gnosis Conditional Token Framework's mergePositions() function. Whenever Ask(YES) + Ask(NO) < $1.00, a bot can buy both sides on the CLOB and immediately merge them on-chain into $1.00 USDC in under 10 seconds.

Does Polymarket charge trading fees on the CLOB that eat into arbitrage spreads?

Polymarket's CLOB charges 0% maker/taker fees on most standard markets (or a small dynamic fee on select high-frequency sports/crypto brackets). Always query the market's maker_base_fee and taker_base_fee parameters via the CLOB API and subtract them from your $1.00 parity threshold before firing orders.

Should I build a Polymarket arbitrage bot in Python or Rust?

You can prototype the WebSocket orderbook listener and py-clob-client integration in Python in an afternoon. However, to win contested YES + NO < 1 and NegRisk spreads against rival bots, migrating the WebSocket parser and EIP-712 Order signer to Rust (tokio + alloy) cuts end-to-end tick-to-trade latency from ~25ms down to <1.5ms.

What is NegRisk on Polymarket and why is it the best target for arbitrage bots?

NegRisk (NegRiskCTFExchange) is Polymarket's contract system for mutually exclusive multi-outcome markets (like elections or price brackets). Because retail traders buy and sell individual candidate/bracket books independently, the sum of probabilities across 5–15 outcomes frequently drifts below 98% or above 102%, creating multi-leg rebalancing opportunities.

Can MEV DAO build a custom Polymarket arbitrage or market-making bot for our team?

Yes! We design and deliver production Rust and Python Polymarket bots (CLOB arbitrage, NegRisk basket rebalancers, automated liquidity-reward market makers, and low-latency news/oracle snipers). See our scope on Custom MEV Bot Development: Hire an Ethereum MEV Bot Developer and pricing in MEV Bot Development Cost: Custom Pricing in 2026.


Want to hire our team to build a custom low-latency Polymarket arbitrage or market-making bot—or test our live Ethereum bots today? Message me directly on Telegram (@merunas) to discuss your strategy, or start a 3-day free trial at mevdao.org/subscribe.

← More on Polymarket Bots All articles