diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index eaa4a66..c090f57 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -1,4 +1,5 @@ import { useMemo, useState } from 'react' +import { useQuery } from '@tanstack/react-query' import { useGeoRiskScore, useAllQuotes, useCalendar, usePortfolioSummary, useLastScores, useAllPatterns, useMacroRegime, @@ -11,7 +12,10 @@ import clsx from 'clsx' import type { Quote } from '../types' import { format } from 'date-fns' import { fr } from 'date-fns/locale' -import { RadarChart, PolarGrid, PolarAngleAxis, Radar, ResponsiveContainer } from 'recharts' +import { + RadarChart, PolarGrid, PolarAngleAxis, Radar, ResponsiveContainer, + AreaChart, Area, XAxis, YAxis, Tooltip, CartesianGrid, +} from 'recharts' import { scoreColor } from '../components/TradeIdeas' const riskGauge = (score: number) => { @@ -88,6 +92,20 @@ export default function Dashboard() { const { data: cycleStatusData } = useCycleStatus() const { data: geoNews } = useGeoNews() + // Historical PnL curve + latest VaR snapshot + const { data: pnlHistoryData } = useQuery({ + queryKey: ['dash-pnl-history'], + queryFn: () => fetch('/api/var/pnl/snapshots?limit=200').then(r => r.ok ? r.json() : { snapshots: [] }), + staleTime: 120_000, + retry: 1, + }) + const { data: latestVarData } = useQuery({ + queryKey: ['dash-var-latest'], + queryFn: () => fetch('/api/var/latest').then(r => r.ok ? r.json() : { snapshot: null }), + staleTime: 120_000, + retry: 1, + }) + const [pnlView, setPnlView] = useState<'simulated' | 'portfolio'>(() => (localStorage.getItem('dash_pnl_view') as any) ?? 'simulated' ) @@ -400,6 +418,46 @@ export default function Dashboard() { )} + {/* PnL historical sparkline */} + {(() => { + const snaps: any[] = [...(pnlHistoryData?.snapshots ?? [])].reverse() + if (snaps.length < 2) return null + const chartData = snaps.map(s => ({ + date: s.snapped_at?.slice(5, 10), + pnl: s.total_pnl_pct ?? 0, + })) + const minVal = Math.min(...chartData.map(d => d.pnl)) + const maxVal = Math.max(...chartData.map(d => d.pnl)) + const isPositive = chartData[chartData.length - 1]?.pnl >= 0 + const strokeColor = isPositive ? '#34d399' : '#f87171' + const gradId = 'pnlGrad' + return ( +