Update closing position on BotStop
This commit is contained in:
@@ -317,44 +317,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
||||
|
||||
private async Task StopAsyncInternal(bool isRestarting, string? reason = null)
|
||||
{
|
||||
// Only check for open positions if this is not part of a restart operation
|
||||
if (!isRestarting)
|
||||
{
|
||||
await CloseAllOpenPositionsAsync();
|
||||
|
||||
// Verify positions are actually closed (even if CloseAllOpenPositionsAsync had timeouts/exceptions)
|
||||
// This ensures we don't report failure if positions were successfully closed despite timeouts
|
||||
try
|
||||
{
|
||||
var botId = this.GetPrimaryKey();
|
||||
var positions = await ServiceScopeHelpers.WithScopedService<ITradingService, IEnumerable<Position>>(
|
||||
_scopeFactory,
|
||||
async tradingService => await tradingService.GetPositionsByInitiatorIdentifierAsync(botId));
|
||||
|
||||
var stillOpenPositions =
|
||||
positions?.Where(p => p.IsOpen() || p.Status.Equals(PositionStatus.New)).ToList() ??
|
||||
new List<Position>();
|
||||
|
||||
if (stillOpenPositions.Any())
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Bot {GrainId} still has {Count} open positions after closure attempt: {Positions}",
|
||||
botId, stillOpenPositions.Count,
|
||||
string.Join(", ", stillOpenPositions.Select(p => p.Identifier)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Bot {GrainId} - all positions verified as closed", botId);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Don't fail the stop operation if we can't verify positions
|
||||
_logger.LogWarning(ex,
|
||||
"Could not verify position closure status for bot {GrainId}, continuing with stop",
|
||||
this.GetPrimaryKey());
|
||||
}
|
||||
}
|
||||
// Note: Position closing is now handled outside the grain in StopBotCommandHandler
|
||||
// to avoid Orleans timeout. This method only handles fast grain operations.
|
||||
|
||||
// The check is now against the registry status
|
||||
var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
|
||||
@@ -607,7 +571,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
||||
if (_state.State.Config.IsForCopyTrading && _state.State.Config.MasterBotIdentifier.HasValue)
|
||||
{
|
||||
_logger.LogInformation("Checking copy trading authorization for bot {GrainId}", this.GetPrimaryKey());
|
||||
|
||||
|
||||
// Check if copy trading validation should be bypassed (for testing)
|
||||
var enableValidation = Environment.GetEnvironmentVariable("ENABLE_COPY_TRADING_VALIDATION")?
|
||||
.Equals("true", StringComparison.OrdinalIgnoreCase) ?? true;
|
||||
|
||||
@@ -4,8 +4,10 @@ using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.ManageBot.Commands;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.ManageBot
|
||||
@@ -15,16 +17,27 @@ namespace Managing.Application.ManageBot
|
||||
private readonly IBotService _botService;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly IGrainFactory _grainFactory;
|
||||
private readonly ILogger<StopBotCommandHandler> _logger;
|
||||
|
||||
public StopBotCommandHandler(IBotService botService, IServiceScopeFactory scopeFactory, IGrainFactory grainFactory)
|
||||
public StopBotCommandHandler(
|
||||
IBotService botService,
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IGrainFactory grainFactory,
|
||||
ILogger<StopBotCommandHandler> logger)
|
||||
{
|
||||
_botService = botService;
|
||||
_scopeFactory = scopeFactory;
|
||||
_grainFactory = grainFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<BotStatus> Handle(StopBotCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Close open positions BEFORE stopping the grain to avoid Orleans timeout
|
||||
// This runs outside the grain context, so it won't cause grain timeouts
|
||||
await CloseAllOpenPositionsAsync(request.Identifier);
|
||||
|
||||
// Now stop the grain (this is fast - just stops timer and updates registry)
|
||||
var result = await _botService.StopBot(request.Identifier);
|
||||
|
||||
try
|
||||
@@ -49,10 +62,70 @@ namespace Managing.Application.ManageBot
|
||||
{
|
||||
// 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}");
|
||||
_logger.LogWarning(ex, "Failed to update agent summary after stopping bot {Identifier}", request.Identifier);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes all open positions for a bot. This runs outside the Orleans grain context
|
||||
/// to avoid grain timeouts when closing positions takes a long time.
|
||||
/// </summary>
|
||||
private async Task CloseAllOpenPositionsAsync(Guid botId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var positions = await ServiceScopeHelpers.WithScopedService<ITradingService, IEnumerable<Position>>(
|
||||
_scopeFactory,
|
||||
async tradingService => await tradingService.GetPositionsByInitiatorIdentifierAsync(botId));
|
||||
|
||||
var openPositions = positions?.Where(p => p.IsOpen() || p.Status.Equals(PositionStatus.New)).ToList() ??
|
||||
new List<Position>();
|
||||
|
||||
if (!openPositions.Any())
|
||||
{
|
||||
_logger.LogDebug("Bot {BotId} has no open positions to close", botId);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"Closing {Count} open positions for bot {BotId}: {Positions}",
|
||||
openPositions.Count, botId, string.Join(", ", openPositions.Select(p => p.Identifier)));
|
||||
|
||||
foreach (var position in openPositions)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Closing position {PositionId} for bot {BotId}", position.Identifier, botId);
|
||||
await ClosePositionAsync(botId, position.Identifier);
|
||||
_logger.LogInformation("Successfully closed position {PositionId} for bot {BotId}",
|
||||
position.Identifier, botId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to close position {PositionId} for bot {BotId}",
|
||||
position.Identifier, botId);
|
||||
// Continue with other positions even if one fails
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Finished closing positions for bot {BotId}", botId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error closing open positions for bot {BotId}", botId);
|
||||
// Don't throw - we want to continue with the stop process even if position closing fails
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a single position using the grain's ClosePositionAsync method
|
||||
/// </summary>
|
||||
private async Task ClosePositionAsync(Guid botId, Guid positionId)
|
||||
{
|
||||
var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(botId);
|
||||
await grain.ClosePositionAsync(positionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user