Refactor BotController and BotService for improved bot management

- Cleaned up constructor parameters in BotController for better readability.
- Enhanced StartCopyTradingCommand handling with improved formatting.
- Updated bot deletion logic in BotService to delete associated positions and trigger agent summary updates.
- Added new method in TradingService for deleting positions by initiator identifier.
- Implemented error handling in StopBotCommandHandler to ensure agent summary updates do not disrupt bot stop operations.
This commit is contained in:
2025-11-23 15:30:11 +07:00
parent 9c8ab71736
commit 411fc41bef
8 changed files with 558 additions and 13 deletions

View File

@@ -74,6 +74,11 @@ namespace Managing.Application.ManageBot
var account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(
_scopeFactory,
async accountService => await accountService.GetAccount(config.AccountName, true, false));
// Delete all positions for this bot from the database
await _tradingService.DeletePositionsByInitiatorIdentifierAsync(identifier);
_tradingBotLogger.LogInformation("Deleted all positions for bot {BotId}", identifier);
await grain.StopAsync("Deleting bot");
await _botRepository.DeleteBot(identifier);
await grain.DeleteAsync();
@@ -85,12 +90,17 @@ namespace Managing.Application.ManageBot
$"⚠️ Bot has been permanently deleted and all data removed";
await _messengerService.SendTradeMessage(deleteMessage, false, account.User);
// Trigger agent summary update after bot deletion
var agentGrain = _grainFactory.GetGrain<IAgentGrain>(account.User.Id);
await agentGrain.ForceUpdateSummary();
return true;
}
catch (Exception e)
{
_tradingBotLogger.LogError(e, "Error deleting bot {Identifier}", identifier);
return false;
throw;
}
}
@@ -111,13 +121,13 @@ namespace Managing.Application.ManageBot
// Check balances for EVM/GMX V2 bots before starting/restarting
var botConfig = await botGrain.GetConfiguration();
Account account;
if (string.IsNullOrEmpty(botConfig.AccountName))
{
// Fallback: Get the first account for the user
var user = await botGrain.GetUserAsync();
account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(
_scopeFactory,
async accountService =>
@@ -128,11 +138,13 @@ namespace Managing.Application.ManageBot
{
throw new InvalidOperationException($"User '{user.Name}' has no accounts configured.");
}
return firstAccount;
});
botConfig.AccountName = account.Name;
_tradingBotLogger.LogInformation("Bot '{BotName}' (ID: {BotId}) using fallback account '{AccountName}' for user '{UserName}'",
_tradingBotLogger.LogInformation(
"Bot '{BotName}' (ID: {BotId}) using fallback account '{AccountName}' for user '{UserName}'",
botConfig.Name, identifier, account.Name, user.Name);
}
else
@@ -421,7 +433,7 @@ namespace Managing.Application.ManageBot
}
var usdcValue = usdcBalance?.Amount ?? 0m;
// Get positions for the user and add their USDC value (similar to AgentGrain.UpdateSummary)
try
{
@@ -437,7 +449,9 @@ namespace Managing.Application.ManageBot
}
catch (Exception ex)
{
_tradingBotLogger.LogError(ex, "Error calculating position values for available allocation for user {UserId}", account.User.Id);
_tradingBotLogger.LogError(ex,
"Error calculating position values for available allocation for user {UserId}",
account.User.Id);
// Continue with calculation even if position retrieval fails
}

View File

@@ -1,6 +1,11 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.ManageBot.Commands;
using Managing.Core;
using Managing.Domain.Accounts;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using static Managing.Common.Enums;
namespace Managing.Application.ManageBot
@@ -8,15 +13,46 @@ namespace Managing.Application.ManageBot
public class StopBotCommandHandler : IRequestHandler<StopBotCommand, BotStatus>
{
private readonly IBotService _botService;
private readonly IServiceScopeFactory _scopeFactory;
private readonly IGrainFactory _grainFactory;
public StopBotCommandHandler(IBotService botService)
public StopBotCommandHandler(IBotService botService, IServiceScopeFactory scopeFactory, IGrainFactory grainFactory)
{
_botService = botService;
_scopeFactory = scopeFactory;
_grainFactory = grainFactory;
}
public async Task<BotStatus> Handle(StopBotCommand request, CancellationToken cancellationToken)
{
return await _botService.StopBot(request.Identifier);
var result = await _botService.StopBot(request.Identifier);
try
{
// Get bot configuration to find the user for agent summary update
var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(request.Identifier);
var config = await grain.GetConfiguration();
// Get account to find user ID for agent grain
var account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(
_scopeFactory,
async accountService => await accountService.GetAccount(config.AccountName, true, false));
if (account?.User != null)
{
// Trigger agent summary update after stopping the bot
var agentGrain = _grainFactory.GetGrain<IAgentGrain>(account.User.Id);
await agentGrain.ForceUpdateSummary();
}
}
catch (Exception ex)
{
// Log the error but don't fail the stop operation
// The bot was successfully stopped, we just couldn't update the summary
Console.WriteLine($"Failed to update agent summary after stopping bot {request.Identifier}: {ex.Message}");
}
return result;
}
}
}

View File

@@ -256,6 +256,11 @@ public class TradingService : ITradingService
return await _tradingRepository.GetPositionsByInitiatorIdentifiersAsync(initiatorIdentifiers);
}
public async Task DeletePositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier)
{
await _tradingRepository.DeletePositionsByInitiatorIdentifierAsync(initiatorIdentifier);
}
public async Task<decimal> GetGlobalPnLFromPositionsAsync()
{
return await _tradingRepository.GetGlobalPnLFromPositionsAsync();