Building a Prediction Market Bot in Python: Dev Guide

Building a Prediction Market Bot in Python: Dev Guide

Building a Prediction Market Bot in Python: Dev Guide

Learn how to create a powerful prediction market bot in Python. This guide covers essential components and setup for successful automation.

Building a Prediction Market Bot in Python: Dev Guide

You can build a production-ready automated prediction market bot in Python by wiring four modular components: a market scanner, a signal engine, an execution engine, and a risk manager, connected to venue APIs (Polymarket’s Gamma and CLOB endpoints, Kalshi’s REST API) and the Assymetrix Data API for real-time WebSocket feeds and historical snapshots.

Before you write a single strategy line, confirm these are in place:

  • Credentials: Polymarket wallet with EIP-712 signing capability, Kalshi API key, and an Assymetrix API key from data.assymetrix.com

  • Core modules: data ingestion layer, signal engine, risk manager, execution client

  • Paper mode: shadow execution enabled before any live capital touches the CLOB

  • Market universe: start with 5–10 high-liquidity markets, not the full catalog

The minimal polling loop that anchors everything looks like this:

async for msg in assymetrix_ws:
    signal = signal_engine.evaluate(msg)
    if risk_manager.approve(signal):
        decision_log.append(signal)  # paper mode: log, don't execute
async for msg in assymetrix_ws:
    signal = signal_engine.evaluate(msg)
    if risk_manager.approve(signal):
        decision_log.append(signal)  # paper mode: log, don't execute

Pro Tip: Restrict your initial universe to markets with consistent daily volume and at least 30 days of history in Assymetrix. Shadow mode on a narrow universe surfaces structural bugs faster than running across hundreds of thin markets.

Key Takeaways

A production prediction market bot requires modular architecture, microstructure-aware backtesting, and live risk controls before a single dollar of real capital is committed.

| Modular architecture | Separate feed layer, market scanner, signal engine, risk manager, and execution engine for safe iteration and independent testing.

| Shadow mode first | Run at least two weeks of paper-mode simulation on live Assymetrix feed data before enabling live execution. |

| Microstructure backtesting | Use Assymetrix historical snapshots with a hazard-based fill model; naive tick-replay backtests overstate edge on prediction markets. |

| Risk controls are non-negotiable | Implement per-position caps, daily loss limits, and a persistent kill switch before connecting to any live CLOB. |

| Assymetrix for cross-venue data | Use the Assymetrix WebSocket for real-time signals and the historical REST endpoint for backtests; wallet tracking endpoints power Smart Money detection. |



Diagram of prediction market bot modular architecture

Table of Contents

  • How does a prediction market bot architecture work?

  • How do you ingest market data from Polymarket and Assymetrix?

  • What signal strategies work for prediction market bots?

  • How does Polymarket’s CLOB execution work in Python?

  • How do you size positions and control risk in a prediction market bot?

  • How do you backtest a prediction market strategy without fooling yourself?

  • What project structure works for a production prediction market bot?

  • A minimal Python bot that detects a signal and logs a trade decision

  • What should your production bot monitor and alert on?

  • How do you deploy a prediction market bot securely?

  • What are the most common prediction market bot failures?

  • What compliance rules apply to prediction market bots in the US?

  • How do you test a live trading bot safely before full deployment?

  • How do you reduce latency in a real-time prediction market bot?

  • How do machine learning models improve prediction market signals?

  • The part of bot building most guides skip

  • Assymetrix gives you the data layer your bot actually needs

  • Sources

  • FAQ

How does a prediction market bot architecture work?

A well-structured prediction market trading bot separates concerns into five discrete modules. Each module has a single responsibility, which makes testing, hot-swapping strategies, and debugging production failures tractable rather than catastrophic.

Data flow, module by module:

  • Feed layer: Ingests raw market events from Assymetrix WebSocket (cross-venue real-time prices, Smart Money wallet events) and venue REST APIs (Polymarket Gamma for metadata, CLOB for order book state, Kalshi REST for centralized market data). Normalizes schemas before anything downstream sees the data.

  • Market scanner: Filters the active market universe by liquidity thresholds, time-to-resolution windows, and spread constraints. Outputs a candidate list to the signal engine.

  • Signal engine: Consumes normalized market events and candidate lists, runs strategy logic, and emits typed signal objects with a confidence score, suggested direction, and expiry timestamp.

  • Risk manager: Receives signal objects and applies pre-trade checks: position caps, daily loss limits, portfolio exposure, and liquidity depth gates. Approves, modifies, or rejects each signal before it reaches the executor.

  • Execution engine: Translates approved signals into signed orders (EIP-712 on Polymarket, standard REST on Kalshi), submits them, tracks fill status, and handles partial fills and cancellations.

  • Persistence and monitoring layer: Writes all events, decisions, fills, and PnL to durable storage; emits metrics and alerts to your observability stack.

Assymetrix fits into this architecture at two points: the historical REST endpoint powers backtests and warm-up data loads, while the WebSocket feed drives real-time market events and Smart Money wallet signals into the feed layer. Because Assymetrix normalizes schemas across Polymarket, Kalshi, and Limitless, the scanner and signal engine never need per-venue parsing logic.

The key design decision is event-driven over synchronous polling. Polling every market on a fixed interval creates thundering-herd API pressure and adds latency proportional to your polling interval. An event-driven architecture, where the feed layer pushes updates to an internal message queue and workers consume them asynchronously, reduces redundant calls and reacts to market moves within milliseconds of the feed update.

How do you ingest market data from Polymarket and Assymetrix?

Market discovery starts with the Gamma API (Polymarket’s metadata layer). It returns active markets, token IDs, resolution criteria, and order book metadata. The CLOB API handles execution-specific endpoints: live order books, trade history, and order submission. These are separate surfaces with different rate limits and authentication requirements.

For a cross-venue bot, the Assymetrix Data API at data.assymetrix.com is the more practical ingestion point for most signal work. The Python integration guide covers the full endpoint surface, but the three endpoints you’ll use most are:

  • WebSocket feed: Real-time cross-venue price updates, order book deltas, and Smart Money wallet entry events, normalized to a single schema regardless of venue.

  • Historical REST endpoint: Paginated snapshots of trade history, order book states, and price series, drawn from Assymetrix’s dataset of nearly one billion rows of trading activity across venues.

  • Wallet tracking endpoint: Sequence-filtered wallet events for Smart Money detection, returning entry/exit events for tracked addresses with timestamps and position sizes.

When to use venue APIs directly vs. Assymetrix: Use the Gamma API for market metadata that requires Polymarket-specific fields (token IDs for CLOB order construction, resolution source details). Use the CLOB API for order submission and fill tracking. Use Assymetrix for everything involving cross-venue price comparison, historical data, and Smart Money signals — the deduplication and normalization alone eliminate a class of bugs that plague bots pulling from multiple venue APIs simultaneously.

import asyncio, websockets, json, os

ASSYMETRIX_WS = "wss://data.assymetrix.com/ws/v1/markets"
API_KEY = os.environ["ASSYMETRIX_API_KEY"]

async def subscribe():
    async with websockets.connect(
        ASSYMETRIX_WS,
        extra_headers={"Authorization": f"Bearer {API_KEY}"}
    ) as ws:
        await ws.send(json.dumps({"action": "subscribe", "channels": ["prices", "wallets"]}))
        async for raw in ws:
            msg = json.loads(raw)
            yield msg

# Defensive reconnect with exponential backoff
async def resilient_feed(handler, max_backoff=60):
    delay = 1
    while True:
        try:
            async for msg in subscribe():
                delay = 1  # reset on success
                await handler(msg)
        except Exception:
            await asyncio.sleep(delay)
            delay = min(delay * 2, max_backoff)
import asyncio, websockets, json, os

ASSYMETRIX_WS = "wss://data.assymetrix.com/ws/v1/markets"
API_KEY = os.environ["ASSYMETRIX_API_KEY"]

async def subscribe():
    async with websockets.connect(
        ASSYMETRIX_WS,
        extra_headers={"Authorization": f"Bearer {API_KEY}"}
    ) as ws:
        await ws.send(json.dumps({"action": "subscribe", "channels": ["prices", "wallets"]}))
        async for raw in ws:
            msg = json.loads(raw)
            yield msg

# Defensive reconnect with exponential backoff
async def resilient_feed(handler, max_backoff=60):
    delay = 1
    while True:
        try:
            async for msg in subscribe():
                delay = 1  # reset on success
                await handler(msg)
        except Exception:
            await asyncio.sleep(delay)
            delay = min(delay * 2, max_backoff)

Keep a lightweight local cache of market metadata — slug, token IDs, resolution date, and last-seen spread — refreshed every few minutes from Gamma. This cuts API calls by an order of magnitude on the hot path and keeps decision latency low.

Pro Tip: Assign a sequence ID to every inbound message and track the last-seen sequence per channel. On reconnect, request a replay from your last sequence ID rather than re-subscribing cold. This prevents missed updates from creating ghost positions.

What signal strategies work for prediction market bots?

The signal engine has four responsibilities: normalize incoming data into features, run strategy logic, score confidence, and emit a typed signal object. Every signal should carry a direction, confidence score (0–1), suggested size, venue, market ID, and an expiry timestamp after which the signal is stale and must be discarded.

Four implementable strategies, in order of complexity:

  1. Momentum on probability changes. Track the rolling 15-minute and 60-minute change in implied probability for each market. A sustained directional move with increasing volume is a momentum signal. Threshold: probability delta exceeds a configurable minimum (e.g., 3 percentage points) with volume above the market’s 7-day median.

  2. Mean reversion to fair value. Estimate fair value from a reference source (news sentiment model, base-rate prior, or ensemble model output). When the market price deviates beyond a spread threshold, fade the move. This works best in liquid markets where the order book recovers quickly.

  3. Cross-venue divergence arbitrage. Compare the same underlying event’s implied probability across Polymarket, Kalshi, and Limitless using the Assymetrix normalized feed. A divergence beyond transaction costs and slippage is an arbitrage signal. The cross-venue arbitrage scanner guide covers the full scanner implementation, including fee-aware edge calculation.

  4. Smart Money wallet detection. Assymetrix’s wallet tracking endpoint surfaces entry events from addresses with high Trader Skill Scores. When a tracked wallet enters a position, filter by: minimum position size, market liquidity threshold, and whether the entry aligns with a concurrent price divergence. Combining wallet entry sequences with cross-venue price data gives a high-signal feature for short-horizon strategies.

