From 09b9efeda7796fbf5a16ff28f6921ea41964b05a Mon Sep 17 00:00:00 2001 From: OpenSquared Date: Tue, 14 Jul 2026 12:17:15 +0200 Subject: [PATCH] feat: new cockpit --- frontend/src/hooks/useApi.ts | 4 +- frontend/src/pages/Dashboard.tsx | 80 +++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/frontend/src/hooks/useApi.ts b/frontend/src/hooks/useApi.ts index 776fca8..f703ac9 100644 --- a/frontend/src/hooks/useApi.ts +++ b/frontend/src/hooks/useApi.ts @@ -102,10 +102,10 @@ export interface FFEvent { impact: 'high' | 'medium' | 'low'; event_name: string actual_value: number | null; forecast_value: number | null; previous_value: number | null } -export const useEcoCalendar = (params: { period?: string; limit?: number } = {}) => +export const useEcoCalendar = (params: { period?: string; limit?: number; impacts?: string } = {}) => useQuery<{ events: FFEvent[] }>({ queryKey: ['eco-calendar', params], - queryFn: () => api.get('/eco/calendar', { params: { period: 'recent', limit: 30, ...params } }).then(r => r.data), + queryFn: () => api.get('/eco/calendar', { params: { period: 'recent', limit: 50, impacts: 'high,medium', ...params } }).then(r => r.data), staleTime: 5 * 60_000, }) diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index fa06bc8..64eb644 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -164,7 +164,7 @@ export default function Dashboard() { const topNews = useMemo(() => [...(geoNews ?? [])] .sort((a, b) => b.impact_score - a.impact_score) - .slice(0, 2) + .slice(0, 5) , [geoNews]) // Watchlist radar: change_pct normalized to a 0-100 scale (50 = flat) @@ -246,30 +246,40 @@ export default function Dashboard() {
) : riskScore && gauge ? ( <> -
{riskScore.score}
-
{gauge.label}
+
+
+
{riskScore.score}
+
{gauge.label}
+
+
+ {riskScore.top_risks?.map(([cat, val]) => ( +
+ {(cat as string).replace('_', ' ')} + {Math.round((val as number) * 100)}% +
+ ))} +
+
-
- {riskScore.top_risks?.map(([cat, val]) => ( -
- {(cat as string).replace('_', ' ')} - {Math.round((val as number) * 100)}% -
- ))} -
{topNews.length > 0 && ( -
+
Top news
- {topNews.map((n, i) => ( -
- {n.title} - {Math.round(n.impact_score * 100)} -
- ))} + {topNews.map((n, i) => { + const impact = Math.round(n.impact_score * 100) + const impactColor = impact >= 75 ? 'text-red-400' : impact >= 50 ? 'text-orange-400' : impact >= 25 ? 'text-yellow-400' : 'text-emerald-400' + const dotColor = impact >= 75 ? 'bg-red-500' : impact >= 50 ? 'bg-orange-500' : impact >= 25 ? 'bg-yellow-500' : 'bg-emerald-500' + return ( +
+ + {n.title} + {impact} +
+ ) + })}
)} @@ -294,7 +304,7 @@ export default function Dashboard() { -
+
{((watchlistQuotesData as any)?.items ?? []).map((it: any) => (
{it.ticker} @@ -794,7 +804,7 @@ export default function Dashboard() { .map((w: any) => snapshots[w.ticker]) .filter(Boolean) .sort((a: any, b: any) => Math.abs((b.iv_rank ?? 50) - 50) - Math.abs((a.iv_rank ?? 50) - 50)) - .slice(0, 5) + .slice(0, 6) return ( @@ -811,13 +821,31 @@ export default function Dashboard() { {highlights.map((h: any) => { const rank = h.iv_rank const skewPct = h.skew?.skew_pct + const ivCur = h.iv_current_pct + const ivPctl = h.iv_percentile + const flow: string | null = h.options_flow?.flow_bias ?? null + const pcRatio = h.options_flow?.pc_oi_ratio return ( -
- {rank != null && rank > 80 ? '🔴' : rank != null && rank < 20 ? '🟢' : '⚪'} - {h.ticker} - IVR {rank != null ? rank.toFixed(0) : '—'} - {skewPct != null && Math.abs(skewPct) > 3 && ( - skew {skewPct >= 0 ? '+' : ''}{skewPct.toFixed(1)} +
+
+ {rank != null && rank > 80 ? '🔴' : rank != null && rank < 20 ? '🟢' : '⚪'} + {h.ticker} + IVR {rank != null ? rank.toFixed(0) : '—'} + {ivCur != null && · IV {ivCur.toFixed(1)}%} + {skewPct != null && Math.abs(skewPct) > 3 && ( + 0 ? 'text-orange-400' : 'text-blue-400')}> + skew {skewPct >= 0 ? '+' : ''}{skewPct.toFixed(1)} + + )} +
+ {(ivPctl != null || flow != null || pcRatio != null) && ( +
+ {ivPctl != null && Pctl {ivPctl.toFixed(0)}} + {ivPctl != null && pcRatio != null && ' · '} + {pcRatio != null && P/C {pcRatio.toFixed(2)}} + {(ivPctl != null || pcRatio != null) && flow && ' · '} + {flow && {flow}} +
)}
)