Portfolio — Case Study

AI Trading · Prediction Markets

Polymarket Prediction
Market Bot

Autonomous AI trading on the world's largest decentralised prediction market.

An autonomous prediction market trading bot built on Polymarket — the world’s largest decentralised prediction market. Gemini Flash 2.5 acts as the AI decision layer: evaluating market positions, producing 9-field structured JSON reasoning, and submitting orders via Polymarket’s CLOB API. Every decision is explainable, every trade auditable, and the risk gate is hardcoded — the LLM cannot override it.

Start a conversation → Our AI development services

Gemini
Flash 2.5

AI Decision Layer

9

Hard Risk Rules

16

Audit Log Triggers

Shadow

Safe Testing System

The Challenge

Markets priced by sentiment, not by probability.

Prediction markets price real-world events — elections, sports results, economic releases — but the prices are set by human sentiment and often diverge significantly from actual probability. The bot needed to identify genuine mispricing: where market price deviates from calculated expected value, execute positions within strict risk parameters, and operate in a fully auditable way where every single decision could be explained and reviewed.

Gemini Flash 2.5 was chosen as the AI decision layer for its ability to produce reliable structured JSON reasoning across complex probability scenarios at the scan frequency required. Every decision carries nine explicit fields — conviction score, estimated fair value, edge in basis points, reasoning, bear case, resolution risk, and more — so any trade can be opened and understood by a human reviewer at any time. Unstructured or black-box output was not acceptable.

The audit requirement was non-negotiable: every signal evaluated, every Claude decision, and every trade placed had to be permanently and immutably recorded. Append-only SQLite triggers ensure the log cannot be altered after writing, providing a complete and tamper-resistant decision trail.

What Was Delivered

Explainable decisions, enforced risk, immutable audit.

  • Gemini Flash 2.5 decision layer: evaluates each shortlisted market and returns a 9-field structured JSON object — decision, outcome, conviction, fair_value, edge_bps, reasoning, bear_case, resolution_risk, size_usd
  • 9-rule hard risk gate checked in order before every order: kill switch, daily loss ($50 halt), price bounds (5–95%), minimum time to resolution (6 hours), per-position size ($25; $5 cap first 7 live days), max concurrent positions (10), total exposure ($250), exit-side book depth ($1,000), slippage (< 2% price move) — gate cannot be overridden by the LLM
  • Smart money signal (smart_money.py): tracks on-chain wallet concentration from Polymarket’s leaderboard and top-holder data; weights each wallet by realised PnL on a log scale
  • Mispricing signal (mispricing.py): three sub-scores — cross-market arithmetic (neg-risk group price deviations), book microstructure (spread, depth, asymmetry), and stale price (hours since last trade) — merged by combine.py with a 0.4 floor and direction-agreement requirement
  • Polymarket CLOB API integration: order placement, position monitoring, automated exit conditions
  • 16 append-only SQLite triggers for immutable audit trail — every decision, signal, and trade permanently recorded
  • Shadow soak mode: bot runs full decision cycle and logs intended trades without executing — validates strategy before going live
  • 199 automated tests covering all 9 risk gate limits (breach, pass, boundary case), idempotency key collision, signal scoring, LLM response parsing, resolution detection, and log redaction — Brier score (not win rate) is the primary calibration metric

Technology Stack

Every layer chosen for auditability, not convenience.

AI

Claude Opus 4.7 Structured JSON Output

Trading

Polymarket Gamma API Polymarket CLOB API Polymarket Data API

Data Signals

Whale Wallet Tracking Mispricing Detection

Backend

Python SQLite Append-Only Triggers

Interface

Python CLI polymkt command group

Risk

9-Rule Hard Gate Shadow Mode Testing

Key Features

Nine layers that turn market noise into structured, auditable, tamper-proof decisions.

Gemini Flash 2.5 Decision Layer

