更新 CLAUDE.md:Polymarket 已全部移除,终端仅气象数据

This commit is contained in:
2569718930@qq.com
2026-05-25 20:03:06 +08:00
parent 3bc1a4a1ed
commit d3c8058178
9 changed files with 916 additions and 755 deletions
+750 -748
View File
File diff suppressed because it is too large Load Diff
+60
View File
@@ -7,6 +7,7 @@ from fastapi import APIRouter, BackgroundTasks, Query, Request
from web.services.city_api import (
get_city_detail_aggregate_payload,
get_city_detail_payload,
get_city_market_scan_payload,
get_city_summary_payload,
list_cities_payload,
)
@@ -151,6 +152,65 @@ async def city_detail_aggregate(
)
@router.get("/api/city/{name}/market-scan")
async def city_market_scan(
request: Request,
background_tasks: BackgroundTasks,
name: str,
force_refresh: bool = False,
market_slug: Optional[str] = None,
target_date: Optional[str] = None,
lite: bool = False,
):
return await get_city_market_scan_payload(
request,
background_tasks,
name,
force_refresh=force_refresh,
market_slug=market_slug,
target_date=target_date,
lite=lite,
)
@router.get("/api/city/{name}/holders")
async def city_holders(
name: str,
limit: int = 10,
):
"""Return top token holders for a city's primary Polymarket market."""
from web.services.city_payloads import _get_polymarket_layer
from web.analysis_service import _analyze
layer = _get_polymarket_layer()
if not layer.enabled:
return {"holders": [], "available": False}
data = _analyze(name, force_refresh=False, include_llm_commentary=False, detail_mode="market")
local_date = str(data.get("local_date") or "").strip()
if not local_date:
return {"holders": [], "available": False}
scan = layer.build_market_scan(
city=name,
target_date=local_date,
include_related_buckets=False,
)
if not scan.get("available"):
return {"holders": [], "available": False}
condition_id = scan.get("selected_condition_id")
if not condition_id:
return {"holders": [], "available": False}
holders = layer.get_market_holders(condition_id, limit=limit)
return {
"holders": holders,
"available": True,
"condition_id": condition_id,
}
@router.get("/api/city/{name}/realtime-stream")
async def city_realtime_stream(name: str):
"""Return a rolling window of recent temperature readings + market
+3 -1
View File
@@ -30,6 +30,7 @@ async def scan_terminal(
force_refresh: bool = False,
region: str = "",
trading_region: str = "",
skip_polymarket: bool = False,
timezone_offset_seconds: int | None = None,
):
return await get_scan_terminal_payload(
@@ -45,7 +46,8 @@ async def scan_terminal(
limit=limit,
force_refresh=force_refresh,
region=region or trading_region or None,
timezone_offset_seconds=timezone_offset_seconds,
skip_polymarket=skip_polymarket,
timezone_offset_seconds=timezone_offset_seconds,
)
+9 -2
View File
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional
from web.core import CITIES
from web.analysis_service import _analyze
from web.analysis_service import _analyze, _build_city_market_scan_payload
from web.scan_city_ai_helpers import _safe_float
from web.scan_terminal_ai_compact import _build_metar_decision_context
from web.scan_terminal_filters import (
@@ -164,7 +164,14 @@ def _scan_city_terminal_rows(
candidate_total = 0
for target_date in target_dates:
scan = {"available": False}
payload = _build_city_market_scan_payload(
data,
market_slug=None,
target_date=target_date,
lite=True,
scan_filters=filters,
)
scan = payload.get("market_scan") or {}
candidate_total += int(scan.get("candidate_count") or 0)
raw_rows = scan.get("scan_rows")
if not isinstance(raw_rows, list) or not raw_rows:
+1 -1
View File
@@ -48,7 +48,7 @@ def normalize_scan_terminal_filters(
or "today",
"limit": max(1, min(safe_int(raw.get("limit"), 25), 200)),
"max_spread": max(0.0, _safe_float(raw.get("max_spread")) or 0.03),
"skip_polymarket": str(raw.get("skip_polymarket") or "false").lower()
in {"1", "true", "yes", "on"},
}
trading_region = str(raw.get("trading_region") or "").strip().lower()
+7 -1
View File
@@ -222,4 +222,10 @@ async def get_city_market_scan_payload(
)
if cached_scan is not None:
return cached_scan
return {"market_scan": {"available": False}, "selected_date": target_date or str(data.get("local_date", "")), "fetched_at": data.get("updated_at")}
return await run_in_threadpool(
legacy_routes._build_city_market_scan_payload,
data,
market_slug,
target_date,
lite,
)
+77 -1
View File
@@ -8,6 +8,27 @@ from web.core import _is_excluded_model_name
TURKISH_MGM_CITIES = {"ankara", "istanbul"}
_polymarket_layer = None
def _get_polymarket_layer():
global _polymarket_layer
if _polymarket_layer is None:
from src.data_collection.polymarket_readonly import PolymarketReadOnlyLayer
_polymarket_layer = PolymarketReadOnlyLayer()
return _polymarket_layer
def _top_probability_bucket(distribution: Any) -> Optional[Dict[str, Any]]:
if not isinstance(distribution, list):
return None
candidates = [row for row in distribution if isinstance(row, dict)]
if not candidates:
return None
return max(candidates, key=lambda row: float(row.get("probability") or -1.0))
def build_city_summary_payload(data: Dict[str, Any]) -> Dict[str, Any]:
return {
"name": data.get("name"),
@@ -33,11 +54,66 @@ def build_city_summary_payload(data: Dict[str, Any]) -> Dict[str, Any]:
"updated_at": data.get("updated_at"),
}
def build_city_market_scan_payload(
data: Dict[str, Any],
market_slug: Optional[str] = None,
target_date: Optional[str] = None,
lite: bool = False,
scan_filters: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
local_date = str(data.get("local_date") or "").strip()
requested_date = str(target_date or "").strip()
selected_date = requested_date or local_date
try:
layer = _get_polymarket_layer()
probabilities = data.get("probabilities") or {}
distribution = probabilities.get("distribution") or []
top_bucket = _top_probability_bucket(distribution)
model_probability = (
float(top_bucket.get("probability"))
if (isinstance(top_bucket, dict) and top_bucket.get("probability") is not None)
else None
)
scan = layer.build_market_scan(
city=data.get("name"),
target_date=selected_date,
temperature_bucket=top_bucket,
model_probability=model_probability,
probability_distribution=distribution,
temp_symbol=str(data.get("temp_symbol") or ""),
forced_market_slug=market_slug,
include_related_buckets=not lite,
scan_filters=scan_filters,
)
return {
"market_scan": scan,
"selected_date": selected_date,
"fetched_at": data.get("updated_at"),
}
except Exception:
import traceback
traceback.print_exc()
return {
"market_scan": {"available": False},
"selected_date": selected_date,
"fetched_at": data.get("updated_at"),
}
def build_city_detail_payload(
data: Dict[str, Any],
market_slug: Optional[str] = None,
target_date: Optional[str] = None,
) -> Dict[str, Any]:
market_payload = build_city_market_scan_payload(
data,
market_slug=market_slug,
target_date=target_date,
)
market_scan = market_payload.get("market_scan")
return {
"city": data.get("name"),
"fetched_at": data.get("updated_at"),
@@ -124,7 +200,7 @@ def build_city_detail_payload(
or _build_intraday_meteorology(data),
"vertical_profile_signal": data.get("vertical_profile_signal") or {},
"taf": data.get("taf") or {},
"market_scan": {"available": False},
"market_scan": market_scan,
"risk": data.get("risk"),
"settlement_station": data.get("settlement_station") or {},
"airport_primary": data.get("airport_primary") or {},
+7 -1
View File
@@ -24,6 +24,7 @@ from web.analysis_service import (
_analyze,
_analyze_summary,
_build_city_detail_payload, # noqa: F401 - compatibility export for tests and transitional routers
_build_city_market_scan_payload,
_build_city_summary_payload,
)
from web.scan_terminal_service import (
@@ -194,7 +195,12 @@ def _attach_market_scan_payload(
) -> dict:
if not isinstance(payload, dict):
return payload
scan_payload = {"market_scan": {"available": False}, "selected_date": target_date or "", "fetched_at": payload.get("updated_at")}
scan_payload = _build_city_market_scan_payload(
payload,
market_slug=market_slug,
target_date=target_date,
lite=lite,
)
now_ts = time.time()
payload["market_scan_payload"] = scan_payload
payload["market_scan_updated_at"] = datetime.now().isoformat()
+2
View File
@@ -47,6 +47,7 @@ async def get_scan_terminal_payload(
limit: int = 25,
force_refresh: bool = False,
region: str = "",
skip_polymarket: bool = False,
timezone_offset_seconds: int | None = None,
) -> Dict[str, Any]:
legacy_routes._assert_entitlement(request)
@@ -60,6 +61,7 @@ async def get_scan_terminal_payload(
"market_type": market_type,
"time_range": time_range,
"limit": limit,
"skip_polymarket": skip_polymarket,
}
if timezone_offset_seconds is not None:
filters["timezone_offset_seconds"] = timezone_offset_seconds