I've spent the last several months building execution infrastructure for prediction market bots,mostly on Polymarket alongside RNG and fairness systems for casino-style games. Two very different domains, but they share a failure mode most devs don't think about until it costs them money: the gap between deciding to trade and actually executing.
Most tutorials on trading bots stop at the pricing model. Poll the orderbook, calculate implied probability, compare against your own model, fire an order when edge clears some threshold. That's the part everyone writes about. It's also the part that matters least once you're running on thin-liquidity markets.
Where the standard approach breaks
Here's the loop almost every intro-level bot uses:
def trading_loop():
book = fetch_orderbook(market_id)
implied_prob = calculate_implied_probability(book)
my_prob = model.predict(market_id)
edge = my_prob - implied_prob
if edge > EDGE_THRESHOLD:
place_order(market_id, size=position_size(edge))
This works fine on markets with real depth. It quietly fails on markets with shallow top-of-book liquidity which describes a large chunk of Polymarket listings outside the flagship markets.
The problem isn't the math. It's timing. Between the moment you fetch the book and the moment your order actually reaches the exchange, the book can change. On a thin market, a single order of moderate size can consume most of the visible liquidity in that window. Your edge variable was calculated against a book state that may no longer exist by the time place_order executes.
You end up filling at a price your model never actually evaluated. The math was right. The timing wasn't.
The fix: validate right before you commit
The solution isn't a better model. It's an execution guard, a cheap re-check immediately before order submission:
def trading_loop():
book = fetch_orderbook(market_id)
implied_prob = calculate_implied_probability(book)
my_prob = model.predict(market_id)
edge = my_prob - implied_prob
if edge > EDGE_THRESHOLD:
size = position_size(edge)
# re-check right before committing
live_book = fetch_orderbook(market_id)
drift = calculate_drift(book, live_book)
if drift > DRIFT_TOLERANCE:
log_and_skip(market_id, reason="book moved past tolerance")
return
place_order(market_id, size=size)
One extra API call per decision. That's the entire cost. In exchange, you stop executing against stale prices, which in my experience matters more on illiquid markets than any amount of additional model refinement.
A rule of thumb that's held up in practice
If top-of-book depth is under roughly 3x your intended position size, treat staleness as your primary risk - not model accuracy. Below that ratio, the market can move meaningfully in the time it takes your order to travel, regardless of how good your pricing signal is.
This isn't unique to prediction markets. The same principle shows up in RNG-based casino games when you're validating outcomes server-side - the state you generate a result against needs to match the state you commit it against, or you open the door to timing-based exploits. Different domain, same root problem: don't trust a snapshot you took before the world had a chance to change.
Where this actually matters
If you're building automated execution on prediction markets, or on any exchange with thin order books, I'd put the execution validation layer ahead of further model tuning on your priority list. A highly accurate model with no staleness protection will still bleed edge on illiquid fills. A modest model with tight execution controls tends to outperform it in the markets that matter.
I build this kind of infrastructure-trading bot execution logic, market-making systems, and provably fair RNG architecture for casino platforms for a living. If you're working on something similar and want a second opinion on your architecture, or need this built from scratch, feel free to reach out.
Here is open-source version:Github
Top comments (0)