def detect_divergence(polymarket_prob: float, kalshi_prob: float,
                      fee_cost: float = 0.02) -> dict | None:
    spread = abs(polymarket_prob - kalshi_prob)
    if spread > fee_cost:
        long_venue = "polymarket" if polymarket_prob < kalshi_prob else "kalshi"
        return {"type": "divergence", "spread": spread, "long_venue": long_venue,
                "confidence": min(spread / 0.10, 1.0)}
    return None
def detect_divergence(polymarket_prob: float, kalshi_prob: float,
                      fee_cost: float = 0.02) -> dict | None:
    spread = abs(polymarket_prob - kalshi_prob)
    if spread > fee_cost:
        long_venue = "polymarket" if polymarket_prob < kalshi_prob else "kalshi"
        return {"type": "divergence", "spread": spread, "long_venue": long_venue,
                "confidence": min(spread / 0.10, 1.0)}
    return None

Converting model or LLM outputs into tradable signals requires calibration. Raw model probabilities are rarely well-calibrated against market prices. Apply Platt scaling or isotonic regression on a held-out validation set, then ensemble across multiple models by averaging calibrated probabilities and rejecting signals where the standard deviation across ensemble members exceeds a threshold (e.g., 0.08). Signals that models disagree on are not worth trading.

Signal lifecycle: detect → validate liquidity and spread gates → compute size suggestion → set expiry. If a signal expires before the executor processes it, discard it. Stale signals are one of the most common sources of bad fills in production bots.

How does Polymarket’s CLOB execution work in Python?

Polymarket V2 separates market discovery (Gamma API) from order execution (CLOB API). Order matching happens off-chain; settlement is on-chain. Every order must be signed using EIP-712 before submission. The py-clob-client library handles signing and submission, but you need to understand the order lifecycle to build a reliable executor.

Order lifecycle:

  1. Create: Construct the order object with token ID, side, size, price, and order type (limit or FOK).

  2. Sign: Sign the order payload using EIP-712 with your wallet’s private key. Never expose the key outside an encrypted vault or environment variable.

  3. Submit: POST the signed order to the CLOB endpoint. Receive an order ID.

  4. Track fills: Poll the order status endpoint or subscribe to fill events. Track partial fills separately from full fills.

  5. Handle ambiguous states: If a submission times out, query the order status before retrying. Duplicate submissions on a timed-out order are a common source of unintended double positions.

  6. Cancel stale orders: Any open order older than your signal expiry window should be canceled. Stale limit orders sitting in the book accumulate adverse selection risk.

Execution check

API surface

Notes

Market metadata and token ID

Gamma API

Required before order construction

Live order book depth

CLOB API

Check before sizing

Order creation and signing

py-clob-client

EIP-712 required

Order status and fills

CLOB API

Poll or subscribe

Order cancellation

CLOB API

Cancel on signal expiry

Settlement reconciliation

On-chain / Chainlink

Post-close PnL finalization

For thin order books, prefer limit maker orders over FOK taker flows. Maker orders add liquidity and typically pay lower fees; FOK orders guarantee fill but at the cost of crossing the spread. For larger entries, split across multiple price levels using a VWAP-style entry to reduce market impact.

Kalshi operates a centralized exchange with a standard REST API, no on-chain signing required. The execution logic is simpler, but the market universe and liquidity profile differ. For a data-driven comparison of venue tradeoffs, the key difference is that Kalshi’s centralized model means faster order acknowledgment but no on-chain settlement guarantee.

Pro Tip: Re-evaluate your edge at submission time, not just at signal generation time. Fetch the current best bid/ask from the CLOB immediately before posting the order. If the spread has widened beyond your slippage budget since the signal fired, abort the order.

How do you size positions and control risk in a prediction market bot?

Risk management is not a feature you add after the bot works. It is the first module you write, because a bug in the signal engine loses one trade; a bug in the risk manager can lose the entire bankroll.

Default guardrails to implement before any live trading:

  • Max position size per market: fixed USD cap (e.g., $50 per position in early testing)

  • Max portfolio exposure: total open positions not to exceed a percentage of bankroll (e.g., 20%)

  • Max concurrent positions: hard limit on open trades (e.g., 10)

  • Daily loss limit: halt all new orders if realized + unrealized loss exceeds a threshold (e.g., 5% of bankroll)

  • Drawdown stop: pause the bot if peak-to-trough drawdown exceeds a configurable level

Fractional Kelly sizing is the standard approach for prediction market bots. Full Kelly is theoretically optimal but practically dangerous due to estimation error in edge. Use a fraction (typically 0.25–0.5 Kelly):

def kelly_fraction(prob: float, odds: float, fraction: float = 0.25) -> float:
    """
    prob: estimated win probability
    odds: decimal odds (payout / stake), e.g. 1/market_price for binary
    fraction: Kelly fraction (0.25 = quarter Kelly)
    """
    edge = prob * odds - 1
    if edge <= 0:
        return 0.0
    kelly = edge / (odds - 1)
    return kelly * fraction
def kelly_fraction(prob: float, odds: float, fraction: float = 0.25) -> float:
    """
    prob: estimated win probability
    odds: decimal odds (payout / stake), e.g. 1/market_price for binary
    fraction: Kelly fraction (0.25 = quarter Kelly)
    """
    edge = prob * odds - 1
    if edge <= 0:
        return 0.0
    kelly = edge / (odds - 1)
    return kelly * fraction

Pre-trade checks run before every order: minimum liquidity depth at the target price level, order book age (reject if the last update is older than N seconds), and fill probability estimate based on current spread. Post-trade checks reconcile actual fill price against expected price and flag large slippage events for review.

Kill switches must be persistent across process restarts. Write the halt state to your database, not just in-memory. If the bot crashes and restarts, it should read the halt flag before placing any orders. Manual override requires a separate authenticated command, not just restarting the process.

Pro Tip: Set your daily loss limit conservatively for the first two weeks of live trading. A limit that feels too tight is correct. You can always widen it after you’ve validated that fills and PnL match your backtest expectations.

How do you backtest a prediction market strategy without fooling yourself?

Naive backtests on prediction markets produce systematically optimistic results for three structural reasons: resolution timing creates a concentration of volume near market close that doesn’t exist mid-life, liquidity is sparse and order book depth fluctuates sharply, and fill assumptions that treat every limit order as filled at the quoted price are unrealistic.

The Homerun framework demonstrates the right approach: L2 order book replay with a hazard-based fill model. Instead of assuming your limit order fills whenever the price touches your level, a Cox-proportional hazard fill model estimates fill probability as a function of your queue position, order size relative to depth, and time elapsed. This materially changes expected edge versus tick-replay backtests.

Assymetrix’s historical dataset, spanning nearly one billion rows of trading activity, gives you the order book snapshots and trade history needed to run this kind of replay. The backtesting methodology guide covers the REST endpoints for pulling historical snapshots by market, time range, and venue.

Shadow mode is the bridge between backtest and live. Run your bot with identical logic, real market data from the Assymetrix feed, and simulated fills using the same fill model as your backtest. The same risk gates apply. The only difference is that orders are logged rather than submitted. Two weeks of shadow mode on your target market universe will surface timing bugs, fill model errors, and signal expiry issues that backtests miss.

Pre-deployment validation checklist:

  • Walk-forward stability: strategy must hold edge across at least three non-overlapping out-of-sample periods

  • Calibration: predicted probabilities must be well-calibrated against realized outcomes (Brier score below a threshold)

  • Slippage testing: re-run backtest with 1.5x and 2x assumed slippage; edge must survive

  • Fill model sensitivity: vary fill probability assumptions; strategy must not depend on optimistic fill rates

  • Statistical significance: edge must be distinguishable from noise across the full backtest period

Pro Tip: Use Assymetrix’s historical wallet data in your backtest warm-up. Load the last 30 days of Smart Money wallet events before your backtest start date so the signal engine has a populated wallet state from day one, rather than a cold start that artificially suppresses Smart Money signals in the early backtest period.

What project structure works for a production prediction market bot?

Event-driven architecture is the right pattern for a production bot. The core flow: market update arrives at the feed layer, gets pushed to an internal message queue (Redis Streams or asyncio.Queue for single-process), consumed by a signal worker, checked by the risk manager, and dispatched to an execution worker. Each worker runs independently; a crash in the signal worker does not take down the executor.

Recommended package layout:

bot/
├── data/           # Feed clients, WebSocket, REST wrappers, local cache
├── strategies/     # Signal engine, individual strategy modules
├── execution/      # Order construction, signing, CLOB/Kalshi clients
├── risk/           # Pre-trade checks, sizing, kill switch, state
├── persistence/    # DB writers, JSONL event log, trade records
├── monitoring/     # Metrics emission, alert rules, health checks
├── config/         # YAML/env config, market universe definitions
└── main.py         # Orchestration entry point
bot/
├── data/           # Feed clients, WebSocket, REST wrappers, local cache
├── strategies/     # Signal engine, individual strategy modules
├── execution/      # Order construction, signing, CLOB/Kalshi clients
├── risk/           # Pre-trade checks, sizing, kill switch, state
├── persistence/    # DB writers, JSONL event log, trade records
├── monitoring/     # Metrics emission, alert rules, health checks
├── config/         # YAML/env config, market universe definitions
└── main.py         # Orchestration entry point

