diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx index 9f0dcd8b..554f0217 100644 --- a/frontend/components/dashboard/ScanTerminalDashboard.tsx +++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx @@ -22,8 +22,6 @@ import { ProductAccessRequired } from "@/components/dashboard/scan-terminal/Prod import { type ContinentGroup, buildContinentGroups, - formatPrice, - formatSpreadLiquidity, GAP_COLOR_MAP, getDefaultExpanded, getGapColor, @@ -46,7 +44,6 @@ import { useRelativeTime } from "@/hooks/useRelativeTime"; 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"; @@ -165,10 +162,6 @@ function decisionLabel(row?: ScanOpportunityRow | null) { return "Monitor"; } -function tablePrice(row: ScanOpportunityRow) { - return formatPrice(row.midpoint, row.ask, row.bid); -} - function CityRegionList({ isEn, rows, @@ -512,17 +505,6 @@ function PolyWeatherTerminal({
{activeNavKey === "training" ? ( - ) : activeNavKey === "markets" ? ( - { - const regionKey = resolveTradingRegionKey(row); - if (regionKey) setSelectedRegionKey(regionKey); - setSelectedRow(row); - setActiveNavKey("contracts"); - }} - /> ) : ( <> {/* Region tabs */} @@ -612,7 +594,7 @@ function PolyWeatherTerminal({ ["DEB", temp(selectedRow.deb_prediction, selectedRow.temp_symbol)], ["Gap", temp(selectedRow.signed_gap ?? selectedRow.gap_to_target, selectedRow.temp_symbol)], ["Edge", pct(selectedRow.edge_percent)], - ["Market", formatPrice(selectedRow.midpoint, selectedRow.ask, selectedRow.bid)], + ["Market", "--"], ].map(([label, value]) => (
{label}
diff --git a/frontend/components/dashboard/scan-terminal/GroupedMarketTable.tsx b/frontend/components/dashboard/scan-terminal/GroupedMarketTable.tsx index 02b40dcc..4d21851f 100644 --- a/frontend/components/dashboard/scan-terminal/GroupedMarketTable.tsx +++ b/frontend/components/dashboard/scan-terminal/GroupedMarketTable.tsx @@ -2,19 +2,17 @@ import { Fragment, useState } from "react"; import clsx from "clsx"; -import { ChevronDown, ChevronRight, ExternalLink } from "lucide-react"; +import { ChevronDown, ChevronRight } from "lucide-react"; import type { ScanOpportunityRow } from "@/lib/dashboard-types"; import { type ContinentGroup, - formatPrice, - formatSpreadLiquidity, GAP_COLOR_MAP, getGapColor, getSignalLabel, getSignalState, getDefaultExpanded, } from "@/components/dashboard/scan-terminal/continent-grouping"; -import { rowName, temp, pct, edgeClass } from "./utils"; +import { rowName, temp } from "./utils"; export function GroupedMarketTable({ groups, @@ -51,7 +49,7 @@ export function GroupedMarketTable({ return (
- +
@@ -59,9 +57,6 @@ export function GroupedMarketTable({ - - - @@ -73,7 +68,7 @@ export function GroupedMarketTable({ {showHeaders && ( - - - - - - - - - - {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", - )} - > - - - - - - - ); - })} + {rows.map((row) => ( + onSelect(row)} + className={clsx( + "cursor-pointer border-b border-slate-100 hover:bg-blue-50/70", + selectedId === row.id && "bg-blue-50", + )} + > + + + ))}
CityHigh DEB GapMarketEdgeSpr/Liq Signal
+ {temp(row.signed_gap ?? row.gap_to_target, row.temp_symbol)} - {row.market_url ? ( - e.stopPropagation()} - className="inline-flex items-center gap-0.5 text-blue-600 hover:text-blue-800 hover:underline" - title={row.market_question || "Polymarket"} - > - {formatPrice(row.midpoint, row.ask, row.bid)} - - - ) : ( - formatPrice(row.midpoint, row.ask, row.bid) - )} - - {pct(row.edge_percent)} - - {formatSpreadLiquidity(row.spread, row.book_liquidity ?? row.market_liquidity)} -
- - + {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)} -
+
+ {rowName(row)} +
+
+ {row.airport || "--"} +
+
); diff --git a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx index b3b9f759..e5dfd687 100644 --- a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx +++ b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx @@ -545,47 +545,30 @@ function buildModelSummaryCards(row: ScanOpportunityRow | null): EvidenceSeries[ })); } -// ── Market temperature ticks for Y-axis ───────────────────────────────── +// ── Integer-degree ticks for Y-axis ────────────────────────────────── -function parseTemperatureOptionsFromText(value?: string | null) { - const raw = String(value || ""); - const matches = raw.match(/-?\d+(?:\.\d+)?/g) || []; - return matches.map(Number).filter((v) => Number.isFinite(v) && v > -80 && v < 80); -} - -function buildMarketTemperatureOptions(row: ScanOpportunityRow | null) { - const buckets = row?.distribution_full?.length - ? row.distribution_full - : row?.distribution_preview; - const values = new Set(); - (buckets || []).forEach((b) => { - const v = validNumber(b.value); - if (v !== null) values.add(v); - parseTemperatureOptionsFromText(b.label).forEach((x) => values.add(x)); - }); - [row?.target_lower, row?.target_upper, row?.target_value, row?.target_threshold] - .forEach((v) => { if (validNumber(v) !== null) values.add(validNumber(v)!); }); - parseTemperatureOptionsFromText(row?.target_label).forEach((x) => values.add(x)); - - const sorted = [...values].sort((a, b) => a - b); - if (sorted.length) return sorted; - const t = validNumber(row?.target_threshold) ?? validNumber(row?.target_value); - if (t === null) return null; - return [t - 2, t - 1, t, t + 1, t + 2]; +function buildIntDegreeTicks(series: EvidenceSeries[], data?: Array>): number[] | null { + const vals = data?.length + ? data.flatMap((point) => series.map((s) => point[s.key])).filter((v): v is number => validNumber(v) !== null) + : series.flatMap((s) => s.values).filter((v): v is number => validNumber(v) !== null); + if (!vals.length) return null; + const min = Math.floor(Math.min(...vals)); + const max = Math.ceil(Math.max(...vals)); + const ticks: number[] = []; + for (let d = min; d <= max; d++) ticks.push(d); + return ticks.length > 0 ? ticks : null; } function buildChartDomain( - ticks: number[] | null, series: EvidenceSeries[], data?: Array>, ): [number, number] | ["auto", "auto"] { const vals = data?.length ? data.flatMap((point) => series.map((s) => point[s.key])).filter((v): v is number => validNumber(v) !== null) : series.flatMap((s) => s.values).filter((v): v is number => validNumber(v) !== null); - const all = [...(ticks || []), ...vals]; - if (!all.length) return ["auto", "auto"]; - const min = Math.min(...all); - const max = Math.max(...all); + if (!vals.length) return ["auto", "auto"]; + const min = Math.min(...vals); + const max = Math.max(...vals); const span = Math.max(1, max - min); const pad = Math.max(0.5, span * 0.08); return [Number((min - pad).toFixed(1)), Number((max + pad).toFixed(1))]; @@ -777,10 +760,10 @@ export function LiveTemperatureThresholdChart({ return list.sort((a, b) => a.threshold - b.threshold); }, [row, allRows]); - const marketTicks = useMemo(() => buildMarketTemperatureOptions(row), [row]); + const intDegreeTicks = useMemo(() => buildIntDegreeTicks(series, data), [series, data]); const chartDomain = useMemo( - () => buildChartDomain(marketTicks, series, data), - [marketTicks, series, data], + () => buildChartDomain(series, data), + [series, data], ); return ( @@ -968,7 +951,7 @@ export function LiveTemperatureThresholdChart({ ))}
- + `${Number(v).toFixed(1)}°`} + tickFormatter={(v) => `${Number(v).toFixed(0)}°`} axisLine={{ stroke: "#cbd5e1" }} tickLine={false} domain={chartDomain} - ticks={marketTicks ?? undefined} + ticks={intDegreeTicks ?? undefined} /> {cityThresholds.map((t, idx) => { const isSelected = row && (Number(row.target_threshold ?? row.target_value) === t.threshold); diff --git a/frontend/components/dashboard/scan-terminal/MobileCityCard.tsx b/frontend/components/dashboard/scan-terminal/MobileCityCard.tsx index 1f8c3291..30e9c517 100644 --- a/frontend/components/dashboard/scan-terminal/MobileCityCard.tsx +++ b/frontend/components/dashboard/scan-terminal/MobileCityCard.tsx @@ -2,7 +2,6 @@ import type { ScanOpportunityRow } from "@/lib/dashboard-types"; import { - formatPrice, GAP_COLOR_MAP, getGapColor, getSignalLabel, @@ -58,7 +57,7 @@ export function MobileCityCard({ Gap {tempVal(row.signed_gap ?? row.gap_to_target, row.temp_symbol)} - {formatPrice(row.midpoint, row.ask, row.bid)} + {row.local_time || "--"}
diff --git a/frontend/components/dashboard/scan-terminal/continent-grouping.ts b/frontend/components/dashboard/scan-terminal/continent-grouping.ts index 11772aaf..2ecace29 100644 --- a/frontend/components/dashboard/scan-terminal/continent-grouping.ts +++ b/frontend/components/dashboard/scan-terminal/continent-grouping.ts @@ -176,16 +176,10 @@ export type GapColor = "green" | "orange" | "slate" | "gray" | "red"; export function getGapColor(row: ScanOpportunityRow): GapColor { const gap = Number(row.signed_gap ?? row.gap_to_target); - const edge = Number(row.edge_percent || 0); - const spread = Number(row.spread || 0); - const liq = Number(row.book_liquidity || row.market_liquidity || 0); - if (!Number.isFinite(gap)) return "gray"; - if (liq <= 0 || spread > 20) return "red"; if (gap >= 2) return "green"; - if (gap >= 0 && edge > 5) return "orange"; - if (gap >= 0) return "slate"; - if (gap < -5 || edge < -10) return "gray"; + if (gap >= 0) return "orange"; + if (gap < -5) return "gray"; return "slate"; } @@ -197,30 +191,12 @@ export const GAP_COLOR_MAP: Record = { red: "text-red-500", }; -export function formatPrice(midpoint?: number | null, ask?: number | null, bid?: number | null): string { - const m = Number(midpoint); - if (Number.isFinite(m) && m > 0) { - const cents = Math.round(m * 100); - return `Y ${cents}¢`; - } - const a = Number(ask); - if (Number.isFinite(a) && a > 0) { - const cents = Math.round(a * 100); - return `Y ${cents}¢`; - } +export function formatPrice(_midpoint?: number | null, _ask?: number | null, _bid?: number | null): string { return "--"; } -export function formatSpreadLiquidity(spread?: number | null, liquidity?: number | null): string { - const sp = Number(spread); - const liq = Number(liquidity); - const spStr = Number.isFinite(sp) ? `${Math.round(sp)}¢` : "--"; - const liqStr = Number.isFinite(liq) - ? liq >= 1000 - ? `$${(liq / 1000).toFixed(1)}K` - : `$${Math.round(liq)}` - : "--"; - return `${spStr} / ${liqStr}`; +export function formatSpreadLiquidity(_spread?: number | null, _liquidity?: number | null): string { + return "--"; } export function buildContinentGroups(rows: ScanOpportunityRow[], isEn: boolean): ContinentGroup[] {