<?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: Casatrick | Polymrket Bot Dev </title>
    <description>The latest articles on DEV Community by Casatrick | Polymrket Bot Dev  (@casatrick).</description>
    <link>https://dev.to/casatrick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3974435%2F79d96b2f-ede8-4bc5-bb08-284099e64e4e.jpg</url>
      <title>DEV Community: Casatrick | Polymrket Bot Dev </title>
      <link>https://dev.to/casatrick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/casatrick"/>
    <language>en</language>
    <item>
      <title>Polymarket Protocol Versions: V1, V2, V3 and What Trading Bots Need to Know</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Wed, 07 Oct 2026 05:21:39 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-protocol-versions-v1-v2-v3-and-what-trading-bots-need-to-know-37ej</link>
      <guid>https://dev.to/casatrick/polymarket-protocol-versions-v1-v2-v3-and-what-trading-bots-need-to-know-37ej</guid>
      <description>&lt;p&gt;Polymarket version numbers are getting harder to talk about casually.&lt;/p&gt;

&lt;p&gt;You can see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;V1
V2
V3
PolyV2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and assume there is one protocol that simply moved from version 1 to version 2 and then version 3.&lt;/p&gt;

&lt;p&gt;That is not how the current stack is organized.&lt;/p&gt;

&lt;p&gt;There are different versions for different parts of the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exchange
Order signing
Position protocol
Market metadata
Data APIs
SDK packages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters when you maintain a trading bot.&lt;/p&gt;

&lt;p&gt;A package upgrade might be trivial.&lt;/p&gt;

&lt;p&gt;A protocol change can affect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Contracts
Collateral
Order structure
EIP-712 signing
Asset identifiers
Execution
Settlement
State reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The April 2026 Polymarket exchange upgrade was a coordinated change to the smart contracts, order-book system, and collateral layer. Polymarket describes the upgrade as moving to CTF Exchange V2 and pUSD, with changes to order structure, matching, fees, and order management.&lt;/p&gt;

&lt;p&gt;Since then, the protocol surface has continued to evolve.&lt;/p&gt;

&lt;p&gt;The current official clients now distinguish token-backed V1/V2 flows from PolyV2 position-backed V3 flows.&lt;/p&gt;

&lt;p&gt;For someone maintaining a Polymarket trading system, that means protocol compatibility needs to be treated as part of the infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  There is no single Polymarket "version"
&lt;/h2&gt;

&lt;p&gt;This is the first thing worth getting straight.&lt;/p&gt;

&lt;p&gt;A useful mental model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Polymarket
    │
    ├── Exchange protocol
    │
    ├── Order signing
    │
    ├── Position protocol
    │
    ├── Market metadata
    │
    ├── Data services
    │
    └── SDK packages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those layers can evolve independently.&lt;/p&gt;

&lt;p&gt;For example, the current official Rust CLOB client documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;V1
V2
V3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as protocol variants for order construction.&lt;/p&gt;

&lt;p&gt;Its README describes V1 as the legacy CTF-token path, V2 as the current CTF-token exchange path, and V3 as the Exchange V3 path used for Polymarket V2 positions.&lt;/p&gt;

&lt;p&gt;That is very different from saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API version = 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no reason to add &lt;code&gt;/v3&lt;/code&gt; to every Polymarket endpoint just because Exchange V3 exists.&lt;/p&gt;

&lt;p&gt;The versions belong to different layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  CLOB / Exchange V2 is a real protocol change
&lt;/h2&gt;

&lt;p&gt;The April 28, 2026 upgrade was not just a new SDK release.&lt;/p&gt;

&lt;p&gt;Polymarket changed the exchange stack itself.&lt;/p&gt;

&lt;p&gt;The official migration announcement describes three coordinated changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New smart contracts
        +
Rewritten order book
        +
New collateral token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new exchange contracts are &lt;strong&gt;CTF Exchange V2&lt;/strong&gt;, and the new collateral token is &lt;strong&gt;pUSD&lt;/strong&gt;, replacing USDC.e as the trading collateral layer.&lt;/p&gt;

&lt;p&gt;The contract repository describes CTF Exchange V2 as the core smart-contract system for trading Conditional Token Framework assets. It currently lists the deployed Polygon contracts for the collateral token, collateral adapters, standard exchange, and negative-risk exchange.&lt;/p&gt;

&lt;p&gt;This matters because an integration that only changes its Python or TypeScript dependency is not necessarily migrated.&lt;/p&gt;

&lt;p&gt;The underlying assumptions may also have changed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The order structure changed
&lt;/h2&gt;

&lt;p&gt;One of the clearest examples is the signed order itself.&lt;/p&gt;

&lt;p&gt;The current Rust client defines separate order structures for V1 and V2.&lt;/p&gt;

&lt;p&gt;The V1 structure contains fields such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;taker
nonce
feeRateBps
expiration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The V2 order structure instead uses fields including:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timestamp
metadata
builder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with expiration carried separately in the payload.&lt;/p&gt;

&lt;p&gt;The EIP-712 domain version also changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;V1 → "1"
V2 → "2"
V3 → "3"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is not cosmetic.&lt;/p&gt;

&lt;p&gt;The exchange contract and signed typed-data structure have to agree.&lt;/p&gt;

&lt;p&gt;A bot that manually constructs EIP-712 messages therefore needs to know which protocol it is signing for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contract addresses are part of the migration
&lt;/h2&gt;

&lt;p&gt;A protocol migration also changes the contracts that the trading system ultimately interacts with.&lt;/p&gt;

&lt;p&gt;The current CTF Exchange V2 repository lists these Polygon contracts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CTFExchangeV2
0xE111180000d2663C0091e4f400237545B87B996B

NegRiskCtfExchangeV2
0xe2222d279d744050d28e00520010520000310F59

CollateralToken proxy
0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB

CtfCollateralAdapter
0xADa100874d00e3331D00F2007a9c336a65009718

NegRiskCtfCollateralAdapter
0xAdA200001000ef00D07553cEE7006808F895c6F1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are documented by the official V2 exchange repository.&lt;/p&gt;

&lt;p&gt;For a trading bot, hardcoded contract addresses are therefore part of the compatibility surface.&lt;/p&gt;

&lt;p&gt;So are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verifying contracts
allowances
collateral handling
settlement assumptions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A migration audit should look for all of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  pUSD is part of the protocol change
&lt;/h2&gt;

&lt;p&gt;The collateral model also changed.&lt;/p&gt;

&lt;p&gt;Polymarket's April 2026 upgrade introduced &lt;strong&gt;pUSD&lt;/strong&gt;, an ERC-20 collateral token on Polygon backed 1:1 by USDC. The exchange documentation describes pUSD as the new technical collateral layer while trading settles in native USDC.&lt;/p&gt;

&lt;p&gt;That creates another compatibility boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old

USDC.e
   ↓
CTF / Exchange


New

USDC / pUSD
   ↓
Collateral layer
   ↓
CTF / Exchange
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bot that checks balances, approves contracts, wraps collateral, or manages inventory directly onchain needs to understand that layer.&lt;/p&gt;

&lt;p&gt;This becomes especially important for systems that are not simply using the web interface but are managing their own wallet flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fees also moved closer to protocol state
&lt;/h2&gt;

&lt;p&gt;Another important change is fee handling.&lt;/p&gt;

&lt;p&gt;Polymarket's current fee documentation says fees are applied at &lt;strong&gt;match time&lt;/strong&gt;, not included as a fee field in the order itself. The current formula is based on share count, fee rate, and share price.&lt;/p&gt;

&lt;p&gt;That means a trading system should avoid treating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fee = property of order creation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as a universal assumption.&lt;/p&gt;

&lt;p&gt;Instead, it should think more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
   ↓
Match
   ↓
Protocol determines fee
   ↓
Execution result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters for analytics as well.&lt;/p&gt;

&lt;p&gt;If you're measuring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gross PnL
Fees
Net PnL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the accounting layer needs to correspond to the actual execution model.&lt;/p&gt;

&lt;p&gt;It also matters for strategy selection because Polymarket currently charges taker fees on several market categories while makers are not charged fees and can receive rebates.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then there is PolyV2
&lt;/h2&gt;

&lt;p&gt;This is where version naming gets even more confusing.&lt;/p&gt;

&lt;p&gt;The current Polymarket clients support &lt;strong&gt;PolyV2 position identifiers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These are not simply another name for normal CTF token IDs.&lt;/p&gt;

&lt;p&gt;The official Python SDK changelog shows the progression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0.8.0
→ expose market protocol versions

0.9.0
→ support Poly V2 identifiers and trading

0.10.0
→ migrate data reads to Data API v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are SDK changes, but they reflect underlying protocol concepts that the application has to understand.&lt;/p&gt;

&lt;p&gt;The official Python models explicitly expose market protocol versions as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v1
v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and distinguish those values from the package version itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  PolyV2 orders use Exchange V3
&lt;/h2&gt;

&lt;p&gt;This is probably the easiest place for a developer to make the wrong assumption.&lt;/p&gt;

&lt;p&gt;The current official Rust CLOB client documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CTF token
    ↓
V2 exchange flow


Polymarket V2 position
    ↓
V3 exchange flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Position-backed orders bypass the normal protocol lookup and use &lt;strong&gt;Exchange V3&lt;/strong&gt;, with EIP-712 domain version &lt;code&gt;"3"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The TypeScript CLOB V2 release also added support for &lt;code&gt;positionID&lt;/code&gt; and explicitly says that it selects Exchange V3 signing for limit and market orders.&lt;/p&gt;

&lt;p&gt;So a trading system should not simply ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"What version of Polymarket am I using?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What asset am I trading?

What protocol does that asset use?

Which exchange signs that order?

Which contract verifies it?

What collateral path applies?

How is the resulting execution reported?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a much better compatibility model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution changed too
&lt;/h2&gt;

&lt;p&gt;Protocol upgrades don't stop at signing.&lt;/p&gt;

&lt;p&gt;The execution path itself has also changed.&lt;/p&gt;

&lt;p&gt;The official Rust CLOB changelog records a July 2026 change for an asynchronous execution pipeline in which matched orders can return &lt;code&gt;tradeIDs&lt;/code&gt; while transaction hashes are resolved later. The client added internal transaction-hash resolution for this flow.&lt;/p&gt;

&lt;p&gt;That changes how a bot should model execution.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER
  ↓
FILLED
  ↓
TRANSACTION HASH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you may need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER
  ↓
MATCHED
  ↓
TRADE ID
  ↓
TRANSACTION RESOLUTION
  ↓
CONFIRMATION
  ↓
SETTLEMENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is directly related to the execution-verification architecture I've been working on.&lt;/p&gt;

&lt;p&gt;A match tells you something important.&lt;/p&gt;

&lt;p&gt;It does not necessarily represent the final state of the entire execution lifecycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters for reconciliation
&lt;/h2&gt;

&lt;p&gt;This is where protocol knowledge becomes operational engineering.&lt;/p&gt;

&lt;p&gt;Suppose your local system records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order:
MATCHED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the transaction is still unresolved.&lt;/p&gt;

&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local position:
+100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while the remote position is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the market changes protocol representation while your application still has older assumptions in memory.&lt;/p&gt;

&lt;p&gt;The recovery problem is the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local assumptions
       ↓
Current protocol state
       ↓
Compare
       ↓
Reconcile
       ↓
Verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Protocol-awareness therefore belongs inside the state and execution layers.&lt;/p&gt;

&lt;p&gt;It is not something you check once during installation.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a protocol-aware trading bot should track
&lt;/h2&gt;

&lt;p&gt;I would keep protocol information explicit in the system.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market
   ↓
Protocol version
   ↓
Asset identifier
   ↓
Exchange path
   ↓
Signing version
   ↓
Contract
   ↓
Collateral
   ↓
Execution state
   ↓
Settlement state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can be represented as internal metadata rather than scattered assumptions.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;asset_type
protocol_version
position_id
token_id
exchange_version
chain_id
verifying_contract
collateral_type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact schema will depend on the implementation.&lt;/p&gt;

&lt;p&gt;The important part is not hiding these distinctions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don't build version logic around package versions
&lt;/h2&gt;

&lt;p&gt;A common mistake is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SDK v0.10
→ therefore protocol v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is not a safe relationship.&lt;/p&gt;

&lt;p&gt;The official Python SDK is currently on its own semantic versioning track, with the package at &lt;code&gt;0.12.0&lt;/code&gt; in the repository metadata, while its changelog separately records support for market protocol versions, PolyV2 identifiers, and Data API v2.&lt;/p&gt;

&lt;p&gt;Likewise, the dedicated CLOB V2 TypeScript package has its own release numbers such as &lt;code&gt;1.2.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These numbers describe software releases.&lt;/p&gt;

&lt;p&gt;They do not define the onchain protocol.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I would check before deploying a Polymarket bot
&lt;/h2&gt;

&lt;p&gt;For an existing system, I would treat this as a compatibility audit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Exchange contract addresses
[ ] EIP-712 domain version
[ ] Signed order fields
[ ] Asset identifiers
[ ] Protocol version metadata
[ ] Collateral path
[ ] Token / position approvals
[ ] Fee handling
[ ] Order submission
[ ] Execution events
[ ] Trade IDs
[ ] Transaction resolution
[ ] Settlement tracking
[ ] Position reconciliation
[ ] WebSocket recovery
[ ] Restart recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any of these are hardcoded assumptions, the system is carrying protocol risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  This is also why migrations should be tested end to end
&lt;/h2&gt;

&lt;p&gt;I would not consider a migration complete because:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;succeeds.&lt;/p&gt;

&lt;p&gt;The useful test is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market discovery
      ↓
Protocol detection
      ↓
Asset identification
      ↓
Order construction
      ↓
Signature
      ↓
Submission
      ↓
Match / fill
      ↓
Transaction
      ↓
Settlement
      ↓
Position
      ↓
PnL / fee accounting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tests the actual system boundary.&lt;/p&gt;

&lt;p&gt;It also gives you a much better way to catch protocol mismatches before they become trading incidents.&lt;/p&gt;




&lt;h2&gt;
  
  
  My current architecture
&lt;/h2&gt;

&lt;p&gt;This is the direction I'm taking with my own Polymarket infrastructure work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 POLYMARKET
                      │
                      ▼
              Protocol / Market
                      │
                      ▼
               Market Adapter
                      │
                      ▼
                  Strategy
                      │
                      ▼
                    Risk
                      │
                      ▼
                  Execution
                      │
                      ▼
              Execution Verifier
                      │
                      ▼
               Reconciliation
                      │
                      ▼
                Control Plane
                      │
             ┌────────┴────────┐
             ↓                 ↓
        Monitoring          Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point is to keep protocol-specific assumptions close to the integration boundary.&lt;/p&gt;

&lt;p&gt;The rest of the system can then work with clearer internal concepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
Fill
Position
Exposure
Risk
Execution status
System health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes future migrations easier to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bigger lesson
&lt;/h2&gt;

&lt;p&gt;Polymarket protocol changes are not really a problem for the SDK alone.&lt;/p&gt;

&lt;p&gt;They're an integration problem.&lt;/p&gt;

&lt;p&gt;A protocol upgrade can affect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Contracts
Signing
Collateral
Orders
Execution
Settlement
Accounting
State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And those changes eventually show up as operational problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;orders stop submitting
fills become harder to interpret
positions drift
retries become unsafe
accounting changes
reconciliation breaks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is why I think protocol compatibility should be treated as part of trading infrastructure.&lt;/p&gt;

&lt;p&gt;Not as a dependency upgrade.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;When someone says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Polymarket V2”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I would now ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;V2 which layer?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because today you may be dealing with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CTF Exchange V2
CLOB V2
PolyV2 positions
Exchange V3
Data API V2
SDK versions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are related, but they are not interchangeable labels.&lt;/p&gt;

&lt;p&gt;For a trading bot, the useful model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Protocol
   ↓
Asset
   ↓
Exchange
   ↓
Signing
   ↓
Execution
   ↓
Settlement
   ↓
Position
   ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the level at which compatibility actually matters.&lt;/p&gt;

&lt;p&gt;And for an existing trading system, the practical question is not whether the latest SDK installs.&lt;/p&gt;

&lt;p&gt;It is whether the complete path still agrees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contracts
→ signing
→ order
→ match
→ execution
→ settlement
→ position
→ risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is what I would test before trusting the bot with capital.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>programming</category>
    </item>
    <item>
      <title>Polymarket Trading Bot Order States: Handling Pending, Failed, and Unknown Orders</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Tue, 06 Oct 2026 05:48:17 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-order-states-handling-pending-failed-and-unknown-orders-g50</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-order-states-handling-pending-failed-and-unknown-orders-g50</guid>
      <description>&lt;p&gt;One of the harder parts of an automated trading system is not submitting an order.&lt;/p&gt;

&lt;p&gt;It is knowing what actually happened after the order was submitted.&lt;/p&gt;

&lt;p&gt;A bot can receive an incomplete response.&lt;/p&gt;

&lt;p&gt;An order can remain unresolved.&lt;/p&gt;

&lt;p&gt;A fill can arrive later.&lt;/p&gt;

&lt;p&gt;A connection can disappear between two events.&lt;/p&gt;

&lt;p&gt;The application can restart before it finishes processing the result.&lt;/p&gt;

&lt;p&gt;That creates a dangerous situation if the bot treats every order response as final.&lt;/p&gt;

&lt;p&gt;For a Polymarket trading bot, I prefer to model order execution as an explicit state machine rather than a single boolean such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order = successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simplified internal lifecycle looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
   ↓
SUBMITTED
   ↓
ACCEPTED
   ↓
MATCHED
   ↓
FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with other paths such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REJECTED
CANCELLED
FAILED
RETRYING
UNKNOWN
INCONSISTENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact state model can vary by implementation.&lt;/p&gt;

&lt;p&gt;The important part is that &lt;strong&gt;unknown should be a real state&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why a single order status is not enough
&lt;/h2&gt;

&lt;p&gt;A simple trading bot often does something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submit order
    ↓
get response
    ↓
if success:
    mark filled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works only when every step behaves exactly as expected.&lt;/p&gt;

&lt;p&gt;A more realistic execution flow looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strategy
   ↓
Order intent
   ↓
Submission
   ↓
Exchange response
   ↓
Matching
   ↓
Fill
   ↓
Transaction
   ↓
Confirmation
   ↓
Position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several different facts here.&lt;/p&gt;

&lt;p&gt;The strategy intended to place an order.&lt;/p&gt;

&lt;p&gt;The system submitted something.&lt;/p&gt;

&lt;p&gt;The venue accepted or rejected it.&lt;/p&gt;

&lt;p&gt;The order may or may not have matched.&lt;/p&gt;

&lt;p&gt;A fill may or may not have occurred.&lt;/p&gt;

&lt;p&gt;The resulting execution may still need to be tracked further.&lt;/p&gt;

&lt;p&gt;Collapsing all of that into one status makes recovery much harder.&lt;/p&gt;




&lt;h2&gt;
  
  
  An internal order state machine
&lt;/h2&gt;

&lt;p&gt;I usually find it easier to reason about execution with explicit states.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
   ↓
SUBMITTED
   ↓
ACCEPTED
   ↓
MATCHED
   ↓
FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible failure paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED → REJECTED
SUBMITTED → FAILED
ACCEPTED  → CANCELLED
MATCHED   → PARTIAL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when the system cannot establish what happened:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED
    ↓
UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The state machine is not meant to describe every possible platform implementation.&lt;/p&gt;

&lt;p&gt;It is an internal representation that lets the rest of the trading system make decisions based on what is actually known.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pending does not mean failed
&lt;/h2&gt;

&lt;p&gt;Suppose the strategy submits an order and does not immediately receive a final outcome.&lt;/p&gt;

&lt;p&gt;The wrong response is often:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No final response
      ↓
Assume failed
      ↓
Submit again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can create duplicate exposure.&lt;/p&gt;

&lt;p&gt;The order may still be alive.&lt;/p&gt;

&lt;p&gt;It may have been accepted but the application has not observed the next event yet.&lt;/p&gt;

&lt;p&gt;It may already have matched.&lt;/p&gt;

&lt;p&gt;The system simply does not know yet.&lt;/p&gt;

&lt;p&gt;So I prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED
    ↓
PENDING
    ↓
WAIT / VERIFY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than immediately converting uncertainty into failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The dangerous UNKNOWN state
&lt;/h2&gt;

&lt;p&gt;This is the state I think deserves the most attention.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order submitted
       ↓
Network interruption
       ↓
Response lost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bot now has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local state:
UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That does not mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also does not mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The application does not currently know.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;If the bot immediately retries, the first order could still exist.&lt;/p&gt;

&lt;p&gt;You could end up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Original order
      +
Retry order
      =
More exposure than intended
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unknown state therefore needs its own recovery path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unknown should block unsafe assumptions
&lt;/h2&gt;

&lt;p&gt;A useful rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't convert uncertainty into a successful or failed state just because the strategy needs an answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
   ↓
PAUSED / NO RETRY
   ↓
RECONCILE
   ↓
VERIFY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only after the system establishes enough information should it decide whether the order should be considered complete, failed, cancelled, or retried.&lt;/p&gt;

&lt;p&gt;This is one reason execution verification and reconciliation belong close to the trading system rather than being treated as dashboard features.&lt;/p&gt;




&lt;h2&gt;
  
  
  Retrying an order is not as simple as submitting it again
&lt;/h2&gt;

&lt;p&gt;A common implementation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if order failed:
    retry()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difficult part is determining whether the order actually failed.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order 123
   ↓
Submitted
   ↓
Response timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application might classify that as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the real state could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACCEPTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MATCHED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The network failure tells you that the application didn't get a response.&lt;/p&gt;

&lt;p&gt;It does not necessarily tell you what happened to the order.&lt;/p&gt;

&lt;p&gt;So the retry decision should depend on verified state, not just the absence of a response.&lt;/p&gt;

&lt;p&gt;A safer flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request timeout
      ↓
Mark UNKNOWN
      ↓
Query / reconcile
      ↓
Did the order exist?
      ↓
What is its current state?
      ↓
Only then decide:
retry / cancel / continue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Partial fills make the state model more important
&lt;/h2&gt;

&lt;p&gt;Suppose the strategy intends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual execution is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40 filled
60 remaining
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is neither a clean failure nor a clean completion.&lt;/p&gt;

&lt;p&gt;The system needs to represent the difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requested quantity = 100
filled quantity    = 40
remaining quantity = 60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting state could be represented internally as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PARTIALLY_FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that downstream calculations use the actual fill.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
  ↓
40 Fill
  ↓
Position +40
  ↓
Exposure
  ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order 100
  ↓
Assume Position +100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This connects directly to position reconciliation.&lt;/p&gt;

&lt;p&gt;A trading system cannot maintain reliable risk controls when it confuses order intent with actual execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Order state affects position state
&lt;/h2&gt;

&lt;p&gt;The order state machine should not live in isolation.&lt;/p&gt;

&lt;p&gt;It feeds other parts of the system.&lt;/p&gt;

&lt;p&gt;A useful dependency chain is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
   ↓
Fill
   ↓
Position
   ↓
Exposure
   ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order = ACCEPTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does not automatically mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position increased
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order = INTENDED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;definitely does not mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position increased
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The position should be based on actual execution.&lt;/p&gt;

&lt;p&gt;That sounds obvious, but this distinction becomes important when processing real-time events, partial fills, retries, reconnects, and application restarts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution state and transaction state should stay separate
&lt;/h2&gt;

&lt;p&gt;Another useful separation is between the order execution state and what happens afterward.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER
  ↓
FILL
  ↓
TRANSACTION
  ↓
CONFIRMATION
  ↓
SETTLEMENT
  ↓
POSITION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't want one field called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status = filled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to represent every stage of that lifecycle.&lt;/p&gt;

&lt;p&gt;A system may know that a trade matched while still waiting on a later stage.&lt;/p&gt;

&lt;p&gt;That is why my execution-verification work treats these stages separately.&lt;/p&gt;

&lt;p&gt;Repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-execution-verifier&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The verifier is designed around checking execution across order, fill, transaction, and settlement states rather than assuming that one event tells the entire story.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happens after a restart?
&lt;/h2&gt;

&lt;p&gt;Imagine the application crashes here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED
   ↓
?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The process restarts several seconds later.&lt;/p&gt;

&lt;p&gt;The local database might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order ID: 123
State: SUBMITTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application should not simply continue from there as though nothing happened.&lt;/p&gt;

&lt;p&gt;A safer startup process is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Load local state
      ↓
Find unresolved orders
      ↓
Query / reconcile current state
      ↓
Update local state
      ↓
Recalculate position
      ↓