Numbered orchestration steps for a clean startup sequence:

  1. Load config and environment variables; validate all required keys are present.

  2. Connect to Assymetrix WebSocket feed and warm up local market cache from Gamma API.

  3. Load historical context (last N days of wallet events, price history) from Assymetrix REST.

  4. Start risk manager and load persisted halt state from the database.

  5. Start signal workers and execution workers as separate async tasks or processes.

  6. Enable monitoring and heartbeat emission before processing any live events.

Storage choices: Time-series database (InfluxDB or TimescaleDB) for price snapshots and metrics. PostgreSQL or SQLite for trade records, fill history, and risk state. JSONL files for raw event logs, which are cheap to write and easy to replay for debugging.

Orchestration: Docker with process supervision (supervisord or systemd inside the container) for single-host deployments. For multi-strategy or multi-venue bots, a lightweight Kubernetes deployment with one pod per worker type gives independent scaling and restart policies without the overhead of a full service mesh.

A minimal Python bot that detects a signal and logs a trade decision

This walkthrough connects to the Assymetrix WebSocket feed, detects a cross-venue price divergence or Smart Money wallet entry, and logs a trade decision. No live orders are submitted. This is the correct starting point: validate the signal logic before touching the CLOB.

Environment template (.env):

ASSYMETRIX_API_KEY=your_key_here
POLYMARKET_PRIVATE_KEY=your_wallet_private_key
KALSHI_API_KEY=your_kalshi_key
PAPER_MODE=true
MIN_DIVERGENCE=0.04
SMART_MONEY_MIN_SIZE=500
LOG_LEVEL=INFO
ASSYMETRIX_API_KEY=your_key_here
POLYMARKET_PRIVATE_KEY=your_wallet_private_key
KALSHI_API_KEY=your_kalshi_key
PAPER_MODE=true
MIN_DIVERGENCE=0.04
SMART_MONEY_MIN_SIZE=500
LOG_LEVEL=INFO

Dependencies:

pip install websockets python-dotenv aiofiles structlog
pip install websockets python-dotenv aiofiles structlog

Minimal bot (main.py):

import asyncio, json, os, structlog
from dotenv import load_dotenv
import websockets

load_dotenv()
log = structlog.get_logger()

API_KEY = os.environ["ASSYMETRIX_API_KEY"]
MIN_DIV = float(os.getenv("MIN_DIVERGENCE", "0.04"))
SM_MIN = float(os.getenv("SMART_MONEY_MIN_SIZE", "500"))
PAPER = os.getenv("PAPER_MODE", "true").lower() == "true"

WS_URL = "wss://data.assymetrix.com/ws/v1/markets"

# --- Signal detection ---
def check_divergence(msg: dict) -> dict | None:
    if msg.get("type") != "price_update":
        return None
    prices = msg.get("venues", {})
    poly = prices.get("polymarket")
    kalshi = prices.get("kalshi")
    if poly is None or kalshi is None:
        return None
    spread = abs(poly - kalshi)
    if spread >= MIN_DIV:
        return {
            "signal": "divergence",
            "market_id": msg["market_id"],
            "spread": round(spread, 4),
            "long_venue": "polymarket" if poly < kalshi else "kalshi",
            "confidence": min(spread / 0.10, 1.0),
        }
    return None

def check_smart_money(msg: dict) -> dict | None:
    if msg.get("type") != "wallet_event":
        return None
    if msg.get("size_usd", 0) >= SM_MIN:
        return {
            "signal": "smart_money",
            "market_id": msg["market_id"],
            "wallet": msg["wallet"],
            "side": msg["side"],
            "size_usd": msg["size_usd"],
            "confidence": 0.7,
        }
    return None

# --- Risk gate stub ---
def risk_approve(signal: dict) -> bool:
    # Replace with full risk manager checks
    return signal.get("confidence", 0) >= 0.5

# --- Main loop ---
async def run():
    delay = 1
    while True:
        try:
            async with websockets.connect(
                WS_URL,
                extra_headers={"Authorization": f"Bearer {API_KEY}"}
            ) as ws:
                await ws.send(json.dumps({
                    "action": "subscribe",
                    "channels": ["prices", "wallets"]
                }))
                log.info("connected", paper=PAPER)
                delay = 1
                async for raw in ws:
                    msg = json.loads(raw)
                    for detector in [check_divergence, check_smart_money]:
                        signal = detector(msg)
                        if signal and risk_approve(signal):
                            log.info("trade_decision", **signal, paper=PAPER)
                            # In paper mode: log only. In live mode: pass to executor.
        except Exception as e:
            log.warning("reconnecting", error=str(e), backoff=delay)
            await asyncio.sleep(delay)
            delay = min(delay * 2, 60)

if __name__ == "__main__":
    asyncio.run(run())
import asyncio, json, os, structlog
from dotenv import load_dotenv
import websockets

load_dotenv()
log = structlog.get_logger()

API_KEY = os.environ["ASSYMETRIX_API_KEY"]
MIN_DIV = float(os.getenv("MIN_DIVERGENCE", "0.04"))
SM_MIN = float(os.getenv("SMART_MONEY_MIN_SIZE", "500"))
PAPER = os.getenv("PAPER_MODE", "true").lower() == "true"

WS_URL = "wss://data.assymetrix.com/ws/v1/markets"

# --- Signal detection ---
def check_divergence(msg: dict) -> dict | None:
    if msg.get("type") != "price_update":
        return None
    prices = msg.get("venues", {})
    poly = prices.get("polymarket")
    kalshi = prices.get("kalshi")
    if poly is None or kalshi is None:
        return None
    spread = abs(poly - kalshi)
    if spread >= MIN_DIV:
        return {
            "signal": "divergence",
            "market_id": msg["market_id"],
            "spread": round(spread, 4),
            "long_venue": "polymarket" if poly < kalshi else "kalshi",
            "confidence": min(spread / 0.10, 1.0),
        }
    return None

def check_smart_money(msg: dict) -> dict | None:
    if msg.get("type") != "wallet_event":
        return None
    if msg.get("size_usd", 0) >= SM_MIN:
        return {
            "signal": "smart_money",
            "market_id": msg["market_id"],
            "wallet": msg["wallet"],
            "side": msg["side"],
            "size_usd": msg["size_usd"],
            "confidence": 0.7,
        }
    return None

# --- Risk gate stub ---
def risk_approve(signal: dict) -> bool:
    # Replace with full risk manager checks
    return signal.get("confidence", 0) >= 0.5

# --- Main loop ---
async def run():
    delay = 1
    while True:
        try:
            async with websockets.connect(
                WS_URL,
                extra_headers={"Authorization": f"Bearer {API_KEY}"}
            ) as ws:
                await ws.send(json.dumps({
                    "action": "subscribe",
                    "channels": ["prices", "wallets"]
                }))
                log.info("connected", paper=PAPER)
                delay = 1
                async for raw in ws:
                    msg = json.loads(raw)
                    for detector in [check_divergence, check_smart_money]:
                        signal = detector(msg)
                        if signal and risk_approve(signal):
                            log.info("trade_decision", **signal, paper=PAPER)
                            # In paper mode: log only. In live mode: pass to executor.
        except Exception as e:
            log.warning("reconnecting", error=str(e), backoff=delay)
            await asyncio.sleep(delay)
            delay = min(delay * 2, 60)

if __name__ == "__main__":
    asyncio.run(run())

The warm-up step (loading recent price history and wallet events from the Assymetrix historical REST endpoint before the WebSocket connects) is omitted here for brevity but is required in production. Without warm-up, the signal engine starts cold and will miss context-dependent signals for the first several minutes.

Pro Tip: Run this script with PAPER_MODE=true for at least two weeks before changing the flag. Watch the structured log output for signal frequency, confidence distribution, and any markets that generate an implausible number of signals — those are usually data quality issues, not alpha.

What should your production bot monitor and alert on?

Monitoring is not optional for a bot that touches live capital. The minimum viable observability stack covers three layers: metrics, logs, and alerts.

Essential metrics to track:

  • API latency per endpoint (Assymetrix WebSocket lag, CLOB response time)

  • Order failure rate and fill rate by strategy and venue

  • PnL by strategy, by market, and portfolio-level (realized and unrealized)

  • Open exposure as a percentage of bankroll

  • Queue depth and processing lag (signal worker backlog)

  • Ghost positions: open positions with no corresponding active signal

Logging practices: Use structured logging (structlog or Python’s logging with JSON formatter). Assign a correlation ID to every market event and propagate it through signal detection, risk check, and execution. This makes postmortem analysis tractable. Retain raw event logs (JSONL) for at least 90 days.

Alert rules and runbook triggers:

  1. Heartbeat missing for more than 60 seconds: page immediately, check feed connection.

  2. Daily loss limit breached: halt bot, send alert, require manual review before restart.

  3. Fill rate below threshold for more than 30 minutes: investigate order book conditions and slippage.

  4. Settlement mismatch: actual settlement differs from bot’s recorded position; reconcile against on-chain data and Chainlink settlement reference.

  5. Large slippage event: any fill more than 2x expected slippage; log for strategy review.

  6. Queue lag exceeding 10 seconds: signal worker is falling behind; scale or throttle ingestion.

For the monitoring stack, Prometheus with Grafana covers metrics and dashboards. For alerts, PagerDuty or a Telegram bot (common in open-source prediction market bot repos) handles notifications. Define service level objectives (SLOs) before going live, such as aiming for high uptime for the feed connection and maintaining a strong fill rate.% for limit orders in liquid markets.

How do you deploy a prediction market bot securely?

Deployment security starts with the assumption that your private key is the most valuable asset in the system. Compromise it and every position, every wallet balance, is at risk.

Docker and process supervision:

  • Use a minimal base image (python:3.12-slim) and run as a non-root user.

  • Pin all dependency versions in requirements.txt and rebuild images on dependency updates.

  • Use supervisord or a process manager inside the container to restart workers on crash without restarting the entire container.

