<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged polymarket</title>
<link>https://coderlegion.com/tag/polymarket</link>
<description>Powered by Question2Answer</description>
<item>
<title>We ran an AI agent swarm on Claude Opus for $98. A single 2.5-cent call gave the same forecast</title>
<link>https://coderlegion.com/32551/we-ran-an-ai-agent-swarm-on-claude-opus-for-98-a-single-2-5-cent-call-gave-the-same-forecast</link>
<description>&lt;p&gt;Multi-agent swarms are a popular way to get &quot;more than one model&quot; out of an LLM: generate dozens of personas from your documents, let them post and argue on a simulated social network, then read off what the crowd thinks. We tested one, the open-source MiroFish engine, as a forecaster on four live Polymarket markets, with Claude Opus 5.5 behind every agent. The full results are &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/claude-opus-ai-swarm-polymarket-forecasts&quot;&gt;on our blog&lt;/a&gt;, and every forecast, prompt, seed file and persona correction is &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/orcalayer/ai-swarm-vs-polymarket&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Short version: the swarm landed 0 to 3 points from one direct Opus answer on all four markets, at 500 to 900 times the cost. This post is about how to evaluate an agent swarm so you can see that, and four things we would check in any multi-agent setup.&lt;/p&gt;
&lt;h3 id=&quot;1-always-run-the-single-call-baseline&quot;&gt;1. Always run the single-call baseline&lt;/h3&gt;
&lt;p&gt;The most important number in the whole test was not a swarm output. It was the same model asked the same question directly, with the same seed files and instructions, five times, median taken:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Market &lt;/th&gt;&lt;th&gt;Swarm on Opus (median agent) &lt;/th&gt;&lt;th&gt;Opus asked directly &lt;/th&gt;&lt;th&gt;Polymarket&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Toronto mayor: Chow wins &lt;/td&gt;&lt;td&gt;52 &lt;/td&gt;&lt;td&gt;49 &lt;/td&gt;&lt;td&gt;65.5&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Israel: Likud most seats &lt;/td&gt;&lt;td&gt;40 &lt;/td&gt;&lt;td&gt;38 &lt;/td&gt;&lt;td&gt;30.5&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Bank of Canada holds &lt;/td&gt;&lt;td&gt;45 &lt;/td&gt;&lt;td&gt;45 &lt;/td&gt;&lt;td&gt;18.7&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;US x Iran: no strike &lt;/td&gt;&lt;td&gt;85.5 &lt;/td&gt;&lt;td&gt;84 &lt;/td&gt;&lt;td&gt;82.5&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Without the baseline, a swarm result looks like an emergent crowd opinion. With it, you can see the crowd is the base model's view of the evidence, re-voiced 34 to 56 times. Sonnet showed the same pattern (50 vs 52, 49.5 vs 50).&lt;/p&gt;
&lt;p&gt;In our first round, repeat runs of the same setup moved the median by up to 10 points, so a 0 to 3 point gap is noise. One run per configuration is a real limitation; we spent the budget on Opus instead of repeats.&lt;/p&gt;
&lt;p&gt;Why it happens: every persona is written by the same model from the same documents. The agents share the same facts, framing and blind spots. Diversity of roles is not diversity of information.&lt;/p&gt;
&lt;h3 id=&quot;2-poll-the-agents-don-t-trust-the-summary&quot;&gt;2. Poll the agents, don't trust the summary&lt;/h3&gt;
&lt;p&gt;MiroFish ends a run with a report agent that writes a summary and its own probability. On the Bank of Canada market, three report agents said 89 to 93, and the Opus report had a chapter titled &quot;Agent Consensus: Banks and Economists Converge on 'No Move'&quot;.&lt;/p&gt;
&lt;p&gt;We asked each agent directly instead. Median: 45. Only 13 of 34 agents put YES above 50.&lt;/p&gt;
&lt;p&gt;The cause was mundane: the report agent tries to interview the agents, but by then the simulation had shut down, so it wrote from the knowledge graph built from the seed files and never saw the agents' answers. If a framework gives you a &quot;consensus&quot; summary, check that it actually read the outputs it claims to summarize.&lt;/p&gt;
&lt;h3 id=&quot;3-validate-generated-personas-against-the-source&quot;&gt;3. Validate generated personas against the source&lt;/h3&gt;
&lt;p&gt;Before each run we compared every generated persona with the seed files, first with a second model, then by hand. Opus 5.5 still produced about 130 factual errors across four markets. Several hit the exact fact a market resolves on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;six Israeli personas moved the election to November;&lt;/li&gt;
&lt;li&gt;three reversed a poll result (the seeds had Eisenkot ahead of Netanyahu 49 to 41);&lt;/li&gt;
&lt;li&gt;several said the Bank of Canada had just hiked, mixing it up with the Fed and the ECB;&lt;/li&gt;
&lt;li&gt;one poll's district sub-result became its overall result in five personas;&lt;/li&gt;
&lt;li&gt;the generator created an account for a mayoral campaign of a person who wasn't running.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also liked to end the simulation setup with a verdict (&quot;the narrative should drift toward cautious confidence that the pause holds&quot;). That is the generator steering the outcome before any agent speaks. We removed those sentences and 11 opening posts with wrong facts or ready-made conclusions, and published originals and fixes.&lt;/p&gt;
&lt;p&gt;One way to make this routine: for every persona, extract its factual claims as a list, check each against the seed files, and flag anything not supported. A cheaper model is fine for the first pass; a human for anything that touches the resolution criterion.&lt;/p&gt;
&lt;h3 id=&quot;4-meter-the-real-cost-and-cache&quot;&gt;4. Meter the real cost, and cache&lt;/h3&gt;
&lt;p&gt;Every agent re-sends its persona, tool list and history on every turn, so cost grows fast with agents times rounds.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Toronto &lt;/th&gt;&lt;th&gt;Likud &lt;/th&gt;&lt;th&gt;Bank of Canada &lt;/th&gt;&lt;th&gt;US x Iran&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Agents &lt;/td&gt;&lt;td&gt;36 &lt;/td&gt;&lt;td&gt;56 &lt;/td&gt;&lt;td&gt;34 &lt;/td&gt;&lt;td&gt;52&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swarm run on Opus 5.5 &lt;/td&gt;&lt;td&gt;$13.73 &lt;/td&gt;&lt;td&gt;$24.00 &lt;/td&gt;&lt;td&gt;$13.63 &lt;/td&gt;&lt;td&gt;$18.91&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swarm run on Haiku 5.5 &lt;/td&gt;&lt;td&gt;$0.23 &lt;/td&gt;&lt;td&gt;$0.56 &lt;/td&gt;&lt;td&gt;$0.27 &lt;/td&gt;&lt;td&gt;$0.45&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Five direct Opus answers &lt;/td&gt;&lt;td&gt;$0.13 &lt;/td&gt;&lt;td&gt;$0.14 &lt;/td&gt;&lt;td&gt;$0.13 &lt;/td&gt;&lt;td&gt;$0.12&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Prompt caching cut the Opus bill by about 40%; without it a run would have been $23 to $40. The whole round, with preparation and persona checks, was $98.56.&lt;/p&gt;
&lt;h3 id=&quot;also-worth-doing-lock-the-forecasts-publicly&quot;&gt;Also worth doing: lock the forecasts publicly&lt;/h3&gt;
&lt;p&gt;A forecasting test is only honest if the numbers are fixed before the outcome. We pushed &lt;code&gt;forecasts.csv&lt;/code&gt; to GitHub before any market resolved, and GitHub's event log records the push time. The scorecard (Brier score and absolute error per source, including the market price) comes after the last market closes on October 31.&lt;/p&gt;
&lt;h3 id=&quot;what-this-does-not-show&quot;&gt;What this does not show&lt;/h3&gt;
&lt;p&gt;Four markets this round, eight across two rounds: an illustration, not a ranking. One run per configuration. Hand-corrected personas, so an uncorrected swarm might behave differently. Accuracy comes after resolution.&lt;/p&gt;
&lt;p&gt;The takeaway so far: putting a frontier model behind every agent did not make the swarm a different forecaster from the model it runs on. If you want what the model thinks, ask it once.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/claude-opus-ai-swarm-polymarket-forecasts&quot;&gt;orcalayer.com&lt;/a&gt;. OrcaLayer is an independent Polymarket analytics provider, not affiliated with Polymarket.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32551/we-ran-an-ai-agent-swarm-on-claude-opus-for-98-a-single-2-5-cent-call-gave-the-same-forecast</guid>
<pubDate>Mon, 26 Oct 2026 12:45:42 +0000</pubDate>
</item>
<item>
<title>We pre-registered a Polymarket insider score. It passed, and we still didn't ship it</title>
<link>https://coderlegion.com/32057/we-pre-registered-a-polymarket-insider-score-it-passed-and-we-still-didnt-ship-it</link>
<description>&lt;p&gt;Polymarket insider trading makes headlines every few weeks, so an insider score sounds like an obvious feature for a Polymarket analytics site. We built one, called it the Early score, wrote the rules down before running it, and tested it on 355,732 wallets. It passed its own test, and we did not release it. The full results are &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-insider-score-entry-price&quot;&gt;on our blog&lt;/a&gt; and the aggregated data and a script are &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/orcalayer/polymarket-insider-score-backtest&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is about the method: how to set up a wallet score so the test can actually fail, the traps we hit before the test, and the check that changed our mind.&lt;/p&gt;
&lt;h3 id=&quot;1-write-the-rules-down-first&quot;&gt;1. Write the rules down first&lt;/h3&gt;
&lt;p&gt;Before computing a single score, we put the whole method in a pre-registration file: what an entry is, how the move is measured, the shrinkage constant, which markets are out, which periods are compared, and what counts as a pass. Every later change went in as a dated line with the reason.&lt;/p&gt;
&lt;p&gt;The pass rule was simple: score wallets on October 2025 to March 2026 (period A), and the top 10% must still be positive on April to September 2026 (period B), also without the 5 largest events.&lt;/p&gt;
&lt;p&gt;Without this file it is very easy to keep adjusting until something works. With it, every change you make has to be justified in writing before you see the outcome.&lt;/p&gt;
&lt;h3 id=&quot;2-the-score&quot;&gt;2. The score&lt;/h3&gt;
&lt;p&gt;If a wallet knows something first, the price should move its way after it trades. So for each entry we measured the price of the side the wallet holds 24 hours later, minus the price it paid, in cents. A wallet's score is the size-weighted average, pulled toward zero for wallets with few entries:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def shrunk_mean(moves, weights, k=5.0):
    &quot;&quot;&quot;Size-weighted mean move, shrunk toward 0.
    weights are entry sizes capped at $10,000, so n_eff counts 'effective entries'.&quot;&quot;&quot;
    total_w = sum(weights)
    if total_w == 0:
        return 0.0
    raw = sum(m * w for m, w in zip(moves, weights)) / total_w
    n_eff = total_w ** 2 / sum(w * w for w in weights)
    return raw * n_eff / (n_eff + k)   # 5 effective entries keep half the raw mean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wallets within 1 cent of zero (one price tick) get a neutral 50; the rest become a percentile. Sports and short Up or Down markets are out: a day after a match the price is the result, so the score would just rank sports bettors.&lt;/p&gt;
