From 3732dafb956391904b6b991368d4f023976bab24 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 25 May 2026 17:23:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=20=5Funused=5FCityGroupedTab?= =?UTF-8?q?le(137=E8=A1=8C)=20+=20KoyfinRowsTable=20=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E5=A4=96=E9=83=A8=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ScanTerminalDashboard 1060→841 行。 --- .../dashboard/ScanTerminalDashboard.tsx | 221 +----------------- web/scan_terminal_city_row.py | 13 +- 2 files changed, 11 insertions(+), 223 deletions(-) diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx index 938c9d40..f2ef4d44 100644 --- a/frontend/components/dashboard/ScanTerminalDashboard.tsx +++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx @@ -47,6 +47,7 @@ import { Panel } from "@/components/dashboard/scan-terminal/Panel"; import { TrainingDashboard } from "@/components/dashboard/scan-terminal/TrainingDashboard"; import { LiveTemperatureThresholdChart } from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart"; import { MarketOverviewView } from "@/components/dashboard/scan-terminal/MarketOverviewView"; +import { KoyfinRowsTable } from "@/components/dashboard/scan-terminal/KoyfinRowsTable"; import { rowName, pct, money, temp, edgeClass } from "@/components/dashboard/scan-terminal/utils"; function createEmptyAccess(loading = true): ProAccessState { @@ -168,89 +169,6 @@ function tablePrice(row: ScanOpportunityRow) { return formatPrice(row.midpoint, row.ask, row.bid); } -function KoyfinRowsTable({ - compact = false, - isEn, - onSelect, - rows, - selectedId, -}: { - compact?: boolean; - isEn: boolean; - onSelect: (row: ScanOpportunityRow) => void; - rows: ScanOpportunityRow[]; - selectedId?: string | null; -}) { - return ( - - - - - - - - - - - - {rows.map((row) => { - const edge = Number(row.edge_percent ?? row.signed_gap ?? row.gap ?? 0); - const positive = edge >= 0; - return ( - onSelect(row)} - className={clsx( - "cursor-pointer border-b border-slate-100 hover:bg-blue-50/70", - selectedId === row.id && "bg-blue-50", - )} - > - - - - - - - ); - })} - -
- - - {isEn ? "City" : "城市"} - - {isEn ? "Price" : "价格"} - - {isEn ? "Chg" : "变化"} - %
- - -
- {rowName(row)} -
-
- {row.target_label || row.market_question || row.airport || "--"} -
-
- {tablePrice(row)} - - {Number.isFinite(edge) ? `${positive ? "+" : ""}${edge.toFixed(1)}` : "--"} - - {pct(row.market_probability ?? row.market_event_probability ?? row.model_probability)} -
- ); -} - function CityRegionList({ isEn, rows, @@ -347,143 +265,6 @@ function CityContractDetail({ ); } -function _unused_CityGroupedTable({ - compact, - isEn, - onSelect, - rows, - selectedId, -}: { - compact?: boolean; - isEn: boolean; - onSelect: (row: ScanOpportunityRow) => void; - rows: ScanOpportunityRow[]; - selectedId?: string | null; -}) { - const [expandedCities, setExpandedCities] = useState>(new Set()); - - const cityGroups = useMemo(() => { - const map = new Map(); - rows.forEach((row) => { - const key = String(row.city || "").toLowerCase(); - if (!map.has(key)) map.set(key, []); - map.get(key)!.push(row); - }); - return Array.from(map.entries()) - .map(([city, cityRows]) => { - const best = cityRows.reduce((a, b) => - Math.abs(Number(b.edge_percent || 0)) > Math.abs(Number(a.edge_percent || 0)) ? b : a - ); - const buckets = [...cityRows].sort((a, b) => - Number(b.edge_percent || 0) - Number(a.edge_percent || 0) - ); - return { city, displayName: rowName(best), best, buckets }; - }) - .sort((a, b) => - Math.abs(Number(b.best.edge_percent || 0)) - Math.abs(Number(a.best.edge_percent || 0)) - ); - }, [rows]); - - const toggle = (city: string) => { - setExpandedCities((prev) => { - const next = new Set(prev); - if (next.has(city)) next.delete(city); - else next.add(city); - return next; - }); - }; - - const labelExpand = isEn ? "Expand" : "展开"; - const labelBuckets = isEn ? "buckets" : "档位"; - - return ( -
- - - - - - - - - - - - - - {cityGroups.map(({ city, displayName, best, buckets }) => { - const isExpanded = expandedCities.has(city); - const hasMultiple = buckets.length > 1; - return ( - - { onSelect(best); if (hasMultiple) toggle(city); }} - > - - - - - - - - - - {isExpanded && buckets.map((row) => ( - - - - - - - - - - ))} - - ); - })} - -
- - {isEn ? "City / Best Signal" : "城市 / 最佳信号"} - {isEn ? "Edge" : "优势"}{isEn ? "Model" : "模型"}LiveDEB{isEn ? "Mkt" : "盘口"}{isEn ? "Spr/Liq" : "价差/流动性"}
- - -
{displayName}
-
- {best.target_label || ""} - {hasMultiple && !isExpanded ? ` +${buckets.length - 1}` : ""} -
-
- {pct(best.edge_percent)} - {pct(best.model_probability ?? best.model_event_probability)}{temp(best.current_temp || best.current_max_so_far, best.temp_symbol)}{temp(best.deb_prediction, best.temp_symbol)}{formatPrice(best.midpoint, best.ask, best.bid)}{formatSpreadLiquidity(best.spread, best.book_liquidity ?? best.market_liquidity)}
- - {row.target_label || "--"} - - {pct(row.edge_percent)} - {pct(row.model_probability ?? row.model_event_probability)}{temp(row.current_temp || row.current_max_so_far, row.temp_symbol)}{temp(row.deb_prediction, row.temp_symbol)}{formatPrice(row.midpoint, row.ask, row.bid)}{formatSpreadLiquidity(row.spread, row.book_liquidity ?? row.market_liquidity)}
-
- ); -} - function PolyWeatherTerminal({ generatedText, isEn, diff --git a/web/scan_terminal_city_row.py b/web/scan_terminal_city_row.py index 1dc69eac..37515d06 100644 --- a/web/scan_terminal_city_row.py +++ b/web/scan_terminal_city_row.py @@ -236,6 +236,11 @@ def _build_quick_row( distribution = probs.get("distribution") or [] local_date = str(data.get("local_date") or "") local_time = str(data.get("local_time") or "") + city_meta = CITIES.get(city) or {} + tz_offset = data.get("utc_offset_seconds") + if tz_offset is None: + tz_offset = _safe_int(city_meta.get("tz"), 0) + market_region = _market_region_from_tz_offset(tz_offset) id_parts = [city, local_date or "today"] if data.get("temp_symbol") == "°F": @@ -249,7 +254,7 @@ def _build_quick_row( "airport": str(risk.get("airport") or ""), "local_date": local_date, "local_time": local_time, - "tz_offset_seconds": data.get("utc_offset_seconds"), + "tz_offset_seconds": tz_offset, "temp_symbol": data.get("temp_symbol"), "risk_level": risk.get("level"), "current_temp": curr.get("temp"), @@ -260,8 +265,10 @@ def _build_quick_row( if v is not None }, "distribution_preview": distribution[:6] if distribution else [], - "trading_region": data.get("trading_region"), - "trading_region_sort": data.get("trading_region_sort"), + "trading_region": market_region["key"], + "trading_region_label": market_region["label_en"], + "trading_region_label_zh": market_region["label_zh"], + "trading_region_sort": market_region.get("sort_order", 0), "active": True, "closed": False, "tradable": False,