From dc5bd9a49673cf532da76d9a2dd382a755fa70b2 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 25 May 2026 06:18:55 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E7=83=AD=E7=BA=BF=E7=A8=8B=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20I/O=20=E5=BC=82=E5=B8=B8=E4=BF=9D=E6=8A=A4=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E8=BF=9B=E7=A8=8B=E5=85=B3=E9=97=AD=E6=97=B6?= =?UTF-8?q?=20file=20closed=20=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/ScanTerminalDashboard.tsx | 2 - .../LiveTemperatureThresholdChart.tsx | 35 ++++++++++++++++ web/scan_terminal_service.py | 42 ++++++++++++------- 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx index eecd9fc4..e772ad67 100644 --- a/frontend/components/dashboard/ScanTerminalDashboard.tsx +++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx @@ -8,7 +8,6 @@ import { ChevronLeft, ChevronRight, GraduationCap, - LineChart, Menu, Search, Table2, @@ -473,7 +472,6 @@ function PolyWeatherTerminal({ const NAV_ITEMS = [ { key: "contracts", Icon: Table2, labelEn: "Contracts", labelZh: "天气合约" }, - { key: "signals", Icon: LineChart, labelEn: "Signals", labelZh: "交易信号" }, { key: "markets", Icon: Activity, labelEn: "Markets", labelZh: "市场概览" }, { key: "training", Icon: GraduationCap, labelEn: "Training", labelZh: "训练数据" }, ]; diff --git a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx index 157db067..81ee6ff5 100644 --- a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx +++ b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx @@ -364,6 +364,27 @@ function buildChartDomain( return [Number((min - padding).toFixed(1)), Number((max + padding).toFixed(1))]; } +function buildProbRefLines(row: ScanOpportunityRow | null) { + const buckets = row?.distribution_full?.length + ? row.distribution_full + : row?.distribution_preview; + if (!buckets?.length) return []; + const maxProb = Math.max(...buckets.map((b) => b.model_probability ?? 0), 0.01); + return buckets + .filter((b) => validNumber(b.value) !== null && (b.model_probability ?? 0) > 0) + .map((b) => { + const prob = b.model_probability ?? 0; + const intensity = prob / maxProb; // 0..1 + return { + y: b.value!, + prob, + stroke: `rgba(37, 99, 235, ${(0.12 + intensity * 0.55).toFixed(2)})`, + width: 0.6 + intensity * 2.4, + label: `${(prob * 100).toFixed(0)}%`, + }; + }); +} + export function LiveTemperatureThresholdChart({ isEn, row, @@ -414,6 +435,7 @@ export function LiveTemperatureThresholdChart({ .slice(0, 5) .map((item) => ({ ...item, ...seriesStats(item.values) })); const marketTemperatureTicks = useMemo(() => buildMarketTemperatureOptions(row), [row]); + const probRefLines = useMemo(() => buildProbRefLines(row), [row]); const chartDomain = useMemo( () => buildChartDomain(marketTemperatureTicks, series), [marketTemperatureTicks, series], @@ -489,6 +511,19 @@ export function LiveTemperatureThresholdChart({ label={{ value: `UMA ${threshold.toFixed(1)}°`, fill: "#f97316", fontSize: 10, position: "left" }} /> )} + {probRefLines.map((item, i) => ( + = 0.15 + ? { value: item.label, fill: "#2563eb", fontSize: 9, position: "insideRight" } + : undefined + } + /> + ))} None: def _run(): started = time.time() ok = 0 - workers = max(1, min(SCAN_TERMINAL_MAX_WORKERS, len(city_names))) - with ThreadPoolExecutor(max_workers=workers) as ex: - futures = {ex.submit(_warm_one, c): c for c in city_names} - for f in as_completed(futures): - try: - f.result() - ok += 1 - except Exception: - pass - elapsed = int(time.time() - started) - logger.info( - "scan terminal pre-warm finished ok={}/{} elapsed={}s", - ok, - len(city_names), - elapsed, - ) + try: + workers = max(1, min(SCAN_TERMINAL_MAX_WORKERS, len(city_names))) + with ThreadPoolExecutor(max_workers=workers) as ex: + futures = {ex.submit(_warm_one, c): c for c in city_names} + for f in as_completed(futures): + try: + f.result() + ok += 1 + except Exception: + pass + elapsed = int(time.time() - started) + logger.info( + "scan terminal pre-warm finished ok={}/{} elapsed={}s", + ok, + len(city_names), + elapsed, + ) + except (ValueError, OSError, IOError): + # Process is shutting down — file handles / threads may be closed + logger.info( + "scan terminal pre-warm interrupted (shutdown) ok={}/{}", + ok, + len(city_names), + ) + except Exception: + logger.exception("scan terminal pre-warm failed") threading.Thread(target=_run, name="scan-prewarm", daemon=True).start()