Recalculate exposure
      ↓
Check risk
      ↓
Resume trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes unresolved orders part of startup recovery.&lt;/p&gt;

&lt;p&gt;This is closely related to position reconciliation: the system needs to re-establish a trustworthy state before creating additional risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  WebSocket events should update state, not define reality
&lt;/h2&gt;

&lt;p&gt;Real-time events are useful for keeping local state current.&lt;/p&gt;

&lt;p&gt;But I don't want the local state machine to assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event received = complete truth forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order submitted
      ↓
Fill event
      ↓
WebSocket disconnect
      ↓
More execution activity
      ↓
Reconnect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some events may have been missed.&lt;/p&gt;

&lt;p&gt;The application now needs a way to establish current state again.&lt;/p&gt;

&lt;p&gt;So I think about real-time processing and reconciliation as two complementary mechanisms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Real-time events
      ↓
Fast local updates

Reconciliation
      ↓
Recovery and verification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first keeps the system responsive.&lt;/p&gt;

&lt;p&gt;The second gives it a way to recover from uncertainty.&lt;/p&gt;




&lt;h2&gt;
  
  
  Duplicate events are another problem
&lt;/h2&gt;

&lt;p&gt;The opposite problem can also happen.&lt;/p&gt;

&lt;p&gt;Instead of missing an event, the application might process the same logical event more than once.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fill event
   ↓
Position +40

Same event processed again
   ↓
Position +80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That creates incorrect state even though the connection was never lost.&lt;/p&gt;

&lt;p&gt;This is why event handling should be designed with idempotency and deduplication in mind.&lt;/p&gt;

&lt;p&gt;The system needs to know which execution events have already been applied.&lt;/p&gt;

&lt;p&gt;That state belongs in the execution layer rather than being left to the strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Order state should be observable
&lt;/h2&gt;

&lt;p&gt;A useful trading dashboard should not simply show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders: 14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should make unresolved conditions visible.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED       3
ACCEPTED        2
PARTIAL         1
FILLED         14
REJECTED        1
CANCELLED       2
UNKNOWN         1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact numbers are only examples.&lt;/p&gt;

&lt;p&gt;The point is that the operator should be able to see where execution is getting stuck.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;UNKNOWN&lt;/code&gt; order should be much more visible than a successful order that needs no action.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unknown orders should have a timeout policy
&lt;/h2&gt;

&lt;p&gt;A system can also define how long an order is allowed to remain unresolved.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
   ↓
recheck
   ↓
still unknown
   ↓
alert
   ↓
continue reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The timeout should not automatically mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cancel and retry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because the system still needs to understand whether the original order exists and what exposure it may have created.&lt;/p&gt;

&lt;p&gt;A useful policy might distinguish between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN for 1 second
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN for 5 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The longer uncertainty persists, the more aggressively the control plane may need to intervene.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk controls should understand execution uncertainty
&lt;/h2&gt;

&lt;p&gt;Risk management often focuses on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position limits
Exposure limits
Daily loss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But execution uncertainty can itself become a reason to block trading.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open position:
+40

Unresolved order:
+60

Known exposure:
40

Potential exposure:
100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the strategy, treating the unresolved order as irrelevant could lead to incorrect decisions.&lt;/p&gt;

&lt;p&gt;This doesn't necessarily mean counting every unknown order as filled.&lt;/p&gt;

&lt;p&gt;It means the system should know that uncertainty exists and apply an appropriate policy.&lt;/p&gt;

&lt;p&gt;That is an important distinction.&lt;/p&gt;




&lt;h2&gt;
  
  
  A control plane can make these decisions explicit
&lt;/h2&gt;

&lt;p&gt;This is where order management connects to the broader trading control plane.&lt;/p&gt;

&lt;p&gt;The strategy should not have to understand every operational failure mode.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 TRADING BOT
                      │
       ┌──────────────┼──────────────┐
       ↓              ↓              ↓
   Strategy       Execution        Risk
                      │
                      ↓
                Order State
                      │
          ┌───────────┴───────────┐
          ↓                       ↓
   Verification            Reconciliation
          │                       │
          └───────────┬───────────┘
                      ↓
                 CONTROL PLANE
                      │
              Pause / Resume
              Alert / Recover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The control plane can decide whether the system is allowed to continue trading.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN ORDER
     ↓
Trading blocked
     ↓
Reconcile
     ↓
Execution verified
     ↓
Risk checked
     ↓
Trading allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That keeps operational safety separate from strategy logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  An example failure flow
&lt;/h2&gt;

&lt;p&gt;Consider this sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Strategy decides to buy 100

2. Order is submitted

3. Network response times out

4. Local state becomes UNKNOWN

5. Strategy wants to retry

6. Control Plane blocks retry

7. Execution state is reconciled

8. Original order is found

9. 40 units were filled

10. Position is updated to +40

11. Risk is recalculated

12. Remaining quantity is handled explicitly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the state machine, the system might have simply retried the order at step 5.&lt;/p&gt;

&lt;p&gt;That is exactly the kind of situation where a small implementation detail can turn into an unnecessary position.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing order-state handling
&lt;/h2&gt;

&lt;p&gt;I would test explicit failure scenarios rather than only the happy path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order accepted
    → continue

Order rejected
    → record rejection

Order cancelled
    → record cancellation

Partial fill
    → update actual quantity

Response timeout
    → UNKNOWN

WebSocket disconnect
    → preserve unresolved state

Application restart
    → reconcile unresolved orders

Duplicate event
    → do not double-apply

Order state mismatch
    → pause and reconcile

Unknown state persists
    → alert and block unsafe actions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These cases are much closer to the real operational problem than simply testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submit()
returns success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What I want the execution layer to answer
&lt;/h2&gt;

&lt;p&gt;At any point, the system should be able to answer questions like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What did the strategy intend?

Was the order submitted?

Was it accepted?

Did it match?

How much actually filled?

What remains unresolved?

Did we receive the relevant events?

Does local state agree with remote state?

What position resulted?

Is the resulting exposure within risk limits?

Is it safe to place another order?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those questions are more useful than a single status field.&lt;/p&gt;

&lt;p&gt;They also provide much better information when investigating an incident.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connecting this to execution verification
&lt;/h2&gt;

&lt;p&gt;This is the reason I keep execution verification separate from the strategy.&lt;/p&gt;

&lt;p&gt;The strategy should care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Should I trade?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution system should care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What happened to the order?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reconciliation layer should care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Does local state match reality?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The risk system should care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the resulting state still acceptable?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the control plane should care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Should the system currently be allowed to trade?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are related questions, but they are not the same question.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture I'm building around this
&lt;/h2&gt;

&lt;p&gt;My current Polymarket infrastructure projects reflect that separation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polymarket Trading Bot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-bot" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The broader bot handles strategy, execution, risk, monitoring and backtesting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polymarket Execution Verifier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-execution-verifier&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The verifier focuses on execution state across order, fill, transaction and later settlement stages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polymarket Trading Control Plane&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-control-plane" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-control-plane&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The control plane focuses on state, health, risk, reconciliation, alerts and operational recovery.&lt;/p&gt;

&lt;p&gt;The goal is not to make the system more complicated for its own sake.&lt;/p&gt;

&lt;p&gt;It's to keep different kinds of uncertainty from being hidden inside one order handler.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A trading bot should not think in terms of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should know where the order is in its lifecycle.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
   ↓
SUBMITTED
   ↓
ACCEPTED
   ↓
MATCHED
   ↓
FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with explicit paths for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REJECTED
CANCELLED
FAILED
PARTIAL
UNKNOWN
INCONSISTENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The particularly important state is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because unknown does not mean failed.&lt;/p&gt;

&lt;p&gt;It means the system does not yet know what happened.&lt;/p&gt;

&lt;p&gt;That should trigger verification or reconciliation, not an automatic assumption.&lt;/p&gt;

&lt;p&gt;For an automated Polymarket trading system, that distinction matters because the next order is going to be based on whatever state the system believes right now.&lt;/p&gt;

&lt;p&gt;The execution layer needs to make that belief explicit.&lt;/p&gt;

&lt;p&gt;And when it cannot establish the truth, the safest state is sometimes simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
   ↓
STOP
   ↓
VERIFY
   ↓
RESUME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a much better foundation for a trading system that needs to keep operating reliably.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>programming</category>
    </item>
    <item>
      <title>What It Actually Takes to Run a Polymarket Trading Bot 24/7</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Mon, 05 Oct 2026 07:11:50 +0000</pubDate>
      <link>https://dev.to/casatrick/what-it-actually-takes-to-run-a-polymarket-trading-bot-247-3209</link>
      <guid>https://dev.to/casatrick/what-it-actually-takes-to-run-a-polymarket-trading-bot-247-3209</guid>
      <description>&lt;p&gt;Getting a Polymarket trading bot to place orders is one problem.&lt;/p&gt;

&lt;p&gt;Keeping it running correctly for days or weeks is a different problem.&lt;/p&gt;

&lt;p&gt;A bot can work perfectly in a local test and still fail in production because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket disconnects&lt;/li&gt;
&lt;li&gt;stale market data&lt;/li&gt;
&lt;li&gt;partial fills&lt;/li&gt;
&lt;li&gt;position mismatches&lt;/li&gt;
&lt;li&gt;execution uncertainty&lt;/li&gt;
&lt;li&gt;process restarts&lt;/li&gt;
&lt;li&gt;memory or resource problems&lt;/li&gt;
&lt;li&gt;failed API requests&lt;/li&gt;
&lt;li&gt;unreconciled state&lt;/li&gt;
&lt;li&gt;risk limits being reached&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The strategy may be fine.&lt;/p&gt;

&lt;p&gt;The problem is everything around the strategy.&lt;/p&gt;

&lt;p&gt;For me, a production-oriented Polymarket system looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market Data
    ↓
Strategy
    ↓
Risk
    ↓
Execution
    ↓
Verification
    ↓
Reconciliation
    ↓
Monitoring
    ↓
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running 24/7 means every one of those layers needs a plan for what happens when something goes wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  A running process is not the same as a healthy bot
&lt;/h2&gt;

&lt;p&gt;The easiest health check is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process = running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That doesn't tell you much.&lt;/p&gt;

&lt;p&gt;The process can be alive while:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket = connected but stale
Position = wrong
Execution = unknown
Risk = unverified
Market data = outdated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I prefer to separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process Health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trading System Health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A process can be running while the trading system is paused.&lt;/p&gt;

&lt;p&gt;That's a useful state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start with explicit system states
&lt;/h2&gt;

&lt;p&gt;A control plane can make the operational state visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEALTHY
DEGRADED
PAUSED
RECOVERING
FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnect
        ↓
HEALTHY
        ↓
DEGRADED
        ↓
PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After recovery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAUSED
   ↓
RECONNECT
   ↓
RECONCILE
   ↓
VERIFY
   ↓
RISK CHECK
   ↓
HEALTHY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is very different from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;socket connected
↓
start trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A connection is only one part of the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  WebSocket connections need supervision
&lt;/h2&gt;

&lt;p&gt;A trading bot depending on real-time data should assume that connections will eventually fail.&lt;/p&gt;

&lt;p&gt;A basic connection lifecycle is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONNECTED
    ↓
DISCONNECTED
    ↓
RECONNECTING
    ↓
CONNECTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there is another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is the data actually fresh?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A connection can report &lt;code&gt;CONNECTED&lt;/code&gt; while no useful events have arrived recently.&lt;/p&gt;

&lt;p&gt;So I would track:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;last_event_time
last_connection_time
reconnect_count
last_disconnect
data_age
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket:
CONNECTED

Last event:
9.4 seconds ago

Maximum allowed age:
2 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should not be considered healthy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconnect doesn't mean recovered
&lt;/h2&gt;

&lt;p&gt;This is one of the most important operational distinctions.&lt;/p&gt;

&lt;p&gt;Suppose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event A
   ↓
Event B
   ↓
DISCONNECT
   ↓
Events C, D
   ↓
RECONNECT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application may never have seen C and D.&lt;/p&gt;

&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECONNECTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does not automatically mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STATE VERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recovery process should be closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Disconnect
   ↓
Pause
   ↓
Reconnect
   ↓
Rebuild / fetch state
   ↓
Reconcile
   ↓
Verify
   ↓
Check risk
   ↓
Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've written more about this in &lt;strong&gt;&lt;a href="https://dev.to/casatrick/polymarket-websocket-reconnects-rebuilding-trading-state-safely-47h3"&gt;Polymarket WebSocket recovery&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Process restarts are normal
&lt;/h2&gt;

&lt;p&gt;A production system should expect restarts.&lt;/p&gt;

&lt;p&gt;The question isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Can the process start?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“What state should the process trust after starting?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A safer startup sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application starts
        ↓
Load persisted state
        ↓
Connect to external sources
        ↓
Read authoritative state
        ↓
Compare
        ↓
Repair discrepancies
        ↓
Verify risk
        ↓
Allow trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application_started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trading_allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;are separate states.&lt;/p&gt;




&lt;h2&gt;
  
  
  Persist state that matters
&lt;/h2&gt;

&lt;p&gt;If the bot restarts, in-memory state disappears.&lt;/p&gt;

&lt;p&gt;That means some information needs to survive the process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;orders
fills
positions
execution state
risk state
recovery state
timestamps
last known events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't necessarily need to persist every piece of data forever.&lt;/p&gt;

&lt;p&gt;But you need enough information to reconstruct the trading state after a restart.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last known position
Last known exposure
Active orders
Unresolved executions
Last reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, every restart becomes a guessing exercise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bot needs a single source of truth for operational state
&lt;/h2&gt;

&lt;p&gt;Different components can know different things.&lt;/p&gt;

&lt;p&gt;The strategy may know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution layer may know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verifier may know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TX_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reconciliation layer may know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSITION_MISMATCH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The risk layer may know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These aren't contradictory.&lt;/p&gt;

&lt;p&gt;They're different views of the same system.&lt;/p&gt;

&lt;p&gt;A control plane can combine them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution = TX_PENDING
Position   = MISMATCH
Risk       = UNVERIFIED
        ↓
Trading Permission = BLOCK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is much more useful than one global boolean.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk has to keep running
&lt;/h2&gt;

&lt;p&gt;Risk checks cannot happen only once when the process starts.&lt;/p&gt;

&lt;p&gt;The system can change after every execution.&lt;/p&gt;

&lt;p&gt;A useful loop is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal
   ↓
Pre-Trade Risk
   ↓
Execution
   ↓
Verification
   ↓
Position Update
   ↓
Exposure Recalculation
   ↓
Risk Recheck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the risk system can react to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position changes
exposure changes
daily loss
execution failures
stale data
state mismatches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more on that side of the system, see &lt;strong&gt;&lt;a href="https://dev.to/casatrick/polymarket-trading-bot-risk-controls-when-should-a-bot-stop-trading-3m3k"&gt;Polymarket Trading Bot Risk Controls&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution verification needs to survive restarts
&lt;/h2&gt;

&lt;p&gt;Suppose the bot submitted an order and then stopped.&lt;/p&gt;

&lt;p&gt;When it starts again, it shouldn't assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;old local state = final execution state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution may have progressed while the application was unavailable.&lt;/p&gt;

&lt;p&gt;It could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
TX_PENDING
CONFIRMED
SETTLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or still unknown.&lt;/p&gt;

&lt;p&gt;That's why I treat execution verification as a separate layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
   ↓
Fill
   ↓
Transaction
   ↓
Confirmation
   ↓
Settlement
   ↓
Position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verifier can re-establish the execution state after a restart instead of relying entirely on stale in-memory state.&lt;/p&gt;

&lt;p&gt;See the &lt;strong&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;Polymarket Execution Verifier&lt;/a&gt;&lt;/strong&gt; project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Partial fills are an operational problem too
&lt;/h2&gt;

&lt;p&gt;A 100-unit order doesn't necessarily become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 filled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40 filled
60 remaining
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bot needs to keep track of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requested quantity
matched quantity
remaining quantity
current order state
position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A restart makes this more important.&lt;/p&gt;

&lt;p&gt;After restarting, the system needs to establish whether the remaining 60:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;still exists
was filled
was cancelled
is unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise local position and external state can diverge.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position state needs reconciliation
&lt;/h2&gt;

&lt;p&gt;Suppose the bot restarts and thinks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position = +100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The account says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position = +40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bot is running.&lt;/p&gt;

&lt;p&gt;The connection is working.&lt;/p&gt;

&lt;p&gt;The strategy is producing signals.&lt;/p&gt;

&lt;p&gt;But the system shouldn't immediately continue trading.&lt;/p&gt;

&lt;p&gt;The correct sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position mismatch
        ↓
PAUSE
        ↓
Reconcile
        ↓
Verify
        ↓
Recalculate exposure
        ↓
Risk check
        ↓
Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the basic reason I treat reconciliation as infrastructure rather than a cleanup task.&lt;/p&gt;




&lt;h2&gt;
  
  
  Monitoring should watch conditions, not just uptime
&lt;/h2&gt;

&lt;p&gt;A basic uptime monitor can tell you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bot process: UP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's useful, but not sufficient.&lt;/p&gt;

&lt;p&gt;I'd rather monitor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process
WebSocket
Market Data
Execution
Position
Risk
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SYSTEM HEALTH

Process        OK
WebSocket      OK
Market Data    OK
Execution      OK
Position       VERIFIED
Risk           OK
Recovery       IDLE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And during an incident:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SYSTEM HEALTH

Process        OK
WebSocket      RECOVERING
Market Data    STALE
Execution      UNKNOWN
Position       UNVERIFIED
Risk           BLOCKED
Recovery       ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the operator knows what is wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alerts should correspond to real actions
&lt;/h2&gt;

&lt;p&gt;An alert is useful when someone or something can act on it.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MARKET_DATA_STALE
WEBSOCKET_DISCONNECTED
POSITION_MISMATCH
EXECUTION_UNKNOWN
RISK_LIMIT_BREACHED
RECOVERY_STARTED
RECOVERY_FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system can then decide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alert only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pause trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;start recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require operator intervention
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't want alerts that are just noise.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metrics become more useful after the bot has been running
&lt;/h2&gt;

&lt;p&gt;Once the basic operating model works, start measuring execution behavior.&lt;/p&gt;

&lt;p&gt;Useful metrics include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order latency
fill latency
fill ratio
partial-fill rate
rejection rate
unknown execution count
position mismatch count
reconciliation duration
recovery duration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not collecting hundreds of numbers.&lt;/p&gt;

&lt;p&gt;It's identifying changes.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fill latency ↑
Rejections ↑
Unknown states ↑
Recovery time ↑
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That combination is much more interesting than any one number by itself.&lt;/p&gt;

&lt;p&gt;That's the focus of the &lt;strong&gt;execution analytics&lt;/strong&gt; work in this cluster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Incident recovery needs history
&lt;/h2&gt;

&lt;p&gt;If something goes wrong at 03:00, I don't want the only answer to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bot restarted at 03:02
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What happened?
When did it start?
What was the last verified state?
Which orders were active?
What fills were observed?
What became unknown?
What did reconciliation find?
What risk state existed?
Why was trading resumed?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful incident timeline might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;03:01:08  Order submitted
03:01:09  Fill observed
03:01:10  WebSocket disconnected
03:01:11  Trading paused
03:01:18  Process stopped
03:02:03  Process restarted
03:02:04  Recovery started
03:02:05  Reconciliation started
03:02:07  Position verified
03:02:08  Risk check passed
03:02:09  Trading resumed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is much more useful than a simple uptime record.&lt;/p&gt;

&lt;p&gt;I've written about this in &lt;strong&gt;&lt;a href="https://dev.to/casatrick/polymarket-trading-bot-incident-recovery-how-to-reconstruct-what-happened-1p1n"&gt;Polymarket Trading Bot Incident Recovery&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resource limits matter too
&lt;/h2&gt;

&lt;p&gt;24/7 operation isn't only about trading state.&lt;/p&gt;

&lt;p&gt;The process itself uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CPU
Memory
Disk
Network
Connections
File descriptors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A system that slowly accumulates memory or logs can eventually fail even if the trading logic is correct.&lt;/p&gt;

&lt;p&gt;So production monitoring should include basic process-level signals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;memory usage
CPU usage
disk usage
restart count
connection count
error rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact limits depend on the deployment environment.&lt;/p&gt;

&lt;p&gt;The important part is noticing the trend before the process becomes unstable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Logging should support investigation
&lt;/h2&gt;

&lt;p&gt;A trading system needs logs that are useful during failures.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execution_id
order_id
trade_id
market_id
event_type
state
timestamp
reason
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avoid logs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;something failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXECUTION_STATE_CHANGED
execution_id=abc123
from=FILLED
to=TX_PENDING
reason=transaction_not_available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes later analysis much easier.&lt;/p&gt;

&lt;p&gt;Sensitive credentials should never appear in logs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment is part of the system
&lt;/h2&gt;

&lt;p&gt;A 24/7 bot needs a predictable runtime environment.&lt;/p&gt;

&lt;p&gt;That means thinking about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process supervision
automatic restart
environment configuration
secrets
network access
logging
storage
health checks
resource limits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact deployment can vary.&lt;/p&gt;

&lt;p&gt;VPS, container, cloud VM, or another environment can all work.&lt;/p&gt;

&lt;p&gt;The engineering requirement is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The bot should fail in a controlled way rather than silently disappearing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Automatic restart isn't enough
&lt;/h2&gt;

&lt;p&gt;A supervisor can restart a process.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process dies
   ↓
supervisor restarts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good.&lt;/p&gt;

&lt;p&gt;But that doesn't guarantee safe trading.&lt;/p&gt;

&lt;p&gt;The trading system still needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;restart
   ↓
load state
   ↓
reconcile
   ↓
verify
   ↓
risk check
   ↓
resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise the supervisor may simply restart the same incorrect state over and over.&lt;/p&gt;




&lt;h2&gt;
  
  
  A simple 24/7 architecture
&lt;/h2&gt;

&lt;p&gt;Putting these ideas together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    POLYMARKET
                        |
                        v
                 Market Data / API
                        |
                        v
                   Trading Bot
                        |
              +---------+---------+
              |                   |
              v                   v
           Strategy              Risk
              |                   |
              +---------+---------+
                        |
                        v
                    Execution
                        |
                        v
                Execution Verifier
                        |
                        v
                Position Reconcile
                        |
                        v
                 Control Plane
                        |
          +-------------+-------------+
          |             |             |
          v             v             v
      Monitoring      Alerts       Recovery
          |
          v
       Metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system is no longer just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strategy → order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is an operational loop.&lt;/p&gt;




&lt;h2&gt;
  
  
  What should happen when the system gets unhealthy?
&lt;/h2&gt;

&lt;p&gt;The reaction should depend on the problem.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market data stale
        ↓
Pause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position mismatch
        ↓
Reconcile
        ↓
Verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Risk limit breached
        ↓
Block
        ↓
Operator / recovery process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnect
        ↓
      Pause
        ↓
   Reconnect
        ↓
    Recover
        ↓
     Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the response is defined before the incident happens.&lt;/p&gt;




&lt;h2&gt;
  
  
  24/7 operation is mostly about boundaries
&lt;/h2&gt;

&lt;p&gt;The longer a bot runs, the more important system boundaries become.&lt;/p&gt;

&lt;p&gt;You need to know where:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strategy
execution
verification
state
risk
monitoring
recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;begin and end.&lt;/p&gt;

&lt;p&gt;A strategy failure shouldn't necessarily become a process failure.&lt;/p&gt;

&lt;p&gt;A WebSocket failure shouldn't automatically become a strategy failure.&lt;/p&gt;

&lt;p&gt;A position mismatch shouldn't become silent extra exposure.&lt;/p&gt;

&lt;p&gt;An execution uncertainty shouldn't become fake success.&lt;/p&gt;

&lt;p&gt;A restart shouldn't automatically mean trading is safe.&lt;/p&gt;

&lt;p&gt;Those boundaries are what make continuous operation manageable.&lt;/p&gt;




&lt;h2&gt;
  
  
  A practical 24/7 checklist
&lt;/h2&gt;

&lt;p&gt;Before calling a Polymarket bot production-oriented, I'd want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Process supervision
[ ] Persistent state
[ ] WebSocket reconnect handling
[ ] Market-data freshness checks
[ ] Execution verification
[ ] Position reconciliation
[ ] Risk controls
[ ] Pause / resume
[ ] Recovery workflow
[ ] Structured logs
[ ] Metrics
[ ] Alerts
[ ] Resource monitoring
[ ] Restart recovery
[ ] Incident history
[ ] CI / tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact implementation will vary.&lt;/p&gt;

