Polytraders Dev Guide
internal
v3 spine Phase 1 · Shared contracts 9 demo-wired · 0 shadow-ready · 0 production-live · 100 pending · 109 total 15/33 infra tasks the plan status board
HomeBy LayerExecution2.6 OrderLifecycleManager

2.6 OrderLifecycleManager

Execution Execution Utility Reshape PLANNED Spec started capital · Direct P5 · Execution rails pending flagship stub

OrderLifecycleManager is the single source of truth for every order's state machine, tracking transitions from intent through terminal state. It reconciles local state against the CLOB V2 user-state endpoint, detects stuck-in-flight and orphaned orders, and emits ExecutionReports on every transition.

v3 readiness

Docs27/27
donehow scored
Impl0/15
pendinghow scored
Backtest0/4
pendinghow scored
Runtime0/8
pendinghow scored

A bot is done when all four scores are. What does done mean?

1. Bot Identity

LayerExecution  Execution
Bot classExecution Utility
AuthorityReshape
StatusPLANNED
ReadinessSpec started
Runs beforeFillQualityAnalyzer, PartialFillHandler, CancelReplaceOptimizer
Runs afterSmartRouter (order signed and submitted to CLOB V2)
Applies toEvery live order from submission through terminal state (FILLED, CANCELLED, EXPIRED)
Default modeshadow_only
User-visibleyes
Developer ownerPolytraders core — Execution pod

Operational profile

Modes supportedquarantine

2. Purpose

OrderLifecycleManager is the single source of truth for every order's state machine, tracking transitions from intent through terminal state. It reconciles local state against the CLOB V2 user-state endpoint, detects stuck-in-flight and orphaned orders, and emits ExecutionReports on every transition.

3. Why This Bot Matters

  • Order state out of sync

    Strategy layer acts on stale position data, potentially doubling into a filled position or missing a cancellation.

  • Stuck-in-flight order not detected

    An order that never received an ack occupies position budget indefinitely, blocking new orders.

  • Orphaned ack not cancelled

    A resting order with no owning strategy leaks capital and may fill at an unintended price.

No worked examples on this bot yet. Worked examples are optional but strongly recommended — they turn an abstract failure mode into something a developer can verify in a fixture.

4. Required Polymarket Inputs

InputSourceRequired?Use
CLOB V2 user orders endpointclob_authYesReconcile local state against exchange state every reconcile_interval_s.
WebSocket user feed — fill and cancel eventsws_userYesReceive real-time order state transitions (ack, partial fill, full fill, cancel, expire).

5. Required Internal Inputs

InputSourceRequired?Use
Signed order from SmartRouterexec.smart_routerYesInitialise order state record on first submission.
KillSwitch active flagrisk.kill_switchYesCancel all open orders and halt state updates when active.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
stuck_order_timeout_s3060120Seconds after submission with no ack before an order is considered stuck-in-flight.
reconcile_interval_s103060How often to poll clob_auth for the authoritative user-order list and diff against local state.
auto_cancel_orphansTrueAutomatically cancel any order found during reconcile that has no owning strategy record.
publish_audit_logTrueEmit a full state-transition audit log entry (with builder_code) to the WAL on every order lifecycle event.

7. Detailed Parameter Instructions

stuck_order_timeout_s

What it means

Seconds after submission with no ack before an order is considered stuck-in-flight.

Default

{ "stuck_order_timeout_s": 30 }

Why this default matters

30s covers normal CLOB latency spikes; beyond 60s indicates a system issue.

Threshold logic

ConditionAction
age_since_submit_s <= 30Normal — await ack
30 < age_since_submit_s <= 60WARN — ORDER_STUCK_WARN emitted
age_since_submit_s > 60HARD_REJECT — cancel and emit ORDER_STUCK_HARD

Developer check

if now_ms() - order.submit_ms > params.stuck_order_timeout_s * 1000: emit(ORDER_STUCK)

User-facing English

An order took too long to be acknowledged by the exchange and was cancelled.

reconcile_interval_s

What it means

How often to poll clob_auth for the authoritative user-order list and diff against local state.

Default

{ "reconcile_interval_s": 10 }

Why this default matters

10s keeps state drift below one reconcile cycle; polling more often wastes rate-limit budget.

Threshold logic

ConditionAction
interval <= 10sNormal reconcile cadence
interval > 30sWARN — reconciliation lag risk
interval > 60sReject config — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