&lt;h3 id=&quot;3-trap-wallets-trading-with-each-other-at-made-up-prices&quot;&gt;3. Trap: wallets trading with each other at made-up prices&lt;/h3&gt;
&lt;p&gt;On one test day the top of our list was a group of related wallets passing the same shares back and forth in finished tennis matches. One bought at 0.02 and sold to a partner at 0.96 four minutes later. Read trade by trade, that is a +94 cent entry.&lt;/p&gt;
&lt;p&gt;The fix was to define an entry as a wallet's &lt;strong&gt;net position change in one market over one UTC day&lt;/strong&gt;, not as a single trade:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from collections import defaultdict

def daily_entries(trades):
    &quot;&quot;&quot;Simplified. trades: dicts with wallet, market, day, side ('YES'/'NO'), shares (+buy/-sell), price.
    Selling NO counts as buying YES. A same-day round trip nets to zero.&quot;&quot;&quot;
    net = defaultdict(lambda: {&quot;yes&quot;: 0.0, &quot;no&quot;: 0.0, &quot;cost_yes&quot;: 0.0, &quot;cost_no&quot;: 0.0})
    for t in trades:
        k = (t[&quot;wallet&quot;], t[&quot;market&quot;], t[&quot;day&quot;])
        s = &quot;yes&quot; if t[&quot;side&quot;] == &quot;YES&quot; else &quot;no&quot;
        net[k][s] += t[&quot;shares&quot;]
        net[k][&quot;cost_&quot; + s] += t[&quot;shares&quot;] * t[&quot;price&quot;]
    for k, v in net.items():
        delta = v[&quot;yes&quot;] - v[&quot;no&quot;]
        if delta == 0:
            continue
        side = &quot;yes&quot; if abs(v[&quot;yes&quot;]) &amp;gt;= abs(v[&quot;no&quot;]) else &quot;no&quot;
        avg_price = v[&quot;cost_&quot; + side] / v[side] if v[side] else None
        yield k, delta, avg_price
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you rank Polymarket wallets by anything trade-based, expect these pairs near the top of your list.&lt;/p&gt;
&lt;h3 id=&quot;4-trap-a-thin-price-after-&quot;&gt;4. Trap: a thin &quot;price after&quot;&lt;/h3&gt;
&lt;p&gt;In one market a handful of trades of $1.50 to $15 at 0.15 set the price 24 hours later, though the market settled at 1. One tiny trade can fake a move. We now require at least $100 of &lt;strong&gt;other wallets'&lt;/strong&gt; trades in that hour before using a price, and fall back to the final price only if the market has closed.&lt;/p&gt;
&lt;h3 id=&quot;5-the-test-passed&quot;&gt;5. The test passed&lt;/h3&gt;
&lt;p&gt;Period A groups, measured on period B, with t-statistics clustered by event (many entries in one event are not independent):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Group from period A &lt;/th&gt;&lt;th&gt;24 h move in B, cents per entry &lt;/th&gt;&lt;th&gt;t&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Top 10% &lt;/td&gt;&lt;td&gt;+0.83 &lt;/td&gt;&lt;td&gt;7.9&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Bottom 10% &lt;/td&gt;&lt;td&gt;-0.76 &lt;/td&gt;&lt;td&gt;-4.7&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;All wallets &lt;/td&gt;&lt;td&gt;-0.01 &lt;/td&gt;&lt;td&gt;-0.2&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;It held without the 5 largest events, without weather markets, and for every shrinkage constant from 0 to 100. By the rule we wrote, a pass.&lt;/p&gt;
&lt;h3 id=&quot;6-the-check-that-changed-our-mind-look-at-the-mechanism&quot;&gt;6. The check that changed our mind: look at the mechanism&lt;/h3&gt;
&lt;p&gt;A pass tells you the score finds &lt;em&gt;something&lt;/em&gt; that repeats. It does not tell you what. An insider and a wallet that simply buys cheaper both show a positive move, but with a different shape:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;news spreading: the price keeps moving for hours;&lt;/li&gt;
&lt;li&gt;a cheap fill: the gap is there at once and stops growing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we split each wallet's move into the first hour and the drift from 1 to 24 hours, and asked which part repeats between the two periods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from statistics import correlation  # Python 3.10+

def ranks(xs):
    order = sorted(range(len(xs)), key=xs.__getitem__)
    r = [0] * len(xs)
    for i, j in enumerate(order):
        r[j] = i
    return r

def spearman(a, b):
    return correlation(ranks(a), ranks(b))

