The reference architecture

Every Polymarket bot worth running has the same four layers. Mixing them up is the most common reason a working hello-world becomes a money-losing production bot.

LayerJobTooling
SenseRead prices, books, and newspy-clob-client + Reuters / X firehose
DecideDecide whether to enter / exitPlain Python rules, or an LLM if your edge is linguistic
ActSign and post orderspy-clob-client + a Polygon wallet
AuditPersist signals, decisions, fillsSQLite to start, Postgres at scale

Build them in this order. Skipping audit until "later" is the most common production failure mode in this niche.

Setup, in five commands

uv venv && source .venv/bin/activate
uv pip install py-clob-client python-dotenv

cat > .env <<'EOF'
PRIVATE_KEY=0x...your_polygon_wallet_private_key
RPC_URL=https://polygon-rpc.com
EOF

# Fund the wallet with USDC on Polygon and ~$5 of MATIC for gas

That is the whole setup. No accounts, no API approvals, no waiting list: Polymarket is permissionless at the SDK layer.

Hello-world: read a book, place a limit order

import os
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, OrderType

load_dotenv()
client = ClobClient(
    host="https://clob.polymarket.com",
    chain_id=137,
    key=os.environ["PRIVATE_KEY"],
)
client.set_api_creds(client.create_or_derive_api_creds())

token_id = "0x..."   # YES token of your chosen market
book = client.get_order_book(token_id=token_id)

best_bid = float(book.bids[0].price)
my_price = round(best_bid - 0.01, 3)

order = client.create_and_post_order(OrderArgs(
    price=my_price, size=10.0, side="BUY", token_id=token_id,
), OrderType.GTC)

print("Posted:", order["orderID"])

That is a working bot. It is also a useless one: there is no strategy. Every honest bot from here is a bigger version of "what should the price be", evaluated against what the price is.

A real strategy: news-reaction trade

Prediction markets price natural-language events. A bot that reads the news faster and judges it better than the median trader has structural edge: for as long as the median trader is a human refreshing X.

import asyncio, json
from anthropic import AsyncAnthropic
from x_firehose import stream_keywords  # pseudo-package

claude = AsyncAnthropic()
PROMPT = """You are a prediction-market analyst. Given this news item:
{news}
And this market description:
{market}
Output JSON: {{ "side": "YES"|"NO"|"NONE", "confidence": 0.0..1.0,
"target_price": 0.0..1.0, "reasoning": "..." }}"""

async def react(news_item, market):
    msg = await claude.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=400,
        messages=[{"role": "user", "content":
            PROMPT.format(news=news_item, market=market["description"])
        }],
    )
    return json.loads(msg.content[0].text)

async def loop():
    async for news in stream_keywords(["fed", "rates", "powell"]):
        for market in WATCHLIST:
            decision = await react(news, market)
            if decision["confidence"] > 0.75 and decision["side"] != "NONE":
                place(market, decision)

That is the entire shape. The hard parts are not in the diagram: they are in the production hardening.

What "production-ready" actually means

  • Risk caps. Per-market notional, per-day notional, per-news-item notional. Pick numbers before you go live, not after.
  • Idempotent orders. Tag every order with a deterministic client_order_id derived from (news_id, market_id, side) so a retry never doubles up.
  • Observability. Log every prompt, every model response, every signed order, every fill. When a bot misbehaves you will be reading logs, not stack traces.
  • Drawdown circuit-breakers. Auto-halt on -3% day, -5% week. The strategy you wrote at 2am is not the strategy you want trading at -5%.
  • Health check. A liveness endpoint your alerting can poll. Polymarket does not call you when an oracle is disputed; you find out from your bot's silence.

The single-LLM trap

The hello-world above uses one model. That is fine for tutorials and dangerous for production. In NickAI's Q1 2026 internal benchmark, a single frontier model misclassified roughly 45% of market-direction signals; seven models in parallel weighted by historical calibration brought that to roughly 10%, a 78% relative reduction. Multi-model consensus is not an optimisation: it is the difference between a casino and an edge.

Implementing consensus by hand is a few hundred lines plus the operational cost of running it. Implementing it as a node in an agent graph is one line. That is the trade-off NickAI exists to collapse.

When you have outgrown py-clob-client

The signal that you have:

  • Your strategy code is 80% plumbing, news ingestion, model retries, audit trail, alerting, and 20% strategy.
  • You are running on more than five markets and dread adding a sixth.
  • Each new model you want to ensemble adds a week of integration work.
  • You spend longer reviewing logs than refining the strategy.

At that point you graduate to an agent runtime. The whole point of the runtime is that the four reference layers above become primitives, not code you maintain.