{
  "schema_version": "1.0.0",
  "bot_id": "3.3",
  "bot_name": "Sum-to-One Arb",
  "slug": "sum-to-one-arb",
  "layer": "Strategy",
  "layer_key": "strat",
  "bot_class": "Alpha Strategy",
  "authority": [
    "Trade"
  ],
  "status": "live",
  "readiness": "General live",
  "flagship": true,
  "is_reference": false,
  "public_export": false,
  "identity": {
    "layer": "Strategy",
    "bot_class": "Alpha Strategy",
    "authority": "Trade",
    "runs_before": "Risk guardrail pipeline",
    "runs_after": "Market scanner / opportunity feed",
    "applies_to": "All standard binary (2-outcome YES/NO) CLOB markets where best_ask_YES + best_ask_NO < 1.00 pUSD net of fee buffer",
    "default_mode": "general_live",
    "user_visible": "Advanced details only",
    "developer_owner": "Polytraders core \u2014 Strategy pod"
  },
  "purpose": "Sum-to-One Arb detects when the combined cost of buying both YES and NO tokens on a standard binary market falls below $1.00 pUSD, net of platform fees and a configurable fee buffer. When edge (in basis points) exceeds min_edge_bps, the bot emits a pair of OrderIntents \u2014 one for YES and one for NO \u2014 sized to the smaller of the two books up to max_leg_size_usd. Both legs fill to produce a guaranteed $1.00 settlement regardless of outcome. This is a user-controlled execution tool that exploits CLOB pricing dislocations in standard 2-outcome markets. It does not touch negative-risk or multi-outcome events; those are handled by neg-risk-sum-arb.",
  "why_it_matters": [
    {
      "failure": "Edge is measured incorrectly (ignores fees)",
      "consequence": "Platform fee C\u00d7feeRate\u00d7p\u00d7(1-p) peaks at p=0.5. Entering without fee buffer eats the edge entirely and turns a positive-expectation trade into a loss."
    },
    {
      "failure": "Slippage consumes one leg between intent generation and fill",
      "consequence": "If the YES leg fills but the NO leg moves before submission, the combined cost may exceed $1.00 and the guaranteed profit becomes a guaranteed loss."
    },
    {
      "failure": "Market closes or resolves between the two legs",
      "consequence": "Submitting a GTC order for the second leg on a market that has resolved or halted leaves an open order with no offsetting settlement path."
    },
    {
      "failure": "feeRateBps hardcoded on signed order (V1 pattern)",
      "consequence": "CLOB V2 rejects orders with feeRateBps present. Fees are operator-set at match time. The signed order must not contain this field."
    }
  ],
  "polymarket_inputs": [
    {
      "input": "Best ask on YES and NO token IDs",
      "source": "ws_market (CLOB WebSocket)",
      "required": true,
      "use": "Compute sum_of_asks = best_ask_YES + best_ask_NO and measure edge against 1.00 pUSD."
    },
    {
      "input": "Top-of-book depth for both legs",
      "source": "clob_public",
      "required": true,
      "use": "Size each leg to the minimum of depth available and max_leg_size_usd."
    },
    {
      "input": "Market condition ID, outcome token IDs, negRisk flag",
      "source": "clob_public / internal",
      "required": true,
      "use": "Confirm market is binary (negRisk=false) and not closed or resolved."
    },
    {
      "input": "Platform fee rate for market category",
      "source": "onchain (CTFExchangeV2 fee config)",
      "required": true,
      "use": "Estimate fee drag C\u00d7feeRate\u00d7p\u00d7(1-p); crypto \u22641.80%, sports 0.75%, geopolitical free."
    },
    {
      "input": "Market open/closed/resolved status",
      "source": "clob_public",
      "required": true,
      "use": "Skip closed or resolved markets immediately."
    }
  ],
  "internal_inputs": [
    {
      "input": "KillSwitch active flag",
      "source": "KillSwitch",
      "required": true,
      "use": "Abort intent emission if KillSwitch is active."
    },
    {
      "input": "Builder code bytes32",
      "source": "internal config",
      "required": true,
      "use": "Injected into builder field on every signed V2 order for attribution."
    }
  ],
  "raw_params": [
    "min_edge_bps \u00b7 int",
    "fee_buffer_bps \u00b7 int",
    "slippage_buffer_bps \u00b7 int",
    "max_leg_size_usd \u00b7 int"
  ],
  "parameters": [
    {
      "name": "min_edge_bps",
      "default": 15,
      "warning": 10,
      "hard": 5,
      "controls": "Minimum net edge in basis points (after fee_buffer and slippage_buffer) required before emitting an OrderIntent pair.",
      "why_default_matters": "15 bps provides meaningful margin above fee drag and expected slippage. Below 10 bps the trade is marginal; below 5 bps the bot will not fire regardless of config to prevent fee-negative entries.",
      "threshold_logic": [
        {
          "condition": "edge_bps \u2265 15",
          "action": "EMIT OrderIntent pair"
        },
        {
          "condition": "10 \u2264 edge_bps < 15",
          "action": "WARN SUM_TO_ONE_ARB_EDGE_MARGINAL \u2014 emit with reduced size (50%)"
        },
        {
          "condition": "edge_bps < 5 (hard floor)",
          "action": "SKIP \u2014 SUM_TO_ONE_ARB_NO_EDGE; do not emit"
        }
      ],
      "dev_check": "if edge_bps < params.hard: return skip('SUM_TO_ONE_ARB_NO_EDGE')",
      "user_facing": "This market does not have enough price dislocation to make a profitable trade after accounting for fees and expected slippage."
    },
    {
      "name": "fee_buffer_bps",
      "default": 30,
      "warning": 20,
      "hard": 10,
      "controls": "Additional basis-point buffer subtracted from raw edge to account for platform fees C\u00d7feeRate\u00d7p\u00d7(1-p). Crypto markets peak near 180 bps at p=0.5; geopolitical markets have no fee.",
      "why_default_matters": "30 bps covers worst-case taker fee on crypto markets near p=0.5. Lowering this without adjusting min_edge_bps can make fee-negative trades appear positive.",
      "threshold_logic": [
        {
          "condition": "fee_buffer_bps \u2265 30",
          "action": "Full fee margin applied"
        },
        {
          "condition": "20\u201330",
          "action": "WARN \u2014 reduced fee margin; acceptable only for low-fee market categories"
        },
        {
          "condition": "< 10",
          "action": "Hard floor; config change rejected"
        }
      ],
      "dev_check": "netEdge = rawEdge - params.fee_buffer_bps - params.slippage_buffer_bps",
      "user_facing": ""
    },
    {
      "name": "slippage_buffer_bps",
      "default": 10,
      "warning": 20,
      "hard": 50,
      "controls": "Basis points reserved for expected slippage between generating the two intents and the second leg filling.",
      "why_default_matters": "10 bps covers typical CLOB latency slippage on liquid binary markets. Higher values on illiquid markets help avoid partial-leg scenarios.",
      "threshold_logic": [
        {
          "condition": "\u2264 10 bps",
          "action": "Normal; emit both legs"
        },
        {
          "condition": "20\u201350 bps",
          "action": "WARN \u2014 high slippage config; only appropriate on low-liquidity markets"
        },
        {
          "condition": "> 50 bps",
          "action": "Reject config change"
        }
      ],
      "dev_check": "if params.slippage_buffer_bps > params.hard: raise ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL')",
      "user_facing": ""
    },
    {
      "name": "max_leg_size_usd",
      "default": 500,
      "warning": 750,
      "hard": 1000,
      "controls": "Maximum pUSD size per leg. Both legs are sized to min(depth_available, max_leg_size_usd).",
      "why_default_matters": "500 pUSD per leg keeps individual order impact modest on typical Polymarket binary books. The Risk guardrail pipeline may further reduce this.",
      "threshold_logic": [
        {
          "condition": "\u2264 500 pUSD",
          "action": "Normal leg sizing"
        },
        {
          "condition": "500\u20131000 pUSD",
          "action": "WARN; consider iceberg split via SmartRouter"
        },
        {
          "condition": "> 1000 pUSD",
          "action": "Reject config change \u2014 PARAMETER_CHANGE_REQUIRES_APPROVAL"
        }
      ],
      "dev_check": "legSize = min(depthAvailable, params.max_leg_size_usd, riskConstraints.max_size_usd)",
      "user_facing": "Each side of this trade was sized to fit within available market liquidity."
    }
  ],
  "default_config": {
    "bot_id": "strat.sum_to_one_arb",
    "version": "2.1.0",
    "mode": "general_live",
    "defaults": {
      "min_edge_bps": 15,
      "fee_buffer_bps": 30,
      "slippage_buffer_bps": 10,
      "max_leg_size_usd": 500
    },
    "locked": {
      "min_edge_bps": {
        "min": 5
      },
      "max_leg_size_usd": {
        "max": 1000
      },
      "slippage_buffer_bps": {
        "max": 50
      }
    }
  },
  "implementation_flow": [
    "Check KillSwitch active flag; if active, skip and emit no OrderIntent.",
    "Subscribe to ws_market book updates for all active binary markets (negRisk=false).",
    "On each book tick: compute sum_of_asks = best_ask_YES + best_ask_NO.",
    "Estimate platform fee drag: fee_drag = C * feeRate * p_yes * (1 - p_yes) + C * feeRate * p_no * (1 - p_no) where p = best_ask.",
    "Compute raw_edge_bps = (1.00 - sum_of_asks) * 10000.",
    "Compute net_edge_bps = raw_edge_bps - fee_buffer_bps - slippage_buffer_bps.",
    "If net_edge_bps < min_edge_bps hard floor (5), emit DecisionReport with intent_emitted=false reason SUM_TO_ONE_ARB_NO_EDGE; skip.",
    "If net_edge_bps < min_edge_bps warning threshold (15), emit WARN SUM_TO_ONE_ARB_EDGE_MARGINAL and reduce leg size by 50%.",
    "Fetch top-of-book depth for YES and NO legs from clob_public; set legSize = min(depth_yes, depth_no, max_leg_size_usd).",
    "Confirm market status is open and not resolved (clob_public market endpoint).",
    "Emit OrderIntent YES: market_id, outcome=YES, side=buy, price=best_ask_YES, size_pUSD=legSize, tif=FOK, builder={code: builderCode, fee_bps: 25}.",
    "Emit OrderIntent NO:  market_id, outcome=NO,  side=buy, price=best_ask_NO,  size_pUSD=legSize, tif=FOK, builder={code: builderCode, fee_bps: 25}.",
    "Note: fees are operator-set at match time in V2 \u2014 feeRateBps is NOT present on the signed order.",
    "Emit DecisionReport with intent_emitted=true, edge_bps=net_edge_bps, reason SUM_TO_ONE_ARB_EDGE_PRESENT.",
    "Sampled 1/100 skipped opportunities also emit a DecisionReport with intent_emitted=false for observability."
  ],
  "decision_logic": {
    "approve": "net_edge_bps \u2265 min_edge_bps, market is open, both legs have available depth, KillSwitch is inactive. Bot emits two OrderIntents (YES + NO legs) as a FOK pair.",
    "reshape_required": "Not applicable \u2014 strat bots emit OrderIntents; reshaping is handled by the downstream Risk guardrail pipeline.",
    "reject": "net_edge_bps < 5 bps hard floor; market closed or resolved; KillSwitch active; stale book data (STALE_MARKET_DATA). Emit DecisionReport with intent_emitted=false.",
    "warning_only": "edge_bps between 5 and 15 triggers SUM_TO_ONE_ARB_EDGE_MARGINAL warn and 50% size reduction before emitting."
  },
  "decision_output_schema": "OrderIntent",
  "decision_output_example": {
    "intent_id": "oi_01HX9KZQ7F2A1B",
    "trace_id": "tr_01HX9KZQ7E8VR5",
    "market_id": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
    "outcome": "YES",
    "side": "buy",
    "price": "0.488",
    "size_pUSD": "500.00",
    "tif": "FOK",
    "post_only": false,
    "builder": {
      "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
      "fee_bps": 25
    },
    "negrisk_aware": false,
    "decision": {
      "edge_bps": 18.4,
      "reasons": [
        "SUM_TO_ONE_ARB_EDGE_PRESENT"
      ]
    },
    "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
  },
  "developer_log": {
    "bot_id": "strat.sum_to_one_arb",
    "market_id": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
    "sum_of_asks": 0.9982,
    "raw_edge_bps": 18.0,
    "fee_drag_bps": 12.3,
    "net_edge_bps": 18.4,
    "leg_size_pusd": 500.0,
    "yes_ask": 0.488,
    "no_ask": 0.51,
    "intent_emitted": true,
    "reason": "SUM_TO_ONE_ARB_EDGE_PRESENT",
    "emitted_at_ms": 1746789600000
  },
  "user_explanations": [
    {
      "situation": "Arb trade initiated",
      "message": "Both sides of this market were priced below $1 combined, creating a low-risk opportunity to buy both and collect the $1 settlement. Orders were submitted to capture this pricing gap."
    },
    {
      "situation": "No edge \u2014 no trade",
      "message": "The combined price of YES and NO tokens was not low enough to profit after fees and expected price movement. No order was placed."
    },
    {
      "situation": "Edge marginal \u2014 reduced size",
      "message": "The pricing gap was present but small. Order size was halved to limit exposure given the thin margin."
    },
    {
      "situation": "Market closed or resolved",
      "message": "This market has already resolved or closed. No new orders can be placed."
    }
  ],
  "failure_modes": {
    "main_failure_mode": "The YES leg fills via FOK but the NO leg's price moves before submission, leaving a one-sided exposure with no guaranteed $1 settlement path.",
    "false_positive_risk": "Stale WebSocket book data shows a sum < 1.00 that has already corrected, causing the bot to attempt an arb that no longer exists and incurring fee costs with no edge.",
    "false_negative_risk": "fee_buffer_bps set too conservatively on a geopolitical (zero-fee) market causes valid arb opportunities to be skipped.",
    "safe_fallback": "If book data is stale (last_seen > 5s), emit STALE_MARKET_DATA and skip without emitting any OrderIntent. Never enter a one-legged position deliberately.",
    "required_dependencies": [
      "ws_market book stream (both outcome token IDs)",
      "clob_public market endpoint (status + depth)",
      "onchain fee rate config",
      "KillSwitch active flag",
      "internal builder code"
    ]
  },
  "acceptance_tests": {
    "unit": [
      {
        "test": "Emit pair when sum_of_asks = 0.982 (edge = 18 bps gross)",
        "setup": "best_ask_YES=0.480, best_ask_NO=0.502, fee_buffer=30, slippage_buffer=10, min_edge=15",
        "expected": "Two OrderIntents emitted; net_edge_bps \u2265 15; intent_emitted=true"
      },
      {
        "test": "Skip when sum_of_asks = 0.998 (raw edge = 2 bps, below hard floor 5)",
        "setup": "best_ask_YES=0.499, best_ask_NO=0.499",
        "expected": "No OrderIntent; DecisionReport intent_emitted=false, reason=SUM_TO_ONE_ARB_NO_EDGE"
      },
      {
        "test": "Reduce leg size 50% when edge is marginal (7 bps)",
        "setup": "net_edge_bps=7, min_edge_bps=15",
        "expected": "OrderIntents emitted with size=250 (50% of max_leg_size_usd=500); WARN SUM_TO_ONE_ARB_EDGE_MARGINAL"
      },
      {
        "test": "Skip closed market",
        "setup": "market_status=closed",
        "expected": "No OrderIntent; reason=MARKET_CLOSED"
      },
      {
        "test": "Skip when KillSwitch active",
        "setup": "killswitch.active=true",
        "expected": "No OrderIntent emitted at all"
      },
      {
        "test": "Leg size capped at min(depth, max_leg_size_usd)",
        "setup": "depth_YES=300, depth_NO=450, max_leg_size_usd=500",
        "expected": "Both legs sized at 300 pUSD"
      }
    ],
    "integration": [
      {
        "test": "Full cycle: ws_market tick \u2192 edge detected \u2192 two signed V2 OrderIntents submitted to CLOB",
        "expected": "Both orders contain builder.code (bytes32), no feeRateBps field, EIP-712 domain version '2'"
      },
      {
        "test": "Stale ws_market feed triggers STALE_MARKET_DATA skip",
        "expected": "DecisionReport intent_emitted=false, reason=STALE_MARKET_DATA after 5s feed gap"
      }
    ],
    "property": [
      {
        "property": "Bot never emits a single-leg OrderIntent; always emits YES + NO pair or neither",
        "required": "Always true"
      },
      {
        "property": "net_edge_bps is always \u2265 min_edge_bps hard floor (5) when an OrderIntent is emitted",
        "required": "Always true"
      },
      {
        "property": "feeRateBps field is never present on any emitted OrderIntent",
        "required": "Always true \u2014 V2 fees are operator-set at match time"
      }
    ]
  },
  "checklist_overrides": {},
  "legacy_goal": "Buy YES + NO when they sum below $1 net of fees.",
  "legacy_pm_signals": [
    "WSS book stream on both token IDs of a binary market",
    "Visible depth at top of each leg",
    "Per-order tick size (so legs sum cleanly)"
  ],
  "legacy_external_feeds": [],
  "reporting_groups": [
    "strategy_decision"
  ],
  "network": [
    "polygon"
  ],
  "api_surface": [
    "clob_public",
    "clob_auth",
    "ws_market",
    "onchain",
    "internal"
  ],
  "version": {
    "spec": "2.0.0",
    "implementation": "2.1.0",
    "schema": "2",
    "released": "2026-04-28"
  },
  "migration_history": [
    {
      "date": "2026-04-28",
      "from": "v1 (USDC.e, feeRateBps on signed order)",
      "to": "v2 (pUSD, fees operator-set at match time)",
      "reason": "CLOB V2 cutover",
      "action_taken": "Switched to py-clob-client-v2. Removed feeRateBps from signed-order construction \u2014 fees are now operator-set at match time by CTFExchangeV2. Updated collateral denomination from USDC.e to pUSD. Injected builder field (bytes32) on both YES and NO leg intents. EIP-712 Exchange domain version updated from '1' to '2'."
    }
  ],
  "polymarket_v2_compat": {
    "clob_version": "v2",
    "collateral": "pUSD",
    "eip712_domain_version": "2",
    "builder_code_aware": true,
    "negrisk_aware": false,
    "multichain_ready": false,
    "sdk_used": "py-clob-client-v2",
    "settlement_contract": "CTFExchangeV2",
    "notes": "Standard 2-outcome binary arb only. NegRisk markets are explicitly excluded (negRisk flag check). Both legs carry builder.code bytes32 for attribution; taker fee_bps capped at 25 (well within 100 bps taker max). feeRateBps is not present on any signed order."
  },
  "reference_implementation": {
    "summary": "Subscribes to CLOB WebSocket book updates for binary markets, computes net edge in bps after fee and slippage buffers, and emits a YES+NO OrderIntent pair when edge exceeds the configured minimum.",
    "language_note": "Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. Translate to TS/Python/Go/Rust.",
    "pseudocode": "FUNCTION evaluateMarket(market_id, bookTick):\n  // --- 0. KillSwitch gate ---\n  ks = FETCH internal.killswitch.status\n  IF ks.active:\n    RETURN\n\n  // --- 1. Validate book freshness ---\n  IF isStale(bookTick, maxAgeS=5):\n    EMIT DecisionReport(intent_emitted=false, reason='STALE_MARKET_DATA')\n    RETURN\n\n  // --- 2. Confirm binary market ---\n  market = FETCH clob_public.GET('/markets/' + market_id)\n  IF market.neg_risk OR market.closed OR market.resolved:\n    EMIT DecisionReport(intent_emitted=false, reason='MARKET_CLOSED')\n    RETURN\n\n  // --- 3. Compute edge ---\n  best_ask_YES = bookTick.yes.asks[0].price\n  best_ask_NO  = bookTick.no.asks[0].price\n  sum_of_asks  = best_ask_YES + best_ask_NO\n  raw_edge_bps = (1.00 - sum_of_asks) * 10000\n\n  // --- 4. Deduct fee drag ---\n  feeRate = FETCH onchain.feeConfig(market_id)  // e.g. 0.018 for crypto\n  fee_drag_bps = feeRate * best_ask_YES * (1 - best_ask_YES) * 10000\n               + feeRate * best_ask_NO  * (1 - best_ask_NO)  * 10000\n  net_edge_bps = raw_edge_bps - params.fee_buffer_bps - params.slippage_buffer_bps\n\n  // --- 5. Hard floor check ---\n  IF net_edge_bps < params.min_edge_bps_hard:  // 5 bps absolute floor\n    // sample 1/100 skips for observability\n    IF random() < 0.01:\n      EMIT DecisionReport(intent_emitted=false, reason='SUM_TO_ONE_ARB_NO_EDGE', edge_bps=net_edge_bps)\n    RETURN\n\n  // --- 6. Warning threshold ---\n  legSizeMultiplier = 1.0\n  IF net_edge_bps < params.min_edge_bps:  // 15 bps default\n    WARN('SUM_TO_ONE_ARB_EDGE_MARGINAL')\n    legSizeMultiplier = 0.5\n\n  // --- 7. Size legs ---\n  depth_YES = bookTick.yes.asks[0].size_pusd\n  depth_NO  = bookTick.no.asks[0].size_pusd\n  legSize   = toPusdUnits(min(depth_YES, depth_NO, params.max_leg_size_usd) * legSizeMultiplier)\n\n  // --- 8. Build OrderIntents (V2: no feeRateBps; builder field carries code) ---\n  intentYES = OrderIntent(\n    market_id   = market_id,\n    outcome     = 'YES',\n    side        = 'buy',\n    price       = best_ask_YES,\n    size_pUSD   = legSize,\n    tif         = 'FOK',\n    post_only   = false,\n    builder     = { code: config.builder_code, fee_bps: 25 },\n    negrisk_aware = false\n  )\n  intentNO = OrderIntent(\n    market_id   = market_id,\n    outcome     = 'NO',\n    side        = 'buy',\n    price       = best_ask_NO,\n    size_pUSD   = legSize,\n    tif         = 'FOK',\n    post_only   = false,\n    builder     = { code: config.builder_code, fee_bps: 25 },\n    negrisk_aware = false\n  )\n\n  EMIT intentYES\n  EMIT intentNO\n  EMIT DecisionReport(\n    intent_emitted = true,\n    edge_bps       = net_edge_bps,\n    reasons        = ['SUM_TO_ONE_ARB_EDGE_PRESENT']\n  )",
    "sdk_calls": [
      "fetchClobPublic('/markets/' + market_id)",
      "ws_market.subscribe('book', [yes_token_id, no_token_id])",
      "onchain.feeConfig(market_id)",
      "toPusdUnits(rawFloat)",
      "buildOrderTypedData(orderParams, { name: 'CTFExchange', version: '2', chainId: 137 })",
      "internal.killswitch.status()",
      "internal.builder_code"
    ],
    "complexity": "O(1) per market book tick"
  },
  "wire_examples": {
    "input": [
      {
        "label": "WebSocket book tick \u2014 binary market with edge",
        "source": "ws_market",
        "payload": {
          "market_id": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
          "yes_token_id": "0xabc001",
          "no_token_id": "0xabc002",
          "yes_best_ask": "0.488",
          "no_best_ask": "0.510",
          "sum_of_asks": "0.998",
          "depth_YES_pusd": "620.00",
          "depth_NO_pusd": "540.00",
          "received_at_ms": 1746789600000
        }
      }
    ],
    "output": [
      {
        "label": "OrderIntent \u2014 YES leg (FOK, builder-attributed)",
        "payload": {
          "intent_id": "oi_01HX9KZQ7F2A1B",
          "trace_id": "tr_01HX9KZQ7E8VR5",
          "market_id": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
          "outcome": "YES",
          "side": "buy",
          "price": "0.488",
          "size_pUSD": "500.00",
          "tif": "FOK",
          "post_only": false,
          "builder": {
            "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
            "fee_bps": 25
          },
          "negrisk_aware": false,
          "decision": {
            "edge_bps": 18.4,
            "reasons": [
              "SUM_TO_ONE_ARB_EDGE_PRESENT"
            ]
          },
          "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
        }
      },
      {
        "label": "OrderIntent \u2014 NO leg (FOK, builder-attributed)",
        "payload": {
          "intent_id": "oi_01HX9KZQ7F2B2C",
          "trace_id": "tr_01HX9KZQ7E8VR5",
          "market_id": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
          "outcome": "NO",
          "side": "buy",
          "price": "0.510",
          "size_pUSD": "500.00",
          "tif": "FOK",
          "post_only": false,
          "builder": {
            "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
            "fee_bps": 25
          },
          "negrisk_aware": false,
          "decision": {
            "edge_bps": 18.4,
            "reasons": [
              "SUM_TO_ONE_ARB_EDGE_PRESENT"
            ]
          },
          "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
        }
      },
      {
        "label": "DecisionReport \u2014 skipped (no edge), sampled 1/100",
        "payload": {
          "report_id": "dr_01HX9KZQ7F9ZZZ",
          "bot_id": "strat.sum_to_one_arb",
          "market_id": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
          "intent_emitted": false,
          "edge_bps": 1.2,
          "reasons": [
            "SUM_TO_ONE_ARB_NO_EDGE"
          ],
          "sampled": true,
          "evaluated_at_ms": 1746789601000
        }
      }
    ]
  },
  "reason_codes": [
    {
      "code": "SUM_TO_ONE_ARB_EDGE_PRESENT",
      "severity": "INFO",
      "meaning": "Net edge after fee and slippage buffers meets or exceeds min_edge_bps. OrderIntent pair emitted.",
      "action": "Emit YES + NO OrderIntents.",
      "user_message": "A pricing gap was detected and orders were placed to capture it."
    },
    {
      "code": "SUM_TO_ONE_ARB_NO_EDGE",
      "severity": "INFO",
      "meaning": "Net edge is below the 5 bps absolute hard floor. No trade opportunity exists after fees.",
      "action": "Skip; emit DecisionReport with intent_emitted=false (sampled 1/100).",
      "user_message": "No pricing gap was large enough to trade after accounting for fees."
    },
    {
      "code": "SUM_TO_ONE_ARB_EDGE_MARGINAL",
      "severity": "WARN",
      "meaning": "Edge is between the hard floor (5 bps) and the warning threshold (15 bps). Trade is marginal.",
      "action": "Emit OrderIntents at 50% leg size; log warning.",
      "user_message": "A small pricing gap was detected. A reduced-size order was placed."
    },
    {
      "code": "SUM_TO_ONE_ARB_DEPTH_INSUFFICIENT",
      "severity": "WARN",
      "meaning": "Top-of-book depth on one or both legs is below the minimum viable trade size (10 pUSD).",
      "action": "Skip; emit DecisionReport with intent_emitted=false.",
      "user_message": "Not enough liquidity on both sides to place a matched trade."
    },
    {
      "code": "STALE_MARKET_DATA",
      "severity": "HARD_REJECT",
      "meaning": "Book snapshot or market metadata is older than 5 seconds.",
      "action": "Skip; no OrderIntent emitted.",
      "user_message": "Market data was too old to act on safely."
    },
    {
      "code": "MARKET_CLOSED",
      "severity": "HARD_REJECT",
      "meaning": "Market is closed, resolved, or has neg_risk=true (wrong strategy).",
      "action": "Skip immediately; no OrderIntent emitted.",
      "user_message": "This market is no longer open for trading."
    },
    {
      "code": "KILL_SWITCH_ACTIVE",
      "severity": "HARD_REJECT",
      "meaning": "Global kill switch is active.",
      "action": "Skip all markets; no OrderIntents emitted.",
      "user_message": "Trading is currently paused."
    },
    {
      "code": "PARAMETER_CHANGE_REQUIRES_APPROVAL",
      "severity": "HARD_REJECT",
      "meaning": "A config change would push a parameter past its locked hard limit (e.g. max_leg_size_usd > 1000).",
      "action": "Reject config change; do not apply.",
      "user_message": ""
    }
  ],
  "metrics": {
    "emitted": [
      {
        "name": "polytraders_strat_sumtoonearb_decisions_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "verdict",
          "reason_code"
        ],
        "meaning": "Total evaluation cycles, split by intent_emitted (true/false) and reason code."
      },
      {
        "name": "polytraders_strat_sumtoonearb_edge_bps",
        "type": "histogram",
        "unit": "basis_points",
        "labels": [],
        "meaning": "Distribution of net edge in bps across all evaluated markets, including skipped opportunities."
      },
      {
        "name": "polytraders_strat_sumtoonearb_leg_size_pusd",
        "type": "histogram",
        "unit": "pusd",
        "labels": [
          "outcome"
        ],
        "meaning": "Distribution of emitted leg sizes in pUSD per YES/NO outcome."
      },
      {
        "name": "polytraders_strat_sumtoonearb_intents_emitted_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "outcome"
        ],
        "meaning": "Total OrderIntents emitted, split by outcome leg (YES, NO)."
      },
      {
        "name": "polytraders_strat_sumtoonearb_eval_latency_ms",
        "type": "histogram",
        "unit": "milliseconds",
        "labels": [],
        "meaning": "Wall-clock time from book tick receipt to OrderIntent emit."
      },
      {
        "name": "polytraders_strat_sumtoonearb_stale_feed_total",
        "type": "counter",
        "unit": "count",
        "labels": [],
        "meaning": "Number of evaluation cycles skipped due to stale ws_market feed."
      }
    ],
    "alerts": [
      {
        "name": "SumToOneArbHighStaleFeed",
        "condition": "rate(polytraders_strat_sumtoonearb_stale_feed_total[5m]) > 0.1",
        "severity": "warn",
        "runbook": "#runbook-sumtoonearb-stale-feed"
      },
      {
        "name": "SumToOneArbNoEdgeStreak",
        "condition": "rate(polytraders_strat_sumtoonearb_decisions_total{verdict='false'}[15m]) / rate(polytraders_strat_sumtoonearb_decisions_total[15m]) > 0.99",
        "severity": "warn",
        "runbook": "#runbook-sumtoonearb-no-edge"
      },
      {
        "name": "SumToOneArbHighLatency",
        "condition": "histogram_quantile(0.99, rate(polytraders_strat_sumtoonearb_eval_latency_ms_bucket[5m])) > 150",
        "severity": "warn",
        "runbook": "#runbook-sumtoonearb-latency"
      },
      {
        "name": "SumToOneArbKillSwitchBlocking",
        "condition": "rate(polytraders_strat_sumtoonearb_decisions_total{reason_code='KILL_SWITCH_ACTIVE'}[1m]) > 0",
        "severity": "page",
        "runbook": "#runbook-killswitch"
      }
    ],
    "dashboards": [
      "Grafana \u2014 Strategy / SumToOneArb edge distribution",
      "Grafana \u2014 Strategy / SumToOneArb intent throughput and leg sizing"
    ],
    "log_level": "info"
  },
  "state": {
    "store": "redis",
    "shape": "Per-market last-seen book tick (yes_ask, no_ask, sum, timestamp_ms) and last DecisionReport outcome; keyed by market_id",
    "ttl": "30s",
    "recovery": "On cold start, state is empty; first book tick per market triggers fresh evaluation without any stale-state comparison.",
    "size_estimate": "~200 bytes per active binary market; typically < 5 MB total across all monitored markets"
  },
  "concurrency": {
    "execution_model": "single-threaded event loop",
    "max_in_flight": 50,
    "idempotency_key": "intent_id",
    "timeout_ms": 150,
    "backpressure": "drop oldest pending tick per market_id when queue depth > 10",
    "locking": "per-market_id mutex for Redis state write"
  },
  "dependencies": {
    "depends_on": [
      {
        "bot_id": "risk.kill_switch",
        "why": "Checked first; blocks all intent emission when active."
      }
    ],
    "emits_to": [
      {
        "bot_id": "risk.portfolio_guard",
        "what": "OrderIntents (YES + NO legs) for risk guardrail evaluation before SmartRouter."
      },
      {
        "bot_id": "gov.builder_attribution",
        "what": "builder.code bytes32 on every OrderIntent for attribution tracking."
      }
    ],
    "sibling": [],
    "external": [
      {
        "service": "Polymarket CLOB v2 (public)",
        "sla": "99.9% (Polymarket-published)",
        "fallback": "Skip evaluation cycle; emit STALE_MARKET_DATA DecisionReport."
      },
      {
        "service": "Polymarket CLOB WebSocket (ws_market)",
        "sla": "best-effort",
        "fallback": "On disconnect, fall back to REST polling at 1s interval; emit STALE_MARKET_DATA if gap > 5s."
      },
      {
        "service": "CTFExchangeV2 (onchain fee config)",
        "sla": "Polygon RPC SLA",
        "fallback": "Use cached fee rate; if cache > 60s old, emit STALE_MARKET_DATA."
      }
    ]
  },
  "security_surfaces": {
    "signs_orders": true,
    "private_key_access": "signing-only",
    "abuse_vectors": [
      "Front-running: emitting YES leg publicly before NO leg allows adversaries to move the NO book",
      "Replaying a signed OrderIntent after the market's arb window has closed",
      "Injecting a modified builder.code to redirect attribution fees"
    ],
    "mitigations": [
      "Both legs use FOK (Fill-or-Kill); a partial fill on one leg is rejected rather than leaving a naked position",
      "V2 order timestamp(ms) field invalidates replays outside the exchange's acceptance window",
      "Builder code is read from immutable internal config; not user-supplied",
      "per-intent_id deduplication within 24h prevents replay"
    ],
    "contract_calls": [
      {
        "contract": "CTFExchangeV2",
        "function": "matchOrders",
        "purpose": "Settlement of YES and NO token purchases; produces $1.00 pUSD on resolution regardless of outcome."
      }
    ]
  },
  "failure_injection": [
    {
      "scenario": "STALE_WS_FEED",
      "how_to_inject": "Pause ws_market WebSocket; let last_seen age beyond 5s",
      "expected_behaviour": "Bot emits STALE_MARKET_DATA DecisionReport and skips all evaluations until feed recovers",
      "recovery": "Automatic on WebSocket reconnect within one evaluation cycle."
    },
    {
      "scenario": "NO_EDGE_MARKET",
      "how_to_inject": "Set mock yes_ask=0.500, no_ask=0.502 (sum=1.002 > 1.00)",
      "expected_behaviour": "SUM_TO_ONE_ARB_NO_EDGE; no OrderIntent; DecisionReport sampled 1/100",
      "recovery": "Automatic when book rebalances."
    },
    {
      "scenario": "FOK_PARTIAL_FILL",
      "how_to_inject": "Mock CLOB to reject second FOK leg after first leg fills",
      "expected_behaviour": "NO leg OrderIntent is rejected by CLOB; SmartRouter does not retry; one-sided exposure is flagged to risk pipeline",
      "recovery": "Risk pipeline detects naked YES position and flags for manual review."
    },
    {
      "scenario": "KILL_SWITCH_ON",
      "how_to_inject": "Set killswitch.active=true",
      "expected_behaviour": "No OrderIntents emitted for any market",
      "recovery": "Automatic on manual KillSwitch reset."
    },
    {
      "scenario": "FEE_CONFIG_STALE",
      "how_to_inject": "Block onchain RPC; let fee cache exceed 60s TTL",
      "expected_behaviour": "STALE_MARKET_DATA; no OrderIntents until cache refreshes",
      "recovery": "Automatic when RPC is restored."
    }
  ],
  "runbook": {
    "summary": "SumToOneArb incidents are usually stale WebSocket feed, no-edge market conditions, or one-sided FOK fills. The last is the most operationally significant and requires manual risk review.",
    "oncall_actions": [
      {
        "alert": "SumToOneArbHighStaleFeed",
        "first_action": "Check ws_market WebSocket connectivity and reconnect loop logs.",
        "escalate_to": "Infra on-call if disconnected > 2 minutes."
      },
      {
        "alert": "SumToOneArbNoEdgeStreak",
        "first_action": "Review market conditions; confirm CLOB spreads are not abnormally wide.",
        "escalate_to": "Strategy pod lead if streak persists > 30 minutes during active trading hours."
      },
      {
        "alert": "SumToOneArbHighLatency",
        "first_action": "Check Redis state write latency and CLOB REST fallback polling rate.",
        "escalate_to": "Exec pod lead if p99 > 300ms sustained."
      },
      {
        "alert": "SumToOneArbKillSwitchBlocking",
        "first_action": "Confirm KillSwitch activation was intentional. Check KillSwitch runbook.",
        "escalate_to": "Risk pod lead immediately."
      }
    ],
    "manual_overrides": [
      {
        "name": "force_skip_market",
        "how": "Add market_id to config.excluded_markets list",
        "when": "Market shows anomalous pricing that is not a genuine arb (e.g. oracle dispute in progress)."
      },
      {
        "name": "pause_bot",
        "how": "polytraders bot pause strat.sum_to_one_arb",
        "when": "Elevated one-sided fill rate or during platform-wide incidents."
      }
    ],
    "healthcheck": "GET /internal/health/sum-to-one-arb -> 200 if ws_market feed last_seen < 5s, Redis reachable, KillSwitch inactive, and at least one market evaluated in last 60s."
  },
  "promotion_gates": {
    "to_shadow": [
      {
        "gate": "All unit tests pass including pair-emission invariant",
        "how_measured": "CI test run",
        "threshold": "100% pass"
      },
      {
        "gate": "feeRateBps absence verified in integration test signed-order payload",
        "how_measured": "Integration test asserting V2 order schema",
        "threshold": "Pass"
      }
    ],
    "to_limited_live": [
      {
        "gate": "p99 eval latency < 150ms over 24h",
        "how_measured": "polytraders_strat_sumtoonearb_eval_latency_ms histogram",
        "threshold": "p99 < 150ms"
      },
      {
        "gate": "Zero one-sided fill incidents in shadow mode over 48h",
        "how_measured": "Fill reconciliation report",
        "threshold": "0 incidents"
      }
    ],
    "to_general_live": [
      {
        "gate": "E2E: edge detected \u2192 two signed V2 OrderIntents \u2192 both FOK fills confirmed on Polygon testnet",
        "how_measured": "E2E test",
        "threshold": "Pass"
      },
      {
        "gate": "DecisionReport sampling verified: 1/100 skips emit report",
        "how_measured": "Integration test",
        "threshold": "Pass"
      }
    ]
  },
  "reporting": {
    "emits_kinds": [
      "DecisionReport"
    ],
    "topics": [
      "polytraders.reports.strategy"
    ],
    "cadence": "every-event",
    "retention_class": "2y",
    "sampling_rule": "emit-every for emitted intents; sample-1/100 for considered-but-skipped",
    "bus_failure_action": "fail-closed",
    "user_visible": "summary-only",
    "consumes_kinds": []
  },
  "capital_impact": "Direct",
  "v3_status": {
    "phase": 8,
    "phase_name": "Additional strategies",
    "docs": {
      "done": 27,
      "total": 27,
      "state": "done"
    },
    "impl": {
      "done": 0,
      "total": 15,
      "state": "pending"
    },
    "runtime": {
      "done": 0,
      "total": 8,
      "state": "pending"
    },
    "overall": "pending"
  }
}