From b2e38811edafbb7d1fb7c21fb0ddb3294022d053 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Thu, 25 Sep 2025 23:23:53 +0700 Subject: [PATCH] Fix global PNL --- .../Repositories/ITradingRepository.cs | 1 + .../Services/ITradingService.cs | 1 + .../Grains/PlatformSummaryGrain.cs | 26 ++++++++++++++++--- .../Trading/TradingService.cs | 5 ++++ .../PostgreSql/PostgreSqlTradingRepository.cs | 22 ++++++++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs index 326c777b..018a3585 100644 --- a/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs +++ b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs @@ -28,6 +28,7 @@ public interface ITradingRepository Task> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier); Task> GetPositionsByInitiatorIdentifiersAsync(IEnumerable initiatorIdentifiers); Task> GetAllPositionsAsync(); + Task GetGlobalPnLFromPositionsAsync(); Task UpdateScenarioAsync(Scenario scenario); Task UpdateStrategyAsync(IndicatorBase indicatorBase); diff --git a/src/Managing.Application.Abstractions/Services/ITradingService.cs b/src/Managing.Application.Abstractions/Services/ITradingService.cs index f0c04db0..2deba19c 100644 --- a/src/Managing.Application.Abstractions/Services/ITradingService.cs +++ b/src/Managing.Application.Abstractions/Services/ITradingService.cs @@ -39,6 +39,7 @@ public interface ITradingService Task> GetAllDatabasePositionsAsync(); Task> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier); Task> GetPositionsByInitiatorIdentifiersAsync(IEnumerable initiatorIdentifiers); + Task GetGlobalPnLFromPositionsAsync(); Task InitPrivyWallet(string publicAddress, TradingExchanges tradingExchange); // Synth API integration methods diff --git a/src/Managing.Application/Grains/PlatformSummaryGrain.cs b/src/Managing.Application/Grains/PlatformSummaryGrain.cs index 4c13edf7..8bb7624a 100644 --- a/src/Managing.Application/Grains/PlatformSummaryGrain.cs +++ b/src/Managing.Application/Grains/PlatformSummaryGrain.cs @@ -106,9 +106,11 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable var totalAgents = agents.Count(); var totalActiveStrategies = strategies.Count(s => s.Status == BotStatus.Running); - // Calculate volume and PnL from strategies + // Calculate volume from strategies var totalVolume = strategies.Sum(s => s.Volume); - var totalPnL = strategies.Sum(s => s.Pnl); + + // Calculate PnL directly from database positions (closed positions only) + var totalPnL = await _tradingService.GetGlobalPnLFromPositionsAsync(); // Calculate real open interest and position count from actual positions var (totalOpenInterest, totalPositionCount) = await CalculatePositionMetricsAsync(); @@ -317,7 +319,11 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable } _state.State.TotalPlatformVolume += evt.Volume; - _state.State.TotalPlatformPnL += evt.RealizedPnL; + + // PnL is now calculated directly from database positions, not from events + // This ensures accuracy and prevents double-counting issues + // Refresh PnL from database to get the latest accurate value + await RefreshPnLFromDatabaseAsync(); // Update volume by asset var asset = evt.Ticker; @@ -452,6 +458,20 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable await _state.WriteStateAsync(); } + private async Task RefreshPnLFromDatabaseAsync() + { + try + { + var totalPnL = await _tradingService.GetGlobalPnLFromPositionsAsync(); + _state.State.TotalPlatformPnL = totalPnL; + _logger.LogDebug("Refreshed PnL from database: {TotalPnL}", totalPnL); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error refreshing PnL from database"); + } + } + private bool IsDataStale() { var timeSinceLastUpdate = DateTime.UtcNow - _state.State.LastUpdated; diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs index d42da74e..939905e5 100644 --- a/src/Managing.Application/Trading/TradingService.cs +++ b/src/Managing.Application/Trading/TradingService.cs @@ -273,6 +273,11 @@ public class TradingService : ITradingService return await _tradingRepository.GetPositionsByInitiatorIdentifiersAsync(initiatorIdentifiers); } + public async Task GetGlobalPnLFromPositionsAsync() + { + return await _tradingRepository.GetGlobalPnLFromPositionsAsync(); + } + private async Task ManageTrader(TraderFollowup a, List tickers) { var shortAddress = a.Account.Address.Substring(0, 6); diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs index cddd89d6..d4a54b5d 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs @@ -505,6 +505,28 @@ public class PostgreSqlTradingRepository : ITradingRepository return PostgreSqlMappers.Map(positions); } + public async Task GetGlobalPnLFromPositionsAsync() + { + try + { + await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context); + + // Calculate total PnL from all finished positions (closed positions) + // Only include positions that are Finished or Flipped (closed positions) + var totalPnL = await _context.Positions + .AsNoTracking() + .Where(p => p.Status == PositionStatus.Finished || p.Status == PositionStatus.Flipped) + .SumAsync(p => p.ProfitAndLoss) + .ConfigureAwait(false); + + return totalPnL; + } + finally + { + await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context); + } + } + #endregion #region Signal Methods