/** * Trading Bot v4 - Analytics Dashboard * * Comprehensive view of trading performance and statistics */ 'use client' import { useState, useEffect } from 'react' interface Stats { period: string realTrades: { total: number winning: number losing: number winRate: string totalPnL: string avgWin: string avgLoss: string profitFactor: string } testTrades: { count: number note: string } } interface LastTrade { id: string symbol: string direction: string entryPrice: number entryTime: string exitPrice?: number exitTime?: string exitReason?: string realizedPnL?: number realizedPnLPercent?: number positionSizeUSD: number leverage: number stopLossPrice: number takeProfit1Price: number takeProfit2Price: number isTestTrade: boolean signalQualityScore?: number } interface NetPosition { symbol: string longUSD: number shortUSD: number netUSD: number netSOL: number netDirection: 'long' | 'short' | 'flat' tradeCount: number } interface PositionSummary { summary: { individualTrades: number testTrades: number totalIndividualExposure: string netExposure: string explanation: string } netPositions: NetPosition[] } export default function AnalyticsPage() { const [stats, setStats] = useState(null) const [positions, setPositions] = useState(null) const [lastTrade, setLastTrade] = useState(null) const [loading, setLoading] = useState(true) const [selectedDays, setSelectedDays] = useState(30) useEffect(() => { loadData() }, [selectedDays]) const loadData = async () => { setLoading(true) try { const [statsRes, positionsRes, lastTradeRes] = await Promise.all([ fetch(`/api/analytics/stats?days=${selectedDays}`), fetch('/api/analytics/positions'), fetch('/api/analytics/last-trade'), ]) const statsData = await statsRes.json() const positionsData = await positionsRes.json() const lastTradeData = await lastTradeRes.json() setStats(statsData.stats) setPositions(positionsData.summary) setLastTrade(lastTradeData.trade) } catch (error) { console.error('Failed to load analytics:', error) } setLoading(false) } const clearManuallyClosed = async () => { if (!confirm('Clear all open trades from database? Use this if you manually closed positions in Drift UI.')) { return } try { const res = await fetch('/api/trading/clear-manual-closes', { method: 'POST', }) if (res.ok) { alert('✅ Manually closed trades cleared from database') loadData() // Reload data } else { const error = await res.json() alert(`❌ Failed to clear: ${error.error}`) } } catch (error) { console.error('Failed to clear trades:', error) alert('❌ Failed to clear trades') } } if (loading) { return (

Loading analytics...

) } return (
{/* Header */}

📊 Analytics Dashboard

Trading performance and statistics

{/* Time Period Selector */}
🎯 TP/SL Optimization
Period:
{/* Position Summary */} {positions && (

📍 Current Positions

Open Trades
{positions.summary.individualTrades}
Test Trades
{positions.summary.testTrades}
Total Exposure
{positions.summary.totalIndividualExposure}
Net Exposure
{positions.summary.netExposure}
{positions.netPositions.length > 0 && (

Net Positions (Drift View)

{positions.netPositions.map((pos, i) => (
🎯
{pos.symbol}
{pos.tradeCount} trade{pos.tradeCount > 1 ? 's' : ''}
{pos.netDirection.toUpperCase()}: {Math.abs(pos.netSOL).toFixed(4)} SOL
${Math.abs(pos.netUSD).toFixed(2)}
))}
)} {positions.summary.individualTrades === 0 && (
📭

No open positions

)}
)} {/* Last Trade Details */} {lastTrade && (

🔍 Last Trade

{lastTrade.direction === 'long' ? '📈' : '📉'}
{lastTrade.symbol}
{lastTrade.direction.toUpperCase()} {lastTrade.isTestTrade && ( TEST )}
{lastTrade.exitTime && lastTrade.realizedPnL !== undefined && (
= 0 ? 'text-green-400' : 'text-red-400'}`}> {lastTrade.realizedPnL >= 0 ? '+' : ''}${lastTrade.realizedPnL.toFixed(2)}
{lastTrade.realizedPnLPercent !== undefined && (
= 0 ? 'text-green-400' : 'text-red-400'}`}> {lastTrade.realizedPnLPercent >= 0 ? '+' : ''}{lastTrade.realizedPnLPercent.toFixed(2)}%
)}
)} {!lastTrade.exitTime && (
OPEN
Currently active
)}
Entry
${lastTrade.entryPrice.toFixed(4)}
{new Date(lastTrade.entryTime).toLocaleString()}
Position Size
${lastTrade.positionSizeUSD.toFixed(2)}
{lastTrade.leverage}x leverage
Signal Quality
{lastTrade.signalQualityScore !== undefined ? ( <>
= 80 ? 'text-green-400' : lastTrade.signalQualityScore >= 70 ? 'text-yellow-400' : 'text-orange-400'}`}> {lastTrade.signalQualityScore}/100
{lastTrade.signalQualityScore >= 80 ? 'Excellent' : lastTrade.signalQualityScore >= 70 ? 'Good' : 'Marginal'}
) : ( <>
N/A
No score available
)}
{lastTrade.exitTime && lastTrade.exitPrice && (
Exit
${lastTrade.exitPrice.toFixed(4)}
{new Date(lastTrade.exitTime).toLocaleString()}
)}
Stop Loss
${lastTrade.stopLossPrice.toFixed(4)}
TP1
${lastTrade.takeProfit1Price.toFixed(4)}
TP2
${lastTrade.takeProfit2Price.toFixed(4)}
{lastTrade.exitReason && (
Exit Reason: {lastTrade.exitReason}
)}
)} {/* Trading Statistics */} {stats && (

📈 Performance ({stats.period})

{/* Main Stats Grid */}
Total Trades
{stats.realTrades.total}
{stats.testTrades.count} test trade{stats.testTrades.count !== 1 ? 's' : ''} excluded
Win Rate
{stats.realTrades.winRate}
{stats.realTrades.winning}W / {stats.realTrades.losing}L
Total P&L
= 0 ? 'text-green-400' : 'text-red-400'}`}> {stats.realTrades.totalPnL}
Profit Factor
{stats.realTrades.profitFactor}
{/* Detailed Stats */}

Winning Trades

Count: {stats.realTrades.winning}
Average Win: {stats.realTrades.avgWin}

Losing Trades

Count: {stats.realTrades.losing}
Average Loss: {stats.realTrades.avgLoss}
{stats.realTrades.total === 0 && (
📊

No trading data yet

Start trading to see your performance statistics

)}
)} {/* Info Note */}
💡

Understanding Position Netting

Drift perpetual futures automatically NET opposite positions in the same market. If you have both LONG and SHORT positions in SOL-PERP, Drift shows only the net exposure. The database tracks individual trades for complete history, while this dashboard shows your actual market exposure.

) }