diff --git a/src/Managing.Application.Abstractions/Grains/IPlatformSummaryGrain.cs b/src/Managing.Application.Abstractions/Grains/IPlatformSummaryGrain.cs index 296ad7b..5369966 100644 --- a/src/Managing.Application.Abstractions/Grains/IPlatformSummaryGrain.cs +++ b/src/Managing.Application.Abstractions/Grains/IPlatformSummaryGrain.cs @@ -40,8 +40,10 @@ public interface IPlatformSummaryGrain : IGrainWithStringKey Task GetTotalPositionCountAsync(); // Event handlers for immediate updates - Task OnStrategyDeployedAsync(StrategyDeployedEvent evt); - Task OnStrategyStoppedAsync(StrategyStoppedEvent evt); + /// + /// Updates the active strategy count + /// + Task UpdateActiveStrategyCountAsync(int newActiveCount); Task OnPositionOpenedAsync(PositionOpenedEvent evt); Task OnPositionClosedAsync(PositionClosedEvent evt); Task OnTradeExecutedAsync(TradeExecutedEvent evt); @@ -57,37 +59,7 @@ public abstract class PlatformMetricsEvent public DateTime Timestamp { get; set; } = DateTime.UtcNow; } -/// -/// Event fired when a new strategy is deployed -/// -[GenerateSerializer] -public class StrategyDeployedEvent : PlatformMetricsEvent -{ - [Id(1)] - public Guid StrategyId { get; set; } - - [Id(2)] - public string AgentName { get; set; } = string.Empty; - - [Id(3)] - public string StrategyName { get; set; } = string.Empty; -} -/// -/// Event fired when a strategy is stopped -/// -[GenerateSerializer] -public class StrategyStoppedEvent : PlatformMetricsEvent -{ - [Id(1)] - public Guid StrategyId { get; set; } - - [Id(2)] - public string AgentName { get; set; } = string.Empty; - - [Id(3)] - public string StrategyName { get; set; } = string.Empty; -} /// /// Event fired when a new position is opened diff --git a/src/Managing.Application/Bots/Grains/LiveBotRegistryGrain.cs b/src/Managing.Application/Bots/Grains/LiveBotRegistryGrain.cs index 34fac44..29fa705 100644 --- a/src/Managing.Application/Bots/Grains/LiveBotRegistryGrain.cs +++ b/src/Managing.Application/Bots/Grains/LiveBotRegistryGrain.cs @@ -65,8 +65,8 @@ public class LiveBotRegistryGrain : Grain, ILiveBotRegistryGrain "Bot {Identifier} registered successfully for user {UserId}. Total bots: {TotalBots}, Active bots: {ActiveBots}", identifier, userId, _state.State.TotalBotsCount, _state.State.ActiveBotsCount); - // Notify platform summary grain about strategy deployment - await NotifyStrategyDeployedAsync(identifier, userId); + // Notify platform summary grain about strategy count change + await NotifyPlatformSummaryAsync(); } catch (Exception ex) { @@ -102,8 +102,8 @@ public class LiveBotRegistryGrain : Grain, ILiveBotRegistryGrain "Bot {Identifier} unregistered successfully from user {UserId}. Total bots: {TotalBots}", identifier, entryToRemove.UserId, _state.State.TotalBotsCount); - // Notify platform summary grain about strategy stopped - await NotifyStrategyStoppedAsync(identifier, entryToRemove.UserId); + // Notify platform summary grain about strategy count change + await NotifyPlatformSummaryAsync(); } catch (Exception ex) { @@ -187,65 +187,19 @@ public class LiveBotRegistryGrain : Grain, ILiveBotRegistryGrain return Task.FromResult(entry.Status); } - private async Task NotifyStrategyDeployedAsync(Guid identifier, int userId) + private async Task NotifyPlatformSummaryAsync() { try { - // Get bot details for the event - var bot = await _botService.GetBotByIdentifier(identifier); - if (bot != null) - { - var platformGrain = GrainFactory.GetGrain("platform-summary"); - - var deployedEvent = new StrategyDeployedEvent - { - StrategyId = identifier, - AgentName = bot.User.AgentName, - StrategyName = bot.Name - }; - - await platformGrain.OnStrategyDeployedAsync(deployedEvent); - _logger.LogDebug("Notified platform summary about strategy deployment: {StrategyName}", bot.Name); - } - else - { - _logger.LogWarning("Could not find bot {Identifier} to notify platform summary", identifier); - } + var platformGrain = GrainFactory.GetGrain("platform-summary"); + await platformGrain.UpdateActiveStrategyCountAsync(_state.State.ActiveBotsCount); + + _logger.LogDebug("Notified platform summary about active strategy count change. New count: {ActiveCount}", + _state.State.ActiveBotsCount); } catch (Exception ex) { - _logger.LogError(ex, "Failed to notify platform summary about strategy deployment for bot {Identifier}", identifier); - } - } - - private async Task NotifyStrategyStoppedAsync(Guid identifier, int userId) - { - try - { - // Get bot details for the event - var bot = await _botService.GetBotByIdentifier(identifier); - if (bot != null) - { - var platformGrain = GrainFactory.GetGrain("platform-summary"); - - var stoppedEvent = new StrategyStoppedEvent - { - StrategyId = identifier, - AgentName = bot.User?.Name ?? $"User-{userId}", - StrategyName = bot.Name - }; - - await platformGrain.OnStrategyStoppedAsync(stoppedEvent); - _logger.LogDebug("Notified platform summary about strategy stopped: {StrategyName}", bot.Name); - } - else - { - _logger.LogWarning("Could not find bot {Identifier} to notify platform summary", identifier); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to notify platform summary about strategy stopped for bot {Identifier}", identifier); + _logger.LogError(ex, "Failed to notify platform summary about strategy count change"); } } } \ No newline at end of file diff --git a/src/Managing.Application/Grains/PlatformSummaryGrain.cs b/src/Managing.Application/Grains/PlatformSummaryGrain.cs index 2b94187..527e081 100644 --- a/src/Managing.Application/Grains/PlatformSummaryGrain.cs +++ b/src/Managing.Application/Grains/PlatformSummaryGrain.cs @@ -190,7 +190,8 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable var openInterest = openPositions .Sum(p => (p.Open.Price * p.Open.Quantity) * p.Open.Leverage); - _logger.LogDebug("Calculated position metrics: {PositionCount} positions, {OpenInterest} leveraged open interest", + _logger.LogDebug( + "Calculated position metrics: {PositionCount} positions, {OpenInterest} leveraged open interest", positionCount, openInterest); return (openInterest, positionCount); @@ -229,20 +230,11 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable } // Event handlers for immediate updates - public async Task OnStrategyDeployedAsync(StrategyDeployedEvent evt) + public async Task UpdateActiveStrategyCountAsync(int newActiveCount) { - _logger.LogInformation("Strategy deployed: {StrategyId} - {StrategyName}", evt.StrategyId, evt.StrategyName); + _logger.LogInformation("Updating active strategies count to: {NewActiveCount}", newActiveCount); - _state.State.TotalActiveStrategies++; - _state.State.HasPendingChanges = true; - await _state.WriteStateAsync(); - } - - public async Task OnStrategyStoppedAsync(StrategyStoppedEvent evt) - { - _logger.LogInformation("Strategy stopped: {StrategyId} - {StrategyName}", evt.StrategyId, evt.StrategyName); - - _state.State.TotalActiveStrategies--; + _state.State.TotalActiveStrategies = newActiveCount; _state.State.HasPendingChanges = true; await _state.WriteStateAsync(); } diff --git a/src/Managing.Infrastructure.Database/PostgreSql/ManagingDbContext.cs b/src/Managing.Infrastructure.Database/PostgreSql/ManagingDbContext.cs index 581ec44..c098ce1 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/ManagingDbContext.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/ManagingDbContext.cs @@ -448,6 +448,8 @@ public class ManagingDbContext : DbContext entity.Property(e => e.Roi).HasPrecision(18, 8); entity.Property(e => e.Volume).HasPrecision(18, 8); entity.Property(e => e.Fees).HasPrecision(18, 8); + entity.Property(e => e.LongPositionCount).IsRequired(); + entity.Property(e => e.ShortPositionCount).IsRequired(); // Create indexes entity.HasIndex(e => e.Identifier).IsUnique(); diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs index 86ee99b..8dd64d5 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs @@ -73,6 +73,8 @@ public class PostgreSqlBotRepository : IBotRepository existingEntity.Volume = bot.Volume; existingEntity.Fees = bot.Fees; existingEntity.UpdatedAt = DateTime.UtcNow; + existingEntity.LongPositionCount = bot.LongPositionCount; + existingEntity.ShortPositionCount = bot.ShortPositionCount; await _context.SaveChangesAsync().ConfigureAwait(false); } @@ -158,13 +160,13 @@ public class PostgreSqlBotRepository : IBotRepository } public async Task<(IEnumerable Bots, int TotalCount)> GetBotsPaginatedAsync( - int pageNumber, - int pageSize, - BotStatus? status = null, - string? name = null, - string? ticker = null, - string? agentName = null, - string sortBy = "CreateDate", + int pageNumber, + int pageSize, + BotStatus? status = null, + string? name = null, + string? ticker = null, + string? agentName = null, + string sortBy = "CreateDate", string sortDirection = "Desc") { // Build the query with filters @@ -200,29 +202,29 @@ public class PostgreSqlBotRepository : IBotRepository // Apply sorting query = sortBy.ToLower() switch { - "name" => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.Name) + "name" => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.Name) : query.OrderByDescending(b => b.Name), - "ticker" => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.Ticker) + "ticker" => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.Ticker) : query.OrderByDescending(b => b.Ticker), - "status" => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.Status) + "status" => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.Status) : query.OrderByDescending(b => b.Status), - "startuptime" => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.StartupTime) + "startuptime" => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.StartupTime) : query.OrderByDescending(b => b.StartupTime), - "pnl" => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.Pnl) + "pnl" => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.Pnl) : query.OrderByDescending(b => b.Pnl), - "winrate" => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.TradeWins / (b.TradeWins + b.TradeLosses)) + "winrate" => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.TradeWins / (b.TradeWins + b.TradeLosses)) : query.OrderByDescending(b => b.TradeWins / (b.TradeWins + b.TradeLosses)), - "agentname" => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.User.AgentName) + "agentname" => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.User.AgentName) : query.OrderByDescending(b => b.User.AgentName), - _ => sortDirection.ToLower() == "asc" - ? query.OrderBy(b => b.CreateDate) + _ => sortDirection.ToLower() == "asc" + ? query.OrderBy(b => b.CreateDate) : query.OrderByDescending(b => b.CreateDate) // Default to CreateDate }; diff --git a/src/Managing.WebApp/src/pages/botsPage/botList.tsx b/src/Managing.WebApp/src/pages/botsPage/botList.tsx index a1743c2..5cd7bc2 100644 --- a/src/Managing.WebApp/src/pages/botsPage/botList.tsx +++ b/src/Managing.WebApp/src/pages/botsPage/botList.tsx @@ -72,7 +72,7 @@ const BotList: React.FC = ({ list }) => { const [showManualPositionModal, setShowManualPositionModal] = useState(false) const [selectedBotForManualPosition, setSelectedBotForManualPosition] = useState(null) const [showTradesModal, setShowTradesModal] = useState(false) - const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ identifier: string; agentName: string } | null>(null) + const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ name: string; agentName: string } | null>(null) const [showBotConfigModal, setShowBotConfigModal] = useState(false) const [selectedBotForUpdate, setSelectedBotForUpdate] = useState<{ identifier: string @@ -144,10 +144,10 @@ const BotList: React.FC = ({ list }) => { ) } - function getTradesBadge(botIdentifier: string, agentName: string) { + function getTradesBadge(name: string, agentName: string) { const classes = baseBadgeClass() + ' bg-secondary' return ( -