-
-
-
-
-
-
-
- {t("header.docs")}
-
-
-
-
-
{trialPromoLabel}
-
-
-
- {isAuthenticated ?
:
}
-
{accountLabel}
-
-
- {showRenewReminder ? (
-
-
{renewReminderLabel}
-
- ) : null}
-
{t("header.live")}
@@ -241,6 +159,48 @@ export function HeaderBar({
>
+
+
+
+ {t("header.docs")}
+
+
+
+ {isAuthenticated ? : }
+
+
+
+
+ {showRenewReminder ? (
+
+ {renewReminderLabel}
+
+ ) : null}
);
diff --git a/frontend/components/dashboard/PolyWeatherDashboard.tsx b/frontend/components/dashboard/PolyWeatherDashboard.tsx
index 5e4a8217..59516241 100644
--- a/frontend/components/dashboard/PolyWeatherDashboard.tsx
+++ b/frontend/components/dashboard/PolyWeatherDashboard.tsx
@@ -238,21 +238,36 @@ type HomeForecastDay = {
maxTemp: number;
};
-type HomeSummaryMetric = {
+type HomeSummaryCard = {
key: string;
- label: string;
- value: string;
- accent?: "green" | "amber" | "red" | "cyan";
+ title: string;
+ linkLabel?: string;
+ items: Array<{
+ label: string;
+ value: string;
+ accent?: "green" | "amber" | "red" | "cyan";
+ }>;
};
-function buildDashboardSummaryMetrics(
+function buildDashboardSummaryCards(
snapshots: CitySnapshot[],
locale: string,
-): HomeSummaryMetric[] {
+): HomeSummaryCard[] {
const topTradable = snapshots.filter((snapshot) => snapshot.tradableOpportunity);
const highCount = snapshots.filter((snapshot) => snapshot.city.deb_recent_tier === "high").length;
const mediumCount = snapshots.filter((snapshot) => snapshot.city.deb_recent_tier === "medium").length;
const lowCount = snapshots.filter((snapshot) => snapshot.city.deb_recent_tier === "low").length;
+ const strongAgreement = snapshots.filter(
+ (snapshot) => Number(snapshot.city.deb_recent_hit_rate ?? 0) >= 0.66,
+ ).length;
+ const mediumAgreement = snapshots.filter((snapshot) => {
+ const rate = Number(snapshot.city.deb_recent_hit_rate ?? 0);
+ return rate >= 0.4 && rate < 0.66;
+ }).length;
+ const weakAgreement = Math.max(
+ snapshots.length - strongAgreement - mediumAgreement,
+ 0,
+ );
const avgEdge = topTradable.length
? topTradable.reduce((sum, snapshot) => {
const edgeValue = Number(getMarketEdgeValue(snapshot.detail));
@@ -266,28 +281,92 @@ function buildDashboardSummaryMetrics(
return [
{
- key: "high",
- label: locale === "en-US" ? "High risk" : "高风险",
- value: String(highCount),
- accent: "red",
+ key: "opportunities",
+ title: locale === "en-US" ? "Today's Opportunities" : "今日机会分布",
+ items: [
+ {
+ label: locale === "en-US" ? "High risk" : "高风险",
+ value: String(highCount),
+ accent: "red",
+ },
+ {
+ label: locale === "en-US" ? "Medium risk" : "中风险",
+ value: String(mediumCount),
+ accent: "amber",
+ },
+ {
+ label: locale === "en-US" ? "Low risk" : "低风险",
+ value: String(lowCount),
+ accent: "green",
+ },
+ ],
},
{
- key: "medium",
- label: locale === "en-US" ? "Medium risk" : "中风险",
- value: String(mediumCount),
- accent: "amber",
+ key: "market-summary",
+ title: locale === "en-US" ? "Market Summary" : "市场概览",
+ items: [
+ {
+ label: locale === "en-US" ? "Total markets" : "总市场数",
+ value: String(snapshots.length),
+ },
+ {
+ label: locale === "en-US" ? "Active" : "活跃市场",
+ value: String(topTradable.length),
+ accent: "cyan",
+ },
+ {
+ label: locale === "en-US" ? "Avg. edge" : "平均优势",
+ value: formatEdge(avgEdge),
+ accent: "green",
+ },
+ ],
},
{
- key: "low",
- label: locale === "en-US" ? "Low risk" : "低风险",
- value: String(lowCount),
- accent: "green",
+ key: "model-agreement",
+ title: locale === "en-US" ? "Model Agreement" : "模型一致性",
+ items: [
+ {
+ label: locale === "en-US" ? "High" : "高",
+ value: `${strongAgreement}`,
+ accent: "green",
+ },
+ {
+ label: locale === "en-US" ? "Medium" : "中",
+ value: `${mediumAgreement}`,
+ accent: "amber",
+ },
+ {
+ label: locale === "en-US" ? "Low" : "低",
+ value: `${weakAgreement}`,
+ accent: "red",
+ },
+ ],
},
{
- key: "edge",
- label: locale === "en-US" ? "Avg. edge" : "平均优势",
- value: formatEdge(avgEdge),
- accent: "cyan",
+ key: "impact-window",
+ title: locale === "en-US" ? "High Impact Window" : "高影响窗口",
+ items: [
+ {
+ label: locale === "en-US" ? "Next 6 hours" : "未来 6 小时",
+ value:
+ locale === "en-US"
+ ? `${Math.min(topTradable.length, 12)} markets`
+ : `${Math.min(topTradable.length, 12)} 个市场`,
+ },
+ {
+ label: locale === "en-US" ? "Focus city" : "焦点城市",
+ value: topTradable[0]?.city.display_name || "--",
+ accent: "cyan",
+ },
+ {
+ label: locale === "en-US" ? "Best edge" : "最佳优势",
+ value:
+ topTradable.length > 0
+ ? formatEdge(getMarketEdgeValue(topTradable[0]?.detail))
+ : "--",
+ accent: "green",
+ },
+ ],
},
];
}
@@ -295,29 +374,50 @@ function buildDashboardSummaryMetrics(
function HomeMapToolbar() {
const { locale } = useI18n();
return (
-
-
-
-
-
+
+
{locale === "en-US" ? "Temperature (°F)" : "温度 (°F)"}
+
+
+ -4
+ 14
+ 32
+ 50
+ 68
+ 86
+ 104
+
+
+ >
);
}
@@ -771,6 +871,15 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
×
+
+
+ {locale === "en-US" ? "Focus city" : "焦点城市"}
+
+
+ {locale === "en-US" ? "Why this city?" : "为什么是它?"}
+
+
+
{showOpportunityLabel
? locale === "en-US"
@@ -1052,8 +1161,8 @@ function OpportunityStrip({
}) {
const { locale } = useI18n();
const store = useDashboardStore();
- const summaryMetrics = useMemo(
- () => buildDashboardSummaryMetrics(snapshots, locale),
+ const summaryCards = useMemo(
+ () => buildDashboardSummaryCards(snapshots, locale),
[locale, snapshots],
);
const items = useMemo(
@@ -1069,10 +1178,22 @@ function OpportunityStrip({
aria-label={locale === "en-US" ? "Opportunity strip" : "机会条"}
>
- {summaryMetrics.map((metric) => (
-
-
{metric.label}
-
{metric.value}
+ {summaryCards.map((card) => (
+
+
+ {card.title}
+ {locale === "en-US" ? "View all" : "查看全部"}
+
+
+ {card.items.map((item) => (
+
+
+ {item.value}
+
+ {item.label}
+
+ ))}
+
))}
@@ -1118,7 +1239,10 @@ function OpportunityStrip({
{locale === "en-US" ? "target" : "目标"}
-
YES {formatCents(detail?.market_scan?.yes_buy)}
+
+ YES {formatCents(detail?.market_scan?.yes_buy)}
+ NO {formatCents(detail?.market_scan?.no_buy)}
+
{formatEdge(getMarketEdgeValue(detail))}