diff --git a/api/server.go b/api/server.go index 72ae2d27..5918e1c5 100644 --- a/api/server.go +++ b/api/server.go @@ -2505,11 +2505,15 @@ func (s *Server) handleEquityHistoryBatch(c *gin.Context) { // getEquityHistoryForTraders Get historical data for multiple traders // Query directly from database, not dependent on trader in memory (so historical data can be retrieved after restart) +// Also appends current real-time data point to ensure chart matches leaderboard func (s *Server) getEquityHistoryForTraders(traderIDs []string) map[string]interface{} { result := make(map[string]interface{}) histories := make(map[string]interface{}) errors := make(map[string]string) + // Use a single consistent timestamp for all real-time data points + now := time.Now() + // Pre-fetch initial balances for all traders initialBalances := make(map[string]float64) for _, traderID := range traderIDs { @@ -2535,21 +2539,16 @@ func (s *Server) getEquityHistoryForTraders(traderIDs []string) map[string]inter continue } - if len(snapshots) == 0 { - // No historical records, return empty array - histories[traderID] = []map[string]interface{}{} - continue - } - // Get initial balance for calculating PnL percentage initialBalance := initialBalances[traderID] - if initialBalance <= 0 { + if initialBalance <= 0 && len(snapshots) > 0 { // If no initial balance configured, use the first snapshot's equity as baseline initialBalance = snapshots[0].TotalEquity } // Build return rate historical data with PnL percentage - history := make([]map[string]interface{}, 0, len(snapshots)) + history := make([]map[string]interface{}, 0, len(snapshots)+1) + var lastSnapshotTime time.Time for _, snap := range snapshots { // Calculate PnL percentage: (current_equity - initial_balance) / initial_balance * 100 pnlPct := 0.0 @@ -2564,6 +2563,43 @@ func (s *Server) getEquityHistoryForTraders(traderIDs []string) map[string]inter "total_pnl_pct": pnlPct, "balance": snap.Balance, }) + if snap.Timestamp.After(lastSnapshotTime) { + lastSnapshotTime = snap.Timestamp + } + } + + // Append current real-time data point to ensure chart matches leaderboard + // This ensures the latest point is always current, not from a potentially stale snapshot + if trader, err := s.traderManager.GetTrader(traderID); err == nil { + if accountInfo, err := trader.GetAccountInfo(); err == nil { + // Only append if it's been more than 30 seconds since last snapshot + if now.Sub(lastSnapshotTime) > 30*time.Second { + totalEquity := 0.0 + if v, ok := accountInfo["total_equity"].(float64); ok { + totalEquity = v + } + totalPnL := 0.0 + if v, ok := accountInfo["total_pnl"].(float64); ok { + totalPnL = v + } + walletBalance := 0.0 + if v, ok := accountInfo["wallet_balance"].(float64); ok { + walletBalance = v + } + pnlPct := 0.0 + if initialBalance > 0 { + pnlPct = (totalEquity - initialBalance) / initialBalance * 100 + } + + history = append(history, map[string]interface{}{ + "timestamp": now, + "total_equity": totalEquity, + "total_pnl": totalPnL, + "total_pnl_pct": pnlPct, + "balance": walletBalance, + }) + } + } } histories[traderID] = history diff --git a/web/src/components/ComparisonChart.tsx b/web/src/components/ComparisonChart.tsx index 216b56ef..dfd75181 100644 --- a/web/src/components/ComparisonChart.tsx +++ b/web/src/components/ComparisonChart.tsx @@ -257,11 +257,19 @@ export function ComparisonChart({ traders }: ComparisonChartProps) { return null } - // Calculate stats - const lastPoint = displayData[displayData.length - 1] + // Calculate stats - find each trader's last available data point const traderStats = traders.map(trader => { - const currentPnl = lastPoint?.[`${trader.trader_id}_pnl_pct`] || 0 - const currentEquity = lastPoint?.[`${trader.trader_id}_equity`] || 0 + // Find the last data point that has data for this trader + let currentPnl = 0 + let currentEquity = 0 + for (let i = displayData.length - 1; i >= 0; i--) { + const pnl = displayData[i]?.[`${trader.trader_id}_pnl_pct`] + if (pnl !== undefined) { + currentPnl = pnl + currentEquity = displayData[i]?.[`${trader.trader_id}_equity`] || 0 + break + } + } return { ...trader, currentPnl, currentEquity } }).sort((a, b) => b.currentPnl - a.currentPnl) @@ -274,7 +282,7 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {