From f72bfc4ab87853739a2e68f283804bfbef39854d Mon Sep 17 00:00:00 2001 From: cryptooda Date: Fri, 3 Oct 2025 16:43:20 +0700 Subject: [PATCH] Update balance tracking --- .../Repositories/IAgentBalanceRepository.cs | 2 +- .../Services/IAgentService.cs | 1 + .../Agents/AgentService.cs | 38 +++++++++++++++-- .../Bots/Grains/AgentGrain.cs | 29 ++++++++----- .../Statistics/AgentBalance.cs | 4 +- .../Statistics/AgentBalanceHistory.cs | 1 + .../InfluxDb/AgentBalanceRepository.cs | 37 +++++++++++----- .../InfluxDb/Models/AgentBalanceDto.cs | 6 ++- .../src/generated/ManagingApi.ts | 5 ++- .../src/generated/ManagingApiTypes.ts | 5 ++- .../src/pages/dashboardPage/agentSearch.tsx | 42 ++++++++++++++++--- 11 files changed, 136 insertions(+), 34 deletions(-) diff --git a/src/Managing.Application.Abstractions/Repositories/IAgentBalanceRepository.cs b/src/Managing.Application.Abstractions/Repositories/IAgentBalanceRepository.cs index 64b8ac9e..a1bd8d85 100644 --- a/src/Managing.Application.Abstractions/Repositories/IAgentBalanceRepository.cs +++ b/src/Managing.Application.Abstractions/Repositories/IAgentBalanceRepository.cs @@ -5,7 +5,7 @@ namespace Managing.Application.Abstractions.Repositories; public interface IAgentBalanceRepository { void InsertAgentBalance(AgentBalance balance); - Task> GetAgentBalances(string agentName, DateTime start, DateTime? end = null); + Task> GetAgentBalancesByUserId(int userId, DateTime start, DateTime? end = null); Task<(IList result, int totalCount)> GetAllAgentBalancesWithHistory(DateTime start, DateTime? end); diff --git a/src/Managing.Application.Abstractions/Services/IAgentService.cs b/src/Managing.Application.Abstractions/Services/IAgentService.cs index 50c0cc2a..4ca1ee0d 100644 --- a/src/Managing.Application.Abstractions/Services/IAgentService.cs +++ b/src/Managing.Application.Abstractions/Services/IAgentService.cs @@ -5,6 +5,7 @@ namespace Managing.Application.Abstractions.Services; public interface IAgentService { Task GetAgentBalances(string agentName, DateTime start, DateTime? end = null); + Task GetAgentBalancesByUserId(int userId, DateTime start, DateTime? end = null); Task<(IList Agents, int TotalCount)> GetBestAgents(DateTime start, DateTime? end = null, int page = 1, diff --git a/src/Managing.Application/Agents/AgentService.cs b/src/Managing.Application/Agents/AgentService.cs index 9333f5cd..311f6d4a 100644 --- a/src/Managing.Application/Agents/AgentService.cs +++ b/src/Managing.Application/Agents/AgentService.cs @@ -31,9 +31,40 @@ public class AgentService : IAgentService public async Task GetAgentBalances(string agentName, DateTime start, DateTime? end = null) + { + // Get userId from agentName by looking up the user + var userId = await ServiceScopeHelpers.WithScopedService(_serviceScopeFactory, + async (userService) => + { + var user = await userService.GetUserByAgentName(agentName); + return user?.Id; + }); + + if (!userId.HasValue) + { + // Return empty result if user not found + return new AgentBalanceHistory + { + UserId = 0, + AgentName = agentName, + AgentBalances = new List() + }; + } + + // Use the UserId-based method internally + var result = await GetAgentBalancesByUserId(userId.Value, start, end); + + // Override the AgentName to use the requested agentName instead of the default + result.AgentName = agentName; + + return result; + } + + public async Task GetAgentBalancesByUserId(int userId, DateTime start, + DateTime? end = null) { var effectiveEnd = end ?? DateTime.UtcNow; - string cacheKey = $"AgentBalances_{agentName}_{start:yyyyMMdd}_{effectiveEnd:yyyyMMdd}"; + string cacheKey = $"AgentBalancesByUserId_{userId}_{start:yyyyMMdd}_{effectiveEnd:yyyyMMdd}"; // Check if the balances are already cached var cachedBalances = _cacheService.GetValue(cacheKey); @@ -43,12 +74,13 @@ public class AgentService : IAgentService return cachedBalances; } - var balances = await _agentBalanceRepository.GetAgentBalances(agentName, start, end); + var balances = await _agentBalanceRepository.GetAgentBalancesByUserId(userId, start, end); // Create a single AgentBalanceHistory with all balances var result = new AgentBalanceHistory { - AgentName = agentName, + UserId = userId, + AgentName = $"User_{userId}", AgentBalances = balances.OrderBy(b => b.Time).ToList() }; diff --git a/src/Managing.Application/Bots/Grains/AgentGrain.cs b/src/Managing.Application/Bots/Grains/AgentGrain.cs index 11bc2c8e..6789c63f 100644 --- a/src/Managing.Application/Bots/Grains/AgentGrain.cs +++ b/src/Managing.Application/Bots/Grains/AgentGrain.cs @@ -193,8 +193,10 @@ public class AgentGrain : Grain, IAgentGrain _ => 0 }; - // Calculate total balance (USDC + open positions value) + // 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(); @@ -209,20 +211,25 @@ public class AgentGrain : Grain, IAgentGrain // Get USDC balance var usdcBalances = await GetOrRefreshBalanceDataAsync(account.Name); var usdcBalance = usdcBalances?.UsdcValue ?? 0; - totalBalance += usdcBalance; + usdcWalletValue += usdcBalance; } foreach (var position in positions.Where(p => !p.IsFinished())) { - totalBalance += position.Open.Price * position.Open.Quantity; - totalBalance += position.ProfitAndLoss?.Realized ?? 0; + var positionUsd = position.Open.Price * position.Open.Quantity; + var realized = position.ProfitAndLoss?.Realized ?? 0; + usdcInPositionsValue += positionUsd + realized; } + + totalBalance = usdcWalletValue + usdcInPositionsValue; } } catch (Exception ex) { _logger.LogError(ex, "Error calculating total balance for agent {UserId}", this.GetPrimaryKeyLong()); totalBalance = 0; // Set to 0 if calculation fails + usdcWalletValue = 0; + usdcInPositionsValue = 0; } var bots = await ServiceScopeHelpers.WithScopedService>(_scopeFactory, @@ -269,7 +276,7 @@ public class AgentGrain : Grain, IAgentGrain await _agentService.SaveOrUpdateAgentSummary(summary); // Insert balance tracking data - InsertBalanceTrackingData(totalBalance, botsAllocationUsdValue, netPnL); + InsertBalanceTrackingData(totalBalance, botsAllocationUsdValue, netPnL, usdcWalletValue, usdcInPositionsValue); _logger.LogDebug( "Updated agent summary from position data for user {UserId}: NetPnL={NetPnL}, TotalPnL={TotalPnL}, Fees={Fees}, Volume={Volume}, Wins={Wins}, Losses={Losses}", @@ -626,14 +633,16 @@ public class AgentGrain : Grain, IAgentGrain /// /// Inserts balance tracking data into the AgentBalanceRepository /// - private void InsertBalanceTrackingData(decimal totalAccountUsdValue, decimal botsAllocationUsdValue, decimal pnl) + private void InsertBalanceTrackingData(decimal totalAccountUsdValue, decimal botsAllocationUsdValue, decimal pnl, decimal usdcWalletValue, decimal usdcInPositionsValue) { try { var agentBalance = new AgentBalance { - AgentName = _state.State.AgentName, + UserId = (int)this.GetPrimaryKeyLong(), TotalBalanceValue = totalAccountUsdValue, + UsdcWalletValue = usdcWalletValue, + UsdcInPositionsValue = usdcInPositionsValue, BotsAllocationUsdValue = botsAllocationUsdValue, PnL = pnl, Time = DateTime.UtcNow @@ -642,13 +651,13 @@ public class AgentGrain : Grain, IAgentGrain _agentBalanceRepository.InsertAgentBalance(agentBalance); _logger.LogDebug( - "Inserted balance tracking data for agent {AgentName}: TotalBalanceValue={TotalBalanceValue}, BotsAllocationUsdValue={BotsAllocationUsdValue}, PnL={PnL}", - agentBalance.AgentName, agentBalance.TotalBalanceValue, agentBalance.BotsAllocationUsdValue, + "Inserted balance tracking data for user {UserId}: TotalBalanceValue={TotalBalanceValue}, BotsAllocationUsdValue={BotsAllocationUsdValue}, PnL={PnL}", + agentBalance.UserId, agentBalance.TotalBalanceValue, agentBalance.BotsAllocationUsdValue, agentBalance.PnL); } catch (Exception ex) { - _logger.LogError(ex, "Error inserting balance tracking data for agent {AgentName}", _state.State.AgentName); + _logger.LogError(ex, "Error inserting balance tracking data for user {UserId}", (int)this.GetPrimaryKeyLong()); } } } \ No newline at end of file diff --git a/src/Managing.Domain/Statistics/AgentBalance.cs b/src/Managing.Domain/Statistics/AgentBalance.cs index d8d395d4..32c31258 100644 --- a/src/Managing.Domain/Statistics/AgentBalance.cs +++ b/src/Managing.Domain/Statistics/AgentBalance.cs @@ -2,8 +2,10 @@ namespace Managing.Domain.Statistics; public class AgentBalance { - public string AgentName { get; set; } + public int UserId { get; set; } public decimal TotalBalanceValue { get; set; } + public decimal UsdcWalletValue { get; set; } + public decimal UsdcInPositionsValue { get; set; } public decimal BotsAllocationUsdValue { get; set; } public decimal PnL { get; set; } public DateTime Time { get; set; } diff --git a/src/Managing.Domain/Statistics/AgentBalanceHistory.cs b/src/Managing.Domain/Statistics/AgentBalanceHistory.cs index 30f2b0ba..a7dab7a8 100644 --- a/src/Managing.Domain/Statistics/AgentBalanceHistory.cs +++ b/src/Managing.Domain/Statistics/AgentBalanceHistory.cs @@ -2,6 +2,7 @@ namespace Managing.Domain.Statistics; public class AgentBalanceHistory { + public int UserId { get; set; } public string AgentName { get; set; } public List AgentBalances { get; set; } diff --git a/src/Managing.Infrastructure.Database/InfluxDb/AgentBalanceRepository.cs b/src/Managing.Infrastructure.Database/InfluxDb/AgentBalanceRepository.cs index cb5e4d3e..0e3ecca4 100644 --- a/src/Managing.Infrastructure.Database/InfluxDb/AgentBalanceRepository.cs +++ b/src/Managing.Infrastructure.Database/InfluxDb/AgentBalanceRepository.cs @@ -25,8 +25,10 @@ public class AgentBalanceRepository : IAgentBalanceRepository { var balanceDto = new AgentBalanceDto { - AgentName = balance.AgentName, + UserId = balance.UserId, TotalBalanceValue = balance.TotalBalanceValue, + UsdcWalletValue = balance.UsdcWalletValue, + UsdcInPositionsValue = balance.UsdcInPositionsValue, BotsAllocationUsdValue = balance.BotsAllocationUsdValue, PnL = balance.PnL, Time = balance.Time @@ -40,7 +42,7 @@ public class AgentBalanceRepository : IAgentBalanceRepository }); } - public async Task> GetAgentBalances(string agentName, DateTime start, DateTime? end = null) + public async Task> GetAgentBalancesByUserId(int userId, DateTime start, DateTime? end = null) { var results = await _influxDbRepository.QueryAsync(async query => { @@ -48,15 +50,17 @@ public class AgentBalanceRepository : IAgentBalanceRepository $"|> range(start: {start:s}Z" + (end.HasValue ? $", stop: {end.Value:s}Z" : "") + $") " + - $"|> filter(fn: (r) => r[\"agent_name\"] == \"{agentName}\") " + + $"|> filter(fn: (r) => r[\"user_id\"] == \"{userId}\") " + $"|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")"; var result = await query.QueryAsync(flux, _influxDbRepository.Organization); return result.Select(balance => new AgentBalance { - AgentName = balance.AgentName, + UserId = balance.UserId, TotalBalanceValue = balance.TotalBalanceValue, + UsdcWalletValue = balance.UsdcWalletValue, + UsdcInPositionsValue = balance.UsdcInPositionsValue, BotsAllocationUsdValue = balance.BotsAllocationUsdValue, PnL = balance.PnL, Time = balance.Time @@ -81,20 +85,31 @@ public class AgentBalanceRepository : IAgentBalanceRepository var balances = await query.QueryAsync(flux, _influxDbRepository.Organization); - // Group balances by agent name + // Group balances by user ID var agentGroups = balances - .GroupBy(b => b.AgentName) - .Select(g => new AgentBalanceHistory + .GroupBy(b => b.UserId) + .Select(g => { - AgentName = g.Key, - AgentBalances = g.Select(b => new AgentBalance + var userBalances = g.Select(b => new AgentBalance { - AgentName = b.AgentName, + UserId = b.UserId, TotalBalanceValue = b.TotalBalanceValue, + UsdcWalletValue = b.UsdcWalletValue, + UsdcInPositionsValue = b.UsdcInPositionsValue, BotsAllocationUsdValue = b.BotsAllocationUsdValue, PnL = b.PnL, Time = b.Time - }).OrderBy(b => b.Time).ToList() + }).OrderBy(b => b.Time).ToList(); + + // Use a default agent name since we don't store it in AgentBalance anymore + var mostRecentAgentName = $"User_{g.Key}"; + + return new AgentBalanceHistory + { + UserId = g.Key, + AgentName = mostRecentAgentName, + AgentBalances = userBalances + }; }).ToList(); return (agentGroups, agentGroups.Count); diff --git a/src/Managing.Infrastructure.Database/InfluxDb/Models/AgentBalanceDto.cs b/src/Managing.Infrastructure.Database/InfluxDb/Models/AgentBalanceDto.cs index 2497a37a..ddc294b1 100644 --- a/src/Managing.Infrastructure.Database/InfluxDb/Models/AgentBalanceDto.cs +++ b/src/Managing.Infrastructure.Database/InfluxDb/Models/AgentBalanceDto.cs @@ -5,10 +5,14 @@ namespace Managing.Infrastructure.Databases.InfluxDb.Models; [Measurement("agent_balance")] public class AgentBalanceDto { - [Column("agent_name", IsTag = true)] public string AgentName { get; set; } + [Column("user_id", IsTag = true)] public int UserId { get; set; } [Column("total_balance_value")] public decimal TotalBalanceValue { get; set; } + [Column("usdc_wallet_value")] public decimal UsdcWalletValue { get; set; } + + [Column("usdc_in_positions_value")] public decimal UsdcInPositionsValue { get; set; } + [Column("bots_allocation_usd_value")] public decimal BotsAllocationUsdValue { get; set; } [Column("pnl")] public decimal PnL { get; set; } diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts index c9f18ae4..07b58955 100644 --- a/src/Managing.WebApp/src/generated/ManagingApi.ts +++ b/src/Managing.WebApp/src/generated/ManagingApi.ts @@ -4588,13 +4588,16 @@ export enum SortableFields { } export interface AgentBalanceHistory { + userId?: number; agentName?: string | null; agentBalances?: AgentBalance[] | null; } export interface AgentBalance { - agentName?: string | null; + userId?: number; totalBalanceValue?: number; + usdcWalletValue?: number; + usdcInPositionsValue?: number; botsAllocationUsdValue?: number; pnL?: number; time?: Date; diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts index 311c33e1..70c6a271 100644 --- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts +++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts @@ -1071,13 +1071,16 @@ export enum SortableFields { } export interface AgentBalanceHistory { + userId?: number; agentName?: string | null; agentBalances?: AgentBalance[] | null; } export interface AgentBalance { - agentName?: string | null; + userId?: number; totalBalanceValue?: number; + usdcWalletValue?: number; + usdcInPositionsValue?: number; botsAllocationUsdValue?: number; pnL?: number; time?: Date; diff --git a/src/Managing.WebApp/src/pages/dashboardPage/agentSearch.tsx b/src/Managing.WebApp/src/pages/dashboardPage/agentSearch.tsx index f623b969..860f6926 100644 --- a/src/Managing.WebApp/src/pages/dashboardPage/agentSearch.tsx +++ b/src/Managing.WebApp/src/pages/dashboardPage/agentSearch.tsx @@ -80,6 +80,8 @@ function AgentSearch({ index }: { index: number }) { const latestBalance = balances[balances.length - 1] return { totalBalanceValue: latestBalance.totalBalanceValue || 0, + usdcWalletValue: latestBalance.usdcWalletValue || 0, + usdcInPositionsValue: latestBalance.usdcInPositionsValue || 0, botsAllocation: latestBalance.botsAllocationUsdValue || 0, } } @@ -139,12 +141,16 @@ function AgentSearch({ index }: { index: number }) { const dates = sortedBalances.map(balance => new Date(balance.time || 0)) const totalBalanceValues = sortedBalances.map(balance => balance.totalBalanceValue || 0) + const usdcWalletValues = sortedBalances.map(balance => balance.usdcWalletValue || 0) + const usdcInPositionsValues = sortedBalances.map(balance => balance.usdcInPositionsValue || 0) const botsAllocationValues = sortedBalances.map(balance => balance.botsAllocationUsdValue || 0) const pnlValues = sortedBalances.map(balance => balance.pnL || 0) return { dates, totalBalanceValues, + usdcWalletValues, + usdcInPositionsValues, botsAllocationValues, pnlValues } @@ -247,11 +253,19 @@ function AgentSearch({ index }: { index: number }) { {currentBalance && (

Current Balance

-
+
Total Balance Value
${currentBalance.totalBalanceValue.toLocaleString(undefined, { maximumFractionDigits: 2 })}
+
+
USDC in Wallet
+
${currentBalance.usdcWalletValue.toLocaleString(undefined, { maximumFractionDigits: 2 })}
+
+
+
USDC in Positions
+
${currentBalance.usdcInPositionsValue.toLocaleString(undefined, { maximumFractionDigits: 2 })}
+
Bots Allocation
${currentBalance.botsAllocation.toLocaleString(undefined, { maximumFractionDigits: 2 })}
@@ -275,14 +289,32 @@ function AgentSearch({ index }: { index: number }) { line: { color: theme.primary, width: 3 }, marker: { size: 6 } }, + { + x: chartData.dates, + y: chartData.usdcWalletValues, + type: 'scatter' as const, + mode: 'lines+markers' as const, + name: 'USDC in Wallet', + line: { color: '#3b82f6', width: 2 }, + marker: { size: 5 } + }, + { + x: chartData.dates, + y: chartData.usdcInPositionsValues, + type: 'scatter' as const, + mode: 'lines+markers' as const, + name: 'USDC in Positions', + line: { color: '#10b981', width: 2 }, + marker: { size: 5 } + }, { x: chartData.dates, y: chartData.botsAllocationValues, type: 'scatter' as const, mode: 'lines+markers' as const, name: 'Bots Allocation', - line: { color: theme.success, width: 3 }, - marker: { size: 6 } + line: { color: theme.success, width: 2 }, + marker: { size: 5 } }, { x: chartData.dates, @@ -290,8 +322,8 @@ function AgentSearch({ index }: { index: number }) { type: 'scatter' as const, mode: 'lines+markers' as const, name: 'PnL', - line: { color: theme.warning, width: 3 }, - marker: { size: 6 } + line: { color: theme.warning, width: 2 }, + marker: { size: 5 } } ]} layout={{