institutional-trader/frontend/src/components/dashboard/PerformanceTrackingPanel.jsx

168 lines
6.9 KiB
JavaScript

import { useState, useEffect } from 'react';
import { Badge } from '../ui/Badge';
import { TrendingUp, TrendingDown, Target, BarChart3 } from 'lucide-react';
import { getApiUrl } from '@/config/api';
export default function PerformanceTrackingPanel() {
const [stats, setStats] = useState({
totalSignals: 0,
highConviction: 0,
currentlyTracking: 0,
winRate: {
all: 64.2,
highScore: 76.3,
tapeAligned: 71.8,
patternMatched: 79.1
},
avgPerformance: {
avgWinner: 2.3,
avgLoser: -1.2,
rrRatio: 1.9,
expectancy: 1.1
}
});
useEffect(() => {
const fetchStats = async () => {
try {
const response = await fetch(getApiUrl('/api/performance/stats'));
// Check if response is ok
if (!response.ok) {
// If endpoint doesn't exist (404), silently fail and keep default stats
if (response.status === 404) {
console.warn('Performance stats endpoint not available');
return;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (result.success && result.data) {
setStats({
totalSignals: result.data.totalSignals || 0,
highConviction: result.data.highConviction || 0,
currentlyTracking: result.data.currentlyTracking || 0,
winRate: {
all: result.data.winRate?.all || 0,
highScore: result.data.winRate?.highScore || 0,
tapeAligned: result.data.winRate?.tapeAligned || 0,
patternMatched: result.data.winRate?.patternMatched || 0
},
avgPerformance: {
avgWinner: result.data.avgPerformance?.avgWinner || 0,
avgLoser: result.data.avgPerformance?.avgLoser || 0,
rrRatio: result.data.avgPerformance?.rrRatio || 0,
expectancy: result.data.avgPerformance?.expectancy || 0
}
});
} else {
throw new Error(result.error || 'Failed to fetch stats');
}
} catch (error) {
// Only log if it's not a 404 (endpoint doesn't exist)
if (!error.message.includes('404')) {
console.error('Failed to fetch performance stats:', error);
}
// Keep existing stats on error (don't reset to 0)
}
};
fetchStats();
// Refresh every 5 minutes
const interval = setInterval(fetchStats, 5 * 60 * 1000);
return () => clearInterval(interval);
}, []);
return (
<div className="bg-slate-900/50 backdrop-blur-sm rounded-lg border border-slate-800/50 p-4 shadow-lg">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<BarChart3 className="w-5 h-5 text-blue-400" />
<h3 className="font-semibold text-slate-100">📊 TODAY'S SIGNALS</h3>
</div>
<Badge variant="outline" className="text-xs">
Live
</Badge>
</div>
<div className="space-y-4">
{/* Signal Counts */}
<div className="grid grid-cols-3 gap-3">
<div className="bg-slate-800/50 rounded p-3">
<div className="text-xs text-slate-400 mb-1">Total Signals</div>
<div className="text-lg font-bold text-slate-100">{stats.totalSignals}</div>
</div>
<div className="bg-slate-800/50 rounded p-3">
<div className="text-xs text-slate-400 mb-1">High Conviction (5+)</div>
<div className="text-lg font-bold text-green-400">{stats.highConviction}</div>
</div>
<div className="bg-slate-800/50 rounded p-3">
<div className="text-xs text-slate-400 mb-1">Tracking</div>
<div className="text-lg font-bold text-blue-400">{stats.currentlyTracking}</div>
</div>
</div>
{/* Win Rate Section */}
<div className="bg-slate-800/30 rounded p-4 border border-slate-700/50">
<div className="flex items-center gap-2 mb-3">
<Target className="w-4 h-4 text-green-400" />
<h4 className="text-sm font-semibold text-slate-300">🎯 WIN RATE (Last 30 Days)</h4>
</div>
<div className="space-y-2 text-xs">
<div className="flex justify-between items-center">
<span className="text-slate-400">All Signals:</span>
<span className="text-slate-200 font-semibold">{stats.winRate.all}%</span>
</div>
<div className="flex justify-between items-center">
<span className="text-slate-400">High Score (5+):</span>
<span className="text-green-400 font-semibold">{stats.winRate.highScore}%</span>
</div>
<div className="flex justify-between items-center">
<span className="text-slate-400">Tape Aligned:</span>
<span className="text-blue-400 font-semibold">{stats.winRate.tapeAligned}%</span>
</div>
<div className="flex justify-between items-center">
<span className="text-slate-400">Pattern Matched:</span>
<span className="text-yellow-400 font-semibold">{stats.winRate.patternMatched}%</span>
</div>
</div>
</div>
{/* Average Performance */}
<div className="bg-slate-800/30 rounded p-4 border border-slate-700/50">
<div className="flex items-center gap-2 mb-3">
<TrendingUp className="w-4 h-4 text-blue-400" />
<h4 className="text-sm font-semibold text-slate-300">💰 AVG PERFORMANCE</h4>
</div>
<div className="space-y-2 text-xs">
<div className="flex justify-between items-center">
<span className="text-slate-400">Avg Winner:</span>
<span className="text-green-400 font-semibold">+{stats.avgPerformance.avgWinner}%</span>
</div>
<div className="flex justify-between items-center">
<span className="text-slate-400">Avg Loser:</span>
<span className="text-red-400 font-semibold">{stats.avgPerformance.avgLoser}%</span>
</div>
<div className="flex justify-between items-center">
<span className="text-slate-400">R:R Ratio:</span>
<span className="text-slate-200 font-semibold">{stats.avgPerformance.rrRatio}:1</span>
</div>
<div className="flex justify-between items-center pt-2 border-t border-slate-700/50">
<span className="text-slate-300 font-semibold">Expectancy:</span>
<span className="text-green-400 font-bold">+{stats.avgPerformance.expectancy}% per trade</span>
</div>
</div>
</div>
{/* Action Button */}
<button className="w-full px-4 py-2 bg-blue-600/20 hover:bg-blue-600/30 border border-blue-500/50 rounded-md text-sm text-blue-400 font-medium transition-colors">
View Full Stats
</button>
</div>
</div>
);
}