<?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: Ahmed Trading</title>
    <description>The latest articles on DEV Community by Ahmed Trading (@ahmedtrade).</description>
    <link>https://dev.to/ahmedtrade</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%2F4174177%2F9db1c149-1554-44cb-9de1-c65c6dbe1ead.png</url>
      <title>DEV Community: Ahmed Trading</title>
      <link>https://dev.to/ahmedtrade</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahmedtrade"/>
    <language>en</language>
    <item>
      <title>Building Bots That Survive a Breach: Rate Guards, Kill Switches, and October 29</title>
      <dc:creator>Ahmed Trading</dc:creator>
      <pubDate>Sun, 01 Nov 2026 18:39:53 +0000</pubDate>
      <link>https://dev.to/ahmedtrade/building-bots-that-survive-a-breach-rate-guards-kill-switches-and-october-29-4lo7</link>
      <guid>https://dev.to/ahmedtrade/building-bots-that-survive-a-breach-rate-guards-kill-switches-and-october-29-4lo7</guid>
      <description>&lt;p&gt;Building Bots That Survive a Breach: Rate Guards, Kill Switches, and October 29&lt;/p&gt;

&lt;p&gt;When a platform gets breached, most trading bots fail in one of three boring ways: they keep hammering endpoints that are being drained, they hold session keys nobody can revoke, or they have no way to stop themselves. October 29, 2026 — when a hijacked script drained $2.73M from 737 Polymarket accounts and Binance lost $6.3B the same day — was a live drill for all three failure modes.&lt;/p&gt;

&lt;p&gt;This is the bot-survivability checklist I run after every incident. It assumes you already have a working bot; if you don't, the &lt;a href="https://brainresearchhub.net/research/sdk/polymarket-bot-python-guide" rel="noopener noreferrer"&gt;Polymarket bot build guide&lt;/a&gt; covers the foundation.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Rate guards: your bot is a client, treat it like one
&lt;/h2&gt;

&lt;p&gt;After a breach, platforms throttle, pause, or reshuffle endpoints while they triage. A bot without request budgeting hammers through the throttle window and gets its key flagged — sometimes permanently, sometimes along with the account.&lt;/p&gt;

&lt;p&gt;Concretely: every outbound call goes through a token-bucket limiter keyed by endpoint class, with exponential backoff on 429/5xx and a circuit breaker that trips after N consecutive failures. My &lt;a href="https://brainresearchhub.net/research/sdk/polymarket-rate-limits" rel="noopener noreferrer"&gt;rate-guard reference implementation&lt;/a&gt; shows the pattern; the same post covers per-endpoint budgets for the current API.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Session keys with expiry, never raw keys
&lt;/h2&gt;

&lt;p&gt;The wallets drained on October 29 were not bot wallets — but the lesson transfers. A bot holding your raw private key is a bot that can lose everything your key can lose. A bot holding a scoped session key with an expiry is a bot that can lose a bounded amount for a bounded time.&lt;/p&gt;

&lt;p&gt;Issue session keys with the narrowest scope your strategy allows, rotate them on a schedule, and rehearse revocation before you need it. The &lt;a href="https://brainresearchhub.net/research/sdk/polymarket-session-keys" rel="noopener noreferrer"&gt;session keys guide&lt;/a&gt; covers issuance and rotation on Polymarket specifically.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A kill switch you can hit from your phone
&lt;/h2&gt;

&lt;p&gt;Every trading bot needs a component that stops it — not a config change, not a redeploy, a switch. Mine is a dead-simple endpoint check: the bot polls a local flag file every loop, and if the flag says "halt", it cancels open orders, closes nothing (closing is a decision, not a reflex), and goes idle.&lt;/p&gt;

&lt;p&gt;On October 29, the bots that survived were the ones whose operators could halt them in one action from a phone, without SSH-ing into anything. If your kill switch requires infrastructure access during an incident, you don't have one — you have a hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Breach-day behavior is different from normal behavior
&lt;/h2&gt;