assert params.reconcile_interval_s <= params.hard

User-facing English

Order status is updated from the exchange every few seconds.

auto_cancel_orphans

What it means

Automatically cancel any order found during reconcile that has no owning strategy record.

Default

{ "auto_cancel_orphans": true }

Why this default matters

Orphaned orders must be cancelled to reclaim position budget; leaving them risks unintended fills.

Threshold logic

ConditionAction
orphan detected AND auto_cancel_orphans=trueCancel order; emit ORDER_ORPHAN_CANCELLED
orphan detected AND auto_cancel_orphans=falseWARN only — operator must cancel manually

Developer check

if orphan and params.auto_cancel_orphans: clob_auth.DELETE('/order/' + order_id)

User-facing English

An unrecognised order was found and cancelled to protect your account.

publish_audit_log

What it means

Emit a full state-transition audit log entry (with builder_code) to the WAL on every order lifecycle event.

Default

{ "publish_audit_log": true }

Why this default matters

Audit logs are required for 7-year retention compliance; disabling is only allowed in isolated test environments.

Threshold logic

ConditionAction
publish_audit_log=trueEvery transition emitted to WAL with trace_id and builder_code
publish_audit_log=falseWARN — audit log disabled; only permitted in test mode

Developer check

if params.publish_audit_log: wal.append(transition_record)

User-facing English

Every change to your order is logged for transparency and record-keeping.

8. Default Configuration

{
  "bot_id": "exec.orderlifecyclemanager",
  "version": "0.1.0",
  "mode": "shadow_only",
  "defaults": {
    "stuck_order_timeout_s": 30,
    "reconcile_interval_s": 10,
    "auto_cancel_orphans": true,
    "publish_audit_log": true
  },
  "locked": {
    "stuck_order_timeout_s": {
      "max": 120
    },
    "reconcile_interval_s": {
      "max": 60
    }
  }
}

9. Implementation Flow

  1. On receipt of a signed order from SmartRouter, create an order state record: {order_id, market_id, side, price, size_usd, status=PENDING_ACK, submit_ms, builder_code}.
  2. Subscribe to ws_user feed for fill, cancel, and expire events on this order.
  3. On CLOB V2 ack received: transition status PENDING_ACK → OPEN; emit ExecutionReport(status=OPEN).
  4. On partial fill event: update filled_usd, remaining_usd; emit ExecutionReport(status=PARTIAL); forward to PartialFillHandler.
  5. On full fill event: transition status → FILLED; emit ExecutionReport(status=FILLED); forward to FillQualityAnalyzer.
  6. On cancel/expire event: transition status → CANCELLED or EXPIRED; emit ExecutionReport.
  7. Every reconcile_interval_s: fetch clob_auth /orders/open; diff against local state; detect orphans and stuck orders.
  8. On stuck order (age > stuck_order_timeout_s): cancel via clob_auth DELETE /order/{id}; emit ORDER_STUCK reason code.
  9. On orphan detected and auto_cancel_orphans=true: cancel via clob_auth; emit ORDER_ORPHAN_CANCELLED.
  10. On KillSwitch active: cancel all open orders; emit KILL_SWITCH_ACTIVE for each; halt further processing.

10. Reference Implementation

Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. IF/THEN/ELSE = decision. Translate directly to TypeScript, Python, Go, or Rust.

FUNCTION manageLifecycle(order):
  // 1. Initialise state
  state = {order_id: order.id, status: 'PENDING_ACK',
           submit_ms: now_ms(), filled_usd: 0,
           builder_code: order.builder_code}
  store.put(order.id, state)

  // 2. Subscribe to ws_user events
  ws_user.subscribe('order_update', order.market_id, order.id)

  // 3. Process ws_user events
  ON ws_user EVENT e WHERE e.order_id == order.id:
    IF e.type == 'ACK':
      state.status = 'OPEN'
    ELIF e.type == 'PARTIAL_FILL':
      state.filled_usd += e.fill_usd
      state.status = 'PARTIAL'
      FORWARD TO partialfillhandler(state)
    ELIF e.type == 'FILL':
      state.status = 'FILLED'
      FORWARD TO fillqualityanalyzer(state)
    ELIF e.type IN ('CANCEL', 'EXPIRE'):
      state.status = e.type
    EMIT ExecutionReport(state)

  // 4. Reconcile loop (every reconcile_interval_s)
  EVERY params.reconcile_interval_s:
    live = FETCH clob_auth.GET('/orders/open')
    FOR order_id IN store.all_open():
      IF order_id NOT IN live AND store.status != 'FILLED':
        state.status = 'RECONCILE_DISCREPANCY'
        EMIT ExecutionReport(state, WARN)
    FOR order_id IN live:
      IF order_id NOT IN store:
        IF params.auto_cancel_orphans:
          clob_auth.DELETE('/order/' + order_id)
          EMIT ExecutionReport(ORDER_ORPHAN_CANCELLED)

  // 5. Stuck-order check
  IF state.status == 'PENDING_ACK':
    age_s = (now_ms() - state.submit_ms) / 1000
    IF age_s > params.stuck_order_timeout_s:
      clob_auth.DELETE('/order/' + state.order_id)
      EMIT ExecutionReport(state, HARD_REJECT, ORDER_STUCK)

