预热线程增加 I/O 异常保护,避免进程关闭时 file closed 报错
This commit is contained in:
@@ -8,7 +8,6 @@ import {
|
|||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
GraduationCap,
|
GraduationCap,
|
||||||
LineChart,
|
|
||||||
Menu,
|
Menu,
|
||||||
Search,
|
Search,
|
||||||
Table2,
|
Table2,
|
||||||
@@ -473,7 +472,6 @@ function PolyWeatherTerminal({
|
|||||||
|
|
||||||
const NAV_ITEMS = [
|
const NAV_ITEMS = [
|
||||||
{ key: "contracts", Icon: Table2, labelEn: "Contracts", labelZh: "天气合约" },
|
{ key: "contracts", Icon: Table2, labelEn: "Contracts", labelZh: "天气合约" },
|
||||||
{ key: "signals", Icon: LineChart, labelEn: "Signals", labelZh: "交易信号" },
|
|
||||||
{ key: "markets", Icon: Activity, labelEn: "Markets", labelZh: "市场概览" },
|
{ key: "markets", Icon: Activity, labelEn: "Markets", labelZh: "市场概览" },
|
||||||
{ key: "training", Icon: GraduationCap, labelEn: "Training", labelZh: "训练数据" },
|
{ key: "training", Icon: GraduationCap, labelEn: "Training", labelZh: "训练数据" },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -364,6 +364,27 @@ function buildChartDomain(
|
|||||||
return [Number((min - padding).toFixed(1)), Number((max + padding).toFixed(1))];
|
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({
|
export function LiveTemperatureThresholdChart({
|
||||||
isEn,
|
isEn,
|
||||||
row,
|
row,
|
||||||
@@ -414,6 +435,7 @@ export function LiveTemperatureThresholdChart({
|
|||||||
.slice(0, 5)
|
.slice(0, 5)
|
||||||
.map((item) => ({ ...item, ...seriesStats(item.values) }));
|
.map((item) => ({ ...item, ...seriesStats(item.values) }));
|
||||||
const marketTemperatureTicks = useMemo(() => buildMarketTemperatureOptions(row), [row]);
|
const marketTemperatureTicks = useMemo(() => buildMarketTemperatureOptions(row), [row]);
|
||||||
|
const probRefLines = useMemo(() => buildProbRefLines(row), [row]);
|
||||||
const chartDomain = useMemo(
|
const chartDomain = useMemo(
|
||||||
() => buildChartDomain(marketTemperatureTicks, series),
|
() => buildChartDomain(marketTemperatureTicks, series),
|
||||||
[marketTemperatureTicks, series],
|
[marketTemperatureTicks, series],
|
||||||
@@ -489,6 +511,19 @@ export function LiveTemperatureThresholdChart({
|
|||||||
label={{ value: `UMA ${threshold.toFixed(1)}°`, fill: "#f97316", fontSize: 10, position: "left" }}
|
label={{ value: `UMA ${threshold.toFixed(1)}°`, fill: "#f97316", fontSize: 10, position: "left" }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{probRefLines.map((item, i) => (
|
||||||
|
<ReferenceLine
|
||||||
|
key={`prob-${i}`}
|
||||||
|
y={item.y}
|
||||||
|
stroke={item.stroke}
|
||||||
|
strokeWidth={item.width}
|
||||||
|
label={
|
||||||
|
item.prob >= 0.15
|
||||||
|
? { value: item.label, fill: "#2563eb", fontSize: 9, position: "insideRight" }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{
|
contentStyle={{
|
||||||
border: "1px solid #cbd5e1",
|
border: "1px solid #cbd5e1",
|
||||||
|
|||||||
@@ -1581,21 +1581,31 @@ def start_scan_terminal_prewarm() -> None:
|
|||||||
def _run():
|
def _run():
|
||||||
started = time.time()
|
started = time.time()
|
||||||
ok = 0
|
ok = 0
|
||||||
workers = max(1, min(SCAN_TERMINAL_MAX_WORKERS, len(city_names)))
|
try:
|
||||||
with ThreadPoolExecutor(max_workers=workers) as ex:
|
workers = max(1, min(SCAN_TERMINAL_MAX_WORKERS, len(city_names)))
|
||||||
futures = {ex.submit(_warm_one, c): c for c in city_names}
|
with ThreadPoolExecutor(max_workers=workers) as ex:
|
||||||
for f in as_completed(futures):
|
futures = {ex.submit(_warm_one, c): c for c in city_names}
|
||||||
try:
|
for f in as_completed(futures):
|
||||||
f.result()
|
try:
|
||||||
ok += 1
|
f.result()
|
||||||
except Exception:
|
ok += 1
|
||||||
pass
|
except Exception:
|
||||||
elapsed = int(time.time() - started)
|
pass
|
||||||
logger.info(
|
elapsed = int(time.time() - started)
|
||||||
"scan terminal pre-warm finished ok={}/{} elapsed={}s",
|
logger.info(
|
||||||
ok,
|
"scan terminal pre-warm finished ok={}/{} elapsed={}s",
|
||||||
len(city_names),
|
ok,
|
||||||
elapsed,
|
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()
|
threading.Thread(target=_run, name="scan-prewarm", daemon=True).start()
|
||||||
|
|||||||
Reference in New Issue
Block a user