Refresh AgentBalance on API Call

This commit is contained in:
2025-10-11 11:41:54 +07:00
parent e1983974fd
commit 3b3e383781

View File

@@ -1,9 +1,16 @@
using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Application.Abstractions;
using Managing.Core; using Managing.Core;
using Managing.Domain.Statistics; using Managing.Domain.Statistics;
using Managing.Domain.Trades;
using Managing.Domain.Users;
using Managing.Domain.Accounts;
using Managing.Domain.Bots;
using Managing.Domain.Shared.Helpers;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
namespace Managing.Application.Agents; namespace Managing.Application.Agents;
@@ -76,6 +83,26 @@ public class AgentService : IAgentService
var balances = await _agentBalanceRepository.GetAgentBalancesByUserId(userId, start, end); var balances = await _agentBalanceRepository.GetAgentBalancesByUserId(userId, start, end);
// Check if we need to calculate fresh balance data
var needsFreshData = await ShouldCalculateFreshBalanceData(userId, balances, effectiveEnd);
if (needsFreshData)
{
_logger.LogInformation("Calculating fresh balance data for user {UserId} - last balance is missing or older than 2 minutes", userId);
var freshBalance = await CalculateFreshBalanceData(userId);
if (freshBalance != null)
{
// Insert the fresh balance data into InfluxDB
_agentBalanceRepository.InsertAgentBalance(freshBalance);
// Add the fresh balance to our results if it falls within the requested time range
if (freshBalance.Time >= start && freshBalance.Time <= effectiveEnd)
{
balances.Add(freshBalance);
}
}
}
// Create a single AgentBalanceHistory with all balances // Create a single AgentBalanceHistory with all balances
var result = new AgentBalanceHistory var result = new AgentBalanceHistory
{ {
@@ -158,4 +185,152 @@ public class AgentService : IAgentService
throw; throw;
} }
} }
/// <summary>
/// Determines if fresh balance data should be calculated based on the last balance timestamp
/// </summary>
private Task<bool> ShouldCalculateFreshBalanceData(int userId, IList<AgentBalance> existingBalances, DateTime effectiveEnd)
{
try
{
// If no balances exist, we need fresh data
if (!existingBalances.Any())
{
_logger.LogDebug("No existing balances found for user {UserId}, calculating fresh data", userId);
return Task.FromResult(true);
}
// Get the most recent balance
var lastBalance = existingBalances.OrderByDescending(b => b.Time).First();
var timeSinceLastBalance = effectiveEnd - lastBalance.Time;
// If the last balance is older than 2 minutes, calculate fresh data
if (timeSinceLastBalance > TimeSpan.FromMinutes(2))
{
_logger.LogDebug("Last balance for user {UserId} is {TimeAgo} old, calculating fresh data",
userId, timeSinceLastBalance);
return Task.FromResult(true);
}
return Task.FromResult(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking if fresh balance data is needed for user {UserId}", userId);
// Default to calculating fresh data on error to ensure we have current information
return Task.FromResult(true);
}
}
/// <summary>
/// Calculates fresh balance data for a user by aggregating from positions and account balances
/// </summary>
private async Task<AgentBalance?> CalculateFreshBalanceData(int userId)
{
try
{
// Get all positions for this user's bots as initiator
var positions = await ServiceScopeHelpers.WithScopedService<ITradingService, List<Position>>(
_serviceScopeFactory,
async tradingService =>
{
var userPositions = await tradingService.GetPositionByUserIdAsync(userId);
return userPositions.Where(p => p.IsValidForMetrics()).ToList();
});
// Calculate PnL from positions
var totalPnL = positions.Sum(p => p.ProfitAndLoss?.Realized ?? 0);
var totalFees = positions.Sum(p => p.CalculateTotalFees());
var netPnL = totalPnL - totalFees;
// Calculate USDC wallet value and USDC in positions
decimal usdcWalletValue = 0;
decimal usdcInPositionsValue = 0;
decimal totalBalance = 0;
try
{
// Get user and accounts
var user = await ServiceScopeHelpers.WithScopedService<IUserService, User>(
_serviceScopeFactory,
async userService => await userService.GetUserByIdAsync(userId));
if (user == null)
{
_logger.LogError("User {UserId} not found for balance calculation", userId);
return null;
}
var userAccounts = await ServiceScopeHelpers.WithScopedService<IAccountService, IEnumerable<Account>>(
_serviceScopeFactory,
async accountService => await accountService.GetAccountsByUserAsync(user, hideSecrets: true, true));
// Calculate USDC wallet value from all accounts
foreach (var account in userAccounts)
{
var balances = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Balance>>(
_serviceScopeFactory,
async exchangeService => await exchangeService.GetBalances(account));
var usdcBalance = balances.FirstOrDefault(b => b.TokenName?.ToUpper() == "USDC");
usdcWalletValue += usdcBalance?.Amount ?? 0;
}
// Calculate USDC in open positions
foreach (var position in positions.Where(p => p.IsOpen()))
{
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 wallet balances for user {UserId}", userId);
// Continue with zero values if balance calculation fails
}
// Calculate bots allocation USD value
var activeStrategies = await ServiceScopeHelpers.WithScopedService<IBotService, List<Bot>>(
_serviceScopeFactory,
async botService =>
{
var userBots = await botService.GetBotsByUser(userId);
return userBots.Where(b => b.Status == BotStatus.Running).ToList();
});
var botsAllocationUsdValue = 0m;
if (activeStrategies.Any())
{
var botIds = activeStrategies.Select(b => b.Identifier);
var botConfigs = await ServiceScopeHelpers.WithScopedService<IBotService, IEnumerable<TradingBotConfig>>(
_serviceScopeFactory,
async botService => await botService.GetBotConfigsByIdsAsync(botIds));
botsAllocationUsdValue = botConfigs.Sum(config => config.BotTradingBalance);
}
var freshBalance = new AgentBalance
{
UserId = userId,
TotalBalanceValue = totalBalance,
UsdcWalletValue = usdcWalletValue,
UsdcInPositionsValue = usdcInPositionsValue,
BotsAllocationUsdValue = botsAllocationUsdValue,
PnL = netPnL,
Time = DateTime.UtcNow
};
_logger.LogDebug("Calculated fresh balance data for user {UserId}: TotalBalance={TotalBalance}, UsdcWallet={UsdcWallet}, UsdcInPositions={UsdcInPositions}, BotsAllocation={BotsAllocation}, PnL={PnL}",
userId, totalBalance, usdcWalletValue, usdcInPositionsValue, botsAllocationUsdValue, netPnL);
return freshBalance;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error calculating fresh balance data for user {UserId}", userId);
return null;
}
}
} }