From b5c1de1078fd607eb678c2602b8fe42a0964f669 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Tue, 21 Apr 2026 22:40:51 +0800 Subject: [PATCH] Add debug logging for Polymarket market scans --- scripts/debug_market_scan.py | 84 ++++++++++++++++++++++ src/data_collection/polymarket_readonly.py | 65 +++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 scripts/debug_market_scan.py diff --git a/scripts/debug_market_scan.py b/scripts/debug_market_scan.py new file mode 100644 index 00000000..7e4a9990 --- /dev/null +++ b/scripts/debug_market_scan.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import argparse +import importlib +import json +import os +import sys +from pathlib import Path +from typing import Any, Dict, Optional + + +PROJECT_ROOT = Path(__file__).resolve().parents[1] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) + + +def _safe_float(value: Optional[str]) -> Optional[float]: + if value is None: + return None + try: + return float(value) + except Exception: + return None + + +def _bucket_from_temp(value: Optional[str]) -> Optional[Dict[str, Any]]: + temp = _safe_float(value) + if temp is None: + return None + return { + "value": temp, + "temp": temp, + "label": f"{temp:g}°C", + } + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Print a read-only Polymarket market-scan diagnostic payload.", + ) + parser.add_argument("--city", required=True, help="City key or display name.") + parser.add_argument("--date", required=True, help="Target date, YYYY-MM-DD.") + parser.add_argument( + "--bucket-temp", + help="Optional model/probability bucket temperature in Celsius.", + ) + parser.add_argument( + "--model-probability", + help="Optional model probability. Accepts 0-1 or 0-100.", + ) + parser.add_argument( + "--market-slug", + help="Optional Polymarket event/market slug.", + ) + parser.add_argument( + "--debug-log", + action="store_true", + help="Also enable POLYMARKET_MARKET_SCAN_DEBUG for this process.", + ) + args = parser.parse_args() + + if args.debug_log: + os.environ["POLYMARKET_MARKET_SCAN_DEBUG"] = "true" + + model_probability = _safe_float(args.model_probability) + if model_probability is not None and model_probability > 1.0: + model_probability = model_probability / 100.0 + + module = importlib.import_module("src.data_collection.polymarket_readonly") + layer = module.PolymarketReadOnlyLayer() + scan = layer.build_market_scan( + city=args.city, + target_date=args.date, + temperature_bucket=_bucket_from_temp(args.bucket_temp), + model_probability=model_probability, + forced_market_slug=args.market_slug, + ) + + print(json.dumps(scan, ensure_ascii=False, indent=2, default=str)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/data_collection/polymarket_readonly.py b/src/data_collection/polymarket_readonly.py index 443eb301..8de611b5 100644 --- a/src/data_collection/polymarket_readonly.py +++ b/src/data_collection/polymarket_readonly.py @@ -407,6 +407,21 @@ class PolymarketReadOnlyLayer: self._clob_unavailable_reason: Optional[str] = None self._ws_quote_cache = PolymarketWsQuoteCache.from_env() + def _market_scan_debug_enabled(self) -> bool: + return ( + str(os.getenv("POLYMARKET_MARKET_SCAN_DEBUG", "false")).strip().lower() + in {"1", "true", "yes", "on"} + ) + + def _debug_market_scan(self, message: str, **payload: Any) -> None: + if not self._market_scan_debug_enabled(): + return + try: + details = json.dumps(payload, ensure_ascii=False, default=str) + except Exception: + details = str(payload) + logger.info(f"POLYMARKET_MARKET_SCAN_DEBUG {message} {details}") + def build_market_scan( self, city: Any, @@ -451,14 +466,17 @@ class PolymarketReadOnlyLayer: if not self.enabled: scan["reason"] = "Market scan disabled by POLYMARKET_MARKET_SCAN_ENABLED." + self._debug_market_scan("disabled", city=city_key, date=date_str) return scan if not city_key or city_key not in CITY_REGISTRY: scan["reason"] = "City is not supported by the Polymarket market layer." + self._debug_market_scan("unsupported_city", city=city, normalized=city_key) return scan if not date_str: scan["reason"] = "Missing target date for market discovery." + self._debug_market_scan("missing_date", city=city_key) return scan try: @@ -474,10 +492,18 @@ class PolymarketReadOnlyLayer: except Exception as exc: logger.warning(f"Polymarket market discovery failed ({city_key}): {exc}") scan["reason"] = "Market discovery failed." + self._debug_market_scan("discovery_exception", city=city_key, error=str(exc)) return scan if not market: scan["reason"] = reason or "No active Polymarket market matched city/date." + self._debug_market_scan( + "no_market", + city=city_key, + date=date_str, + forced_slug=requested_slug, + reason=scan["reason"], + ) return scan market_date = self._extract_market_date(market) @@ -528,6 +554,13 @@ class PolymarketReadOnlyLayer: scan["selected_slug"] = market_slug scan["liquidity"] = liquidity scan["volume"] = volume + self._debug_market_scan( + "not_tradable", + city=city_key, + date=date_str, + slug=market_slug, + trade_state=trade_state, + ) return scan tokens = self._extract_market_tokens(market) @@ -539,6 +572,13 @@ class PolymarketReadOnlyLayer: scan["selected_slug"] = market_slug scan["liquidity"] = liquidity scan["volume"] = volume + self._debug_market_scan( + "missing_tokens", + city=city_key, + date=date_str, + slug=market_slug, + token_count=len(tokens), + ) return scan yes_prices = self._get_token_market_data(str(yes_token.get("token_id"))) @@ -663,6 +703,31 @@ class PolymarketReadOnlyLayer: }, } ) + self._debug_market_scan( + "scan_ready", + city=city_key, + date=date_str, + selected_slug=market_slug, + market_price=scan.get("market_price"), + yes_buy=scan.get("yes_buy"), + yes_sell=scan.get("yes_sell"), + no_buy=scan.get("no_buy"), + no_sell=scan.get("no_sell"), + price_analysis_available=bool(price_analysis and price_analysis.get("available")), + all_buckets_count=len(all_buckets), + top_buckets=[ + { + "temp": row.get("temp"), + "yes_buy": row.get("yes_buy"), + "market_price": row.get("market_price"), + "quote_source": row.get("quote_source"), + "slug": row.get("slug"), + } + for row in all_buckets[:6] + if isinstance(row, dict) + ], + websocket=scan.get("websocket"), + ) return scan def _hydrate_bucket_prices(self, buckets: List[Dict[str, Any]]) -> None: