修复机构落地页构建失败并恢复 Polymarket 市场扫描
InstitutionalLandingPage.tsx 补充 "use client" 指令以通过 Next.js 15 构建。 polymarket_readonly.py 集成 WebSocket 报价缓存加速价格获取。 city_payloads.py 复用 Polymarket 层构建真实市场扫描数据替代空返回。 Constraint: 市场扫描需在无 Polymarket 价格 UI 的前提下提供数据 Tested: npm run build 通过
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Polymarket read-only market layer.
|
|||||||
P0 scope:
|
P0 scope:
|
||||||
- Market discovery from Gamma REST
|
- Market discovery from Gamma REST
|
||||||
- Price / midpoint / spread / orderbook read from CLOB REST
|
- Price / midpoint / spread / orderbook read from CLOB REST
|
||||||
|
- Optional WebSocket quote acceleration via PolymarketWsQuoteCache
|
||||||
- No signing, no order placement
|
- No signing, no order placement
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ import httpx
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from src.data_collection.city_registry import ALIASES, CITY_REGISTRY
|
from src.data_collection.city_registry import ALIASES, CITY_REGISTRY
|
||||||
|
from src.data_collection.polymarket_ws_cache import PolymarketWsQuoteCache
|
||||||
|
|
||||||
|
|
||||||
def _safe_float(value: Any) -> Optional[float]:
|
def _safe_float(value: Any) -> Optional[float]:
|
||||||
@@ -448,6 +450,9 @@ class PolymarketReadOnlyLayer:
|
|||||||
self._price_cache: Dict[str, Dict[str, Any]] = {}
|
self._price_cache: Dict[str, Dict[str, Any]] = {}
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
|
|
||||||
|
self._ws_cache = PolymarketWsQuoteCache.from_env()
|
||||||
|
self._ws_cache.start()
|
||||||
|
|
||||||
def _market_scan_debug_enabled(self) -> bool:
|
def _market_scan_debug_enabled(self) -> bool:
|
||||||
return (
|
return (
|
||||||
str(os.getenv("POLYMARKET_MARKET_SCAN_DEBUG", "false")).strip().lower()
|
str(os.getenv("POLYMARKET_MARKET_SCAN_DEBUG", "false")).strip().lower()
|
||||||
@@ -1769,6 +1774,14 @@ class PolymarketReadOnlyLayer:
|
|||||||
if cached and now - cached.get("t", 0) < self.price_cache_ttl:
|
if cached and now - cached.get("t", 0) < self.price_cache_ttl:
|
||||||
return cached.get("data", {})
|
return cached.get("data", {})
|
||||||
|
|
||||||
|
ws_data = self._ws_cache.get_market_data(token_id)
|
||||||
|
if ws_data:
|
||||||
|
with self._lock:
|
||||||
|
self._price_cache[token_id] = {"data": ws_data, "t": now}
|
||||||
|
return ws_data
|
||||||
|
|
||||||
|
self._ws_cache.subscribe([token_id])
|
||||||
|
|
||||||
data = self._fetch_token_market_data(token_id)
|
data = self._fetch_token_market_data(token_id)
|
||||||
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
|||||||
@@ -8,6 +8,26 @@ from web.core import _is_excluded_model_name
|
|||||||
|
|
||||||
TURKISH_MGM_CITIES = {"ankara", "istanbul"}
|
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]:
|
def build_city_summary_payload(data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
@@ -46,11 +66,41 @@ def build_city_market_scan_payload(
|
|||||||
requested_date = str(target_date or "").strip()
|
requested_date = str(target_date or "").strip()
|
||||||
selected_date = requested_date or local_date
|
selected_date = requested_date or local_date
|
||||||
|
|
||||||
return {
|
try:
|
||||||
"market_scan": {"available": False},
|
layer = _get_polymarket_layer()
|
||||||
"selected_date": selected_date,
|
probabilities = data.get("probabilities") or {}
|
||||||
"fetched_at": data.get("updated_at"),
|
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(
|
def build_city_detail_payload(
|
||||||
|
|||||||
Reference in New Issue
Block a user