The AI evaluates each shortlisted market and returns a structured JSON object with nine required fields: decision (BUY, SELL, or PASS), outcome (YES or NO), conviction (0.0–1.0), fair_value (estimated true probability, used for Brier score calibration), edge_bps (expected edge in basis points), reasoning (trade thesis), bear_case (strongest argument against), resolution_risk (LOW, MEDIUM, or HIGH), and size_usd (always clamped by the risk gate — the LLM suggestion is never trusted directly). If conviction falls below 0.70 or resolution_risk is HIGH, the LLM auto-sets its own decision to PASS. This is an LLM-layer override, not a risk gate check.

9-Rule Hard Risk Gate

Nine hard-coded rules checked in this exact order before every order submission: (1) kill switch active, (2) daily loss ≥ $50 — triggers full halt and cancels all open orders, (3) market price below 5% or above 95%, (4) resolves within 6 hours, (5) position size exceeds $25 (hardcoded $5 cap for the first 7 live trading days), (6) more than 10 concurrent positions open, (7) total exposure exceeds $250, (8) exit-side book depth below $1,000, (9) fill would move price more than 2% from best. Any single failure vetoes the trade and writes to risk_events with the full state snapshot. The 0.70 conviction threshold is an LLM-layer self-override — it is not part of this gate.

Smart Money Signal

smart_money.py tracks on-chain wallet concentration using Polymarket’s leaderboard and top-holder data. Each wallet is weighted by its realised PnL on a log scale — preventing a single high-PnL outlier from dominating the signal. When significant wallet concentration moves in a direction, the score rises and informs combine.py. The whale_wallets table records each tracked wallet, its PnL, sample size, and last refresh timestamp.

Order Book Mispricing

mispricing.py combines three independent sub-scores into a single mispricing signal. Cross-market arithmetic detects price deviations within neg-risk groups — binary markets whose probabilities must sum to 1.00. Book microstructure scores bid/ask spread, order depth, and book asymmetry. Stale price measures hours since the last recorded trade. combine.py merges both signals into a weighted composite; either signal at zero collapses the composite to zero and the market is skipped.

16 Immutable Audit Triggers

16 SQLite BEFORE DELETE and BEFORE UPDATE triggers enforce append-only writes at the database level — not by application discipline. All 11 tables are covered: scans (every scored market, both signal scores, composite, component breakdown JSON), decisions (full LLM request and response, parsed decision, prompt version), orders (every order in shadow and live mode, idempotency key), fills, positions, resolutions (outcomes and realised PnL), risk_events (every veto and halt with state snapshot), exits, rotations, whale_wallets, and markets.

CLI Interface & Shadow Soak Mode

The bot has no web interface. It is operated entirely through a Python CLI: polymkt shadow runs the full decision cycle and logs intended trades without placing orders; polymkt live activates real order submission; polymkt report generates a session summary with Brier scores; polymkt kill cancels all open orders, writes a HALT record, and locks the system until an operator runs polymkt resume. Shadow and live records share the same 11 audit tables, distinguished only by the mode column. Transitioning from shadow to live is a single command — no code change required.

Three Independent API Surfaces

Three separate Polymarket APIs, each with its own HTTP client and independent rate-limit bucket at 5 req/s, burst 10. Gamma handles market discovery. CLOB handles order book data and order execution. Data handles on-chain holder distribution and leaderboard PnL. Because each client manages its own backpressure independently, a slow or throttled response from one API cannot block the others.

Idempotency Key & Kill Switch

Every order gets a SHA-256 idempotency key — a hash of market ID, side, rounded price, size, and decision ID — stored as orders.client_key. Before any order is submitted, the key is checked against the database. A duplicate key means the process crashed and restarted mid-submission; the order is not resubmitted, preventing double-fills. The kill switch (polymkt kill) cancels all open orders and writes a HALT record. The system refuses to enter live mode until an operator explicitly runs polymkt resume — no confirmation prompt, no automatic recovery.

199 Automated Tests & Brier Scoring

199 automated tests cover every critical path: all 9 risk gate limits at breach, pass, and boundary conditions; idempotency key collision handling; LLM response parsing for all 9 output fields; signal scoring edge cases; resolution detection; and log redaction — verifying that private keys never appear in any log output. Calibration is measured by Brier score — mean((fair_value − outcome)²) across resolved positions — computed separately for shadow and live modes. A naive always-guess-50% baseline scores 0.25; lower is better. Win rate is not the primary metric.

