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)