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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user