<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Blockchain Rust Engineer</title>
    <description>The latest articles on DEV Community by Blockchain Rust Engineer (@casatrick).</description>
    <link>https://dev.to/casatrick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3974435%2F236c2f7c-f929-4206-b498-7420159935e8.jpg</url>
      <title>DEV Community: Blockchain Rust Engineer</title>
      <link>https://dev.to/casatrick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/casatrick"/>
    <language>en</language>
    <item>
      <title>Polymarket Trading Bot Dominance: 14 of Top 20 Wallets</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Tue, 11 Aug 2026 05:59:52 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-dominance-14-of-top-20-wallets-37of</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-dominance-14-of-top-20-wallets-37of</guid>
      <description>&lt;p&gt;A review of Polymarket's public leaderboard turned up a number worth sitting with: 14 of the 20 most profitable wallets on the platform are bots. Not assisted by bots. Not partially automated. Fully bot-run. If you're trading manually on Polymarket right now, you're competing against a leaderboard that's already three-quarters automated.&lt;/p&gt;

&lt;p&gt;This isn't a fringe statistic - it's the clearest evidence yet that Polymarket has quietly become a bot-dominated market, and the mechanics behind why are worth understanding whether you're building a Polymarket trading bot yourself or just trying to figure out if manual trading still makes sense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where the edge is actually coming from&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The instinct is to assume these bots are winning because they're better at predicting outcomes - smarter models, better data, sharper judgment. That's mostly not what's happening. Research on Polymarket arbitrage estimates that traders extracted roughly $40 million from the platform between April 2024 and April 2025 by exploiting structural pricing inefficiencies, with the advantage coming from execution speed rather than predictive accuracy.&lt;/p&gt;

&lt;p&gt;That distinction matters enormously if you're building a Polymarket trading bot. It means the dominant strategy on the platform isn't "have a better opinion about the future" - it's "notice a pricing gap before anyone else and close it faster." Most automated trading in prediction markets relies on structural arbitrage rather than superior predictions, which tracks with everything about execution latency being the layer most bot builders underinvest in relative to their pricing model.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The numbers behind individual bots are getting extreme&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Some of the specific results circulating recently illustrate just how concentrated this edge has become. One bot reportedly turned $313 into $414,000 in a single month, trading exclusively in short-duration BTC, ETH, and SOL markets with a reported 98% win rate. The strategy wasn't predicting direction - it was exploiting temporal arbitrage and thin liquidity with a consistency manual traders can't match.&lt;/p&gt;

&lt;p&gt;Separately, an operator running an AI-agent framework called OpenClaw - an autonomous framework that connects to trading platforms via API and uses LLMs to read news headlines and automatically adjust positions - reportedly generated $115,000 in a single week on Polymarket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A necessary caveat on the AI-agent trend specifically&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before this reads as "just plug an LLM into Polymarket and print money" - it isn't that simple, and the regulatory environment is already pushing back. The CFTC has warned that fraudsters are exploiting public interest in AI to promote automated trading tools that promise unreasonably high or guaranteed returns. Headline numbers from any single bot in any single week or month are survivorship-biased by definition - you don't see the AI-agent bots that lost money in the same window, because nobody publishes those screenshots.&lt;/p&gt;

&lt;p&gt;The more grounded takeaway, from actually watching these systems operate: production discipline tends to matter more than squeezing additional model accuracy. Risk management beats optimization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this means if you're building a Polymarket trading bot right now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few practical implications follow directly from this data.&lt;/p&gt;

&lt;p&gt;First, if 70% of the top leaderboard is automated, the remaining structural arbitrage opportunities are being contested by increasingly sophisticated competition - the $40M extracted over that one-year window wasn't from an empty field. Edge decay is real, and a bot architecture that would have been profitable eighteen months ago may already be crowded out today.&lt;/p&gt;

&lt;p&gt;Second, execution speed is not a nice-to-have - it's the primary competitive axis on this platform, more so than on most retail trading venues. If your Polymarket trading bot's execution layer isn't validated against stale order book fills, you're not just leaving money on the table, you're competing at a structural disadvantage against bots that are.&lt;/p&gt;

&lt;p&gt;Third, the AI-agent layer (news-reading, sentiment-driven position adjustment) is a newer and less-proven category than pure arbitrage execution. The profitable examples are real, but so is the survivorship bias, and regulatory scrutiny on this specific category is already active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where this connects to what's changing next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This leaderboard shift is happening at the same time Polymarket is closing one of the more exploitable structural gaps that helped enable it. A working paper from Stanford and Singapore Management University researchers studying Polymarket's 5-minute Bitcoin contract found that order flow spiked at settlement times and reversed shortly after - the signature of temporary price pressure rather than genuine information - with roughly 821 wallets capturing about $8.2 million, the losses falling mostly on retail traders.&lt;/p&gt;

&lt;p&gt;That's part of what's driving Polymarket's move to TWAP settlement on August 7. The two trends are connected: as bots get better at exploiting settlement mechanics, the platform is forced to harden those mechanics, which in turn raises the bar for what a competitive Polymarket trading bot actually needs to account for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The practical bottom line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Polymarket isn't a platform where manual trading and bot trading coexist as roughly equal strategies anymore - the leaderboard data makes that fairly clear. For manual traders, it's a signal that certain market segments (especially thin, short-duration crypto contracts) are increasingly unfavorable ground. For bot builders, it's confirmation that the opportunity is real, but the bar for a competitive execution layer is higher than most tutorials suggest - and getting higher as settlement mechanics like TWAP close off the easiest structural exploits.&lt;/p&gt;

&lt;p&gt;I build execution, risk, and arbitrage infrastructure for Polymarket trading bots, along with provably fair systems for casino platforms. If you're trying to figure out where your own bot's execution layer stands relative to what's actually competitive on this platform right now, feel free to reach out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related reading:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Execution latency and stale orderbook fills: &lt;a href="https://casatrick.substack.com/p/polymarket-trading-bot-execution-latency-orderbook" rel="noopener noreferrer"&gt;Substack link&lt;/a&gt;&lt;br&gt;
Position sizing with fractional Kelly: &lt;a href="https://casatrick.substack.com/p/polymarket-bot-position-sizing-kelly-criterion" rel="noopener noreferrer"&gt;Substack link&lt;/a&gt;&lt;br&gt;
TWAP resolution and reconciliation: &lt;a href="https://casatrick.substack.com/p/polymarket-twap-latency-trading-bots" rel="noopener noreferrer"&gt;Substack link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>python</category>
      <category>fintech</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Polymarket Trading Bot Development: What Actually Works</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Thu, 06 Aug 2026 14:57:27 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-development-what-actually-works-2ga6</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-development-what-actually-works-2ga6</guid>
      <description>&lt;p&gt;A Polymarket trading bot isn't one piece of software - it's a stack of systems that each have to work correctly for the whole thing to be profitable. I've spent the last several months building this kind of infrastructure, and this post is the overview I wish existed when I started: what a real Polymarket trading bot actually needs, and where most builders lose money without realizing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What a Polymarket trading bot actually does&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the simplest level, a Polymarket trading bot compares the market's implied probability (the current price) against its own probability estimate, and trades the gap when it's large enough to be worth the cost of trading. That sounds simple. In practice, a working bot needs five distinct systems working together:&lt;/p&gt;

&lt;p&gt;Data layer - streaming order-book updates and external price feeds&lt;br&gt;
Signal engine - detecting changes that might move the probability&lt;br&gt;
Probability model - an independent, continuously-updated fair-value estimate&lt;br&gt;
Execution engine - placing, adjusting, and canceling orders correctly&lt;br&gt;
Risk manager - sizing positions and enforcing hard limits&lt;/p&gt;

&lt;p&gt;Most tutorials on building a Polymarket trading bot only cover the probability model. That's the least differentiated part of the system - the other four are where bots actually succeed or fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where the edge really comes from&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The probability model gets the model. Bayesian updating - turning a news event or price signal into a precise probability shift rather than a gut feeling - is the standard approach, and it works. A market priced at 30¢ with a strong signal can update to a fair value well above that, creating a measurable gap the instant new information lands.&lt;/p&gt;

