| import React, { useState, useEffect } from 'react'; |
| import { motion } from 'framer-motion'; |
| import { TrendingUp, Activity, DollarSign, Target } from 'lucide-react'; |
|
|
| const ModelPerformance = () => { |
| const [metrics, setMetrics] = useState(null); |
|
|
| useEffect(() => { |
| const fetchPnL = async () => { |
| try { |
| const res = await fetch(`${import.meta.env.VITE_API_URL}/api/pnl`); |
| const data = await res.json(); |
| setMetrics(data); |
| } catch (e) { |
| console.error("PnL fetch error", e); |
| } |
| }; |
| fetchPnL(); |
| const interval = setInterval(fetchPnL, 5000); |
| return () => clearInterval(interval); |
| }, []); |
|
|
| if (!metrics) return <div className="animate-pulse h-64 bg-slate-900 rounded-xl"></div>; |
|
|
| return ( |
| <div className="bg-slate-900 border border-slate-800 rounded-xl p-6 shadow-2xl h-full"> |
| <div className="flex items-center gap-3 mb-6"> |
| <div className="p-2 bg-emerald-500/10 rounded-lg"> |
| <TrendingUp className="w-6 h-6 text-emerald-400" /> |
| </div> |
| <div> |
| <h3 className="text-lg font-bold text-white">Model Track Record</h3> |
| <p className="text-xs text-slate-400 uppercase tracking-wider">Live P&L Simulation</p> |
| </div> |
| </div> |
| |
| <div className="grid grid-cols-2 gap-4 mb-6"> |
| <div className="bg-slate-800/50 p-4 rounded-lg border border-slate-700/50"> |
| <div className="flex items-center gap-2 text-slate-400 text-xs mb-1"> |
| <DollarSign className="w-3 h-3" /> Total P&L |
| </div> |
| <div className={`text-2xl font-bold ${metrics.total_pnl >= 0 ? 'text-emerald-400' : 'text-rose-400'}`}> |
| {metrics.total_pnl >= 0 ? '+' : ''}${metrics.total_pnl.toLocaleString(undefined, { maximumFractionDigits: 0 })} |
| </div> |
| </div> |
| <div className="bg-slate-800/50 p-4 rounded-lg border border-slate-700/50"> |
| <div className="flex items-center gap-2 text-slate-400 text-xs mb-1"> |
| <Activity className="w-3 h-3" /> ROI |
| </div> |
| <div className={`text-2xl font-bold ${metrics.roi_pct >= 0 ? 'text-emerald-400' : 'text-rose-400'}`}> |
| {metrics.roi_pct >= 0 ? '+' : ''}{metrics.roi_pct}% |
| </div> |
| </div> |
| </div> |
| |
| <div className="space-y-4"> |
| <div> |
| <div className="flex justify-between text-sm mb-2"> |
| <span className="text-slate-400">Win Rate</span> |
| <span className="text-white font-mono">{metrics.win_rate}%</span> |
| </div> |
| <div className="h-2 bg-slate-800 rounded-full overflow-hidden"> |
| <div |
| className="h-full bg-blue-500 rounded-full" |
| style={{ width: `${metrics.win_rate}%` }} |
| /> |
| </div> |
| </div> |
| |
| <div className="grid grid-cols-2 gap-4 text-sm"> |
| <div> |
| <span className="block text-slate-500 text-xs">Avg Win</span> |
| <span className="text-emerald-400 font-mono">+{metrics.avg_win_pct}%</span> |
| </div> |
| <div> |
| <span className="block text-slate-500 text-xs">Avg Loss</span> |
| <span className="text-rose-400 font-mono">{metrics.avg_loss_pct}%</span> |
| </div> |
| </div> |
| |
| <div className="pt-4 border-t border-slate-800"> |
| <div className="flex items-center justify-between text-xs text-slate-500"> |
| <span>Active Positions: {metrics.active_positions}</span> |
| <span>Total Trades: {metrics.total_trades}</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| ); |
| }; |
|
|
| export default ModelPerformance; |
|
|