Polymarket Trading Bot Examples: 4 Patterns with Python Code

If you're thinking about building a Polymarket trading bot, you're in a small but growing club. Polymarket's CLOB (central limit order book) is fully accessible via REST and WebSocket APIs, the markets are deep enough to absorb meaningful size, and the inefficiencies relative to mature financial markets are still significant.

Below are four patterns we've seen work in production, with minimal Python code. Each one has a real edge — and each one has a hidden failure mode that takes down most beginner bots: dispute risk. We'll cover that explicitly in pattern #4.

Setup: The Polymarket CLOB Client

All examples use the official py-clob-client package and assume you've funded a Polygon wallet with USDC.

pip install py-clob-client requests
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON

HOST = "https://clob.polymarket.com"
KEY  = "0x..."  # private key (use env var in production)
CHAIN_ID = POLYGON

client = ClobClient(HOST, key=KEY, chain_id=CHAIN_ID)
client.set_api_creds(client.create_or_derive_api_creds())

Pattern 1: Same-Event Arbitrage

Polymarket sometimes lists multiple markets on the same underlying event with slightly different framing. If their implied probabilities sum to more than 1.0 (or the YES/NO mirror differs from 1.0), there's an arb.

def find_yes_no_arb(market_id, threshold=0.02):
    """Buy YES and NO on the same market when sum < 1 - threshold."""
    book = client.get_order_book(market_id)
    best_yes_ask = float(book.asks[0].price)
    best_no_ask  = float(book.asks_no[0].price)
    total_cost = best_yes_ask + best_no_ask
    if total_cost < 1 - threshold:
        return {"profit_per_dollar": 1 - total_cost, "yes": best_yes_ask, "no": best_no_ask}
    return None

Edge: Polymarket's market makers occasionally let YES + NO drift below 1.00 during high-volatility moments. Bots that fire fast capture the spread.

Failure mode: Slippage on the second leg. By the time you've filled YES, the NO ask may have moved.

Pattern 2: Momentum / Order Book Imbalance

Track the bid/ask imbalance over a rolling window. Sustained one-sided pressure often precedes price moves.

from collections import deque
import time

window = deque(maxlen=60)  # 60-second rolling

def compute_imbalance(market_id):
    book = client.get_order_book(market_id)
    bid_size = sum(float(b.size) for b in book.bids[:5])
    ask_size = sum(float(a.size) for a in book.asks[:5])
    return (bid_size - ask_size) / (bid_size + ask_size + 1e-9)

while True:
    imb = compute_imbalance("0xMARKET_ID")
    window.append(imb)
    avg_imb = sum(window) / len(window)
    if avg_imb > 0.4:
        print("Strong bid pressure — consider long")
    elif avg_imb < -0.4:
        print("Strong ask pressure — consider short")
    time.sleep(1)

Edge: Polymarket's retail flow creates persistent imbalances on news-driven markets.

Failure mode: Whale orders that get pulled. The imbalance disappears, your fill price worsens.

Pattern 3: Liquidity Provision (Market Making)

Place limit orders on both sides of the spread, capture the spread, manage inventory.

SPREAD_BPS = 200  # 2% spread
MAX_INVENTORY = 100  # max contracts per side

def make_market(market_id):
    book = client.get_order_book(market_id)
    mid = (float(book.bids[0].price) + float(book.asks[0].price)) / 2
    half_spread = SPREAD_BPS / 20000  # convert bps to price units (1.00 = full)
    bid_price = round(mid - half_spread, 3)
    ask_price = round(mid + half_spread, 3)

    # Cancel stale orders
    client.cancel_all(market_id)

    # Place new pair
    client.create_order(market_id, side="BUY",  price=bid_price, size=10)
    client.create_order(market_id, side="SELL", price=ask_price, size=10)

Edge: On low-volume markets without dedicated MMs, you can capture 2–5% spreads consistently.

Failure mode: Adverse selection. Informed traders hit your stale quotes during news events. Always cancel-replace on new info.

Pattern 4: Dispute-Risk-Aware Position Sizing

This is the one most beginner bots miss. Every position you hold has a non-zero probability of being frozen for days when the market gets disputed on UMA. The frozen capital has an opportunity cost — and on dispute-prone markets, that cost can wipe out your edge.

The fix: scale your position size by (1 − dispute_risk) using a dispute risk score.

import requests

def get_dispute_risk(market_id):
    """Returns 0.0 to 1.0 dispute risk score from OracleMangle."""
    r = requests.get(f"https://api.oraclemangle.com/v1/markets/{market_id}")
    if r.status_code != 200:
        return 0.5  # default to caution if unavailable
    return r.json().get("dispute_risk", 0.5)

def sized_position(market_id, base_size_usd):
    """Scale position by inverse dispute risk."""
    risk = get_dispute_risk(market_id)
    if risk > 0.5:
        return 0  # skip extreme-risk markets entirely
    # Linear scaling: 0% risk = full size, 50% risk = 0
    return base_size_usd * (1 - 2 * risk)

# In your main loop:
size = sized_position(market_id, base_size_usd=500)
if size > 0:
    client.create_order(market_id, side="BUY", price=ask, size=size / ask)

Edge: You stop entering the markets where 8–10% of your capital ends up frozen for 2+ weeks. Across our dataset, the highest-risk bucket gets disputed at 9.4% — vs the 1.14% baseline. That's a meaningful drag on bot Sharpe ratios.

Failure mode: Skipping legitimately good trades because the score is conservative. Tune the threshold to your risk tolerance.

What Most Bot Authors Get Wrong

Across the bots we've seen ship to production, three mistakes recur:

  1. No dispute risk filter. Bots track price, volume, expiry, slippage — and ignore the one risk that can lock capital for weeks.
  2. Polling instead of WebSockets. The CLOB has a WebSocket feed (wss://ws-subscriptions-clob.polymarket.com). Polling REST every second is wasteful and slow.
  3. Ignoring USDC bridge fees. Moving USDC on/off Polygon costs gas + bridge fees. Small positions get eaten by fees.

Putting It Together

A production-grade Polymarket bot combines:

The first three are straightforward engineering. The fourth — dispute risk — is the differentiator. Most bots ship without it, get caught in a few disputes, and quietly underperform.

The Bottom Line

Polymarket bots are profitable when they combine a real edge (arb, momentum, market making) with disciplined risk management. The risk most beginners miss is dispute risk — capital frozen for days while UMA token holders vote. Add a single API call to your entry logic and you've removed 80%+ of the freeze risk for free.


OracleMangle's REST API returns dispute risk for any Polymarket market. See the API docs.

Add dispute risk to your bot in one API call

Try Free on Telegram