Refactor LiveTradingBotGrain to close all open positions before stopping the bot. Introduced CloseAllOpenPositionsAsync method to handle position closure and logging, ensuring a smoother stop process. Removed the previous check for open positions in the database.

This commit is contained in:
2025-11-16 18:22:48 +07:00
parent 1e15d5f23b
commit ec88b124e6

View File

@@ -316,15 +316,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
// Only check for open positions if this is not part of a restart operation
if (!isRestarting)
{
var hasOpenPositions = await HasOpenPositionsInDatabaseAsync();
if (hasOpenPositions)
{
_logger.LogWarning(
"Stopping bot {Name} while it still has open positions in database. Trading loop will stop but positions remain managed by system.",
_tradingBot?.Config.Name);
throw new InvalidOperationException(
"Cannot stop bot while it has open positions. Please close all positions first.");
}
await CloseAllOpenPositionsAsync();
}
// The check is now against the registry status
@@ -1079,6 +1071,55 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
}
}
/// <summary>
/// Closes all open positions for this bot before stopping
/// </summary>
private async Task CloseAllOpenPositionsAsync()
{
try
{
var botId = this.GetPrimaryKey();
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.LogInformation(
"Bot {GrainId} has {Count} open positions that will be closed before stopping: {Positions}",
botId, openPositions.Count, string.Join(", ", openPositions.Select(p => p.Identifier)));
foreach (var position in openPositions)
{
try
{
_logger.LogInformation("Closing position {PositionId} for bot {GrainId}", position.Identifier, botId);
await ClosePositionAsync(position.Identifier);
_logger.LogInformation("Successfully closed position {PositionId} for bot {GrainId}", position.Identifier, botId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to close position {PositionId} for bot {GrainId}", position.Identifier, botId);
// Continue with other positions even if one fails
}
}
_logger.LogInformation("Finished closing all open positions for bot {GrainId}", botId);
}
else
{
_logger.LogDebug("Bot {GrainId} has no open positions to close", botId);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error closing open positions for bot {GrainId}", this.GetPrimaryKey());
// Don't throw here - we want to continue with the stop process even if position closing fails
}
}
/// <summary>
/// Checks for open positions in database by bot identifier (initiator identifier).
/// This is the source of truth for preventing bot stop when there are unfinished positions.