Secrets handling:

  1. Never store private keys or API keys in the repository, in plaintext config files, or in Docker image layers.

  2. Use HashiCorp Vault, AWS KMS, or GCP Secret Manager for key storage in production.

  3. For local development, use a .env file excluded from version control via .gitignore.

  4. Rotate API keys on a schedule and immediately after any suspected exposure.

Environment separation:

  • Maintain distinct wallets and API keys for shadow/paper and live environments.

  • Use a DRY_RUN environment variable that the execution engine checks before every order submission.

  • For large trades (above a configurable threshold), require a human confirmation step before the order posts. This is a guardrail against runaway sizing bugs.

Post-deploy security checklist:

  1. Confirm no secrets appear in container environment variable dumps or logs.

  2. Verify the kill switch persists correctly across a simulated process restart.

  3. Run a test order in paper mode and confirm it does not reach the live CLOB.

  4. Check that API key permissions are scoped to the minimum required (read-only where execution is not needed).

  5. Enable drift detection: alert if the running container image differs from the pinned version in your deployment manifest.

What are the most common prediction market bot failures?

Liquidity thinness is the most frequent cause of poor live performance relative to backtests. Prediction markets, even on Polymarket, have order books that can be thin outside of high-interest events. Mitigations: require a minimum executable depth at your target price before sizing, restrict the market universe to markets with consistent daily volume, and use VWAP entry for any position above a minimum size threshold.

Slippage and partial fills compound the liquidity problem. A limit order that fills partially leaves you with a position that doesn’t match your sizing model. Track partial fills explicitly, decide whether to complete the position or cancel the remainder, and never assume a submitted order is fully filled until the fill event confirms it.

Oracle and resolution risk is specific to prediction markets. A market can resolve differently from what the order book implied, particularly for events with ambiguous outcomes or delayed resolution. Open-source bots address this by aligning with Chainlink settlement data as a reference and reconciling official settlement post-close. Treat any market with a non-standard resolution source conservatively: reduce position size and widen your edge threshold.

API rate limits hit bots that poll aggressively across large market universes. Mitigations: cache market metadata locally, use exponential backoff on 429 responses, stagger polling intervals across markets, and prefer WebSocket subscriptions over REST polling for real-time data. Assymetrix’s unified feed reduces the number of venue API calls required by consolidating cross-venue data into a single stream.

Pro Tip: For resolution risk, set a position expiry rule: automatically close or reduce any position that is within 24 hours of resolution and has not yet reached your target price. The adverse selection risk near resolution is highest for positions that haven’t moved in your direction.

What compliance rules apply to prediction market bots in the US?

Operating an automated trading bot on US-accessible prediction markets involves regulatory considerations that differ from traditional financial markets. Polymarket operates under a CFTC no-action letter framework and restricts US-based users from trading on its platform directly. Kalshi is a CFTC-regulated designated contract market (DCM), which means it operates under formal federal oversight and its markets are legally accessible to US participants.

Before deploying a bot, confirm the venue’s current terms of service explicitly permit automated trading and API-based order submission. Kalshi’s API terms cover algorithmic access; review them for rate limits, prohibited strategies, and reporting obligations. Polymarket’s terms and geographic restrictions should be reviewed with legal counsel if you are a US-based operator.

For tax purposes, gains from prediction market trading are generally treated as ordinary income or capital gains depending on the holding period and structure, but the classification is not settled law for all venue types. Consult a tax professional familiar with derivatives and prediction market instruments.

Market manipulation, wash trading, and spoofing are prohibited under CFTC rules on regulated venues. Ensure your bot’s strategy does not place and cancel orders in patterns that could be construed as manipulative, even unintentionally. This is a genuine risk for high-frequency strategies with aggressive cancellation logic.

This section is general information, not legal or tax advice. Confirm current rules with a qualified attorney or the relevant regulatory authority before operating a bot in the US market.

How do you test a live trading bot safely before full deployment?

The testing progression for a prediction market bot has four stages, each with a clear exit criterion before advancing.

Stage 1: Unit and integration tests. Every module (signal engine, risk manager, execution client) must have unit tests with mocked API responses. Integration tests should replay a recorded sequence of Assymetrix WebSocket messages and assert that the bot produces the expected signal and risk decisions. This catches logic bugs before any market connection.

Stage 2: Shadow mode on historical data. Run the full bot stack against Assymetrix historical snapshots, simulating fills with your fill model. This is your backtest. Exit criterion: walk-forward stability across three out-of-sample periods, calibrated probabilities, and edge that survives 2x slippage assumptions.

Stage 3: Shadow mode on live data. Connect to the real Assymetrix WebSocket feed and live venue APIs, but with PAPER_MODE=true. The bot runs its full logic, including risk checks, but logs decisions instead of submitting orders. Run for at least two weeks. Exit criterion: signal frequency, confidence distribution, and simulated PnL match backtest expectations within a reasonable tolerance.

Stage 4: Staged live rollout. Start with a single market, minimum position size, and conservative daily loss limit. Increase the market universe and position sizes only after confirming that live fills and PnL match shadow-mode expectations. A gradual rollout surfaces execution-specific issues (partial fills, order book conditions, latency) that shadow mode cannot fully replicate.

Kalshi provides a sandbox environment for API testing. Use it to validate your execution client’s order construction, signing, and fill tracking before connecting to the live exchange. Polymarket’s CLOB has a testnet available for integration testing of the signing and submission flow.

How do you reduce latency in a real-time prediction market bot?

Latency in a prediction market bot has two components: feed latency (time from market event to your bot receiving it) and processing latency (time from receipt to order submission). Both matter, but they require different optimizations.

Feed latency is minimized by using WebSocket subscriptions over REST polling, co-locating your bot geographically close to the data source, and using a single normalized feed (Assymetrix) rather than polling multiple venue APIs in parallel. The Assymetrix WebSocket delivers cross-venue updates in a single stream, eliminating the fan-out latency of polling Polymarket, Kalshi, and Limitless separately.

Processing latency is dominated by your signal engine and risk manager. Keep the hot path synchronous and in-memory: no database reads, no external API calls, no blocking I/O between message receipt and decision output. Pre-load all market metadata, wallet state, and risk parameters at startup. Use asyncio for concurrency rather than threading, which avoids GIL contention on the hot path.

Specific optimizations:

  • Use ujson or orjson instead of the standard json module for message parsing. The difference is measurable at high message rates.

  • Pre-compile signal detection logic where possible; avoid re-instantiating objects on every message.

  • Use asyncio.Queue with a bounded size to apply backpressure when the signal worker falls behind; drop or sample messages rather than letting the queue grow unbounded.

  • Profile with cProfile or py-spy before optimizing. Most bots have one or two hot functions that account for the majority of processing time.

For strategies where millisecond latency matters (end-cycle sniping, for example), consider moving the hot path to a compiled extension using Cython or a Rust extension via PyO3. For most prediction market strategies, Python’s processing speed is not the bottleneck; feed latency and order book conditions dominate.

How do machine learning models improve prediction market signals?

Basic momentum and mean-reversion signals are a starting point, not a ceiling. The AI agent trading guide covers how to wire ML model outputs into a prediction market bot’s signal engine, but the core pattern is consistent: treat the model as a probability estimator, calibrate its output against market prices, and use the calibrated output as one input to an ensemble.

Practical ML approaches for prediction market signal enhancement:

  • Gradient boosting (XGBoost, LightGBM): Train on features derived from Assymetrix historical data: price momentum, volume ratios, cross-venue spread history, time-to-resolution, and Smart Money wallet entry counts. These models handle tabular prediction market features well and are fast to retrain.

  • Sequence models (LSTM, Transformer): Useful for modeling the temporal dynamics of probability evolution within a market’s lifecycle. Train on order book snapshots and price series from Assymetrix historical snapshots.

  • LLM-based probability estimation: Large language models can estimate event probabilities from news text and structured context. The key is calibration: raw LLM probability outputs are poorly calibrated against market prices. Apply temperature scaling or Platt scaling on a held-out validation set before using LLM outputs in a trading signal.

  • Ensemble averaging: Combine gradient boosting, sequence model, and LLM outputs by averaging calibrated probabilities. Reject signals where the standard deviation across ensemble members exceeds a threshold. This is the pattern used in production bots like guberm/polymarket-bot, which implements multi-provider AI ensembles with Kelly sizing.

The critical discipline is out-of-sample validation. Prediction market data has strong temporal structure: training on future data leaks information that the model would not have had in real time. Use strict time-based train/validation/test splits, and always validate on data from a period after your training window ends. Assymetrix’s historical dataset, with nearly one billion rows spanning multiple years, gives you enough data to run meaningful out-of-sample tests across different market regimes.

Feature engineering from Assymetrix data that consistently adds signal: the ratio of Smart Money wallet volume to total market volume, the cross-venue spread history over the last 6 hours, and the rate of change in implied probability relative to the market’s historical volatility.

The part of bot building most guides skip

The conventional framing of prediction market bot development treats the signal engine as the hard problem and execution as a solved one. That framing is wrong, and it costs developers real money.

The execution layer on Polymarket’s CLOB is where most production failures actually occur: stale signals reaching the executor after the order book has moved, partial fills creating positions that don’t match the sizing model, and ambiguous order states after a timeout that result in duplicate submissions. These are not edge cases. They are the normal operating conditions of a thin, event-driven market.

The lesson that shadow mode teaches, if you run it long enough, is that your signal engine is probably fine. The bugs that matter are in the state machine that tracks open orders, handles partial fills, and decides when to cancel versus retry. A bot that generates mediocre signals but executes cleanly and sizes conservatively will outperform a bot with sharp signals and a fragile executor.

Cross-venue data from Assymetrix changes the signal picture materially. A price divergence between Polymarket and Kalshi on the same underlying event is a structurally different signal from a momentum move on a single venue. It has a natural edge estimate (the spread minus transaction costs), a natural exit (convergence), and a natural hedge (long one venue, short the other). That structure makes it far easier to validate in a backtest and far easier to size correctly in live trading.

