Refresh AgentBalance on API Call
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Core;
|
||||
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.Logging;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Agents;
|
||||
|
||||
@@ -76,6 +83,26 @@ public class AgentService : IAgentService
|
||||
|
||||
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
|
||||
var result = new AgentBalanceHistory
|
||||
{
|
||||
@@ -158,4 +185,152 @@ public class AgentService : IAgentService
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user