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 LayerIntelligence4.3 OrderFlowAnalyzer

4.3 OrderFlowAnalyzer

Intelligence Signal Service Read-only LIVE General live capital · Indirect P2 · Data normalisation pending stub

OrderFlowAnalyzer subscribes to ws_market for real-time trade prints and clob_public for order-book snapshots, then classifies each flow event as toxic (informed, likely to precede adverse price movement), benign (noise/retail), or informed (structural, correlated with neg-risk rebalancing). It emits an ObservationReport for every toxic flip and samples 1/10 routine ticks. Output feeds liquidity-aware strategies and the LiquidityGuard risk bot. OrderFlowAnalyzer is strictly read-only — it never submits or signs orders.

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

LayerIntelligence  Intelligence
Bot classSignal Service
AuthorityRead-only
StatusLIVE
ReadinessGeneral live
Runs beforeliquidity-aware strategy layer, LiquidityGuard
Runs afterws_market subscription established; clob_public book snapshot loaded
Applies toAll markets with active ws_market subscriptions
Default modegeneral_live
User-visibleAdvanced details only
Developer ownerPolytraders core — Intelligence pod

2. Purpose

OrderFlowAnalyzer subscribes to ws_market for real-time trade prints and clob_public for order-book snapshots, then classifies each flow event as toxic (informed, likely to precede adverse price movement), benign (noise/retail), or informed (structural, correlated with neg-risk rebalancing). It emits an ObservationReport for every toxic flip and samples 1/10 routine ticks. Output feeds liquidity-aware strategies and the LiquidityGuard risk bot. OrderFlowAnalyzer is strictly read-only — it never submits or signs orders.

3. Why This Bot Matters

  • Toxic flow not detected before entry

    Strategy enters a market being targeted by informed traders; adverse selection erodes edge within seconds of fill.

  • Order-book imbalance not propagated to LiquidityGuard

    LiquidityGuard uses stale depth figures; permits entry at sizes that would incur >2× expected slippage.

  • Informed neg-risk flow misclassified as benign

    Neg-risk rebalancing activity (bulk outcome swaps) treated as noise; strategy takes wrong side of a structural price shift.

  • Microstructure features computed from stale snapshot

    Stale imbalance and micro-volatility signals produce incorrect flow classification, causing either over-trading or missed entries.

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
Real-time trade prints (taker side, size, price, timestamp_ms)ws_marketYesClassify each fill as toxic / benign / informed based on size, speed, and direction patterns.
Order-book snapshots (top-50 bid/ask levels, sizes in pUSD)clob_publicYesCompute bid-ask imbalance, visible depth, and queue-position features for flow classification.
Market neg-risk flagGamma API (via internal cache)NoAmplify informed-flow scoring for neg-risk markets where bulk outcome swaps are common.

5. Required Internal Inputs

InputSourceRequired?Use
KillSwitch active flagKillSwitchYesContinue computing microstructure features but suppress ObservationReport emissions when KillSwitch is active.
LiquidityGuard depth thresholdsLiquidityGuard configNoCalibrate minimum imbalance threshold against current depth guard parameters.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
imbalance_window_s30105Rolling window in seconds over which order-book imbalance is computed.
micro_vol_window_s602010Window in seconds for micro-volatility estimation (std-dev of trade prices).
publish_rate_hz1510Maximum ObservationReport emission rate per market in Hertz (reports/second).
queue_position_methodpro_rataNoneNoneMethod used to estimate queue position for maker orders. Options: pro_rata | fifo | none.

7. Detailed Parameter Instructions

imbalance_window_s

What it means

Rolling window in seconds over which order-book imbalance is computed.

Default

{ "imbalance_window_s": 30 }

Why this default matters

30 s captures meaningful directional pressure without over-reacting to single large prints.

Threshold logic

ConditionAction
window ≥ 30 sNormal — stable imbalance signal
10–30 sWARN — signal noisier; may increase false toxic classifications
< 5 sReject — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

