From 79f07af89950dde6028eb25c9404ca7422bb7f5c Mon Sep 17 00:00:00 2001 From: cryptooda Date: Mon, 20 Oct 2025 16:20:36 +0700 Subject: [PATCH] Fix get Balance --- .../Bots/Grains/AgentGrain.cs | 4 +- .../Grains/PlatformSummaryGrain.cs | 303 +++++++++++------- .../Services/ChainService.cs | 2 +- .../Services/TokenService.cs | 3 +- 4 files changed, 193 insertions(+), 119 deletions(-) diff --git a/src/Managing.Application/Bots/Grains/AgentGrain.cs b/src/Managing.Application/Bots/Grains/AgentGrain.cs index 9a5d80c3..1ff41004 100644 --- a/src/Managing.Application/Bots/Grains/AgentGrain.cs +++ b/src/Managing.Application/Bots/Grains/AgentGrain.cs @@ -361,7 +361,7 @@ public class AgentGrain : Grain, IAgentGrain // Get or refresh cached balance data var balanceData = await GetOrRefreshBalanceDataAsync(accountName); - if (balanceData == null) + if (balanceData == null || balanceData.IsValid == false) { _logger.LogError("Failed to get balance data for account {AccountName}, user {UserId}", accountName, this.GetPrimaryKeyLong()); @@ -645,7 +645,7 @@ public class AgentGrain : Grain, IAgentGrain { _logger.LogError(ex, "Error fetching balance data for account {AccountName}, user {UserId}", accountName, this.GetPrimaryKeyLong()); - return null; + throw; } } diff --git a/src/Managing.Application/Grains/PlatformSummaryGrain.cs b/src/Managing.Application/Grains/PlatformSummaryGrain.cs index 2ea1849f..24be8d0a 100644 --- a/src/Managing.Application/Grains/PlatformSummaryGrain.cs +++ b/src/Managing.Application/Grains/PlatformSummaryGrain.cs @@ -64,6 +64,9 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable _logger.LogInformation("Daily reminder scheduled - Next daily: {NextDaily}, Due time: {DueTime}", nextDailyTime, timeUntilNextDay); + // Fix the first snapshot date if needed (should be day before first position) + await FixFirstSnapshotDateAsync(); + // Wipe daily snapshots except for the first day await WipeDailySnapshotsExceptFirstAsync(); @@ -76,10 +79,28 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable // Create initial empty daily snapshot if none exist if (!_state.State.DailySnapshots.Any()) { - var today = DateTime.UtcNow.Date.AddSeconds(1); // Today at 00:00:01 UTC + // Find the first position date to determine when to create the initial snapshot + var positions = await _tradingService.GetAllDatabasePositionsAsync(); + DateTime initialSnapshotDate; + + if (positions.Any()) + { + // Create initial snapshot on the day BEFORE the first position + var firstPositionDate = positions.Min(p => p.Date).Date; + initialSnapshotDate = firstPositionDate.AddDays(-1); + _logger.LogInformation("First position found on {FirstPositionDate}, creating initial snapshot on {InitialSnapshotDate}", + firstPositionDate, initialSnapshotDate); + } + else + { + // No positions yet, create snapshot for today + initialSnapshotDate = DateTime.UtcNow.Date; + _logger.LogInformation("No positions found, creating initial snapshot for today: {InitialSnapshotDate}", initialSnapshotDate); + } + var initialSnapshot = new DailySnapshot { - Date = today, + Date = initialSnapshotDate, TotalAgents = 0, TotalStrategies = 0, TotalVolume = 0, @@ -94,7 +115,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable _state.State.LastSnapshot = initialSnapshot.Date; _state.State.LastUpdated = initialSnapshot.Date; - _logger.LogInformation("Created initial empty daily snapshot for {Date}", today); + _logger.LogInformation("Created initial empty daily snapshot for {Date}", initialSnapshotDate); } await RefreshDataAsync(); @@ -138,6 +159,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable // Calculate all metrics from positions in a single loop var newVolume = 0m; // Volume from positions after last snapshot + var totalVolumeFromAllPositions = 0m; // Total volume calculated from ALL positions (for comparison) var totalFees = 0m; var totalPnL = 0m; var totalOpenInterest = 0m; @@ -154,16 +176,25 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable // Calculate volume using the dedicated method var positionVolume = TradingHelpers.GetVolumeForPosition(position); + + // Track total volume from ALL positions for debugging + totalVolumeFromAllPositions += positionVolume; // For cumulative volume: only add volume from positions created AFTER last snapshot // For volume breakdown: include all positions if (position.Date.Date > lastSnapshotDate) { newVolume += positionVolume; - _logger.LogDebug( + _logger.LogInformation( "Position {PositionId} created after last snapshot ({PositionDate} > {LastSnapshotDate}), adding volume: {Volume}", position.Identifier, position.Date.Date, lastSnapshotDate, positionVolume); } + else + { + _logger.LogDebug( + "Position {PositionId} created before/on last snapshot ({PositionDate} <= {LastSnapshotDate}), skipping volume: {Volume}", + position.Identifier, position.Date.Date, lastSnapshotDate, positionVolume); + } // Calculate breakdown metrics from ALL positions (for current state) var ticker = position.Ticker; @@ -210,19 +241,30 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable // CUMULATIVE volume: baseline + new volume since last snapshot var updatedCumulativeVolume = cumulativeVolume + newVolume; - _logger.LogInformation("Volume calculation: Base={BaseVolume}, New={NewVolume}, Total={TotalVolume}", - cumulativeVolume, newVolume, updatedCumulativeVolume); + _logger.LogWarning( + "Volume calculation DEBUG: TotalFromAllPositions={TotalFromAll}, LastSnapshotVolume={LastSnapshot}, NewVolumeSinceSnapshot={NewVolume}, CalculatedCumulative={Cumulative}, CurrentState={CurrentState}", + totalVolumeFromAllPositions, cumulativeVolume, newVolume, updatedCumulativeVolume, + _state.State.TotalPlatformVolume); + + // ISSUE FIX: The correct cumulative volume should be calculated from ALL positions + // not just by adding new positions to the last snapshot, because: + // 1. Positions might be closed/updated which affects their volume + // 2. The daily snapshot might be outdated + // We should use the total from all positions, but never let it decrease + var correctCumulativeVolume = totalVolumeFromAllPositions; // Ensure volume never decreases - if (updatedCumulativeVolume < _state.State.TotalPlatformVolume) + if (correctCumulativeVolume < _state.State.TotalPlatformVolume) { _logger.LogWarning( - "Calculated cumulative volume ({Calculated}) is less than current volume ({Current}). Keeping current value.", - updatedCumulativeVolume, _state.State.TotalPlatformVolume); - updatedCumulativeVolume = _state.State.TotalPlatformVolume; + "Calculated volume ({Calculated}) is less than current volume ({Current}). Keeping current value to maintain cumulative nature.", + correctCumulativeVolume, _state.State.TotalPlatformVolume); + correctCumulativeVolume = _state.State.TotalPlatformVolume; } - _state.State.TotalPlatformVolume = updatedCumulativeVolume; + _state.State.TotalPlatformVolume = correctCumulativeVolume; + + _logger.LogInformation("Final volume set to: {FinalVolume}", correctCumulativeVolume); _state.State.TotalPlatformFees = totalFees; _state.State.TotalPlatformPnL = totalPnL; _state.State.NetPnL = totalPnL - totalFees; // Calculate NetPnL @@ -368,6 +410,67 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable return timeSinceLastUpdate > TimeSpan.FromMinutes(5); } + /// + /// Fixes the first snapshot date to ensure it's the day before the first position + /// + private async Task FixFirstSnapshotDateAsync() + { + try + { + if (!_state.State.DailySnapshots.Any()) + { + _logger.LogInformation("No daily snapshots to fix"); + return; + } + + var positions = await _tradingService.GetAllDatabasePositionsAsync(); + if (!positions.Any()) + { + _logger.LogInformation("No positions found, cannot determine correct first snapshot date"); + return; + } + + var firstPositionDate = positions.Min(p => p.Date).Date; + var correctFirstSnapshotDate = firstPositionDate.AddDays(-1); + var currentFirstSnapshot = _state.State.DailySnapshots.OrderBy(s => s.Date).First(); + + _logger.LogInformation( + "First position date: {FirstPositionDate}, Current first snapshot: {CurrentFirstSnapshot}, Correct first snapshot should be: {CorrectFirstSnapshot}", + firstPositionDate, currentFirstSnapshot.Date, correctFirstSnapshotDate); + + // If the first snapshot date is wrong, fix it + if (currentFirstSnapshot.Date.Date != correctFirstSnapshotDate.Date) + { + _logger.LogWarning( + "First snapshot date {CurrentDate} is incorrect. Should be {CorrectDate} (day before first position). Fixing...", + currentFirstSnapshot.Date, correctFirstSnapshotDate); + + // Update the first snapshot date + currentFirstSnapshot.Date = correctFirstSnapshotDate; + + // Reset all metrics to 0 for the first snapshot (it's before any activity) + currentFirstSnapshot.TotalVolume = 0; + currentFirstSnapshot.TotalPnL = 0; + currentFirstSnapshot.NetPnL = 0; + currentFirstSnapshot.TotalOpenInterest = 0; + currentFirstSnapshot.TotalLifetimePositionCount = 0; + currentFirstSnapshot.TotalPlatformFees = 0; + + await _state.WriteStateAsync(); + + _logger.LogInformation("Fixed first snapshot date to {CorrectDate}", correctFirstSnapshotDate); + } + else + { + _logger.LogInformation("First snapshot date is correct"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error fixing first snapshot date"); + } + } + /// /// Wipes all daily snapshots except for the first day /// @@ -485,6 +588,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable /// /// Calculates a CUMULATIVE daily snapshot from positions up to a specific date + /// Based on SQL query logic: OpenVolume + ClosingVolume per position /// /// All positions to analyze /// The date to calculate the snapshot up to @@ -492,127 +596,96 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable private async Task CalculateDailySnapshotFromPositionsAsync(List positions, DateTime targetDate) { - var dayStart = targetDate; - var dayEnd = targetDate.AddDays(1); - - // For CUMULATIVE daily snapshots, we need to consider ALL positions to calculate: - // 1. TOTAL volume from all trades that occurred on or before this day - // 2. Open interest from positions that were active during this day - // 3. Cumulative position count up to this date - // So we'll process all positions and include relevant data cumulatively - - // Calculate metrics for this specific day + // Calculate CUMULATIVE metrics: sum of ALL volume/PnL from positions opened on or before target date var totalVolume = 0m; var totalFees = 0m; var totalPnL = 0m; - var maxOpenInterest = 0m; + var currentOpenInterest = 0m; var totalPositionCount = 0; - // Calculate open interest at different points during the day to find the maximum - var hourlyOpenInterest = new List(); - - // Check open interest at each hour of the day (0-23) - for (int hour = 0; hour < 24; hour++) - { - var hourDateTime = targetDate.AddHours(hour); - var hourlyOI = 0m; - - foreach (var position in positions) - { - // Check if position was active at this hour - var wasActiveAtThisHour = position.Date <= hourDateTime && - (position.IsOpen() || - (position.StopLoss.Status == TradeStatus.Filled && - position.StopLoss.Date > hourDateTime) || - (position.TakeProfit1.Status == TradeStatus.Filled && - position.TakeProfit1.Date > hourDateTime) || - (position.TakeProfit2 != null && - position.TakeProfit2.Status == TradeStatus.Filled && - position.TakeProfit2.Date > hourDateTime)); - - if (wasActiveAtThisHour) - { - // For open interest, only count the opening volume (not closing trades) - var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage; - hourlyOI += openingVolume; - } - } - - hourlyOpenInterest.Add(hourlyOI); - } - - // Find the maximum open interest during the day - maxOpenInterest = hourlyOpenInterest.Max(); + _logger.LogInformation("Calculating snapshot for {TargetDate} with {PositionCount} positions", + targetDate, positions.Count); foreach (var position in positions) { - // Calculate CUMULATIVE volume up to this point in time - // Include all positions that were opened on or before the target date + // Only include positions that were OPENED on or before the target date + if (position.Date.Date > targetDate) + { + _logger.LogDebug("Position {PositionId} opened after target date ({PositionDate} > {TargetDate}), skipping", + position.Identifier, position.Date.Date, targetDate); + continue; + } + + // Count this position in the cumulative count + totalPositionCount++; + + // Calculate volume for this position using SQL logic: + // OpenVolume = Price * Quantity * Leverage (from opening trade) + var openVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage; + var closingVolume = 0m; + + // ClosingVolume = Sum of all filled closing trades (SL, TP1, TP2) that happened on or before target date + if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped) + { + // Stop Loss volume (if filled and on or before target date) + if (position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) + { + closingVolume += position.StopLoss.Price * position.StopLoss.Quantity * position.StopLoss.Leverage; + } + + // Take Profit 1 volume (if filled and on or before target date) + if (position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) + { + closingVolume += position.TakeProfit1.Price * position.TakeProfit1.Quantity * position.TakeProfit1.Leverage; + } + + // Take Profit 2 volume (if filled and on or before target date) + if (position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date <= targetDate) + { + closingVolume += position.TakeProfit2.Price * position.TakeProfit2.Quantity * position.TakeProfit2.Leverage; + } + } + + // Total volume for this position = opening + closing + var positionVolume = openVolume + closingVolume; + totalVolume += positionVolume; + _logger.LogDebug( - "Checking position {PositionId}: Position.Date={PositionDate}, TargetDate={TargetDate}, Position.Date.Date={PositionDateOnly}", - position.Identifier, position.Date, targetDate, position.Date.Date); + "Position {PositionId} (opened {OpenDate}): OpenVolume={OpenVol}, ClosingVolume={CloseVol}, Total={Total}", + position.Identifier, position.Date.Date, openVolume, closingVolume, positionVolume); - // Add opening volume if position was opened on or before this day - // Use more flexible date comparison to handle timezone differences - if (position.Date.Date <= targetDate) + // Calculate open interest (only for positions that are still open on target date) + if (position.IsOpen()) { - var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage; - totalVolume += openingVolume; - _logger.LogDebug( - "Position {PositionId} opened on/before {TargetDate}: Opening volume = {OpeningVolume}", - position.Identifier, targetDate, openingVolume); + currentOpenInterest += openVolume; } - - // Add closing volume if position was closed on or before this day - if (position.IsValidForMetrics()) + else if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped) { - if (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) - { - var closingVolume = position.StopLoss.Price * position.StopLoss.Quantity * - position.StopLoss.Leverage; - totalVolume += closingVolume; - _logger.LogDebug( - "Position {PositionId} closed on/before {TargetDate} via StopLoss: Closing volume = {ClosingVolume}", - position.Identifier, targetDate, closingVolume); - } + // Check if position was still open on the target date (closed after target date) + var closedAfterTargetDate = + (position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date.Date > targetDate) || + (position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date > targetDate) || + (position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date > targetDate); - if (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) + if (closedAfterTargetDate) { - var closingVolume = position.TakeProfit1.Price * position.TakeProfit1.Quantity * - position.TakeProfit1.Leverage; - totalVolume += closingVolume; - _logger.LogDebug( - "Position {PositionId} closed on/before {TargetDate} via TakeProfit1: Closing volume = {ClosingVolume}", - position.Identifier, targetDate, closingVolume); - } - - if (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled && - position.TakeProfit2.Date.Date <= targetDate) - { - var closingVolume = position.TakeProfit2.Price * position.TakeProfit2.Quantity * - position.TakeProfit2.Leverage; - totalVolume += closingVolume; + currentOpenInterest += openVolume; } } - // Calculate CUMULATIVE fees and PnL for positions closed on or before this day - var wasClosedOnOrBeforeThisDay = position.IsValidForMetrics() && ( - (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) || - (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) || - (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled && - position.TakeProfit2.Date.Date <= targetDate) - ); - - if (wasClosedOnOrBeforeThisDay) + // Calculate fees and PnL for FINISHED positions (only if closed on or before target date) + if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped) { - totalFees += position.CalculateTotalFees(); - totalPnL += position.ProfitAndLoss?.Realized ?? 0; - } + var wasClosedByTargetDate = + (position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) || + (position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) || + (position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date <= targetDate); - // Count positions that were created on or before this day (CUMULATIVE position count) - if (position.Date.Date <= targetDate) - { - totalPositionCount++; + if (wasClosedByTargetDate) + { + totalFees += position.CalculateTotalFees(); + totalPnL += position.ProfitAndLoss?.Realized ?? 0; + } } } @@ -621,8 +694,8 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable var totalStrategies = _state.State.TotalActiveStrategies; _logger.LogInformation( - "Calculated CUMULATIVE daily snapshot for {TargetDate}: CumVolume={TotalVolume}, MaxOpenInterest={MaxOpenInterest}, CumPositionCount={TotalPositionCount}", - targetDate, totalVolume, maxOpenInterest, totalPositionCount); + "Calculated CUMULATIVE snapshot for {TargetDate}: CumVolume={TotalVolume}, OpenInterest={OpenInterest}, CumPositionCount={TotalPositionCount}, Fees={Fees}, PnL={PnL}", + targetDate, totalVolume, currentOpenInterest, totalPositionCount, totalFees, totalPnL); return new DailySnapshot { @@ -632,7 +705,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable TotalVolume = totalVolume, TotalPnL = totalPnL, NetPnL = totalPnL - totalFees, - TotalOpenInterest = maxOpenInterest, + TotalOpenInterest = currentOpenInterest, TotalLifetimePositionCount = totalPositionCount, TotalPlatformFees = (int)totalFees, }; diff --git a/src/Managing.Infrastructure.Web3/Services/ChainService.cs b/src/Managing.Infrastructure.Web3/Services/ChainService.cs index b205e729..bb16e73a 100644 --- a/src/Managing.Infrastructure.Web3/Services/ChainService.cs +++ b/src/Managing.Infrastructure.Web3/Services/ChainService.cs @@ -26,7 +26,7 @@ public static class ChainService var chains = new List() { GetArbitrum(), - GetEthereum(), + // GetEthereum(), //GetArbitrumGoerli(), //GetGoerli() }; diff --git a/src/Managing.Infrastructure.Web3/Services/TokenService.cs b/src/Managing.Infrastructure.Web3/Services/TokenService.cs index 89c356fc..16ce898f 100644 --- a/src/Managing.Infrastructure.Web3/Services/TokenService.cs +++ b/src/Managing.Infrastructure.Web3/Services/TokenService.cs @@ -80,7 +80,8 @@ public static class TokenService Ticker.AAVE, Ticker.XRP, Ticker.PENDLE, - Ticker.BNB + Ticker.BNB, + Ticker.USDC }; } } \ No newline at end of file