diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index a08977c6..22698316 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -186,7 +186,7 @@ public class TradingBotBase : ITradingBot Logger.LogInformation( "Bot Status {Identifier} - ServerDate: {ServerDate}, LastCandleDate: {LastCandleDate}, Signals: {SignalCount}, Executions: {ExecutionCount}, Positions: {PositionCount}", - Identifier, DateTime.UtcNow, LastCandle.Date, Signals.Count, ExecutionCount, Positions.Count); + Identifier, DateTime.UtcNow, LastCandle?.Date, Signals.Count, ExecutionCount, Positions.Count); } } @@ -196,7 +196,7 @@ public class TradingBotBase : ITradingBot if (!Config.FlipPosition && Positions.Any(p => !p.Value.IsFinished())) return; // Check if we're in cooldown period for any direction - if (IsInCooldownPeriod()) + if (await IsInCooldownPeriodAsync()) { // Still in cooldown period, skip signal generation return; @@ -312,7 +312,7 @@ public class TradingBotBase : ITradingBot foreach (var signal in signalsWaitingForPosition) { - if (signal.Date < LastCandle.Date) + if (LastCandle != null && signal.Date < LastCandle.Date) { await LogWarning( $"❌ **Signal Expired**\nSignal `{signal.Identifier}` is older than last candle `{LastCandle.Date}`\nStatus: `Expired`"); @@ -934,7 +934,7 @@ public class TradingBotBase : ITradingBot // Check if we're in backtest mode if (Config.IsForBacktest) { - return !IsInCooldownPeriod() && await CheckLossStreak(signal); + return !await IsInCooldownPeriodAsync() && await CheckLossStreak(signal); } // Check broker positions for live trading @@ -970,7 +970,7 @@ public class TradingBotBase : ITradingBot } // Check cooldown period and loss streak - return !IsInCooldownPeriod() && await CheckLossStreak(signal); + return !await IsInCooldownPeriodAsync() && await CheckLossStreak(signal); } private async Task CheckLossStreak(LightSignal signal) @@ -2006,13 +2006,24 @@ public class TradingBotBase : ITradingBot /// Checks if the bot is currently in a cooldown period for any direction. /// /// True if in cooldown period for any direction, false otherwise - private bool IsInCooldownPeriod() + private async Task IsInCooldownPeriodAsync() { if (LastPositionClosingTime == null) { return false; // No previous position closing time, no cooldown } + // Force refresh last candle if it's null + if (LastCandle == null) + { + await ForceRefreshLastCandleAsync(); + if (LastCandle == null) + { + Logger.LogWarning("Unable to refresh last candle, skipping cooldown check"); + return false; // No last candle available, no cooldown check possible + } + } + // Calculate cooldown end time based on last position closing time var baseIntervalSeconds = CandleHelpers.GetBaseIntervalInSeconds(Config.Timeframe); var cooldownEndTime = LastPositionClosingTime.Value.AddSeconds(baseIntervalSeconds * Config.CooldownPeriod); @@ -2034,6 +2045,38 @@ public class TradingBotBase : ITradingBot return isInCooldown; } + /// + /// Forces a refresh of the last candle by calling the CandleStoreGrain + /// + private async Task ForceRefreshLastCandleAsync() + { + try + { + await ServiceScopeHelpers.WithScopedService(_scopeFactory, async grainFactory => + { + var grainKey = CandleHelpers.GetCandleStoreGrainKey(Account.Exchange, Config.Ticker, Config.Timeframe); + var grain = grainFactory.GetGrain(grainKey); + + var lastCandles = await grain.GetLastCandle(1); + LastCandle = lastCandles.FirstOrDefault(); + + if (LastCandle != null) + { + Logger.LogDebug("Successfully refreshed last candle for {Ticker} at {Date}", + Config.Ticker, LastCandle.Date); + } + else + { + Logger.LogWarning("No candles available from CandleStoreGrain for {Ticker}", Config.Ticker); + } + }); + } + catch (Exception ex) + { + Logger.LogError(ex, "Error refreshing last candle for {Ticker}", Config.Ticker); + } + } + /// /// Gets the trade that was used to close the position ///