From 7b8826dfa14f881e89c54549c57f8e7479ec05f1 Mon Sep 17 00:00:00 2001
From: "2569718930@qq.com" <2569718930@qq.com>
Date: Mon, 25 May 2026 20:53:03 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=9C=80=E5=90=8E=E4=B8=80?=
=?UTF-8?q?=E6=89=B9=20Polymarket=20=E6=B3=A8=E9=87=8A+=E6=AD=BB=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=20WeatherDecisionBand?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../dashboard/ScanTerminalDashboard.tsx | 136 +++++++-----
.../scan-terminal/AirportEvidencePanel.tsx | 2 +-
.../scan-terminal/CitySelectorDropdown.tsx | 201 ++++++++++++++++++
.../LiveTemperatureThresholdChart.tsx | 46 ++--
.../dashboard/scan-terminal/Panel.tsx | 2 +-
.../scan-terminal/WeatherDecisionBand.tsx | 35 ---
.../ops/health/HealthPageClient.tsx | 2 -
frontend/content/docs/docs.ts | 4 +-
src/data_collection/city_registry.py | 2 +-
src/data_collection/city_risk_profiles.py | 2 +-
src/data_collection/weather_sources.py | 4 +-
src/utils/telegram_push.py | 2 +-
web/scan_terminal_city_row.py | 2 +-
13 files changed, 328 insertions(+), 112 deletions(-)
create mode 100644 frontend/components/dashboard/scan-terminal/CitySelectorDropdown.tsx
delete mode 100644 frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx
diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx
index b72384f0..74b263f3 100644
--- a/frontend/components/dashboard/ScanTerminalDashboard.tsx
+++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx
@@ -46,6 +46,7 @@ import { TrainingDashboard } from "@/components/dashboard/scan-terminal/Training
import { LiveTemperatureThresholdChart } from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
import { KoyfinRowsTable } from "@/components/dashboard/scan-terminal/KoyfinRowsTable";
import { rowName, pct, money, temp, edgeClass } from "@/components/dashboard/scan-terminal/utils";
+import { CitySelectorDropdown } from "@/components/dashboard/scan-terminal/CitySelectorDropdown";
function createEmptyAccess(loading = true): ProAccessState {
return {
@@ -248,20 +249,21 @@ function EmptySlotCard({
slotIndex,
isActive,
isEn,
- availableCities,
onSelectSlot,
- onSelectCity,
+ onOpenSearch,
}: {
slotIndex: number;
isActive: boolean;
isEn: boolean;
- availableCities: { city: string; name: string }[];
onSelectSlot: () => void;
- onSelectCity: (city: string) => void;
+ onOpenSearch: () => void;
}) {
return (
{
+ onSelectSlot();
+ onOpenSearch();
+ }}
className={clsx(
"flex flex-col items-center justify-center h-full rounded-[4px] border-2 border-dashed p-6 cursor-pointer bg-slate-50/50 transition-all",
isActive
@@ -277,24 +279,15 @@ function EmptySlotCard({
{isEn
- ? "Select this card and click a city on the left, or select below:"
- : "激活此卡片并从左侧选择城市,或从下方直接选择:"}
+ ? "Click to choose a city weather chart for this slot."
+ : "点击为该槽位选择一个城市天气图表。"}
- e.stopPropagation()} // prevent triggering onSelectSlot
- onChange={(e) => {
- if (e.target.value) onSelectCity(e.target.value);
- }}
- className="text-[11px] font-semibold text-slate-600 px-2 py-1 rounded border border-slate-300 bg-white outline-none max-w-[160px]"
+
- {isEn ? "Choose city..." : "选择城市..."}
- {availableCities.map((c) => (
-
- {c.name}
-
- ))}
-
+ {isEn ? "Choose City..." : "选择城市..."}
+
);
}
@@ -376,6 +369,7 @@ function PolyWeatherTerminal({
});
const [activeSlotIndex, setActiveSlotIndex] = useState(0);
const [maximizedSlotIndex, setMaximizedSlotIndex] = useState(null);
+ const [activeSearchSlotIndex, setActiveSearchSlotIndex] = useState(null);
const NAV_ITEMS = [
{ key: "contracts", Icon: Table2, labelEn: "Contracts", labelZh: "天气合约" },
@@ -388,6 +382,7 @@ function PolyWeatherTerminal({
}, [selectedRegionKey, setSelectedCity]);
const filteredRegionRows = useMemo(() => {
+ if (selectedRegionKey === "all") return rows;
return rows.filter(
(row) => getCityRegion(row) === selectedRegionKey,
);
@@ -395,7 +390,12 @@ function PolyWeatherTerminal({
useEffect(() => {
if (filteredRegionRows.length && slots.every((s) => s === null)) {
- const next = [filteredRegionRows[0].city, null, null, null];
+ const next = [
+ filteredRegionRows[0]?.city || null,
+ filteredRegionRows[1]?.city || null,
+ filteredRegionRows[2]?.city || null,
+ filteredRegionRows[3]?.city || null,
+ ];
setSlots(next);
try {
localStorage.setItem("polyweather_terminal_slots", JSON.stringify(next));
@@ -677,29 +677,19 @@ function PolyWeatherTerminal({
{/* Desktop layout */}
-
-
- handleSelectCityForSlot(activeSlotIndex, city)}
- slots={slots}
- activeSlotIndex={activeSlotIndex}
- />
-
-
-
+
+
{maximizedSlotIndex !== null ? (
// Maximized view
setActiveSlotIndex(maximizedSlotIndex)}
className={clsx(
- "relative h-full rounded-[4px] border overflow-hidden border-blue-500 ring-2 ring-blue-500/20 shadow-md z-10"
+ "relative h-full rounded-[4px] border border-blue-500 ring-2 ring-blue-500/20 shadow-md z-10",
+ activeSearchSlotIndex === maximizedSlotIndex ? "" : "overflow-hidden"
)}
>
{/* Floating actions toolbar */}
-
+
{
@@ -736,7 +726,21 @@ function PolyWeatherTerminal({
row={filteredRegionRows.find((r) => String(r.city || "").toLowerCase() === slots[maximizedSlotIndex]) || null}
allRows={filteredRegionRows}
compact={false}
+ onSearchClick={() => setActiveSearchSlotIndex(maximizedSlotIndex)}
/>
+
+ {activeSearchSlotIndex === maximizedSlotIndex && (
+ {
+ handleSelectCityForSlot(maximizedSlotIndex, city);
+ setActiveSearchSlotIndex(null);
+ }}
+ onClose={() => setActiveSearchSlotIndex(null)}
+ className="absolute left-3 top-9 z-50 w-[280px] bg-white border border-slate-200 rounded shadow-lg p-2"
+ />
+ )}
) : (
// 2x2 grid layout
@@ -747,15 +751,28 @@ function PolyWeatherTerminal({
if (!cityInSlot) {
return (
-
setActiveSlotIndex(slotIndex)}
- onSelectCity={(city) => handleSelectCityForSlot(slotIndex, city)}
- />
+
+ setActiveSlotIndex(slotIndex)}
+ onOpenSearch={() => setActiveSearchSlotIndex(slotIndex)}
+ />
+
+ {activeSearchSlotIndex === slotIndex && (
+ {
+ handleSelectCityForSlot(slotIndex, city);
+ setActiveSearchSlotIndex(null);
+ }}
+ onClose={() => setActiveSearchSlotIndex(null)}
+ className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 w-[280px] bg-white border border-slate-200 rounded shadow-lg p-2"
+ />
+ )}
+
);
}
@@ -768,14 +785,15 @@ function PolyWeatherTerminal({
key={slotIndex}
onClick={() => setActiveSlotIndex(slotIndex)}
className={clsx(
- "relative h-full rounded-[4px] border overflow-hidden transition-all",
+ "relative h-full rounded-[4px] border transition-all",
isSlotActive
? "border-blue-500 ring-2 ring-blue-500/20 shadow-md z-10"
- : "border-[#d2d9e2] hover:border-slate-400"
+ : "border-[#d2d9e2] hover:border-slate-400",
+ activeSearchSlotIndex === slotIndex ? "" : "overflow-hidden"
)}
>
{/* Floating actions toolbar */}
-
+
{
@@ -812,7 +830,21 @@ function PolyWeatherTerminal({
row={rowForSlot}
allRows={filteredRegionRows}
compact={true}
+ onSearchClick={() => setActiveSearchSlotIndex(slotIndex)}
/>
+
+ {activeSearchSlotIndex === slotIndex && (
+ {
+ handleSelectCityForSlot(slotIndex, city);
+ setActiveSearchSlotIndex(null);
+ }}
+ onClose={() => setActiveSearchSlotIndex(null)}
+ className="absolute left-3 top-9 z-50 w-[280px] bg-white border border-slate-200 rounded shadow-lg p-2"
+ />
+ )}
);
})}
@@ -845,9 +877,9 @@ function ScanTerminalScreen() {
hydrated && (proAccess.subscriptionActive || canUseLocalFullAccess);
const userLocalTime = useUserLocalClock();
const { themeMode } = useScanTerminalTheme();
- const [selectedRegionKey, setSelectedRegionKey] = useState
("east_asia");
+ const [selectedRegionKey, setSelectedRegionKey] = useState("all");
const [localTimezoneOffsetSeconds, setLocalTimezoneOffsetSeconds] = useState(null);
- const [useLocalTimezoneDefault, setUseLocalTimezoneDefault] = useState(true);
+ const [useLocalTimezoneDefault, setUseLocalTimezoneDefault] = useState(false);
const [visibleRegions, setVisibleRegions] = useState>(() => {
try {
const stored = localStorage.getItem("polyweather_visible_regions");
@@ -938,7 +970,7 @@ function ScanTerminalScreen() {
}, []);
useEffect(() => {
- setSelectedRegionKey(getDefaultRegion());
+ setSelectedRegionKey("all");
setLocalTimezoneOffsetSeconds(-new Date().getTimezoneOffset() * 60);
}, []);
diff --git a/frontend/components/dashboard/scan-terminal/AirportEvidencePanel.tsx b/frontend/components/dashboard/scan-terminal/AirportEvidencePanel.tsx
index b61798d6..389d9e67 100644
--- a/frontend/components/dashboard/scan-terminal/AirportEvidencePanel.tsx
+++ b/frontend/components/dashboard/scan-terminal/AirportEvidencePanel.tsx
@@ -4,7 +4,7 @@ import type { CityDetail } from "@/lib/dashboard-types";
import { getDisplayAirportPrimary } from "@/lib/airport-observation-display";
import { formatTemperatureValue } from "@/lib/temperature-utils";
-// Settlement runway mapping — matches Polymarket settlement anchors
+// Settlement runway mapping — matches settlement anchors
const SETTLEMENT_RUNWAY_PAIRS: Record> = {
shanghai: [["17L", "35R"]],
beijing: [["01", "19"]],
diff --git a/frontend/components/dashboard/scan-terminal/CitySelectorDropdown.tsx b/frontend/components/dashboard/scan-terminal/CitySelectorDropdown.tsx
new file mode 100644
index 00000000..069e0476
--- /dev/null
+++ b/frontend/components/dashboard/scan-terminal/CitySelectorDropdown.tsx
@@ -0,0 +1,201 @@
+"use client";
+
+import { useEffect, useMemo, useRef, useState } from "react";
+import clsx from "clsx";
+import type { ScanOpportunityRow } from "@/lib/dashboard-types";
+import { REGIONS, getCityRegion } from "./continent-grouping";
+import { rowName, temp } from "./utils";
+
+interface CitySelectorDropdownProps {
+ isEn: boolean;
+ rows: ScanOpportunityRow[];
+ onSelectCity: (city: string) => void;
+ onClose: () => void;
+ className?: string;
+}
+
+export function CitySelectorDropdown({
+ isEn,
+ rows,
+ onSelectCity,
+ onClose,
+ className,
+}: CitySelectorDropdownProps) {
+ const containerRef = useRef(null);
+ const inputRef = useRef(null);
+ const [searchQuery, setSearchQuery] = useState("");
+ const [activeTab, setActiveTab] = useState("all");
+
+ // Auto-focus input on mount
+ useEffect(() => {
+ inputRef.current?.focus();
+ }, []);
+
+ // Handle click outside and Escape key
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
+ onClose();
+ }
+ };
+
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === "Escape") {
+ onClose();
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ document.addEventListener("keydown", handleKeyDown);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ document.removeEventListener("keydown", handleKeyDown);
+ };
+ }, [onClose]);
+
+ // Tab definitions
+ const tabs = useMemo(() => {
+ return [
+ { key: "all", labelEn: "ALL", labelZh: "全部" },
+ ...REGIONS.map((r) => ({
+ key: r.key,
+ labelEn: r.labelEn.replace("Asia", "").replace("America", "").trim() || r.labelEn,
+ labelZh: r.labelZh,
+ })),
+ ];
+ }, []);
+
+ // Filter rows
+ const filteredRows = useMemo(() => {
+ const q = searchQuery.toLowerCase().trim();
+ return rows.filter((row) => {
+ // 1. Region filter
+ if (activeTab !== "all") {
+ const region = getCityRegion(row);
+ if (region !== activeTab) return false;
+ }
+
+ // 2. Query filter
+ if (!q) return true;
+ const haystack = [
+ row.city,
+ row.city_display_name,
+ row.display_name,
+ row.airport,
+ ]
+ .filter(Boolean)
+ .map((s) => s!.toLowerCase());
+ return haystack.some((s) => s.includes(q));
+ });
+ }, [rows, searchQuery, activeTab]);
+
+ return (
+ e.stopPropagation()} // Prevent card activation
+ >
+ {/* Search Input Area */}
+
+ setSearchQuery(e.target.value)}
+ placeholder={isEn ? "Search city or airport..." : "搜索城市、机场..."}
+ className="w-full px-2.5 py-1.5 border border-slate-300 rounded bg-white text-xs outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/20"
+ />
+
+ ✕
+
+
+
+ {/* Region filter tabs */}
+
+ {tabs.map((tab) => {
+ const isActive = activeTab === tab.key;
+ return (
+ setActiveTab(tab.key)}
+ className={clsx(
+ "px-2 py-0.5 text-[10px] font-bold rounded whitespace-nowrap transition-all",
+ isActive
+ ? "bg-blue-600 text-white shadow-sm"
+ : "bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-700"
+ )}
+ >
+ {isEn ? tab.labelEn : tab.labelZh}
+
+ );
+ })}
+
+
+ {/* Scrollable list */}
+
+ {filteredRows.length === 0 ? (
+
+ {isEn ? "No matching cities" : "无匹配城市"}
+
+ ) : (
+ filteredRows.map((row) => {
+ const cityName = rowName(row);
+ const obsTemp = row.current_temp ?? row.current_max_so_far;
+ const debPrediction = row.deb_prediction;
+ const symbol = row.temp_symbol || "°C";
+
+ return (
+
onSelectCity(String(row.city || "").toLowerCase())}
+ className="flex w-full items-center justify-between px-3 py-2 text-left hover:bg-blue-50/50 transition-colors group"
+ >
+
+
+ {cityName}
+
+ {row.airport && (
+
+ {row.airport}
+
+ )}
+
+
+ {/* Weather info */}
+
+ {obsTemp !== undefined && obsTemp !== null && (
+
+ Obs
+
+ {obsTemp}
+ {symbol}
+
+
+ )}
+ {debPrediction !== undefined && debPrediction !== null && (
+
+ DEB
+
+ {debPrediction}
+ {symbol}
+
+
+ )}
+
+
+ );
+ })
+ )}
+
+
+ );
+}
diff --git a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx
index 8abacdca..d914bff4 100644
--- a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx
+++ b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx
@@ -832,11 +832,13 @@ export function LiveTemperatureThresholdChart({
row,
allRows = [],
compact = false,
+ onSearchClick,
}: {
isEn: boolean;
row: ScanOpportunityRow | null;
allRows?: ScanOpportunityRow[];
compact?: boolean;
+ onSearchClick?: () => void;
}) {
const [hourly, setHourly] = useState(null);
const city = String(row?.city || "").toLowerCase().trim();
@@ -1009,19 +1011,37 @@ export function LiveTemperatureThresholdChart({
[series, data],
);
- const panelTitle = row
- ? `${rowName(row)} · ${
- isEn
- ? timeframe === "1D"
- ? "Live & Forecast"
- : `${timeframe} Forecast`
- : timeframe === "1D"
- ? "实测与预测"
- : `${timeframe}预报`
- }`
- : isEn
- ? "Temperature Chart"
- : "气温图表";
+ const subtitle = row
+ ? isEn
+ ? timeframe === "1D"
+ ? "Live & Forecast"
+ : `${timeframe} Forecast`
+ : timeframe === "1D"
+ ? "实测与预测"
+ : `${timeframe}预报`
+ : "";
+
+ const panelTitle = row ? (
+
+
+ {rowName(row)}
+ {onSearchClick && ▼ }
+
+ ·
+ {subtitle}
+
+ ) : isEn ? (
+ "Temperature Chart"
+ ) : (
+ "气温图表"
+ );
const timeframeActions = (
diff --git a/frontend/components/dashboard/scan-terminal/Panel.tsx b/frontend/components/dashboard/scan-terminal/Panel.tsx
index 9cfb9740..fa77e8ab 100644
--- a/frontend/components/dashboard/scan-terminal/Panel.tsx
+++ b/frontend/components/dashboard/scan-terminal/Panel.tsx
@@ -10,7 +10,7 @@ export function Panel({
}: {
children: React.ReactNode;
className?: string;
- title: string;
+ title: React.ReactNode;
actions?: React.ReactNode;
}) {
return (
diff --git a/frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx b/frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx
deleted file mode 100644
index fb7d48f0..00000000
--- a/frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-
-import clsx from "clsx";
-import type { WeatherDecisionView } from "@/components/dashboard/scan-terminal/city-card-decision-utils";
-
-export function WeatherDecisionBand({
- decisionView,
- decisionWhyText,
- isEn,
- paceDeltaText,
-}: {
- decisionView: WeatherDecisionView;
- decisionWhyText: string;
- isEn: boolean;
- paceDeltaText: string;
-}) {
- return (
-
-
-
{decisionView.kicker}
-
{decisionView.action}
-
{decisionWhyText}
-
-
-
- {isEn ? "Weather range" : "天气区间"}
- {decisionView.targetRange}
-
-
- {isEn ? "Path delta" : "路径偏差"} {paceDeltaText}
-
-
-
- );
-}
diff --git a/frontend/components/ops/health/HealthPageClient.tsx b/frontend/components/ops/health/HealthPageClient.tsx
index 52625961..5e4008e7 100644
--- a/frontend/components/ops/health/HealthPageClient.tsx
+++ b/frontend/components/ops/health/HealthPageClient.tsx
@@ -27,8 +27,6 @@ const LABELS: Record
= {
hko: "HKO (香港)",
singapore_mss: "Singapore MSS",
cwa: "CWA (台湾)",
- polymarket_gamma: "Polymarket Gamma",
- polymarket_clob: "Polymarket CLOB",
amos: "AMOS (韩国跑道)",
amsc_awos: "AMSC AWOS (中国)",
noaa_wrh: "NOAA WRH (美国结算)",
diff --git a/frontend/content/docs/docs.ts b/frontend/content/docs/docs.ts
index 725c4cf5..e781170b 100644
--- a/frontend/content/docs/docs.ts
+++ b/frontend/content/docs/docs.ts
@@ -492,7 +492,7 @@ export const DOCS_PAGES: DocsPage[] = [
{
type: "bullets",
items: [
- "自动识别当前 Polymarket 页面中的城市,也支持手动切换。",
+ "自动识别当前页面中的城市,也支持手动切换。",
"展示城市档案:结算站点、站点距离、观测更新时间、周边站点数量。",
"展示今日日内走势(简版):DEB 走势与机场主站实况 / 官方增强站网对照,可悬停查看时间与温度。",
"展示多日最高温预报(简版),并提供一键刷新与跳转主站入口。",
@@ -566,7 +566,7 @@ export const DOCS_PAGES: DocsPage[] = [
{
type: "bullets",
items: [
- "Auto-detects the current Polymarket page city, with manual switching also available.",
+ "Auto-detects the current page city, with manual switching also available.",
"Shows a city profile with settlement station, station distance, observation timestamp, and nearby station count.",
"Shows a compact intraday chart with DEB versus airport-primary observations and official nearby-network observations, including hoverable time and temperature.",
"Shows a compact multi-day daily-high forecast, plus refresh and jump-to-site actions.",
diff --git a/src/data_collection/city_registry.py b/src/data_collection/city_registry.py
index a6f9d3bf..780d0f29 100644
--- a/src/data_collection/city_registry.py
+++ b/src/data_collection/city_registry.py
@@ -151,7 +151,7 @@ CITY_REGISTRY = {
"risk_emoji": "🟡",
"airport_name": "流浮山天文台站",
"distance_km": 0.0,
- "warning": "香港天文台流浮山站分钟级观测,Polymarket 深圳温度市场结算源;非机场 METAR,不可与 ZGSZ/VHHH 报文混用。",
+ "warning": "香港天文台流浮山站分钟级观测,深圳温度市场结算源;非机场 METAR,不可与 ZGSZ/VHHH 报文混用。",
},
"taipei": {
"name": "Taipei",
diff --git a/src/data_collection/city_risk_profiles.py b/src/data_collection/city_risk_profiles.py
index a98d4caa..b831a728 100644
--- a/src/data_collection/city_risk_profiles.py
+++ b/src/data_collection/city_risk_profiles.py
@@ -1,4 +1,4 @@
-# Polymarket 城市温度市场 - 数据偏差风险档案
+# 城市温度市场 - 数据偏差风险档案
# 基于 METAR 机场站与市区实际温度的系统性差异
from src.data_collection.city_registry import CITY_REGISTRY
diff --git a/src/data_collection/weather_sources.py b/src/data_collection/weather_sources.py
index a5063bff..afb9cf31 100644
--- a/src/data_collection/weather_sources.py
+++ b/src/data_collection/weather_sources.py
@@ -37,7 +37,7 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
- NWS (US National Weather Service)
- MGM (Turkish Meteorological Service)
- JMA / HKO / CWA (country official networks)
- - Polymarket (weather derivative markets)
+ - Weather derivative markets
"""
from src.data_collection.city_registry import CITY_REGISTRY
@@ -612,7 +612,7 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
def extract_city_from_question(self, question: str) -> Optional[str]:
"""
- 从 Polymarket 问题描述或 Slug 中提取城市名称
+ 从市场问题描述或 Slug 中提取城市名称
"""
q = question.lower()
diff --git a/src/utils/telegram_push.py b/src/utils/telegram_push.py
index 9d9899bb..65204460 100644
--- a/src/utils/telegram_push.py
+++ b/src/utils/telegram_push.py
@@ -595,7 +595,7 @@ HIGH_FREQ_AIRPORT_ICAO = {
"san francisco": "KSFO", "houston": "KHOU", "dallas": "KDAL",
"austin": "KAUS", "seattle": "KSEA",
}
-# Settlement runway mapping — matches Polymarket settlement anchor stations.
+# Settlement runway mapping — matches settlement anchor stations.
# Format: (low_number, high_number) order-independent; stored sorted for lookup.
SETTLEMENT_RUNWAY_PAIRS: Dict[str, Set[Tuple[str, str]]] = {
"shanghai": {("17L", "35R")},
diff --git a/web/scan_terminal_city_row.py b/web/scan_terminal_city_row.py
index 98992033..ff5e03b3 100644
--- a/web/scan_terminal_city_row.py
+++ b/web/scan_terminal_city_row.py
@@ -148,7 +148,7 @@ def _scan_city_terminal_rows_quick(
*,
force_refresh: bool = False,
) -> Dict[str, Any]:
- """Fast path that skips Polymarket matching — returns a single row per city
+ """Fast path that returns cached analysis rows only — returns a single row per city
with cached analysis data (Obs, DEB, probabilities) but no market prices."""
data = _analyze(
city,