if (p.imbalance_window_s < p.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');

User-facing English

Order-book pressure is measured over a rolling window to distinguish sustained trends from one-off prints.

micro_vol_window_s

What it means

Window in seconds for micro-volatility estimation (std-dev of trade prices).

Default

{ "micro_vol_window_s": 60 }

Why this default matters

60 s provides a stable micro-vol baseline; shorter windows produce noisy estimates.

Threshold logic

ConditionAction
window ≥ 60 sNormal
20–60 sWARN — noisier micro-vol estimate
< 10 sReject — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

if (p.micro_vol_window_s < p.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');

User-facing English

Short-term price volatility is tracked to detect unusual market stress.

publish_rate_hz

What it means

Maximum ObservationReport emission rate per market in Hertz (reports/second).

Default

{ "publish_rate_hz": 1 }

Why this default matters

1 Hz prevents bus flooding while ensuring fresh flow classification for liquidity strategies.

Threshold logic

ConditionAction
rate ≤ 1 HzNormal
1–5 HzWARN — higher bus load
> 10 HzHard cap — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

if (p.publish_rate_hz > p.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');

User-facing English

Flow classification updates are rate-limited to prevent overloading downstream systems.

queue_position_method

What it means

Method used to estimate queue position for maker orders. Options: pro_rata | fifo | none.

Default

{ "queue_position_method": "pro_rata" }

Why this default matters

Polymarket uses pro-rata queue mechanics; pro_rata provides the most accurate toxic-flow detection.

Threshold logic

— not yet authored —

Developer check

// not yet authored

User-facing English

Queue position is estimated using the correct mechanics for this exchange type.

8. Default Configuration

{
  "bot_id": "intel.orderflowanalyzer",
  "version": "2.1.0",
  "mode": "general_live",
  "defaults": {
    "imbalance_window_s": 30,
    "micro_vol_window_s": 60,
    "publish_rate_hz": 1,
    "queue_position_method": "pro_rata"
  },
  "locked": {
    "imbalance_window_s": {
      "min": 5
    },
    "micro_vol_window_s": {
      "min": 10
    },
    "publish_rate_hz": {
      "max": 10
    }
  }
}

9. Implementation Flow

  1. Subscribe to ws_market for trade prints (taker_side, size_pusd, price, timestamp_ms) for all tracked markets.
  2. Subscribe to clob_public for order-book snapshots (top-50 bid/ask levels) per market at publish_rate_hz.
  3. On each trade print: append to rolling imbalance buffer (imbalance_window_s). Compute bid_ask_imbalance = (bid_depth - ask_depth) / total_depth.
  4. Compute micro_vol: std-dev of last N trade prices within micro_vol_window_s.
  5. Classify flow_class: TOXIC if (taker_side==BUY AND imbalance < -0.3) OR (taker_side==SELL AND imbalance > 0.3) OR print_size_pusd > 2× avg_print_size; INFORMED if neg_risk_flag AND abs(imbalance) > 0.5; BENIGN otherwise.
  6. Check KillSwitch; if active, continue computing but suppress emissions.
  7. Apply sampling: if flow_class == TOXIC or last ObservationReport for this market > 1/publish_rate_hz s ago → emit-every. Otherwise sample-1/10.
  8. Emit ObservationReport with: report_id, trace_id, condition_id, flow_class, bid_ask_imbalance, micro_vol, print_size_pusd, taker_side, book_depth_pusd, neg_risk_flag, warnings.
  9. Log per-market cycle: flow_class distribution, imbalance, micro_vol, prints_received, emitted.

10. Reference Implementation

Subscribes to ws_market for trade prints and clob_public for book snapshots, computes rolling imbalance and micro-volatility features, classifies each flow event as TOXIC/BENIGN/INFORMED, and emits ObservationReports with per-market sampling (emit-every for TOXIC flips, sample-1/10 for routine ticks).

Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output.

// --- Initialisation ---
FOR market IN tracked_markets:
  ws_market.subscribe(market.condition_id, handler=onTradePrint)
  imbalance_buffer[market.condition_id] = RollingWindow(imbalance_window_s)
  microvol_buffer[market.condition_id]  = RollingWindow(micro_vol_window_s)

FUNCTION onTradePrint(event):
  cid = event.condition_id

  // --- 1. Staleness gate ---
  IF (now_ms() - event.timestamp_ms) > imbalance_window_s * 1000:
    LOG WARN 'STALE_DATA — skipping stale print'
    RETURN

  // --- 2. Update rolling windows ---
  imbalance_buffer[cid].add(event)
  microvol_buffer[cid].add(event.price)

  // --- 3. Fetch book snapshot ---
  book = fetchClobPublic('/book?market=' + cid + '&depth=50')
  IF book IS NULL:
    flow_class = 'UNKNOWN'; warnings = ['STALE_DATA']
    GOTO emit_check

  bid_depth  = SUM(level.size * level.price FOR level IN book.bids[:50])
  ask_depth  = SUM(level.size * level.price FOR level IN book.asks[:50])
  total_depth = bid_depth + ask_depth
  imbalance   = (bid_depth - ask_depth) / total_depth IF total_depth > 0 ELSE 0

  micro_vol   = STDEV(microvol_buffer[cid].prices())

  // --- 4. Flow classification ---
  avg_print = imbalance_buffer[cid].avg_print_size_pusd()
  neg_risk  = internal.market_cache[cid].neg_risk OR false

  IF (event.taker_side == 'BUY'  AND imbalance < -0.3)   OR (event.taker_side == 'SELL' AND imbalance >  0.3)   OR event.size_pusd > 2 * avg_print:
    flow_class = 'TOXIC'
  ELSE IF neg_risk AND ABS(imbalance) > 0.5:
    flow_class = 'INFORMED'
  ELSE:
    flow_class = 'BENIGN'

  // --- 5. Warnings ---
  warnings = []
  IF total_depth < 2 * liquidity_guard.min_depth_pusd:
    warnings.append('ORDERFLOW_THIN_BOOK')

  :emit_check
  // --- 6. KillSwitch ---
  ks = FETCH internal.killswitch.status
  IF ks.active:
    LOG INFO 'KILL_SWITCH_ACTIVE — suppressing ObservationReport'
    RETURN

  // --- 7. Rate-limit + sampling ---
  IF flow_class == 'TOXIC' OR time_since_last_emit[cid] >= 1/params.publish_rate_hz:
    sampling_applied = false
  ELSE:
    IF random() > 0.1: RETURN     // sample-1/10
    sampling_applied = true

  // --- 8. Emit ---
  report = ObservationReport(
    report_id         = 'rep_ofa_' + cid[:6] + '_' + now_ms(),
    trace_id          = newTraceId(),
    bot_id            = 'intel.orderflowanalyzer',
    kind              = 'ObservationReport',
    condition_id      = cid,
    flow_class        = flow_class,
    bid_ask_imbalance = imbalance,
    micro_vol         = micro_vol,
    print_size_pusd   = event.size_pusd,
    taker_side        = event.taker_side,
    book_depth_pusd   = total_depth,
    neg_risk_flag     = neg_risk,
    sampling_applied  = sampling_applied,
    warnings          = warnings,
    emitted_at_ms     = now_ms()
  )
  EMIT internal.bus.observations <- report
  time_since_last_emit[cid] = now_ms()

SDK calls used

  • ws_market.subscribe(condition_id, handler)
  • fetchClobPublic('/book?market=<condition_id>&depth=50')

Complexity: O(1) per trade print; O(N) per book snapshot where N = book levels (≤50)

11. Wire Examples

Input — what arrives on the wire

{
  "label": "ws_market trade print",
  "source": "ws_market",
  "payload": {
    "event_type": "trade",
    "condition_id": "0xbcd2340000000000000000000000000000000000000000000000000000000000",
    "taker_side": "BUY",
    "size_pusd": 4200,
    "price": 0.72,
    "timestamp_ms": 1746701000000,
    "maker_order_id": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab"
  }
}

Output — what the bot emits

{
  "label": "ObservationReport — TOXIC flow detected",
  "payload": {
    "report_id": "rep_ofa_0xbcd2_1746701000000",
    "trace_id": "trc_0xcafe010203040506070809",
    "bot_id": "intel.orderflowanalyzer",
    "kind": "ObservationReport",
    "condition_id": "0xbcd2340000000000000000000000000000000000000000000000000000000000",
    "flow_class": "TOXIC",
    "bid_ask_imbalance": -0.41,
    "micro_vol": 0.018,
    "print_size_pusd": 4200,
    "taker_side": "BUY",
    "book_depth_pusd": 8900,
    "neg_risk_flag": true,
    "sampling_applied": false,
    "warnings": [
      "ORDERFLOW_THIN_BOOK"
    ],
    "emitted_at_ms": 1746701000085
  }
}

12. Decision Logic

APPROVE

Not applicable — OrderFlowAnalyzer is read-only; it never approves or submits orders.

RESHAPE_REQUIRED

Not applicable.

REJECT

ObservationReport emissions are suppressed only when KillSwitch is active (KILL_SWITCH_ACTIVE). Routine ticks sampled out are not emitted but are still computed.

WARNING_ONLY

ORDERFLOW_THIN_BOOK is included as a warning when visible book depth < 2× LiquidityGuard minimum; downstream strategies apply additional size restrictions.

13. Standard Decision Output

This bot returns a ObservationReport object. See ObservationReport schema.

{
  "report_id": "rep_ofa_0xbcd2_1746701000000",
  "trace_id": "trc_0xcafe010203040506",
  "bot_id": "intel.orderflowanalyzer",
  "kind": "ObservationReport",
  "condition_id": "0xbcd2340000000000000000000000000000000000000000000000000000000000",
  "flow_class": "TOXIC",
  "bid_ask_imbalance": -0.41,
  "micro_vol": 0.018,
  "print_size_pusd": 4200,
  "taker_side": "BUY",
  "book_depth_pusd": 8900,
  "neg_risk_flag": true,
  "sampling_applied": false,
  "warnings": [
    "ORDERFLOW_THIN_BOOK"
  ],
  "emitted_at_ms": 1746701000085
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
ORDERFLOW_TOXIC_FLIPWARNFlow classified as TOXIC: large print or directional imbalance signals informed order flow.Emit ObservationReport with flow_class=TOXIC emit-every; downstream strategies apply adverse-selection guard.Unusual buying or selling pressure detected on this market. Entry paused temporarily.
ORDERFLOW_INFORMEDWARNNeg-risk market shows structural bulk-rebalancing flow classified as INFORMED.Emit ObservationReport with flow_class=INFORMED; strategies treat as structural directional signal.
ORDERFLOW_THIN_BOOKWARNVisible book depth below 2× LiquidityGuard minimum during flow classification.Include in warnings on ObservationReport; LiquidityGuard applies tighter size restrictions.This market has unusually thin liquidity. Order sizes are being restricted.
STALE_DATAWARNws_market disconnected or book snapshot stale for > 2× imbalance_window_s.Set flow_class=UNKNOWN for affected markets; halt emissions; downstream strategies do not enter.
KILL_SWITCH_ACTIVEHARD_REJECTKillSwitch active; ObservationReport emissions suppressed.Continue computing features but suppress all emissions.Flow analysis is paused while trading is suspended system-wide.
MARKET_CLOSEDEXPLAINTrade print received for a market that is closed or resolved.Ignore print; unsubscribe from ws_market for this condition_id.
ORDERFLOW_RATE_CAPPEDINFOObservationReport emission rate-capped at publish_rate_hz for a benign market.Subsequent prints sampled; next scheduled emit will carry accumulated imbalance.
PARAMETER_CHANGE_REQUIRES_APPROVALHARD_REJECTA parameter change violates a locked bound (e.g. imbalance_window_s < 5).Reject config change; do not apply.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_intel_orderflowanalyzer_prints_received_totalcountercountcondition_idTotal trade prints received from ws_market per market.
polytraders_intel_orderflowanalyzer_observations_emitted_totalcountercountflow_class, sampling_appliedObservationReports emitted broken down by flow class and sampling status.
polytraders_intel_orderflowanalyzer_bid_ask_imbalancegaugeratiocondition_idCurrent bid-ask imbalance per market (range -1 to 1).
polytraders_intel_orderflowanalyzer_micro_volgaugeratiocondition_idCurrent micro-volatility (std-dev of recent trade prices) per market.
polytraders_intel_orderflowanalyzer_toxic_flips_totalcountercountcondition_idTotal TOXIC flow classifications, per market.
polytraders_intel_orderflowanalyzer_ws_reconnects_totalcountercountTotal ws_market reconnection events.

Alerts

AlertConditionSeverityRunbook
OrderFlowAnalyzerWSDisconnectrate(polytraders_intel_orderflowanalyzer_ws_reconnects_total[5m]) > 3page#runbook-orderflowanalyzer-ws-disconnect
OrderFlowAnalyzerToxicFloodSpikerate(polytraders_intel_orderflowanalyzer_toxic_flips_total[5m]) > 20warn#runbook-orderflowanalyzer-toxic-spike
OrderFlowAnalyzerStaleDatapolytraders_intel_orderflowanalyzer_prints_received_total rate == 0 for 5mpage#runbook-orderflowanalyzer-stale-data
OrderFlowAnalyzerHighImbalanceabs(polytraders_intel_orderflowanalyzer_bid_ask_imbalance) > 0.7 for any condition_idwarn#runbook-orderflowanalyzer-high-imbalance

Dashboards

  • Grafana — Intelligence / OrderFlowAnalyzer flow classification distribution
  • Grafana — Intelligence / per-market imbalance and micro-vol heatmap

16. Developer Reporting

{
  "bot_id": "intel.orderflowanalyzer",
  "cycle_ts_ms": 1746701000000,
  "markets_tracked": 47,
  "prints_received": 312,
  "toxic_flips": 3,
  "informed_events": 1,
  "benign_ticks": 308,
  "emitted": 14,
  "sampled_out": 298,
  "killswitch_active": false
}

17. Plain-English Reporting

SituationUser-facing explanation
Strategy entry blocked — toxic flow detectedThe order book shows signs of informed selling pressure on this market. Entry has been paused to avoid adverse selection.
Lower-than-expected fill rate on a marketRecent order-flow analysis detected directional imbalance. Liquidity-aware sizing reduced the order size to protect against unfavourable execution.
Neg-risk market flagged as informed flowBulk rebalancing activity typical of multi-outcome markets was detected. This is structural, not predatory, but strategies will treat it as an informed signal.

18. Failure-Mode Block

main_failure_modews_market disconnection causes OrderFlowAnalyzer to miss trade prints during a high-activity period, allowing strategies to enter during undetected toxic flow.
false_positive_riskA single large benign print (e.g. a market maker hedging) classified as TOXIC based on size alone, causing unnecessary strategy pausing.
false_negative_riskSlow-drip toxic flow spread across many small prints falls below per-print size threshold; imbalance accumulates but classification update is delayed by the rolling window.
safe_fallbackIf ws_market is disconnected for > 2× imbalance_window_s, emit STALE_DATA and set flow_class=UNKNOWN for all affected markets. Downstream strategies treat UNKNOWN as conservative (do not enter). Reconnect with exponential back-off.
required_dependenciesws_market trade-print subscription, clob_public order-book snapshot endpoint, KillSwitch active flag readable

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
WS_DISCONNECTDrop TCP connection to ws_market for 65 s (> 2× imbalance_window_s=30)Automatic reconnect with exponential back-off; buffer warms up over next imbalance_window_s
TOXIC_FLOODInject 25 TOXIC prints/s across 3 markets for 10 sAutomatic when toxic print rate subsides
STALE_BOOK_SNAPSHOTReturn 503 from clob_public for 30 sAutomatic when clob_public recovers
KILL_SWITCH_ONSet killswitch.active=true; inject TOXIC printEmissions resume on first print after KillSwitch reset
BUFFER_OVERFLOWInject 200 prints/s per market for 5 s (> per-market queue=100)Automatic when ingest rate drops below drain rate

20. State & Persistence

Cold-start recovery

On cold start, rolling windows are empty. First imbalance_window_s of operation produces no TOXIC classifications until buffers warm up. flow_class=UNKNOWN emitted during warm-up.

21. Concurrency & Idempotency

AspectSpecification
Execution modelsingle-threaded event loop
Max in-flight50
Idempotency keycondition_id + timestamp_ms
Per-call timeout (ms)500
Backpressure strategydrop-after-buffer — excess prints dropped when per-market queue > 100; STALE_DATA warned
Locking / mutual exclusionnone — per-market state accessed only from the single event loop

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchKillSwitch gate suppresses ObservationReport emissions.

Emits to (downstream consumers)

BotWhyContract
risk.liquidity_guard
strat.liquidity_aware_strategies

Used by (auto-aggregated)

2.14 4.16 1.17 1.18

External services

ServiceEndpointSLA assumedOn failure
Polymarket ws_marketwss://ws-subscriptions-clob.polymarket.com/ws/marketbest-effort
CLOB public API (book snapshots)https://clob.polymarket.com99.95% / 200 ms p99

23. Security Surfaces

Abuse vectors considered

  • Adversary floods ws_market with wash-trade prints to trigger false TOXIC classifications, causing strategies to stand down
  • Manipulated clob_public book snapshot (e.g. spoofed depth) causing incorrect imbalance computation

Mitigations

  • Minimum print_size_pusd threshold prevents single 1-lot prints from triggering TOXIC classification
  • Imbalance computed over rolling window — single snapshot manipulation insufficient to flip classification
  • All ObservationReports are recommendations only — LiquidityGuard and strategies independently validate depth

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldno
Aware of negative-risk marketsyes
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesOrderFlowAnalyzer consumes CLOB V2 ws_market trade prints (timestamp_ms format) and clob_public book snapshots with all sizes denominated in pUSD. Neg-risk flag from Gamma API amplifies the informed-flow classification threshold for multi-outcome markets where bulk outcome swaps are structural.

API surfaces declared

clob_publicws_marketinternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation2.1.0
schema2
released2026-04-28

Migration history

DateFromToReasonAction taken
2026-04-28v1v2CLOB V2 cutover — pUSD denomination and new book/trade-print field namesTrade-print size and book depth figures updated from USDC.e to pUSD denomination. ws_market subscription updated to CLOB V2 message format (timestamp_ms field; removed legacy nonce from trade payload). No feeRateBps plumbing in this bot.

26. Acceptance Tests

Unit Tests

TestSetupExpected result
Large BUY print with negative imbalance classified as TOXICprint_size=4000 pUSD, imbalance=-0.35, taker_side=BUYObservationReport emitted with flow_class=TOXIC, sampling_applied=false
Routine small print classified as BENIGN and sampled 1/10print_size=50 pUSD, imbalance=0.05, taker_side=BUY; run 10 printsApproximately 1 ObservationReport emitted with flow_class=BENIGN
Neg-risk bulk rebalance classified as INFORMEDneg_risk_flag=true, abs(imbalance)=0.55, print_size=1500 pUSDObservationReport with flow_class=INFORMED
ws_market outage sets flow_class=UNKNOWNDisconnect ws_market for 65 s (> 2× imbalance_window_s=30)ObservationReport with flow_class=UNKNOWN, warnings=['STALE_DATA']
KillSwitch suppresses emissionskillswitch.active=true; TOXIC print arrivesFlow classified as TOXIC; no ObservationReport emitted; KILL_SWITCH_ACTIVE logged
publish_rate_hz hard cap enforced100 TOXIC prints in 1 s, publish_rate_hz=1At most 1 ObservationReport emitted per second per market

Integration Tests

TestExpected result
TOXIC ObservationReport reaches LiquidityGuard and triggers size reductionLiquidityGuard receives flow_class=TOXIC and applies additional size restriction on next OrderIntent
ws_market reconnection resumes correct flow classificationAfter reconnect, first ObservationReport has flow_class determined by fresh prints, not stale UNKNOWN state
Neg-risk market informed flow correctly annotated end-to-endLiquidity-aware strategy receives ObservationReport with neg_risk_flag=true and flow_class=INFORMED

Property Tests

PropertyRequired behaviour
OrderFlowAnalyzer never submits, signs, or modifies any orderAlways true
No ObservationReport emitted when KillSwitch is activeAlways true
flow_class=UNKNOWN set for all markets when ws_market is staleAlways true — stale data must never produce a BENIGN or TOXIC classification

27. Operational Runbook

OrderFlowAnalyzer incidents are usually ws_market disconnections or unusual toxic flow spikes. Disconnections affect all downstream liquidity-aware strategies; page immediately.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
OrderFlowAnalyzerWSDisconnect
OrderFlowAnalyzerToxicFloodSpike
OrderFlowAnalyzerStaleData
OrderFlowAnalyzerHighImbalance

Manual overrides

Healthcheck

GET /internal/health/orderflowanalyzer -> 200 if ws_market connected AND prints_received in last 10 s AND no STALE_DATA for any market. RED if ws_market disconnected > 30 s OR zero prints for > 60 s OR STALE_DATA on > 20% of tracked markets.

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
Unit tests pass for TOXIC, BENIGN, INFORMED classifications and KillSwitch suppressionCI test run100% pass
ws_market integration test: trade print received and classified correctlyIntegration test against Polymarket staging WebSocketPass

Promote to Limited live

GateHow measuredThreshold
p99 end-to-end latency (print received → ObservationReport emitted) < 100 ms over 24 hemitted_at_ms - event.timestamp_ms histogram p99p99 < 100 ms
Neg-risk INFORMED flow classification correct for known neg-risk marketIntegration test with live neg-risk marketPass

Promote to General live

GateHow measuredThreshold
Zero false STALE_DATA warnings during normal ws_market connectivity over 7 daysGrafana stale_data reason_code counter0 false positives
Toxic-flow recall ≥ 80% on labelled back-test dataset of known informed-flow episodesBack-test against labelled trade history≥ 80% recall
KillSwitch suppression: zero ObservationReports when KillSwitch activeIntegration testPass

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