Prevent bot from stopping if position is open

This commit is contained in:
2025-10-04 17:43:43 +07:00
parent a97b5804a0
commit 15eba0fc3c

View File

@@ -248,6 +248,15 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
public async Task StopAsync() public async Task StopAsync()
{ {
// Check if bot has open positions in database before allowing stop
var hasOpenPositions = await HasOpenPositionsInDatabaseAsync();
if (hasOpenPositions)
{
_logger.LogWarning("Cannot stop LiveTradingBotGrain {GrainId} - bot has open positions in database",
this.GetPrimaryKey());
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
var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0); var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
var botStatus = await botRegistry.GetBotStatus(this.GetPrimaryKey()); var botStatus = await botRegistry.GetBotStatus(this.GetPrimaryKey());
@@ -569,6 +578,15 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
{ {
_logger.LogInformation("Restarting LiveTradingBotGrain {GrainId}", this.GetPrimaryKey()); _logger.LogInformation("Restarting LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
// Check if bot has open positions in database before allowing restart
var hasOpenPositions = await HasOpenPositionsInDatabaseAsync();
if (hasOpenPositions)
{
_logger.LogWarning("Cannot restart LiveTradingBotGrain {GrainId} - bot has open positions in database",
this.GetPrimaryKey());
throw new InvalidOperationException("Cannot restart bot while it has open positions. Please close all positions first.");
}
try try
{ {
await StopAsync(); await StopAsync();
@@ -594,6 +612,15 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
public async Task DeleteAsync() public async Task DeleteAsync()
{ {
// Check if bot has open positions in database before allowing deletion
var hasOpenPositions = await HasOpenPositionsInDatabaseAsync();
if (hasOpenPositions)
{
_logger.LogWarning("Cannot delete LiveTradingBotGrain {GrainId} - bot has open positions in database",
this.GetPrimaryKey());
throw new InvalidOperationException("Cannot delete bot while it has open positions. Please close all positions first.");
}
try try
{ {
// Stop the bot first if it's running // Stop the bot first if it's running
@@ -616,6 +643,11 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
_logger.LogInformation("LiveTradingBotGrain {GrainId} deleted successfully", this.GetPrimaryKey()); _logger.LogInformation("LiveTradingBotGrain {GrainId} deleted successfully", this.GetPrimaryKey());
} }
catch (InvalidOperationException)
{
// Re-throw InvalidOperationException from StopAsync (open positions check)
throw;
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Failed to delete LiveTradingBotGrain {GrainId}", this.GetPrimaryKey()); _logger.LogError(ex, "Failed to delete LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
@@ -865,4 +897,38 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
return Task.FromResult(false); // Default to false on error to avoid blocking autoswap return Task.FromResult(false); // Default to false on error to avoid blocking autoswap
} }
} }
/// <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.
/// </summary>
private async Task<bool> HasOpenPositionsInDatabaseAsync()
{
try
{
var botId = this.GetPrimaryKey();
var positions = await ServiceScopeHelpers.WithScopedService<ITradingService, IEnumerable<Position>>(
_scopeFactory,
async tradingService => await tradingService.GetPositionsByInitiatorIdentifierAsync(botId));
var hasOpenPositions = positions?.Any(p => !p.IsFinished()) ?? false;
_logger.LogDebug("Bot {GrainId} has open positions in database: {HasOpenPositions}",
botId, hasOpenPositions);
if (hasOpenPositions)
{
var openPositions = positions?.Where(p => !p.IsFinished()).ToList() ?? new List<Position>();
_logger.LogWarning("Bot {GrainId} cannot be stopped - has {Count} open positions in database: {Positions}",
botId, openPositions.Count, string.Join(", ", openPositions.Select(p => p.Identifier)));
}
return hasOpenPositions;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking database positions for bot {GrainId}", this.GetPrimaryKey());
// Default to true on error to err on the side of caution - don't stop bot if we can't verify
return true;
}
}
} }