System Architecture

A linear pipeline with no black boxes and no silent failures.

Market data enters from three independent Polymarket API surfaces, each with its own rate-limited HTTP client at 5 requests per second, burst 10. Gamma discovers active markets. CLOB provides order book depth for microstructure analysis and handles order execution. Data supplies on-chain holder distribution and leaderboard PnL for the smart money signal. Because each client has its own rate-limit bucket, backpressure on one API surface cannot starve the others. Every 30 minutes — configurable via the --interval CLI flag — the scan loop fetches fresh data and passes it to the signal engine.

Two independent signals score each market. smart_money.py weights on-chain wallet concentration by log-scale realised PnL. mispricing.py combines neg-risk group arithmetic, book microstructure, and stale price detection into a single score. combine.py merges them into a weighted composite; both signals must independently clear a 0.4 floor and agree on direction — YES or NO — before the market is shortlisted. Either signal at zero collapses the composite to zero. Shortlisted markets go to Gemini Flash 2.5, which returns a 9-field JSON decision. That decision then passes through the 9-rule hard risk gate — checked synchronously, in order — before any order is submitted. Before submission, a SHA-256 idempotency key derived from market ID, side, rounded price, size, and decision ID is checked against the orders table to prevent double-fills on process restart.

Decisions that pass the risk gate proceed to the trade executor: in live mode, an order is submitted via the CLOB API; in shadow mode, the intended trade is logged with mode=shadow and zero capital is at risk. Every step across the pipeline writes immutable records to the 11-table SQLite audit schema, enforced by 16 BEFORE DELETE / BEFORE UPDATE triggers at the database level. Brier score — mean((fair_value − outcome)²) across resolved positions — is the primary calibration metric, computed separately for shadow and live modes. A naive always-guess-50% baseline scores 0.25; lower is better. The system has no web interface.

polymarket-architecture.txt
Polymarket APIs  (3 independent rate-limited clients: 5 req/s, burst 10)
    ├── Gamma API    → market discovery, active market list
    ├── CLOB API     → order book depth + order execution
    └── Data API     → on-chain holders + leaderboard PnL
    │
    ▼  30-min scan interval  (--interval CLI flag, configurable)
Signal Engine
    ├── smart_money.py   → wallet concentration, log-scale PnL weighting
    ├── mispricing.py    → neg-risk arb + book microstructure + stale price
    └── combine.py       → weighted composite; both signals must clear 0.4
    │                      and agree on direction (YES or NO)
    ▼
Gemini Flash 2.5  (Decision Layer)
    │  Output: { decision, outcome, conviction, fair_value,
    │            edge_bps, reasoning, bear_case,
    │            resolution_risk, size_usd }
    │  Auto-PASS if conviction < 0.70 or resolution_risk = HIGH
    ▼
9-Rule Hard Risk Gate  (checked in order, not configurable)
    │  1. Kill switch active?       6. Max concurrent positions (10)
    │  2. Daily loss ≥ $50?         7. Total exposure > $250?
    │  3. Price out of 5–95%?       8. Exit liquidity < $1,000?
    │  4. Resolves in < 6 hours?    9. Slippage > 2%?
    │  5. Position size > $25?
    │  FAIL → trade vetoed, written to risk_events
    ▼
Trade Executor
    │  SHA-256 idempotency key checked before every submission
    │  Live: CLOB API order   │   Shadow: log only  (mode = shadow)
    ▼
SQLite Audit  (11 tables, 16 append-only BEFORE DELETE / BEFORE UPDATE triggers)
    ├── scans / decisions / orders / fills / positions
    └── resolutions / risk_events / exits / rotations
        whale_wallets / markets

What This Means In Practice

Numbers that define what a trustworthy AI trading system looks like.

100%

Decisions Auditable

30min

Default Scan Interval

9

Risk Rules Always Enforced

0

Black-Box Decisions

Ready to build?

Have an AI system that needs to operate within strict, auditable rules?

Whether you need an AI decision layer with enforced risk controls, an append-only audit trail, or a shadow testing environment — we'll design the architecture around your constraints and give you a fixed price before writing a line of code.

Start a conversation → Our AI development services