The developers who build durable prediction market bots are the ones who spend more time on execution state management and risk controls than on signal generation, and who use historical data at the scale Assymetrix provides to validate that their edge is real before committing capital.

Assymetrix gives you the data layer your bot actually needs

Cross-venue prediction market data at the scale required for serious bot development is not something you assemble from venue APIs alone. Assymetrix provides the unified intelligence layer: a real-time WebSocket feed delivering normalized price updates and Smart Money wallet events across Polymarket, Kalshi, and Limitless, and a historical REST API backed by nearly one billion rows of trading activity for backtesting and model training.


Assymetrix

The Assymetrix Data API covers every phase of the bot development pipeline: historical snapshots for microstructure-aware backtests, live WebSocket feeds for shadow and production modes, and wallet tracking endpoints for Smart Money signal generation. The Python developer guide includes endpoint references, authentication examples, and code patterns that map directly to the architecture described in this article.

Start with the free tier at data.assymetrix.com to validate your integration, then upgrade when your strategy requires higher rate limits or bulk historical exports.

Sources

The resources below cover the full implementation path from architecture to deployment.

Venue APIs and official docs:

Open-source bot repos (read these in order):

Where to start based on your goal:

FAQ

What Python libraries do you need to build a Polymarket bot?

The core dependencies are py-clob-client for Polymarket CLOB interaction and EIP-712 signing, websockets for Assymetrix WebSocket feeds, python-dotenv for environment variable management, and structlog for structured logging. Add aiofiles and an async HTTP client (httpx or aiohttp) for REST calls.

How does EIP-712 signing work for Polymarket orders?

EIP-712 is a structured data signing standard for Ethereum. The py-clob-client library handles the signing process: you provide your wallet’s private key, and the client constructs and signs the order payload before submission to the CLOB. Never expose the private key outside an encrypted vault or environment variable.

Can a US-based developer legally run a bot on Kalshi?

Kalshi is a CFTC-regulated designated contract market that permits US participants and explicitly supports API-based algorithmic trading. Review Kalshi’s current API terms of service for permitted strategies and rate limits before deploying. Polymarket restricts US-based users; confirm current geographic restrictions with legal counsel.

How much historical data does Assymetrix provide for backtesting?

Assymetrix’s historical dataset spans nearly one billion rows of trading activity across Polymarket, Kalshi, and Limitless, covering order book snapshots, trade history, and wallet events. The historical REST endpoint at data.assymetrix.com supports paginated queries by market, venue, and time range.

What is the minimum safe testing period before going live?

Run at least two weeks of shadow mode on the live Assymetrix WebSocket feed before enabling live execution. Exit shadow mode only after confirming that signal frequency, confidence distribution, and simulated PnL match your backtest expectations within a reasonable tolerance, and after all risk controls and monitoring alerts are confirmed operational.

Building a Prediction Market Bot in Python: Dev Guide

You can build a production-ready automated prediction market bot in Python by wiring four modular components: a market scanner, a signal engine, an execution engine, and a risk manager, connected to venue APIs (Polymarket’s Gamma and CLOB endpoints, Kalshi’s REST API) and the Assymetrix Data API for real-time WebSocket feeds and historical snapshots.

Before you write a single strategy line, confirm these are in place:

  • Credentials: Polymarket wallet with EIP-712 signing capability, Kalshi API key, and an Assymetrix API key from data.assymetrix.com

  • Core modules: data ingestion layer, signal engine, risk manager, execution client

  • Paper mode: shadow execution enabled before any live capital touches the CLOB

  • Market universe: start with 5–10 high-liquidity markets, not the full catalog

The minimal polling loop that anchors everything looks like this:

async for msg in assymetrix_ws:
    signal = signal_engine.evaluate(msg)
    if risk_manager.approve(signal):
        decision_log.append(signal)  # paper mode: log, don't execute

Pro Tip: Restrict your initial universe to markets with consistent daily volume and at least 30 days of history in Assymetrix. Shadow mode on a narrow universe surfaces structural bugs faster than running across hundreds of thin markets.

Key Takeaways

A production prediction market bot requires modular architecture, microstructure-aware backtesting, and live risk controls before a single dollar of real capital is committed.

| Modular architecture | Separate feed layer, market scanner, signal engine, risk manager, and execution engine for safe iteration and independent testing.

| Shadow mode first | Run at least two weeks of paper-mode simulation on live Assymetrix feed data before enabling live execution. |

| Microstructure backtesting | Use Assymetrix historical snapshots with a hazard-based fill model; naive tick-replay backtests overstate edge on prediction markets. |

| Risk controls are non-negotiable | Implement per-position caps, daily loss limits, and a persistent kill switch before connecting to any live CLOB. |

| Assymetrix for cross-venue data | Use the Assymetrix WebSocket for real-time signals and the historical REST endpoint for backtests; wallet tracking endpoints power Smart Money detection. |



Diagram of prediction market bot modular architecture

Table of Contents

  • How does a prediction market bot architecture work?

  • How do you ingest market data from Polymarket and Assymetrix?

  • What signal strategies work for prediction market bots?

  • How does Polymarket’s CLOB execution work in Python?

  • How do you size positions and control risk in a prediction market bot?

  • How do you backtest a prediction market strategy without fooling yourself?

  • What project structure works for a production prediction market bot?

  • A minimal Python bot that detects a signal and logs a trade decision

  • What should your production bot monitor and alert on?

  • How do you deploy a prediction market bot securely?

  • What are the most common prediction market bot failures?

  • What compliance rules apply to prediction market bots in the US?

  • How do you test a live trading bot safely before full deployment?

  • How do you reduce latency in a real-time prediction market bot?

  • How do machine learning models improve prediction market signals?

  • The part of bot building most guides skip

  • Assymetrix gives you the data layer your bot actually needs

  • Sources

  • FAQ

How does a prediction market bot architecture work?

A well-structured prediction market trading bot separates concerns into five discrete modules. Each module has a single responsibility, which makes testing, hot-swapping strategies, and debugging production failures tractable rather than catastrophic.

Data flow, module by module:

  • Feed layer: Ingests raw market events from Assymetrix WebSocket (cross-venue real-time prices, Smart Money wallet events) and venue REST APIs (Polymarket Gamma for metadata, CLOB for order book state, Kalshi REST for centralized market data). Normalizes schemas before anything downstream sees the data.

  • Market scanner: Filters the active market universe by liquidity thresholds, time-to-resolution windows, and spread constraints. Outputs a candidate list to the signal engine.

  • Signal engine: Consumes normalized market events and candidate lists, runs strategy logic, and emits typed signal objects with a confidence score, suggested direction, and expiry timestamp.

  • Risk manager: Receives signal objects and applies pre-trade checks: position caps, daily loss limits, portfolio exposure, and liquidity depth gates. Approves, modifies, or rejects each signal before it reaches the executor.

  • Execution engine: Translates approved signals into signed orders (EIP-712 on Polymarket, standard REST on Kalshi), submits them, tracks fill status, and handles partial fills and cancellations.

  • Persistence and monitoring layer: Writes all events, decisions, fills, and PnL to durable storage; emits metrics and alerts to your observability stack.

Assymetrix fits into this architecture at two points: the historical REST endpoint powers backtests and warm-up data loads, while the WebSocket feed drives real-time market events and Smart Money wallet signals into the feed layer. Because Assymetrix normalizes schemas across Polymarket, Kalshi, and Limitless, the scanner and signal engine never need per-venue parsing logic.

The key design decision is event-driven over synchronous polling. Polling every market on a fixed interval creates thundering-herd API pressure and adds latency proportional to your polling interval. An event-driven architecture, where the feed layer pushes updates to an internal message queue and workers consume them asynchronously, reduces redundant calls and reacts to market moves within milliseconds of the feed update.

How do you ingest market data from Polymarket and Assymetrix?

Market discovery starts with the Gamma API (Polymarket’s metadata layer). It returns active markets, token IDs, resolution criteria, and order book metadata. The CLOB API handles execution-specific endpoints: live order books, trade history, and order submission. These are separate surfaces with different rate limits and authentication requirements.

For a cross-venue bot, the Assymetrix Data API at data.assymetrix.com is the more practical ingestion point for most signal work. The Python integration guide covers the full endpoint surface, but the three endpoints you’ll use most are:

  • WebSocket feed: Real-time cross-venue price updates, order book deltas, and Smart Money wallet entry events, normalized to a single schema regardless of venue.

  • Historical REST endpoint: Paginated snapshots of trade history, order book states, and price series, drawn from Assymetrix’s dataset of nearly one billion rows of trading activity across venues.

  • Wallet tracking endpoint: Sequence-filtered wallet events for Smart Money detection, returning entry/exit events for tracked addresses with timestamps and position sizes.

When to use venue APIs directly vs. Assymetrix: Use the Gamma API for market metadata that requires Polymarket-specific fields (token IDs for CLOB order construction, resolution source details). Use the CLOB API for order submission and fill tracking. Use Assymetrix for everything involving cross-venue price comparison, historical data, and Smart Money signals — the deduplication and normalization alone eliminate a class of bugs that plague bots pulling from multiple venue APIs simultaneously.

import asyncio, websockets, json, os

ASSYMETRIX_WS = "wss://data.assymetrix.com/ws/v1/markets"
API_KEY = os.environ["ASSYMETRIX_API_KEY"]

async def subscribe():
    async with websockets.connect(
        ASSYMETRIX_WS,
        extra_headers={"Authorization": f"Bearer {API_KEY}"}
    ) as ws:
        await ws.send(json.dumps({"action": "subscribe", "channels": ["prices", "wallets"]}))
        async for raw in ws:
            msg = json.loads(raw)
            yield msg

# Defensive reconnect with exponential backoff
async def resilient_feed(handler, max_backoff=60):
    delay = 1
    while True:
        try:
            async for msg in subscribe():
                delay = 1  # reset on success
                await handler(msg)
        except Exception:
            await asyncio.sleep(delay)
            delay = min(delay * 2, max_backoff)

