{
  "schema_version": "1.0.0",
  "bot_id": "3.24",
  "bot_name": "MarketCreationScout",
  "slug": "marketcreationscout",
  "layer": "Strategy",
  "layer_key": "strat",
  "bot_class": "Alpha Strategy",
  "authority": [
    "Trade"
  ],
  "status": "planned",
  "readiness": "Spec started",
  "flagship": false,
  "is_reference": false,
  "public_export": false,
  "identity": {
    "layer": "Strategy",
    "bot_class": "Alpha Strategy",
    "authority": "Trade",
    "runs_before": "Risk guardrail pipeline",
    "runs_after": "Observation bus / internal analytics",
    "applies_to": "Newly created Polymarket binary markets within max_age_minutes of creation that score >= min_quality_score from the internal market evaluator",
    "default_mode": "shadow_only",
    "user_visible": "Advanced details only",
    "developer_owner": "Polytraders core \u2014 Strategy pod"
  },
  "purpose": "MarketCreationScout monitors newly created Polymarket markets and identifies those worth seeding with early maker liquidity before the spread tightens. It evaluates quality score, market age, and event category, then emits GTC post-only OrderIntents to seed the book with initial maker quotes, generating rebate income from early-adopter taker flow.",
  "why_it_matters": [
    {
      "failure": "Quality score mis-evaluates market",
      "consequence": "If the quality model gives a high score to a market that is poorly formed or resolves unexpectedly, the seed capital is exposed to an unintended directional position."
    },
    {
      "failure": "Stale input data",
      "consequence": "Acting on stale signals for MarketCreationScout produces trades based on outdated market conditions, generating adverse fills."
    },
    {
      "failure": "KillSwitch not respected",
      "consequence": "Emitting OrderIntents while KillSwitch is active bypasses risk controls."
    }
  ],
  "polymarket_inputs": [
    {
      "input": "CLOB book (mid, depth, spread)",
      "source": "ws_market",
      "required": true,
      "use": "Read current market price and available depth for order sizing."
    },
    {
      "input": "Market status (open/closed/resolved)",
      "source": "clob_public",
      "required": true,
      "use": "Skip closed or resolved markets."
    }
  ],
  "internal_inputs": [
    {
      "input": "KillSwitch active flag",
      "source": "KillSwitch",
      "required": true,
      "use": "Abort all intent emission if KillSwitch active."
    },
    {
      "input": "MarketCreationScout analytics signal",
      "source": "internal (analytics engine)",
      "required": true,
      "use": "Provides the core MarketCreationScout signal that drives trade decisions."
    },
    {
      "input": "Builder code bytes32",
      "source": "internal config",
      "required": true,
      "use": "Injected into builder field on every signed V2 OrderIntent."
    }
  ],
  "raw_params": [
    "min_quality_score \u00b7 0\u2013100",
    "seed_size_usd \u00b7 int",
    "max_age_minutes \u00b7 int",
    "auto_handoff_to \u00b7 enum"
  ],
  "parameters": [
    {
      "name": "min_quality_score",
      "default": 0.7,
      "warning": 0.55,
      "hard": 0.4,
      "controls": "Minimum market quality score (0\u20131) from the internal market evaluator required before seeding maker liquidity.",
      "why_default_matters": "0.70 ensures only well-formed, liquid-potential markets receive seed capital.",
      "threshold_logic": [
        {
          "condition": ">= 0.70",
          "action": "Allow seed quote"
        },
        {
          "condition": "0.55\u20130.70",
          "action": "WARN MCS_LOW_QUALITY; halve seed size"
        },
        {
          "condition": "< 0.40",
          "action": "SKIP MCS_QUALITY_BELOW_FLOOR"
        }
      ],
      "dev_check": "if quality_score < params.hard: return skip('MCS_QUALITY_BELOW_FLOOR')",
      "user_facing": "The market quality score was too low for seeding."
    },
    {
      "name": "seed_size_usd",
      "default": 200,
      "warning": 400,
      "hard": 600,
      "controls": "pUSD size for the initial seed maker quote per side.",
      "why_default_matters": "200 pUSD seeds visible liquidity without over-committing on an unproven new market.",
      "threshold_logic": [
        {
          "condition": "<= 200 pUSD",
          "action": "Normal seed"
        },
        {
          "condition": "> 600 pUSD",
          "action": "Reject config"
        }
      ],
      "dev_check": "if params.seed_size_usd > params.hard: raise ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL')",
      "user_facing": "Seed size capped at the configured maximum."
    },
    {
      "name": "max_age_minutes",
      "default": 30,
      "warning": 60,
      "hard": 120,
      "controls": "Maximum market age in minutes before the bot considers a market too established for scout seeding.",
      "why_default_matters": "30 minutes targets the early-discovery window before arbitrageurs and other makers tighten the spread.",
      "threshold_logic": [
        {
          "condition": "<= 30 min",
          "action": "Seed candidate"
        },
        {
          "condition": "60\u2013120 min",
          "action": "WARN MCS_MARKET_MATURING; halve seed"
        },
        {
          "condition": "> 120 min",
          "action": "SKIP MCS_MARKET_TOO_OLD"
        }
      ],
      "dev_check": "if market_age_min > params.hard: return skip('MCS_MARKET_TOO_OLD')",
      "user_facing": "The market has been open too long for a scout seed."
    },
    {
      "name": "auto_handoff_to",
      "default": "maker-wide",
      "warning": null,
      "hard": null,
      "controls": "Slug of the maker strategy bot to automatically hand off the seeded market to after the seed quote is placed.",
      "why_default_matters": "Ensures seeded liquidity transitions to a persistent maker strategy rather than expiring.",
      "threshold_logic": [
        {
          "condition": "handoff bot active",
          "action": "Emit handoff signal on first fill"
        },
        {
          "condition": "handoff bot unavailable",
          "action": "WARN MCS_HANDOFF_UNAVAILABLE"
        }
      ],
      "dev_check": "if not handoff_bot_available: log warning; proceed without handoff",
      "user_facing": "The market was seeded and handed off to the configured maker strategy."
    }
  ],
  "default_config": {
    "bot_id": "strat.marketcreationscout",
    "version": "0.1.0",
    "mode": "shadow_only",
    "defaults": {
      "min_quality_score": 0.7,
      "seed_size_usd": 200,
      "max_age_minutes": 30,
      "auto_handoff_to": "maker-wide"
    },
    "locked": {
      "min_quality_score": {
        "min": 0.4
      },
      "seed_size_usd": {
        "min": 600
      },
      "max_age_minutes": {
        "min": 120
      }
    }
  },
  "implementation_flow": [
    "Check KillSwitch; if active, emit no OrderIntents.",
    "FETCH MarketCreationScout analytics signal from internal engine.",
    "IF signal below hard floor: SKIP, emit sampled DecisionReport MCS_NO_EDGE.",
    "FETCH clob_public market status; skip if closed or resolved.",
    "FETCH ws_market book; compute current mid and available depth.",
    "IF signal < warning threshold: WARN MCS_MARGINAL; reduce size 50%.",
    "Compute order size = min(max_size_param, available_depth).",
    "EMIT IOC OrderIntent with builder code.",
    "EMIT DecisionReport with intent_emitted=true, reason=MCS_TRADE."
  ],
  "decision_logic": {
    "approve": "All gates passed, KillSwitch inactive, market open. Emit IOC OrderIntent.",
    "reshape_required": "Not applicable \u2014 reshaping handled by downstream Risk guardrail.",
    "reject": "Signal below hard floor; stale data; market closed; KillSwitch active.",
    "warning_only": "Signal in warning zone triggers 50% size reduction."
  },
  "decision_output_schema": "OrderIntent",
  "decision_output_example": {
    "intent_id": "oi_01HMCS0000001A",
    "trace_id": "tr_01HMCS000TR001",
    "market_id": "0xmarketcr000000000000000000000000000000000000000000000000000000000001",
    "outcome": "YES",
    "side": "buy",
    "price": "0.540",
    "size_pUSD": "200.00",
    "tif": "IOC",
    "post_only": false,
    "builder": {
      "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
      "fee_bps": 25
    },
    "negrisk_aware": false,
    "decision": {
      "signal_score": 0.75,
      "reasons": [
        "MCS_TRADE"
      ]
    },
    "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
  },
  "developer_log": {
    "bot_id": "strat.marketcreationscout",
    "market_id": "0xmarketcr000000000000000000000000000000000000000000000000000000000001",
    "signal_score": 0.75,
    "intent_emitted": true,
    "reason": "MCS_TRADE",
    "emitted_at_ms": 1746790800000
  },
  "user_explanations": [
    {
      "situation": "MarketCreationScout trade placed",
      "message": "The MarketCreationScout strategy detected a suitable opportunity and placed a trade."
    },
    {
      "situation": "Edge too small \u2014 no trade",
      "message": "The signal was below the minimum threshold. No trade was placed."
    },
    {
      "situation": "Safety gate active \u2014 no trade",
      "message": "A safety condition (stale data, kill switch, or parameter limit) blocked the trade."
    }
  ],
  "failure_modes": {
    "main_failure_mode": "If the quality model gives a high score to a market that is poorly formed or resolves unexpectedly, the seed capital is exposed to an unintended directional position.",
    "false_positive_risk": "Signal mis-fires when market conditions change rapidly, producing trades that quickly move against the MarketCreationScout thesis.",
    "false_negative_risk": "Hard floor set too conservatively misses genuine opportunities.",
    "safe_fallback": "If ws_market feed stale or analytics signal unavailable, skip without emitting any OrderIntent.",
    "required_dependencies": [
      "ws_market",
      "clob_public",
      "internal MarketCreationScout analytics engine",
      "KillSwitch",
      "internal builder code"
    ]
  },
  "acceptance_tests": {
    "unit": [
      {
        "test": "Emit IOC when signal=0.75 and all gates pass",
        "setup": "standard config",
        "expected": "IOC OrderIntent; reason=MCS_TRADE"
      },
      {
        "test": "Skip when signal below hard floor",
        "setup": "signal=below_hard",
        "expected": "No OrderIntent; sampled reason=MCS_NO_EDGE"
      },
      {
        "test": "Skip when KillSwitch active",
        "setup": "killswitch.active=true",
        "expected": "No OrderIntents emitted"
      }
    ],
    "integration": [
      {
        "test": "Full cycle: signal \u2192 computation \u2192 IOC OrderIntent on Polygon testnet",
        "expected": "Order has builder.code, no feeRateBps, EIP-712 domain v2"
      }
    ],
    "property": [
      {
        "property": "Bot never emits OrderIntent when KillSwitch is active",
        "required": "Always true"
      },
      {
        "property": "feeRateBps never present on any signed OrderIntent",
        "required": "Always true"
      }
    ]
  },
  "checklist_overrides": {},
  "legacy_goal": "Identify newly created markets worth seeding maker liquidity in, before the spread tightens.",
  "legacy_pm_signals": [
    "NewMarketWatcher (0.5) feed",
    "MarketQualityRanker (0.2) score and source class",
    "Top-of-book width and recent trade pacing"
  ],
  "legacy_external_feeds": [],
  "reporting_groups": [
    "strategy_decision"
  ],
  "reason_codes": [
    {
      "code": "MCS_TRADE",
      "severity": "INFO",
      "meaning": "All gates passed. IOC OrderIntent emitted for MarketCreationScout.",
      "action": "Emit IOC OrderIntent.",
      "user_message": "A MarketCreationScout trade was placed."
    },
    {
      "code": "MCS_MARGINAL",
      "severity": "WARN",
      "meaning": "Edge is within the warning threshold; size reduced 50%.",
      "action": "Emit at 50% size; log warning.",
      "user_message": "A small edge was found; a reduced-size MarketCreationScout trade was placed."
    },
    {
      "code": "MCS_NO_EDGE",
      "severity": "INFO",
      "meaning": "Edge below hard floor. Skipping.",
      "action": "Skip; emit sampled DecisionReport.",
      "user_message": "The edge was too small to justify a trade."
    },
    {
      "code": "MCS_QUALITY_BELOW_FLOOR",
      "severity": "INFO",
      "meaning": "Quality score below 0.40 hard floor.",
      "action": "Skip; no seed quote.",
      "user_message": "Market quality too low for seeding."
    },
    {
      "code": "MCS_MARKET_TOO_OLD",
      "severity": "INFO",
      "meaning": "Market age exceeds max_age_minutes hard limit.",
      "action": "Skip; no seed quote.",
      "user_message": "Market is too established for a scout seed."
    },
    {
      "code": "MCS_HARD_REJECT",
      "severity": "HARD_REJECT",
      "meaning": "A critical gate condition blocked the trade (stale data, kill switch, or hard parameter breach).",
      "action": "Skip; no OrderIntent.",
      "user_message": "A safety condition blocked the trade."
    },
    {
      "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."
    }
  ],
  "metrics": {
    "emitted": [
      {
        "name": "polytraders_strat_marketcreationscout_decisions_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "verdict",
          "reason_code"
        ],
        "meaning": "Total evaluation cycles by verdict and reason code."
      },
      {
        "name": "polytraders_strat_marketcreationscout_signal_score",
        "type": "histogram",
        "unit": "score",
        "labels": [],
        "meaning": "Distribution of analytics signal scores at evaluation."
      },
      {
        "name": "polytraders_strat_marketcreationscout_intents_emitted_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "outcome"
        ],
        "meaning": "Total IOC OrderIntents emitted."
      },
      {
        "name": "polytraders_strat_marketcreationscout_eval_latency_ms",
        "type": "histogram",
        "unit": "milliseconds",
        "labels": [],
        "meaning": "Latency from signal receipt to OrderIntent emit."
      }
    ],
    "alerts": [
      {
        "name": "MarketCreationScoutStaleFeed",
        "condition": "rate(polytraders_strat_marketcreationscout_decisions_total{reason_code='STALE_MARKET_DATA'}[5m]) > 0.1",
        "severity": "warn",
        "runbook": "#runbook-marketcreationscout-stale"
      },
      {
        "name": "MarketCreationScoutKillSwitch",
        "condition": "rate(polytraders_strat_marketcreationscout_decisions_total{reason_code='KILL_SWITCH_ACTIVE'}[1m]) > 0",
        "severity": "page",
        "runbook": "#runbook-killswitch"
      },
      {
        "name": "MarketCreationScoutNoEdge",
        "condition": "rate(polytraders_strat_marketcreationscout_decisions_total{verdict='skip',reason_code='MCS_NO_EDGE'}[10m]) / rate(polytraders_strat_marketcreationscout_decisions_total[10m]) > 0.95",
        "severity": "warn",
        "runbook": "#runbook-marketcreationscout-edge"
      }
    ]
  },
  "state": {
    "store": "redis",
    "shape": "Per market: last signal score, last evaluated timestamp; keyed by market_id",
    "ttl": "60s per signal snapshot",
    "recovery": "On cold start, signals rebuilt from next analytics engine poll.",
    "size_estimate": "~150 bytes per tracked market; < 500 KB total"
  },
  "concurrency": {
    "execution_model": "actor-per-market",
    "max_in_flight": 25,
    "idempotency_key": "intent_id",
    "timeout_ms": 300,
    "backpressure": "drop oldest signal per market_id when queue > 2",
    "locking": "per-market_id mutex for signal state"
  },
  "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": "IOC OrderIntents for risk guardrail evaluation."
      },
      {
        "bot_id": "gov.builder_attribution",
        "what": "builder.code bytes32 on every OrderIntent."
      }
    ],
    "sibling": [],
    "external": [
      {
        "service": "Polymarket CLOB WebSocket (ws_market)",
        "sla": "best-effort",
        "fallback": "Skip all OrderIntent emission on disconnect."
      },
      {
        "service": "Internal MarketCreationScout analytics engine",
        "sla": "internal SLA",
        "fallback": "If signal unavailable, treat as below hard floor and skip."
      }
    ]
  },
  "security_surfaces": {
    "signs_orders": true,
    "private_key_access": "signing-only",
    "abuse_vectors": [
      "Signal injection to produce false MarketCreationScout trades",
      "Order sizing parameter manipulation to exceed position limits"
    ],
    "mitigations": [
      "MarketCreationScout analytics signals sourced from authenticated internal engine only",
      "Hard limits on position size enforced before OrderIntent emission",
      "Builder code injected from secure internal config"
    ]
  },
  "failure_injection": [
    {
      "scenario": "SIGNAL_UNAVAILABLE",
      "how_to_inject": "Cut internal analytics engine connection",
      "expected_behaviour": "No entries; DecisionReport with reason=MCS_HARD_REJECT emitted",
      "recovery": "Automatic when engine reconnects."
    },
    {
      "scenario": "HARD_FLOOR_BREACH",
      "how_to_inject": "Inject signal below hard floor",
      "expected_behaviour": "MCS_NO_EDGE; no OrderIntent",
      "recovery": "Automatic on next valid signal."
    },
    {
      "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."
    }
  ],
  "runbook": {
    "summary": "MarketCreationScout incidents are typically stale analytics feeds or kill-switch activations. Hard-floor skips are normal.",
    "oncall_actions": [
      {
        "alert": "MarketCreationScoutStaleFeed",
        "first_action": "Check internal MarketCreationScout analytics engine connectivity and feed health.",
        "escalate_to": "Infra on-call if feed lag > 5 min."
      },
      {
        "alert": "MarketCreationScoutKillSwitch",
        "first_action": "Confirm KillSwitch activation was intentional.",
        "escalate_to": "Risk pod lead immediately."
      },
      {
        "alert": "MarketCreationScoutNoEdge",
        "first_action": "Review signal calibration; check if market conditions have changed.",
        "escalate_to": "Strategy pod lead if prolonged."
      }
    ],
    "manual_overrides": [
      {
        "name": "exclude_market",
        "how": "Add market_id to config.excluded_markets",
        "when": "Market is showing anomalous conditions that invalidate the MarketCreationScout signal."
      }
    ],
    "healthcheck": "GET /internal/health/marketcreationscout -> 200 if Analytics engine active; signal age < 60s; KillSwitch inactive.. Red: Analytics engine down or KillSwitch active.."
  },
  "promotion_gates": {
    "to_shadow": [
      {
        "gate": "Unit tests pass including hard-floor skip and kill-switch block",
        "how_measured": "CI test run",
        "threshold": "100% pass"
      }
    ],
    "to_limited_live": [
      {
        "gate": "p99 eval latency < 300ms over 24h shadow run",
        "how_measured": "polytraders_strat_marketcreationscout_eval_latency_ms histogram",
        "threshold": "p99 < 300ms"
      }
    ],
    "to_general_live": [
      {
        "gate": "E2E: signal \u2192 computation \u2192 IOC OrderIntent on Polygon testnet with builder.code and no feeRateBps",
        "how_measured": "E2E test",
        "threshold": "Pass"
      }
    ]
  },
  "wire_examples": {
    "input": [
      {
        "label": "MarketCreationScout analytics signal",
        "source": "internal (analytics engine)",
        "payload": {
          "market_id": "0xmarketcr000000000000000000000000000000000000000000000000000000000001",
          "signal_score": "0.75",
          "received_at_ms": 1746790800000
        }
      }
    ],
    "output": [
      {
        "label": "OrderIntent \u2014 MarketCreationScout IOC buy YES",
        "payload": {
          "intent_id": "oi_01HMCS0000001A",
          "market_id": "0xmarketcr000000000000000000000000000000000000000000000000000000000001",
          "outcome": "YES",
          "side": "buy",
          "price": "0.540",
          "size_pUSD": "200.00",
          "tif": "IOC",
          "builder": {
            "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
            "fee_bps": 25
          },
          "decision": {
            "signal_score": 0.75,
            "reasons": [
              "MCS_TRADE"
            ]
          }
        }
      }
    ]
  },
  "reference_implementation": {
    "pseudocode": "FUNCTION onSignalUpdate(market_id, signal):\n  ks = FETCH internal.killswitch.status\n  IF ks.active: RETURN\n\n  // Hard floor gate\n  IF signal.score < params.min_quality_score_hard:\n    IF random() < 0.01:\n      EMIT DecisionReport(intent_emitted=false, reason='MCS_NO_EDGE')\n    RETURN\n\n  mkt = FETCH clob_public.GET('/markets/' + market_id)\n  IF mkt.closed OR mkt.resolved: RETURN\n\n  // Warning threshold check\n  sizeMultiplier = 0.5 IF signal.score < params.min_quality_score_warn ELSE 1.0\n  IF sizeMultiplier < 1.0: WARN('MCS_MARGINAL')\n\n  // Book snapshot\n  book = FETCH ws_market.book(market_id)\n  mid = (book.best_bid + book.best_ask) / 2\n  depth = FETCH clob_public.depth(market_id)\n\n  // Size computation\n  orderSize = toPusdUnits(min(params.seed_size_usd * sizeMultiplier, depth.available))\n\n  EMIT OrderIntent(market=market_id, outcome='YES', side='buy', price=mid,\n                   size_pUSD=orderSize, tif='IOC', builder=internalBuilderCode)\n  EMIT DecisionReport(intent_emitted=true, signal_score=signal.score,\n                      reason='MCS_TRADE')",
    "sdk_calls": [
      "ws_market.subscribe('book', [market_id])",
      "fetchClobPublic('/markets/' + market_id)",
      "internal.analyticsEngine.signal(market_id)",
      "buildOrderTypedData(orderParams, {name:'CTFExchange', version:'2', chainId:137})",
      "internal.builder_code"
    ],
    "complexity": "O(1) per signal update per market"
  },
  "api_surface": [
    "clob_public",
    "clob_auth",
    "ws_market",
    "gamma",
    "internal"
  ],
  "network": [
    "polygon"
  ],
  "version": {
    "spec": "2.0.0",
    "implementation": "0.1.0",
    "schema": "2",
    "released": null,
    "planned_release": "Q3-2026"
  },
  "migration_history": [
    {
      "date": "2026-04-28",
      "from": "n/a",
      "to": "v2-spec",
      "reason": "Spec drafted post-CLOB-V2 cutover; bot not yet implemented",
      "action_taken": "Designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain)"
    }
  ],
  "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": "Bot not yet implemented; designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain). feeRateBps not present on any signed OrderIntent."
  },
  "reporting": {
    "emits_kinds": [
      "DecisionReport"
    ],
    "topics": [
      "polytraders.reports.decision"
    ],
    "cadence": "every-event",
    "retention_class": "2y",
    "sampling_rule": "emit-every",
    "bus_failure_action": "fail-closed",
    "user_visible": "yes",
    "consumes_kinds": [
      "ObservationReport",
      "RiskVote"
    ]
  },
  "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"
  }
}