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
|
// Only check for open positions if this is not part of a restart operation
|
||||||
if (!isRestarting)
|
if (!isRestarting)
|
||||||
{
|
{
|
||||||
var hasOpenPositions = await HasOpenPositionsInDatabaseAsync();
|
await CloseAllOpenPositionsAsync();
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The check is now against the registry status
|
// 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>
|
/// <summary>
|
||||||
/// Checks for open positions in database by bot identifier (initiator identifier).
|
/// 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.
|
/// This is the source of truth for preventing bot stop when there are unfinished positions.
|
||||||
|
|||||||
Reference in New Issue
Block a user