&lt;p&gt;The checklist above is table stakes. Breach-day behavior is a separate mode, and it should be explicit in your code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On circuit-breaker trip: stop quoting, cancel resting orders, hold positions. Don't "wait it out" with live orders in a book that's being repriced by panic.&lt;/li&gt;
&lt;li&gt;On session-key anomaly (any signature request you didn't initiate): halt, revoke, rotate. Assume compromise first, investigate after.&lt;/li&gt;
&lt;li&gt;On platform announcement of an incident: assume the announcement lags reality by minutes to hours. Your risk window starts at first anomaly, not first announcement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://brainresearchhub.net/research/security/wallet-drained-first-24-hours" rel="noopener noreferrer"&gt;first-24-hours playbook&lt;/a&gt; is written for humans with drained wallets, but the ordering logic — move what survives first, revoke second, document third — maps directly onto bot priorities.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The audit that costs ten minutes
&lt;/h2&gt;

&lt;p&gt;After any incident: every approval on every wallet the bot touches, every session key, every standing allowance — reviewed and trimmed. Unlimited approvals don't survive my audits anymore, and neither should yours. It's the same &lt;a href="https://brainresearchhub.net/research/security/revoke-approvals-guide" rel="noopener noreferrer"&gt;revoke discipline&lt;/a&gt; human traders need, applied to machine traders.&lt;/p&gt;

&lt;p&gt;October 29 didn't create new risks. It priced the ones everyone was already carrying.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Background: the &lt;a href="https://brainresearchhub.net/research/security/october-29-double-breach-polymarket-binance" rel="noopener noreferrer"&gt;full October 29 double-breach analysis&lt;/a&gt;, and &lt;a href="https://brainresearchhub.net/research/sdk/polymarket-authentication" rel="noopener noreferrer"&gt;API authentication mechanics&lt;/a&gt; if you're reviewing your key handling. General reading on the Binance side: &lt;a href="https://en.wikipedia.org/wiki/2026_Binance_hack" rel="noopener noreferrer"&gt;2026 Binance hack&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
    </item>
    <item>
      <title>Exponential Backoff and Request Budgets for Trading APIs in Python</title>
      <dc:creator>Ahmed Trading</dc:creator>
      <pubDate>Tue, 20 Oct 2026 16:33:28 +0000</pubDate>
      <link>https://dev.to/ahmedtrade/exponential-backoff-and-request-budgets-for-trading-apis-in-python-4i6n</link>
      <guid>https://dev.to/ahmedtrade/exponential-backoff-and-request-budgets-for-trading-apis-in-python-4i6n</guid>
      <description>&lt;p&gt;Trading APIs fail in a predictable rhythm: fine for hours, then a burst of 429s exactly when your bot is busiest. The fix is not "retry harder" — it is a request budget with jittered exponential backoff, enforced in one place.&lt;/p&gt;

&lt;p&gt;This post shows the minimal pattern I use for Polymarket CLOB/Data API work: a token bucket for steady-state pacing, backoff with jitter for bursts, and a hard ceiling so one bad hour does not take the scanner offline all day. The design rationale and observed limits are documented separately: &lt;a href="https://brainresearchhub.net/research/sdk/rate-limit-design-patterns" rel="noopener noreferrer"&gt;https://brainresearchhub.net/research/sdk/rate-limit-design-patterns&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The three components
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Token bucket&lt;/strong&gt; — enforces average rate with burst allowance, centrally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backoff with jitter&lt;/strong&gt; — handles 429s without re-synchronizing your workers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry cap + kill switch hook&lt;/strong&gt; — fails loud instead of hammering forever.
&lt;/li&gt;
&lt;/ol&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;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TokenBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;rate = tokens per second; capacity = max burst.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&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;capacity&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&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;1.0&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="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&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="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;n&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="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_retry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;base&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.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cap&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;45.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;fn() returns an object with .status_code and .headers (requests-style).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_retry&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="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&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;wait&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="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fn&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;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;429&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;resp&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;max_retry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&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;budget exhausted after &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_retry&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; retries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ra&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="n"&gt;headers&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;Retry-After&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;delay&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;ra&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;ra&lt;/span&gt; &lt;span class="k"&gt;else&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;cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt&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="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&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;delay&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# jitter: de-synchronize workers
&lt;/span&gt;        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wiring it in
&lt;/h2&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;requests&lt;/span&gt;

&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TokenBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# 5 rps sustained, 20 burst
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_markets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;requests&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://data-api.polymarket.com/markets?slug=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;slug&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="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules I follow that the code cannot enforce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One bucket per process, not per module.&lt;/strong&gt; Two components each "within limits" is how you exceed limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never retry non-idempotent writes blind.&lt;/strong&gt; An order POST that times out may have succeeded; reconcile from the order channel instead of re-posting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscribe instead of polling.&lt;/strong&gt; The market WebSocket channel kills most of your REST load; the stream architecture is covered here: &lt;a href="https://brainresearchhub.net/research/sdk/websocket-stream-architecture" rel="noopener noreferrer"&gt;https://brainresearchhub.net/research/sdk/websocket-stream-architecture&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The retry loop calls the kill switch, not the logger.&lt;/strong&gt; When retries exhaust, halt order placement: &lt;a href="https://brainresearchhub.net/research/sdk/kill-switch-design" rel="noopener noreferrer"&gt;https://brainresearchhub.net/research/sdk/kill-switch-design&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why jitter is non-negotiable
&lt;/h2&gt;

&lt;p&gt;Without jitter, all your workers that hit the 429 at the same moment retry on the same beat, re-synchronize, and trip the limit again. Uniform jitter in &lt;code&gt;delay * (1 ± 0.2)&lt;/code&gt; breaks the lockstep. It looks trivial and it halves your ban windows in practice.&lt;/p&gt;

&lt;p&gt;Background reading: the generic mechanics of rate limiting (token bucket, leaky bucket) are well covered on Wikipedia: &lt;a href="https://en.wikipedia.org/wiki/Rate_limiting" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Rate_limiting&lt;/a&gt; — and for a regulated-venue comparison, the Kalshi API guide: &lt;a href="https://brainresearchhub.net/research/sdk/kalshi-api-guide" rel="noopener noreferrer"&gt;https://brainresearchhub.net/research/sdk/kalshi-api-guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational purposes only. Not financial advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
    </item>
    <item>
      <title>Building a Polymarket Perps Trading Bot: Architecture Guide (October 2026)</title>
      <dc:creator>Ahmed Trading</dc:creator>
      <pubDate>Sun, 11 Oct 2026 11:04:47 +0000</pubDate>
      <link>https://dev.to/ahmedtrade/building-a-polymarket-perps-trading-bot-architecture-guide-october-2026-b5h</link>
      <guid>https://dev.to/ahmedtrade/building-a-polymarket-perps-trading-bot-architecture-guide-october-2026-b5h</guid>
      <description>&lt;p&gt;When I came to America from Dubai with nothing but a suitcase and a plan, I didn't know those plans would lead through MIT, Jump Trading, and Alameda Research. But the engineering patterns I learned along the way are exactly what you need to build a production-grade trading bot for Polymarket Perps. This guide is the architecture I wish someone had written down in October 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The big picture
&lt;/h2&gt;

&lt;p&gt;A perps trading bot has four layers, and every layer deserves its own process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data ingestion&lt;/strong&gt; — subscribe to websocket feeds for orderbook, trades, and index prices. Never poll REST in a loop for data you can stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strategy engine&lt;/strong&gt; — takes normalized market state, decides actions. Pure function in, orders out. No hidden state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution&lt;/strong&gt; — signs and submits orders, handles partial fills and retries with idempotency keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk management&lt;/strong&gt; — position limits, drawdown limits, kill switch. This layer overrides everything else, always.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I was at Jump, the desk principle was simple: the risk layer must be able to cancel every order in one operation, without asking the strategy engine for permission. Build that first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data ingestion details
&lt;/h2&gt;

&lt;p&gt;For Polymarket Perps you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a persistent websocket connection per market group, with sequence numbers checked on every message&lt;/li&gt;
&lt;li&gt;a REST fallback for snapshots on reconnect&lt;/li&gt;
&lt;li&gt;a clock-sync routine so you can reason about latency honestly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reconnect logic is where hobby bots die. Exponential backoff, snapshot-then-delta recovery, and a watchdog that restarts the whole ingestion layer if staleness exceeds your threshold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy engine: keep it boring
&lt;/h2&gt;

&lt;p&gt;Market making on perps is quoting two-sided prices around a fair value you compute from the underlying prediction market. Your fair value model can start simple: index price plus a skew adjustment from orderbook imbalance.&lt;/p&gt;

&lt;p&gt;The complexity belongs in inventory management, not in signals. Track net position, skew quotes away from inventory extremes, and cap notional per market. A boring strategy with good inventory control beats a clever strategy with none.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution: idempotency or bust
&lt;/h2&gt;

&lt;p&gt;Every order submission carries a client-generated idempotency key. On timeout, query order state before retrying — never blind-retry submissions. Partial fills reduce remaining size atomically.&lt;/p&gt;

&lt;p&gt;Rate limits are part of the API surface. Budget your calls like memory in embedded systems: fixed allocation, no leaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risk management is the product
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Max position per market&lt;/strong&gt; — absolute notional cap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Max drawdown per day&lt;/strong&gt; — when hit, flatten and stop until next session, automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kill switch&lt;/strong&gt; — one operation cancels all quotes and orders. Test it weekly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanity checks&lt;/strong&gt; — reject any order that moves your position beyond limits, no matter what the strategy says.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Alameda I watched risk discipline decay gradually and then suddenly. The lesson: automation of limits is not optional; humans are terrible at cutting their own losses in the moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;In the next posts I'll walk through the funding-rate mechanics of Polymarket Perps (what the docs say, term by term), then margin modes and the liquidation waterfall with real numbers.&lt;/p&gt;

&lt;p&gt;The full architecture with code lives in my GitHub repo: &lt;a href="https://github.com/tradingbotsbuilder/polymarket-mm-bot" rel="noopener noreferrer"&gt;polymarket-mm-bot&lt;/a&gt;. It's MIT licensed — take it, fork it, build on it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;In America, if you work hard and smart, you win. Not financial advice — trading involves substantial risk of loss.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  polymarket #algorithmictrading #marketmaking #crypto #python
&lt;/h1&gt;

</description>
      <category>polymarket</category>
      <category>cryptocurrency</category>
    </item>
  </channel>
</rss>