&lt;p&gt;The important thing is that every box has an actual behavior behind it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The broader system I'm building
&lt;/h2&gt;

&lt;p&gt;This is where the different Polymarket projects start fitting together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Polymarket Trading Bot
        ↓
    Execution
        ↓
Execution Verifier
        ↓
Position Reconciliation
        ↓
  Risk Controls
        ↓
Trading Control Plane
        ↓
Monitoring / Analytics
        ↓
Incident Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bot is the part that trades.&lt;/p&gt;

&lt;p&gt;The rest is what allows it to operate continuously.&lt;/p&gt;

&lt;p&gt;That distinction is becoming more important as I work on the infrastructure around it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Running a Polymarket bot for a few hours isn't the same engineering problem as running one continuously.&lt;/p&gt;

&lt;p&gt;For 24/7 operation, the system needs to handle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disconnects
restarts
partial fills
state mismatches
execution uncertainty
risk breaches
stale data
resource problems
incidents
recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The basic operating loop becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run
 ↓
Observe
 ↓
Verify
 ↓
Reconcile
 ↓
Control
 ↓
Recover when needed
 ↓
Continue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't to build a bot that never fails.&lt;/p&gt;

&lt;p&gt;That's not realistic.&lt;/p&gt;

&lt;p&gt;The goal is to build a system that &lt;strong&gt;fails visibly, recovers deliberately, and never assumes that a running process means trading is safe.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>rust</category>
      <category>programming</category>
      <category>tradingbot</category>
    </item>
    <item>
      <title>Polymarket Trading Bot Execution Analytics: What Should You Measure After the Trade?</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Sat, 03 Oct 2026 06:41:59 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-execution-analytics-what-should-you-measure-after-the-trade-9gn</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-execution-analytics-what-should-you-measure-after-the-trade-9gn</guid>
      <description>&lt;p&gt;A Polymarket trading bot can be running normally and still have problems you won't notice from the P&amp;amp;L.&lt;/p&gt;

&lt;p&gt;Orders may be taking longer to execute.&lt;/p&gt;

&lt;p&gt;Partial fills may be increasing.&lt;/p&gt;

&lt;p&gt;Rejections may be happening more often.&lt;/p&gt;

&lt;p&gt;Execution state may be staying unresolved longer than expected.&lt;/p&gt;

&lt;p&gt;Position reconciliation may be running too frequently.&lt;/p&gt;

&lt;p&gt;A recovery that normally takes a few seconds may suddenly take a minute.&lt;/p&gt;

&lt;p&gt;None of that is obvious from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bot: RUNNING
P&amp;amp;L: +$...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an automated trading system, I want to know not only &lt;strong&gt;whether it traded&lt;/strong&gt;, but how the execution layer is behaving.&lt;/p&gt;

&lt;p&gt;That is where execution analytics becomes useful.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution
   ↓
Metrics
   ↓
Health / Anomalies
   ↓
Risk / Control
   ↓
Action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm treating this as another layer around the Polymarket trading infrastructure I'm building.&lt;/p&gt;




&lt;h2&gt;
  
  
  What should you measure?
&lt;/h2&gt;

&lt;p&gt;There isn't one universal list.&lt;/p&gt;

&lt;p&gt;The useful metrics depend on the strategy and the execution model.&lt;/p&gt;

&lt;p&gt;But for a production-oriented Polymarket bot, I would start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order latency
fill latency
fill ratio
partial-fill rate
rejection rate
execution failures
unknown execution states
position mismatches
reconciliation frequency
recovery duration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not collecting dozens of metrics.&lt;/p&gt;

&lt;p&gt;It's being able to connect a metric to an operational decision.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rejection rate ↑
       ↓
execution health ↓
       ↓
investigate / pause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much more useful than a dashboard full of numbers nobody acts on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Order latency
&lt;/h2&gt;

&lt;p&gt;The first useful measurement is how long an order takes to move through the execution path.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strategy decision
      ↓
order created
      ↓
order submitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Measure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;decision → order created
order created → submitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then start looking at the distribution rather than a single average.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p50:  120 ms
p95:  480 ms
p99:  910 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact numbers aren't important here.&lt;/p&gt;

&lt;p&gt;The point is that averages can hide slow executions.&lt;/p&gt;

&lt;p&gt;A bot that normally processes most orders quickly but occasionally takes much longer can have a very different operational profile from one with consistently stable latency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fill latency
&lt;/h2&gt;

&lt;p&gt;Order submission is not the same thing as execution.&lt;/p&gt;

&lt;p&gt;So the next measurement is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order submitted
      ↓
fill observed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track the elapsed time.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order A → 140 ms
Order B → 165 ms
Order C → 3.8 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That third observation deserves attention even if the order eventually completed successfully.&lt;/p&gt;

&lt;p&gt;Over time, fill latency can be tracked by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;market&lt;/li&gt;
&lt;li&gt;side&lt;/li&gt;
&lt;li&gt;order type&lt;/li&gt;
&lt;li&gt;strategy&lt;/li&gt;
&lt;li&gt;time window&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it possible to find patterns instead of treating every slow execution as an isolated event.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fill ratio
&lt;/h2&gt;

&lt;p&gt;Suppose the strategy requested 100 units.&lt;/p&gt;

&lt;p&gt;The execution produced 75.&lt;/p&gt;

&lt;p&gt;That's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requested = 100
matched   = 75
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fill ratio is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;75 / 100 = 75%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is more informative than simply recording:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because requested quantity and actual execution are different things.&lt;/p&gt;

&lt;p&gt;A system that starts seeing lower fill ratios may need a different execution policy, more inventory planning, or simply a closer look at the market conditions.&lt;/p&gt;

&lt;p&gt;The metric by itself doesn't tell you what to do.&lt;/p&gt;

&lt;p&gt;It tells you where to look.&lt;/p&gt;




&lt;h2&gt;
  
  
  Partial-fill rate
&lt;/h2&gt;

&lt;p&gt;Fill ratio and partial-fill rate answer different questions.&lt;/p&gt;

&lt;p&gt;Fill ratio asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How much of the requested quantity was actually executed?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Partial-fill rate asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How often are orders ending up partially executed?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 orders
30 had partial execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;partial-fill rate = 30%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful because partial fills can create downstream work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;partial fill
   ↓
remaining quantity
   ↓
position update
   ↓
exposure update
   ↓
possible reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This connects directly to my earlier Polymarket partial fills work, where requested quantity and actual execution have to remain separate.&lt;/p&gt;

&lt;p&gt;The interesting metric isn't simply “how many orders were partial.”&lt;/p&gt;

&lt;p&gt;It's what those partial fills cause elsewhere in the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rejection rate
&lt;/h2&gt;

&lt;p&gt;Another straightforward metric is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rejected orders
----------------
submitted orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000 submitted
25 rejected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 2.5% rejection rate means something very different from a system where almost every order succeeds.&lt;/p&gt;

&lt;p&gt;More importantly, track changes over time.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Monday    0.8%
Tuesday   0.9%
Wednesday 1.1%
Thursday  4.7%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Thursday number is where I'd start investigating.&lt;/p&gt;

&lt;p&gt;Possible causes need to be established from the actual execution data rather than guessed from the metric alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution failures
&lt;/h2&gt;

&lt;p&gt;Not every execution issue is a rejection.&lt;/p&gt;

&lt;p&gt;You can also have failures around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submission
tracking
confirmation
state updates
reconciliation
recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I would separate execution failures into useful categories rather than using one giant counter.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER_SUBMISSION_FAILURE
FILL_PROCESSING_FAILURE
TRANSACTION_VERIFICATION_FAILURE
POSITION_RECONCILIATION_FAILURE
RECOVERY_FAILURE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the metric much more actionable.&lt;/p&gt;

&lt;p&gt;If recovery failures are increasing while order submission remains healthy, you have a very different problem from a system where order submission itself is failing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unknown execution states
&lt;/h2&gt;

&lt;p&gt;This is one of the metrics I care about most.&lt;/p&gt;

&lt;p&gt;A trading system will sometimes encounter states it can't verify immediately.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
   ↓
TX_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execution = UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important metric is not just the count.&lt;/p&gt;

&lt;p&gt;Track how long executions remain unresolved.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN executions: 7

oldest unresolved:
18.4 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the control plane has something meaningful to evaluate.&lt;/p&gt;

&lt;p&gt;You can define a policy around unresolved execution state rather than treating &lt;code&gt;UNKNOWN&lt;/code&gt; as an invisible edge case.&lt;/p&gt;

&lt;p&gt;This connects directly to the &lt;strong&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;Polymarket Execution Verifier&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position mismatches
&lt;/h2&gt;

&lt;p&gt;Execution analytics shouldn't stop at the execution layer.&lt;/p&gt;

&lt;p&gt;Suppose the system believes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while the external state is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position = 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position mismatch = -60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mismatch count
mismatch duration
markets affected
largest difference
time to resolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is directly related to my earlier Polymarket position reconciliation work.&lt;/p&gt;

&lt;p&gt;I haven't put a guessed URL into that link because the exact published page URL should come from the live DEV page rather than being invented.&lt;/p&gt;

&lt;p&gt;The important analytics question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How often does the system disagree with the account, and how long does it take to become consistent again?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Reconciliation frequency
&lt;/h2&gt;

&lt;p&gt;Reconciliation itself is something worth measuring.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;automatic reconciliations
manual reconciliations
reconciliations after reconnect
reconciliations after restart
reconciliations caused by mismatch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reconciliation count
+
reconciliation duration
+
reconciliation result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gives you a much better picture of system stability.&lt;/p&gt;

&lt;p&gt;If reconciliation starts running constantly, that can be a signal that another part of the system needs attention.&lt;/p&gt;

&lt;p&gt;Again, the metric doesn't explain the cause.&lt;/p&gt;

&lt;p&gt;It tells you where to investigate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovery duration
&lt;/h2&gt;

&lt;p&gt;Suppose a WebSocket disconnect occurs.&lt;/p&gt;

&lt;p&gt;The system goes through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disconnect
   ↓
pause
   ↓
reconnect
   ↓
reconcile
   ↓
verify
   ↓
risk check
   ↓
resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Measure the total time.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recovery #1 → 2.4 s
Recovery #2 → 3.1 s
Recovery #3 → 18.7 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That third recovery may deserve investigation.&lt;/p&gt;

&lt;p&gt;You can also break the duration into stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disconnect → reconnect
reconnect → reconciliation
reconciliation → verification
verification → risk check
risk check → resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can see where the recovery process is actually spending time.&lt;/p&gt;

&lt;p&gt;This builds on the &lt;strong&gt;&lt;a href="https://dev.to/casatrick/polymarket-websocket-reconnects-rebuilding-trading-state-safely-47h3"&gt;Polymarket WebSocket recovery&lt;/a&gt;&lt;/strong&gt; work from earlier in the cluster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metrics need context
&lt;/h2&gt;

&lt;p&gt;A single number is rarely enough.&lt;/p&gt;

&lt;p&gt;Suppose you see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fill ratio = 72%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is that good?&lt;/p&gt;

&lt;p&gt;You can't answer that without context.&lt;/p&gt;

&lt;p&gt;Compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market A: 72%
Market B: 41%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;normal: 74%
today: 72%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strategy A: 91%
strategy B: 53%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analytics become useful when they're attached to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market
strategy
side
time
execution type
system state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why I prefer structured event data over a dashboard that only stores aggregate counters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metrics should come from state transitions
&lt;/h2&gt;

&lt;p&gt;The easiest way to produce useful execution analytics is to make state transitions observable.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER_CREATED
      ↓
ORDER_SUBMITTED
      ↓
FILL_OBSERVED
      ↓
TX_PENDING
      ↓
CONFIRMED
      ↓
SETTLED
      ↓
POSITION_VERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every transition can have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timestamp
execution_id
order_id
trade_id
market_id
state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then metrics can be derived from the events.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER_SUBMITTED → FILL_OBSERVED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gives fill latency.&lt;/p&gt;

&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DISCONNECT → TRADING_RESUMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gives recovery duration.&lt;/p&gt;

&lt;p&gt;This is a much cleaner design than adding unrelated counters throughout the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  An execution timeline is more useful than a single number
&lt;/h2&gt;

&lt;p&gt;Imagine this execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:41:08.120  order submitted
10:41:08.340  fill observed
10:41:08.342  transaction pending
10:41:08.910  transaction confirmed
10:41:09.020  position updated
10:41:09.040  position verified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can calculate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order → fill
220 ms

fill → confirmation
570 ms

confirmation → position verified
130 ms

total
920 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That lets you see where the time is going.&lt;/p&gt;

&lt;p&gt;The same timeline can also help when something goes wrong.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order submitted
fill observed
WebSocket disconnect
process restart
reconciliation
position mismatch
repair
resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the analytics become part of incident investigation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Analytics and risk should be connected
&lt;/h2&gt;

&lt;p&gt;The same metrics can feed &lt;a href="https://dev.to/casatrick/polymarket-trading-bot-risk-controls-when-should-a-bot-stop-trading-3m3k"&gt;Polymarket trading bot risk controls&lt;/a&gt; when an operational threshold is crossed.&lt;/p&gt;

&lt;p&gt;Metrics become much more useful when the control plane can act on them.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execution latency ↑
rejection rate ↑
unknown state duration ↑
        ↓
Execution Health = DEGRADED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position mismatch count ↑
        ↓
Reconciliation Health = DEGRADED
        ↓
Trading Permission = BLOCK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;recovery duration ↑
        ↓
System Health = DEGRADED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact thresholds should be configurable.&lt;/p&gt;

&lt;p&gt;The important part is the architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Metrics
   ↓
Health
   ↓
Risk
   ↓
Control
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That turns analytics into an operational input instead of a report that nobody reads.&lt;/p&gt;

&lt;p&gt;This is part of the broader &lt;a href="https://dev.to/casatrick/building-a-polymarket-trading-bot-architecture-execution-and-risk-management-4gd7"&gt;Polymarket trading bot architecture&lt;/a&gt; I've been building.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don't optimize for dashboards
&lt;/h2&gt;

&lt;p&gt;It's easy to build a beautiful dashboard with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders
Fills
Latency
Volume
P&amp;amp;L
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and still not know whether the system is healthy.&lt;/p&gt;

&lt;p&gt;The dashboard should answer operational questions.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are executions taking longer than normal?&lt;/p&gt;

&lt;p&gt;Are partial fills increasing?&lt;/p&gt;

&lt;p&gt;Are unresolved executions accumulating?&lt;/p&gt;

&lt;p&gt;Are positions frequently drifting from external state?&lt;/p&gt;

&lt;p&gt;Is reconciliation taking longer?&lt;/p&gt;

&lt;p&gt;Are recovery incidents becoming more frequent?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are questions that can lead to engineering action.&lt;/p&gt;




&lt;h2&gt;
  
  
  A useful execution-health view
&lt;/h2&gt;

&lt;p&gt;I would want something roughly like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXECUTION HEALTH

Orders                 1,248
Fill ratio               86%
Partial fills             94
Rejections                11
Unknown executions         3

p50 fill latency         180 ms
p95 fill latency         620 ms

Position mismatches        2
Active recoveries          0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual UI doesn't need to look exactly like this.&lt;/p&gt;

&lt;p&gt;The important thing is that the metrics represent the system's current operational state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anomaly detection can come later
&lt;/h2&gt;

&lt;p&gt;You don't need machine learning to start.&lt;/p&gt;

&lt;p&gt;Simple thresholds are enough.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p95 latency &amp;gt; threshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unknown executions &amp;gt; threshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;recovery duration &amp;gt; threshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position mismatches &amp;gt; threshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal
   ↓
Threshold crossed
   ↓
DEGRADED
   ↓
Investigate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start with deterministic rules.&lt;/p&gt;

&lt;p&gt;You can add more sophisticated anomaly detection later if there is enough historical data to justify it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Store the raw events
&lt;/h2&gt;

&lt;p&gt;One useful design decision is to preserve enough raw event information to reconstruct metrics later.&lt;/p&gt;

&lt;p&gt;For an execution, you might store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execution_id
order_id
trade_id
market_id

event_type
event_timestamp
observed_timestamp

requested_quantity
matched_quantity

state
reason
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then metrics can be recalculated.&lt;/p&gt;

&lt;p&gt;That's better than only storing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;average_fill_latency = 240ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because once you throw away the underlying observations, it becomes difficult to investigate why the number changed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metrics should support incident analysis
&lt;/h2&gt;

&lt;p&gt;Suppose today's recovery time is much worse than yesterday's.&lt;/p&gt;

&lt;p&gt;A good system should let you drill down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recovery Duration ↑
      ↓
Reconciliation Duration ↑
      ↓
Position Mismatch Count ↑
      ↓
Market X
      ↓
Specific execution / event sequence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now analytics and incident recovery are connected.&lt;/p&gt;

&lt;p&gt;That is the direction I want the infrastructure to take.&lt;/p&gt;

&lt;p&gt;That makes analytics useful for &lt;a href="https://dev.to/casatrick/polymarket-trading-bot-incident-recovery-how-to-reconstruct-what-happened-1p1n"&gt;Polymarket trading bot incident recovery&lt;/a&gt; as well as day-to-day monitoring.&lt;/p&gt;




&lt;h2&gt;
  
  
  The broader stack
&lt;/h2&gt;

&lt;p&gt;This fits into the rest of my Polymarket work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Polymarket Trading Bot
        ↓
Execution
        ↓
Execution Verifier
        ↓
Position Reconciliation
        ↓
Risk Controls
        ↓
Trading Control Plane
        ↓
Execution Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer produces information for the next.&lt;/p&gt;

&lt;p&gt;The bot produces execution events.&lt;/p&gt;

&lt;p&gt;The verifier establishes execution state.&lt;/p&gt;

&lt;p&gt;Reconciliation establishes state consistency.&lt;/p&gt;

&lt;p&gt;Risk evaluates the resulting exposure and system conditions.&lt;/p&gt;

&lt;p&gt;The control plane decides whether trading continues.&lt;/p&gt;

&lt;p&gt;Analytics measures how all of those pieces are behaving over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I would measure first
&lt;/h2&gt;

&lt;p&gt;I wouldn't start with 50 metrics.&lt;/p&gt;

&lt;p&gt;I'd start with a small set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Order latency
2. Fill latency
3. Fill ratio
4. Partial-fill rate
5. Rejection rate
6. Unknown execution count
7. Unknown execution duration
8. Position mismatch count
9. Reconciliation duration
10. Recovery duration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those ten already give you visibility into a large part of the execution lifecycle.&lt;/p&gt;

&lt;p&gt;Then add metrics when there is a real operational question they can answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A trading bot being profitable doesn't tell you whether the execution system is healthy.&lt;/p&gt;

&lt;p&gt;A bot can be making money while:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;latency is increasing
partial fills are increasing
rejections are increasing
execution state is staying unknown
positions are drifting
recovery is getting slower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's why I'm interested in measuring what happens &lt;strong&gt;after the strategy produces a trade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution
   ↓
Events
   ↓
Metrics
   ↓
Health
   ↓
Risk / Control
   ↓
Action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't to build a dashboard full of numbers.&lt;/p&gt;

&lt;p&gt;It's to make the trading system measurable enough that you can answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is happening to execution right now, and does it require the system to change its behavior?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the level of observability I want around an automated Polymarket trading system.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>programming</category>
    </item>
    <item>
      <title>Polymarket Trading Bot Incident Recovery: How to Reconstruct What Happened</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Fri, 02 Oct 2026 05:57:34 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-incident-recovery-how-to-reconstruct-what-happened-1p1n</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-incident-recovery-how-to-reconstruct-what-happened-1p1n</guid>
      <description>&lt;p&gt;A Polymarket trading bot stops.&lt;/p&gt;

&lt;p&gt;Maybe a WebSocket connection dropped.&lt;/p&gt;

&lt;p&gt;Maybe an order behaved unexpectedly.&lt;/p&gt;

&lt;p&gt;Maybe the position no longer matches local state.&lt;/p&gt;

&lt;p&gt;Maybe the process restarted.&lt;/p&gt;

&lt;p&gt;The immediate reaction is usually:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;restart the bot&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But restarting only gets the process running again.&lt;/p&gt;

&lt;p&gt;It doesn't answer the more important questions:&lt;/p&gt;

&lt;p&gt;What happened before the incident?&lt;/p&gt;

&lt;p&gt;What state did the bot believe it was in?&lt;/p&gt;

&lt;p&gt;What actually happened to the orders and fills?&lt;/p&gt;

&lt;p&gt;Which events were missed?&lt;/p&gt;

&lt;p&gt;What is the current position?&lt;/p&gt;

&lt;p&gt;Is the system safe to trade again?&lt;/p&gt;

&lt;p&gt;That's the part I'm interested in.&lt;/p&gt;

&lt;p&gt;For a production-oriented trading system, &lt;strong&gt;incident recovery isn't just restarting a process. It's reconstructing enough state to understand what happened and verify that the system can safely continue.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A restart is not a recovery
&lt;/h2&gt;

&lt;p&gt;A simple recovery flow might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process crashed
    ↓
Restart
    ↓
Trading resumes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That assumes the state before the crash was correct.&lt;/p&gt;

&lt;p&gt;It also assumes nothing important happened while the process was unavailable.&lt;/p&gt;

&lt;p&gt;Neither assumption is safe.&lt;/p&gt;

&lt;p&gt;A better flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incident
    ↓
Pause Trading
    ↓
Recover State
    ↓
Reconcile
    ↓
Verify
    ↓
Check Risk
    ↓
Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The restart is only one step.&lt;/p&gt;




&lt;h2&gt;
  
  
  The first problem is figuring out what happened
&lt;/h2&gt;

&lt;p&gt;Imagine the bot was running normally.&lt;/p&gt;

&lt;p&gt;Then something goes wrong.&lt;/p&gt;

&lt;p&gt;When the process comes back, you may have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last known local state
+
Persisted state
+
Orders
+
Fills
+
Market events
+
Position data
+
Risk state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These don't necessarily line up.&lt;/p&gt;

&lt;p&gt;You might discover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local position:   +100
Remote position:   +40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order:
FILLED

Transaction:
UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connection:
RECONNECTED

State:
UNVERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The incident isn't resolved just because the process is running again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start with the last known good state
&lt;/h2&gt;

&lt;p&gt;A useful recovery process needs a reference point.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last Known Good State
        ↓
Incident
        ↓
Events around incident
        ↓
Current external state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system should be able to answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;When was the last verified state?

What orders were active?

What had already filled?

What positions were expected?

What was the exposure?

What was the risk state?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without that history, recovery becomes guesswork.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why event history matters
&lt;/h2&gt;

&lt;p&gt;Suppose the system saw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order A
Fill A
Fill B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the process crashed.&lt;/p&gt;

&lt;p&gt;When it restarts, the local database may only contain part of the state.&lt;/p&gt;

&lt;p&gt;The recovery system needs to establish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What was known before the incident?
What happened during the gap?
What is true now?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means keeping enough structured information to reconstruct state.&lt;/p&gt;

&lt;p&gt;Not every raw event has to be stored forever.&lt;/p&gt;

&lt;p&gt;But important state transitions should be traceable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The trading bot can be alive while its state is wrong
&lt;/h2&gt;

&lt;p&gt;This is similar to the problem I covered in &lt;strong&gt;&lt;a href="https://dev.to/casatrick/polymarket-websocket-reconnects-rebuilding-trading-state-safely-47h3"&gt;Polymarket WebSocket reconnects&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process:
RUNNING

WebSocket:
CONNECTED

Strategy:
ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks healthy.&lt;/p&gt;

&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position:
UNVERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the system has a much more serious problem.&lt;/p&gt;

&lt;p&gt;The process is healthy.&lt;/p&gt;

&lt;p&gt;The trading state is not.&lt;/p&gt;

&lt;p&gt;This distinction is important during incident recovery because the recovery process needs to establish &lt;strong&gt;system state&lt;/strong&gt;, not just process state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconstructing an incident
&lt;/h2&gt;

&lt;p&gt;A useful incident-recovery pipeline looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incident Detected
      ↓
Freeze New Risk
      ↓
Capture Current State
      ↓
Load Last Known State
      ↓
Inspect Orders / Fills
      ↓
Inspect Execution State
      ↓
Read Remote Position
      ↓
  Compare
      ↓
Identify Mismatches
      ↓
    Repair
      ↓
   Verify
      ↓
Risk Check
      ↓
   Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact implementation will vary.&lt;/p&gt;

&lt;p&gt;The principle is the important part:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;don't resume trading before you understand the state you are resuming from.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Freeze new risk first
&lt;/h2&gt;

&lt;p&gt;The first operational action should normally be to stop adding new exposure.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incident
   ↓
TRADING PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Existing state can still be inspected.&lt;/p&gt;

