feat: Implement initial PolyWeather application with interactive map UI, backend API, and Polymarket data client.

This commit is contained in:
2569718930@qq.com
2026-03-06 09:38:28 +08:00
parent b308709edb
commit d9876256b3
18 changed files with 755 additions and 1097 deletions
+85
View File
@@ -577,6 +577,91 @@ async def city_detail(name: str, force_refresh: bool = False):
return _analyze(name, force_refresh=force_refresh)
def _normalize_city_or_404(name: str) -> str:
city = name.lower().strip().replace("-", " ")
city = ALIASES.get(city, city)
if city not in CITIES:
raise HTTPException(404, detail=f"Unknown city: {city}")
return city
def _resolve_target_date(city: str, target_date: Optional[str]) -> str:
"""
Resolve requested market date. If absent, default to current local date of city.
"""
if target_date:
try:
datetime.strptime(target_date, "%Y-%m-%d")
except Exception:
raise HTTPException(400, detail="target_date must be YYYY-MM-DD")
return target_date
tz_seconds = CITIES.get(city, {}).get("tz", 0)
return (datetime.now(timezone.utc) + timedelta(seconds=tz_seconds)).strftime(
"%Y-%m-%d"
)
@app.get("/api/polymarket/{name}")
async def city_polymarket_snapshot(
name: str,
target_date: Optional[str] = None,
force_refresh: bool = False,
):
"""
Return Polymarket city/date market snapshot with buy/sell prices and spreads.
"""
city = _normalize_city_or_404(name)
resolved_date = _resolve_target_date(city, target_date)
from src.data_collection.polymarket_client import build_city_market_snapshot
proxy = (
(_config.get("polymarket", {}) or {}).get("proxy")
or (_config.get("app", {}) or {}).get("proxy")
)
snapshot = build_city_market_snapshot(
city=city,
target_date=resolved_date,
proxy=proxy,
force_refresh=force_refresh,
)
return snapshot
@app.get("/api/polymarket/{name}/alerts")
async def city_polymarket_alerts(
name: str,
target_date: Optional[str] = None,
force_refresh: bool = False,
):
"""
Return only anomaly alerts for Polymarket city/date orderbooks.
"""
city = _normalize_city_or_404(name)
resolved_date = _resolve_target_date(city, target_date)
from src.data_collection.polymarket_client import build_city_market_snapshot
proxy = (
(_config.get("polymarket", {}) or {}).get("proxy")
or (_config.get("app", {}) or {}).get("proxy")
)
snapshot = build_city_market_snapshot(
city=city,
target_date=resolved_date,
proxy=proxy,
force_refresh=force_refresh,
)
return {
"city": snapshot.get("city"),
"target_date": snapshot.get("target_date"),
"updated_at": snapshot.get("updated_at"),
"summary": snapshot.get("summary"),
"alerts": snapshot.get("alerts", []),
}
@app.get("/api/history/{name}")
async def city_history(name: str):
"""Return historical accuracy data (DEB, mu, actuals) for a city."""
+4 -9
View File
@@ -88,15 +88,10 @@ function updateMapVisibility() {
if (!map.hasLayer(nearbyLayerGroup)) map.addLayer(nearbyLayerGroup);
}
// 2. Handle Primary City Markers (Major vs Minor)
Object.values(markers).forEach(({ marker, city }) => {
const isMajor = city.is_major !== false;
// Hide minor cities (like Ankara/Atlanta) when zoomed way out
if (zoom < 4 && !isMajor) {
if (map.hasLayer(marker)) map.removeLayer(marker);
} else {
if (!map.hasLayer(marker)) map.addLayer(marker);
}
// 2. Keep all primary city markers visible at all zoom levels.
// This avoids cities like Ankara disappearing when zoomed out.
Object.values(markers).forEach(({ marker }) => {
if (!map.hasLayer(marker)) map.addLayer(marker);
});
}