{
  "schema_version": "1.0.0",
  "bot_id": "3.1",
  "bot_name": "Maker-Tight",
  "slug": "maker-tight",
  "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": "Liquid binary markets where 24h volume \u2265 min_volume_24h_usd, spread > min_spread_bps, and no toxic order flow is detected",
    "default_mode": "general_live",
    "user_visible": "Advanced details only",
    "developer_owner": "Polytraders core \u2014 Strategy pod"
  },
  "purpose": "Maker-Tight is a passive market-making strategy that posts resting maker orders inside the current touch when market conditions are benign (low volatility, adequate volume, clean order flow). It quotes both YES and NO sides at a configurable edge (edge_bps) from the mid-price, sized to clip_size_usd. When spread is too tight to earn the edge, or when order-flow imbalance signals toxic flow, the bot skips the cycle and cancels any open quotes. This is a user-controlled execution tool for posting maker liquidity on predictive-event markets \u2014 not a directional betting strategy. Maker rebates (20\u201325% of platform fees, paid in pUSD) contribute to the overall return profile.",
  "why_it_matters": [
    {
      "failure": "Quoting into toxic flow without detection",
      "consequence": "Informed takers repeatedly fill the maker side immediately after posting, resulting in adverse selection losses that erode the quoted spread over time.",
      "worked_example": {
        "setup": "Market 0x33b shows a 0.62/0.63 book with 4,500 pUSD depth on each side. Spread is 1 tick. Strategy is configured for `min_edge_bps=8, ladder_levels=3, per_level_size=400 pUSD`.",
        "without_bot": "Without a tight-market specialist, the team's only maker is the generic ladder bot, which is configured for 3-tick spreads. It quotes inside 0.61/0.64 \u2014 outside the actual market \u2014 and never gets a fill. The rebate budget for the day goes unused.",
        "with_bot": "maker_tight quotes 0.621/0.629 across 3 levels per side. It earns the ladder rebate on the 12 fills that day, with Risk capping per-market inventory at 1,200 pUSD. Every quote carries a builderCode so the rebate is attributed correctly."
      }
    },
    {
      "failure": "Posting quotes when spread < min_spread_bps",
      "consequence": "Quoting inside an already-tight spread earns no edge; the maker rebate does not cover the risk of being adversely selected on a fast-moving market."
    },
    {
      "failure": "Inventory skew not applied",
      "consequence": "Without skewing quotes toward the short side as inventory builds, the bot accumulates a directional position that violates the passive-maker intent and increases settlement risk."
    },
    {
      "failure": "feeRateBps hardcoded on signed maker order (V1 pattern)",
      "consequence": "CLOB V2 rejects orders with feeRateBps. Maker fees are operator-set at match time; the signed order must not contain this field. Maker fee_bps is capped at 50 bps."
    }
  ],
  "polymarket_inputs": [
    {
      "input": "Live order book \u2014 best bid, best ask, mid-price",
      "source": "ws_market (CLOB WebSocket)",
      "required": true,
      "use": "Compute current spread (ask - bid) and mid-price to determine quoting levels."
    },
    {
      "input": "Order-flow imbalance over last N seconds",
      "source": "ws_market (trade tape)",
      "required": true,
      "use": "Detect toxic flow: if taker-initiated buy volume >> sell volume, the market is directional and maker posting is paused."
    },
    {
      "input": "24h trading volume in pUSD",
      "source": "clob_public",
      "required": true,
      "use": "Only post on markets meeting min_volume_24h_usd; illiquid markets have wide spreads that attract toxic flow."
    },
    {
      "input": "Running inventory position on each market",
      "source": "clob_auth (open positions)",
      "required": true,
      "use": "Compute inventory skew factor; shift bid and ask prices toward the direction that reduces open inventory."
    }
  ],
  "internal_inputs": [
    {
      "input": "KillSwitch active flag",
      "source": "KillSwitch",
      "required": true,
      "use": "Cancel all open maker quotes and emit no new OrderIntents if KillSwitch is active."
    },
    {
      "input": "Builder code bytes32",
      "source": "internal config",
      "required": true,
      "use": "Injected into builder field on every maker OrderIntent for attribution. Maker fee_bps \u2264 50."
    }
  ],
  "raw_params": [
    "edge_bps \u00b7 int",
    "clip_size_usd \u00b7 int",
    "inventory_skew_factor \u00b7 0\u20131",
    "min_volume_24h_usd \u00b7 int (default 250000)"
  ],
  "parameters": [
    {
      "name": "edge_bps",
      "default": 10,
      "warning": 6,
      "hard": 3,
      "controls": "Minimum edge in basis points from mid-price at which the bot will post a quote. Quotes are placed at mid \u00b1 edge_bps/2.",
      "why_default_matters": "10 bps covers expected adverse selection on benign low-vol markets. Below 6 bps the edge is unlikely to persist long enough to fill; below 3 bps (hard floor) the bot will not quote regardless of config.",
      "threshold_logic": [
        {
          "condition": "edge_bps \u2265 10",
          "action": "Post quotes at mid \u00b1 edge_bps/2"
        },
        {
          "condition": "6 \u2264 edge_bps < 10",
          "action": "WARN MAKER_TIGHT_EDGE_MARGINAL; post at reduced clip size (50%)"
        },
        {
          "condition": "edge_bps < 3 (hard floor)",
          "action": "SKIP \u2014 MAKER_TIGHT_SPREAD_TOO_TIGHT"
        }
      ],
      "dev_check": "if edge_bps < params.hard: return skip('MAKER_TIGHT_SPREAD_TOO_TIGHT')",
      "user_facing": "The market spread is too narrow to post competitively. Quoting was skipped for this cycle."
    },
    {
      "name": "clip_size_usd",
      "default": 200,
      "warning": 400,
      "hard": 600,
      "controls": "Size in pUSD of each individual maker quote (bid or ask). Both sides are quoted at this size unless inventory skew applies.",
      "why_default_matters": "200 pUSD per side keeps individual quote impact small on typical Polymarket binary books. The Risk guardrail pipeline may further reduce this.",
      "threshold_logic": [
        {
          "condition": "\u2264 200 pUSD",
          "action": "Normal single-side quote"
        },
        {
          "condition": "200\u2013600 pUSD",
          "action": "WARN; risk guardrail will reshape if above portfolio budget"
        },
        {
          "condition": "> 600 pUSD",
          "action": "Reject config change \u2014 PARAMETER_CHANGE_REQUIRES_APPROVAL"
        }
      ],
      "dev_check": "if params.clip_size_usd > params.hard: raise ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL')",
      "user_facing": "Each quote was sized to keep the order visible but not dominant on the market."
    },
    {
      "name": "inventory_skew_factor",
      "default": 0.3,
      "warning": 0.6,
      "hard": 0.9,
      "controls": "Coefficient applied to shift quote prices toward reducing open inventory. At 0.3, a long YES position shifts the YES ask down and the YES bid up proportionally.",
      "why_default_matters": "0.3 provides a moderate skew that discourages further inventory accumulation without widening quotes so aggressively that they never fill.",
      "threshold_logic": [
        {
          "condition": "\u2264 0.3",
          "action": "Normal skew"
        },
        {
          "condition": "0.3\u20130.6",
          "action": "WARN MAKER_TIGHT_HIGH_SKEW; inventory is being aggressively managed"
        },
        {
          "condition": "> 0.9",
          "action": "Hard ceiling; config rejected"
        }
      ],
      "dev_check": "bidPrice = mid - edge_half - skew * inventory_ratio; askPrice = mid + edge_half - skew * inventory_ratio",
      "user_facing": "Quote prices were adjusted to help balance the current position."
    },
    {
      "name": "min_volume_24h_usd",
      "default": 250000,
      "warning": 150000,
      "hard": 50000,
      "controls": "Minimum 24h trading volume (in pUSD) required for this strategy to post on a market. Below this threshold the spread is likely structural rather than temporary.",
      "why_default_matters": "Markets with < $250K daily volume tend to have wide, illiquid spreads that attract informed takers rather than uninformed flow. Posting on them is adverse-selection-prone.",
      "threshold_logic": [
        {
          "condition": "volume_24h \u2265 250,000 pUSD",
          "action": "Eligible to post"
        },
        {
          "condition": "150,000\u2013250,000 pUSD",
          "action": "WARN MAKER_TIGHT_LOW_VOLUME; post at 50% clip size"
        },
        {
          "condition": "< 50,000 pUSD (hard floor)",
          "action": "SKIP \u2014 MAKER_TIGHT_SPREAD_TOO_TIGHT (volume too low)"
        }
      ],
      "dev_check": "if volume_24h < params.hard: return skip('MAKER_TIGHT_SPREAD_TOO_TIGHT')",
      "user_facing": "This market does not have enough daily trading activity for safe maker quoting."
    }
  ],
  "default_config": {
    "bot_id": "strat.maker_tight",
    "version": "2.1.0",
    "mode": "general_live",
    "defaults": {
      "edge_bps": 10,
      "clip_size_usd": 200,
      "inventory_skew_factor": 0.3,
      "min_volume_24h_usd": 250000
    },
    "locked": {
      "edge_bps": {
        "min": 3
      },
      "clip_size_usd": {
        "max": 600
      },
      "inventory_skew_factor": {
        "max": 0.9
      }
    }
  },
  "implementation_flow": [
    "Check KillSwitch active flag; if active, cancel all open maker quotes and emit no new OrderIntents.",
    "Subscribe to ws_market book and trade-tape streams for all eligible markets.",
    "Filter markets: 24h volume \u2265 min_volume_24h_usd (clob_public). Skip low-volume markets.",
    "On each book tick: compute mid = (best_bid + best_ask) / 2; spread_bps = (best_ask - best_bid) / mid * 10000.",
    "If spread_bps < 2 * edge_bps: skip cycle \u2014 spread too tight to earn edge; emit DecisionReport intent_emitted=false MAKER_TIGHT_SPREAD_TOO_TIGHT (sampled 1/100).",
    "Compute order-flow imbalance over last 30s from trade tape. If taker-buy_volume / total_volume > toxic_flow_threshold: skip cycle \u2014 MAKER_TIGHT_TOXIC_FLOW_DETECTED.",
    "Fetch open YES/NO position from clob_auth to compute inventory_ratio = current_position / max_inventory.",
    "Compute skewed bid/ask prices: bidPrice = mid - edge_bps/2/10000 - skew * inventory_ratio; askPrice = mid + edge_bps/2/10000 - skew * inventory_ratio.",
    "Apply edge_bps warning threshold: if edge_bps < 6, reduce clip to 50%.",
    "Emit OrderIntent YES (post_only=true, side=buy, price=bidPrice, tif=GTC, builder={code, fee_bps: 30}).",
    "Emit OrderIntent NO  (post_only=true, side=buy, price=ask_of_NO=1-bidPrice, tif=GTC, builder={code, fee_bps: 30}).",
    "Note: fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order. Maker fee_bps \u2264 50.",
    "Emit DecisionReport with intent_emitted=true, edge_bps, inventory_ratio, skew applied."
  ],
  "decision_logic": {
    "approve": "spread_bps > 2 * edge_bps, volume_24h \u2265 min_volume_24h_usd, no toxic flow detected, KillSwitch inactive, inventory_ratio < 1.0. Emit bid + ask OrderIntents as GTC post_only maker orders.",
    "reshape_required": "Not applicable \u2014 strat bots emit OrderIntents; reshaping is handled downstream by the Risk guardrail pipeline.",
    "reject": "spread_bps < 2 * edge_bps hard floor; volume too low; toxic flow detected; KillSwitch active; stale feed. Cancel open quotes and emit DecisionReport intent_emitted=false.",
    "warning_only": "edge_bps between 3 and 6, or volume between 50K and 150K pUSD, triggers warning and 50% size reduction before emitting."
  },
  "decision_output_schema": "OrderIntent",
  "decision_output_example": {
    "intent_id": "oi_01HX9MKRT1A4Z1B",
    "trace_id": "tr_01HX9MKRT1A4VR5",
    "market_id": "0xcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890cd",
    "outcome": "YES",
    "side": "buy",
    "price": "0.619",
    "size_pUSD": "200.00",
    "tif": "GTC",
    "post_only": true,
    "builder": {
      "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
      "fee_bps": 30
    },
    "negrisk_aware": false,
    "decision": {
      "edge_bps": 10.0,
      "inventory_ratio": 0.12,
      "skew_applied": true,
      "reasons": [
        "MAKER_TIGHT_QUOTING"
      ]
    },
    "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
  },
  "developer_log": {
    "bot_id": "strat.maker_tight",
    "market_id": "0xcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890cd",
    "mid": 0.624,
    "best_bid": 0.619,
    "best_ask": 0.629,
    "spread_bps": 16.0,
    "edge_bps": 10.0,
    "inventory_ratio": 0.12,
    "skew_applied": true,
    "bid_price": 0.619,
    "ask_price": 0.629,
    "clip_size_pusd": 200.0,
    "toxic_flow": false,
    "intent_emitted": true,
    "reason": "MAKER_TIGHT_QUOTING",
    "emitted_at_ms": 1746789800000
  },
  "user_explanations": [
    {
      "situation": "Maker quote posted",
      "message": "A resting order was placed inside the market's current spread to provide liquidity. It will fill if a taker crosses the price."
    },
    {
      "situation": "Quote skipped \u2014 spread too tight",
      "message": "The market's current bid-ask spread is too narrow to post a quote at a worthwhile price. Quoting was skipped for this cycle."
    },
    {
      "situation": "Quote skipped \u2014 directional flow detected",
      "message": "Recent trading activity showed a strong directional pattern. Posting a maker order in these conditions increases the risk of being traded against by better-informed participants."
    },
    {
      "situation": "Quotes adjusted for inventory balance",
      "message": "Quote prices were shifted slightly to encourage trades that bring the current position back toward neutral."
    }
  ],
  "failure_modes": {
    "main_failure_mode": "Adverse selection: quoted maker orders fill repeatedly immediately after posting because of undetected informed flow, resulting in a directional inventory that cannot be profitably unwound.",
    "false_positive_risk": "Toxic flow detector is overly sensitive, triggering MAKER_TIGHT_TOXIC_FLOW_DETECTED on normal short-term imbalances and causing the bot to skip valid quoting opportunities.",
    "false_negative_risk": "Order-flow imbalance window too long (e.g. 60s) causes the bot to miss a brief but high-intensity informed-flow episode, posting quotes that fill immediately at a loss.",
    "safe_fallback": "If book data is stale (last_seen > 5s) or clob_auth position read fails, cancel all open quotes and emit no new OrderIntents. Fail closed on missing inventory state.",
    "required_dependencies": [
      "ws_market book and trade-tape stream",
      "clob_public 24h volume",
      "clob_auth open positions",
      "KillSwitch active flag",
      "internal builder code"
    ]
  },
  "acceptance_tests": {
    "unit": [
      {
        "test": "Post bid+ask when spread > 2*edge_bps and volume > min_volume",
        "setup": "spread=16bps, edge=10bps, volume_24h=500000, toxic_flow=false",
        "expected": "Two OrderIntents (bid + ask) emitted with post_only=true"
      },
      {
        "test": "Skip when spread < 2*edge_bps hard floor (spread = 4bps, edge = 3bps)",
        "setup": "spread=4bps, edge=3bps",
        "expected": "No OrderIntents; DecisionReport intent_emitted=false, reason=MAKER_TIGHT_SPREAD_TOO_TIGHT"
      },
      {
        "test": "Skip when toxic flow detected",
        "setup": "taker_buy_ratio=0.82 (> 0.75 threshold)",
        "expected": "No OrderIntents; reason=MAKER_TIGHT_TOXIC_FLOW_DETECTED"
      },
      {
        "test": "Apply inventory skew correctly",
        "setup": "mid=0.624, edge_bps=10, inventory_ratio=0.4, skew_factor=0.3",
        "expected": "bidPrice = 0.624 - 0.005 - 0.3*0.4*0.001 (skew reduces bid); askPrice shifted symmetrically"
      },
      {
        "test": "Reduce clip size when edge is marginal (6 bps warning threshold)",
        "setup": "edge_bps=6, clip_size_usd=200",
        "expected": "OrderIntents emitted with size=100; WARN MAKER_TIGHT_EDGE_MARGINAL"
      },
      {
        "test": "Skip when KillSwitch active; cancel open quotes",
        "setup": "killswitch.active=true, open_quotes=[quote1, quote2]",
        "expected": "No new OrderIntents; cancellation of open quotes emitted"
      }
    ],
    "integration": [
      {
        "test": "Full cycle: ws_market tick \u2192 spread check \u2192 two signed V2 maker OrderIntents submitted to CLOB as post_only=true",
        "expected": "Both orders contain builder.code (bytes32), no feeRateBps, post_only=true, EIP-712 domain version '2'"
      },
      {
        "test": "Sampled 1/100 skip events generate DecisionReport with intent_emitted=false",
        "expected": "Exactly ~1% of SPREAD_TOO_TIGHT skips emit a DecisionReport"
      }
    ],
    "property": [
      {
        "property": "post_only=true on every maker OrderIntent; bot never submits taker orders",
        "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"
      },
      {
        "property": "Builder fee_bps is always \u2264 50 (maker max)",
        "required": "Always true"
      }
    ]
  },
  "checklist_overrides": {},
  "legacy_goal": "Two-sided passive market-making on liquid markets.",
  "legacy_pm_signals": [
    "Mid price (top-bid / top-ask midpoint)",
    "Inventory skew on the running position",
    "Realised vs. quoted spread (rolling)",
    "Order-flow imbalance over last N seconds"
  ],
  "legacy_external_feeds": [],
  "reporting_groups": [
    "strategy_decision"
  ],
  "network": [
    "polygon"
  ],
  "api_surface": [
    "clob_public",
    "clob_auth",
    "ws_market",
    "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, maker fee_bps \u2264 50)",
      "reason": "CLOB V2 cutover",
      "action_taken": "Switched to py-clob-client-v2. Removed feeRateBps from all signed maker order construction \u2014 fees are operator-set at match time by CTFExchangeV2. Updated collateral denomination to pUSD. Injected builder field (bytes32) on every OrderIntent. Confirmed maker fee_bps cap of 50 bps. EIP-712 Exchange domain version updated from '1' to '2'. Verified maker rebate flow (20\u201325% of platform fee, paid in pUSD)."
    }
  ],
  "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": "All maker orders use post_only=true to qualify for maker rebates (20\u201325% of platform fees, paid in pUSD). builder.fee_bps is capped at 30 bps, well within the V2 maker maximum of 50 bps. feeRateBps is not present on any signed order \u2014 operator-set at match time."
  },
  "reference_implementation": {
    "summary": "Subscribes to CLOB WebSocket book and trade-tape streams, checks spread and flow conditions, and emits GTC post_only maker OrderIntents inside the touch when conditions are benign.",
    "language_note": "Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. Translate to TS/Python/Go/Rust.",
    "pseudocode": "FUNCTION onBookTick(market_id, bookTick):\n  // --- 0. KillSwitch gate ---\n  ks = FETCH internal.killswitch.status\n  IF ks.active:\n    CANCEL_OPEN_QUOTES(market_id)\n    RETURN\n\n  // --- 1. Volume filter ---\n  vol24h = FETCH clob_public.GET('/markets/' + market_id + '/volume')\n  IF vol24h < params.min_volume_24h_usd_hard:  // 50,000 pUSD\n    SKIP 'MAKER_TIGHT_SPREAD_TOO_TIGHT' (volume)\n    RETURN\n\n  // --- 2. Spread check ---\n  mid        = (bookTick.best_bid + bookTick.best_ask) / 2\n  spread_bps = (bookTick.best_ask - bookTick.best_bid) / mid * 10000\n  IF spread_bps < 2 * params.edge_bps_hard:  // 3 bps hard floor\n    // sample 1/100 for observability\n    IF random() < 0.01:\n      EMIT DecisionReport(intent_emitted=false, reason='MAKER_TIGHT_SPREAD_TOO_TIGHT')\n    RETURN\n\n  // --- 3. Toxic flow detection ---\n  tape = FETCH ws_market.tradeTape(market_id, window_s=30)\n  takerBuyRatio = tape.taker_buy_volume / (tape.total_volume + 1e-9)\n  IF takerBuyRatio > 0.75:\n    EMIT DecisionReport(intent_emitted=false, reason='MAKER_TIGHT_TOXIC_FLOW_DETECTED')\n    RETURN\n\n  // --- 4. Inventory skew ---\n  position    = FETCH clob_auth.GET('/positions?market=' + market_id)\n  inventoryRatio = position.yes_notional / params.max_inventory_usd\n  skew        = params.inventory_skew_factor * inventoryRatio\n  edgeHalf    = params.edge_bps / 2 / 10000\n\n  bidPrice    = round(mid - edgeHalf - skew, 4)\n  askPrice    = round(mid + edgeHalf - skew, 4)\n\n  // --- 5. Warning threshold ---\n  clipSize = toPusdUnits(params.clip_size_usd)\n  IF params.edge_bps < 6:\n    WARN('MAKER_TIGHT_EDGE_MARGINAL')\n    clipSize = toPusdUnits(params.clip_size_usd * 0.5)\n\n  // --- 6. Emit maker OrderIntents (V2: no feeRateBps; builder field; post_only=true) ---\n  // Maker fee_bps <= 50 (V2 maker cap)\n  EMIT OrderIntent(\n    market_id  = market_id,\n    outcome    = 'YES',\n    side       = 'buy',\n    price      = bidPrice,\n    size_pUSD  = clipSize,\n    tif        = 'GTC',\n    post_only  = true,\n    builder    = { code: config.builder_code, fee_bps: 30 },\n    negrisk_aware = false\n  )\n  EMIT OrderIntent(\n    market_id  = market_id,\n    outcome    = 'YES',\n    side       = 'sell',\n    price      = askPrice,\n    size_pUSD  = clipSize,\n    tif        = 'GTC',\n    post_only  = true,\n    builder    = { code: config.builder_code, fee_bps: 30 },\n    negrisk_aware = false\n  )\n\n  EMIT DecisionReport(\n    intent_emitted = true,\n    edge_bps       = params.edge_bps,\n    inventory_ratio = inventoryRatio,\n    skew_applied   = (skew != 0),\n    reasons        = ['MAKER_TIGHT_QUOTING']\n  )",
    "sdk_calls": [
      "ws_market.subscribe('book', [market_id])",
      "ws_market.subscribe('trade_tape', [market_id])",
      "fetchClobPublic('/markets/' + market_id + '/volume')",
      "clob_auth.GET('/positions?market=' + 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 benign market, spread 16 bps",
        "source": "ws_market",
        "payload": {
          "market_id": "0xcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890cd",
          "best_bid": "0.619",
          "best_ask": "0.629",
          "mid": "0.624",
          "spread_bps": "16.0",
          "taker_buy_ratio_30s": "0.52",
          "volume_24h_pusd": "480000",
          "received_at_ms": 1746789800000
        }
      }
    ],
    "output": [
      {
        "label": "OrderIntent \u2014 maker bid (YES buy, post_only, GTC)",
        "payload": {
          "intent_id": "oi_01HX9MKRT1A4Z1B",
          "trace_id": "tr_01HX9MKRT1A4VR5",
          "market_id": "0xcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890cd",
          "outcome": "YES",
          "side": "buy",
          "price": "0.619",
          "size_pUSD": "200.00",
          "tif": "GTC",
          "post_only": true,
          "builder": {
            "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
            "fee_bps": 30
          },
          "negrisk_aware": false,
          "decision": {
            "edge_bps": 10.0,
            "inventory_ratio": 0.12,
            "skew_applied": true,
            "reasons": [
              "MAKER_TIGHT_QUOTING"
            ]
          },
          "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
        }
      },
      {
        "label": "DecisionReport \u2014 skipped (spread too tight), sampled 1/100",
        "payload": {
          "report_id": "dr_01HX9MKRT1BZZZ",
          "bot_id": "strat.maker_tight",
          "market_id": "0xcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890cd",
          "intent_emitted": false,
          "spread_bps": 3.2,
          "reasons": [
            "MAKER_TIGHT_SPREAD_TOO_TIGHT"
          ],
          "sampled": true,
          "evaluated_at_ms": 1746789801000
        }
      }
    ]
  },
  "reason_codes": [
    {
      "code": "MAKER_TIGHT_QUOTING",
      "severity": "INFO",
      "meaning": "Spread is adequate, volume passes filter, no toxic flow detected. Maker bid+ask OrderIntents emitted.",
      "action": "Emit two GTC post_only OrderIntents.",
      "user_message": "Maker quotes were posted inside the market spread."
    },
    {
      "code": "MAKER_TIGHT_SPREAD_TOO_TIGHT",
      "severity": "INFO",
      "meaning": "Market spread is narrower than 2 * edge_bps hard floor, or volume is below min_volume_24h_usd hard floor. Quoting is not viable.",
      "action": "Skip; emit DecisionReport intent_emitted=false (sampled 1/100).",
      "user_message": "The market spread was too tight to post a competitive quote."
    },
    {
      "code": "MAKER_TIGHT_TOXIC_FLOW_DETECTED",
      "severity": "WARN",
      "meaning": "Taker-initiated buy volume over the last 30s exceeds the toxic flow threshold (0.75 ratio). Directional flow detected.",
      "action": "Skip; cancel open quotes; emit DecisionReport intent_emitted=false.",
      "user_message": "Strong directional trading was detected. Maker quotes were paused to avoid adverse selection."
    },
    {
      "code": "MAKER_TIGHT_EDGE_MARGINAL",
      "severity": "WARN",
      "meaning": "edge_bps is between the warning threshold (6) and the hard floor (3). Quoting at reduced clip size.",
      "action": "Emit OrderIntents at 50% clip size; log warning.",
      "user_message": "Market conditions are borderline. Maker quotes were posted at reduced size."
    },
    {
      "code": "MAKER_TIGHT_HIGH_SKEW",
      "severity": "WARN",
      "meaning": "inventory_skew_factor is above the warning threshold (0.6). Inventory is being aggressively managed.",
      "action": "Continue quoting; log warning; monitor inventory_ratio.",
      "user_message": "Quote prices were significantly adjusted to help balance the current position."
    },
    {
      "code": "MAKER_TIGHT_LOW_VOLUME",
      "severity": "WARN",
      "meaning": "24h volume is between the warning (150K) and hard floor (50K). Market is less liquid than preferred.",
      "action": "Emit at 50% clip size; log warning.",
      "user_message": "This market has lower than usual activity. Quote sizes were reduced."
    },
    {
      "code": "STALE_MARKET_DATA",
      "severity": "HARD_REJECT",
      "meaning": "Book snapshot older than 5s or position data unavailable.",
      "action": "Cancel open quotes; no new OrderIntents.",
      "user_message": "Market data was too old. Quotes were cancelled."
    },
    {
      "code": "KILL_SWITCH_ACTIVE",
      "severity": "HARD_REJECT",
      "meaning": "Global kill switch is active.",
      "action": "Cancel all open quotes; no new OrderIntents.",
      "user_message": "Trading is currently paused."
    }
  ],
  "metrics": {
    "emitted": [
      {
        "name": "polytraders_strat_makertight_decisions_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "verdict",
          "reason_code"
        ],
        "meaning": "Total evaluation cycles by intent_emitted (true/false) and reason code."
      },
      {
        "name": "polytraders_strat_makertight_spread_bps",
        "type": "histogram",
        "unit": "basis_points",
        "labels": [
          "market_id"
        ],
        "meaning": "Distribution of observed market spread in bps at each evaluation, including skipped cycles."
      },
      {
        "name": "polytraders_strat_makertight_inventory_ratio",
        "type": "gauge",
        "unit": "ratio",
        "labels": [
          "market_id"
        ],
        "meaning": "Current inventory ratio (yes_notional / max_inventory) per market."
      },
      {
        "name": "polytraders_strat_makertight_intents_emitted_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "side"
        ],
        "meaning": "Total maker OrderIntents emitted by side (buy bid / sell ask)."
      },
      {
        "name": "polytraders_strat_makertight_toxic_flow_skips_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "market_id"
        ],
        "meaning": "Number of cycles skipped due to toxic flow detection per market."
      },
      {
        "name": "polytraders_strat_makertight_eval_latency_ms",
        "type": "histogram",
        "unit": "milliseconds",
        "labels": [],
        "meaning": "Wall-clock latency from book tick to OrderIntent emit."
      }
    ],
    "alerts": [
      {
        "name": "MakerTightHighToxicFlowRate",
        "condition": "rate(polytraders_strat_makertight_toxic_flow_skips_total[10m]) > 5",
        "severity": "warn",
        "runbook": "#runbook-makertight-toxic-flow"
      },
      {
        "name": "MakerTightHighInventory",
        "condition": "polytraders_strat_makertight_inventory_ratio > 0.8",
        "severity": "warn",
        "runbook": "#runbook-makertight-inventory"
      },
      {
        "name": "MakerTightStaleFeed",
        "condition": "rate(polytraders_strat_makertight_decisions_total{reason_code='STALE_MARKET_DATA'}[5m]) > 0.1",
        "severity": "warn",
        "runbook": "#runbook-makertight-stale-feed"
      },
      {
        "name": "MakerTightKillSwitchBlocking",
        "condition": "rate(polytraders_strat_makertight_decisions_total{reason_code='KILL_SWITCH_ACTIVE'}[1m]) > 0",
        "severity": "page",
        "runbook": "#runbook-killswitch"
      }
    ],
    "dashboards": [
      "Grafana \u2014 Strategy / MakerTight spread distribution and quote throughput",
      "Grafana \u2014 Strategy / MakerTight inventory ratio and toxic flow skip rate"
    ],
    "log_level": "info"
  },
  "state": {
    "store": "redis",
    "shape": "Per-market last book tick (mid, spread_bps, timestamp_ms), open quote IDs, and current inventory_ratio; keyed by market_id",
    "ttl": "60s for book state; open quote IDs are persistent until fill or cancel",
    "recovery": "On cold start, open quote IDs are re-fetched from clob_auth. Inventory is re-read from clob_auth. Book state is rebuilt from first ws_market tick.",
    "size_estimate": "~300 bytes per quoted market; typically < 3 MB total"
  },
  "concurrency": {
    "execution_model": "actor-per-market",
    "max_in_flight": 40,
    "idempotency_key": "intent_id",
    "timeout_ms": 100,
    "backpressure": "drop oldest pending tick per market_id when queue depth > 5",
    "locking": "per-market_id mutex for inventory state read/write and open quote tracking"
  },
  "dependencies": {
    "depends_on": [
      {
        "bot_id": "risk.kill_switch",
        "why": "Checked first; cancels all open quotes and blocks new intent emission when active."
      }
    ],
    "emits_to": [
      {
        "bot_id": "risk.portfolio_guard",
        "what": "GTC post_only OrderIntents (bid + ask) for risk guardrail evaluation before SmartRouter."
      },
      {
        "bot_id": "gov.builder_attribution",
        "what": "builder.code bytes32 on every maker OrderIntent for attribution and rebate tracking."
      }
    ],
    "sibling": [],
    "external": [
      {
        "service": "Polymarket CLOB WebSocket (ws_market)",
        "sla": "best-effort",
        "fallback": "On disconnect, cancel open quotes; no new OrderIntents until feed recovers."
      },
      {
        "service": "Polymarket CLOB auth API (positions)",
        "sla": "99.95%",
        "fallback": "If position read fails, cancel open quotes and skip cycle (fail-closed on inventory state)."
      }
    ]
  },
  "security_surfaces": {
    "signs_orders": true,
    "private_key_access": "signing-only",
    "abuse_vectors": [
      "Quote stuffing: adversary places and cancels orders to artificially move the mid-price seen by Maker-Tight",
      "Inventory manipulation: feeding a spoofed position to inflate inventory_ratio and widen quotes",
      "Open maker orders left live after KillSwitch activation"
    ],
    "mitigations": [
      "Book tick staleness check (> 5s) cancels quotes before accepting an adversarial price update",
      "Inventory ratio is read from clob_auth (authenticated), not from ws_market (public feed)",
      "KillSwitch cancels all open quotes immediately on activation",
      "V2 order timestamp(ms) prevents replay of old signed maker orders",
      "post_only=true prevents inadvertent taker fills on latency spikes"
    ],
    "contract_calls": [
      {
        "contract": "CTFExchangeV2",
        "function": "matchOrders",
        "purpose": "Settlement of maker YES token purchases/sales when a taker crosses the posted quote."
      }
    ]
  },
  "failure_injection": [
    {
      "scenario": "STALE_WS_FEED",
      "how_to_inject": "Pause ws_market WebSocket; let last_seen age beyond 5s",
      "expected_behaviour": "Open quotes cancelled; no new OrderIntents; STALE_MARKET_DATA DecisionReport emitted",
      "recovery": "Automatic on WebSocket reconnect; quotes re-posted on next clean tick."
    },
    {
      "scenario": "TOXIC_FLOW_SPIKE",
      "how_to_inject": "Inject mock trade tape with taker_buy_ratio=0.85 for 30s",
      "expected_behaviour": "MAKER_TIGHT_TOXIC_FLOW_DETECTED; no OrderIntents for duration of spike; quotes cancelled",
      "recovery": "Automatic when taker_buy_ratio falls below 0.75 over next 30s window."
    },
    {
      "scenario": "SPREAD_COLLAPSE",
      "how_to_inject": "Set mock best_bid=0.623, best_ask=0.624 (spread = 1.6 bps < 2*edge_bps=20 bps)",
      "expected_behaviour": "MAKER_TIGHT_SPREAD_TOO_TIGHT; no OrderIntents; DecisionReport sampled 1/100",
      "recovery": "Automatic when spread widens above threshold."
    },
    {
      "scenario": "KILL_SWITCH_ON",
      "how_to_inject": "Set killswitch.active=true",
      "expected_behaviour": "All open quotes cancelled; no new OrderIntents",
      "recovery": "Automatic on manual KillSwitch reset."
    },
    {
      "scenario": "POSITION_READ_FAIL",
      "how_to_inject": "Block clob_auth GET /positions",
      "expected_behaviour": "STALE_MARKET_DATA; open quotes cancelled; no new OrderIntents until position readable",
      "recovery": "Automatic on clob_auth reconnect."
    }
  ],
  "runbook": {
    "summary": "Maker-Tight incidents are typically elevated toxic flow detection (causing missed quoting) or high inventory ratios requiring manual review. Quote failures due to stale feeds should resolve automatically.",
    "oncall_actions": [
      {
        "alert": "MakerTightHighToxicFlowRate",
        "first_action": "Review trade tape on the flagged market(s). Confirm whether flow is genuinely informed or a temporary imbalance.",
        "escalate_to": "Strategy pod lead if toxic flow persists > 15 minutes on a major market."
      },
      {
        "alert": "MakerTightHighInventory",
        "first_action": "Check inventory_ratio on Grafana. Confirm bot is skewing quotes toward reducing inventory.",
        "escalate_to": "Risk pod lead if inventory_ratio > 0.9 and not decreasing."
      },
      {
        "alert": "MakerTightStaleFeed",
        "first_action": "Check ws_market WebSocket connectivity.",
        "escalate_to": "Infra on-call if disconnected > 2 minutes."
      },
      {
        "alert": "MakerTightKillSwitchBlocking",
        "first_action": "Confirm KillSwitch activation was intentional.",
        "escalate_to": "Risk pod lead immediately."
      }
    ],
    "manual_overrides": [
      {
        "name": "pause_market",
        "how": "Add market_id to config.excluded_markets",
        "when": "Market is experiencing anomalous conditions (oracle dispute, liquidity withdrawal event)."
      },
      {
        "name": "cancel_all_quotes",
        "how": "polytraders bot cancel-quotes strat.maker_tight --all",
        "when": "Emergency; suspected adverse selection event in progress."
      }
    ],
    "healthcheck": "GET /internal/health/maker-tight -> 200 if ws_market feed last_seen < 5s, clob_auth reachable, KillSwitch inactive, and at least one market quoted in last 60s."
  },
  "promotion_gates": {
    "to_shadow": [
      {
        "gate": "All unit tests pass including post_only invariant and toxic flow detection",
        "how_measured": "CI test run",
        "threshold": "100% pass"
      },
      {
        "gate": "feeRateBps absence verified; maker fee_bps \u2264 50 verified in integration test",
        "how_measured": "Integration test asserting V2 order schema",
        "threshold": "Pass"
      }
    ],
    "to_limited_live": [
      {
        "gate": "p99 eval latency < 100ms over 24h",
        "how_measured": "polytraders_strat_makertight_eval_latency_ms histogram",
        "threshold": "p99 < 100ms"
      },
      {
        "gate": "Inventory_ratio stays below 0.7 over 48h shadow run",
        "how_measured": "polytraders_strat_makertight_inventory_ratio gauge",
        "threshold": "max < 0.7"
      }
    ],
    "to_general_live": [
      {
        "gate": "E2E: book tick \u2192 two signed V2 post_only GTC OrderIntents submitted and resting on CLOB testnet",
        "how_measured": "E2E test",
        "threshold": "Pass"
      },
      {
        "gate": "Sampled 1/100 DecisionReport for skipped cycles verified in integration test",
        "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 skipped cycles",
    "bus_failure_action": "fail-closed",
    "user_visible": "summary-only",
    "consumes_kinds": []
  },
  "capital_impact": "Direct",
  "v3_status": {
    "phase": 6,
    "phase_name": "One simple strategy",
    "docs": {
      "done": 27,
      "total": 27,
      "state": "done"
    },
    "impl": {
      "done": 0,
      "total": 15,
      "state": "pending"
    },
    "runtime": {
      "done": 0,
      "total": 8,
      "state": "pending"
    },
    "overall": "pending"
  }
}