&lt;p&gt;Orders can be reconciled.&lt;/p&gt;

&lt;p&gt;Positions can be checked.&lt;/p&gt;

&lt;p&gt;But the system shouldn't keep creating additional risk while it is trying to figure out what happened.&lt;/p&gt;

&lt;p&gt;This is one of the reasons risk controls and incident recovery belong together.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconcile orders first
&lt;/h2&gt;

&lt;p&gt;The system needs to understand the order state around the incident.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order 1 → Filled
Order 2 → Open
Order 3 → Cancelled
Order 4 → Unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An order that is &lt;code&gt;UNKNOWN&lt;/code&gt; needs different handling from one that is clearly cancelled.&lt;/p&gt;

&lt;p&gt;Likewise, an open order may still create future exposure.&lt;/p&gt;

&lt;p&gt;So recovery needs to establish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Known orders
+
Known fills
+
Unknown orders
+
Expected remaining quantities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before rebuilding the rest of the state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then reconstruct execution state
&lt;/h2&gt;

&lt;p&gt;This connects directly to the &lt;strong&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;Polymarket Execution Verifier&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An incident can leave an execution somewhere in the middle of its lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED
    ↓
MATCHED
    ↓
FILLED
    ↓
TX_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The process stops.&lt;/p&gt;

&lt;p&gt;When it comes back, the application shouldn't assume the final state.&lt;/p&gt;

&lt;p&gt;It should recover the execution information and determine whether the trade is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VERIFIED
UNKNOWN
INCONSISTENT
FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that recovery uses evidence rather than assumptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Partial fills make recovery harder
&lt;/h2&gt;

&lt;p&gt;Suppose the bot intended:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and before the incident it knew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40 filled
60 remaining
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the process goes down.&lt;/p&gt;

&lt;p&gt;After restarting, several things could be true.&lt;/p&gt;

&lt;p&gt;The remaining 60 may still be open.&lt;/p&gt;

&lt;p&gt;They may have filled.&lt;/p&gt;

&lt;p&gt;They may have been cancelled.&lt;/p&gt;

&lt;p&gt;The application may not know yet.&lt;/p&gt;

&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 requested
40 previously known
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does not automatically mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40 final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recovery process has to reconcile the current order and fill state.&lt;/p&gt;

&lt;p&gt;This is one reason partial fills and incident recovery are closely related but distinct problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position reconstruction comes next
&lt;/h2&gt;

&lt;p&gt;After resolving orders and executions, the system needs to establish the current position.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected:
+100

Observed:
+40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a mismatch.&lt;/p&gt;

&lt;p&gt;The system should not simply overwrite one number with the other and continue.&lt;/p&gt;

&lt;p&gt;It needs to know why the difference exists.&lt;/p&gt;

&lt;p&gt;Possible causes include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Missed fill
Partial execution
Missed WebSocket event
Unexpected order state
Stale local state
Restart during execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to move from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN / INCONSISTENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Incident recovery is different from reconciliation
&lt;/h2&gt;

&lt;p&gt;These two concepts are connected, but I don't think they should be treated as the same thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reconciliation
&lt;/h3&gt;

&lt;p&gt;Asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does my local state match the external state?&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local
  ↓
Remote
  ↓
Compare
  ↓
Repair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Incident recovery
&lt;/h3&gt;

&lt;p&gt;Asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What happened, what state was lost or made uncertain, and what do I need to verify before the system can operate again?&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incident
  ↓
Reconstruct
  ↓
Reconcile
  ↓
Verify
  ↓
Recover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reconciliation is part of recovery.&lt;/p&gt;

&lt;p&gt;Recovery is the larger process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovery needs a timeline
&lt;/h2&gt;

&lt;p&gt;One of the most useful things an incident system can provide is a timeline.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:41:08  Order submitted
10:41:09  Fill observed
10:41:10  WebSocket disconnected
10:41:11  Execution state uncertain
10:41:18  Process stopped
10:42:03  Process restarted
10:42:04  Trading paused
10:42:05  Reconciliation started
10:42:06  Position mismatch detected
10:42:07  Remote state fetched
10:42:08  Position verified
10:42:09  Risk checks passed
10:42:10  Trading resumed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the operator can understand what happened.&lt;/p&gt;

&lt;p&gt;Without this, the incident may look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bot crashed
Bot restarted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's not enough information.&lt;/p&gt;




&lt;h2&gt;
  
  
  The control plane should record the recovery state
&lt;/h2&gt;

&lt;p&gt;This is where the &lt;strong&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-control-plane" rel="noopener noreferrer"&gt;Polymarket Trading Control Plane&lt;/a&gt;&lt;/strong&gt; fits into the larger architecture.&lt;/p&gt;

&lt;p&gt;The system can move through explicit states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEALTHY
    ↓
DEGRADED
    ↓
PAUSED
    ↓
RECOVERING
    ↓
 HEALTHY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that &lt;code&gt;RECOVERING&lt;/code&gt; is an actual system state.&lt;/p&gt;

&lt;p&gt;It tells the rest of the system:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The process is running, but normal trading has not been restored yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is much clearer than a generic “online” status.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovery should have clear stages
&lt;/h2&gt;

&lt;p&gt;A useful model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Detect
2. Pause
3. Capture
4. Reconstruct
5. Reconcile
6. Verify
7. Check Risk
8. Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each stage should produce observable state.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECOVERY_STARTED
RECONCILIATION_STARTED
STATE_MISMATCH
RECONCILIATION_COMPLETED
RISK_CHECK_COMPLETED
RECOVERY_COMPLETED
TRADING_RESUMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That turns recovery from a collection of retry loops into an operational process.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happens when reconstruction fails?
&lt;/h2&gt;

&lt;p&gt;Sometimes the system won't be able to establish a clean state immediately.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local:
+100

Remote:
UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution:
UNKNOWN

Position:
UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system should not invent an answer.&lt;/p&gt;

&lt;p&gt;It should remain controlled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECOVERING
    ↓
PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and retry or request further reconciliation.&lt;/p&gt;

&lt;p&gt;This is another reason I prefer explicit &lt;code&gt;UNKNOWN&lt;/code&gt; states.&lt;/p&gt;

&lt;p&gt;Unknown state is uncomfortable.&lt;/p&gt;

&lt;p&gt;Pretending unknown state is healthy is worse.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovery after application restart
&lt;/h2&gt;

&lt;p&gt;A restart deserves the same treatment.&lt;/p&gt;

&lt;p&gt;The process starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
    ↓
Load persisted state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that state may be stale.&lt;/p&gt;

&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Load Local State
       ↓
   Connect
       ↓
Read Remote State
       ↓
   Compare
       ↓
    Repair
       ↓
    Verify
       ↓
Risk Check
       ↓
Allow Trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting the application and starting trading are therefore separate operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovery after a WebSocket gap
&lt;/h2&gt;

&lt;p&gt;The same applies when a connection drops.&lt;/p&gt;

&lt;p&gt;Suppose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event A
Event B
   ↓
DISCONNECT
   ↓
Events C, D
   ↓
RECONNECT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The process may not have seen C and D.&lt;/p&gt;

&lt;p&gt;The recovery path has to establish the current state rather than assuming the missing events can be ignored.&lt;/p&gt;

&lt;p&gt;That's why the sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Disconnect
   ↓
Pause
   ↓
Reconnect
   ↓
Reconcile
   ↓
Verify
   ↓
Risk Check
   ↓
Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The connection returning is only one part of the recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovery should be idempotent
&lt;/h2&gt;

&lt;p&gt;This is important for automated systems.&lt;/p&gt;

&lt;p&gt;Suppose the recovery worker gets triggered twice.&lt;/p&gt;

&lt;p&gt;You don't want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recovery #1
Recovery #2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to produce conflicting state changes.&lt;/p&gt;

&lt;p&gt;The operations should be safe to repeat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECONCILE
RECONCILE
RECONCILE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without double-counting fills or creating duplicate records.&lt;/p&gt;

&lt;p&gt;Likewise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAUSE
PAUSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should remain simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recovery process should be designed around idempotent operations wherever possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Incident history is useful after the system is healthy
&lt;/h2&gt;

&lt;p&gt;Recovery isn't only about getting back online.&lt;/p&gt;

&lt;p&gt;The incident record can help answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What failed?

How long was trading paused?

Which state became inconsistent?

How was it repaired?

Did risk limits change?

What prevented the system from resuming immediately?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over time, that becomes useful for identifying recurring problems.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket incidents
Execution failures
Position mismatches
Stale-data incidents
Recovery failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can start seeing patterns instead of isolated outages.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I want to see from an incident record
&lt;/h2&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incident ID:
INC-2026-0012

Started:
10:41:10

Trigger:
WEBSOCKET_DISCONNECTED

System State:
PAUSED

Affected Execution:
abc123

Position:
MISMATCH

Recovery:
COMPLETED

Duration:
62 seconds

Final Risk State:
HEALTHY

Trading:
RESUMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact implementation doesn't need to look like this.&lt;/p&gt;

&lt;p&gt;But the system should retain enough information to explain the recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don't hide recovery inside logs
&lt;/h2&gt;

&lt;p&gt;Logs are useful.&lt;/p&gt;

&lt;p&gt;But recovery state should also be part of the application's domain model.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR websocket disconnected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the system should know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system_state = PAUSED
recovery_state = RECONCILING
trading_permission = BLOCK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now dashboards, alerts, APIs, and automation can use the same source of truth.&lt;/p&gt;

&lt;p&gt;That's one of the main reasons I'm separating the Control Plane from the trading strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  What should make trading resume?
&lt;/h2&gt;

&lt;p&gt;This is the final gate.&lt;/p&gt;

&lt;p&gt;I don't want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process restarted
    ↓
resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incident resolved
+
Orders reconciled
+
Executions verified
+
Positions verified
+
Exposure recalculated
+
Risk healthy
+
Market data fresh
=
Trading allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one of those critical conditions remains unknown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;until the state is established.&lt;/p&gt;




&lt;h2&gt;
  
  
  The broader architecture
&lt;/h2&gt;

&lt;p&gt;The incident-recovery layer fits into the stack I've been building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Polymarket Trading Bot
        ↓
    Strategy
        ↓
      Risk
        ↓
    Execution
        ↓
Execution Verifier
        ↓
Position Reconciliation
        ↓
Trading Control Plane
        ↓
Incident Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The responsibilities stay separate.&lt;/p&gt;

&lt;p&gt;The strategy generates decisions.&lt;/p&gt;

&lt;p&gt;Risk controls those decisions.&lt;/p&gt;

&lt;p&gt;Execution carries them out.&lt;/p&gt;

&lt;p&gt;The verifier determines what happened.&lt;/p&gt;

&lt;p&gt;Reconciliation checks whether state matches reality.&lt;/p&gt;

&lt;p&gt;The control plane coordinates health and trading permission.&lt;/p&gt;

&lt;p&gt;Incident recovery reconstructs and restores the system after something goes wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  A practical recovery checklist
&lt;/h2&gt;

&lt;p&gt;Before allowing a bot to resume after an incident:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Incident trigger identified
[ ] New trading paused
[ ] Last known state recovered
[ ] Orders reconciled
[ ] Fills reconciled
[ ] Execution state verified
[ ] Position verified
[ ] Exposure recalculated
[ ] Risk checked
[ ] Market data confirmed fresh
[ ] No critical UNKNOWN state remains
[ ] Recovery completed
[ ] Trading permission = ALLOW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact policy will depend on the system.&lt;/p&gt;

&lt;p&gt;But the important part is having an explicit gate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A failed trading bot is not necessarily the hard part.&lt;/p&gt;

&lt;p&gt;The harder problem is restarting it &lt;strong&gt;without knowing what happened before the restart&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A process can start successfully while:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;orders are uncertain
fills are incomplete
positions are wrong
exposure is stale
risk is unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's why I think incident recovery should be treated as part of the trading system itself.&lt;/p&gt;

&lt;p&gt;The recovery path I want is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INCIDENT
   ↓
PAUSE
   ↓
RECONSTRUCT
   ↓
RECONCILE
   ↓
VERIFY
   ↓
CHECK RISK
   ↓
RESUME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't simply to get the bot running again.&lt;/p&gt;

&lt;p&gt;It's to get the system back to a state where it &lt;strong&gt;knows what happened, knows what is true now, and has a reason to trust the next trade&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>rust</category>
      <category>programming</category>
      <category>tradingbot</category>
    </item>
    <item>
      <title>Polymarket Trading Bot Risk Controls: When Should a Bot Stop Trading?</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Thu, 01 Oct 2026 06:47:01 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-risk-controls-when-should-a-bot-stop-trading-3m3k</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-risk-controls-when-should-a-bot-stop-trading-3m3k</guid>
      <description>&lt;p&gt;A Polymarket trading bot can have a good strategy, fresh market data, and a working execution engine - and still be unsafe to let it trade.&lt;/p&gt;

&lt;p&gt;The difficult part isn't only deciding:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Should I buy?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is another question underneath it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Is the system currently in a state where it should be allowed to trade at all?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where risk controls become infrastructure.&lt;/p&gt;

&lt;p&gt;A production-oriented Polymarket trading bot needs more than a strategy and an order API.&lt;/p&gt;

&lt;p&gt;It needs explicit rules for when to continue, when to reduce activity, and when to stop.&lt;/p&gt;

&lt;p&gt;I've been building my Polymarket trading infrastructure around that idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market Data
     ↓
Strategy
     ↓
Risk Checks
     ↓
Execution
     ↓
Verification
     ↓
Position / Exposure
     ↓
Monitoring
     ↓
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the risk layer sits &lt;strong&gt;between the strategy and execution&lt;/strong&gt; - and can also react to problems discovered after execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  A good signal doesn't guarantee a safe trade
&lt;/h2&gt;

&lt;p&gt;Imagine your strategy generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signal looks good.&lt;/p&gt;

&lt;p&gt;Market data is available.&lt;/p&gt;

&lt;p&gt;The execution engine is working.&lt;/p&gt;

&lt;p&gt;Normally you might immediately submit the order.&lt;/p&gt;

&lt;p&gt;But what if:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current position = already near limit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market data = stale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Previous execution = still unverified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position reconciliation = failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Daily loss limit = already breached
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy may still be correct.&lt;/p&gt;

&lt;p&gt;The system should still refuse the trade.&lt;/p&gt;

&lt;p&gt;That's the difference between &lt;strong&gt;strategy logic&lt;/strong&gt; and &lt;strong&gt;trading-system control&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk should be a gate, not a suggestion
&lt;/h2&gt;

&lt;p&gt;I prefer thinking about risk as a gate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal
  ↓
Risk Gate
  |
  +---- BLOCK
  |
  +---- ALLOW
          ↓
       Execute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy produces an intent.&lt;/p&gt;

&lt;p&gt;The risk layer decides whether that intent is permitted under the current system state.&lt;/p&gt;

&lt;p&gt;This makes risk reusable across different strategies.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Momentum
Arbitrage
Market Making
Copy Trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can all feed the same risk engine.&lt;/p&gt;

&lt;p&gt;You don't want every strategy implementing its own completely different exposure and safety rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  What should a Polymarket bot check before trading?
&lt;/h2&gt;

&lt;p&gt;There isn't one universal configuration.&lt;/p&gt;

&lt;p&gt;The exact limits depend on the account, strategy, capital, market, and operating policy.&lt;/p&gt;

&lt;p&gt;But a useful production-oriented system can evaluate several categories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Position limits
&lt;/h2&gt;

&lt;p&gt;The simplest check is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How large can this position become?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current position: 80
New order:        30
Maximum:          100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct decision is not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;80 &amp;lt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to consider the resulting position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;80 + 30 = 110
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Risk checks should evaluate &lt;strong&gt;post-trade state&lt;/strong&gt;, not just current state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Market exposure
&lt;/h2&gt;

&lt;p&gt;A position limit isn't always enough.&lt;/p&gt;

&lt;p&gt;A token or market can become a large portion of the overall portfolio even when an individual order looks small.&lt;/p&gt;

&lt;p&gt;So the system can maintain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market Exposure
+
Portfolio Exposure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market A:   $8,000
Market B:   $5,000
Market C:   $3,000

Total:     $16,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new trade might be acceptable at the market level but unacceptable at the portfolio level.&lt;/p&gt;

&lt;p&gt;That is why I prefer two layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Per-Market Risk
       +
Portfolio Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Total exposure
&lt;/h2&gt;

&lt;p&gt;The same principle applies at the system level.&lt;/p&gt;

&lt;p&gt;Suppose the bot operates across several Polymarket markets.&lt;/p&gt;

&lt;p&gt;Each individual position may be inside its own limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market A → OK
Market B → OK
Market C → OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the aggregate exposure could still be too large.&lt;/p&gt;

&lt;p&gt;The risk engine therefore needs a global view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Positions
   ↓
Exposure
   ↓
Portfolio Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes particularly important when multiple strategies or execution workers share the same account.&lt;/p&gt;




&lt;h2&gt;
  
  
  Daily loss limits
&lt;/h2&gt;

&lt;p&gt;Another simple but useful control is a daily loss boundary.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Daily P&amp;amp;L
   ↓
Compare with configured threshold
   ↓
Within limit → continue
Over limit   → pause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact accounting method needs to be defined carefully.&lt;/p&gt;

&lt;p&gt;The important thing is that the limit is implemented as a &lt;strong&gt;system policy&lt;/strong&gt;, not as a number hidden inside one strategy.&lt;/p&gt;

&lt;p&gt;Once the threshold is reached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RISK LIMIT BREACHED
        ↓
TRADING PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy doesn't get to override that simply because it sees another attractive signal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution failures should affect risk
&lt;/h2&gt;

&lt;p&gt;Risk isn't only about P&amp;amp;L.&lt;/p&gt;

&lt;p&gt;Repeated execution failures can indicate that something is wrong with the system.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order 1 → failed
Order 2 → failed
Order 3 → failed
Order 4 → failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At some point the control plane should ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why am I still allowing new orders?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A simple operational rule can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution Failures
       ↓
Failure Threshold
       ↓
Pause
       ↓
Investigate / Recover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This connects the execution layer with the risk layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stale market data is a risk condition
&lt;/h2&gt;

&lt;p&gt;This is one of the easiest conditions to overlook.&lt;/p&gt;

&lt;p&gt;A strategy might believe it has current market information because the WebSocket connection still exists.&lt;/p&gt;

&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connected = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;doesn't necessarily mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data = fresh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need a freshness measurement.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last market event: 500 ms ago
Maximum allowed age: 2 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's healthy.&lt;/p&gt;

&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last market event: 12 seconds ago
Maximum allowed age: 2 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MARKET_DATA_STALE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reduce permissions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;depending on policy.&lt;/p&gt;

&lt;p&gt;The important part is that stale data becomes an explicit system condition rather than being silently ignored.&lt;/p&gt;




&lt;h2&gt;
  
  
  A WebSocket reconnect is not a clean bill of health
&lt;/h2&gt;

&lt;p&gt;This connects directly to the recovery work I've been doing around &lt;strong&gt;&lt;a href="https://dev.to/casatrick/polymarket-websocket-reconnects-rebuilding-trading-state-safely-47h3"&gt;Polymarket WebSocket recovery&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event A
Event B
DISCONNECT
Event C
Event D
RECONNECT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After reconnecting, the socket is healthy again.&lt;/p&gt;

&lt;p&gt;Your local trading state may not be.&lt;/p&gt;

&lt;p&gt;There could be missing events.&lt;/p&gt;

&lt;p&gt;That means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONNECTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should not automatically produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TRADING_ALLOWED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A safer sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DISCONNECT
   ↓
PAUSE
   ↓
RECONNECT
   ↓
REBUILD STATE
   ↓
RECONCILE
   ↓
VERIFY
   ↓
CHECK RISK
   ↓
RESUME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Risk controls are therefore connected directly to recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position mismatch should block new exposure
&lt;/h2&gt;

&lt;p&gt;This is where the risk engine needs to consume reconciliation results.&lt;/p&gt;

&lt;p&gt;Suppose the system believes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected position: 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the external account shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Observed position: 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system has a mismatch.&lt;/p&gt;

&lt;p&gt;My earlier work on &lt;strong&gt;&lt;a href="https://dev.to/casatrick/polymarket-position-reconciliation-keeping-trading-bot-state-in-sync-xxxxxxxx"&gt;Polymarket position reconciliation&lt;/a&gt;&lt;/strong&gt; treats this as a state problem rather than something the strategy should silently fix.&lt;/p&gt;

&lt;p&gt;Until the mismatch is understood, the risk engine may need to produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSITION_UNVERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TRADING_BLOCKED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That prevents the system from adding more exposure based on incorrect local state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution uncertainty should propagate into risk
&lt;/h2&gt;

&lt;p&gt;The same applies to execution verification.&lt;/p&gt;

&lt;p&gt;Suppose an order reaches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the rest of the execution lifecycle is not yet verified.&lt;/p&gt;

&lt;p&gt;The system might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution = UNKNOWN
Position   = UNKNOWN
Risk       = UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that situation, the safest operational response is generally not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;continue trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAUSE
↓
VERIFY
↓
RECONCILE
↓
RECHECK RISK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's one reason I built a separate &lt;strong&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;Polymarket Execution Verifier&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Execution verification and risk control answer different questions.&lt;/p&gt;

&lt;p&gt;The verifier asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What happened?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The risk engine asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Given what happened, is further trading permitted?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Risk needs multiple layers
&lt;/h2&gt;

&lt;p&gt;I think about risk as a stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Risk
     ↓
Position Risk
     ↓
Market Exposure
     ↓
Portfolio Exposure
     ↓
System Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And another stack operates alongside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market Data Health
     ↓
Execution Health
     ↓
Reconciliation Health
     ↓
Recovery State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final trading decision can combine both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SIGNAL
  ↓
RISK
  +
SYSTEM HEALTH
  ↓
ALLOW / BLOCK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much stronger than checking one maximum position value.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk decisions should be explainable
&lt;/h2&gt;

&lt;p&gt;A good risk engine shouldn't just return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should return the reason.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision: BLOCK

Reason:
MAX_TOTAL_EXPOSURE_BREACHED

Current:
$51,200

Limit:
$50,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision: BLOCK

Reason:
MARKET_DATA_STALE

Current age:
8.4s

Maximum:
2.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision: BLOCK

Reason:
POSITION_UNVERIFIED

Expected:
100

Observed:
40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes risk decisions observable and much easier to debug.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk state should be visible to the control plane
&lt;/h2&gt;

&lt;p&gt;This is where the separate &lt;strong&gt;Polymarket Trading Control Plane&lt;/strong&gt; fits into the architecture.&lt;/p&gt;

&lt;p&gt;The control plane can consume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Health
Risk
Execution
Position
Reconciliation
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and convert them into a system state.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEALTHY
   ↓
Risk breach
   ↓
PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DISCONNECTED
   ↓
RECOVERING
   ↓
RECONCILING
   ↓
RISK CHECK
   ↓
HEALTHY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy doesn't need to understand every operational state.&lt;/p&gt;

&lt;p&gt;The control plane handles that responsibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Kill switch vs pause
&lt;/h2&gt;

&lt;p&gt;These shouldn't necessarily mean the same thing.&lt;/p&gt;

&lt;p&gt;A normal pause might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TRADING PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;market data is stale&lt;/li&gt;
&lt;li&gt;reconciliation is pending&lt;/li&gt;
&lt;li&gt;an execution failed&lt;/li&gt;
&lt;li&gt;exposure is temporarily too high&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system can potentially recover and resume after verification.&lt;/p&gt;

&lt;p&gt;A kill switch is stronger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KILL SWITCH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It represents an emergency operational control that blocks trading until explicitly cleared.&lt;/p&gt;

&lt;p&gt;The important property is that neither should be buried inside strategy code.&lt;/p&gt;

&lt;p&gt;They belong to the control layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  When should the bot actually stop trading?
&lt;/h2&gt;

&lt;p&gt;This is the question I would build the entire risk architecture around.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Can the strategy generate another signal?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“What conditions should make another trade unacceptable?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position limit breached
        OR
Portfolio exposure too high
        OR
Daily loss threshold breached
        OR
Market data stale
        OR
Execution state unknown
        OR
Position mismatch
        OR
Critical execution failures
        OR
Recovery incomplete
        ↓
TRADING BLOCKED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is effectively a &lt;strong&gt;trading permission engine&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trading permission as a state machine
&lt;/h2&gt;

&lt;p&gt;Instead of a boolean like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;can_trade = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the system can have explicit permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALLOW
BLOCK
REQUIRES_RECONCILIATION
REQUIRES_RECOVERY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Healthy
   ↓
