From 7a3ede03ca98d3015fed721d50bcc02029673357 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Fri, 26 Dec 2025 15:43:46 +0700 Subject: [PATCH] Add balance tracking on bot start/restart; implement TrackBalanceOnBotStartAsync in IAgentGrain and AgentGrain, and trigger it in LiveTradingBotGrain. Enhance logging for balance tracking operations. --- .../Abstractions/Grains/IAgentGrain.cs | 7 ++ .../Bots/Grains/AgentGrain.cs | 90 +++++++++++++++++++ .../Bots/Grains/LiveTradingBotGrain.cs | 18 ++++ .../Bots/TradingBotBase.cs | 11 +-- 4 files changed, 117 insertions(+), 9 deletions(-) diff --git a/src/Managing.Application/Abstractions/Grains/IAgentGrain.cs b/src/Managing.Application/Abstractions/Grains/IAgentGrain.cs index 41410f79..f9bace3d 100644 --- a/src/Managing.Application/Abstractions/Grains/IAgentGrain.cs +++ b/src/Managing.Application/Abstractions/Grains/IAgentGrain.cs @@ -67,5 +67,12 @@ namespace Managing.Application.Abstractions.Grains /// [OneWay] Task ForceUpdateSummary(); + + /// + /// Triggers balance tracking data insertion when a bot starts/restarts. + /// This captures the balance change related to botsAllocationUsdValue. + /// + [OneWay] + Task TrackBalanceOnBotStartAsync(); } } \ No newline at end of file diff --git a/src/Managing.Application/Bots/Grains/AgentGrain.cs b/src/Managing.Application/Bots/Grains/AgentGrain.cs index 1eb2d07a..2d9610db 100644 --- a/src/Managing.Application/Bots/Grains/AgentGrain.cs +++ b/src/Managing.Application/Bots/Grains/AgentGrain.cs @@ -665,4 +665,94 @@ public class AgentGrain : Grain, IAgentGrain (int)this.GetPrimaryKeyLong()); } } + + /// + /// Triggers balance tracking data insertion when a bot starts/restarts. + /// This captures the balance change related to botsAllocationUsdValue. + /// + [OneWay] + public async Task TrackBalanceOnBotStartAsync() + { + try + { + _logger.LogInformation("Tracking balance on bot start/restart for user {UserId}", this.GetPrimaryKeyLong()); + + // Get all positions for this agent's bots as initiator + var positions = (await _tradingService.GetPositionByUserIdAsync((int)this.GetPrimaryKeyLong())) + .Where(p => p.IsValidForMetrics()).ToList(); + + var metrics = TradingBox.CalculateAgentSummaryMetrics(positions); + + // Calculate total balance (USDC wallet + USDC in open positions value) + decimal totalBalance = 0; + decimal usdcWalletValue = 0; + decimal usdcInPositionsValue = 0; + try + { + var userId = (int)this.GetPrimaryKeyLong(); + var user = await _userService.GetUserByIdAsync(userId); + var userAccounts = await _accountService.GetAccountsByUserAsync(user, hideSecrets: true, true); + + foreach (var account in userAccounts) + { + // Get USDC balance + var usdcBalances = await GetOrRefreshBalanceDataAsync(account.Name); + var usdcBalance = usdcBalances?.UsdcValue ?? 0; + usdcWalletValue += usdcBalance; + } + + foreach (var position in positions.Where(p => p.IsOpen())) + { + var positionUsd = position.Open.Price * position.Open.Quantity; + var net = position.ProfitAndLoss?.Net ?? 0; + usdcInPositionsValue += positionUsd + net; + } + + totalBalance = usdcWalletValue + usdcInPositionsValue; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error calculating total balance for agent {UserId} during bot start tracking", + this.GetPrimaryKeyLong()); + totalBalance = 0; // Set to 0 if calculation fails + usdcWalletValue = 0; + usdcInPositionsValue = 0; + } + + // Get active strategies to calculate botsAllocationUsdValue + var activeStrategies = await ServiceScopeHelpers.WithScopedService>(_scopeFactory, + async (botService) => + { + return (await botService.GetBotsByUser((int)this.GetPrimaryKeyLong())) + .Where(b => b.Status == BotStatus.Running) + .ToList(); + }); + + var botsAllocationUsdValue = 0m; + + if (activeStrategies.Any()) + { + // Calculate bots allocation USD value from bot configurations + var botIds = activeStrategies.Select(b => b.Identifier); + var botConfigs = + await ServiceScopeHelpers.WithScopedService>( + _scopeFactory, + async (botService) => { return await botService.GetBotConfigsByIdsAsync(botIds); }); + botsAllocationUsdValue = botConfigs.Sum(config => config.BotTradingBalance); + } + + // Insert balance tracking data + InsertBalanceTrackingData(totalBalance, botsAllocationUsdValue, metrics.NetPnL, usdcWalletValue, + usdcInPositionsValue); + + _logger.LogInformation( + "Balance tracking completed on bot start/restart for user {UserId}: TotalBalance={TotalBalance}, BotsAllocation={BotsAllocation}, NetPnL={NetPnL}", + this.GetPrimaryKeyLong(), totalBalance, botsAllocationUsdValue, metrics.NetPnL); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error tracking balance on bot start/restart for user {UserId}", + this.GetPrimaryKeyLong()); + } + } } \ No newline at end of file diff --git a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs index 36ae06ec..24f15564 100644 --- a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs +++ b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs @@ -247,6 +247,24 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable await SaveBotAsync(BotStatus.Running); await UpdateBotRegistryStatus(BotStatus.Running); + // Trigger balance tracking since we now have a balance change related to botsAllocationUsdValue + if (_state.State.User != null) + { + try + { + var agentGrain = GrainFactory.GetGrain(_state.State.User.Id); + await agentGrain.TrackBalanceOnBotStartAsync(); + } + catch (Exception ex) + { + _logger.LogWarning(ex, + "Failed to track balance on bot start for LiveTradingBotGrain {GrainId}, but bot started successfully", + this.GetPrimaryKey()); + SentrySdk.CaptureException(ex); + // Don't throw - bot started successfully, balance tracking is non-critical + } + } + _logger.LogInformation("LiveTradingBotGrain {GrainId} resumed successfully", this.GetPrimaryKey()); } catch (Exception ex) diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index 0761225a..f90d8316 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -109,18 +109,11 @@ public abstract class TradingBotBase : ITradingBot break; case BotStatus.Running: + case BotStatus.Stopped: return; - case BotStatus.Stopped: - // If status was Stopped we log a message to inform the user that the bot is restarting - await LogInformationAsync($"🔄 Bot Restarted\n" + - $"📊 Resuming operations with {Signals.Count} signals and {Positions.Count} positions\n" + - $"✅ Ready to continue trading"); - break; - default: - // Handle any other status if needed - break; + return; } } catch (Exception ex)