Keep a lightweight local cache of market metadata — slug, token IDs, resolution date, and last-seen spread — refreshed every few minutes from Gamma. This cuts API calls by an order of magnitude on the hot path and keeps decision latency low.

Pro Tip: Assign a sequence ID to every inbound message and track the last-seen sequence per channel. On reconnect, request a replay from your last sequence ID rather than re-subscribing cold. This prevents missed updates from creating ghost positions.

What signal strategies work for prediction market bots?

The signal engine has four responsibilities: normalize incoming data into features, run strategy logic, score confidence, and emit a typed signal object. Every signal should carry a direction, confidence score (0–1), suggested size, venue, market ID, and an expiry timestamp after which the signal is stale and must be discarded.

Four implementable strategies, in order of complexity:

  1. Momentum on probability changes. Track the rolling 15-minute and 60-minute change in implied probability for each market. A sustained directional move with increasing volume is a momentum signal. Threshold: probability delta exceeds a configurable minimum (e.g., 3 percentage points) with volume above the market’s 7-day median.

  2. Mean reversion to fair value. Estimate fair value from a reference source (news sentiment model, base-rate prior, or ensemble model output). When the market price deviates beyond a spread threshold, fade the move. This works best in liquid markets where the order book recovers quickly.

  3. Cross-venue divergence arbitrage. Compare the same underlying event’s implied probability across Polymarket, Kalshi, and Limitless using the Assymetrix normalized feed. A divergence beyond transaction costs and slippage is an arbitrage signal. The cross-venue arbitrage scanner guide covers the full scanner implementation, including fee-aware edge calculation.

  4. Smart Money wallet detection. Assymetrix’s wallet tracking endpoint surfaces entry events from addresses with high Trader Skill Scores. When a tracked wallet enters a position, filter by: minimum position size, market liquidity threshold, and whether the entry aligns with a concurrent price divergence. Combining wallet entry sequences with cross-venue price data gives a high-signal feature for short-horizon strategies.

def detect_divergence(polymarket_prob: float, kalshi_prob: float,
                      fee_cost: float = 0.02) -> dict | None:
    spread = abs(polymarket_prob - kalshi_prob)
    if spread > fee_cost:
        long_venue = "polymarket" if polymarket_prob < kalshi_prob else "kalshi"
        return {"type": "divergence", "spread": spread, "long_venue": long_venue,
                "confidence": min(spread / 0.10, 1.0)}
    return None

Converting model or LLM outputs into tradable signals requires calibration. Raw model probabilities are rarely well-calibrated against market prices. Apply Platt scaling or isotonic regression on a held-out validation set, then ensemble across multiple models by averaging calibrated probabilities and rejecting signals where the standard deviation across ensemble members exceeds a threshold (e.g., 0.08). Signals that models disagree on are not worth trading.

Signal lifecycle: detect → validate liquidity and spread gates → compute size suggestion → set expiry. If a signal expires before the executor processes it, discard it. Stale signals are one of the most common sources of bad fills in production bots.

How does Polymarket’s CLOB execution work in Python?

Polymarket V2 separates market discovery (Gamma API) from order execution (CLOB API). Order matching happens off-chain; settlement is on-chain. Every order must be signed using EIP-712 before submission. The py-clob-client library handles signing and submission, but you need to understand the order lifecycle to build a reliable executor.

Order lifecycle:

  1. Create: Construct the order object with token ID, side, size, price, and order type (limit or FOK).

  2. Sign: Sign the order payload using EIP-712 with your wallet’s private key. Never expose the key outside an encrypted vault or environment variable.

  3. Submit: POST the signed order to the CLOB endpoint. Receive an order ID.

  4. Track fills: Poll the order status endpoint or subscribe to fill events. Track partial fills separately from full fills.

  5. Handle ambiguous states: If a submission times out, query the order status before retrying. Duplicate submissions on a timed-out order are a common source of unintended double positions.

  6. Cancel stale orders: Any open order older than your signal expiry window should be canceled. Stale limit orders sitting in the book accumulate adverse selection risk.

Execution check

API surface

Notes

Market metadata and token ID

Gamma API

Required before order construction

Live order book depth

CLOB API

Check before sizing

Order creation and signing

py-clob-client

EIP-712 required

Order status and fills

CLOB API

Poll or subscribe

Order cancellation

CLOB API

Cancel on signal expiry

Settlement reconciliation

On-chain / Chainlink

Post-close PnL finalization

For thin order books, prefer limit maker orders over FOK taker flows. Maker orders add liquidity and typically pay lower fees; FOK orders guarantee fill but at the cost of crossing the spread. For larger entries, split across multiple price levels using a VWAP-style entry to reduce market impact.

Kalshi operates a centralized exchange with a standard REST API, no on-chain signing required. The execution logic is simpler, but the market universe and liquidity profile differ. For a data-driven comparison of venue tradeoffs, the key difference is that Kalshi’s centralized model means faster order acknowledgment but no on-chain settlement guarantee.

Pro Tip: Re-evaluate your edge at submission time, not just at signal generation time. Fetch the current best bid/ask from the CLOB immediately before posting the order. If the spread has widened beyond your slippage budget since the signal fired, abort the order.

How do you size positions and control risk in a prediction market bot?

Risk management is not a feature you add after the bot works. It is the first module you write, because a bug in the signal engine loses one trade; a bug in the risk manager can lose the entire bankroll.

Default guardrails to implement before any live trading:

  • Max position size per market: fixed USD cap (e.g., $50 per position in early testing)

  • Max portfolio exposure: total open positions not to exceed a percentage of bankroll (e.g., 20%)

  • Max concurrent positions: hard limit on open trades (e.g., 10)

  • Daily loss limit: halt all new orders if realized + unrealized loss exceeds a threshold (e.g., 5% of bankroll)

  • Drawdown stop: pause the bot if peak-to-trough drawdown exceeds a configurable level

Fractional Kelly sizing is the standard approach for prediction market bots. Full Kelly is theoretically optimal but practically dangerous due to estimation error in edge. Use a fraction (typically 0.25–0.5 Kelly):

def kelly_fraction(prob: float, odds: float, fraction: float = 0.25) -> float:
    """
    prob: estimated win probability
    odds: decimal odds (payout / stake), e.g. 1/market_price for binary
    fraction: Kelly fraction (0.25 = quarter Kelly)
    """
    edge = prob * odds - 1
    if edge <= 0:
        return 0.0
    kelly = edge / (odds - 1)
    return kelly * fraction

Pre-trade checks run before every order: minimum liquidity depth at the target price level, order book age (reject if the last update is older than N seconds), and fill probability estimate based on current spread. Post-trade checks reconcile actual fill price against expected price and flag large slippage events for review.

Kill switches must be persistent across process restarts. Write the halt state to your database, not just in-memory. If the bot crashes and restarts, it should read the halt flag before placing any orders. Manual override requires a separate authenticated command, not just restarting the process.

Pro Tip: Set your daily loss limit conservatively for the first two weeks of live trading. A limit that feels too tight is correct. You can always widen it after you’ve validated that fills and PnL match your backtest expectations.

How do you backtest a prediction market strategy without fooling yourself?

Naive backtests on prediction markets produce systematically optimistic results for three structural reasons: resolution timing creates a concentration of volume near market close that doesn’t exist mid-life, liquidity is sparse and order book depth fluctuates sharply, and fill assumptions that treat every limit order as filled at the quoted price are unrealistic.

The Homerun framework demonstrates the right approach: L2 order book replay with a hazard-based fill model. Instead of assuming your limit order fills whenever the price touches your level, a Cox-proportional hazard fill model estimates fill probability as a function of your queue position, order size relative to depth, and time elapsed. This materially changes expected edge versus tick-replay backtests.

Assymetrix’s historical dataset, spanning nearly one billion rows of trading activity, gives you the order book snapshots and trade history needed to run this kind of replay. The backtesting methodology guide covers the REST endpoints for pulling historical snapshots by market, time range, and venue.

Shadow mode is the bridge between backtest and live. Run your bot with identical logic, real market data from the Assymetrix feed, and simulated fills using the same fill model as your backtest. The same risk gates apply. The only difference is that orders are logged rather than submitted. Two weeks of shadow mode on your target market universe will surface timing bugs, fill model errors, and signal expiry issues that backtests miss.

Pre-deployment validation checklist:

  • Walk-forward stability: strategy must hold edge across at least three non-overlapping out-of-sample periods

  • Calibration: predicted probabilities must be well-calibrated against realized outcomes (Brier score below a threshold)

  • Slippage testing: re-run backtest with 1.5x and 2x assumed slippage; edge must survive

  • Fill model sensitivity: vary fill probability assumptions; strategy must not depend on optimistic fill rates

  • Statistical significance: edge must be distinguishable from noise across the full backtest period

Pro Tip: Use Assymetrix’s historical wallet data in your backtest warm-up. Load the last 30 days of Smart Money wallet events before your backtest start date so the signal engine has a populated wallet state from day one, rather than a cold start that artificially suppresses Smart Money signals in the early backtest period.

What project structure works for a production prediction market bot?

Event-driven architecture is the right pattern for a production bot. The core flow: market update arrives at the feed layer, gets pushed to an internal message queue (Redis Streams or asyncio.Queue for single-process), consumed by a signal worker, checked by the risk manager, and dispatched to an execution worker. Each worker runs independently; a crash in the signal worker does not take down the executor.

Recommended package layout:

bot/
├── data/           # Feed clients, WebSocket, REST wrappers, local cache
├── strategies/     # Signal engine, individual strategy modules
├── execution/      # Order construction, signing, CLOB/Kalshi clients
├── risk/           # Pre-trade checks, sizing, kill switch, state
├── persistence/    # DB writers, JSONL event log, trade records
├── monitoring/     # Metrics emission, alert rules, health checks
├── config/         # YAML/env config, market universe definitions
└── main.py         # Orchestration entry point

