From aecdc108f6f5ae54a3afc229a3957bd2c9a8fff9 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Fri, 31 Oct 2025 10:47:19 +0100 Subject: [PATCH] Add last trade details to analytics dashboard - Add getLastTrade() function to database service - Create /api/analytics/last-trade endpoint - Display last trade with full details on analytics page - Show entry/exit prices, P&L, position size, targets - Visual indicators for trade direction and exit reason - Helps quickly diagnose where trades went (TP1, TP2, or SL) --- app/analytics/page.tsx | 126 +++++++++++++++++++++++++- app/api/analytics/last-trade/route.ts | 50 ++++++++++ lib/database/trades.ts | 18 ++++ 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 app/api/analytics/last-trade/route.ts diff --git a/app/analytics/page.tsx b/app/analytics/page.tsx index ce88e37..8e8bb28 100644 --- a/app/analytics/page.tsx +++ b/app/analytics/page.tsx @@ -26,6 +26,25 @@ interface Stats { } } +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 +} + interface NetPosition { symbol: string longUSD: number @@ -50,6 +69,7 @@ interface PositionSummary { 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) @@ -60,16 +80,19 @@ export default function AnalyticsPage() { const loadData = async () => { setLoading(true) try { - const [statsRes, positionsRes] = await Promise.all([ + 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) } @@ -194,6 +217,107 @@ export default function AnalyticsPage() { )} + {/* 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 +
+
+ + {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 && (
diff --git a/app/api/analytics/last-trade/route.ts b/app/api/analytics/last-trade/route.ts new file mode 100644 index 0000000..b1f4789 --- /dev/null +++ b/app/api/analytics/last-trade/route.ts @@ -0,0 +1,50 @@ +/** + * Last Trade API Endpoint + * + * Returns details of the most recent trade + */ + +import { NextResponse } from 'next/server' +import { getLastTrade } from '@/lib/database/trades' + +export async function GET() { + try { + const trade = await getLastTrade() + + if (!trade) { + return NextResponse.json({ + trade: null, + }) + } + + // Format the trade data for the frontend + const formattedTrade = { + id: trade.id, + symbol: trade.symbol, + direction: trade.direction, + entryPrice: trade.entryPrice, + entryTime: trade.entryTime.toISOString(), + exitPrice: trade.exitPrice || undefined, + exitTime: trade.exitTime?.toISOString() || undefined, + exitReason: trade.exitReason || undefined, + realizedPnL: trade.realizedPnL || undefined, + realizedPnLPercent: trade.realizedPnLPercent || undefined, + positionSizeUSD: trade.positionSizeUSD, + leverage: trade.leverage, + stopLossPrice: trade.stopLossPrice, + takeProfit1Price: trade.takeProfit1Price, + takeProfit2Price: trade.takeProfit2Price, + isTestTrade: trade.isTestTrade || false, + } + + return NextResponse.json({ + trade: formattedTrade, + }) + } catch (error) { + console.error('Failed to fetch last trade:', error) + return NextResponse.json( + { error: 'Failed to fetch last trade' }, + { status: 500 } + ) + } +} diff --git a/lib/database/trades.ts b/lib/database/trades.ts index 0cb4ef1..79b24c9 100644 --- a/lib/database/trades.ts +++ b/lib/database/trades.ts @@ -272,6 +272,24 @@ export async function getLastTradeTime(): Promise { } } +/** + * Get the most recent trade with full details + */ +export async function getLastTrade() { + const prisma = getPrismaClient() + + try { + const lastTrade = await prisma.trade.findFirst({ + orderBy: { createdAt: 'desc' }, + }) + + return lastTrade + } catch (error) { + console.error('❌ Failed to get last trade:', error) + return null + } +} + /** * Get count of trades in the last hour */