Fix agent grain calculation

This commit is contained in:
2025-09-28 11:47:47 +07:00
parent d432549d26
commit 6e07bac6ae
4 changed files with 36 additions and 132 deletions

View File

@@ -5,9 +5,12 @@ using Managing.Application.Abstractions.Services;
using Managing.Application.Bots.Models;
using Managing.Application.Orleans;
using Managing.Common;
using Managing.Core;
using Managing.Core.Exceptions;
using Managing.Domain.Statistics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Orleans.Concurrency;
using static Managing.Common.Enums;
namespace Managing.Application.Bots.Grains;
@@ -27,6 +30,7 @@ public class AgentGrain : Grain, IAgentGrain
private readonly IUserService _userService;
private readonly IAccountService _accountService;
private readonly ITradingService _tradingService;
private readonly IServiceScopeFactory _scopeFactory;
public AgentGrain(
[PersistentState("agent-state", "agent-store")]
@@ -37,7 +41,8 @@ public class AgentGrain : Grain, IAgentGrain
IExchangeService exchangeService,
IUserService userService,
IAccountService accountService,
ITradingService tradingService)
ITradingService tradingService,
IServiceScopeFactory scopeFactory)
{
_state = state;
_logger = logger;
@@ -47,6 +52,7 @@ public class AgentGrain : Grain, IAgentGrain
_userService = userService;
_accountService = accountService;
_tradingService = tradingService;
_scopeFactory = scopeFactory;
}
public override Task OnActivateAsync(CancellationToken cancellationToken)
@@ -98,14 +104,6 @@ public class AgentGrain : Grain, IAgentGrain
_logger.LogInformation("Position opened event received for user {UserId}, position: {PositionId}",
this.GetPrimaryKeyLong(), evt.PositionIdentifier);
// Update event-driven metrics
_state.State.TotalVolume += evt.Volume;
_state.State.TotalFees += evt.Fee;
_state.State.LastSummaryUpdate = DateTime.UtcNow;
await _state.WriteStateAsync();
// Update the agent summary with the new data
await UpdateSummary();
}
catch (Exception ex)
@@ -122,25 +120,6 @@ public class AgentGrain : Grain, IAgentGrain
_logger.LogInformation("Position closed event received for user {UserId}, position: {PositionId}, PnL: {PnL}",
this.GetPrimaryKeyLong(), evt.PositionIdentifier, evt.RealizedPnL);
// Update event-driven metrics
_state.State.TotalPnL += evt.RealizedPnL;
_state.State.TotalVolume += evt.Volume;
// Update wins/losses count
if (evt.RealizedPnL > 0)
{
_state.State.Wins++;
}
else if (evt.RealizedPnL < 0)
{
_state.State.Losses++;
}
_state.State.LastSummaryUpdate = DateTime.UtcNow;
await _state.WriteStateAsync();
// Update the agent summary with the new data
await UpdateSummary();
}
catch (Exception ex)
@@ -157,13 +136,6 @@ public class AgentGrain : Grain, IAgentGrain
_logger.LogInformation("Position updated event received for user {UserId}, position: {PositionId}",
this.GetPrimaryKeyLong(), evt.PositionIdentifier);
// Update event-driven metrics for PnL changes
// Note: This is for real-time PnL updates, not cumulative like closed positions
_state.State.LastSummaryUpdate = DateTime.UtcNow;
await _state.WriteStateAsync();
// Update the agent summary with the new data
await UpdateSummary();
}
catch (Exception ex)
@@ -176,6 +148,7 @@ public class AgentGrain : Grain, IAgentGrain
/// <summary>
/// Updates the agent summary by recalculating from position data (used for initialization or manual refresh)
/// </summary>
[OneWay]
private async Task UpdateSummary()
{
try
@@ -185,35 +158,20 @@ public class AgentGrain : Grain, IAgentGrain
// Calculate aggregated statistics from position data
var totalPnL = positions.Sum(p => p.ProfitAndLoss?.Realized ?? 0);
var totalVolume = positions.Sum(p => p.Open.Price * p.Open.Quantity);
var totalVolume = positions.Sum(p => p.Open.Price * p.Open.Quantity * p.Open.Leverage);
var totalVolumeWithoutLeverage = positions.Sum(p => p.Open.Price * p.Open.Quantity);
var totalFees = positions.Sum(p => p.CalculateTotalFees());
// Calculate wins/losses from position PnL
var totalWins = positions.Count(p => (p.ProfitAndLoss?.Realized ?? 0) > 0);
var totalLosses = positions.Count(p => (p.ProfitAndLoss?.Realized ?? 0) < 0);
var totalLosses = positions.Count(p => (p.ProfitAndLoss?.Realized ?? 0) <= 0);
decimal totalROI;
if (totalVolume > 0)
var totalROI = totalVolume switch
{
totalROI = (totalPnL / totalVolume) * 100;
}
else if (totalVolume == 0 && totalPnL == 0)
{
// No trading activity yet
totalROI = 0;
}
else if (totalVolume == 0 && totalPnL != 0)
{
// Edge case: PnL exists but no volume (shouldn't happen in normal cases)
_logger.LogWarning("Agent {UserId} has PnL {PnL} but zero volume", this.GetPrimaryKeyLong(), totalPnL);
totalROI = 0;
}
else
{
// Fallback for any other edge cases
totalROI = 0;
}
> 0 => (totalPnL / totalVolumeWithoutLeverage) * 100,
>= 0 => 0,
_ => 0
};
// Calculate Runtime based on the earliest position date
DateTime? runtime = null;
@@ -236,9 +194,8 @@ public class AgentGrain : Grain, IAgentGrain
foreach (var account in userAccounts)
{
// Get USDC balance
var usdcBalances = await _exchangeService.GetBalances(account);
var usdcBalance = usdcBalances.FirstOrDefault(b => b.TokenName?.ToUpper() == "USDC")?.Amount ??
0;
var usdcBalances = await GetOrRefreshBalanceDataAsync(account.Name);
var usdcBalance = usdcBalances?.UsdcValue ?? 0;
totalBalance += usdcBalance;
}
@@ -260,7 +217,11 @@ public class AgentGrain : Grain, IAgentGrain
}
// Get active strategies count from bot data (this is still needed for running bots)
var bots = await _botService.GetBotsByIdsAsync(_state.State.BotIds);
var bots = await ServiceScopeHelpers.WithScopedService<IGrainFactory, List<BotRegistryEntry>> (_scopeFactory, async (grainFactory) => {
var registry = grainFactory.GetGrain<ILiveBotRegistryGrain>(0);
return await registry.GetBotsForUser((int)this.GetPrimaryKeyLong());
});
var activeStrategiesCount = bots.Count(b => b.Status == BotStatus.Running);
var summary = new AgentSummary
@@ -293,14 +254,8 @@ public class AgentGrain : Grain, IAgentGrain
{
if (_state.State.BotIds.Add(botId))
{
// Update active strategies count
_state.State.ActiveStrategiesCount = _state.State.BotIds.Count;
_state.State.LastSummaryUpdate = DateTime.UtcNow;
await _state.WriteStateAsync();
_logger.LogInformation("Bot {BotId} registered to Agent {UserId}", botId, this.GetPrimaryKeyLong());
// Update summary after registering bot
await UpdateSummary();
}
}
@@ -309,14 +264,8 @@ public class AgentGrain : Grain, IAgentGrain
{
if (_state.State.BotIds.Remove(botId))
{
// Update active strategies count
_state.State.ActiveStrategiesCount = _state.State.BotIds.Count;
_state.State.LastSummaryUpdate = DateTime.UtcNow;
await _state.WriteStateAsync();
_logger.LogInformation("Bot {BotId} unregistered from Agent {UserId}", botId, this.GetPrimaryKeyLong());
// Update summary after unregistering bot
await UpdateSummary();
}
}