Numbered orchestration steps for a clean startup sequence:

  1. Load config and environment variables; validate all required keys are present.

  2. Connect to Assymetrix WebSocket feed and warm up local market cache from Gamma API.

  3. Load historical context (last N days of wallet events, price history) from Assymetrix REST.

  4. Start risk manager and load persisted halt state from the database.

  5. Start signal workers and execution workers as separate async tasks or processes.

  6. Enable monitoring and heartbeat emission before processing any live events.

Storage choices: Time-series database (InfluxDB or TimescaleDB) for price snapshots and metrics. PostgreSQL or SQLite for trade records, fill history, and risk state. JSONL files for raw event logs, which are cheap to write and easy to replay for debugging.

Orchestration: Docker with process supervision (supervisord or systemd inside the container) for single-host deployments. For multi-strategy or multi-venue bots, a lightweight Kubernetes deployment with one pod per worker type gives independent scaling and restart policies without the overhead of a full service mesh.

A minimal Python bot that detects a signal and logs a trade decision

This walkthrough connects to the Assymetrix WebSocket feed, detects a cross-venue price divergence or Smart Money wallet entry, and logs a trade decision. No live orders are submitted. This is the correct starting point: validate the signal logic before touching the CLOB.

Environment template (.env):

ASSYMETRIX_API_KEY=your_key_here
POLYMARKET_PRIVATE_KEY=your_wallet_private_key
KALSHI_API_KEY=your_kalshi_key
PAPER_MODE=true
MIN_DIVERGENCE=0.04
SMART_MONEY_MIN_SIZE=500
LOG_LEVEL=INFO

Dependencies:

pip install websockets python-dotenv aiofiles structlog

Minimal bot (main.py):

import asyncio, json, os, structlog
from dotenv import load_dotenv
import websockets

load_dotenv()
log = structlog.get_logger()

API_KEY = os.environ["ASSYMETRIX_API_KEY"]
MIN_DIV = float(os.getenv("MIN_DIVERGENCE", "0.04"))
SM_MIN = float(os.getenv("SMART_MONEY_MIN_SIZE", "500"))
PAPER = os.getenv("PAPER_MODE", "true").lower() == "true"

WS_URL = "wss://data.assymetrix.com/ws/v1/markets"

# --- Signal detection ---
def check_divergence(msg: dict) -> dict | None:
    if msg.get("type") != "price_update":
        return None
    prices = msg.get("venues", {})
    poly = prices.get("polymarket")
    kalshi = prices.get("kalshi")
    if poly is None or kalshi is None:
        return None
    spread = abs(poly - kalshi)
    if spread >= MIN_DIV:
        return {
            "signal": "divergence",
            "market_id": msg["market_id"],
            "spread": round(spread, 4),
            "long_venue": "polymarket" if poly < kalshi else "kalshi",
            "confidence": min(spread / 0.10, 1.0),
        }
    return None

def check_smart_money(msg: dict) -> dict | None:
    if msg.get("type") != "wallet_event":
        return None
    if msg.get("size_usd", 0) >= SM_MIN:
        return {
            "signal": "smart_money",
            "market_id": msg["market_id"],
            "wallet": msg["wallet"],
            "side": msg["side"],
            "size_usd": msg["size_usd"],
            "confidence": 0.7,
        }
    return None

# --- Risk gate stub ---
def risk_approve(signal: dict) -> bool:
    # Replace with full risk manager checks
    return signal.get("confidence", 0) >= 0.5

# --- Main loop ---
async def run():
    delay = 1
    while True:
        try:
            async with websockets.connect(
                WS_URL,
                extra_headers={"Authorization": f"Bearer {API_KEY}"}
            ) as ws:
                await ws.send(json.dumps({
                    "action": "subscribe",
                    "channels": ["prices", "wallets"]
                }))
                log.info("connected", paper=PAPER)
                delay = 1
                async for raw in ws:
                    msg = json.loads(raw)
                    for detector in [check_divergence, check_smart_money]:
                        signal = detector(msg)
                        if signal and risk_approve(signal):
                            log.info("trade_decision", **signal, paper=PAPER)
                            # In paper mode: log only. In live mode: pass to executor.
        except Exception as e:
            log.warning("reconnecting", error=str(e), backoff=delay)
            await asyncio.sleep(delay)
            delay = min(delay * 2, 60)

if __name__ == "__main__":
    asyncio.run(run())

The warm-up step (loading recent price history and wallet events from the Assymetrix historical REST endpoint before the WebSocket connects) is omitted here for brevity but is required in production. Without warm-up, the signal engine starts cold and will miss context-dependent signals for the first several minutes.

Pro Tip: Run this script with PAPER_MODE=true for at least two weeks before changing the flag. Watch the structured log output for signal frequency, confidence distribution, and any markets that generate an implausible number of signals — those are usually data quality issues, not alpha.

What should your production bot monitor and alert on?

Monitoring is not optional for a bot that touches live capital. The minimum viable observability stack covers three layers: metrics, logs, and alerts.

Essential metrics to track:

  • API latency per endpoint (Assymetrix WebSocket lag, CLOB response time)

  • Order failure rate and fill rate by strategy and venue

  • PnL by strategy, by market, and portfolio-level (realized and unrealized)

  • Open exposure as a percentage of bankroll

  • Queue depth and processing lag (signal worker backlog)

  • Ghost positions: open positions with no corresponding active signal

Logging practices: Use structured logging (structlog or Python’s logging with JSON formatter). Assign a correlation ID to every market event and propagate it through signal detection, risk check, and execution. This makes postmortem analysis tractable. Retain raw event logs (JSONL) for at least 90 days.

Alert rules and runbook triggers:

  1. Heartbeat missing for more than 60 seconds: page immediately, check feed connection.

  2. Daily loss limit breached: halt bot, send alert, require manual review before restart.

  3. Fill rate below threshold for more than 30 minutes: investigate order book conditions and slippage.

  4. Settlement mismatch: actual settlement differs from bot’s recorded position; reconcile against on-chain data and Chainlink settlement reference.

  5. Large slippage event: any fill more than 2x expected slippage; log for strategy review.

  6. Queue lag exceeding 10 seconds: signal worker is falling behind; scale or throttle ingestion.

For the monitoring stack, Prometheus with Grafana covers metrics and dashboards. For alerts, PagerDuty or a Telegram bot (common in open-source prediction market bot repos) handles notifications. Define service level objectives (SLOs) before going live, such as aiming for high uptime for the feed connection and maintaining a strong fill rate.% for limit orders in liquid markets.

How do you deploy a prediction market bot securely?

Deployment security starts with the assumption that your private key is the most valuable asset in the system. Compromise it and every position, every wallet balance, is at risk.

Docker and process supervision:

  • Use a minimal base image (python:3.12-slim) and run as a non-root user.

  • Pin all dependency versions in requirements.txt and rebuild images on dependency updates.

  • Use supervisord or a process manager inside the container to restart workers on crash without restarting the entire container.

Secrets handling:

  1. Never store private keys or API keys in the repository, in plaintext config files, or in Docker image layers.

  2. Use HashiCorp Vault, AWS KMS, or GCP Secret Manager for key storage in production.

  3. For local development, use a .env file excluded from version control via .gitignore.

  4. Rotate API keys on a schedule and immediately after any suspected exposure.

Environment separation:

  • Maintain distinct wallets and API keys for shadow/paper and live environments.

  • Use a DRY_RUN environment variable that the execution engine checks before every order submission.

  • For large trades (above a configurable threshold), require a human confirmation step before the order posts. This is a guardrail against runaway sizing bugs.

Post-deploy security checklist:

  1. Confirm no secrets appear in container environment variable dumps or logs.

  2. Verify the kill switch persists correctly across a simulated process restart.

  3. Run a test order in paper mode and confirm it does not reach the live CLOB.

  4. Check that API key permissions are scoped to the minimum required (read-only where execution is not needed).

  5. Enable drift detection: alert if the running container image differs from the pinned version in your deployment manifest.

What are the most common prediction market bot failures?

Liquidity thinness is the most frequent cause of poor live performance relative to backtests. Prediction markets, even on Polymarket, have order books that can be thin outside of high-interest events. Mitigations: require a minimum executable depth at your target price before sizing, restrict the market universe to markets with consistent daily volume, and use VWAP entry for any position above a minimum size threshold.

Slippage and partial fills compound the liquidity problem. A limit order that fills partially leaves you with a position that doesn’t match your sizing model. Track partial fills explicitly, decide whether to complete the position or cancel the remainder, and never assume a submitted order is fully filled until the fill event confirms it.

Oracle and resolution risk is specific to prediction markets. A market can resolve differently from what the order book implied, particularly for events with ambiguous outcomes or delayed resolution. Open-source bots address this by aligning with Chainlink settlement data as a reference and reconciling official settlement post-close. Treat any market with a non-standard resolution source conservatively: reduce position size and widen your edge threshold.

API rate limits hit bots that poll aggressively across large market universes. Mitigations: cache market metadata locally, use exponential backoff on 429 responses, stagger polling intervals across markets, and prefer WebSocket subscriptions over REST polling for real-time data. Assymetrix’s unified feed reduces the number of venue API calls required by consolidating cross-venue data into a single stream.

Pro Tip: For resolution risk, set a position expiry rule: automatically close or reduce any position that is within 24 hours of resolution and has not yet reached your target price. The adverse selection risk near resolution is highest for positions that haven’t moved in your direction.

What compliance rules apply to prediction market bots in the US?

Operating an automated trading bot on US-accessible prediction markets involves regulatory considerations that differ from traditional financial markets. Polymarket operates under a CFTC no-action letter framework and restricts US-based users from trading on its platform directly. Kalshi is a CFTC-regulated designated contract market (DCM), which means it operates under formal federal oversight and its markets are legally accessible to US participants.