&lt;p&gt;But a mispriced contract isn't automatically a profitable trade. Fees, slippage, and partial fills eat into that gap before it ever becomes realized profit. A serious Polymarket trading bot calculates net edge - what's left after real execution costs - not just the theoretical gap between model and market price.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The part most builders skip: execution timing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where a lot of bots quietly lose money despite having a correct model. Between the moment you calculate your edge and the moment your order actually reaches the exchange, the order book can move - especially on thin-liquidity markets, where a single order can consume most of the visible depth in that window. You end up filling at a price your model never actually evaluated.&lt;/p&gt;

&lt;p&gt;The fix is a validation step immediately before order submission: re-fetch the live book, diff it against the snapshot you priced against, and skip the trade if drift exceeds a tolerance. It's a small addition that protects the edge your model already calculated, and it matters more on illiquid markets than any further model tuning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Position sizing: why full Kelly is the wrong default&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once a bot has a real edge, how much to bet is its own problem. The Kelly Criterion sizes positions proportional to edge and odds, and in theory maximizes long-term growth. In practice, full Kelly amplifies whatever confidence your model outputs - and since your probability estimate is a model output, not a certainty, any overconfidence gets sized directly into risk.&lt;/p&gt;

&lt;p&gt;Most production Polymarket trading bots run fractional Kelly instead - typically 25-50% of full Kelly. It trades some theoretical growth rate for meaningfully lower variance, which matters more in practice than the textbook formula suggests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arbitrage: a different edge entirely&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not every Polymarket trading bot is directional. Arbitrage strategies detect pricing inconsistencies between related or complementary markets and capture the spread with minimal directional exposure - buying Up and Down at different moments when their combined cost drops below $1, for example, rather than betting on which side wins. This requires different infrastructure than a directional bot: inventory tracking across multiple partial positions, and careful handling of the risk that one side fills before the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TWAP: the resolution mechanism just changed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As of August 7, 2026, Polymarket is resolving crypto markets using Time-Weighted Average Price instead of a single price snapshot - averaging price over a 30-60 second window rather than trusting one instant. This closes a real manipulation vector (analysis attributed roughly $7.6 million in losses to last-second price manipulation under the old system), but it also means any bot built around forecasting a point-in-time price now needs to forecast a window average instead. This is a meaningfully different target, and it affects execution, resolution modeling, and late-market strategies across the board.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where I fit into this&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I build execution, risk, and arbitrage infrastructure for Polymarket trading bots - the layers most tutorials skip, not just the probability model. That includes execution validation, position sizing logic, multi-market arbitrage systems, and TWAP-aware resolution modeling for the current transition. I also build provably fair RNG systems for casino platforms, which turns out to share more infrastructure with prediction market bots than it looks like on the surface - both come down to proving an outcome is trustworthy, not just claiming it.&lt;/p&gt;

&lt;p&gt;If you're building a Polymarket trading bot and want a second set of eyes on your architecture - or need one built from scratch - feel free to reach out. Open-source code and deeper technical breakdowns on each of these topics are linked below.&lt;/p&gt;

&lt;p&gt;Related reading:&lt;/p&gt;

&lt;p&gt;Execution latency and stale orderbook fills: &lt;a href="https://casatrick.substack.com/p/polymarket-trading-bot-execution-latency-orderbook" rel="noopener noreferrer"&gt;Substack link&lt;/a&gt;&lt;br&gt;
Position sizing with fractional Kelly: &lt;a href="https://casatrick.substack.com/p/polymarket-bot-position-sizing-kelly-criterion" rel="noopener noreferrer"&gt;Substack link&lt;/a&gt;&lt;br&gt;
TWAP resolution and reconciliation: &lt;a href="https://casatrick.substack.com/p/polymarket-twap-latency-trading-bots" rel="noopener noreferrer"&gt;Substack link&lt;/a&gt;&lt;br&gt;
Arbitrage bot live result: &lt;a href="https://youtu.be/zeIyuIRhn-A?si=qRq-EMMQEwMDwZYh" rel="noopener noreferrer"&gt;https://youtu.be/zeIyuIRhn-A?si=qRq-EMMQEwMDwZYh&lt;/a&gt;&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>fintech</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Update a Polymarket Trading Bot for TWAP Resolution (Live August 7)</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Tue, 04 Aug 2026 06:47:53 +0000</pubDate>
      <link>https://dev.to/casatrick/how-to-update-a-polymarket-trading-bot-for-twap-resolution-live-august-7-597c</link>
      <guid>https://dev.to/casatrick/how-to-update-a-polymarket-trading-bot-for-twap-resolution-live-august-7-597c</guid>
      <description>&lt;p&gt;Polymarket is switching its crypto up/down markets from single-price-snapshot resolution to Time-Weighted Average Price (TWAP) resolution, effective August 7, 2026, 00:00 UTC. Any Polymarket trading bot built around a single price tick at expiry needs to be updated before that date - the resolution target changes from a point value to an averaged window (30–60 seconds depending on market duration). This article walks through exactly how I'm updating my own Polymarket trading bot for TWAP: the resolution engine, the Binance/Chainlink feed comparison, the signal research pipeline, and a live monitoring dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The clock is ticking on my bot's current logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've spent the last few months running a Polymarket trading bot on the platform's short-duration crypto up/down markets - the 5-minute and 15-minute BTC contracts. The logic was simple, almost embarrassingly so: track the price, get a read close to expiry, place the bet, collect (or lose) based on wherever the price landed at the exact second the market closed.&lt;/p&gt;

&lt;p&gt;That worked because the market itself was simple: resolution is currently based on one price snapshot at expiry. Whatever the price is at that instant decides the bet.&lt;/p&gt;

&lt;p&gt;On August 7, 2026, that changes for good. Polymarket is switching resolution to a time-weighted average price, and I'm not waiting until it goes live to find out how much of my bot's logic breaks - I'm updating my Polymarket trading bot for TWAP now, ahead of the cutover, so it's ready on day one instead of scrambling after the fact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Is TWAP Resolution on Polymarket?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of resolving on:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;resolution_price = price(T_expiry)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Polymarket's TWAP mechanism resolves on:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;resolution_price = (1 / W) * Σ price(t_i) * Δt_i   for t_i in [T_expiry - W, T_expiry]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where &lt;code&gt;W&lt;/code&gt; is the averaging window:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Market duration&lt;/th&gt;
&lt;th&gt;TWAP window&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15 minutes&lt;/td&gt;
&lt;td&gt;60 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 hours&lt;/td&gt;
&lt;td&gt;60 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The reasoning checks out: single-tick resolution is trivially gameable if you have enough capital to nudge the price for even one second. Reports tie roughly $7.6M in losses to exactly that exploit. Averaging over a window means you'd have to sustain a price move for the whole window while everyone else trades against you the entire time - a much worse trade than a one-tick snipe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters for Any Polymarket Trading Bot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Good for the platform. Bad for a bot whose entire strategy is implicitly built around "what will the price be at this one instant" - which is exactly why this isn't a wait-and-see update for anyone running a Polymarket trading bot on these markets.&lt;/p&gt;

&lt;p&gt;So I stopped adding features and started rebuilding the core now, with a hard deadline: everything needs to be validated and running before August 7.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reframing the Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first thing I had to accept: my old bot wasn't answering the right question anymore. I wasn't predicting a point anymore, I was predicting a short trajectory. A signal that was great at nailing the exact terminal tick might be mediocre at predicting a 30-second average, and vice versa. So instead of patching the old bot, I rebuilt it in four pieces: a resolution engine, a feed comparison layer, a signal research pipeline, and a live dashboard to watch it all happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updating a Polymarket Trading Bot for TWAP: Step by Step&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The TWAP Engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before trusting any signal, I needed to be able to compute the exact same number Polymarket computes. No shortcuts here - if my TWAP calculation doesn't match theirs, everything built on top of it is noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_twap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;window_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    ticks: list of (timestamp, price), sorted ascending
    Returns (twap_price, num_ticks_used, coverage_pct)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;relevant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ticks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;window_start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;window_end&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="n"&gt;weighted_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="n"&gt;covered_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;next_ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;window_end&lt;/span&gt;
        &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_ts&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;
        &lt;span class="n"&gt;weighted_sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;
        &lt;span class="n"&gt;covered_duration&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;

    &lt;span class="n"&gt;twap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weighted_sum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;covered_duration&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;covered_duration&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;coverage_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;covered_duration&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window_end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;window_start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;twap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;coverage_pct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;coverage_pct&lt;/code&gt; return value turned out to matter more than I expected - a TWAP computed from 95% window coverage and one computed from 40% coverage are not equally trustworthy numbers, and early on I was silently treating them the same. Now every downstream piece checks it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Feed Comparison - Binance vs. Chainlink Calibration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chainlink Data Streams mainnet access doesn't go live until August 4th, three days before markets start resolving on it. So I couldn't just point at "the real feed" and start testing. Instead, I built a synthetic TWAP from Binance tick history first, using the exact same 30s/60s windows, to get an early read on how wrong my old snapshot-based logic actually was.&lt;/p&gt;