ALLOW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position mismatch
   ↓
REQUIRES_RECONCILIATION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Critical risk breach
   ↓
BLOCK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket recovery
   ↓
REQUIRES_RECOVERY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the rest of the system something concrete to act on.&lt;/p&gt;




&lt;h2&gt;
  
  
  A practical pre-trade pipeline
&lt;/h2&gt;

&lt;p&gt;The architecture can therefore become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strategy Signal
      |
      v
Market Data Check
      |
      v
Execution State Check
      |
      v
Position Check
      |
      v
Exposure Check
      |
      v
Risk Limits
      |
      v
Trading Permission
      |
      +---- BLOCK
      |
      +---- ALLOW
              |
              v
           Execute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is that the order does not reach execution until the relevant checks have passed.&lt;/p&gt;




&lt;h2&gt;
  
  
  But pre-trade checks aren't enough
&lt;/h2&gt;

&lt;p&gt;Something can change immediately after the order is submitted.&lt;/p&gt;

&lt;p&gt;So risk has to continue after execution.&lt;/p&gt;

&lt;p&gt;A simplified lifecycle is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pre-Trade
   ↓
Execute
   ↓
Verify Execution
   ↓
Reconcile Position
   ↓
Recalculate Exposure
   ↓
Recheck Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That creates a feedback loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TRADE
  ↓
NEW STATE
  ↓
RISK
  ↓
NEXT DECISION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system isn't evaluating risk once.&lt;/p&gt;

&lt;p&gt;It's continuously evaluating the state that future decisions depend on.&lt;/p&gt;




&lt;h2&gt;
  
  
  How this connects to the broader Polymarket stack
&lt;/h2&gt;

&lt;p&gt;This is the direction I’m building toward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Polymarket Trading Bot
        |
        v
     Strategy
        |
        v
      Risk
        |
        v
    Execution
        |
        v
Execution Verifier
        |
        v
Position Reconciliation
        |
        v
Trading Control Plane
        |
        +---- Monitoring
        +---- Alerts
        +---- Recovery
        +---- Pause / Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer has a different job.&lt;/p&gt;

&lt;p&gt;The strategy generates decisions.&lt;/p&gt;

&lt;p&gt;The risk engine determines whether those decisions are permitted.&lt;/p&gt;

&lt;p&gt;Execution submits them.&lt;/p&gt;

&lt;p&gt;The execution verifier establishes what actually happened.&lt;/p&gt;

&lt;p&gt;Reconciliation checks the resulting state.&lt;/p&gt;

&lt;p&gt;The control plane determines whether the overall system is healthy enough to continue.&lt;/p&gt;




&lt;h2&gt;
  
  
  A simple risk checklist
&lt;/h2&gt;

&lt;p&gt;Before allowing a new Polymarket trade, the system should be able to answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Is market data fresh?
[ ] Is the connection healthy?
[ ] Is the current execution state verified?
[ ] Is the current position known?
[ ] Is reconciliation complete?
[ ] Is the resulting position inside its limit?
[ ] Is market exposure inside its limit?
[ ] Is total exposure inside its limit?
[ ] Is daily loss inside its limit?
[ ] Are execution failures below the configured threshold?
[ ] Is the system fully recovered?
[ ] Is trading permission ALLOW?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one of the critical answers is unknown, the system should know that it is unknown.&lt;/p&gt;

&lt;p&gt;That's much better than silently assuming everything is fine.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real goal isn't to stop trading
&lt;/h2&gt;

&lt;p&gt;Risk controls aren't there because trading should always be conservative.&lt;/p&gt;

&lt;p&gt;They're there because the system needs a clear answer to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When should trading stop?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without explicit rules, the bot can continue trading through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stale data
bad state
wrong positions
execution uncertainty
risk breaches
recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At that point, the strategy isn't really controlling the system anymore.&lt;/p&gt;

&lt;p&gt;The system is just continuing because nobody told it to stop.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A Polymarket trading bot doesn't only need a strategy that knows when to trade.&lt;/p&gt;

&lt;p&gt;It needs infrastructure that knows when &lt;strong&gt;not&lt;/strong&gt; to trade.&lt;/p&gt;

&lt;p&gt;The model I'm working toward is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal
   ↓
Risk Check
   ↓
Execution
   ↓
Verification
   ↓
Reconciliation
   ↓
Risk Recheck
   ↓
Continue / Pause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the final permission should come from actual system state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market data healthy
+
Execution verified
+
Position reconciled
+
Exposure within limits
+
Risk within limits
+
Recovery complete
=
TRADING ALLOWED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAUSE
↓
RECONCILE
↓
VERIFY
↓
RECOVER
↓
RECHECK RISK
↓
RESUME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the difference between a bot that can place orders and a trading system that knows when it should stop.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>programming</category>
    </item>
    <item>
      <title>Polymarket Execution Verification: When “Filled” Doesn’t Mean Settled</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Wed, 30 Sep 2026 05:44:27 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-execution-verification-when-filled-doesnt-mean-settled-15ni</link>
      <guid>https://dev.to/casatrick/polymarket-execution-verification-when-filled-doesnt-mean-settled-15ni</guid>
      <description>&lt;p&gt;A Polymarket trading bot can receive a successful execution response and still not know enough to safely continue trading.&lt;/p&gt;

&lt;p&gt;That sounds like a small implementation detail.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;In a production trading system, there is a big difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"I sent the order."

"I received a fill."

"The fill has a transaction."

"The transaction is confirmed."

"The execution is settled."

"My position matches reality."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are different states.&lt;/p&gt;

&lt;p&gt;If your bot treats them as one state called &lt;code&gt;FILLED&lt;/code&gt;, eventually you get a system that believes it owns one position while the account says something else.&lt;/p&gt;

&lt;p&gt;This is why execution verification deserves its own layer.&lt;/p&gt;

&lt;p&gt;I built a small Polymarket execution-verification project around this idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't let a trading system assume an execution is complete before the relevant state has been verified.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;Polymarket Execution Verifier&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem with treating &lt;code&gt;FILLED&lt;/code&gt; as the end
&lt;/h2&gt;

&lt;p&gt;Imagine the strategy decides to buy 100 shares.&lt;/p&gt;

&lt;p&gt;The bot submits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUY 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it receives an execution update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A naive implementation does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;filled = true
position += 100
continue trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is dangerous.&lt;/p&gt;

&lt;p&gt;The bot has made several assumptions at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the requested quantity was actually matched&lt;/li&gt;
&lt;li&gt;the execution event was complete&lt;/li&gt;
&lt;li&gt;the settlement transaction is known&lt;/li&gt;
&lt;li&gt;the transaction was successfully processed&lt;/li&gt;
&lt;li&gt;the external position reflects the expected result&lt;/li&gt;
&lt;li&gt;no relevant event was missed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those assumptions can fail independently.&lt;/p&gt;

&lt;p&gt;A safer trading system treats execution as a state transition rather than a boolean.&lt;/p&gt;




&lt;h2&gt;
  
  
  Order → Fill → Transaction → Confirmation → Settlement → Position
&lt;/h2&gt;

&lt;p&gt;The execution lifecycle I use is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
   ↓
SUBMITTED
   ↓
ACCEPTED
   ↓
MATCHED
   ↓
FILLED
   ↓
TX_PENDING
   ↓
CONFIRMED
   ↓
SETTLED
   ↓
POSITION VERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also failure and uncertainty states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REJECTED
CANCELLED
FAILED
RETRYING
UNKNOWN
INCONSISTENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that &lt;strong&gt;&lt;code&gt;FILLED&lt;/code&gt; is not necessarily the final state your risk engine should trust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Polymarket's current CLOB tooling also distinguishes trade completion from simply observing a fill: the current client logic considers a trade resolved when it reaches a terminal outcome, such as having a settlement transaction hash or having failed. The client can also expose transaction hashes when available and trade IDs that can be followed while a hash is not yet available.&lt;/p&gt;

&lt;p&gt;That distinction is exactly what an execution verifier should make explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why execution state becomes uncertain
&lt;/h2&gt;

&lt;p&gt;There are several ways a trading bot can end up with incomplete execution information.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The order was matched, but transaction information is not available yet
&lt;/h2&gt;

&lt;p&gt;A matching event tells you something important happened.&lt;/p&gt;

&lt;p&gt;It does not necessarily mean every downstream piece of information is already available to your application.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
  ↓
Matched
  ↓
Fill observed
  ↓
Transaction information unavailable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct state is not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DONE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TX_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;depending on what the system actually knows.&lt;/p&gt;

&lt;p&gt;Your execution layer should preserve that uncertainty instead of hiding it.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Partial execution
&lt;/h2&gt;

&lt;p&gt;Suppose you intended:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 shares
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but only 40 were matched.&lt;/p&gt;

&lt;p&gt;Your bot now has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested: 100
Matched:    40
Remaining:  60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That remaining quantity cannot simply disappear from your accounting.&lt;/p&gt;

&lt;p&gt;The execution system needs to know whether the remaining 60:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;are still active&lt;/li&gt;
&lt;li&gt;were cancelled&lt;/li&gt;
&lt;li&gt;expired&lt;/li&gt;
&lt;li&gt;failed&lt;/li&gt;
&lt;li&gt;were never accepted&lt;/li&gt;
&lt;li&gt;require another verification step&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why requested quantity and executed quantity should always be separate fields.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requested_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matched_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remaining_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"execution_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PARTIAL"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact data model can vary, but the principle should remain the same.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The transaction is confirmed, but the position still isn't verified
&lt;/h2&gt;

&lt;p&gt;This is one of the most important boundaries.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
   ↓
TX CONFIRMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A developer may immediately update the internal position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local_position += executed_size
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the trading system still has another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the external account state actually match what I expected?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected position: 125.5

Observed position: 100.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The transaction succeeded.&lt;/p&gt;

&lt;p&gt;The internal state is still wrong.&lt;/p&gt;

&lt;p&gt;This is where execution verification connects directly to position reconciliation.&lt;/p&gt;

&lt;p&gt;A trading bot should not confuse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;transaction verified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position verified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are different checks.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. A WebSocket event can be missed
&lt;/h2&gt;

&lt;p&gt;Real-time trading systems depend heavily on event streams.&lt;/p&gt;

&lt;p&gt;That creates another failure mode.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event 101 received
Event 102 received
[connection drops]
Event 103 happens
Event 104 happens
connection restored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your application reconnects.&lt;/p&gt;

&lt;p&gt;But reconnecting the socket does not magically reconstruct the events that were missed.&lt;/p&gt;

&lt;p&gt;You now have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local state != external state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct recovery sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DISCONNECT
   ↓
PAUSE
   ↓
RECONNECT
   ↓
REBUILD STATE
   ↓
RECONCILE
   ↓
VERIFY
   ↓
RESUME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why execution verification cannot be implemented as a single API callback.&lt;/p&gt;

&lt;p&gt;It belongs inside a larger state-management system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution verification should be explicit
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="py"&gt;.filled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;update_position&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;continue_trading&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;use an explicit state machine.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ExecutionState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Intended&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Submitted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Accepted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Matched&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Filled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TxPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Confirmed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Settled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PositionVerified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;Rejected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Cancelled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Failed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Retrying&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Inconsistent&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;Now the rest of the system can react to the state.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
    ↓
Does transaction information exist?
    ↓
No
    ↓
TX_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TX_PENDING
    ↓
Transaction confirmed?
    ↓
Yes
    ↓
CONFIRMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONFIRMED
    ↓
Position matches expected state?
    ↓
No
    ↓
INCONSISTENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much safer than a single &lt;code&gt;filled=true&lt;/code&gt; flag.&lt;/p&gt;




&lt;h2&gt;
  
  
  The verifier should separate facts from assumptions
&lt;/h2&gt;

&lt;p&gt;A useful execution record might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strategy_decision_id
internal_execution_id

polymarket_order_id
trade_id

market_id
token_id
side

requested_quantity
matched_quantity

order_timestamp
fill_timestamp

transaction_hash
confirmation_timestamp

execution_state
position_state

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

&lt;/div&gt;



&lt;p&gt;The important property is that each value represents something actually observed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requested_quantity = 100
matched_quantity   = 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is different from assuming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;matched_quantity = requested_quantity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Likewise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execution_state = CONFIRMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should not automatically imply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;position_state = VERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good trading infrastructure makes those boundaries visible.&lt;/p&gt;




&lt;h2&gt;
  
  
  What should happen when verification fails?
&lt;/h2&gt;

&lt;p&gt;This is where execution verification becomes a risk-control problem.&lt;/p&gt;

&lt;p&gt;Suppose the verifier reaches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INCONSISTENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy layer should not simply continue because it still has a signal.&lt;/p&gt;

&lt;p&gt;The control flow should look more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution uncertain
       ↓
Pause affected trading
       ↓
Query authoritative state
       ↓
Reconcile orders / fills / positions
       ↓
Verify risk
       ↓
    Healthy?
   ↙       ↘
 YES       NO
 ↓          ↓
RESUME     PAUSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trading decision is therefore not just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Should I buy?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the system in a state where buying is safe?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a very different engineering problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  A useful execution verifier architecture
&lt;/h2&gt;

&lt;p&gt;The project I am working on separates verification into layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Strategy
                    │
                    ▼
              Execution Request
                    │
                    ▼
              Order Submission
                    │
                    ▼
            ┌──────────────────┐
            │   Execution      │
            │    Verifier      │
            └──────────────────┘
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
      Order        Fill       Transaction
        │           │           │
        └───────────┼───────────┘
                    ▼
               Settlement
                    │
                    ▼
            Position Verification
                    │
                    ▼
                 Risk Layer
                    │
                    ▼
             Resume / Pause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the system a clear boundary:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;the strategy produces an intention; the infrastructure determines whether reality matches that intention.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution verification vs position reconciliation
&lt;/h2&gt;

&lt;p&gt;These two concepts are closely related, but they solve different problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution verification
&lt;/h3&gt;

&lt;p&gt;Question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did the intended trade actually reach a verified terminal execution state?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
→ Fill
→ Transaction
→ Confirmation
→ Settlement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Position reconciliation
&lt;/h3&gt;

&lt;p&gt;Question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the account's actual position match the position my system believes it has?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected State
      ↓
External State
      ↓
Compare
      ↓
Match / Mismatch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need both.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution: VERIFIED
Position:  MISMATCH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is completely possible.&lt;/p&gt;

&lt;p&gt;Likewise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution: UNKNOWN
Position:  temporarily unchanged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is also possible.&lt;/p&gt;

&lt;p&gt;The system should retain both pieces of information.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters for automated trading
&lt;/h2&gt;

&lt;p&gt;A strategy can be statistically correct and still lose control of the system.&lt;/p&gt;

&lt;p&gt;The failure may happen after the signal.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal
  ↓
Order
  ↓
Partial Fill
  ↓
WebSocket Gap
  ↓
Incorrect Local State
  ↓
Incorrect Position
  ↓
Incorrect Risk Calculation
  ↓
New Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original strategy wasn't necessarily the problem.&lt;/p&gt;

&lt;p&gt;The execution state was.&lt;/p&gt;

&lt;p&gt;This is why production trading infrastructure needs more than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market data
+
strategy
+
order placement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market data
+
strategy
+
execution
+
verification
+
reconciliation
+
risk
+
recovery
+
observability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When should the bot resume trading?
&lt;/h2&gt;

&lt;p&gt;This is the final question.&lt;/p&gt;

&lt;p&gt;Suppose the bot reconnects after losing its market-data connection.&lt;/p&gt;

&lt;p&gt;Should it immediately resume?&lt;/p&gt;

&lt;p&gt;No state machine should answer that with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;socket_connected == true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A safer condition is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connection healthy
AND
market data fresh
AND
orders reconciled
AND
fills reconciled
AND
positions verified
AND
exposure verified
AND
risk limits healthy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only then should the system move back toward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TRADING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is also where an execution verifier connects to a broader trading control plane.&lt;/p&gt;

&lt;p&gt;The verifier answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What actually happened?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reconciliation layer answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Does my state match reality?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The risk layer answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is continuing safe according to configured limits?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The control plane coordinates the result.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the execution verifier
&lt;/h2&gt;

&lt;p&gt;My current project focuses on making these execution states explicit instead of burying them inside strategy code.&lt;/p&gt;

&lt;p&gt;Repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;github.com/casatrickdev/polymarket-execution-verifier&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is intentionally focused on one problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Verify Polymarket execution
across
order → fill → transaction → confirmation → settlement → position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes it easier to integrate with the rest of a production trading stack.&lt;/p&gt;

&lt;p&gt;The broader architecture connects this with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Polymarket trading bot&lt;/li&gt;
&lt;li&gt;position reconciliation&lt;/li&gt;
&lt;li&gt;trading monitoring&lt;/li&gt;
&lt;li&gt;risk controls&lt;/li&gt;
&lt;li&gt;recovery logic&lt;/li&gt;
&lt;li&gt;operational control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Projects:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-bot" rel="noopener noreferrer"&gt;Polymarket Trading Bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-control-plane" rel="noopener noreferrer"&gt;Polymarket Trading Control Plane&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical execution-verification checklist
&lt;/h2&gt;

&lt;p&gt;Before allowing a trading bot to treat an execution as complete, I want to be able to answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Was the order accepted?
[ ] Was it matched?
[ ] How much actually filled?
[ ] Is the remaining quantity known?
[ ] Is the relevant transaction known?
[ ] Has the transaction reached the expected terminal state?
[ ] Is settlement verified?
[ ] Does the external position match expected state?
[ ] Is exposure correct?
[ ] Are risk limits still valid?
[ ] Is there any unresolved execution uncertainty?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one of those answers is unknown, the system should know that it is unknown.&lt;/p&gt;

&lt;p&gt;That sounds obvious.&lt;/p&gt;

&lt;p&gt;In production, it is one of the differences between a bot that places trades and a trading system that can actually be trusted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A Polymarket trading bot should not think:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED = DONE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A better mental model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
   ↓
SUBMITTED
   ↓
MATCHED
   ↓
FILLED
   ↓
TRANSACTION VERIFIED
   ↓
SETTLED
   ↓
POSITION VERIFIED
   ↓
RISK VERIFIED
   ↓
SAFE TO CONTINUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important question is not only whether an order was filled.&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can the system prove that its current trading state matches reality?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the reason for building an execution-verification layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Polymarket Execution Verifier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Verify execution across:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER
FILL
TRANSACTION
CONFIRMATION
SETTLEMENT
POSITION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-execution-verifier&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Polymarket Trading Bot: Execution, Risk Management, and Backtesting&lt;/li&gt;
&lt;li&gt;Polymarket Partial Fills: How Automated Trading Bots Should Handle Them&lt;/li&gt;
&lt;li&gt;Polymarket Position Reconciliation: Keeping Trading Bot State in Sync&lt;/li&gt;
&lt;li&gt;Polymarket WebSocket Reconnects: Rebuilding Trading State Safely&lt;/li&gt;
&lt;li&gt;Polymarket Trading Bot Monitoring: State, Risk and Recovery&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Polymarket WebSocket Reconnects: Rebuilding Trading State Safely</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Tue, 29 Sep 2026 05:53:03 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-websocket-reconnects-rebuilding-trading-state-safely-47h3</link>
      <guid>https://dev.to/casatrick/polymarket-websocket-reconnects-rebuilding-trading-state-safely-47h3</guid>
      <description>&lt;p&gt;Your Polymarket trading bot is connected to the WebSocket.&lt;/p&gt;

&lt;p&gt;Then the connection drops.&lt;/p&gt;

&lt;p&gt;A few seconds later, it reconnects.&lt;/p&gt;

&lt;p&gt;Everything looks normal again.&lt;/p&gt;

&lt;p&gt;But should the bot immediately start trading?&lt;/p&gt;

&lt;p&gt;I don't think so.&lt;/p&gt;

&lt;p&gt;A successful WebSocket reconnect only tells you that the connection has returned. It does &lt;strong&gt;not&lt;/strong&gt; prove that the trading system has recovered the events it may have missed or that its local orders, fills, positions, and risk state are still correct.&lt;/p&gt;

&lt;p&gt;The recovery problem is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Disconnect
   ↓
Unknown state
   ↓
Reconnect
   ↓
Reconcile
   ↓
Verify
   ↓
Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the problems I'm exploring with the &lt;strong&gt;Polymarket Trading Control Plane&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The project is an operational layer around automated Polymarket trading systems, focused on state, health, risk, reconciliation, and recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  A WebSocket reconnect is not a recovery
&lt;/h2&gt;

&lt;p&gt;It's tempting to write something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnected
       ↓
Reconnect
       ↓
Trading resumes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's fine for a simple data consumer.&lt;/p&gt;

&lt;p&gt;For an automated trading system, there is a missing step.&lt;/p&gt;

&lt;p&gt;What happened while the connection was unavailable?&lt;/p&gt;

&lt;p&gt;Maybe an order changed state.&lt;/p&gt;

&lt;p&gt;Maybe a fill occurred.&lt;/p&gt;

&lt;p&gt;Maybe a position changed.&lt;/p&gt;

&lt;p&gt;Maybe the application missed an event.&lt;/p&gt;

&lt;p&gt;Maybe several events arrived in a different sequence.&lt;/p&gt;

&lt;p&gt;The system cannot assume that reconnecting restores its internal state automatically.&lt;/p&gt;

&lt;p&gt;A more defensive model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnect
        ↓
Trading = PAUSED
        ↓
Reconnect
        ↓
Rebuild / reconcile state
        ↓
Verify orders
        ↓
Verify fills
        ↓
Verify positions
        ↓
Verify risk
        ↓
Trading = RESUMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction is central to the Control Plane architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  What can happen during a disconnect?
&lt;/h2&gt;

&lt;p&gt;Consider a simple sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event A
   ↓
WebSocket disconnect
   ↓
Events B, C, D
   ↓
WebSocket reconnect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local application may only know about A.&lt;/p&gt;

&lt;p&gt;If B, C, and D affected trading state, local state is now incomplete.&lt;/p&gt;

&lt;p&gt;For a trading bot, that can affect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open orders&lt;/li&gt;
&lt;li&gt;fills&lt;/li&gt;
&lt;li&gt;positions&lt;/li&gt;
&lt;li&gt;exposure&lt;/li&gt;
&lt;li&gt;PnL&lt;/li&gt;
&lt;li&gt;risk decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process may still be running.&lt;/p&gt;

&lt;p&gt;The problem is that it may no longer know the current state.&lt;/p&gt;




&lt;h2&gt;
  
  
  The dangerous part is that nothing has to crash
&lt;/h2&gt;

&lt;p&gt;A crash is obvious.&lt;/p&gt;

&lt;p&gt;A state discrepancy is not.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process:       RUNNING
WebSocket:     CONNECTED
API:           OK
Strategy:      ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything looks healthy.&lt;/p&gt;

&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position state: UNVERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a very different situation.&lt;/p&gt;

&lt;p&gt;This is why I prefer distinguishing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;process health&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;from:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;trading-system health&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Control Plane currently models explicit operational states such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEALTHY
DEGRADED
PAUSED
RECOVERING
FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than treating “process is alive” as the complete health model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why pause trading during the reconnect
&lt;/h2&gt;

&lt;p&gt;Suppose the bot has no verified state during the disconnect.&lt;/p&gt;

&lt;p&gt;If it keeps trading, its next decision could be based on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stale positions
stale exposure
stale order state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That creates a bad sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unknown state
    ↓
Strategy continues
    ↓
New order
    ↓
More exposure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A safer approach is:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="7t9r9d"&lt;br&gt;
Unknown state&lt;br&gt;
    ↓&lt;br&gt;
No new risk&lt;br&gt;
    ↓&lt;br&gt;
Recovery&lt;br&gt;
    ↓&lt;br&gt;
Verified state&lt;br&gt;
    ↓&lt;br&gt;
Resume&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The Control Plane's operational model explicitly includes `PAUSE TRADING`, `RESUME TRADING`, `KILL SWITCH`, and `RECONCILE STATE` as control actions.

---

## Reconnection should transition the system into recovery

I like thinking about reconnect as a state transition rather than a single callback.

For example:



