Files
stonks-oracle/frontend/src/pages/trading/PerformanceCharts.tsx
T
Celes Renata 516731e69a fix: remove explicit type annotations on Recharts formatter callbacks
TypeScript strict mode in CI rejects explicit parameter types on
Recharts formatter/tickFormatter callbacks. Use inference with
'as number' casts on the value instead. Also fix unsafe cast in
PortfolioComposition and handle possibly-undefined percent.
2026-04-15 16:17:00 +00:00

116 lines
4.1 KiB
TypeScript

import { useMemo } from 'react';
import { useTradingMetricsHistory } from '../../api/tradingHooks';
import { Card, LoadingSpinner } from '../../components/ui';
import {
LineChart, Line, BarChart, Bar, AreaChart, Area,
XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, Cell,
} from 'recharts';
const tooltipStyle = {
backgroundColor: '#1e293b',
border: '1px solid #334155',
borderRadius: 8,
};
export function PerformanceCharts() {
const { data: snapshots, isLoading } = useTradingMetricsHistory();
const chartData = useMemo(() => {
if (!snapshots) return [];
return snapshots.map((s) => ({
date: new Date(s.snapshot_date).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }),
portfolioValue: s.portfolio_value,
cumulativeReturn: (s.cumulative_return ?? 0) * 100,
dailyReturn: (s.daily_return ?? 0) * 100,
drawdown: (s.current_drawdown_pct ?? 0) * 100,
maxDrawdown: (s.max_drawdown ?? 0) * 100,
}));
}, [snapshots]);
if (isLoading) return <LoadingSpinner />;
if (chartData.length === 0) {
return (
<Card>
<p className="text-sm text-gray-500">No performance history available yet</p>
</Card>
);
}
return (
<div className="space-y-4">
{/* Cumulative P&L */}
<Card>
<h2 className="mb-3 text-sm font-medium text-gray-400">Cumulative P&amp;L</h2>
<ResponsiveContainer width="100%" height={280}>
<LineChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" stroke="#334155" />
<XAxis dataKey="date" tick={{ fill: '#6b7280', fontSize: 11 }} />
<YAxis tick={{ fill: '#6b7280', fontSize: 11 }} tickFormatter={(v) => `${v.toFixed(1)}%`} />
<Tooltip
contentStyle={tooltipStyle}
labelStyle={{ color: '#9ca3af' }}
formatter={(value) => [`${Number(value).toFixed(2)}%`, 'Cumulative Return']}
/>
<Line
type="monotone"
dataKey="cumulativeReturn"
stroke="#6366f1"
strokeWidth={2}
dot={false}
name="Cumulative Return"
/>
</LineChart>
</ResponsiveContainer>
</Card>
{/* Daily Returns */}
<Card>
<h2 className="mb-3 text-sm font-medium text-gray-400">Daily Returns</h2>
<ResponsiveContainer width="100%" height={220}>
<BarChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" stroke="#334155" />
<XAxis dataKey="date" tick={{ fill: '#6b7280', fontSize: 11 }} />
<YAxis tick={{ fill: '#6b7280', fontSize: 11 }} tickFormatter={(v) => `${v.toFixed(1)}%`} />
<Tooltip
contentStyle={tooltipStyle}
labelStyle={{ color: '#9ca3af' }}
formatter={(value) => [`${Number(value).toFixed(2)}%`, 'Daily Return']}
/>
<Bar dataKey="dailyReturn" name="Daily Return">
{chartData.map((entry, i) => (
<Cell key={i} fill={entry.dailyReturn >= 0 ? '#22c55e' : '#ef4444'} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</Card>
{/* Drawdown */}
<Card>
<h2 className="mb-3 text-sm font-medium text-gray-400">Drawdown</h2>
<ResponsiveContainer width="100%" height={220}>
<AreaChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" stroke="#334155" />
<XAxis dataKey="date" tick={{ fill: '#6b7280', fontSize: 11 }} />
<YAxis tick={{ fill: '#6b7280', fontSize: 11 }} tickFormatter={(v) => `${v.toFixed(1)}%`} />
<Tooltip
contentStyle={tooltipStyle}
labelStyle={{ color: '#9ca3af' }}
formatter={(value) => [`${Number(value).toFixed(2)}%`, 'Drawdown']}
/>
<Area
type="monotone"
dataKey="drawdown"
stroke="#ef4444"
fill="#ef444433"
name="Current Drawdown"
/>
</AreaChart>
</ResponsiveContainer>
</Card>
</div>
);
}