SDK calls used

  • ws_user.subscribe('order_update', market_id, order_id)
  • clob_auth.GET('/orders/open')
  • clob_auth.DELETE('/order/' + order_id)

Complexity: O(N) per reconcile where N = open orders

11. Wire Examples

Input — what arrives on the wire

ws_user partial fill eventws_user

{
  "order_id": "0xaaaa1111bbbb2222cccc3333dddd4444eeee5555ffff6666aaaa7777bbbb8888",
  "type": "PARTIAL_FILL",
  "fill_usd": 150,
  "fill_price": 0.62,
  "fill_ts_ms": 1746770000000,
  "collateral": "pUSD"
}

Output — what the bot emits

ExecutionReport — PARTIAL status

{
  "report_id": "rep_1a2b3c4d5e6f7a8b",
  "trace_id": "trc_9f8e7d6c5b4a3f2e",
  "bot_id": "exec.orderlifecyclemanager",
  "order_id": "0xaaaa1111bbbb2222cccc3333dddd4444eeee5555ffff6666aaaa7777bbbb8888",
  "status": "PARTIAL",
  "filled_usd": 150,
  "remaining_usd": 300,
  "collateral": "pUSD",
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
  "evaluated_at_ms": 1746770000000
}

12. Decision Logic

APPROVE

Order lifecycle event is processed normally; state transitions recorded and ExecutionReport emitted.

RESHAPE_REQUIRED

Reconciliation detects a state discrepancy; local state overwritten with exchange-authoritative state.

REJECT

KillSwitch active or order stuck beyond hard threshold — cancel order, emit HARD_REJECT reason code.

WARNING_ONLY

Order approaching stuck threshold or reconcile interval exceeds warning level — emit WARN, continue.

13. Standard Decision Output

This bot returns a ExecutionReport object. See ExecutionReport schema.

