Prevent bot from stopping if position is open
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user