Refresh AgentBalance on API Call
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user