{
  "report_id": "rep_1a2b3c4d5e6f7a8b",
  "trace_id": "trc_9f8e7d6c5b4a3f2e",
  "bot_id": "exec.orderlifecyclemanager",
  "order_id": "0xaaaa1111bbbb2222cccc3333dddd4444eeee5555ffff6666aaaa7777bbbb8888",
  "market_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
  "side": "BUY",
  "status": "PARTIAL",
  "filled_usd": 150,
  "remaining_usd": 300,
  "collateral": "pUSD",
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
  "eip712_domain_version": "2",
  "evaluated_at_ms": 1746770000000
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
ORDER_LIFECYCLE_TRANSITIONINFONormal state transition recorded.Emit ExecutionReport; no intervention required.
ORDER_STUCKHARD_REJECTOrder has been PENDING_ACK beyond stuck_order_timeout_s.Cancel order via clob_auth; emit ExecutionReport.Your order was cancelled because the exchange did not acknowledge it in time.
ORDER_ORPHAN_CANCELLEDWARNOrder found during reconcile with no owning strategy record.Cancel order; log warning.An unrecognised order on your account was cancelled.
KILL_SWITCH_ACTIVEHARD_REJECTGlobal kill switch active; cancel all open orders.Cancel all open orders; halt processing.Trading is currently paused.
RECONCILE_DISCREPANCYWARNLocal state does not match exchange state after reconcile poll.Overwrite local state with exchange state; emit WARN.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_exec_orderlifecyclemanager_transitions_totalcountercountstatusTotal order state transitions by terminal or intermediate status.
polytraders_exec_orderlifecyclemanager_stuck_orders_totalcountercountTotal orders cancelled due to stuck-in-flight timeout.
polytraders_exec_orderlifecyclemanager_reconcile_discrepancies_totalcountercountTotal reconcile cycles that found a state discrepancy.
polytraders_exec_orderlifecyclemanager_open_ordersgaugecountCurrent number of open orders tracked by the state machine.

Alerts

AlertConditionSeverityRunbook
OLMStuckOrderRaterate(polytraders_exec_orderlifecyclemanager_stuck_orders_total[5m]) > 0.1P2#runbook-olm-stuck-orders
OLMReconcileDiscrepancyrate(polytraders_exec_orderlifecyclemanager_reconcile_discrepancies_total[5m]) > 0.05P1#runbook-olm-reconcile

16. Developer Reporting

{
  "report_id": "rep_1a2b3c4d5e6f7a8b",
  "order_id": "0xaaaa1111bbbb2222cccc3333dddd4444eeee5555ffff6666aaaa7777bbbb8888",
  "status_from": "OPEN",
  "status_to": "PARTIAL",
  "filled_usd": 150,
  "remaining_usd": 300,
  "reconcile_diff": null,
  "stuck_age_s": 0,
  "orphan": false,
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
  "evaluated_at_ms": 1746770000000
}

17. Plain-English Reporting

SituationUser-facing explanation
Order partially filledPart of your order was filled. The remaining portion is still open in the market.
Order stuck — cancelledYour order did not receive a response from the exchange within the expected time and was cancelled for safety.
Orphaned order cancelledAn unrecognised order was found on your account and cancelled to protect your balance.

18. Failure-Mode Block

main_failure_modews_user feed disconnects silently, causing fill events to be missed and local state to diverge from exchange state until the next reconcile poll.
false_positive_riskDeclaring an order stuck during a legitimate CLOB latency spike, causing a premature cancel and missed fill.
false_negative_riskReconcile interval too long means orphaned orders linger and consume position budget for up to reconcile_interval_s seconds.
safe_fallbackIf ws_user feed is unavailable, fall back to polling clob_auth /orders every reconcile_interval_s. If clob_auth is unreachable, hold state and emit WARN; escalate to KillSwitch if unreachable > 60s.
required_dependenciesws_user fill/cancel/expire feed, clob_auth /orders/open endpoint, KillSwitch active flag

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
WS_USER_DISCONNECTKill ws_user WebSocket connectionWebSocket reconnects; event replay from last seq_id
STUCK_ORDER_TIMEOUTBlock CLOB ack for target order for 90sAutomatic; next order proceeds normally
KILL_SWITCH_ONSet killswitch.active=true with 3 open ordersManual KillSwitch reset required

20. State & Persistence

Cold-start recovery

On restart, re-read open order state from clob_auth and rebuild local map.

21. Concurrency & Idempotency

AspectSpecification
Execution modelevent-driven single-threaded loop per order
Max in-flight200
Idempotency keyorder_id + event_type + event_ts_ms
Per-call timeout (ms)500
Backpressure strategyshed oldest pending reconcile if queue > 200
Locking / mutual exclusionper-order_id mutex for state writes

22. Dependencies

Depends on (must run first)

BotWhyContract
exec.smart_routerProvides signed order with builder_code to initialise state.Order state record created only after SmartRouter emits signed order.
risk.kill_switchKillSwitch triggers mass cancel of all open orders.No open order survives an active KillSwitch.

Emits to (downstream consumers)

BotWhyContract
exec.partialfillhandlerForwards PARTIAL status events for remainder policy decisions.PartialFillHandler receives state with filled_usd and remaining_usd.
exec.fillqualityanalyzerForwards FILLED events for fill quality scoring.FillQualityAnalyzer receives final ExecutionReport with fill price and size.

Used by (auto-aggregated)

2.11 2.15 2.7 2.8 6.16 4.16 1.19 5.9

External services

ServiceEndpointSLA assumedOn failure
CLOB V2 auth APIhttps://clob.polymarket.com99.95% / 200ms p99Fall back to ws_user-only state; escalate if unreachable > 60s.
WS user feedwss://ws-subscriptions-clob.polymarket.com/ws/userbest-effortFall back to REST reconcile polling.

23. Security Surfaces

Abuse vectors considered

  • Injecting a fake FILL event to prematurely close an order and suppress further fills
  • Flooding the reconcile endpoint to exhaust rate limits and prevent orphan detection

Mitigations

  • ws_user event signatures validated against CLOB V2 HMAC; unsigned events discarded
  • Reconcile rate-limit budget tracked; if exhausted, local state is trusted and WARN emitted

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldyes
Aware of negative-risk marketsno
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesEvery ExecutionReport includes builder_code (bytes32) from the originating signed order for builder attribution on all lifecycle transitions.

API surfaces declared

clob_authws_userinternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation0.1.0
schema2
releasedNone
planned_releaseQ4-2026

Migration history

DateFromToReasonAction taken
2026-04-28n/av2-specSpec drafted post-CLOB-V2 cutover; bot not yet implementedDesigned against V2 schema (pUSD, builder codes, V2 EIP-712 domain)

26. Acceptance Tests

Unit Tests

TestSetupExpected result
PENDING_ACK → OPEN transition on ackSubmit order; inject ack eventstatus=OPEN, ExecutionReport emitted
OPEN → PARTIAL on partial fillInject partial fill event; filled=150, size=450remaining_usd=300, status=PARTIAL
Stuck order cancelled after timeoutage_since_submit=90s, stuck_order_timeout_s=30cancel issued, ORDER_STUCK emitted
Orphan auto-cancelled when auto_cancel_orphans=trueReconcile finds order_id with no local recordDELETE /order/{id} issued, ORDER_ORPHAN_CANCELLED emitted

Integration Tests

TestExpected result
Full lifecycle: submit → ack → partial fill → full fillAll transitions recorded; final status=FILLED; ExecutionReport emitted to polytraders.reports.execution
ws_user feed disconnect → reconcile fallback → state restoredAfter reconnect, state matches clob_auth; no duplicate events emitted

Property Tests

PropertyRequired behaviour
Order state always moves forward (no regression from FILLED/CANCELLED to OPEN)Always true
builder_code present on every emitted ExecutionReportAlways true

27. Operational Runbook

OLM incidents are usually ws_user feed disconnects causing reconcile lag, or a burst of stuck orders during CLOB latency spikes.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
OLMStuckOrderRateCheck CLOB latency dashboard; if elevated, extend stuck_order_timeout_s temporarily.Exec pod lead if stuck rate > 0.5/min sustained
OLMReconcileDiscrepancyCompare local order state dump with clob_auth /orders/open; identify which orders diverged.Exec pod lead + infra if discrepancy persists > 10 min

Manual overrides

  • polytraders bot cancel-order exec.orderlifecyclemanager --order <order_id> — Manual intervention required to cancel a stuck or orphaned order.

Healthcheck

GET /internal/health/orderlifecyclemanager → green if ws_user feed connected, clob_auth reachable, open_orders gauge stable, reconcile running within interval; red if ws_user disconnected > 30s, clob_auth unreachable, stuck_orders_total spiking

28. Promotion Gates

A bot does not advance to the next readiness state until every gate below is green. Gates are observable from production data — no subjective sign-off.

Promote to Shadow

GateHow measuredThreshold
All unit tests pass including state-machine invariantsCI test run100% pass

Promote to Limited live

GateHow measuredThreshold
Reconcile discrepancy rate < 0.1% over 48h shadow runpolytraders_exec_orderlifecyclemanager_reconcile_discrepancies_total< 0.1%

Promote to General live

GateHow measuredThreshold
Zero orphaned orders surviving > 1 reconcile cycle over 7-day limited-liveAudit log cross-referenceZero violations

29. Developer Checklist

Ready-to-ship score: 27/27 sections complete · 100%

RequirementStatus
Purpose defined✓ done
Required inputs listed✓ done
Parameters defined✓ done
Defaults defined✓ done
Warning thresholds defined✓ done
Hard thresholds defined✓ done
Safe fallback defined✓ done
Structured output defined✓ done
Developer log defined✓ done
Plain-English explanation✓ done
Unit tests defined✓ done
Integration tests defined✓ done
Property tests defined✓ done
Failure-mode block complete✓ done
Reference implementation pseudocode✓ done
Wire examples (input + output)✓ done
Reason codes listed✓ done
Metrics & logs defined✓ done
State & persistence defined✓ done
Concurrency & idempotency defined✓ done
Dependencies declared✓ done
Security surfaces declared✓ done
Polymarket V2 compatibility declared✓ done
Version & migration history declared✓ done
Operational runbook defined✓ done
Promotion gates defined✓ done
Failure-injection recipes defined✓ done