# per wallet, period A vs period B
rho_first_hour = spearman(first_hour_A, first_hour_B)   # 0.138
rho_drift      = spearman(drift_A, drift_B)             # 0.050
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The top 10% were already +0.75 cents ahead after one hour and +0.83 after 24. The first-hour part repeats; the drift, the part an insider score exists to catch, barely does. The score was ranking wallets by fill quality.&lt;/p&gt;
&lt;p&gt;This check was not in the pre-registration, and we label it exploratory everywhere. But it is the one that decided whether a label like &quot;insider&quot; would tell users the truth. It would not, so the score is not on the site, in the API or in MCP.&lt;/p&gt;
&lt;h3 id=&quot;7-money-not-moves&quot;&gt;7. Money, not moves&lt;/h3&gt;
&lt;p&gt;A 24-hour move is not profit, so we also held every entry in a resolved market to settlement and subtracted the fee each wallet actually paid on-chain. The top 10% made +1.17 cents per entry after fees (t 2.9), on both mostly taker and mostly maker entries. One detail that matters if you repeat this: compare groups with &lt;strong&gt;all wallets&lt;/strong&gt;, not with zero. On longer-dated markets every group drifts up, so zero is the wrong baseline.&lt;/p&gt;
&lt;h3 id=&quot;reproduce-it&quot;&gt;Reproduce it&lt;/h3&gt;
&lt;p&gt;The repo has the aggregated tables behind every number and &lt;code&gt;scripts/early_score_check.py&lt;/code&gt; (standard library only), which rebuilds entries, scores and the period test from the OrcaLayer API:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export ORCALAYER_API_KEY=sk_orca_...        # never commit it
python3 scripts/early_score_check.py --csv entries.csv        # first 1,000 markets of data/markets.csv
python3 scripts/early_score_check.py --load-csv entries.csv   # rerun the analysis, no API calls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The API's trade history starts on May 1, 2026, so the script scores on May and June and checks on July to September, on a fixed list of 1,000 markets. The script writes wallets only as salted hashes, and the repo publishes groups only, no addresses.&lt;/p&gt;
&lt;h3 id=&quot;what-this-does-not-show-1&quot;&gt;What this does not show&lt;/h3&gt;
&lt;p&gt;Sports, individual wallets, and copying these wallets (a copy trades after them and does not get their price). On the shorter API check, the groups did not separate when held to settlement, so treat the money part as the weakest result.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-insider-score-entry-price&quot;&gt;orcalayer.com&lt;/a&gt;. OrcaLayer is an independent Polymarket analytics provider, not affiliated with Polymarket.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32057/we-pre-registered-a-polymarket-insider-score-it-passed-and-we-still-didnt-ship-it</guid>
<pubDate>Thu, 22 Oct 2026 09:55:28 +0000</pubDate>
</item>
<item>
<title>Backtesting live football on Polymarket: 6 traps in a recorded order book</title>
<link>https://coderlegion.com/31052/backtesting-live-football-on-polymarket-6-traps-in-a-recorded-order-book</link>
<description>&lt;p&gt;We recorded the order book of every Polymarket football (soccer) market for three weeks and tested two in-play ideas: buying a team right after a late price jump at a level score, and protecting goal bets with a stop-loss. Neither paid. Buying the jump 2 seconds later lost $2.85 per $10 over 837 jumps (t -7.9), and every one of 28 stop settings did worse than holding on the replay. The full results are &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-live-football-late-goals-stop-loss&quot;&gt;on our blog&lt;/a&gt; and the data is &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/orcalayer/polymarket-football-late-goal-stop-loss-backtest&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is about the data side: six traps we hit while building the test. Most of them will bite anyone who backtests in-play markets on Polymarket.&lt;/p&gt;
&lt;h3 id=&quot;1-you-have-to-record-the-book-yourself-then-check-the-recording&quot;&gt;1. You have to record the book yourself, then check the recording&lt;/h3&gt;
&lt;p&gt;Public Polymarket APIs give you trades and the current book, not the book as it was a second ago. For an in-play test you need the best bid and ask around every event, so we recorded the CLOB WebSocket market channel (&lt;code&gt;book&lt;/code&gt;, &lt;code&gt;price_change&lt;/code&gt;, &lt;code&gt;last_trade_price&lt;/code&gt;) with exchange timestamps.&lt;/p&gt;
&lt;p&gt;Before using it, check it against on-chain trades. Ours had 99.9% of on-chain trades, and 97.6% of them happened at the best price the recording showed at that moment. If your numbers are much lower, your replay will invent fills.&lt;/p&gt;
&lt;h3 id=&quot;2-events-arrive-out-of-order&quot;&gt;2. Events arrive out of order&lt;/h3&gt;
&lt;p&gt;The exchange can send the &lt;code&gt;price_change&lt;/code&gt; for a level that a trade just ate before the &lt;code&gt;last_trade_price&lt;/code&gt; of that same trade. Read events in arrival order and fills look about 25% worse than they were. Put each trade ahead of the book change it caused before you measure anything.&lt;/p&gt;
&lt;h3 id=&quot;3-the-mid-is-not-a-price-you-can-trade&quot;&gt;3. The mid is not a price you can trade&lt;/h3&gt;
&lt;p&gt;Our first detector watched the mid-price: if a team's mid jumps by 0.25 or more late in a level match, call it a goal. Many of those &quot;goals&quot; were sellers pulling their quotes. The mid doubles, the bid stays where it was, and the spread opens up.&lt;/p&gt;
&lt;p&gt;One example from the data: the mid went from 0.24 to 0.505 in seconds, but the ask was 0.78 and the bid about 0.23. A bot buying 2 seconds later paid 0.81. Ten seconds after the jump the ask was back at 0.25, and the match ended in a draw.&lt;/p&gt;
&lt;p&gt;Store the ask and the spread, not only the mid. The implied bid is easy to get back if you only kept mid and ask:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def spread_at_jump(mid: float, ask: float) -&amp;gt; float:
    bid = 2 * mid - ask          # implied bid
    return round(ask - bid, 2)   # e.g. mid 0.505, ask 0.78 -&amp;gt; 0.55
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our data 627 of 837 late jumps had a spread of 20 cents or more, and the jumping side won only 36% of those.&lt;/p&gt;
&lt;h3 id=&quot;4-the-level-score-can-be-fake-too&quot;&gt;4. The &quot;level score&quot; can be fake too&lt;/h3&gt;
&lt;p&gt;Our rule also needed the draw to be the favorite 30 seconds before the jump. That condition is read from mids as well, so pulled quotes can fake it in a match one team already leads. When all three books of a 1X2 match are two-sided, the three mids add up to about 1.00. Check that before you trust the state:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def book_looks_clean(home_mid: float, draw_mid: float, away_mid: float) -&amp;gt; bool:
    return 0.95 &amp;lt;= home_mid + draw_mid + away_mid &amp;lt;= 1.05
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In 51 of the 85 jumps that had a tight spread, the three mids before the jump added up to more than 1.10. One of them was a team that had led for 20 minutes: its bid disappeared for a moment, the score looked level, and the bid coming back looked like a late goal.&lt;/p&gt;
&lt;h3 id=&quot;5-daily-batch-files-overlap&quot;&gt;5. Daily batch files overlap&lt;/h3&gt;
&lt;p&gt;We ran the scan one day at a time, and each day file also covered the first hours after midnight UTC. Jumps after midnight landed in two files: 48 duplicates out of 885 raw rows. They did not change the conclusion, but they would have inflated every count and t-statistic. If you split a time series by day, check the joins.&lt;/p&gt;
&lt;h3 id=&quot;6-the-fastest-entry-is-not-reachable&quot;&gt;6. The fastest entry is not reachable&lt;/h3&gt;
&lt;p&gt;Football markets have &lt;code&gt;secondsDelay: 1&lt;/code&gt; in the Gamma API: a taker order waits a second before it can match, and you cannot cancel it during that time. So a result &quot;at +0 s&quot; is not a strategy, it is a plot point. In our test the earliest entries lost the most (-$4.21 per $10 at +0 s, -$2.85 at +2 s), because the first seconds after a jump are when the price is most wrong in the direction you are buying.&lt;/p&gt;
&lt;p&gt;The same delay hurts stops. Polymarket has no stop orders, so a stop is a bot watching trades and then market-selling after the delay, paying the taker fee a second time. In live football prices swing several cents within minutes, and a 3-cent stop fired on 86 to 96% of our replayed bets.&lt;/p&gt;
&lt;h3 id=&quot;reproduce-it-1&quot;&gt;Reproduce it&lt;/h3&gt;
&lt;p&gt;The repo has &lt;code&gt;data/late_goal_jumps.csv&lt;/code&gt; (one row per jump, with the asks 0 to 60 seconds after it and the result) and a standard-library script that recomputes every late-goal number:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 scripts/summarize_late_goals.py data/late_goal_jumps.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;METHOD.md&lt;/code&gt; describes the recorder, the detection rule and each trap above in more detail.&lt;/p&gt;
&lt;h3 id=&quot;what-this-does-not-show-2&quot;&gt;What this does not show&lt;/h3&gt;
&lt;p&gt;About three weeks of football, mostly club leagues. Pre-match markets, other sports and stops on strategies whose entries already make money were not tested. Bets in the same match are not independent, so read t values near 2 with care.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-live-football-late-goals-stop-loss&quot;&gt;orcalayer.com&lt;/a&gt;. OrcaLayer is an independent Polymarket analytics provider, not affiliated with Polymarket.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31052/backtesting-live-football-on-polymarket-6-traps-in-a-recorded-order-book</guid>
<pubDate>Wed, 14 Oct 2026 11:44:45 +0000</pubDate>
</item>
<item>
<title>Backtesting a Polymarket copy trading bot without fooling yourself</title>
<link>https://coderlegion.com/30590/backtesting-a-polymarket-copy-trading-bot-without-fooling-yourself</link>
<description>&lt;p&gt;Copy trading is the most common bot idea on Polymarket: find the wallets that win and buy what they buy. We tested it on five sports (MLB, NFL, college football, League of Legends and Valorant) and it did not survive. The full results are &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-copy-trading-whales-backtest&quot;&gt;on our blog&lt;/a&gt; and the data and script are &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/orcalayer/polymarket-copy-trading-whales-backtest&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is about the backtest itself: how to set it up so it can tell you &quot;no&quot;, and the one mistake that makes almost every copy bot backtest look better than it would be live.&lt;/p&gt;
&lt;h3 id=&quot;1-define-a-copy-you-could-actually-execute&quot;&gt;1. Define a copy you could actually execute&lt;/h3&gt;
&lt;p&gt;A &quot;copy&quot; in our test is a wallet's first buy of at least $20 in a market, at a price between 0.30 and 0.85. We buy the same outcome for $10 at the wallet's price &lt;strong&gt;plus 1 cent&lt;/strong&gt;, pay the sports taker fee and hold to resolution. Roughly, in code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def copy_result(price: float, won: bool, stake: float = 10.0, slip: float = 0.01) -&amp;gt; float:
    p = price + slip                      # you never get the whale's exact price
    shares = stake / p
    fee = shares * 0.05 * p * (1 - p)     # Polymarket sports taker fee
    payout = shares if won else 0.0
    return payout - stake - fee
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One cent is generous. In fast in-play markets the price moves much further before a copy fills. We also reran MLB at +3 cents: every result went negative, including the top 10.&lt;/p&gt;
&lt;h3 id=&quot;2-pick-on-one-period-measure-on-the-next&quot;&gt;2. Pick on one period, measure on the next&lt;/h3&gt;
&lt;p&gt;The rule: rank wallets by their copy results over the two months before the test month, then copy only in the test month. Nothing from the test month touches the selection. We used three rules fixed before the run:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ALL10&lt;/code&gt;: every wallet with at least 10 copies (the &quot;average whale&quot;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POS&lt;/code&gt;: at least 20 copies, mean above zero, t-statistic of 2 or more&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TOP10&lt;/code&gt;: the 10 best by t-statistic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MLB, top 10, per $10 copied:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Test month &lt;/th&gt;&lt;th&gt;While picked &lt;/th&gt;&lt;th&gt;Month after&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;July 2026 &lt;/td&gt;&lt;td&gt;+$3.65 &lt;/td&gt;&lt;td&gt;+$0.16&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;August 2026 &lt;/td&gt;&lt;td&gt;+$3.06 &lt;/td&gt;&lt;td&gt;-$0.04&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;September 2026 &lt;/td&gt;&lt;td&gt;+$2.72 &lt;/td&gt;&lt;td&gt;-$0.47&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Oct 1-4 2026 &lt;/td&gt;&lt;td&gt;+$3.78 &lt;/td&gt;&lt;td&gt;-$2.23 (n 7)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The same rule that showed +$3 in the pick window showed about zero the month after.&lt;/p&gt;
&lt;h3 id=&quot;3-check-whether-rank-carries-over-at-all&quot;&gt;3. Check whether rank carries over at all&lt;/h3&gt;
&lt;p&gt;Before tuning thresholds, check if past performance predicts future performance for individual wallets. For MLB wallets with at least 10 copies in both periods, the Spearman correlation between the pick months and the next month was -0.01, +0.15, +0.08 and -0.03. NFL was between -0.09 and +0.03.&lt;/p&gt;
&lt;p&gt;With correlations like that, picking &quot;the best&quot; is picking the luckiest. With thousands of active wallets, dozens will have a great two months by chance.&lt;/p&gt;
&lt;h3 id=&quot;4-look-at-the-baseline-before-the-winners&quot;&gt;4. Look at the baseline before the winners&lt;/h3&gt;
&lt;p&gt;The average MLB whale, copied one cent worse than its own price, lost 31 to 63 cents per $10 in every test month. If the baseline is negative, a selection rule has to beat a loss before it makes anything. Check &lt;code&gt;ALL10&lt;/code&gt; first.&lt;/p&gt;
&lt;h3 id=&quot;5-the-trap-wallet-labels-computed-today&quot;&gt;5. The trap: wallet labels computed today&lt;/h3&gt;
&lt;p&gt;This one is easy to miss. Analytics sites (ours included) label wallets: &quot;smart money&quot;, &quot;50+ resolved markets&quot;, win rate, profit factor. Those labels are computed &lt;strong&gt;today, on each wallet's full history&lt;/strong&gt;, including the months you are about to backtest.&lt;/p&gt;
&lt;p&gt;If you pick wallets with today's labels and backtest them on last summer, the selection already knows who survived. Wallets that blew up are not labeled &quot;smart&quot;, so the backtest never copies them. It will always look better than it would have live.&lt;/p&gt;
&lt;p&gt;We hit a mild version of this ourselves: one filter in our profile screen used each wallet's share of activity in the sport on its full history. We disclosed it in the methodology. It most likely flatters the pools, and they still showed no edge.&lt;/p&gt;
&lt;p&gt;Rule of thumb: every feature used to pick a wallet must be computable from data strictly before the pick date.&lt;/p&gt;
&lt;h3 id=&quot;6-make-it-reproducible&quot;&gt;6. Make it reproducible&lt;/h3&gt;
&lt;p&gt;The repo has &lt;code&gt;scripts/copy_backtest.py&lt;/code&gt; (Python 3.9+, standard library only) that runs the same walk-forward on the OrcaLayer API for any league:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export ORCALAYER_API_KEY=sk_orca_...        # never commit it
python3 scripts/copy_backtest.py --league mlb --start 2026-05-01 --end 2026-09-30 --max-markets 800
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To check that the API path and our internal database agree, we ran both on the same 800 MLB games: 161,094 copies in both, every wallet, price and result equal, total -$83,012.60 in both. The script output showed the same picture: top 10 from about +$3 per $10 to between -$1.29 and +$1.06 the month after.&lt;/p&gt;
&lt;p&gt;The script needs a Premium API key. If you just want to look at a wallet's record before copying it, the free &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/docs/mcp&quot;&gt;OrcaLayer MCP server&lt;/a&gt; works from Claude without a key.&lt;/p&gt;
&lt;h3 id=&quot;what-this-does-not-show-3&quot;&gt;What this does not show&lt;/h3&gt;
&lt;p&gt;Five sports, and picking by past results only. Slower markets like politics, where informed money can stay ahead for days, and picking wallets by how they trade rather than how much they made, were not tested.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-copy-trading-whales-backtest&quot;&gt;orcalayer.com&lt;/a&gt;. OrcaLayer is an independent Polymarket analytics provider, not affiliated with Polymarket.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30590/backtesting-a-polymarket-copy-trading-bot-without-fooling-yourself</guid>
<pubDate>Sat, 10 Oct 2026 13:28:30 +0000</pubDate>
</item>
<item>
<title>6 things that broke our Polymarket trading bot before it made a dollar</title>
<link>https://coderlegion.com/30137/6-things-that-broke-our-polymarket-trading-bot-before-it-made-a-dollar</link>
<description>&lt;p&gt;We spent a few weeks trying to build a profitable Polymarket bot. Arbitrage between linked markets, in-play market making, copying wallets that trade before goals, betting against miscalibrated prices. We recorded the full order book for every football market, indexed on-chain trades and replayed each idea.&lt;/p&gt;
&lt;p&gt;None of them made money. The full research with numbers is &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-strategies-that-dont-work&quot;&gt;on our blog&lt;/a&gt;, and the CSVs are &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/orcalayer/polymarket-arbitrage-market-making-copy-trading-backtests&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is the engineering side: six things about the Polymarket API and our own pipeline that would have broken the bot even if a strategy had worked.&lt;/p&gt;
&lt;h3 id=&quot;1-your-taker-order-waits-a-second-and-you-can-t-cancel-it&quot;&gt;1. Your taker order waits a second, and you can't cancel it&lt;/h3&gt;
&lt;p&gt;Sports markets have a field in the Gamma API called &lt;code&gt;secondsDelay&lt;/code&gt;. Football is &lt;code&gt;1&lt;/code&gt;, NBA is &lt;code&gt;0&lt;/code&gt;. You can see it on any event:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET https://gamma-api.polymarket.com/events?slug=fif-sri-mri-2026-10-05
... &quot;secondsDelay&quot;: 1 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Per the &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.polymarket.com/concepts/order-lifecycle&quot;&gt;Order Lifecycle docs&lt;/a&gt;, a marketable order waits out that delay before matching, and &quot;during either delay, the order is pending and cannot be canceled&quot;. Resting limit orders can still be cancelled. Some crypto and finance up/down markets have a 250 ms version of the same thing.&lt;/p&gt;
&lt;p&gt;What it did to us: our arbitrage detector found 369 mispriced sets across linked football markets in 18 hours. Of the 2c+ ones we rechecked one second later, all legs were still in the book in 3, worth $1.83 total. Makers pull their quotes inside your delay, or a faster taker takes the legs. If your bot sees a price and hits it, check &lt;code&gt;secondsDelay&lt;/code&gt; first.&lt;/p&gt;
&lt;h3 id=&quot;2-fees-are-not-a-percentage&quot;&gt;2. Fees are not a percentage&lt;/h3&gt;
&lt;p&gt;Only takers pay, and the fee depends on price:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def taker_fee(shares: float, price: float, fee_rate: float) -&amp;gt; float:
    # fee_rate from Gamma feeSchedule.rate, e.g. 0.05 for sports
    return shares * fee_rate * price * (1 - price)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It peaks at 0.50 (&lt;code&gt;p * (1 - p) = 0.25&lt;/code&gt;) and goes to zero near 0 and 1. A 1c edge on a 50c contract is mostly eaten. Rates and maker rebates are in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.polymarket.com/trading/fees.md&quot;&gt;fees docs&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;3-the-mempool-gives-you-nothing&quot;&gt;3. The mempool gives you nothing&lt;/h3&gt;
&lt;p&gt;Orders and cancels are off-chain (EIP-712 signatures), only the settlement goes to Polygon. We measured:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Source &lt;/th&gt;&lt;th&gt;Lag behind matching&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Order book WebSocket &lt;/td&gt;&lt;td&gt;~17 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;On-chain settlement &lt;/td&gt;&lt;td&gt;~2 s median&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;By the time a trade shows up in the Polygon mempool, it already happened and the book already moved. Watch the WebSocket, not the chain.&lt;/p&gt;
&lt;h3 id=&quot;4-websocket-events-arrive-out-of-order&quot;&gt;4. WebSocket events arrive out of order&lt;/h3&gt;
&lt;p&gt;The exchange can send &lt;code&gt;price_change&lt;/code&gt; (a level got eaten) before the &lt;code&gt;last_trade_price&lt;/code&gt; event of the same trade. We replayed fills in arrival order at first, and every fill looked about 25% worse than it really was. Sort by the trade, not by arrival time, before you trust any replay.&lt;/p&gt;
&lt;p&gt;For what it's worth, a plain WebSocket recorder (&lt;code&gt;price_change&lt;/code&gt;, &lt;code&gt;book&lt;/code&gt;, &lt;code&gt;last_trade_price&lt;/code&gt; with tx hash) captured 99.9% of the trades that later appeared on-chain, so you don't need anything fancier to build a replay dataset.&lt;/p&gt;
&lt;h3 id=&quot;5-your-backtest-can-lie-with-perfect-data&quot;&gt;5. Your backtest can lie with perfect data&lt;/h3&gt;
&lt;p&gt;This one wasn't the API, it was us. We picked wallets that bought right before goals and simulated copying them 1-5 seconds later: +$4.23 per $10. Then we noticed we had only selected the buys that came right before goals. In real time you don't know which buys those are.&lt;/p&gt;
&lt;p&gt;Rerun on every buy by the same wallets, without looking at the outcome: -$0.41 per $10.&lt;/p&gt;
&lt;p&gt;Any filter that uses something you only learn later (a goal, a resolution, a price move) has to be removed from the selection step. If the result survives that, then you can start believing it.&lt;/p&gt;
&lt;h3 id=&quot;6-some-polymarket-bot-repos-want-your-private-key&quot;&gt;6. Some &quot;Polymarket bot&quot; repos want your private key&lt;/h3&gt;
&lt;p&gt;Before you reach for an existing open-source bot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.stepsecurity.io/blog/malicious-polymarket-bot-hides-in-hijacked-dev-protocol-github-org-and-steals-wallet-keys&quot;&gt;StepSecurity&lt;/a&gt; found 20+ fake repos with bought stars (&lt;code&gt;polymarket-trading-bot-new&lt;/code&gt;, &lt;code&gt;polymarket-copy-trading-bot-sports&lt;/code&gt;, ...) pulling typosquatted npm packages that steal keys and &lt;code&gt;.env&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://safedep.io/malicious-polymarket-npm-crypto-wallet-drainer/&quot;&gt;SafeDep&lt;/a&gt; found 9 npm packages (&lt;code&gt;polymarket-bot&lt;/code&gt;, &lt;code&gt;polymarket-claude-code&lt;/code&gt;, ...) that ask you to paste your wallet key.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read the code and the dependency tree before anything touches a funded key.&lt;/p&gt;
&lt;h3 id=&quot;smaller-gamma-api-notes&quot;&gt;Smaller Gamma API notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;offset&lt;/code&gt; pagination returns 422 somewhere after 2,100-2,500 records. Use the keyset endpoints for a full crawl.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/events/keyset&lt;/code&gt; ignores the &lt;code&gt;negRisk&lt;/code&gt; filter, filter on the market field instead. &lt;code&gt;/markets/keyset&lt;/code&gt; respects &lt;code&gt;end_date_min&lt;/code&gt; / &lt;code&gt;end_date_max&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Some requests fail without a &lt;code&gt;User-Agent&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The data is CC BY 4.0. If you're building something on Polymarket and want to check our numbers, the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/orcalayer/polymarket-arbitrage-market-making-copy-trading-backtests&quot;&gt;repo&lt;/a&gt; has every CSV, and we'd like to hear where we got it wrong.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-strategies-that-dont-work&quot;&gt;orcalayer.com&lt;/a&gt;. OrcaLayer is an independent Polymarket analytics provider, not affiliated with Polymarket.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30137/6-things-that-broke-our-polymarket-trading-bot-before-it-made-a-dollar</guid>
<pubDate>Wed, 07 Oct 2026 12:47:20 +0000</pubDate>
</item>
<item>
<title>Polymarket Gamma API endDate Is Not the Resolution Deadline</title>
<link>https://coderlegion.com/28532/polymarket-gamma-api-enddate-is-not-the-resolution-deadline</link>
<description>&lt;p&gt;&lt;em&gt;Checked against the Polymarket Gamma API on 24 September 2026.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Short answer.&lt;/strong&gt; OrcaLayer compared the &lt;code&gt;endDate&lt;/code&gt; field with the written rules on 23 Polymarket markets. Polymarket's documentation describes a market's end date as the point when it becomes eligible for resolution. The field did not behave that way on sports markets: on NFL and soccer matches it was the kickoff, on esports the start plus 6 hours, on tennis the original start plus 7 days. The deadline that governs a market is in its &lt;code&gt;description&lt;/code&gt;, and whether it still trades is in &lt;code&gt;closed&lt;/code&gt; and &lt;code&gt;acceptingOrders&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you build on Polymarket data, this changes what your code should read. A bot that stops trading at &lt;code&gt;endDate&lt;/code&gt;, a dashboard that counts down to it, and a model that treats it as the resolution time are all reading a field that means different things on different market types.&lt;/p&gt;
&lt;h3 id=&quot;what-is-enddate-in-the-polymarket-gamma-api-&quot;&gt;What is endDate in the Polymarket Gamma API?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;endDate&lt;/code&gt; is a UTC timestamp on every market in the Polymarket Gamma API. The API does not document what it represents, and it is not the resolution deadline. In OrcaLayer's sample it was kickoff on NFL and soccer matches, start plus 6 hours on esports, and original start plus 7 days on tennis. The binding deadline is in &lt;code&gt;description&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every market returned by &lt;code&gt;https://gamma-api.polymarket.com/markets/{id}&lt;/code&gt; carries &lt;code&gt;endDate&lt;/code&gt; at the top level. If you are new to these endpoints, start with our &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-rest-api-developers&quot;&gt;Polymarket REST API guide for developers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The response usually tells you what kind of market you are looking at. Sports matches carry &lt;code&gt;gameStartTime&lt;/code&gt;, &lt;code&gt;eventStartTime&lt;/code&gt;, &lt;code&gt;gameId&lt;/code&gt; and &lt;code&gt;sportsMarketType&lt;/code&gt; (for example &lt;code&gt;&quot;moneyline&quot;&lt;/code&gt;). Markets that belong to a group carry &lt;code&gt;groupItemTitle&lt;/code&gt;, which can be a label such as &lt;code&gt;&quot;Match Winner&quot;&lt;/code&gt; or a date such as &lt;code&gt;&quot;September 30&quot;&lt;/code&gt;. Closed markets add &lt;code&gt;closedTime&lt;/code&gt; and &lt;code&gt;umaEndDate&lt;/code&gt;. The rules are in plain English in &lt;code&gt;description&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Polymarket's own &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.polymarket.com/concepts/resolution&quot;&gt;resolution documentation&lt;/a&gt; puts it plainly: &quot;Always read the resolution rules before trading.&quot; The rest of this article shows what happens when code reads &lt;code&gt;endDate&lt;/code&gt; instead.&lt;/p&gt;
&lt;h3 id=&quot;polymarket-enddate-by-market-type&quot;&gt;Polymarket endDate by market type&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Market type &lt;/th&gt;&lt;th&gt;Markets &lt;/th&gt;&lt;th&gt;What endDate was &lt;/th&gt;&lt;th&gt;What the rules say &lt;/th&gt;&lt;th&gt;Closed before or after endDate&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Esports (CS2, LoL, Dota 2) &lt;/td&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;Scheduled start + exactly 6 hours, 4 of 4 &lt;/td&gt;&lt;td&gt;A postponed match must be rescheduled to start within 14 days, or the market resolves 50-50 &lt;/td&gt;&lt;td&gt;All 4 before, by 19 minutes to 3 h 18 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tennis (ATP, ITF) &lt;/td&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;Original scheduled start + exactly 7 days, 4 of 4 &lt;/td&gt;&lt;td&gt;A winner must be determined within 14 days, or the market resolves 50-50 &lt;/td&gt;&lt;td&gt;All 4 before, by 6.3 to 6.8 days&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;NFL, college football, soccer matches &lt;/td&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;Kickoff, 4 of 4 &lt;/td&gt;&lt;td&gt;No fixed deadline: open until the game is completed &lt;/td&gt;&lt;td&gt;All 4 after, by 2 h 17 min to 4 h 57 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;MLB match &lt;/td&gt;&lt;td&gt;1 &lt;/td&gt;&lt;td&gt;Start + 7 days &lt;/td&gt;&lt;td&gt;No fixed deadline &lt;/td&gt;&lt;td&gt;Before, by about 7 days&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Sports futures &lt;/td&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;Varies &lt;/td&gt;&lt;td&gt;A &quot;postponed after&quot; backstop &lt;/td&gt;&lt;td&gt;Still open&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Deadline and price markets &lt;/td&gt;&lt;td&gt;7 &lt;/td&gt;&lt;td&gt;Matched the rules in 4 of 7 &lt;/td&gt;&lt;td&gt;&quot;By&quot; a date, or a date in the title &lt;/td&gt;&lt;td&gt;Mixed&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The sample is 23 hand-picked markets covering each type, so read each row as an observed pattern, not a platform-wide rate.&lt;/p&gt;
&lt;h3 id=&quot;esports-enddate-is-the-start-plus-six-hours&quot;&gt;Esports: endDate is the start plus six hours&lt;/h3&gt;
&lt;p&gt;All four esports matches had an &lt;code&gt;endDate&lt;/code&gt; exactly six hours after &lt;code&gt;gameStartTime&lt;/code&gt;. The Counter-Strike match between QUAZAR and Misa Esports started at 08:00 UTC on 23 September, carried an &lt;code&gt;endDate&lt;/code&gt; of 14:00 UTC, and closed at 13:41 UTC.&lt;/p&gt;
&lt;p&gt;Six hours is a fair guess for when a best-of-three finishes, but the rules for the same market allow far longer:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If the match is postponed, it must be rescheduled to a start time on or before October 7, 2026 at 11:59 PM ET (14 calendar days after the originally scheduled start of September 23, 2026 at 4:00 AM ET). If, by that deadline, the most recently announced rescheduled start time is on or before the deadline, this market resolves based on the result of the match as played. Otherwise, this market resolves 50-50, regardless of whether the match is subsequently played.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The 14 days apply to the rescheduled start, not to the result. A match rescheduled to start on 7 October can resolve later still, long after &lt;code&gt;endDate&lt;/code&gt; has passed. You can see current esports markets on &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/esports&quot;&gt;OrcaLayer's esports page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;tennis-seven-days-after-the-original-schedule-and-it-does-not-move&quot;&gt;Tennis: seven days after the original schedule, and it does not move&lt;/h3&gt;
&lt;p&gt;Every tennis match in the sample had an &lt;code&gt;endDate&lt;/code&gt; exactly seven days after its originally scheduled start. Here the 14-day rule applies to the result:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If the match is canceled (not played at all), ends in a tie, or a winner has not been determined by October 6, 2026, 11:59 PM ET (14 days after the scheduled start), this market will resolve to 50-50.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rescheduling is where it goes wrong. In two ATP matches, &lt;code&gt;gameStartTime&lt;/code&gt; was later than the start printed in the rules. The US Open match between Ben Shelton and Carlos Alcaraz was scheduled for 8 September at 15:30 UTC and started on 9 September at 03:00 UTC. Its &lt;code&gt;endDate&lt;/code&gt; was 15 September at 15:30 UTC: seven days after the original slot, not the new one.&lt;/p&gt;
&lt;p&gt;A countdown built on &lt;code&gt;endDate&lt;/code&gt; for a tennis market counts from a schedule that may no longer exist. Take the start from &lt;code&gt;gameStartTime&lt;/code&gt; and the deadline from the rules.&lt;/p&gt;
&lt;h3 id=&quot;nfl-and-soccer-markets-enddate-is-kickoff-not-the-end-of-the-game&quot;&gt;NFL and soccer markets: endDate is kickoff, not the end of the game&lt;/h3&gt;
&lt;p&gt;On the Broncos vs Chiefs NFL game, the SMU vs Louisville college football game, a La Liga match and a Romanian Liga I match, &lt;code&gt;endDate&lt;/code&gt; was equal to kickoff to the second. All four markets closed after it, between 2 h 17 min and 4 h 57 min later, which is how long the games took.&lt;/p&gt;
&lt;p&gt;The rules on these markets set no deadline at all:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If the game is postponed, this market will remain open until the game has been completed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A kickoff is the one moment when a match market is certainly not ready to resolve. Code that compares the current time with &lt;code&gt;endDate&lt;/code&gt; would have marked all four of these markets as finished at kickoff, while the games were being played and the markets were still trading.&lt;/p&gt;
&lt;p&gt;Across the 16 closed markets in the sample, 6 closed after their &lt;code&gt;endDate&lt;/code&gt;. Four were these matches. The other two were a Bitcoin price market that closed 14 minutes after its reference time and a Fed rate market, covered below.&lt;/p&gt;
&lt;h3 id=&quot;sports-futures-sometimes-the-event-sometimes-the-backstop&quot;&gt;Sports futures: sometimes the event, sometimes the backstop&lt;/h3&gt;
&lt;p&gt;League and tournament winner markets followed no single rule. On Shakhtar Donetsk winning the 2026-27 UEFA Champions League, &lt;code&gt;endDate&lt;/code&gt; was 30 May 2027 at 23:59 UTC, while the rules fall back to &quot;Other&quot; only if the season is &quot;postponed after June 19, 2027, 11:59 PM ET&quot;, about twenty days later. The New York Yankees World Series market had an &lt;code&gt;endDate&lt;/code&gt; of 31 October 2026, and its rules allow postponement until 31 December 2026. The Boston Celtics NBA Finals market matched its rules to the minute.&lt;/p&gt;
&lt;p&gt;For futures, the field alone does not tell you whether you are looking at an expected event date or the formal backstop.&lt;/p&gt;
&lt;h3 id=&quot;deadline-markets-right-in-4-of-7-once-wrong-by-92-days&quot;&gt;Deadline markets: right in 4 of 7, once wrong by 92 days&lt;/h3&gt;
&lt;p&gt;Markets that ask whether something happens &quot;by&quot; a date came closest to what most people assume &lt;code&gt;endDate&lt;/code&gt; means. Two of them, &quot;Will Anthropic or OpenAI IPO first?&quot; and the Clarity Act market, matched the deadline in the rules, one with a one-minute offset (05:00 instead of 04:59 UTC). The two Bitcoin price markets also matched, though their rules point to &quot;the date specified in the title&quot; or &quot;the month specified in the title&quot;, so the deadline lives in the market name.&lt;/p&gt;
&lt;p&gt;Three did not match:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&quot;Will the US confirm that aliens exist by September 30?&quot;&lt;/strong&gt; The rules say &quot;by September 30, 2026, 11:59 PM ET&quot;, which is 1 October at 03:59 UTC. &lt;code&gt;endDate&lt;/code&gt; was 1 January 2027 at 04:59 UTC, &lt;strong&gt;92 days later&lt;/strong&gt;. That is exactly the &lt;code&gt;endDate&lt;/code&gt; of the parent event the market belongs to, which runs to the end of 2026, so the market appears to carry its event's date rather than its own. Two other fields in the same response point the right way or the wrong way: &lt;code&gt;groupItemTitle&lt;/code&gt; is &lt;code&gt;&quot;September 30&quot;&lt;/code&gt;, which matches the rules, and &lt;code&gt;hasReviewedDates&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, which shows that a reviewed flag does not guarantee the date matches the rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Fed rate market for the September 2026 meeting.&lt;/strong&gt; &lt;code&gt;endDate&lt;/code&gt; was 16 September at 00:00 UTC, which is 8 PM ET on 15 September, the evening of the first day of a two-day meeting. The market closed 20 hours after its &lt;code&gt;endDate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The market on the next Russian parliamentary election.&lt;/strong&gt; &lt;code&gt;endDate&lt;/code&gt; was 30 September 2026 at 00:00 UTC. The rules fall back to &quot;Other&quot; only if results are not known by 30 September 2027. The market closed on 21 September.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On grouped date markets, check &lt;code&gt;groupItemTitle&lt;/code&gt; before you trust &lt;code&gt;endDate&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;why-enddate-changes-and-why-the-gamma-api-returns-old-values&quot;&gt;Why endDate changes, and why the Gamma API returns old values&lt;/h3&gt;
&lt;p&gt;Two markets returned different &lt;code&gt;endDate&lt;/code&gt; values depending on which snapshot the API served.&lt;/p&gt;
&lt;p&gt;For the aliens market, a response with &lt;code&gt;updatedAt&lt;/code&gt; of 21 September showed &lt;code&gt;endDate&lt;/code&gt; as 1 October 2026 at 03:59 UTC, which matches the rules. A fresh response on 24 September showed 1 January 2027. For the IPO market, a response with &lt;code&gt;updatedAt&lt;/code&gt; of 8 August showed 31 December 2027 at 00:00 UTC, 29 hours before the written deadline, and the fresh response showed 1 January 2028 at 04:59 UTC, which matches the rules. Between those snapshots, the IPO market was corrected and the aliens market moved 92 days away from its own rules.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;updatedAt&lt;/code&gt; will not tell you when that happened. It changes often and in bulk: at one point on 24 September the aliens, IPO, Celtics and Bitcoin markets all carried the same &lt;code&gt;updatedAt&lt;/code&gt; to the microsecond. Store your own fetch time instead.&lt;/p&gt;
&lt;p&gt;The older values reached us through cached responses. In three cases in the sample, a plain request to &lt;code&gt;/markets/{id}&lt;/code&gt; returned an older snapshot, and adding an unused query parameter returned the current one. If your code reads &lt;code&gt;endDate&lt;/code&gt; once when a market is listed, it may be holding a value the platform has since changed.&lt;/p&gt;
&lt;h3 id=&quot;enddate-vs-gamestarttime-vs-closedtime-vs-umaenddate&quot;&gt;endDate vs gameStartTime vs closedTime vs umaEndDate&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Field &lt;/th&gt;&lt;th&gt;What it held in the sample &lt;/th&gt;&lt;th&gt;Safe use&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;endDate&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Kickoff, start + 6 h, start + 7 days, or a deadline, depending on market type &lt;/td&gt;&lt;td&gt;A hint, never a deadline&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;gameStartTime&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Start of the sports event, updated when a match was rescheduled &lt;/td&gt;&lt;td&gt;Event start&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;closedTime&lt;/code&gt; &lt;/td&gt;&lt;td&gt;When the market actually closed &lt;/td&gt;&lt;td&gt;After-the-fact close time&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;umaEndDate&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Identical to &lt;code&gt;closedTime&lt;/code&gt; on all 16 closed markets &lt;/td&gt;&lt;td&gt;Same as &lt;code&gt;closedTime&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;description&lt;/code&gt; &lt;/td&gt;&lt;td&gt;The written rules, including any deadline in ET &lt;/td&gt;&lt;td&gt;The real deadline&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The formats differ. &lt;code&gt;gameStartTime&lt;/code&gt; and &lt;code&gt;closedTime&lt;/code&gt; come as &lt;code&gt;&quot;2026-09-23 08:00:00+00&quot;&lt;/code&gt;, while &lt;code&gt;endDate&lt;/code&gt; and &lt;code&gt;eventStartTime&lt;/code&gt; use &lt;code&gt;&quot;2026-09-23T08:00:00Z&quot;&lt;/code&gt;. &lt;code&gt;datetime.fromisoformat&lt;/code&gt; rejects both forms before Python 3.11, so normalize first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from datetime import datetime

def parse_ts(s: str) -&amp;gt; datetime:
    &quot;&quot;&quot;Parse any Gamma API timestamp into an aware UTC datetime.&quot;&quot;&quot;
    s = s.replace(&quot; &quot;, &quot;T&quot;).replace(&quot;Z&quot;, &quot;+00:00&quot;)
    if s.endswith(&quot;+00&quot;):
        s += &quot;:00&quot;
    return datetime.fromisoformat(s)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;how-to-tell-if-a-polymarket-market-is-still-trading&quot;&gt;How to tell if a Polymarket market is still trading&lt;/h3&gt;
&lt;p&gt;Do not use &lt;code&gt;endDate&lt;/code&gt; for this, and do not use &lt;code&gt;active&lt;/code&gt; either. The closed CS2 market in the sample returned &lt;code&gt;&quot;active&quot;: true&lt;/code&gt; together with &lt;code&gt;&quot;closed&quot;: true&lt;/code&gt; and &lt;code&gt;&quot;acceptingOrders&quot;: false&lt;/code&gt;. Read &lt;code&gt;closed&lt;/code&gt; and &lt;code&gt;acceptingOrders&lt;/code&gt;, and for the resolution state read &lt;code&gt;umaResolutionStatus&lt;/code&gt;, which was &lt;code&gt;&quot;resolved&quot;&lt;/code&gt; on that market.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def is_tradable(m: dict) -&amp;gt; bool:
    return not m.get(&quot;closed&quot;) and bool(m.get(&quot;acceptingOrders&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not bulletproof. In January 2026 a public report on Polymarket's Rust SDK repository showed finished Africa Cup of Nations markets still returning &lt;code&gt;active=true&lt;/code&gt;, &lt;code&gt;closed=false&lt;/code&gt; and &lt;code&gt;acceptingOrders=true&lt;/code&gt;, with &lt;code&gt;endDate&lt;/code&gt; equal to kickoff (&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Polymarket/rs-clob-client/issues/199&quot;&gt;issue #199&lt;/a&gt;). On our September sample the two flags were reliable, but treat them as the best signal available, not a guarantee.&lt;/p&gt;
&lt;p&gt;Closing and resolving are separate steps. After a market closes, the outcome is proposed to the UMA Optimistic Oracle, and according to Polymarket's documentation an undisputed proposal resolves after a 2-hour challenge period.&lt;/p&gt;
&lt;h3 id=&quot;how-to-find-the-real-resolution-deadline-of-a-polymarket-market&quot;&gt;How to find the real resolution deadline of a Polymarket market&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Read &lt;code&gt;description&lt;/code&gt;.&lt;/strong&gt; That is the contract. Sports matches say &quot;on or before … ET&quot; or &quot;by … ET (14 days after the scheduled start)&quot;. US match markets say they &quot;remain open until the game has been completed&quot;. Futures say &quot;postponed after …&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check the title and &lt;code&gt;groupItemTitle&lt;/code&gt;&lt;/strong&gt; on price and date markets. &quot;On September 17&quot; or a &lt;code&gt;groupItemTitle&lt;/code&gt; of &lt;code&gt;&quot;September 30&quot;&lt;/code&gt; is the deadline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Take the start from &lt;code&gt;gameStartTime&lt;/code&gt;&lt;/strong&gt;, not from &lt;code&gt;endDate&lt;/code&gt; minus a guess.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;closedTime&lt;/code&gt; or &lt;code&gt;umaEndDate&lt;/code&gt;&lt;/strong&gt; to know when a market actually closed. Both are empty until it does.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Re-fetch before you act.&lt;/strong&gt; Add a cache-busting parameter and store your fetch time next to every value you keep.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;python-parse-the-resolution-deadline-from-a-market-description&quot;&gt;Python: parse the resolution deadline from a market description&lt;/h4&gt;
&lt;p&gt;This parser found the deadline on every market in the sample whose rules state a full date and a clock time in Eastern Time:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import re
from datetime import datetime
from zoneinfo import ZoneInfo

MONTHS = &quot;January|February|March|April|May|June|July|August|September|October|November|December&quot;
DEADLINE = re.compile(
    rf&quot;\b(?:on or before|by|after) ((?:{MONTHS}) \d{{1,2}}, \d{{4}}),? (?:at )?(\d{{1,2}}:\d{{2}} ?[AP]M) E[DS]?T\b&quot;
)

def rule_deadlines_utc(description: str) -&amp;gt; list[datetime]:
    &quot;&quot;&quot;Return every ET deadline stated in a Polymarket market description, in UTC.&quot;&quot;&quot;
    found = []
    for day, clock in DEADLINE.findall(description):
        local = datetime.strptime(f&quot;{day} {clock.replace(' ', '')}&quot;, &quot;%B %d, %Y %I:%M%p&quot;)
        local = local.replace(tzinfo=ZoneInfo(&quot;America/New_York&quot;))
        found.append(local.astimezone(ZoneInfo(&quot;UTC&quot;)))
    return found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;America/New_York&lt;/code&gt; rather than a fixed offset: ET is UTC-4 until 1 November 2026 and UTC-5 after, so a hard-coded offset will be an hour wrong for several months of every year. The parser returns nothing on US match markets, because their rules contain no deadline, and nothing on price markets, where the date is in the title. It does not handle &quot;noon&quot;, &quot;p.m.&quot; or abbreviated months, and it does not tell a start deadline from a result deadline, so read the sentence around each match.&lt;/p&gt;
&lt;h3 id=&quot;what-this-sample-does-not-tell-you&quot;&gt;What this sample does not tell you&lt;/h3&gt;
&lt;p&gt;Twenty-three markets show that &lt;code&gt;endDate&lt;/code&gt; means different things on different market types. They do not show how often each case occurs across Polymarket, or that every esports market uses exactly six hours. Valorant is not in the sample, and there is a single MLB game.&lt;/p&gt;
&lt;p&gt;We did not establish why the aliens market moved, or whether that change was deliberate. All markets here come from the international Polymarket Gamma API. Polymarket US is a separate exchange with its own contract rules, and nothing in this article applies to it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Data: 23 markets fetched one by one from &lt;code&gt;gamma-api.polymarket.com/markets/{id}&lt;/code&gt; on 24 September 2026. The full sample is downloadable as &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/data/polymarket-enddate-sample-2026-09-24.json&quot;&gt;JSON&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://orcalayer.com/blog/polymarket-api-enddate&quot;&gt;orcalayer.com&lt;/a&gt;. OrcaLayer is an independent Polymarket analytics provider, not affiliated with Polymarket.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28532/polymarket-gamma-api-enddate-is-not-the-resolution-deadline</guid>
<pubDate>Thu, 24 Sep 2026 17:36:25 +0000</pubDate>
</item>
<item>
<title>Planning to Build a Scalable Prediction Market Platform Using a Polymarket Clone Script?</title>
<link>https://coderlegion.com/23856/planning-to-build-a-scalable-prediction-market-platform-using-a-polymarket-clone-script</link>
<description>&lt;p&gt;Many businesses are now showing interest in prediction market platforms because they create new ways for users to participate and generate revenue. Building a platform from the beginning takes more time, money, and technical work. To make the process easier, many businesses choose a Polymarket Clone Script because it helps them launch a prediction market platform faster while supporting future business growth.&lt;br&gt;
The Growing Demand for Prediction Market Platforms&lt;br&gt;
Prediction market platforms are becoming more popular across different industries. People like to predict the outcome of sports, politics, crypto, finance, and other real-world events. As more users join these platforms, businesses are finding new opportunities to enter this growing market.&lt;br&gt;
Building a Scalable Prediction Market Business&lt;br&gt;
Starting a prediction market business requires proper planning and the right technology. Businesses need a platform that is easy to manage, secure for users, and ready to handle future growth.&lt;br&gt;
Core Business Advantages&lt;br&gt;
Every business wants a platform that is easy to manage, supports business growth, and provides a better experience for users. A well-developed prediction market platform offers several advantages that help businesses grow in a competitive market.&lt;br&gt;
Rapid Platform Launch with Ready-to-Deploy Solutions&lt;br&gt;
Ready-made solutions help businesses reduce development time and launch their platform much faster. This allows companies to enter the market quickly and start attracting users without waiting for a long development process.&lt;br&gt;
Cost-Effective Development for Startups and Enterprises&lt;br&gt;
Building a platform from scratch can be expensive. Using a ready solution helps businesses reduce development costs while still getting the important features needed to run a prediction market platform.&lt;br&gt;
Blockchain-Powered Transparency and Security&lt;br&gt;
Users always expect a secure and transparent platform. Blockchain technology helps record transactions safely and improves trust between the platform and its users.&lt;br&gt;
Scalable Infrastructure for Business Growth&lt;br&gt;
As more users join the platform, the system should continue to work smoothly. A scalable infrastructure supports higher traffic and larger trading activities without reducing platform performance.&lt;br&gt;
Enhanced User Experience and Market Performance&lt;br&gt;
An easy-to-use platform helps users participate without confusion. Simple navigation, fast loading speed, and smooth performance improve user satisfaction and encourage them to return.&lt;br&gt;
Multiple Revenue Opportunities for Platform Owners&lt;br&gt;
Prediction market platforms can generate income in different ways. Businesses can earn through transaction fees, market listing fees, premium services, and other platform-based revenue models.&lt;br&gt;
Expanding Your Business&lt;br&gt;
A growing prediction market platform gives businesses more opportunities to reach new users and enter different markets. As the business expands, new features and services can be introduced to improve user engagement. A strong platform also helps businesses build customer trust, increase platform activity, and create long-term growth opportunities in the prediction market industry.&lt;br&gt;
Conclusion&lt;br&gt;
The prediction market industry continues to grow, creating new business opportunities for startups and established companies. Choosing a Polymarket Clone Script helps businesses launch their platform faster, reduce development costs, and prepare for future growth. Working with an experienced development company like Maticz can help businesses build a reliable prediction market platform that supports long-term business success.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23856/planning-to-build-a-scalable-prediction-market-platform-using-a-polymarket-clone-script</guid>
<pubDate>Fri, 31 Jul 2026 12:35:20 +0000</pubDate>
</item>
</channel>
</rss>