Speed on Polymarket is not what I thought it was when I started building this.
I assumed faster was always better. Get the signal, fire the order, win. Turns out the execution problem on Polymarket is more interesting than that - and the bottlenecks are not where most people expect them to be.
This is the execution layer behind a bot that's placed 11,717 trades. Here's what I learned building it.
Repo: https://github.com/Eixen30/polymarket-bot
Why latency matters differently on Polymarket vs CEX
On a centralized exchange, latency is everything. Colocation, kernel bypass networking, FPGA order routing - the firms spending millions on microsecond advantages are doing it because the edge is real and the competition is that tight.
Polymarket is different. It's on-chain. Your order goes through the CLOB API, gets signed with EIP-712, and lands on Polygon. The blockchain has its own timing - block times, mempool congestion, gas priority. You can optimize your client-side execution to 10ms and still wait 2 seconds for the transaction to confirm on-chain.
So the execution problem on Polymarket is not "how do I get my order to the matching engine in 50 microseconds." It's "how do I structure my execution so that the latency I can't control doesn't kill trades I should be winning."
That's a different problem. It took me a while to reframe it correctly.
The four latency layers
Breaking down where time actually goes in a Polymarket trade:
1. Signal generation - 50-200ms
Price feed comes in, the model runs, signal fires. This is fully within your control and should be fast. The Chainlink Data Streams feed delivers updates sub-second. If your signal generation is taking longer than 200ms you have a processing problem somewhere.
The bot processes signal generation in a tight loop with no blocking calls. Everything async. If a feed update comes in while the previous signal is still processing, it queues rather than blocks.
2. Order construction + signing - 10-50ms
EIP-712 signing with ethers.js. This is local CPU - no network call, just cryptographic signing of the order payload. Should be under 20ms on any modern machine.
Where this gets slow: if you're constructing the order object from scratch on every trade, you're wasting cycles. The bot pre-builds order templates for the most common trade types at startup. At signal time it fills in the variable fields (price, size, market ID) and signs. Saves maybe 30ms per trade - doesn't sound like much, keeps it cleaner.
3. CLOB API submission - 100-400ms
This is the main variable. The Polymarket CLOB API round trip is typically 100-200ms from a well-connected server. From a residential connection it's slower. From a server in the wrong region it's slower still.
The bot runs on a VPS in the US East region - closest available to Polymarket's infrastructure based on latency testing across regions. Not colocation, but consistent 120-180ms round trips vs 300-400ms from other regions.
One thing that matters here: keep-alive connections. If you're opening a new HTTP connection on every order submission you're adding 50-100ms of TCP handshake overhead per trade. The bot maintains persistent connections to the CLOB API and reuses them.
4. On-chain confirmation - 1-3 seconds
This is the part you can't optimize away. Polygon block times are around 2 seconds. Your order lands in the mempool, waits for the next block, confirms. During congestion it can take longer.
What you can do: set appropriate gas prices. Polygon is cheap enough that slightly elevated gas doesn't matter for the economics of any individual trade. The bot sets gas at the 90th percentile of recent blocks - fast enough to confirm in the next block without overpaying meaningfully.
What you can't do: make the blockchain faster. Accept this layer as a fixed cost and design around it.
The T-90 rule and why it exists
The hard cutoff at 90 seconds before resolution exists because of execution latency, not because the signal disappears.
Here's the math: if a signal fires at T-85 seconds and the full execution cycle takes 2-3 seconds including on-chain confirmation, the order lands at T-82 to T-83 seconds. That's fine.
But if Polygon is congested and confirmation takes 5 seconds, the order lands at T-80. Still fine.
If the CLOB API is slow and confirmation is delayed, you might be submitting an order to a market that's already in the resolution window. At that point the order either gets rejected or fills at a price that no longer reflects the market you were trading.
The T-90 cutoff gives enough buffer that even with worst-case latency across all four layers, the order lands with time to spare. It's not a signal quality decision - it's an execution reliability decision.
Order types and fill quality
Polymarket's CLOB supports limit orders and market orders. The bot uses limit orders almost exclusively.
Market orders on thin binary markets are dangerous. The spread on a 5-minute market can be 2-4 cents wide. A market order crosses the full spread on entry. On a market where your edge is 2 percentage points, paying 2-4 cents of slippage on entry and potentially more on exit wipes the edge before the trade has started.
The bot places limit orders at mid or slightly aggressive of mid - just enough to get filled quickly without crossing the full spread. On liquid markets this fills in under a second. On thinner markets it sometimes doesn't fill at all.
Unfilled orders are cancelled after 30 seconds. No chasing. If the market moved enough that the limit wasn't hit, the setup is probably stale anyway.
Telegram alerts and what they actually tell me
Every trade fires a Telegram alert: market, direction, entry price, position size, timestamp. Resolution fires another alert: outcome, P&L on the trade, running daily P&L.
The alerts aren't just for monitoring. They're a latency check. The timestamp on the entry alert vs the timestamp on the CLOB confirmation tells me actual round-trip time per trade. I log these. If the average starts creeping up, something's wrong - server load, API slowness, network issue.
Two things I track per session:
- Average signal-to-confirmation time
- Number of orders that didn't fill within 30 seconds
Both are early warning signals for execution problems before they show up in P&L.
What I'd build differently
If I was starting the execution layer from scratch:
WebSocket for order updates instead of polling. The bot currently polls for fill confirmation. It works but it's inefficient and adds latency on the confirmation leg. Switching to the CLOB WebSocket feed for order status updates would cut confirmation detection time by 200-300ms in some cases.
Separate processes for signal generation and order execution. Currently both run in the same Node.js process. Under load, the event loop can get backed up. Separating them means a slow signal computation doesn't delay an order submission that's already ready to go.
Regional failover. One VPS in one region is a single point of failure. A second instance in a different region as hot standby - not for load balancing, just for failover if the primary goes down during a trading session.
None of these are critical at current scale. At higher volume or with more concurrent markets they'd matter.
Numbers on execution
Across 11,717 trades:
- Average signal-to-confirmation: ~280ms on good days, ~600ms on congested days
- Orders that failed to fill within 30 seconds: ~4% of total
- Orders cancelled due to T-90 cutoff: ~1% of total
- Slippage vs mid at entry: average 0.8 cents
The 4% unfilled rate is the interesting one. Those are setups that fired and didn't get filled - either the market moved before the limit was hit, or liquidity wasn't there. Some of those would have been winners. Some would have been losers. I don't know which. What I do know is that chasing them with market orders would have cost more in slippage than the ones that were winners would have made back.
Unfilled is fine. Filled at a bad price is not.
Full code including the execution layer: https://github.com/Eixen30/polymarket-bot
The order submission logic is in execution/clob.ts and the latency logging is in utils/metrics.ts. Questions in the comments about any of this.
Top comments (0)