Before deploying a bot, confirm the venue’s current terms of service explicitly permit automated trading and API-based order submission. Kalshi’s API terms cover algorithmic access; review them for rate limits, prohibited strategies, and reporting obligations. Polymarket’s terms and geographic restrictions should be reviewed with legal counsel if you are a US-based operator.

For tax purposes, gains from prediction market trading are generally treated as ordinary income or capital gains depending on the holding period and structure, but the classification is not settled law for all venue types. Consult a tax professional familiar with derivatives and prediction market instruments.

Market manipulation, wash trading, and spoofing are prohibited under CFTC rules on regulated venues. Ensure your bot’s strategy does not place and cancel orders in patterns that could be construed as manipulative, even unintentionally. This is a genuine risk for high-frequency strategies with aggressive cancellation logic.

This section is general information, not legal or tax advice. Confirm current rules with a qualified attorney or the relevant regulatory authority before operating a bot in the US market.

How do you test a live trading bot safely before full deployment?

The testing progression for a prediction market bot has four stages, each with a clear exit criterion before advancing.

Stage 1: Unit and integration tests. Every module (signal engine, risk manager, execution client) must have unit tests with mocked API responses. Integration tests should replay a recorded sequence of Assymetrix WebSocket messages and assert that the bot produces the expected signal and risk decisions. This catches logic bugs before any market connection.

Stage 2: Shadow mode on historical data. Run the full bot stack against Assymetrix historical snapshots, simulating fills with your fill model. This is your backtest. Exit criterion: walk-forward stability across three out-of-sample periods, calibrated probabilities, and edge that survives 2x slippage assumptions.

Stage 3: Shadow mode on live data. Connect to the real Assymetrix WebSocket feed and live venue APIs, but with PAPER_MODE=true. The bot runs its full logic, including risk checks, but logs decisions instead of submitting orders. Run for at least two weeks. Exit criterion: signal frequency, confidence distribution, and simulated PnL match backtest expectations within a reasonable tolerance.

Stage 4: Staged live rollout. Start with a single market, minimum position size, and conservative daily loss limit. Increase the market universe and position sizes only after confirming that live fills and PnL match shadow-mode expectations. A gradual rollout surfaces execution-specific issues (partial fills, order book conditions, latency) that shadow mode cannot fully replicate.

Kalshi provides a sandbox environment for API testing. Use it to validate your execution client’s order construction, signing, and fill tracking before connecting to the live exchange. Polymarket’s CLOB has a testnet available for integration testing of the signing and submission flow.

How do you reduce latency in a real-time prediction market bot?

Latency in a prediction market bot has two components: feed latency (time from market event to your bot receiving it) and processing latency (time from receipt to order submission). Both matter, but they require different optimizations.

Feed latency is minimized by using WebSocket subscriptions over REST polling, co-locating your bot geographically close to the data source, and using a single normalized feed (Assymetrix) rather than polling multiple venue APIs in parallel. The Assymetrix WebSocket delivers cross-venue updates in a single stream, eliminating the fan-out latency of polling Polymarket, Kalshi, and Limitless separately.

Processing latency is dominated by your signal engine and risk manager. Keep the hot path synchronous and in-memory: no database reads, no external API calls, no blocking I/O between message receipt and decision output. Pre-load all market metadata, wallet state, and risk parameters at startup. Use asyncio for concurrency rather than threading, which avoids GIL contention on the hot path.

Specific optimizations:

  • Use ujson or orjson instead of the standard json module for message parsing. The difference is measurable at high message rates.

  • Pre-compile signal detection logic where possible; avoid re-instantiating objects on every message.

  • Use asyncio.Queue with a bounded size to apply backpressure when the signal worker falls behind; drop or sample messages rather than letting the queue grow unbounded.

  • Profile with cProfile or py-spy before optimizing. Most bots have one or two hot functions that account for the majority of processing time.

For strategies where millisecond latency matters (end-cycle sniping, for example), consider moving the hot path to a compiled extension using Cython or a Rust extension via PyO3. For most prediction market strategies, Python’s processing speed is not the bottleneck; feed latency and order book conditions dominate.

How do machine learning models improve prediction market signals?

Basic momentum and mean-reversion signals are a starting point, not a ceiling. The AI agent trading guide covers how to wire ML model outputs into a prediction market bot’s signal engine, but the core pattern is consistent: treat the model as a probability estimator, calibrate its output against market prices, and use the calibrated output as one input to an ensemble.

Practical ML approaches for prediction market signal enhancement:

  • Gradient boosting (XGBoost, LightGBM): Train on features derived from Assymetrix historical data: price momentum, volume ratios, cross-venue spread history, time-to-resolution, and Smart Money wallet entry counts. These models handle tabular prediction market features well and are fast to retrain.

  • Sequence models (LSTM, Transformer): Useful for modeling the temporal dynamics of probability evolution within a market’s lifecycle. Train on order book snapshots and price series from Assymetrix historical snapshots.

  • LLM-based probability estimation: Large language models can estimate event probabilities from news text and structured context. The key is calibration: raw LLM probability outputs are poorly calibrated against market prices. Apply temperature scaling or Platt scaling on a held-out validation set before using LLM outputs in a trading signal.

  • Ensemble averaging: Combine gradient boosting, sequence model, and LLM outputs by averaging calibrated probabilities. Reject signals where the standard deviation across ensemble members exceeds a threshold. This is the pattern used in production bots like guberm/polymarket-bot, which implements multi-provider AI ensembles with Kelly sizing.

The critical discipline is out-of-sample validation. Prediction market data has strong temporal structure: training on future data leaks information that the model would not have had in real time. Use strict time-based train/validation/test splits, and always validate on data from a period after your training window ends. Assymetrix’s historical dataset, with nearly one billion rows spanning multiple years, gives you enough data to run meaningful out-of-sample tests across different market regimes.

Feature engineering from Assymetrix data that consistently adds signal: the ratio of Smart Money wallet volume to total market volume, the cross-venue spread history over the last 6 hours, and the rate of change in implied probability relative to the market’s historical volatility.

The part of bot building most guides skip

The conventional framing of prediction market bot development treats the signal engine as the hard problem and execution as a solved one. That framing is wrong, and it costs developers real money.

The execution layer on Polymarket’s CLOB is where most production failures actually occur: stale signals reaching the executor after the order book has moved, partial fills creating positions that don’t match the sizing model, and ambiguous order states after a timeout that result in duplicate submissions. These are not edge cases. They are the normal operating conditions of a thin, event-driven market.

The lesson that shadow mode teaches, if you run it long enough, is that your signal engine is probably fine. The bugs that matter are in the state machine that tracks open orders, handles partial fills, and decides when to cancel versus retry. A bot that generates mediocre signals but executes cleanly and sizes conservatively will outperform a bot with sharp signals and a fragile executor.

Cross-venue data from Assymetrix changes the signal picture materially. A price divergence between Polymarket and Kalshi on the same underlying event is a structurally different signal from a momentum move on a single venue. It has a natural edge estimate (the spread minus transaction costs), a natural exit (convergence), and a natural hedge (long one venue, short the other). That structure makes it far easier to validate in a backtest and far easier to size correctly in live trading.

The developers who build durable prediction market bots are the ones who spend more time on execution state management and risk controls than on signal generation, and who use historical data at the scale Assymetrix provides to validate that their edge is real before committing capital.

Assymetrix gives you the data layer your bot actually needs

Cross-venue prediction market data at the scale required for serious bot development is not something you assemble from venue APIs alone. Assymetrix provides the unified intelligence layer: a real-time WebSocket feed delivering normalized price updates and Smart Money wallet events across Polymarket, Kalshi, and Limitless, and a historical REST API backed by nearly one billion rows of trading activity for backtesting and model training.


Assymetrix

The Assymetrix Data API covers every phase of the bot development pipeline: historical snapshots for microstructure-aware backtests, live WebSocket feeds for shadow and production modes, and wallet tracking endpoints for Smart Money signal generation. The Python developer guide includes endpoint references, authentication examples, and code patterns that map directly to the architecture described in this article.

Start with the free tier at data.assymetrix.com to validate your integration, then upgrade when your strategy requires higher rate limits or bulk historical exports.

Sources

The resources below cover the full implementation path from architecture to deployment.

Venue APIs and official docs:

Open-source bot repos (read these in order):

Where to start based on your goal:

FAQ

What Python libraries do you need to build a Polymarket bot?

The core dependencies are py-clob-client for Polymarket CLOB interaction and EIP-712 signing, websockets for Assymetrix WebSocket feeds, python-dotenv for environment variable management, and structlog for structured logging. Add aiofiles and an async HTTP client (httpx or aiohttp) for REST calls.

How does EIP-712 signing work for Polymarket orders?

EIP-712 is a structured data signing standard for Ethereum. The py-clob-client library handles the signing process: you provide your wallet’s private key, and the client constructs and signs the order payload before submission to the CLOB. Never expose the private key outside an encrypted vault or environment variable.

Can a US-based developer legally run a bot on Kalshi?

Kalshi is a CFTC-regulated designated contract market that permits US participants and explicitly supports API-based algorithmic trading. Review Kalshi’s current API terms of service for permitted strategies and rate limits before deploying. Polymarket restricts US-based users; confirm current geographic restrictions with legal counsel.

How much historical data does Assymetrix provide for backtesting?

Assymetrix’s historical dataset spans nearly one billion rows of trading activity across Polymarket, Kalshi, and Limitless, covering order book snapshots, trade history, and wallet events. The historical REST endpoint at data.assymetrix.com supports paginated queries by market, venue, and time range.

What is the minimum safe testing period before going live?

Run at least two weeks of shadow mode on the live Assymetrix WebSocket feed before enabling live execution. Exit shadow mode only after confirming that signal frequency, confidence distribution, and simulated PnL match your backtest expectations within a reasonable tolerance, and after all risk controls and monitoring alerts are confirmed operational.