&lt;p&gt;The chart that mattered most wasn't a live price chart - it was a divergence histogram across historical data: for a given lead time before expiry (0s, 15s, 30s, 60s, 2min, 5min), how far off was the instantaneous price from what the TWAP actually settled at?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;divergence_at_lead_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historical_markets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lead_seconds&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;diffs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;historical_markets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;instant_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;price_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expiry&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lead_seconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;final_twap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actual_twap&lt;/span&gt;  &lt;span class="c1"&gt;# or synthetic Binance TWAP pre-mainnet
&lt;/span&gt;        &lt;span class="n"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instant_price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;final_twap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diffs&lt;/span&gt;  &lt;span class="c1"&gt;# feed into a histogram
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the number that tells you concretely how much to change your Polymarket trading bot's confidence threshold - not a vague sense that "TWAP makes things smoother," but an actual bps figure per lead time, per market duration. I'm running this separately for 5-minute and 15-minute markets, since the window is a different fraction of the total market length for each (10% vs ~6.7%) - they don't degrade the same way.&lt;/p&gt;

&lt;p&gt;(I'll share the actual divergence numbers once I have a solid sample from live Chainlink data post-Aug-4 - right now this is running on synthetic Binance data as a placeholder, and I don't want to publish numbers that might shift once real feed data comes in.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Signal Research - Forecast vs. Nowcast&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the part I had to be most disciplined about. Once you're inside the TWAP window, you're not really forecasting anymore - you're partially observing the thing you're trying to predict. Those are different problems and I was sloppy about conflating them early on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Forecast = what's my best guess before the window even opens?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nowcast = given the ticks I've already seen inside the window, what's my updated estimate of where the average lands?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nowcast accuracy trivially improves the closer you get to expiry, because you're literally seeing more of the average. That's not a signal discovery, it's just math. The actual research question - the one that determines how early a Polymarket trading bot can safely act - is: how good is the forecast before the window opens at all? That's what the lead-time sweep is for, and it's the honest version of "timing" for this new mechanism.&lt;/p&gt;

&lt;p&gt;Candidate features I'm testing for the pre-window forecast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Rolling momentum over multiple short lookbacks (5s/15s/30s/60s)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rolling realized volatility over the same windows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Distance from the market's opening reference price&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Divergence between Polymarket's current implied odds and my rolling TWAP-so-far estimate&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;A Live TWAP Dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Numbers in a terminal don't build intuition the way a chart does. I built a small FastAPI + WebSocket dashboard with four panels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Live view - raw tick price (spline-smoothed) + rolling TWAP-so-far, with the active window shaded and a countdown to expiry&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basis panel - Chainlink vs. Binance lag/basis, once real Chainlink data is flowing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zoomed replay - pick any historical market and watch how the instantaneous price and the TWAP diverged and converged&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calibration panel - the divergence histogram from step 2, filterable by lead time and market duration&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Getting the live chart to actually look smooth (rather than jumping tick-to-tick) took more effort than I expected - the trick was buffering incoming WebSocket ticks client-side and interpolating between them on &lt;code&gt;requestAnimationFrame&lt;/code&gt;, instead of snapping the chart to each new point the instant it arrives. Chainlink ticks don't arrive at a perfectly even cadence, so without that buffering the chart looked jittery even though the underlying data was fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Current Progress&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ TWAP engine built and unit-tested against synthetic data&lt;br&gt;
✅ Feed comparison pipeline running on Binance-only data&lt;br&gt;
🔄 Signal research in progress - forecast-vs-nowcast split implemented, lead-time sweep running&lt;br&gt;
⏳ Dashboard live-view working; basis/calibration panels waiting on real Chainlink mainnet access (Aug 4)&lt;br&gt;
⏳ Full resolution validation against real Polymarket TWAP settlements - can't run until markets actually resolve under the new mechanism (Aug 7+)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polymarket TWAP FAQ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When does Polymarket's TWAP resolution go live?&lt;/strong&gt; August 7, 2026, at 00:00 UTC, for crypto up/down markets (5-minute, 15-minute, and 4-hour BTC contracts).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is TWAP resolution on Polymarket?&lt;/strong&gt; Instead of resolving on a single price snapshot at expiry, the market resolves on the time-weighted average price over a window before expiry - 30 seconds for 5-minute markets, 60 seconds for 15-minute and 4-hour markets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does TWAP affect existing Polymarket trading bots?&lt;/strong&gt; Yes, if the bot's logic assumes resolution happens on a single instantaneous price. Any strategy built around timing a single tick at expiry needs to be rebuilt around forecasting an averaged window instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What data feed does Polymarket use for TWAP?&lt;/strong&gt; Chainlink Data Streams, delivered through Polymarket's Real-Time Data Streaming (RTDS) infrastructure. Testnet feeds are live now; mainnet feeds launch August 4, 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can I test my Polymarket trading bot against TWAP before it goes live?&lt;/strong&gt; Build a synthetic TWAP from spot exchange tick data (e.g., Binance) using the same window sizes, then validate against real Chainlink data once mainnet access opens on August 4 - three days before the resolution mechanism actually switches over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Actual Lesson Here&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The interesting part of updating a Polymarket trading bot for TWAP isn't the code - TWAP is a well-understood, almost boring bit of math. It's that a mechanism change like this forces you to notice how many of your assumptions were baked in without ever being examined. My old bot "worked" partly because it was quietly leaning on a property of the market (single-tick resolution) that had nothing to do with actually forecasting price direction. Losing that crutch is annoying, but it's pushing the bot toward doing the thing I actually wanted it to do in the first place: predict price movement, not game a settlement mechanism.&lt;/p&gt;

&lt;p&gt;I'll post a follow-up once real Chainlink mainnet data is flowing (Aug 4) and again once I have post-Aug-7 resolution data to validate against. If you're running a Polymarket trading bot on these markets, this is very much a "get ahead of it now, not on August 7th" situation.&lt;/p&gt;

&lt;p&gt;Following along? I'll be sharing the divergence data and lead-time results as they come in - drop a comment if you're updating a Polymarket trading bot for TWAP too, curious what everyone else's old bots were secretly relying on.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>twap</category>
      <category>tradingbot</category>
      <category>crypto</category>
    </item>
    <item>
      <title>How Polymarket Is Closing the Manipulation Window in Crypto Markets</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Fri, 31 Jul 2026 13:34:15 +0000</pubDate>
      <link>https://dev.to/casatrick/how-polymarket-is-closing-the-manipulation-window-in-crypto-markets-5hag</link>
      <guid>https://dev.to/casatrick/how-polymarket-is-closing-the-manipulation-window-in-crypto-markets-5hag</guid>
      <description>&lt;p&gt;Starting August 7, 2026, 00:00 UTC, Polymarket's short-duration crypto up/down markets (5-min, 15-min, 4-hour) stop resolving on a single price snapshot at expiry and start resolving on a Time-Weighted Average Price (TWAP) - a 30–60 second window depending on market duration. This closes a real, quantified manipulation exploit and changes what "the right prediction" even means for anyone running a bot against these markets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem: point-in-time resolution is a single point of failure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Polymarket's crypto up/down markets ask a simple binary question: will an asset's price be higher or lower at the end of a window than it was at the start?&lt;/p&gt;

&lt;p&gt;Until now, the answer came from one price observation - whatever the oracle read at the exact expiry timestamp. That single number could decide a market with real money on both sides, which creates an obvious incentive: if you can influence the price at that one instant, you can influence the outcome, regardless of what the asset actually did for the rest of the window.&lt;/p&gt;

&lt;p&gt;This is the same class of problem DeFi has dealt with for years with naive spot-price oracles - a single observation is trivially manipulable if you have enough capital to move it, even briefly. &lt;cite&gt;The 5-minute contracts alone did $4 billion in cumulative volume, with an estimated $7.6 million in losses attributable to exactly this kind of manipulation&lt;/cite&gt; - &lt;cite&gt;a well-capitalized trader pushing the price in the final seconds, collecting the payout, and leaving other participants holding the loss.&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix: TWAP resolution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resolution_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T_expiry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the contract now computes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resolution_price = (1 / W) * Σ price(t_i) * Δt_i   for t_i in [T_expiry - W, T_expiry]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;W&lt;/code&gt; is the averaging window and the sum is over sampled ticks weighted by how long each was valid - a standard discretized TWAP.&lt;/p&gt;

&lt;p&gt;Window length scales with market duration, but not linearly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Market duration&lt;/th&gt;
&lt;th&gt;TWAP window&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15 minutes&lt;/td&gt;
&lt;td&gt;60 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 hours&lt;/td&gt;
&lt;td&gt;60 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The fact that a 4-hour market gets the same 60-second window as a 15-minute market is the tell: the window isn't sized proportionally to market length, it's sized to the minimum duration needed to make a manipulation attempt capital-inefficient. To move a TWAP over even 60 seconds on a liquid asset, you have to sustain a price deviation the whole time-during which arbitrageurs can (and will) trade against you - instead of just winning one lucky/paid-for tick.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure behind it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The price feed itself comes from Chainlink Data Streams, delivered through Polymarket's own Real-Time Data Streaming (RTDS) layer. &lt;cite&gt;Testnet TWAP feeds are already live; mainnet feeds and RTDS delivery launch August 4&lt;/cite&gt; - three days before markets actually start resolving on it, giving integrators a short live-fire testing window before it's load-bearing.&lt;/p&gt;

&lt;p&gt;Chainlink Data Streams is a pull-based, low-latency oracle product - distinct from Chainlink's older push-based Price Feeds - built for exactly this kind of high-frequency settlement use case. That's what makes sub-minute TWAP windows computationally practical for on-chain resolution.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;Polymarket is also putting $1M in liquidity rewards across affected markets through August&lt;/cite&gt; to keep spreads/depth healthy while market makers recalibrate their models - pricing a TWAP-resolved market means modeling the average price path into the close, not just the terminal price, which is a different risk than what they were quoting before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why build it this way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three overlapping motivations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A quantified exploit, not a theoretical one - the losses were measurable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;2.Consistency with the platform's core value proposition. A prediction market's whole pitch is an honest, continuously updated price consensus. Resolving on one exploitable tick undermined that at the exact moment it mattered most - settlement.&lt;/p&gt;

&lt;p&gt;3.Competitive pressure. &lt;cite&gt;Competitors like Kalshi have more robust settlement mechanisms, and the manipulation losses gave them a concrete talking point for institutional users.&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually breaks in a trading bot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Strategy logic&lt;/p&gt;

&lt;p&gt;Any edge that depended on influencing or predicting a single terminal tick is dead. The prediction target itself changes - from a point value (price(T_expiry)) to a path integral (mean(price(t)) over the window). This structurally favors:&lt;/p&gt;

&lt;p&gt;Short-horizon momentum/mean-reversion models that output a distribution, not a point forecast&lt;br&gt;
Volatility-aware sizing - variance of an average over W seconds is lower than variance of a single tick, so the "one random print decides everything" tail risk shrinks&lt;/p&gt;

&lt;p&gt;Data ingestion&lt;/p&gt;

&lt;p&gt;Last-trade price is no longer enough. You need the same input the resolution oracle uses: a continuous or high-frequency tick series over the TWAP window, from Chainlink Data Streams or Polymarket's RTDS WebSocket. Store raw ticks, not pre-averaged numbers - you'll want to reconcile your own computation against the published resolution price. Latency budget also tightens: with a 30-60s window, you need low-latency coverage of the whole window, not just a fresh final read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backtesting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hard regime split required - tag every historical market pre-Aug-7 (snapshot) vs post-Aug-7 (TWAP) and never pool them. Pre-Aug-7 backtests will systematically overstate the value of last-second timing tactics that no longer work. Note also: historical TWAP can't be reconstructed retroactively unless you were independently logging tick data before the feed existed - Polymarket's old resolutions used the snapshot, full stop.&lt;/p&gt;

&lt;p&gt;Execution/risk modeling&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Don't calibrate long-term slippage assumptions on August data - the $1M rewards pool will temporarily tighten spreads/depth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since resolution is now a smoothed average, the tail risk of "the market flips on one random print" near expiry is reduced - worth revisiting position-sizing rules for the closing window specifically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Model the closing window as a short-horizon stochastic process (a volatility model sized to the specific TWAP window), rather than treating expiry as a single random draw.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reconcile your own tick-based TWAP calculation against Polymarket's published resolution price during the transition - first against testnet, then against mainnet from Aug 4–7 before it's load-bearing. Add explicit handling for feed gaps/stale ticks within the averaging window; these are new failure modes that simply didn't exist under single-tick resolution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Closing thought&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the same principle DeFi has used against flash-loan-style oracle manipulation, applied here to prediction-market settlement: average over a window instead of trusting an instant. It's a genuine integrity fix, and for anyone building against these markets, the honest response isn't to mourn a dead exploit - it's to rebuild the data pipeline and prediction target around what actually decides outcomes now.&lt;/p&gt;

&lt;p&gt;The $7.6M loss figure and related characterizations come from crypto-news coverage of Polymarket's announcement, not a Polymarket-published number - treat it as a reported estimate.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>tradingbot</category>
      <category>manipulation</category>
      <category>math</category>
    </item>
    <item>
      <title>Polymarket Trading Bot Strategies - Bayesian Updating for Real Edge</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Wed, 29 Jul 2026 06:39:54 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-strategies-bayesian-updating-for-real-edge-2kao</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-strategies-bayesian-updating-for-real-edge-2kao</guid>
      <description>&lt;h2&gt;
  
  
  Polymarket Trading Bot Strategies: Turning Stale Probabilities into Edge with Bayes
&lt;/h2&gt;

&lt;p&gt;A prediction-market price is not a price.&lt;br&gt;&lt;br&gt;
It is a probability the crowd agreed on a moment ago.&lt;/p&gt;

&lt;p&gt;The second real news arrives, that number is wrong - not forever, just for the few minutes it takes everyone else to finish the same math a &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; can run in milliseconds.&lt;/p&gt;

&lt;p&gt;Especially on low-liquidity markets.&lt;/p&gt;

&lt;p&gt;That math has a name. It was written down in 1763 by a dead man, and it remains the single most important tool for anyone (or any bot) that trades a number meant to represent a chance.&lt;/p&gt;

&lt;p&gt;Most traders think their job is to predict what happens.&lt;br&gt;&lt;br&gt;
It is not.&lt;br&gt;&lt;br&gt;
Your job - and the job of any serious &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; - is to figure out what the price &lt;em&gt;should&lt;/em&gt; be after new information and get there before the crowd does.&lt;/p&gt;

&lt;p&gt;Prediction is guessing the future.&lt;br&gt;&lt;br&gt;
This is measuring a probability, watching evidence hit it, and updating that probability correctly and fast.&lt;/p&gt;

&lt;p&gt;There is one formula for updating a probability correctly. There has only ever been one. This article is that formula, where it came from, the fast version quant desks actually use, and exactly how to turn it into a trade (or into code for a &lt;strong&gt;Polymarket trading bot&lt;/strong&gt;).&lt;/p&gt;
&lt;h2&gt;
  
  
  The price is already a probability
&lt;/h2&gt;

&lt;p&gt;When a market says an outcome trades at 38 cents, it is telling you the crowd’s probability for that outcome is about 38 %. On Polymarket the price and the probability are the same object.&lt;/p&gt;

&lt;p&gt;A contract at 0.38 is a crowd standing on “roughly a 38 % chance.” That is not a metaphor. It is literally what the number means.&lt;/p&gt;

&lt;p&gt;Every price on your screen (or in your bot’s websocket feed) is a snapshot of a belief. And a belief has a property that a stock chart hides: the correct way to change it when new information shows up is not a matter of opinion. It is a matter of math.&lt;/p&gt;

&lt;p&gt;This is the part retail gets wrong on instinct. News drops and the untrained reaction is a feeling: “This is big, it should go way up.” How much is way up? From 38 to 45? 60? 80? The feeling has no number, so the trade has no edge.&lt;/p&gt;

&lt;p&gt;Retail asks: is this news good or bad?&lt;br&gt;&lt;br&gt;
A quant (or a well-written &lt;strong&gt;Polymarket trading bot&lt;/strong&gt;) asks: given this news, what is the &lt;em&gt;new&lt;/em&gt; probability, exactly, and how far is it from the price still sitting on the order book?&lt;/p&gt;

&lt;p&gt;The gap between those two numbers is the entire trade. And there is one formula that produces the new number.&lt;/p&gt;
&lt;h2&gt;
  
  
  The 1763 formula
&lt;/h2&gt;

&lt;p&gt;In 1763 the Royal Society published &lt;em&gt;An Essay Towards Solving a Problem in the Doctrine of Chances&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
The author, Reverend Thomas Bayes, had been dead for two years. His friend Richard Price found the essay, cleaned it up, and submitted it.&lt;/p&gt;

&lt;p&gt;Ten years later Pierre-Simon Laplace rediscovered the same idea and turned it into the foundation of modern statistical inference.&lt;/p&gt;

&lt;p&gt;What they found is the only correct rule for updating a belief when evidence arrives:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;H&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∣&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∣&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;H&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;H&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Four pieces, each plain once named:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;H&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 - your &lt;strong&gt;prior&lt;/strong&gt;. The probability of the outcome before the news. On a market this is handed to you for free: the current price.&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∣&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;H&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 - the &lt;strong&gt;likelihood&lt;/strong&gt;. If the outcome really were going to happen, how likely was this particular piece of news?&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 - how likely the news was in general.&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;H&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∣&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 - the &lt;strong&gt;posterior&lt;/strong&gt;. The updated probability after the news. This is the number your &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; is trying to find - the price the market &lt;em&gt;should&lt;/em&gt; move to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In words: your new belief is your old belief, tilted by how much more the evidence fits the world where the outcome happens than the world where it does not.&lt;/p&gt;

&lt;p&gt;Evidence is only worth something if it would happen more in one world than the other. A headline equally likely whether or not the event occurs tells you nothing, and the formula correctly leaves the probability unchanged. Strong evidence is not loud evidence. It is evidence that &lt;em&gt;separates&lt;/em&gt; the two worlds.&lt;/p&gt;

&lt;p&gt;That is the whole engine. Everyone can see the news. The edge is turning it into the right number instead of a feeling - and a bot can do it in the time it takes a human to open Twitter.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fast version desks (and bots) actually use
&lt;/h2&gt;

&lt;p&gt;The classic formula is correct but clumsy because of the (P(E)) denominator. Nobody who does this for a living uses that form. They use odds:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;posterior&amp;nbsp;odds&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;prior&amp;nbsp;odds&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;likelihood&amp;nbsp;ratio&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;The likelihood ratio (Bayes factor) is simply how much more likely the news is if the outcome happens versus if it does not: (P(E \mid H) / P(E \mid \neg H)).&lt;/p&gt;

&lt;p&gt;A ratio of 3 means the news is three times more consistent with the outcome happening. A ratio of 1 means it is noise.&lt;/p&gt;

&lt;p&gt;Example: market at 30 ¢. Prior odds ≈ 0.43. Likelihood ratio = 3.&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;posterior&amp;nbsp;odds&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.43&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1.29&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⇒&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;probability&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.56&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Market is at 30. Math says 56. That is a 26-point gap that appeared the instant the news did. A &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; does not need the price to prove it right; it only needs to be holding (or have the limit order resting) before the crowd finishes the same calculation.&lt;/p&gt;

&lt;p&gt;Even faster: work in log-odds. Multiplication becomes addition:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop"&gt;lo&lt;span&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;-odds(posterior)&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop"&gt;lo&lt;span&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;-odds(prior)&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop"&gt;lo&lt;span&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;likelihood&amp;nbsp;ratio&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Each new independent headline is just a number you add to a running total. Alan Turing and I.J. Good used exactly this at Bletchley Park to break Enigma. The same accumulation logic powers a modern trading model that ingests headline after headline and keeps a live posterior.&lt;/p&gt;

&lt;p&gt;You are not predicting the event. You are computing where the price should sit after the news and trading the gap before the crowd finishes the same math.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to turn this into a Polymarket trading bot
&lt;/h2&gt;

&lt;p&gt;The theory is clean. Turning it into money comes down to four disciplined steps and a short list of ways it can blow up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 - Let the market price be your prior&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Do not invent a prior out of thin air. The current price is the crowd’s aggregated belief. Start from the price, then move it only with genuinely new evidence. Your bot should never throw out the market’s number and substitute a gut feeling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 - Price the evidence, not the emotion&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
For every new piece of news ask one question: how much more likely is this if the outcome happens than if it does not? That is your likelihood ratio. Be honest and conservative - most news is weaker evidence than it feels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 - Update, compare, and only trade a real gap&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Multiply prior odds by the likelihood ratio, convert back to a probability, and compare it to the live price. If your posterior is 56 and the market is 30, that is a live edge. If the numbers are 33 vs 30, there is nothing left after fees. The formula does not just find trades; it tells the bot when to sit still - which is most of the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 - Respect that the edge decays as the crowd catches up&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Bayesian updating tells you &lt;em&gt;where&lt;/em&gt; the price is going, not that it will get there slowly. The entire edge lives in the window between the news landing and the crowd finishing the reprice. A well-tuned &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; moves inside that window or does not move at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ways the formula kills people (and bots)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do not double-count evidence.&lt;/strong&gt; If the news is already in the price, its likelihood ratio for you is 1, not 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlated headlines are one piece of evidence.&lt;/strong&gt; Five outlets reporting the same leak is one fact. Multiply five ratios and you will size into a gap that does not exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Garbage likelihoods produce garbage posteriors - confidently.&lt;/strong&gt; Calibrate honestly or the math’s precision works against you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ready-to-run implementation
&lt;/h2&gt;

&lt;p&gt;If you want a production-ready starting point that already handles multiple arbitrage and statistical strategies on Polymarket (including crypto Up/Down markets), the open-source Python bot below implements five parallel strategies with proper risk controls, signal ranking, and position sizing:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/casatrick/polymarket-arbitrage-bot-python" rel="noopener noreferrer"&gt;https://github.com/casatrick/polymarket-arbitrage-bot-python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is MIT-licensed, modular, and designed so you can plug Bayesian likelihood updates into the signal engine with only a few extra lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Essential reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;An Essay Towards Solving a Problem in the Doctrine of Chances&lt;/em&gt; - Thomas Bayes &amp;amp; Richard Price, 1763
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Probability and the Weighing of Evidence&lt;/em&gt; - I.J. Good, 1950
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The Theory That Would Not Die&lt;/em&gt; - Sharon McGrayne
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A Polymarket price is a probability the crowd settled on a moment ago. The instant new information arrives, that probability is stale, and there is exactly one correct way to compute what it should become.&lt;/p&gt;

&lt;p&gt;A dead reverend wrote it down in 1763. Codebreakers used it to beat Enigma. Today a &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; can use it to beat the crowd by a few hundred milliseconds.&lt;/p&gt;

&lt;p&gt;You do not need to predict the future.&lt;br&gt;&lt;br&gt;
You need to start from the market’s own number, weigh each new piece of evidence by how strongly it separates the two worlds, update once, and act inside the short window before everyone else finishes the same calculation.&lt;/p&gt;

&lt;p&gt;The formula was never hidden.&lt;br&gt;&lt;br&gt;
The edge is that most people (and most bots) still trade on a feeling about the news, while the price is quietly waiting to become a number you could have computed the second it broke.&lt;/p&gt;

&lt;p&gt;Build the bot. Run the math. Capture the gap.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>tradingbot</category>
      <category>predictionmarkets</category>
      <category>bayesian</category>
    </item>
    <item>
      <title>I Reverse-Engineered a Polymarket Trader's Strategy From Their Public Trade History</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Mon, 27 Jul 2026 06:18:49 +0000</pubDate>
      <link>https://dev.to/casatrick/i-reverse-engineered-a-polymarket-traders-strategy-from-their-public-trade-history-1kli</link>
      <guid>https://dev.to/casatrick/i-reverse-engineered-a-polymarket-traders-strategy-from-their-public-trade-history-1kli</guid>
      <description>&lt;p&gt;Every trade on Polymarket is public. Wallet address, market, side, price, size, timestamp - all sitting in an API response, for any wallet you want to look at.&lt;/p&gt;

&lt;p&gt;So I built a toolkit that takes any Polymarket wallet address and answers four questions: what did they trade, when and under what conditions, how much per trade, and - the hard one - UP or DOWN, and why. Then it turns those answers into a config-driven bot that replays the discovered rules. Not a copy-trading bot. It doesn't mirror the wallet in real time — it extracts the strategy and runs it independently, in paper mode by default.&lt;/p&gt;

&lt;p&gt;Here's how it works, and the one finding that made this actually interesting instead of just a data-collection exercise.&lt;/p&gt;

&lt;p&gt;The pipeline&lt;/p&gt;

&lt;p&gt;&lt;code&gt;collect → enrich → signals → analyse → report&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Collect pulls full trade history for a wallet from Polymarket's Data API (handles 100k+ fills via cursor pagination), plus market metadata for everything they traded.&lt;/p&gt;

&lt;p&gt;Enrich joins external context onto every single trade at the moment it happened: Binance and Coinbase OHLCV, the Chainlink ETH/USD oracle price on Polygon, order book spread and imbalance from the Polymarket CLOB, and how close the trade was to market close (I bucket this into &lt;code&gt;early&lt;/code&gt;, &lt;code&gt;mid&lt;/code&gt;, &lt;code&gt;late&lt;/code&gt;, &lt;code&gt;urgent&lt;/code&gt; phases).&lt;/p&gt;

&lt;p&gt;Signals computes candidate features on every row - momentum z-scores, oracle-vs-price agreement, spread percentage, timing phase.&lt;/p&gt;

&lt;p&gt;Analyse runs statistical analysers across all of it - win rate, Sharpe, Brier score, sizing patterns, and the core piece: strategy discovery.&lt;/p&gt;

&lt;p&gt;The interesting part: one rule doesn't work&lt;/p&gt;

&lt;p&gt;My first assumption was that a trader has a strategy - pick a direction rule, measure how well it holds. That assumption was wrong, and testing it wrong is what made the real pattern visible.&lt;/p&gt;

&lt;p&gt;Running this on a real wallet (~65k trades in ETH 5-minute Up/Down markets), a single global rule like "always follow the Chainlink oracle" doesn't hold. But segmenting by phase and price bucket reveals two genuinely different behaviors:&lt;/p&gt;

&lt;p&gt;Mode 1 - cheap lottery. In the late/urgent phase, when a token is priced 0–35¢, the trader buys the cheaper side. Win rate is only ~20% - but the payoff structure makes it positive EV. This mode barely correlates with the Chainlink oracle at all (~19% agreement).&lt;/p&gt;

&lt;p&gt;Mode 2 - oracle follow. At any phase, once a token is priced 50¢+, the trader follows Chainlink's price vs. the window-open price. Win rate here is ~78%, with a smaller payoff per win.&lt;/p&gt;

&lt;p&gt;Two completely different behaviors, cleanly separated by price and timing, both consistently profitable in their own regime - but only visible once you stop assuming one global rule and start segmenting. There's a gap between 35-50¢ in mid-phase where no rule reaches significance, and the bot is built to skip that gap rather than force a signal that isn't there.&lt;/p&gt;

&lt;p&gt;That's the actual deliverable: a &lt;code&gt;strategy_config.json&lt;/code&gt; with a &lt;code&gt;direction.strategies[]&lt;/code&gt; array, each entry a phase/price-scoped rule with its own logic, no hardcoded strategy in the bot itself. Re-run the analysis, restart the bot, get new behavior - nothing to redeploy.&lt;/p&gt;

&lt;p&gt;The bot side&lt;/p&gt;

&lt;p&gt;Phase 2 loads that config and runs a ~5-second scan loop: find active markets, match phase+price to a strategy mode, pull the mode-specific signal (oracle diff or cheap-side book price), run an EV/sizing check, execute paper or live. Daily loss limits and max-position caps sit underneath all of it, independent of which strategy mode is active.&lt;/p&gt;

&lt;p&gt;What this isn't&lt;/p&gt;

&lt;p&gt;Worth being upfront about: this isn't a guaranteed-profit system, it's not real-time copy trading (it extracts rules, it doesn't mirror the wallet), and it's not a general ML platform - it's segmented statistical rule discovery. Fill history also isn't full intent - you see what someone traded, not what they considered and skipped. Historical validity isn't future validity either; paper trading before live is the whole point of the default config.&lt;/p&gt;

&lt;p&gt;I build tooling like this for clients working on Polymarket - custom strategy extraction, market-making bots, or ongoing maintenance on an existing system. Full source, no black-box logic, paper-trading validation before anything goes live. If that's useful, reach out: Telegram &lt;a class="mentioned-user" href="https://dev.to/casatrick"&gt;@casatrick&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>trading</category>
      <category>python</category>
      <category>api</category>
      <category>polymarket</category>
    </item>
    <item>
      <title>Why Execution Speed Beats Detection Logic in Polymarket Arbitrage Bot</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Fri, 24 Jul 2026 08:06:59 +0000</pubDate>
      <link>https://dev.to/casatrick/why-execution-speed-beats-detection-logic-in-polymarket-arbitrage-bot-20na</link>
      <guid>https://dev.to/casatrick/why-execution-speed-beats-detection-logic-in-polymarket-arbitrage-bot-20na</guid>
      <description>&lt;p&gt;Every writeup on Polymarket arbitrage explains the same thing: YES and NO token prices should sum to $1.00, and when they don't, buying both sides and redeeming the pair locks in the gap. Correct, and also not the hard part. Every bot scanning the public order book sees the same gap at roughly the same moment. Detection was never the differentiator.&lt;/p&gt;

&lt;p&gt;The actual problem is what happens in the 2-3 seconds after the gap appears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why the window closes faster than you'd expect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A pricing gap on Polymarket doesn't sit there waiting. The moment it's visible, it's visible to everyone polling that market, and the first execution to hit the book starts consuming the liquidity the gap depended on. A $0.15 gap on $5,000 of depth might only support $1,200 of size before the rest re-prices back toward $1.00. Miss the window and you're not doing arbitrage - you're exit liquidity for whoever got there first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where naive implementations lose the race&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most bots handle this as separate sequential steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_order_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# step 1
&lt;/span&gt;&lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_gap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# step 2
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                       &lt;span class="c1"&gt;# step 3
&lt;/span&gt;    &lt;span class="nf"&gt;submit_order&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;                     &lt;span class="c1"&gt;# step 4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step adds latency. On a window that closes in seconds, that sequence is the bottleneck - not the underlying math. By the time step 4 fires, the depth you validated in step 2 may already be gone.&lt;/p&gt;

&lt;p&gt;Fold the depth check into the decision itself&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evaluate_and_execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_gap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;yes_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;no_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yes_price&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;no_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;min_gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;yes_price&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.005&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bids&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;no_price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.005&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fillable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MIN_VIABLE_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;submit_paired_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yes_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fillable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point isn't this exact snippet - it's that depth-checking has to happen inline with the trade decision, not as a downstream validation step run against data that's already stale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this gets harder, not easier, over time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Arbitrage windows aren't a fixed resource. They're a byproduct of market inefficiency, and more capital chasing the same gaps compresses both the size and duration of each one. Execution speed increasingly matters more than strategy novelty - a different competitive dynamic than most Polymarket arbitrage content accounts for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to actually evaluate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're building or reviewing a Polymarket arbitrage bot, "does it detect mispricing" isn't a useful question - every implementation does. Better questions: how fast does it go from signal to submitted order, does it size against live depth or a stale snapshot, and what happens when a partial fill leaves one leg exposed?&lt;/p&gt;

&lt;p&gt;Full implementation, including signal-scoring and execution logic across all five strategies: &lt;a href="https://github.com/casatrick/polymarket-analysis-toolkit" rel="noopener noreferrer"&gt;https://github.com/casatrick/polymarket-analysis-toolkit&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>polymarket</category>
      <category>algorithms</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>Building a Polymarket Arbitrage Bot: 5 Strategies, One Signal-Ranking Problem</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Wed, 22 Jul 2026 13:53:41 +0000</pubDate>
      <link>https://dev.to/casatrick/building-a-polymarket-arbitrage-bot-5-strategies-one-signal-ranking-problem-8e1</link>
      <guid>https://dev.to/casatrick/building-a-polymarket-arbitrage-bot-5-strategies-one-signal-ranking-problem-8e1</guid>
      <description>&lt;p&gt;Most "Polymarket arbitrage bot" writeups stop at explaining what arbitrage is. The actual engineering problem starts after that - once you have five strategies running in parallel, all watching the same markets, and need to decide which signal to act on when two or three of them fire at once. That's the part worth documenting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The five strategies, briefly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Intra-market arbitrage exploits the fact that YES + NO token prices should sum to $1.00. When they don't, buying both sides and redeeming the complete set locks a profit independent of outcome. Combinatorial arbitrage is the same mechanic extended to multi-outcome markets. Both of these are close to structurally risk-free - you're buying a guaranteed redemption, not predicting anything.&lt;/p&gt;

&lt;p&gt;Cross-platform arbitrage compares Polymarket's implied probability against spot prices on exchanges like Binance and bets on convergence. Endgame arbitrage targets near-resolution markets where one side already trades above ~93%. Momentum/mean-reversion applies Z-score, RSI, and VWAP divergence to Polymarket's price series across multiple timeframes. These three are directional, not risk-free, and treating them the same as the first two is a modeling mistake I see a lot of amateur implementations make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The actual hard problem: signal ranking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running five detection strategies concurrently means a single market can trigger multiple, sometimes conflicting, signals in the same scan cycle. A naive implementation executes whichever signal fires first - which is a bug waiting to compound, because "first" has nothing to do with "best."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix is a weighted composite score per signa&lt;/strong&gt;l:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;score_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected_profit_pct&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;WEIGHT_PROFIT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;WEIGHT_CONFIDENCE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="n"&gt;STRATEGY_PRIORITY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strategy_type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;WEIGHT_STRATEGY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urgency&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;WEIGHT_URGENCY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk_reward_ratio&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;WEIGHT_RISK_REWARD&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ranked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;score_signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;executable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ranked&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EXECUTION_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TRATEGY_PRIORITY is where the risk-profile distinction actually gets encoded - intra-market and combinatorial get weighted up, cross-platform and momentum get weighted down, by design, not by accident. Getting this weighting wrong is the difference between a bot that behaves like an arbitrage system and one that quietly turns into a directional trading system without anyone deciding that on purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters if you're building or evaluating one&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot of "arbitrage bot" marketing glosses over the fact that these are five different risk categories bundled under one label. If you're building your own, the signal-ranking layer is where real engineering judgment lives - it's a genuinely harder problem than detecting any single strategy in isolation, and it's the part that separates a working system from a collection of independent scripts.&lt;/p&gt;

&lt;p&gt;Full open-source implementation, including the strategy detection and scoring logic: github.com/casatrick/polymarket-arbitrage-bot-python&lt;/p&gt;

&lt;p&gt;Longer technical breakdown of each strategy: casatrick.substack.com&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>python</category>
      <category>algorithms</category>
      <category>crypto</category>
    </item>
    <item>
      <title>Screening Polymarket Markets: Liquidity and Resolution Risk</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Tue, 21 Jul 2026 13:30:36 +0000</pubDate>
      <link>https://dev.to/casatrick/screening-polymarket-markets-liquidity-and-resolution-risk-ak3</link>
      <guid>https://dev.to/casatrick/screening-polymarket-markets-liquidity-and-resolution-risk-ak3</guid>
      <description>&lt;p&gt;Most of what I've written about building a Polymarket bot so far assumes you're already trading a specific market - execution timing, position sizing. Neither of those matters if you're trading the wrong markets to begin with. Before any of that logic runs, you need a filter that decides which markets are even worth putting a bot in front of.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why market selection matters more than it looks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Polymarket lists hundreds of markets at any given time, and the majority of them aren't worth trading algorithmically. Some have almost no depth. Some are structurally ambiguous and carry real resolution risk. Running your edge calculation and execution logic against a market that fails on either of these fronts isn't a minor inefficiency - it's where a lot of "my bot has a working model but still loses money" complaints actually originate.&lt;/p&gt;

&lt;p&gt;Filter one: liquidity depth&lt;/p&gt;

&lt;p&gt;The execution and sizing logic I've covered in previous posts assumes there's enough resting liquidity near the market price to fill your intended position without excessive slippage. That assumption fails silently on thin markets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_liquid_enough&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderbook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intended_position_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_depth_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Checks whether top-of-book depth supports the intended position
    without excessive price impact.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;top_of_book_depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orderbook&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bids&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;top_of_book_depth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intended_position_size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;min_depth_ratio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ties directly back to the staleness problem from execution: if depth is under roughly 3x your intended position, you're not just risking a stale fill - you shouldn't be sizing a full position into that market in the first place, regardless of how good your edge calculation looks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter two: resolution risk&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one is specific to prediction markets and doesn't have a clean equivalent in traditional trading. Every Polymarket market resolves based on a defined resolution source and criteria - and not all of them are equally unambiguous.&lt;/p&gt;

&lt;p&gt;Markets with vague resolution criteria (subjective wording, dependent on a source that could plausibly be interpreted multiple ways) carry a real risk that has nothing to do with your probability model: the market could resolve in a way that doesn't match the "obvious" outcome, or get disputed and delayed. A perfectly calculated edge is worthless if the resolution itself is contested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical screening questions worth encoding into your filter&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Does the resolution source come from a single, unambiguous, verifiable feed, or does it depend on subjective judgment?&lt;br&gt;
Has this market (or its category) had prior resolution disputes on Polymarket's UMA oracle?&lt;br&gt;
Is the resolution date clearly defined, or is there room for the window itself to be ambiguous?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolution_risk_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_metadata&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Simple scoring heuristic - lower is safer.
    Replace with your own calibrated weights based on historical dispute data.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;market_metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;has_prior_disputes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;market_metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resolution_source_type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;subjective&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;market_metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clear_resolution_date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Putting the filter together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The combined screen isn't complicated - it's just a step most bot builders skip because it's less interesting than the pricing model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_trade_market&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderbook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intended_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;is_liquid_enough&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderbook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intended_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;resolution_risk_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RISK_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this before your edge calculation even fires, not after. There's no point spending compute or API calls pricing a market your risk logic would reject anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern connecting all three pieces&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Execution timing, position sizing, and market selection are really the same underlying discipline applied at three different stages: don't trust a snapshot that might not reflect reality by the time you act on it. Execution timing is about book staleness. Sizing is about model confidence. Market selection is about whether the market itself is even a fair, tradeable proposition before you engage either of the other two systems.&lt;/p&gt;

&lt;p&gt;I build this kind of infrastructure - execution logic, sizing, and market screening - for prediction market bots, along with provably fair systems for casino platforms. If you're working through similar filtering logic, happy to compare notes.&lt;/p&gt;

</description>
      <category>python</category>
      <category>fintech</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Execution Layer Most Trading Bot Tutorials Skip</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Fri, 17 Jul 2026 07:54:47 +0000</pubDate>
      <link>https://dev.to/casatrick/the-execution-layer-most-trading-bot-tutorials-skip-2lag</link>
      <guid>https://dev.to/casatrick/the-execution-layer-most-trading-bot-tutorials-skip-2lag</guid>
      <description>&lt;p&gt;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.&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where the standard approach breaks&lt;/strong&gt;&lt;br&gt;
Here's the loop almost every intro-level bot uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trading_loop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_orderbook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;implied_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_implied_probability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;my_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_prob&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;implied_prob&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EDGE_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;position_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;br&gt;
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 &lt;code&gt;edge&lt;/code&gt; variable was calculated against a book state that may no longer exist by the time &lt;code&gt;place_order&lt;/code&gt; executes.&lt;br&gt;
You end up filling at a price your model never actually evaluated. The math was right. The timing wasn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix: validate right before you commit&lt;/strong&gt;&lt;br&gt;
The solution isn't a better model. It's an execution guard, a cheap re-check immediately before order submission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trading_loop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_orderbook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;implied_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_implied_probability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;my_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_prob&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;implied_prob&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EDGE_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;position_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# re-check right before committing
&lt;/span&gt;        &lt;span class="n"&gt;live_book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_orderbook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;drift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_drift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;live_book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;drift&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DRIFT_TOLERANCE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;log_and_skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;book moved past tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A rule of thumb that's held up in practice&lt;/strong&gt;&lt;br&gt;
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.&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where this actually matters&lt;/strong&gt;&lt;br&gt;
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.&lt;br&gt;
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.&lt;br&gt;
Here is open-source version:&lt;a href="https://github.com/casatrick/polymarket-arbitrage-bot-python" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>crypto</category>
      <category>fintech</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>How I'm Getting Organic Traffic Building a Polymarket Bot in Public (No Growth Hacks)</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Wed, 15 Jul 2026 06:06:39 +0000</pubDate>
      <link>https://dev.to/casatrick/how-im-getting-organic-traffic-building-a-polymarket-bot-in-public-no-growth-hacks-21e5</link>
      <guid>https://dev.to/casatrick/how-im-getting-organic-traffic-building-a-polymarket-bot-in-public-no-growth-hacks-21e5</guid>
      <description>&lt;p&gt;I've been posting about building a Polymarket trading bot as I go, mostly because I was stuck and wanted to think out loud. A few things I noticed that I didn't expect:&lt;br&gt;
The broken version gets more engagement than the working one. A post about my bot losing money to slippage on a thin market outperformed a clean-architecture writeup by a wide margin. People comment on mistakes they recognize, not on things that already work.&lt;br&gt;
Rewriting per platform beats copy-pasting, even when it's more work. Same core article, different framing: HN gets the technical argument with zero self-promo, dev.to gets the code, Twitter gets one sharp claim instead of a summary. Identical copies performed worse everywhere I tried it.&lt;br&gt;
Code-to-engagement ratio is real here specifically. Posts with actual runnable snippets got bookmarked and shared. Posts that were mostly explanation got read once and forgotten.&lt;br&gt;
I don't have this fully figured out - some of it might just be posting often enough that it compounds, not some traffic secret. Sharing what's worked so far, not a playbook.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>bot</category>
    </item>
    <item>
      <title>Building a Polymarket Trading Bot: Order Book Monitoring, Slippage Handling, and Position Sizing in Python</title>
      <dc:creator>Blockchain Rust Engineer</dc:creator>
      <pubDate>Mon, 13 Jul 2026 08:05:14 +0000</pubDate>
      <link>https://dev.to/casatrick/building-a-polymarket-trading-bot-order-book-monitoring-slippage-handling-and-position-sizing-in-26cl</link>
      <guid>https://dev.to/casatrick/building-a-polymarket-trading-bot-order-book-monitoring-slippage-handling-and-position-sizing-in-26cl</guid>
      <description>&lt;p&gt;Why naive orders lose money on Polymarket&lt;br&gt;
Most people's first Polymarket bot places a market order and calls it done. That works fine on liquid, high-volume markets like major elections But Polymarket has thousands of long-tail markets with thin order books and wide bid-ask spreads. On those markets, a naive market order can fill at a price 5-10 cents away from the quote you saw a second ago, which for a binary contract priced in cents is a massive effective cost.&lt;br&gt;
Polymarket splits its API into two separate services worth knowing up front: the Gamma API (gamma-api.polymarket.com) for public, read-only market discovery, and the CLOB API (clob.polymarket.com) for order books, prices, and actual trading. Market data reads require no authentication; placing orders does.&lt;/p&gt;

&lt;p&gt;Basic bot skeleton: discovering a market and reading the book&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;py_clob_client.client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClobClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;py_clob_client.clob_types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BookParams&lt;/span&gt;

&lt;span class="c1"&gt;# Discover a market via the public Gamma API
&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gamma-api.polymarket.com/markets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;volume24hr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ascending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;market&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;condition_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;conditionId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;clob_token_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clobTokenIds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;yes_token_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clob_token_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;no_token_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clob_token_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Market: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Yes token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;yes_token_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Read-only CLOB client — no private key needed just to read the book
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ClobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://clob.polymarket.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_order_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yes_token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_midpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yes_token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implied probability vs. your model's probability&lt;br&gt;
Polymarket prices are already implied probabilities - a Yes price of $0.65 means the market is pricing that outcome at 65%. Your edge, if any, comes from your model disagreeing with that price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_probability&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Positive edge means your model thinks the outcome is more likely
    than the market is pricing it.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;model_probability&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt;

&lt;span class="n"&gt;model_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.72&lt;/span&gt;          &lt;span class="c1"&gt;# your model's estimate
&lt;/span&gt;&lt;span class="n"&gt;market_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# current market midpoint
&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;MIN_EDGE_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;  &lt;span class="c1"&gt;# don't trade on noise-level edges
&lt;/span&gt;&lt;span class="n"&gt;should_enter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MIN_EDGE_THRESHOLD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Risk logic: position sizing based on edge %&lt;br&gt;
A fractional-Kelly approach scaled down for prediction-market-specific risk (binary payout, model uncertainty) works better than full Kelly, which tends to oversize on overconfident models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;position_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bankroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                   &lt;span class="n"&gt;kelly_fraction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_position_pct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fractional Kelly sizing, capped by a hard max-exposure-per-market limit.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="c1"&gt;# Simplified binary Kelly: edge / odds against you
&lt;/span&gt;    &lt;span class="n"&gt;odds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt;
    &lt;span class="n"&gt;kelly_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;odds&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;odds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;sized_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kelly_pct&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kelly_fraction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;capped_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sized_pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_position_pct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# hard cap per market
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bankroll&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;capped_pct&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The max_position_pct cap matters more than the Kelly fraction itself - it's your hard stop against a single mispriced model wrecking the account.&lt;/p&gt;

&lt;p&gt;Handling partial fills and slippage&lt;/p&gt;

&lt;p&gt;Thin order books mean your order may only partially fill at your target price. Rather than chasing the fill with a market order (which is how slippage compounds), check remaining depth before adding size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_fillable_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BUY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sum size available at or better than target_price.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;levels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BUY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bids&lt;/span&gt;
    &lt;span class="n"&gt;fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BUY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;target_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; 
           &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;target_price&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;fillable&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fillable&lt;/span&gt;

&lt;span class="n"&gt;fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_fillable_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;market_price&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;order_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bankroll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;market_price&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;fillable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;order_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MIN_ORDER_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# not enough liquidity at an acceptable price  skip or use a limit order and wait
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If fillable liquidity is far below your intended size, the honest move is a resting limit order rather than forcing a market order through a thin book.&lt;/p&gt;

&lt;p&gt;What I'd do differently&lt;br&gt;
A few lessons that only became obvious after running this against real markets:&lt;/p&gt;

&lt;p&gt;Poll less, cache more. Hammering the Gamma API on a tight loop for markets that update every few minutes wastes rate limit budget you'll need when a market is actually moving fast. Poll frequency should scale with time-to-resolution and recent volume, not run on a flat interval.&lt;br&gt;
Log every rejected trade, not just executed ones. The trades your bot decided not to make (edge below threshold, insufficient liquidity) are the dataset that tells you whether your edge threshold is even calibrated correctly.&lt;br&gt;
Treat correlated markets as one position, not many. If your model has edge on "Fed cuts in March" and "Fed cuts in Q1," those aren't independent bets  size them as a combined exposure or you'll be more leveraged to one underlying event than your risk logic assumes.&lt;br&gt;
The order book snapshot is already stale by the time you act on it. For anything beyond casual trading, a WebSocket feed (wss://ws-subscriptions-clob.polymarket.com/ws/market) beats polling - the latency difference is the gap between getting filled at your price and getting picked off.&lt;br&gt;
Originally explored in more depth here: &lt;a href="https://github.com/casatrick/polymarket-arbitrage-bot-python" rel="noopener noreferrer"&gt;Github full description&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>tutorial</category>
      <category>python</category>
    </item>
  </channel>
</rss>