```plaintext
HEALTHY
    ↓
WebSocket disconnect
    ↓
DEGRADED
    ↓
PAUSED
    ↓
RECONNECTING
    ↓
RECOVERING
    ↓
VERIFIED
    ↓
HEALTHY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The exact implementation can vary.&lt;/p&gt;

&lt;p&gt;The principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The system should know which phase of recovery it is in.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes behavior easier to reason about and easier to observe.&lt;/p&gt;


&lt;h2&gt;
  
  
  Rebuild state after reconnect
&lt;/h2&gt;

&lt;p&gt;Once the connection returns, the system needs a way to establish its current state.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reconnect
    ↓
Read current remote state
    ↓
Compare local state
    ↓
Repair discrepancies
    ↓
Verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is different from simply subscribing to the stream again.&lt;/p&gt;

&lt;p&gt;The Control Plane is designed around local-versus-remote state comparison for orders and positions, with reconciliation after reconnects, restarts, suspected event gaps, unexpected API responses, and mismatches.&lt;/p&gt;




&lt;h2&gt;
  
  
  What exactly should be reconciled?
&lt;/h2&gt;

&lt;p&gt;Position is only one part of the problem.&lt;/p&gt;

&lt;p&gt;The trading system has multiple related state domains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders
   ↓
Trades / Fills
   ↓
Positions
   ↓
Exposure
   ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means recovery should not stop at:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“My position looks correct.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should eventually establish that the broader trading state is internally consistent.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders:      verified
Fills:       verified
Positions:   verified
Exposure:    verified
Risk:        verified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only then does the system have a strong basis for resuming normal operation.&lt;/p&gt;

&lt;p&gt;The Control Plane explicitly treats these as separate observable state areas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Missed events are not the only problem
&lt;/h2&gt;

&lt;p&gt;A disconnect can create several types of uncertainty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missed event
&lt;/h3&gt;

&lt;p&gt;The application never saw a state transition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplicate event
&lt;/h3&gt;

&lt;p&gt;The application processes the same event more than once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delayed event
&lt;/h3&gt;

&lt;p&gt;The event arrives much later than expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Out-of-order state
&lt;/h3&gt;

&lt;p&gt;The system observes related events in an unexpected sequence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restart during the gap
&lt;/h3&gt;

&lt;p&gt;The application loses in-memory state before recovery.&lt;/p&gt;

&lt;p&gt;That means the recovery layer has to be designed for &lt;strong&gt;uncertainty&lt;/strong&gt;, not only connection failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unknown should be an explicit state
&lt;/h2&gt;

&lt;p&gt;One of the most useful states in a trading system is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine the bot submits an order immediately before the connection drops.&lt;/p&gt;

&lt;p&gt;The application doesn't know whether the order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;was rejected
was accepted
partially filled
fully filled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wrong behavior is to guess.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
   ↓
PAUSE
   ↓
RECONCILE
   ↓
DETERMINE ACTUAL STATE
   ↓
CONTINUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Control Plane is designed around making uncertain state visible rather than hiding it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconnect after a partial fill
&lt;/h2&gt;

&lt;p&gt;This is where yesterday's &lt;strong&gt;Polymarket partial fills&lt;/strong&gt; work connects directly to today's problem.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested: 100
Filled:     40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the WebSocket disconnects.&lt;/p&gt;

&lt;p&gt;During the gap, the remaining 60 may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remain open&lt;/li&gt;
&lt;li&gt;be cancelled&lt;/li&gt;
&lt;li&gt;fill later&lt;/li&gt;
&lt;li&gt;change execution state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bot reconnects.&lt;/p&gt;

&lt;p&gt;It should not assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position = +40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without checking.&lt;/p&gt;

&lt;p&gt;And it should not assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position = +100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;either.&lt;/p&gt;

&lt;p&gt;It needs to reconcile.&lt;/p&gt;

&lt;p&gt;That's why partial-fill handling and WebSocket recovery are separate but connected problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Partial Fill
     ↓
Execution State
     ↓
WebSocket Gap
     ↓
Potential State Drift
     ↓
Reconciliation
     ↓
Verified Position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Reconnect after an application restart
&lt;/h2&gt;

&lt;p&gt;The same problem appears during restart.&lt;/p&gt;

&lt;p&gt;Suppose the process exits.&lt;/p&gt;

&lt;p&gt;When it comes back, it has only the state that was persisted locally.&lt;/p&gt;

&lt;p&gt;That state may not represent everything that happened while the application was down.&lt;/p&gt;

&lt;p&gt;A safer startup path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application starts
        ↓
Load local state
        ↓
    Connect
        ↓
Read remote state
        ↓
   Compare
        ↓
    Repair
        ↓
Verify risk
        ↓
Allow trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting the application and starting trading should therefore be two separate decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The recovery sequence I want
&lt;/h2&gt;

&lt;p&gt;The system I'm designing around this problem uses a simple mental model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Detect
2. Pause
3. Reconnect
4. Reconcile
5. Repair
6. Verify
7. Check risk
8. Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The purpose is not to make recovery slow.&lt;/p&gt;

&lt;p&gt;The purpose is to avoid turning an infrastructure problem into an automated trading problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk needs to be part of recovery
&lt;/h2&gt;

&lt;p&gt;Suppose the system reconciles positions successfully.&lt;/p&gt;

&lt;p&gt;That still doesn't necessarily mean trading should immediately resume.&lt;/p&gt;

&lt;p&gt;The system should also check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;current exposure&lt;/li&gt;
&lt;li&gt;position limits&lt;/li&gt;
&lt;li&gt;loss limits&lt;/li&gt;
&lt;li&gt;data freshness&lt;/li&gt;
&lt;li&gt;execution-failure count&lt;/li&gt;
&lt;li&gt;overall health&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Control Plane's risk model explicitly includes position limits, market exposure, total exposure, daily loss, execution-failure limits, and stale-data conditions.&lt;/p&gt;

&lt;p&gt;So the final recovery sequence becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reconcile
   ↓
Verify
   ↓
Risk Check
   ↓
ALLOW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reconcile
   ↓
Mismatch / Risk Breach
   ↓
REMAIN PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What if reconciliation fails?
&lt;/h2&gt;

&lt;p&gt;This is important too.&lt;/p&gt;

&lt;p&gt;Suppose the system reconnects but cannot establish a consistent state.&lt;/p&gt;

&lt;p&gt;It should not keep trying to trade just because the connection is technically healthy.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reconnect
   ↓
Reconcile
   ↓
Mismatch remains
   ↓
PAUSED
   ↓
Alert operator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is designed to make state mismatches and critical conditions observable so they can be handled deliberately.&lt;/p&gt;




&lt;h2&gt;
  
  
  The control plane architecture
&lt;/h2&gt;

&lt;p&gt;This is the larger architecture I'm building around the trading bot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         POLYMARKET
                              │
               ┌──────────────┴──────────────┐
               │                             │
         WebSocket Streams              API / Reads
               │                             │
               ▼                             ▼
       ┌────────────────┐          ┌──────────────────┐
       │ Event Ingestion│          │ State Reconciler │
       └───────┬────────┘          └────────┬─────────┘
               │                            │
               └────────────┬───────────────┘
                            ▼
                    ┌──────────────────┐
                    │    State Store   │
                    │                  │
                    │ Orders           │
                    │ Trades           │
                    │ Positions        │
                    │ Exposure         │
                    │ PnL              │
                    │ Health           │
                    └────────┬─────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
         Risk Engine    Health Engine   Alert Engine
              │              │              │
              └──────────────┼──────────────┘
                             ▼
                    ┌──────────────────┐
                    │  Control Plane   │
                    │                  │
                    │ Pause            │
                    │ Resume           │
                    │ Kill Switch      │
                    │ Reconcile        │
                    └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This architecture is intentionally centered on operational state rather than another trading strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Observability during recovery
&lt;/h2&gt;

&lt;p&gt;Recovery shouldn't happen inside a black box.&lt;/p&gt;

&lt;p&gt;The system should expose events such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WEBSOCKET_DISCONNECTED
RECONCILIATION_STARTED
STATE_MISMATCH
RECONCILIATION_COMPLETED
TRADING_PAUSED
TRADING_RESUMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The broader event model also includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER_FAILURE
PARTIAL_FILL
MARKET_DATA_STALE
RISK_LIMIT_BREACHED
KILL_SWITCH_TRIGGERED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That information can feed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dashboards&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;alerts&lt;/li&gt;
&lt;li&gt;webhooks&lt;/li&gt;
&lt;li&gt;external monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now an operator can see not only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The bot is connected.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;but:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The bot reconnected, reconciled, verified state, passed risk checks, and resumed.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much more useful operational signal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure scenarios worth testing
&lt;/h2&gt;

&lt;p&gt;A WebSocket recovery implementation should be tested against more than a clean disconnect.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Expected behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket disconnect&lt;/td&gt;
&lt;td&gt;Enter degraded state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reconnect&lt;/td&gt;
&lt;td&gt;Trigger recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missed event&lt;/td&gt;
&lt;td&gt;Reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate event&lt;/td&gt;
&lt;td&gt;Avoid double-counting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delayed event&lt;/td&gt;
&lt;td&gt;Validate current state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial fill during gap&lt;/td&gt;
&lt;td&gt;Reconcile actual fill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown order&lt;/td&gt;
&lt;td&gt;Block blind retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Process restart&lt;/td&gt;
&lt;td&gt;Reload and reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Position mismatch&lt;/td&gt;
&lt;td&gt;Pause trading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk breach&lt;/td&gt;
&lt;td&gt;Pause or kill&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Control Plane roadmap explicitly includes these kinds of failure and recovery scenarios.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconnect versus recovery
&lt;/h2&gt;

&lt;p&gt;This distinction is the main idea of the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reconnect
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can I connect to the stream again?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Recovery
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can I prove that my trading state is correct again?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are not the same question.&lt;/p&gt;

&lt;p&gt;A reconnect can take seconds.&lt;/p&gt;

&lt;p&gt;State recovery can require:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event verification
+
State reconciliation
+
Position verification
+
Risk checks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only after that should normal trading resume.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I'm building this
&lt;/h2&gt;

&lt;p&gt;The more I work on automated Polymarket trading, the more interesting the infrastructure around the strategy becomes.&lt;/p&gt;

&lt;p&gt;The strategy answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What should I trade?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The execution layer answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How should I place the order?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The execution verifier answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What actually happened?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The control plane answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I safely continue?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That separation is the direction I'm exploring across these projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Polymarket projects
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Polymarket Trading Bot
&lt;/h3&gt;

&lt;p&gt;Automated trading, execution, risk controls, and backtesting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-bot" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-bot&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Polymarket Execution Verifier
&lt;/h3&gt;

&lt;p&gt;Verifies the execution lifecycle across:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
 ↓
Fill
 ↓
Transaction
 ↓
Settlement
 ↓
Position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-execution-verifier&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Polymarket Trading Control Plane
&lt;/h3&gt;

&lt;p&gt;Monitoring, state, reconciliation, risk, health, alerts, and recovery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-control-plane" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-control-plane&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trading Bot
    ↓
Execution
    ↓
Execution Verification
    ↓
State Reconciliation
    ↓
Control Plane
    ↓
Risk / Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A WebSocket reconnect can tell you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“The connection is back.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It cannot automatically tell you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“The trading system is healthy again.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system may have missed events.&lt;/p&gt;

&lt;p&gt;Local state may be stale.&lt;/p&gt;

&lt;p&gt;Orders may be uncertain.&lt;/p&gt;

&lt;p&gt;Positions may need reconciliation.&lt;/p&gt;

&lt;p&gt;Risk may have changed.&lt;/p&gt;

&lt;p&gt;So the recovery path I want to build around is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DISCONNECT
    ↓
PAUSE
    ↓
RECONNECT
    ↓
RECONCILE
    ↓
VERIFY
    ↓
RISK CHECK
    ↓
RESUME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an automated Polymarket trading bot, &lt;strong&gt;reconnecting is a networking problem. Recovering safely is a trading-system problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the distinction behind the Control Plane.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>programming</category>
    </item>
    <item>
      <title>Polymarket Position Reconciliation: Keeping Trading Bot State in Sync</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Mon, 28 Sep 2026 05:51:27 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-position-reconciliation-keeping-trading-bot-state-in-sync-1n6f</link>
      <guid>https://dev.to/casatrick/polymarket-position-reconciliation-keeping-trading-bot-state-in-sync-1n6f</guid>
      <description>&lt;p&gt;Your Polymarket trading bot says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position: +100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the account actually holds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position: +40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which one should the trading system trust?&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;position reconciliation&lt;/strong&gt; problem.&lt;/p&gt;

&lt;p&gt;For an automated &lt;a href="https://github.com/casatrickdev/polymarket-trading-bot" rel="noopener noreferrer"&gt;Polymarket trading bot&lt;/a&gt;, local state can become different from actual account state after:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;partial fills&lt;/li&gt;
&lt;li&gt;WebSocket disconnects&lt;/li&gt;
&lt;li&gt;missed events&lt;/li&gt;
&lt;li&gt;application restarts&lt;/li&gt;
&lt;li&gt;delayed events&lt;/li&gt;
&lt;li&gt;unexpected API responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My approach is to treat reconciliation as a first-class part of the trading system rather than something added after execution fails.&lt;/p&gt;

&lt;p&gt;The basic idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local State
     ↓
Remote State
     ↓
Compare
     ↓
Repair
     ↓
Verified State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the core ideas behind the &lt;strong&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-control-plane[](url)" rel="noopener noreferrer"&gt;Polymarket Trading Control Plane&lt;/a&gt;&lt;/strong&gt; I'm building.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why position state can become wrong
&lt;/h2&gt;

&lt;p&gt;A simple trading bot often assumes that every event arrives correctly and that its local state always represents the account.&lt;/p&gt;

&lt;p&gt;In reality, there are several places where state can diverge.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnect
        ↓
Execution event missed
        ↓
Local state not updated
        ↓
Position becomes stale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system may still be running.&lt;/p&gt;

&lt;p&gt;The process may still be healthy.&lt;/p&gt;

&lt;p&gt;The strategy may still be producing signals.&lt;/p&gt;

&lt;p&gt;But its understanding of the current position can already be wrong.&lt;/p&gt;

&lt;p&gt;The Control Plane is designed around making those conditions visible and controllable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local position vs actual position
&lt;/h2&gt;

&lt;p&gt;Consider this simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local position:
+100

Remote position:
+40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the strategy uses the local value for its next decision, it is now calculating from incorrect state.&lt;/p&gt;

&lt;p&gt;That can affect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position sizing
Exposure
Hedging
Risk limits
Available capital
Next orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the trading system needs to know not just:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What does my local state say?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;but:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Does my local state agree with the account?”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Position reconciliation is not just a database comparison
&lt;/h2&gt;

&lt;p&gt;It is tempting to think of reconciliation as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT local_position
SELECT remote_position

if different:
    update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that a trading system has multiple related state domains.&lt;/p&gt;

&lt;p&gt;A useful model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders
   ↓
Trades / Fills
   ↓
Positions
   ↓
Exposure
   ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Control Plane explicitly keeps these concepts separate.&lt;/p&gt;

&lt;p&gt;An order is what the strategy requested.&lt;/p&gt;

&lt;p&gt;A fill is what actually executed.&lt;/p&gt;

&lt;p&gt;A position is what the account holds.&lt;/p&gt;

&lt;p&gt;Exposure is the risk created by that position.&lt;/p&gt;

&lt;p&gt;If one of those layers is wrong, the downstream state can also become wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real problem: events can be missed
&lt;/h2&gt;

&lt;p&gt;Real-time event streams are useful because automated trading systems need low-latency updates.&lt;/p&gt;

&lt;p&gt;But an event-driven system cannot assume that every event will always arrive exactly when expected.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order submitted
      ↓
Partial fill
      ↓
WebSocket disconnect
      ↓
Fill event missed
      ↓
WebSocket reconnect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The connection is back.&lt;/p&gt;

&lt;p&gt;But the local position may still be wrong.&lt;/p&gt;

&lt;p&gt;The system should not treat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECONNECTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STATE VERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are different conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconnect is not reconciliation
&lt;/h2&gt;

&lt;p&gt;This distinction is central to the architecture.&lt;/p&gt;

&lt;p&gt;A reconnect answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Can I connect again?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A reconciliation answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Does my local state match the current remote state?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The recovery flow should therefore look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnect
        ↓
Trading = PAUSED
        ↓
Reconnect
        ↓
Read remote state
        ↓
Compare local state
        ↓
Repair discrepancies
        ↓
Verify position
        ↓
Verify risk
        ↓
Resume trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Control Plane's current recovery model explicitly includes reconnects, restarts, suspected event gaps, unexpected API responses, and state mismatches as reconciliation triggers.&lt;/p&gt;




&lt;h2&gt;
  
  
  A concrete position mismatch
&lt;/h2&gt;

&lt;p&gt;Suppose the local system contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market: BTC Up/Down

Local position:
+100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remote account reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Remote position:
+40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reconciliation report could become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSITION MISMATCH

Market: BTC Up/Down

Local:
+100

Remote:
+40

Difference:
-60

Status:
REQUIRES_RECONCILIATION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, I don't want the strategy to continue creating more positions.&lt;/p&gt;

&lt;p&gt;The system first needs to establish a verified state.&lt;/p&gt;

&lt;p&gt;That is the purpose of the reconciliation layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  What should trigger reconciliation?
&lt;/h2&gt;

&lt;p&gt;A trading system doesn't need to reconcile only after something obviously breaks.&lt;/p&gt;

&lt;p&gt;Useful triggers include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket reconnect
Application restart
Suspected event gap
Unexpected API response
State mismatch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are already part of the Control Plane's planned reconciliation model.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal operation
      ↓
Real-time events
      ↓
Local state

Failure / uncertainty
      ↓
Reconciliation
      ↓
Verified state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives the system both speed and a recovery mechanism.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconciliation should compare more than positions
&lt;/h2&gt;

&lt;p&gt;Suppose the position is correct, but an order is still unexpectedly open.&lt;/p&gt;

&lt;p&gt;Or the fills are correct, but the local exposure calculation is wrong.&lt;/p&gt;

&lt;p&gt;So a reconciliation process should eventually compare multiple dimensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders
  ↓
Fills
  ↓
Positions
  ↓
Exposure
  ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why the Control Plane's state model includes orders, trades, positions, exposure, PnL, and health as separate observable areas.&lt;/p&gt;

&lt;p&gt;The goal is not merely:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Position matches.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“The trading state is internally consistent and verified.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What should happen during a mismatch?
&lt;/h2&gt;

&lt;p&gt;I would not allow the system to continue trading normally while critical state is unresolved.&lt;/p&gt;

&lt;p&gt;A simplified control flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position mismatch
       ↓
Trading = PAUSED
       ↓
Reconcile
       ↓
Repair
       ↓
Verify
       ↓
Risk check
       ↓
Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is consistent with the broader control-plane principle of making critical state visible and controllable before allowing additional risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why pausing matters
&lt;/h2&gt;

&lt;p&gt;Imagine the system believes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position = +40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the real position is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position = +100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy might see enough available capacity to add another 40.&lt;/p&gt;

&lt;p&gt;The real account would then become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+140
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;even though the trading system thought it would become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A state mismatch therefore isn't only a display problem.&lt;/p&gt;

&lt;p&gt;It can become a &lt;strong&gt;risk problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's why stale or inconsistent state should be able to block new trading.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position reconciliation after a partial fill
&lt;/h2&gt;

&lt;p&gt;Partial fills are a good example of why reconciliation matters.&lt;/p&gt;

&lt;p&gt;Suppose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested:
100

Filled:
40

Remaining:
60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy intended 100.&lt;/p&gt;

&lt;p&gt;The execution layer actually filled 40.&lt;/p&gt;

&lt;p&gt;The remote position should reflect the actual fill, not the original intention.&lt;/p&gt;

&lt;p&gt;The state flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
 ↓
40 Fill
 ↓
Position +40
 ↓
Exposure calculation
 ↓
Risk check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the local system instead assumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position +100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then every downstream control is working from the wrong state.&lt;/p&gt;

&lt;p&gt;This is why yesterday's &lt;strong&gt;partial-fill&lt;/strong&gt; work connects directly to today's reconciliation problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconciliation after an application restart
&lt;/h2&gt;

&lt;p&gt;Another common case is restarting the trading process.&lt;/p&gt;

&lt;p&gt;Before the restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local State
Orders:     ...
Positions:  ...
Exposure:   ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After restarting, the application may only know what was persisted locally.&lt;/p&gt;

&lt;p&gt;It should not blindly assume that the persisted state is still current.&lt;/p&gt;

&lt;p&gt;A safer startup sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application starts
        ↓
Load local state
        ↓
Read remote state
        ↓
Compare
        ↓
Repair discrepancies
        ↓
Verify risk
        ↓
Allow trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes startup a recovery operation rather than simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The process has started, so trading can start.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Reconciliation after a WebSocket gap
&lt;/h2&gt;

&lt;p&gt;Another example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket
   ↓
Event A
   ↓
Disconnect
   ↓
Events B, C, D
   ↓
Reconnect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application may know about A but not B, C, or D.&lt;/p&gt;

&lt;p&gt;The system should have a way to establish the current state after reconnecting.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last known state
       ↓
Reconnect
       ↓
Remote state
       ↓
Compare
       ↓
Repair
       ↓
Verified state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one reason the project treats real-time events and reconciliation as complementary mechanisms rather than alternatives.&lt;/p&gt;




&lt;h2&gt;
  
  
  A state machine makes this easier to reason about
&lt;/h2&gt;

&lt;p&gt;One useful way to express the operational state is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEALTHY
    ↓
DEGRADED
    ↓
PAUSED
    ↓
RECOVERING
    ↓
HEALTHY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnect
        ↓
HEALTHY → DEGRADED
        ↓
Trading → PAUSED
        ↓
Reconnect
        ↓
Reconcile
        ↓
Verify
        ↓
RECOVERING
        ↓
HEALTHY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Control Plane currently defines explicit health states including &lt;code&gt;HEALTHY&lt;/code&gt;, &lt;code&gt;DEGRADED&lt;/code&gt;, &lt;code&gt;PAUSED&lt;/code&gt;, &lt;code&gt;RECOVERING&lt;/code&gt;, and &lt;code&gt;FAILED&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconciliation should be observable
&lt;/h2&gt;

&lt;p&gt;A good reconciliation process should not silently modify state.&lt;/p&gt;

&lt;p&gt;It should produce observable events.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECONCILIATION_STARTED
RECONCILIATION_COMPLETED
STATE_MISMATCH
TRADING_PAUSED
TRADING_RESUMED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The broader Control Plane event model also includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER_FAILURE
PARTIAL_FILL
MARKET_DATA_STALE
WEBSOCKET_DISCONNECTED
RISK_LIMIT_BREACHED
KILL_SWITCH_TRIGGERED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These events can feed dashboards, logs, alerts, and webhooks.&lt;/p&gt;

&lt;p&gt;That makes reconciliation part of the system's operational history rather than a hidden database operation.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happens when state cannot be verified?
&lt;/h2&gt;

&lt;p&gt;This is an important case.&lt;/p&gt;

&lt;p&gt;Sometimes reconciliation cannot immediately determine the correct state.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local:
+100

Remote:
unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system should be able to represent uncertainty.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REMOTE = LOCAL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it can move into a controlled state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
    ↓
PAUSED
    ↓
RETRY / RECONCILE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Unverified state should not be treated as verified state.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is especially important for systems that can place additional orders automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  The control plane around the trading bot
&lt;/h2&gt;

&lt;p&gt;This is where the broader architecture comes together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 POLYMARKET
                      │
                      ▼
               Trading Bot
                      │
                Strategy
                      │
                  Execution
                      │
                      ▼
              Execution State
                      │
                      ▼
             ┌─────────────────┐
             │  CONTROL PLANE  │
             │                 │
             │ State           │
             │ Risk            │
             │ Health          │
             │ Reconciliation  │
             │ Alerts          │
             │ Recovery        │
             └────────┬────────┘
                      │
                      ▼
                Verified State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Control Plane is not another trading strategy.&lt;/p&gt;

&lt;p&gt;It is the operational layer around the trading system.&lt;/p&gt;

&lt;p&gt;That is the purpose of the project.&lt;/p&gt;




&lt;h2&gt;
  
  
  A practical reconciliation checklist
&lt;/h2&gt;

&lt;p&gt;Before allowing a trading bot to resume, the system should eventually be able to answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[✓] Market data is fresh
[✓] WebSocket is healthy
[✓] Orders are known
[✓] Fills are known
[✓] Positions match
[✓] Exposure is correct
[✓] Risk limits are satisfied
[✓] No unresolved critical mismatch
[✓] Trading can resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns recovery from a vague concept into a sequence of verifiable conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing position reconciliation
&lt;/h2&gt;

&lt;p&gt;I would test reconciliation against explicit failure scenarios:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Expected behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Position matches&lt;/td&gt;
&lt;td&gt;Continue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Position mismatch&lt;/td&gt;
&lt;td&gt;Pause and reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missed event&lt;/td&gt;
&lt;td&gt;Detect and reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate event&lt;/td&gt;
&lt;td&gt;Avoid double-counting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial fill&lt;/td&gt;
&lt;td&gt;Update actual quantity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown order state&lt;/td&gt;
&lt;td&gt;Reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket disconnect&lt;/td&gt;
&lt;td&gt;Degrade and recover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application restart&lt;/td&gt;
&lt;td&gt;Reload and reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale data&lt;/td&gt;
&lt;td&gt;Pause affected trading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk breach&lt;/td&gt;
&lt;td&gt;Pause or kill&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Control Plane's planned failure scenarios already follow this kind of model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;A trading bot should not be considered healthy just because:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process = running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A more useful definition is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process
   +
Connections
   +
State
   +
Risk
   +
Execution
   +
Verified Position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system needs to know whether those pieces are consistent.&lt;/p&gt;

&lt;p&gt;That is what makes &lt;strong&gt;position reconciliation&lt;/strong&gt; an important part of automated trading infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;The question isn't only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Is my bot running?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Does my bot know what the account actually looks like?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A local position can become stale.&lt;/p&gt;

&lt;p&gt;A WebSocket can disconnect.&lt;/p&gt;

&lt;p&gt;An event can be missed.&lt;/p&gt;

&lt;p&gt;An order can partially fill.&lt;/p&gt;

&lt;p&gt;An application can restart.&lt;/p&gt;

&lt;p&gt;When those things happen, the trading system needs a reliable path back to a verified state.&lt;/p&gt;

&lt;p&gt;For me, that path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local State
    ↓
Remote State
    ↓
Compare
    ↓
Repair
    ↓
Verify
    ↓
Risk Check
    ↓
Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the core idea behind the &lt;strong&gt;Polymarket Trading Control Plane&lt;/strong&gt; I'm building.&lt;/p&gt;

&lt;p&gt;Not another strategy.&lt;/p&gt;

&lt;p&gt;An operational layer that helps an automated Polymarket trading system know when its state can be trusted—and when it should stop trading until it can.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>programming</category>
    </item>
    <item>
      <title>Polymarket Partial Fills: How Automated Trading Bots Should Handle Them</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Sun, 27 Sep 2026 05:10:38 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-partial-fills-how-automated-trading-bots-should-handle-them-5b50</link>
      <guid>https://dev.to/casatrick/polymarket-partial-fills-how-automated-trading-bots-should-handle-them-5b50</guid>
      <description>&lt;p&gt;An automated Polymarket trading bot requests 100.&lt;/p&gt;

&lt;p&gt;Only 40 actually fill.&lt;/p&gt;

&lt;p&gt;What should happen next?&lt;/p&gt;

&lt;p&gt;A simple bot might treat the order as completed.&lt;/p&gt;

&lt;p&gt;A production-oriented trading system cannot.&lt;/p&gt;

&lt;p&gt;The system now has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested: 100
Filled:     40
Remaining:  60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That changes the position, the exposure, the hedge, the next trade, and potentially the entire risk state.&lt;/p&gt;

&lt;p&gt;This is why I think &lt;strong&gt;partial fills are a state-management problem, not just an order-management problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk through how I think about partial fills in an automated Polymarket trading system and why execution verification and reconciliation need to be separate parts of the architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is a partial fill?
&lt;/h2&gt;

&lt;p&gt;A partial fill occurs when an order is executed for only part of its requested quantity.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested quantity: 100
Executed quantity:   40
Remaining quantity:  60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy wanted 100.&lt;/p&gt;

&lt;p&gt;The market gave the system 40.&lt;/p&gt;

&lt;p&gt;Those are two different facts.&lt;/p&gt;

&lt;p&gt;A trading bot that continues to reason about the original 100 as though it were executed is now operating with incorrect state.&lt;/p&gt;

&lt;p&gt;The first rule is therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Requested quantity and executed quantity must be tracked separately.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why partial fills are dangerous for trading bots
&lt;/h2&gt;

&lt;p&gt;Imagine a strategy generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUY 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution layer submits the order.&lt;/p&gt;

&lt;p&gt;The market only fills:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the system has to answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the remaining 60 still resting?&lt;/li&gt;
&lt;li&gt;Should the order be cancelled?&lt;/li&gt;
&lt;li&gt;Should the remaining quantity be re-priced?&lt;/li&gt;
&lt;li&gt;Is the position supposed to be hedged?&lt;/li&gt;
&lt;li&gt;How much exposure actually exists?&lt;/li&gt;
&lt;li&gt;Does the strategy still want the trade?&lt;/li&gt;
&lt;li&gt;What happens if the remaining order never fills?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of those questions can be answered correctly if the system stores only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order.quantity = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual execution state needs to be explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  The execution state should be more than FILLED / NOT FILLED
&lt;/h2&gt;

&lt;p&gt;A common simplification is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED
    ↓
FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real execution is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
    ↓
SUBMITTED
    ↓
ACCEPTED
    ↓
PARTIALLY_FILLED
    ↓
FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there are other outcomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REJECTED
CANCELLED
FAILED
RETRYING
UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FILLED
    ↓
TRANSACTION
    ↓
CONFIRMED
    ↓
SETTLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is why I prefer thinking about execution as a state machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
   ↓
SUBMITTED
   ↓
ACCEPTED
   ↓
MATCHED
   ↓
PARTIALLY FILLED
   ↓
FILLED
   ↓
TX_PENDING
   ↓
CONFIRMED
   ↓
SETTLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact state model will depend on the trading system, but the important idea is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The trading system should represent what actually happened.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Partial fills change your risk
&lt;/h2&gt;

&lt;p&gt;Consider a hedged trade.&lt;/p&gt;

&lt;p&gt;Suppose the strategy expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Leg 1: 100
Leg 2: 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Leg 1 only fills 40.&lt;/p&gt;

&lt;p&gt;The system does not have a 100-unit exposure on Leg 1.&lt;/p&gt;

&lt;p&gt;It has 40.&lt;/p&gt;

&lt;p&gt;That means a second leg sized for 100 could create an unintended position.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested Leg 1
       ↓
     100
       ↓
Actual Fill
       ↓
      40
       ↓
Hedge calculation
       ↓
      40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why hedge logic should be based on &lt;strong&gt;actual execution&lt;/strong&gt;, not intended execution.&lt;/p&gt;

&lt;p&gt;The distinction is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested size ≠ Filled size
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Filled size → actual exposure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The bot needs to track residual quantity
&lt;/h2&gt;

&lt;p&gt;After a partial fill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested: 100
Filled:     40
Remaining:  60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remaining quantity should be explicit.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remaining = requested - filled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the trading system also needs to know what the remaining quantity &lt;em&gt;means&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;60 resting on the book
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;60 cancelled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;60 unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;60 requiring a retry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quantity alone is not enough.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;state of the remaining quantity&lt;/strong&gt; matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happens when the second leg doesn't fill?
&lt;/h2&gt;

&lt;p&gt;This is where partial fills become particularly important for arbitrage and hedged strategies.&lt;/p&gt;

&lt;p&gt;Suppose the intended trade is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Leg 1 → 100
Leg 2 → 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Leg 1 → 100 filled
Leg 2 →   0 filled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system now owns an unintended exposure.&lt;/p&gt;

&lt;p&gt;A naive implementation might simply retry Leg 2.&lt;/p&gt;

&lt;p&gt;That can be dangerous if the market moved.&lt;/p&gt;

&lt;p&gt;The price may no longer support the original trade.&lt;/p&gt;

&lt;p&gt;A more deliberate execution flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Leg 1 executed
      ↓
Measure actual fill
      ↓
Check current market
      ↓
Check risk
      ↓
Attempt hedge
      ↓
Verify hedge
      ↓
Reconcile final position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system should be able to choose among actions such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HEDGE
REPRICE
CANCEL
UNWIND
PAUSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct action depends on the strategy and risk policy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Partial fills and position reconciliation
&lt;/h2&gt;

&lt;p&gt;This is where execution state meets account state.&lt;/p&gt;

&lt;p&gt;Suppose your local system says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected position: +100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But execution records say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Filled: +40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the account currently reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Actual position: +40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system is consistent.&lt;/p&gt;

&lt;p&gt;Now consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected position: +100
Filled: +40
Actual position: +25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a different problem.&lt;/p&gt;

&lt;p&gt;Something else happened between execution and account state.&lt;/p&gt;

&lt;p&gt;Now the trading system needs reconciliation.&lt;/p&gt;

&lt;p&gt;A useful architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              ORDER
                ↓
             FILL(S)
                ↓
          Local Position
                ↓
        Position Reconciler
                ↓
          Remote Position
                ↓
             Verified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point is not to assume that one event tells you the whole story.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why WebSocket events aren't enough
&lt;/h2&gt;

&lt;p&gt;Real-time events are essential for low-latency systems.&lt;/p&gt;

&lt;p&gt;But a trading bot should not assume that every event will arrive exactly once and in perfect order.&lt;/p&gt;

&lt;p&gt;Possible problems include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;disconnects&lt;/li&gt;
&lt;li&gt;missed events&lt;/li&gt;
&lt;li&gt;duplicated events&lt;/li&gt;
&lt;li&gt;delayed events&lt;/li&gt;
&lt;li&gt;application restart&lt;/li&gt;
&lt;li&gt;API inconsistencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the system needs two complementary mechanisms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Real-time updates
       +
Reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real-time events keep the system fast.&lt;/p&gt;

&lt;p&gt;Reconciliation helps it recover when reality and local state diverge.&lt;/p&gt;




&lt;h2&gt;
  
  
  A safer recovery sequence
&lt;/h2&gt;

&lt;p&gt;Imagine a partial-fill event happens during a WebSocket disconnect.&lt;/p&gt;

&lt;p&gt;The bot reconnects.&lt;/p&gt;

&lt;p&gt;It should not automatically decide:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Everything is fine. Continue trading.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A safer flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket disconnect
        ↓
Trading = PAUSED
        ↓
Reconnect
        ↓
Rebuild / reload local state
        ↓
Reconcile orders
        ↓
Reconcile fills
        ↓
Reconcile positions
        ↓
Verify exposure
        ↓
Verify risk
        ↓
Resume trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key distinction is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Reconnect is a network operation. Recovery is a trading-system operation.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Unknown execution state needs its own path
&lt;/h2&gt;

&lt;p&gt;One of the most dangerous states is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose the application submits an order.&lt;/p&gt;

&lt;p&gt;The network fails before the application receives the final response.&lt;/p&gt;

&lt;p&gt;Now the system does not know whether the order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;was rejected&lt;/li&gt;
&lt;li&gt;was accepted&lt;/li&gt;
&lt;li&gt;partially filled&lt;/li&gt;
&lt;li&gt;completely filled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Immediately submitting the same order again could create duplicate exposure.&lt;/p&gt;

&lt;p&gt;So instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNKNOWN
   ↓
Do not blindly retry
   ↓
Query / reconcile
   ↓
Determine actual state
   ↓
Continue according to policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is another reason I prefer an explicit execution-verification layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution verification
&lt;/h2&gt;

&lt;p&gt;I’ve been building a separate &lt;strong&gt;Polymarket Execution Verifier&lt;/strong&gt; around this problem.&lt;/p&gt;

&lt;p&gt;The idea is to verify execution across multiple layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
  ↓
Fill
  ↓
Transaction
  ↓
Settlement
  ↓
Position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of allowing the trading bot to assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER = DONE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the verifier asks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What actually happened?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible results include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KNOWN
UNKNOWN
FAILED
SETTLED
INCONSISTENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes execution verification an explicit component rather than something hidden inside a strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Partial fills in a control-plane architecture
&lt;/h2&gt;

&lt;p&gt;I think of the broader system like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 POLYMARKET
                      │
                 Market Data
                      │
                      ▼
              ┌───────────────┐
              │ Trading Bot   │
              │               │
              │ Strategy      │
              │ Execution     │
              └───────┬───────┘
                      │
                      ▼
             Execution Verifier
                      │
            ┌─────────┴─────────┐
            ▼                   ▼
        Fill State          TX State
            │                   │
            └─────────┬─────────┘
                      ▼
                 Position
                      │
                      ▼
               Control Plane
                      │
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
      Risk         Health      Reconciliation
        │             │             │
        └─────────────┼─────────────┘
                      ▼
                   Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to separate responsibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy
&lt;/h3&gt;

&lt;p&gt;Decides what it wants to trade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution
&lt;/h3&gt;

&lt;p&gt;Attempts to place the order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution verifier
&lt;/h3&gt;

&lt;p&gt;Determines what actually happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control plane
&lt;/h3&gt;

&lt;p&gt;Determines whether the system should continue operating.&lt;/p&gt;

&lt;p&gt;That separation becomes increasingly valuable as the trading system grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  The most important invariant
&lt;/h2&gt;

&lt;p&gt;For me, the key invariant is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Actual Position
    =
Verified Executions
    =
Risk Calculation Input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At least, that should be the goal.&lt;/p&gt;

&lt;p&gt;If those three things disagree, the system should stop treating its local state as trustworthy.&lt;/p&gt;

&lt;p&gt;That leads to a simple operational rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When critical state is uncertain, reduce or stop new risk until state is verified.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Testing partial fills
&lt;/h1&gt;

&lt;p&gt;Partial-fill handling should be tested explicitly.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Expected behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full fill&lt;/td&gt;
&lt;td&gt;Mark execution complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial fill&lt;/td&gt;
&lt;td&gt;Track actual filled quantity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remaining order open&lt;/td&gt;
&lt;td&gt;Track residual quantity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remaining order cancelled&lt;/td&gt;
&lt;td&gt;Recalculate state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial hedge&lt;/td&gt;
&lt;td&gt;Recalculate exposure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown execution&lt;/td&gt;
&lt;td&gt;Reconcile before retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate event&lt;/td&gt;
&lt;td&gt;Avoid double-counting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket reconnect&lt;/td&gt;
&lt;td&gt;Reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Position mismatch&lt;/td&gt;
&lt;td&gt;Block trading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settlement mismatch&lt;/td&gt;
&lt;td&gt;Escalate / reconcile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The happy path is easy.&lt;/p&gt;

&lt;p&gt;The failure paths are where the trading system needs engineering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building around the strategy
&lt;/h2&gt;

&lt;p&gt;This is why I've become increasingly interested in the infrastructure around automated Polymarket trading.&lt;/p&gt;

&lt;p&gt;A strategy can answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What should I trade?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But that doesn't answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How much actually filled?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What position do I really have?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is it safe to place another order?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those questions belong to the execution, state, risk, and control layers.&lt;/p&gt;

&lt;p&gt;The architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strategy
   ↓
Execution
   ↓
Verification
   ↓
State
   ↓
Risk
   ↓
Monitoring
   ↓
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the direction I'm exploring with the Polymarket Trading Bot, Execution Verifier, and Trading Control Plane projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related projects
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Polymarket Trading Bot
&lt;/h3&gt;

&lt;p&gt;Automated trading, execution, risk management, and backtesting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-bot" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-bot&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Polymarket Execution Verifier
&lt;/h3&gt;

&lt;p&gt;Verify execution across orders, fills, transactions, settlement, and position state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-execution-verifier" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-execution-verifier&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Polymarket Trading Control Plane
&lt;/h3&gt;

&lt;p&gt;Monitoring, state reconciliation, risk controls, health, alerts, and recovery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-control-plane" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-control-plane&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trading Bot
    ↓
Execution
    ↓
Partial Fill Handling
    ↓
Execution Verification
    ↓
State Reconciliation
    ↓
Risk / Monitoring
    ↓
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A partial fill looks like a small execution detail.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;Once an automated trading system starts managing real positions, a partial fill changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;actual position&lt;/li&gt;
&lt;li&gt;remaining quantity&lt;/li&gt;
&lt;li&gt;exposure&lt;/li&gt;
&lt;li&gt;hedge requirements&lt;/li&gt;
&lt;li&gt;risk&lt;/li&gt;
&lt;li&gt;execution state&lt;/li&gt;
&lt;li&gt;what the strategy should do next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key is not to make the system assume that an intended trade became a completed trade.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intent
  ↓
Execution
  ↓
Actual Fill
  ↓
Verified State
  ↓
Position
  ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A trading bot becomes much easier to reason about when it knows the difference between &lt;strong&gt;what it wanted&lt;/strong&gt;, &lt;strong&gt;what it executed&lt;/strong&gt;, and &lt;strong&gt;what it actually owns&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the problem I'm exploring with execution verification and trading-system infrastructure for Polymarket.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>programming</category>
    </item>
    <item>
      <title>Polymarket Trading Bot: Execution, Risk Management, and Backtesting</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Fri, 25 Sep 2026 05:42:26 +0000</pubDate>
      <link>https://dev.to/casatrick/polymarket-trading-bot-execution-risk-management-and-backtesting-1nn5</link>
      <guid>https://dev.to/casatrick/polymarket-trading-bot-execution-risk-management-and-backtesting-1nn5</guid>
      <description>&lt;p&gt;Building a &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; is not only about finding a strategy that produces profitable signals.&lt;/p&gt;

&lt;p&gt;The harder part starts after the signal.&lt;/p&gt;

&lt;p&gt;A real automated trading system has to deal with changing order-book prices, partial fills, execution costs, open exposure, failed trades, stale data, repeated losses, gas availability, and the difference between what the bot &lt;em&gt;thinks&lt;/em&gt; happened and what actually happened.&lt;/p&gt;

&lt;p&gt;That is the problem I wanted to work on with &lt;strong&gt;Polymarket Trading Bot v3.2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The project is an open-source automated trading system for Polymarket with strategy execution, risk management, execution protection, monitoring, and backtesting.&lt;/p&gt;

&lt;p&gt;GitHub:&lt;br&gt;
&lt;a href="https://github.com/casatrickdev/polymarket-trading-bot" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-bot&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  A Polymarket bot needs more than a trading strategy
&lt;/h2&gt;

&lt;p&gt;A simple trading bot can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market Data
    ↓
Strategy
    ↓
Buy / Sell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is enough for a prototype.&lt;/p&gt;

&lt;p&gt;A more complete automated trading system looks closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Market Data
     ↓
Strategy Signal
     ↓
Risk Checks
     ↓
Position Sizing
     ↓
Protected Order
     ↓
Execution
     ↓
Position / Exposure Tracking
     ↓
Monitoring
     ↓
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is important.&lt;/p&gt;

&lt;p&gt;A strategy can be correct while the actual trade still goes wrong.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the price changes between signal generation and execution&lt;/li&gt;
&lt;li&gt;an arbitrage leg fills only partially&lt;/li&gt;
&lt;li&gt;fees eliminate the expected edge&lt;/li&gt;
&lt;li&gt;exposure becomes larger than intended&lt;/li&gt;
&lt;li&gt;a copied trade is already stale&lt;/li&gt;
&lt;li&gt;the wallet does not have enough gas&lt;/li&gt;
&lt;li&gt;the system continues trading after a sequence of losses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the bot has to treat &lt;strong&gt;execution and risk as part of the trading system&lt;/strong&gt;, not as optional additions.&lt;/p&gt;




&lt;h2&gt;
  
  
  What changed in v3.2?
&lt;/h2&gt;

&lt;p&gt;The latest version focuses heavily on execution safety and risk controls.&lt;/p&gt;

&lt;p&gt;The release includes fee-aware profit checks, price-protected orders, sequential arbitrage execution, improved hedging, stale-trade filtering, exposure caps, a wallet circuit breaker, backtesting, Polygon gas monitoring, and loss-streak protection.&lt;/p&gt;

&lt;p&gt;The result is a system that tries to answer a bigger question than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Should I trade?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It also asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Can I safely execute this trade right now?”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. Fee-aware Polymarket trading
&lt;/h2&gt;

&lt;p&gt;A price difference is not automatically a profitable trade.&lt;/p&gt;

&lt;p&gt;Execution costs matter.&lt;/p&gt;

&lt;p&gt;The bot now calculates arbitrage and DipArb opportunities while accounting for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;taker fees&lt;/li&gt;
&lt;li&gt;gas costs&lt;/li&gt;
&lt;li&gt;minimum net-profit requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means a signal can be rejected even when the gross price relationship looks attractive.&lt;/p&gt;

&lt;p&gt;That is an important distinction for automated trading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gross Opportunity
       ↓
- Trading Fees
- Gas
       ↓
Net Opportunity
       ↓
Profit Threshold
       ↓
Execute / Skip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy is therefore evaluated against the cost of actually trading rather than only the theoretical price difference.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Price-protected orders
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways for an automated trading system to behave unexpectedly is to submit an order without sufficient price protection.&lt;/p&gt;

&lt;p&gt;Markets move.&lt;/p&gt;

&lt;p&gt;The bot now derives worst-price caps or floors from the live order book before sending market orders.&lt;/p&gt;

&lt;p&gt;The idea is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Live Order Book
      ↓
Current execution conditions
      ↓
Worst acceptable price
      ↓
Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of treating the market price as static, the execution layer explicitly accounts for the price at which the system is still willing to trade.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Sequential arbitrage execution
&lt;/h2&gt;

&lt;p&gt;Arbitrage introduces another problem.&lt;/p&gt;

&lt;p&gt;Suppose the system wants to buy both YES and NO.&lt;/p&gt;

&lt;p&gt;A naive implementation might submit both orders at approximately the same time.&lt;/p&gt;

&lt;p&gt;That creates a race.&lt;/p&gt;

&lt;p&gt;What happens if one order fills and the other does not?&lt;/p&gt;

&lt;p&gt;Now the bot has exposure that was not part of the intended position.&lt;/p&gt;

&lt;p&gt;In v3.2, the arbitrage legs execute sequentially, with unwind and reconciliation logic around partial fills.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;YES / Leg 1
    ↓
Actual Fill
    ↓
Validate
    ↓
NO / Leg 2
    ↓
Actual Fill
    ↓
Reconcile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system is no longer treating the two orders as if they were guaranteed to fill together.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Handling partial fills
&lt;/h2&gt;

&lt;p&gt;Partial fills are one of those problems that look small in a prototype and become important in an automated system.&lt;/p&gt;

&lt;p&gt;The intended position might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 shares
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But actual execution could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;60 filled
40 remaining
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means the system needs to know what it actually owns rather than what it originally requested.&lt;/p&gt;

&lt;p&gt;The bot's execution logic therefore works around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;actual fills&lt;/li&gt;
&lt;li&gt;incomplete legs&lt;/li&gt;
&lt;li&gt;residual exposure&lt;/li&gt;
&lt;li&gt;reconciliation&lt;/li&gt;
&lt;li&gt;unwind behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one reason execution state needs to be treated separately from strategy state.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. DipArb execution protection
&lt;/h2&gt;

&lt;p&gt;The DipArb strategy watches short-duration crypto markets for rapid price movements.&lt;/p&gt;

&lt;p&gt;The current implementation includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BTC and ETH 15-minute markets&lt;/li&gt;
&lt;li&gt;rapid price-move detection&lt;/li&gt;
&lt;li&gt;a first-leg entry&lt;/li&gt;
&lt;li&gt;an opposite-side hedge&lt;/li&gt;
&lt;li&gt;shorter hedge timeout&lt;/li&gt;
&lt;li&gt;stop-loss behavior&lt;/li&gt;
&lt;li&gt;1:1 hedge requirements based on actual fills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important engineering point is that the hedge is not based only on the intended order quantity.&lt;/p&gt;

&lt;p&gt;It is tied to what actually filled.&lt;/p&gt;

&lt;p&gt;That matters because:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested quantity ≠ Actual quantity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in a real execution environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Filtering stale copy-trading signals
&lt;/h2&gt;

&lt;p&gt;Copy trading creates another timing problem.&lt;/p&gt;

&lt;p&gt;A profitable-looking wallet transaction may already be several seconds old by the time the bot sees it.&lt;/p&gt;

&lt;p&gt;v3.2 skips stale whale prints and re-quotes the entry against the current market, with spread, premium, and liquidity checks.&lt;/p&gt;

&lt;p&gt;The flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Observed Wallet Trade
        ↓
Freshness Check
        ↓
Current Order Book
        ↓
Spread / Liquidity Checks
        ↓
Entry Decision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents the system from blindly treating an old trade as a current market opportunity.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Six-layer risk management
&lt;/h2&gt;

&lt;p&gt;The trading strategy is only one part of capital protection.&lt;/p&gt;

&lt;p&gt;The bot now uses six layers of risk controls:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protection&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Daily loss limit&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly loss limit&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maximum drawdown&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total-loss halt&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loss-streak pause&lt;/td&gt;
&lt;td&gt;6 losses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total exposure cap&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These controls are intended to operate at different levels.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trade
  ↓
Position Limit
  ↓
Market Exposure
  ↓
Loss Streak
  ↓
Daily Loss
  ↓
Drawdown
  ↓
Total Loss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates several opportunities to stop trading before one failure becomes a much larger problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Dynamic position sizing
&lt;/h2&gt;

&lt;p&gt;Position size is also connected to recent performance.&lt;/p&gt;

&lt;p&gt;The default system starts at 2% of configured capital and changes position size according to consecutive wins or losses, subject to the configured cap.&lt;/p&gt;

&lt;p&gt;The basic concept is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Winning streak
     ↓
Potentially larger size

Losing streak
     ↓
Smaller size
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also a minimum position-size floor so the system does not continuously create tiny orders that are difficult to manage.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Exposure limits
&lt;/h2&gt;

&lt;p&gt;Another distinction between a simple bot and a larger trading system is &lt;strong&gt;portfolio state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is not enough to know:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I have a new signal.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system also needs to know:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How much capital is already exposed?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;v3.2 tracks total open exposure and per-market exposure and blocks new positions once configured limits are reached.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Capital
   ↓
Open Position A
Open Position B
Open Position C
   ↓
Total Exposure
   ↓
Risk Check
   ↓
Allow / Reject New Trade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents individual signals from being evaluated independently when they are actually part of the same portfolio.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Trading dashboard
&lt;/h2&gt;

&lt;p&gt;The bot includes a dashboard for monitoring the trading system in real time.&lt;/p&gt;

&lt;p&gt;The dashboard exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;live or dry-run mode&lt;/li&gt;
&lt;li&gt;USDC and Polygon balances&lt;/li&gt;
&lt;li&gt;session PnL&lt;/li&gt;
&lt;li&gt;strategy state&lt;/li&gt;
&lt;li&gt;risk limits&lt;/li&gt;
&lt;li&gt;drawdown&lt;/li&gt;
&lt;li&gt;open exposure&lt;/li&gt;
&lt;li&gt;win/loss streak&lt;/li&gt;
&lt;li&gt;halted or paused state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also includes controls such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strategy toggles&lt;/li&gt;
&lt;li&gt;emergency stop&lt;/li&gt;
&lt;li&gt;panic sell&lt;/li&gt;
&lt;li&gt;live / dry-run switching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For automated trading, visibility is part of the system.&lt;/p&gt;

&lt;p&gt;A bot that trades without an operator being able to understand its state is difficult to debug when something goes wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Backtesting the execution layer
&lt;/h2&gt;

&lt;p&gt;The repository also includes a JSONL order-book replay backtesting harness with fee and gas modeling.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run backtest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The purpose is not simply to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Would this strategy have made money?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is also useful for examining how execution logic behaves against recorded market conditions.&lt;/p&gt;

&lt;p&gt;That makes it possible to test things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;execution assumptions&lt;/li&gt;
&lt;li&gt;order-book conditions&lt;/li&gt;
&lt;li&gt;fees&lt;/li&gt;
&lt;li&gt;gas&lt;/li&gt;
&lt;li&gt;strategy behavior&lt;/li&gt;
&lt;li&gt;position sizing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A backtest that ignores execution costs can produce results that are very different from what happens in a live market.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Gas and operational protection
&lt;/h2&gt;

&lt;p&gt;Trading logic can also fail because of infrastructure.&lt;/p&gt;

&lt;p&gt;The bot monitors Polygon gas balance and can pause trading when the balance falls below the configured minimum.&lt;/p&gt;

&lt;p&gt;There is also a wallet circuit breaker for copy trading.&lt;/p&gt;

&lt;p&gt;After repeated failures, a wallet can be disabled for a cooldown period.&lt;/p&gt;

&lt;p&gt;These controls are examples of a broader principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operational failures are trading risks too.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The four strategies
&lt;/h2&gt;

&lt;p&gt;The current bot includes four trading modes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arbitrage
&lt;/h3&gt;

&lt;p&gt;Looks for opportunities where the combined YES and NO prices satisfy the configured arbitrage condition.&lt;/p&gt;

&lt;h3&gt;
  
  
  DipArb
&lt;/h3&gt;

&lt;p&gt;Looks for rapid moves in short-duration BTC and ETH markets and attempts a hedged entry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smart Money
&lt;/h3&gt;

&lt;p&gt;Filters selected Polymarket traders using performance and consistency requirements before copying trades.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct Trading
&lt;/h3&gt;

&lt;p&gt;Provides manual execution tools including FOK orders and quick trading controls.&lt;/p&gt;

&lt;p&gt;The complete strategy configuration is available in the repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Clone the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/casatrickdev/polymarket-trading-bot
&lt;span class="nb"&gt;cd &lt;/span&gt;Polymarket-trading-bot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;dashboard
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run build
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create your environment file from &lt;code&gt;.env.example&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For initial testing, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DRY_RUN=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start the bot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsx bot-with-dashboard.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project documentation contains the full configuration and setup instructions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I built it this way
&lt;/h2&gt;

&lt;p&gt;There is a common tendency when building trading bots to spend most of the engineering effort on the strategy.&lt;/p&gt;

&lt;p&gt;The strategy is the interesting part.&lt;/p&gt;

&lt;p&gt;But once the system is actually running, many of the difficult problems move somewhere else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the order really fill?&lt;/li&gt;
&lt;li&gt;How much actually filled?&lt;/li&gt;
&lt;li&gt;Is the position state correct?&lt;/li&gt;
&lt;li&gt;Is the current market data fresh?&lt;/li&gt;
&lt;li&gt;How much capital is exposed?&lt;/li&gt;
&lt;li&gt;Did the transaction complete?&lt;/li&gt;
&lt;li&gt;Should the system continue trading after repeated failures?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are infrastructure problems.&lt;/p&gt;

&lt;p&gt;That is why this project is increasingly focused on the layer around the strategy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strategy
   +
Execution
   +
Risk
   +
State
   +
Monitoring
   +
Recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For me, that is what makes a &lt;strong&gt;Polymarket trading bot&lt;/strong&gt; an actual trading system rather than just a script that sends orders.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;The complete project is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/casatrickdev/polymarket-trading-bot" rel="noopener noreferrer"&gt;https://github.com/casatrickdev/polymarket-trading-bot&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The repository includes the bot, dashboard, configuration examples, documentation, and backtesting tooling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;A Polymarket bot does not become reliable simply because its strategy works.&lt;/p&gt;

&lt;p&gt;The system also needs to control execution, manage exposure, respond to failures, track actual fills, and know when it should stop trading.&lt;/p&gt;

&lt;p&gt;That is the direction of v3.2:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;less “just place the trade,” more “know exactly what is happening around the trade.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚠️ Trading involves risk. This software does not guarantee profits. Always test carefully and never trade more than you can afford to lose.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>trading</category>
      <category>typescript</category>
      <category>backtesting</category>
    </item>
    <item>
      <title>Building a Polymarket Trading Control Plane: State, Risk, and Recovery</title>
      <dc:creator>Casatrick | Polymrket Bot Dev </dc:creator>
      <pubDate>Wed, 23 Sep 2026 06:17:13 +0000</pubDate>
      <link>https://dev.to/casatrick/building-a-polymarket-trading-control-plane-state-risk-and-recovery-p31</link>
      <guid>https://dev.to/casatrick/building-a-polymarket-trading-control-plane-state-risk-and-recovery-p31</guid>
      <description>&lt;p&gt;A Polymarket trading bot can be connected, receiving market data, submitting orders, and still be unsafe to operate.&lt;/p&gt;

&lt;p&gt;The problem isn't always the strategy.&lt;/p&gt;

&lt;p&gt;It can be the system around the strategy.&lt;/p&gt;

&lt;p&gt;A production trading system needs to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what orders are actually open&lt;/li&gt;
&lt;li&gt;what has actually filled&lt;/li&gt;
&lt;li&gt;what positions are actually held&lt;/li&gt;
&lt;li&gt;whether market data is still fresh&lt;/li&gt;
&lt;li&gt;whether local state matches remote state&lt;/li&gt;
&lt;li&gt;whether risk limits are still satisfied&lt;/li&gt;
&lt;li&gt;whether the system should be allowed to place another order&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That leads to a different way of thinking about a trading bot.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market data
    ↓
strategy
    ↓
  order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want the architecture to look more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market data
    ↓
event processing
    ↓
  state
    ↓
  risk
    ↓
execution
    ↓
reconciliation
    ↓
monitoring
    ↓
recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm building a small &lt;strong&gt;Polymarket Trading Control Plane&lt;/strong&gt; around that idea.&lt;/p&gt;

&lt;p&gt;The goal is not another trading strategy.&lt;/p&gt;

&lt;p&gt;The goal is to build the operational layer that allows a strategy to run with a much clearer understanding of its own state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why build a control plane?
&lt;/h2&gt;

&lt;p&gt;When you're prototyping a trading bot, it's easy to keep everything in one process.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;market&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_market_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fine for exploring an idea.&lt;/p&gt;

&lt;p&gt;But once the system has real orders and positions, the questions become different.&lt;/p&gt;

&lt;p&gt;What happens when the WebSocket disconnects?&lt;/p&gt;

&lt;p&gt;What happens when an order partially fills?&lt;/p&gt;

&lt;p&gt;What happens when the process restarts?&lt;/p&gt;

&lt;p&gt;What happens when a response is delayed?&lt;/p&gt;

&lt;p&gt;What happens when local state says one thing and the remote state says another?&lt;/p&gt;

&lt;p&gt;These are operational problems, not strategy problems.&lt;/p&gt;

&lt;p&gt;And they become more important as the amount of automation increases.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/casatrickdev/polymarket-trading-control-plane" rel="noopener noreferrer"&gt;Polymarket Trading Control Plane&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The first principle: local state is not reality
&lt;/h2&gt;

&lt;p&gt;Suppose a strategy submits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUY 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local application records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order_id = 123
status   = OPEN
size     = 100
filled   = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then 40 contracts fill.&lt;/p&gt;

&lt;p&gt;The real state is now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status    = PARTIALLY_FILLED
filled    = 40
remaining = 60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine the process misses that update.&lt;/p&gt;

&lt;p&gt;The process may still believe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;filled = 0
remaining = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing has necessarily crashed.&lt;/p&gt;

&lt;p&gt;The program is still running.&lt;/p&gt;

&lt;p&gt;The strategy is still calculating.&lt;/p&gt;

&lt;p&gt;The system just has the wrong state.&lt;/p&gt;

&lt;p&gt;That is much more dangerous than a visible exception because the system can continue making decisions from incorrect information.&lt;/p&gt;




&lt;h2&gt;
  
  
  WebSockets are useful, but they are not your entire state model
&lt;/h2&gt;

&lt;p&gt;Polymarket's current Rust CLOB client supports WebSocket streams for orderbooks, prices, and authenticated user order/trade events. The current V2 client also exposes Data and Gamma API integrations and other CLOB functionality.&lt;/p&gt;

&lt;p&gt;That's exactly what a low-latency trading system needs.&lt;/p&gt;

&lt;p&gt;But I don't want my architecture to assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket connected
=
state is correct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are two different conditions.&lt;/p&gt;

&lt;p&gt;I think about the system as having two paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 POLYMARKET
                     │
          ┌──────────┴──────────┐
          │                     │
      WebSocket              API / Reads
          │                     │
          ▼                     ▼
   Event Processor        Reconciliation
          │                     │
          └──────────┬──────────┘
                     ▼
                Local State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first path is optimized for speed.&lt;/p&gt;

&lt;p&gt;The second path is responsible for validating and repairing state.&lt;/p&gt;




&lt;h2&gt;
  
  
  State should be explicit
&lt;/h2&gt;

&lt;p&gt;One of the first design decisions in the control plane is to separate the major concepts.&lt;/p&gt;

&lt;p&gt;I don't want one giant &lt;code&gt;BotState&lt;/code&gt; object that mixes everything together.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders
   ↓
Trades / Fills
   ↓
Positions
   ↓
Exposure
   ↓
Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer answers a different question.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orders
&lt;/h3&gt;

&lt;p&gt;What did the system request?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order_id
market
side
price
requested_size
status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fills
&lt;/h3&gt;

&lt;p&gt;What actually executed?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trade_id
order_id
executed_size
execution_price
timestamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Positions
&lt;/h3&gt;

&lt;p&gt;What do we currently hold?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market
size
average_entry
realized_pnl
unrealized_pnl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Exposure
&lt;/h3&gt;

&lt;p&gt;What risk is currently on the book?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market_exposure
total_exposure
concentration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Risk
&lt;/h3&gt;

&lt;p&gt;Should the system be allowed to place another order?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;allowed
paused
degraded
killed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping these concepts separate makes the system easier to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Model order lifecycles explicitly
&lt;/h2&gt;

&lt;p&gt;Another common mistake is treating an order as simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLOSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real execution is more complicated.&lt;/p&gt;

&lt;p&gt;A simplified lifecycle can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INTENDED
   ↓
SUBMITTED
   ↓
ACCEPTED
   ↓
MATCHED
   ↓
FILLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With other possible paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUBMITTED → REJECTED
SUBMITTED → CANCELLED
MATCHED   → RETRYING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact lifecycle depends on the interface and execution path, but the architecture should allow for intermediate states.&lt;/p&gt;

&lt;p&gt;This is particularly relevant with Polymarket's evolving asynchronous execution model. The current V2 Rust client changelog notes that matched orders can carry &lt;code&gt;tradeIDs&lt;/code&gt; rather than immediately returning transaction hashes, and the client can resolve those hashes afterward.&lt;/p&gt;

&lt;p&gt;That is an important architectural lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An accepted order isn't necessarily the end of the execution lifecycle.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Event processing should be idempotent
&lt;/h2&gt;

&lt;p&gt;Real-time systems eventually see duplicates, reconnects, retries, and restarts.&lt;/p&gt;

&lt;p&gt;So event processing needs to be safe to repeat.&lt;/p&gt;

&lt;p&gt;A simplified version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;already_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&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;apply_transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;mark_processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The production implementation can use a stronger persistence and transaction model, but the principle remains:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Processing the same event twice should not create two executions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This becomes even more important if the application needs to replay events after a restart.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconciliation is the recovery mechanism
&lt;/h2&gt;

&lt;p&gt;The main job of reconciliation is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Compare what the application believes with what the remote system currently reports.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOCAL

Order 123
filled = 0

REMOTE

Order 123
filled = 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reconciler identifies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;difference = 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and creates an explicit discrepancy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STATE_MISMATCH

order_id: 123
local_filled: 0
remote_filled: 40
action: repair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't want this to be a hidden correction somewhere inside the strategy.&lt;/p&gt;

&lt;p&gt;It should be its own subsystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconciliation after a reconnect
&lt;/h2&gt;

&lt;p&gt;A reconnect shouldn't automatically mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connected
→ resume trading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd rather use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disconnect
    ↓
reconnect
    ↓
load persisted state
    ↓
read current remote state
    ↓
compare
    ↓
repair
    ↓
verify risk
    ↓
resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That extra step is where a lot of production safety comes from.&lt;/p&gt;

&lt;p&gt;The same pattern applies after a process restart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process starts
    ↓
load state
    ↓
synchronize
    ↓
reconcile
    ↓
validate
    ↓
trade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recovery becomes part of normal operation instead of an emergency patch.&lt;/p&gt;




&lt;h2&gt;
  
  
  The control plane should be able to stop trading
&lt;/h2&gt;

&lt;p&gt;One of the most useful ideas in this project is that &lt;strong&gt;trading permission should be separate from the strategy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strategy says:
BUY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;doesn't necessarily mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System says:
ALLOW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strategy signal
      ↓
Risk check
      ↓
Health check
      ↓
State check
      ↓
Execution permission
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the system detects a critical issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state mismatch
market data stale
risk limit exceeded
execution failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the answer can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TRADING_PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much safer than forcing every strategy to implement its own emergency behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  A simple risk model
&lt;/h2&gt;

&lt;p&gt;The first version doesn't need a complicated risk engine.&lt;/p&gt;

&lt;p&gt;A few explicit limits already create a useful safety layer.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;max_position_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
&lt;span class="na"&gt;max_market_exposure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
&lt;span class="na"&gt;max_total_exposure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
&lt;span class="na"&gt;max_daily_loss&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;
&lt;span class="na"&gt;max_consecutive_failures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="na"&gt;max_market_data_age_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the decision engine can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              RISK CHECK
                  │
       ┌──────────┼──────────┐
       │          │          │
 position     exposure     data age
   limit        limit        limit
       │          │          │
       └──────────┼──────────┘
                  │
          ┌───────┴────────┐
          │                │
        SAFE             UNSAFE
          │                │
       ALLOW             PAUSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the rules exist independently from the trading strategy.&lt;/p&gt;

&lt;p&gt;That makes them reusable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Kill switch
&lt;/h2&gt;

&lt;p&gt;Every automated trading system should have a simple way to stop placing new orders.&lt;/p&gt;

&lt;p&gt;The control plane should expose something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAUSE
RESUME
KILL
RECONCILE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A kill switch might be triggered by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;operator action&lt;/li&gt;
&lt;li&gt;daily loss threshold&lt;/li&gt;
&lt;li&gt;abnormal exposure&lt;/li&gt;
&lt;li&gt;persistent state mismatch&lt;/li&gt;
&lt;li&gt;repeated execution failure&lt;/li&gt;
&lt;li&gt;severe market-data staleness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               KILL SWITCH
                    │
                    ▼
             STOP NEW ORDERS
                    │
                    ▼
             FREEZE STRATEGY
                    │
                    ▼
              ALERT OPERATOR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact behavior around existing positions should be configurable.&lt;/p&gt;

&lt;p&gt;The important thing is that a runaway strategy shouldn't require the developer to manually terminate the process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Health is more than uptime
&lt;/h2&gt;

&lt;p&gt;A process being alive doesn't mean a trading system is healthy.&lt;/p&gt;

&lt;p&gt;I want health to include things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebSocket connection
Market-data freshness
Order-stream freshness
API availability
Event processing latency
Last reconciliation
Position consistency
Risk state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful state might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SYSTEM: DEGRADED

Market data:          OK
Order stream:         OK
API connectivity:     OK
Position state:       MISMATCH
Last reconciliation:  38s ago
Risk state:           WARNING
Trading:              PAUSED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much more actionable than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process = running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Observability should follow the trading lifecycle
&lt;/h2&gt;

&lt;p&gt;The system should eventually be able to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What happened to this order?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good execution timeline might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:00:01.120  strategy signal
10:00:01.132  risk approved
10:00:01.145  order submitted
10:00:01.180  order accepted
10:00:01.240  partial fill 40
10:00:01.242  position updated
10:00:01.245  websocket disconnected
10:00:03.101  websocket reconnected
10:00:03.120  reconciliation started
10:00:03.141  state mismatch detected
10:00:03.150  state repaired
10:00:03.170  trading resumed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is incredibly useful for debugging.&lt;/p&gt;

&lt;p&gt;It's also useful for research.&lt;/p&gt;

&lt;p&gt;And eventually it can become useful for post-trade analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why replay matters
&lt;/h2&gt;

&lt;p&gt;There's another reason to persist events.&lt;/p&gt;

&lt;p&gt;Once the system stores important state transitions, you can replay them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;historical events
        ↓
event replay
        ↓
rebuild state
        ↓
inspect behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives the same infrastructure several uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;debugging&lt;/li&gt;
&lt;li&gt;recovery&lt;/li&gt;
&lt;li&gt;backtesting&lt;/li&gt;
&lt;li&gt;incident analysis&lt;/li&gt;
&lt;li&gt;strategy research&lt;/li&gt;
&lt;li&gt;simulation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A trading system that can replay its own decisions is much easier to understand than one that only stores the final PnL.&lt;/p&gt;




&lt;h2&gt;
  
  
  A small architecture
&lt;/h2&gt;

&lt;p&gt;The first version of the project is intentionally small.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;polymarket-trading-control-plane/

crates/
├── ingest/
├── events/
├── state/
├── reconcile/
├── risk/
├── health/
├── alerts/
└── control/

examples/
├── monitor/
├── reconcile/
└── simulated_failure/

tests/
├── state/
├── reconciliation/
├── risk/
└── recovery/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 POLYMARKET
                     │
          ┌──────────┴──────────┐
          │                     │
      WebSocket               APIs
          │                     │
          ▼                     ▼
   Event Ingestion       Reconciliation
          │                     │
          └──────────┬──────────┘
                     ▼
                 State Store
                     │
           ┌─────────┼─────────┐
           ▼         ▼         ▼
         Risk      Health    Alerts
           │         │         │
           └─────────┼─────────┘
                     ▼
               Control Plane
                     │
                     ▼
                Strategy /
                Execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't intended to become a full trading platform on day one.&lt;/p&gt;

&lt;p&gt;The objective is to make a few critical problems explicit and testable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure scenarios I want to test
&lt;/h2&gt;

&lt;p&gt;A useful test suite should simulate more than successful trades.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket disconnect
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disconnect
→ reconnect
→ reconcile
→ resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Partial fill
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order size = 100
fill = 40
remaining = 60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Duplicate event
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event 1001
event 1001

result:
one state transition
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Missed event
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1001
1002
1004

detect gap
→ reconcile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Process restart
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;persisted state
→ process restart
→ reload
→ reconcile
→ continue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Risk breach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exposure &amp;gt; limit
→ trading paused
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stale market data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;last update &amp;gt; threshold
→ affected strategy disabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These scenarios are more valuable to me than simply showing that an order can be submitted successfully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this fits in a trading system
&lt;/h2&gt;

&lt;p&gt;The control plane isn't the strategy.&lt;/p&gt;

&lt;p&gt;It's the layer that protects the strategy.&lt;/p&gt;

&lt;p&gt;That means different strategies can sit above the same infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             STRATEGIES

     momentum   arbitrage   market making
          \        |        /
           \       |       /
            ▼      ▼      ▼
        ┌───────────────────┐
        │   CONTROL PLANE   │
        │                   │
        │ state             │
        │ reconciliation    │
        │ risk              │
        │ health            │
        │ recovery          │
        └─────────┬─────────┘
                  │
                  ▼
             EXECUTION
                  │
                  ▼
              POLYMARKET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the direction I find more interesting.&lt;/p&gt;

&lt;p&gt;The strategy can change.&lt;/p&gt;

&lt;p&gt;The operational infrastructure can remain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I'm building this
&lt;/h2&gt;

&lt;p&gt;I've spent a lot of time looking at the strategy side of automated Polymarket trading.&lt;/p&gt;

&lt;p&gt;But the more realistic the system becomes, the more obvious the engineering problems around the strategy become.&lt;/p&gt;

&lt;p&gt;A profitable signal doesn't solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stale state&lt;/li&gt;
&lt;li&gt;partial fills&lt;/li&gt;
&lt;li&gt;reconnects&lt;/li&gt;
&lt;li&gt;inconsistent positions&lt;/li&gt;
&lt;li&gt;risk breaches&lt;/li&gt;
&lt;li&gt;operational failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those need their own architecture.&lt;/p&gt;

&lt;p&gt;And that's the part I want to explore here.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bigger lesson
&lt;/h2&gt;

&lt;p&gt;The most dangerous trading-system failure isn't always a crash.&lt;/p&gt;

&lt;p&gt;A crashed process is obvious.&lt;/p&gt;

&lt;p&gt;A process that keeps running while believing the wrong thing is much harder to detect.&lt;/p&gt;

&lt;p&gt;That's why I think a serious Polymarket trading system needs more than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;signal
→ order
→ profit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;market data
→ events
→ state
→ reconciliation
→ risk
→ execution
→ monitoring
→ recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategy answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What should I do?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The control plane answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I safely do it right now?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the reconciliation layer answers the question underneath both:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do I actually know what state the system is in?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the problem this project is designed to solve.&lt;/p&gt;




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

&lt;p&gt;The next step is to make the event stream &lt;strong&gt;replayable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once important order, fill, position, risk, and reconciliation events can be stored and replayed, the same infrastructure can support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;debugging&lt;/li&gt;
&lt;li&gt;historical analysis&lt;/li&gt;
&lt;li&gt;recovery&lt;/li&gt;
&lt;li&gt;simulation&lt;/li&gt;
&lt;li&gt;backtesting&lt;/li&gt;
&lt;li&gt;strategy research&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's when the control plane becomes more than a monitoring tool.&lt;/p&gt;

&lt;p&gt;It becomes part of the trading system itself.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This project focuses on trading-system infrastructure, reliability, and operational controls. Proprietary trading strategies and implementation details are intentionally omitted.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>rust</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
