Research
Research

Building a Polymarket Bot, Part 2: Market Selection

📒Docs: https://zeit-1.gitbook.io/zeit ☎️Telegram: https://t.me/zeitfi X: https://x.com/ZEITFinance X2: https://x.com/autonomous_af Previous article: Part 1, Microstructure before math If you’re building a

ZEIT Research

📒Docs: https://zeit-1.gitbook.io/zeit ☎️Telegram: https://t.me/zeitfi X: https://x.com/ZEITFinance X2: https://x.com/autonomous_af

Previous article: Part 1, Microstructure before math

If you’re building a bot, the rookie move is to just plug into the WebSocket, subscribe to * (everything), and tell yourself, "I'll just filter out the noise in my loop."

Congratulations, you just built a slow bot.

A serious trading engine implementation doesn't try to trade "Polymarket" as a whole. It trades a specific, ruthless subset of markets that fit its hardware limits. The events you decide to monitor literally define your physics:

  • Your Latency: Every useless packet you ingest is a tax on your reaction time. Eating too much data makes you slow.

  • Your Uptime: Trying to drink from the firehose is the fastest way to OOM ( your server.

  • Your Edge: Don't run races you can't win. Focus on markets the tracks where your code actually has the advantage.

In high-frequency environments, Market Selection it is a hard dependency for performance engineering.

1. The Architecture: The Scanner vs. The Executor

**The Bottleneck: **Most builders mistakenly put Discovery (finding valid markets) and Execution (reacting to price) in the same event loop.

The Golden Rule: You cannot afford to parse "Will it rain?" metadata while you are trying to arb the "US Election. market.

To stay fast, you must decouple your architecture into two distinct processes:

Article image

2. The Deep Dive: Why You Need a Custom Indexer

Most developers assume the Scanner just calls GET* /markets?active=true *and passes the result to the bot. This is insufficient.

A robust Scanner is actually a Market Indexer. It should ingest **everything **active, closed, and resolved, and restructure it into a relational database (Postgres/...).

The "Unlimited Flexibility" Strategy

Polymarket's API is great for fetching raw data, but it is terrible for complex filtering. It cannot answer compound questions like "Find liquid, short-term political markets with low complexity."

If you rely on the API, you are stuck writing complex client-side loops that fetch thousands of pages. If you index the data locally, you replace those loops with a single, millisecond-fast SQL query.

**Example SQL: **Instead of fighting API filters, you run one query against your local index:

This gives you a precise list of targets instantly. You waste zero CPU cycles on noise.

3. The Logic Pipeline: A Funnel of Gates

Once you have the data indexed, how do you decide what to pass to the Executor?

Stop viewing market selection as a binary "Good/Bad" switch. View it as a Funnel of Strict Gates.

Your goal is to aggressively discard noise to protect your Executor’s latency.

Here is the 4-Gate Framework:

Gate 1: Coherence (Structure)

Before looking at price, check if the market is physically tradable.

  • The Check: Is the Event fully defined? Are all N sibling markets indexed and active?

  • The Logic: If an Event has broken metadata or a "disabled" outcome, you cannot safely calculate probability.

  • Action: Discard immediately.

Gate 2: Liquidity (The Weakest Link Rule)

This is the most critical gate for NegRisk (multi-outcome) strategies. Because you must buy all outcomes to build a hedged package, your ability to trade is bottlenecked by the worst order book in the set.

  • The Trap (The Liquidity Vacuum): You see a 5-candidate Event. The top 2 candidates trade $100k volume. The bottom 3 are "dust" with $5 depth.

  • The Failure Mode: If you try to buy the package, you will clear the top candidates easily, but smash through the dust books on the bottom candidates. You incur massive slippage on the cheap legs, destroying your edge.

  • The Check: Does the weakest Market in the Event meet your minimum depth threshold?

  • Action: If Min(Liquidity_Leg_1...N) < Threshold → The entire Event is dead.

Gate 3: The Goldilocks Zone (Horizon)

Markets behave differently based on expiry. Capital has an opportunity cost.

  • The Check: Is Expiry_Date inside your strategy's Goldilocks zone?

  • **The Logic:

  • **Too Soon (< 24h): Extreme gamma. Prices jump violently. Capital lockup is short, but execution risk is high.

  • Too Late (> 90d): Dead capital. Your money is locked for months for a 2% yield.

  • Action: Filter for the duration that matches your capital efficiency targets.

Gate 4: The Hardware Cap (Compute)

The final gate is physical, not financial.

  • The Check: Is Count(Selected_Markets) < Max_Capacity?

  • The Logic: If your Executor hits CPU limits at 500 active subscriptions, but your filters return 800 candidates, you are in danger. A lagging bot loses money.

  • Action: Rank the survivors by Volume/Spread and strictly cap the list size to your hardware limit.

It is better to trade 50 markets fast than 500 markets slow.

Summary: Selection is the First Line of Defense

Your bot's performance is defined by what you choose to ignore.

  • Architecture: Split your bot. The Scanner indexes and filters; the Executor trades. Never put them in the same event loop.
  1. Own the Data: Scrape the raw endpoints. Build a local SQL index to unlock "Unlimited Flexibility" for complex strategy queries.

  2. The Funnel: Apply strict Gates (Coherence -> Liquidity -> Horizon -> Cap) to ensure your Executor only wastes cycles on high-quality targets.

  3. Weakest Link Logic: Always filter NegRisk events by their thinnest leg, not their total volume.

**Next Up: **Now that we have a clean, fast stream of tradable Markets, we need to understand the data itself. Part 3 will cover The Local Mirror (WebSockets & State): Why asking for the price is too slow, and how to maintain a millisecond-perfect copy of the order book in RAM.

Keep Building

Continue the Series