Merge workers into API

This commit is contained in:
2025-08-16 04:55:33 +07:00
parent 9841219e8b
commit d2975be0f5
22 changed files with 340 additions and 346 deletions

View File

@@ -97,11 +97,11 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
if (botStatus == BotStatus.Running && _tradingBot == null)
{
// Now, we can proceed with resuming the bot.
await ResumeBotInternalAsync();
await ResumeBotInternalAsync(botStatus);
}
}
private async Task ResumeBotInternalAsync()
private async Task ResumeBotInternalAsync(BotStatus previousStatus)
{
// Idempotency check
if (_tradingBot != null)
@@ -113,7 +113,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
{
// Create and initialize trading bot instance
_tradingBot = CreateTradingBotInstance(_state.State.Config);
await _tradingBot.Start();
await _tradingBot.Start(previousStatus);
// Set startup time when bot actually starts running
_state.State.StartupTime = DateTime.UtcNow;
@@ -155,7 +155,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
try
{
// Resume the bot - this handles registry status update internally
await ResumeBotInternalAsync();
await ResumeBotInternalAsync(status);
_logger.LogInformation("LiveTradingBotGrain {GrainId} started successfully", this.GetPrimaryKey());
}
catch (Exception ex)

View File

@@ -54,7 +54,7 @@ public class TradingBotBase : ITradingBot
PreloadSince = CandleExtensions.GetBotPreloadSinceFromTimeframe(config.Timeframe);
}
public async Task Start()
public async Task Start(BotStatus previousStatus)
{
if (!Config.IsForBacktest)
{
@@ -77,27 +77,37 @@ public class TradingBotBase : ITradingBot
// await CancelAllOrders();
// Send startup message only for fresh starts (not reboots)
if (!true)
switch (previousStatus)
{
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
$"📊 **Trading Setup:**\n" +
$"🎯 Ticker: `{Config.Ticker}`\n" +
$" Timeframe: `{Config.Timeframe}`\n" +
$"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
$"💰 Balance: `${Config.BotTradingBalance:F2}`\n" +
$"👀 Mode: `{(Config.IsForWatchingOnly ? "Watch Only" : "Live Trading")}`\n\n" +
$"📈 **Active Indicators:** `{string.Join(", ", indicatorNames)}`\n\n" +
$"✅ Ready to monitor signals and execute trades!\n" +
$"📢 I'll notify you when signals are triggered.";
case BotStatus.Saved:
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
$"📊 **Trading Setup:**\n" +
$"🎯 Ticker: `{Config.Ticker}`\n" +
$"⏰ Timeframe: `{Config.Timeframe}`\n" +
$"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
$"💰 Balance: `${Config.BotTradingBalance:F2}`\n" +
$"👀 Mode: `{(Config.IsForWatchingOnly ? "Watch Only" : "Live Trading")}`\n\n" +
$"📈 **Active Indicators:** `{string.Join(", ", indicatorNames)}`\n\n" +
$"✅ Ready to monitor signals and execute trades!\n" +
$"📢 I'll notify you when signals are triggered.";
await LogInformation(startupMessage);
}
else
{
await LogInformation($"🔄 **Bot Restarted**\n" +
$"📊 Resuming operations with {Signals.Count} signals and {Positions.Count} positions\n" +
$"✅ Ready to continue trading");
await LogInformation(startupMessage);
break;
case BotStatus.Running:
return;
case BotStatus.Stopped:
// If status was Stopped we log a message to inform the user that the bot is restarting
await LogInformation($"🔄 **Bot Restarted**\n" +
$"📊 Resuming operations with {Signals.Count} signals and {Positions.Count} positions\n" +
$"✅ Ready to continue trading");
break;
default:
// Handle any other status if needed
break;
}
}
catch (Exception ex)
@@ -160,6 +170,7 @@ public class TradingBotBase : ITradingBot
{
ExecutionCount++;
Logger.LogInformation($"Date server : {DateTime.UtcNow} - Last candle date bot : {LastCandle.Date}");
Logger.LogInformation($"Signals : {Signals.Count}");
Logger.LogInformation($"ExecutionCount : {ExecutionCount}");
Logger.LogInformation($"Positions : {Positions.Count}");
@@ -377,26 +388,30 @@ public class TradingBotBase : ITradingBot
if (position.Status.Equals(PositionStatus.New))
{
await SetPositionStatus(position.SignalIdentifier, PositionStatus.Filled);
// Notify platform summary about the executed trade
try
{
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
{
var platformGrain = grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
var tradeExecutedEvent = new TradeExecutedEvent
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory,
async grainFactory =>
{
TradeId = position.Identifier,
Ticker = position.Ticker,
Volume = position.Open.Price * position.Open.Quantity * position.Open.Leverage
};
await platformGrain.OnTradeExecutedAsync(tradeExecutedEvent);
});
var platformGrain =
grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
var tradeExecutedEvent = new TradeExecutedEvent
{
TradeId = position.Identifier,
Ticker = position.Ticker,
Volume = position.Open.Price * position.Open.Quantity * position.Open.Leverage
};
await platformGrain.OnTradeExecutedAsync(tradeExecutedEvent);
});
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Failed to notify platform summary about trade execution for position {PositionId}", position.Identifier);
Logger.LogWarning(ex,
"Failed to notify platform summary about trade execution for position {PositionId}",
position.Identifier);
}
}
@@ -979,7 +994,8 @@ public class TradingBotBase : ITradingBot
_scopeFactory, async (exchangeService, accountService, tradingService) =>
{
closedPosition =
await new ClosePositionCommandHandler(exchangeService, accountService, tradingService, _scopeFactory)
await new ClosePositionCommandHandler(exchangeService, accountService, tradingService,
_scopeFactory)
.Handle(command);
});
@@ -1024,35 +1040,39 @@ public class TradingBotBase : ITradingBot
if (currentCandle != null)
{
List<Candle> recentCandles = null;
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
{
recentCandles = Config.IsForBacktest
? (LastCandle != null ? new List<Candle>() { LastCandle } : new List<Candle>())
: (await exchangeService.GetCandlesInflux(TradingExchanges.Evm, Config.Ticker,
DateTime.UtcNow.AddHours(-4), Config.Timeframe)).ToList();
});
// Check if we have any candles before proceeding
if (recentCandles == null || !recentCandles.Any())
{
await LogWarning($"No recent candles available for position {position.Identifier}. Using current candle data instead.");
// Fallback to current candle if available
if (currentCandle != null)
List<Candle> recentCandles = null;
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
{
recentCandles = new List<Candle> { currentCandle };
}
else
{
await LogWarning($"No candle data available for position {position.Identifier}. Cannot determine stop loss/take profit hit.");
Logger.LogError("No candle data available for position {PositionId}. Cannot determine stop loss/take profit hit.", position.Identifier);
return;
}
}
recentCandles = Config.IsForBacktest
? (LastCandle != null ? new List<Candle>() { LastCandle } : new List<Candle>())
: (await exchangeService.GetCandlesInflux(TradingExchanges.Evm, Config.Ticker,
DateTime.UtcNow.AddHours(-4), Config.Timeframe)).ToList();
});
var minPriceRecent = recentCandles.Min(c => c.Low);
var maxPriceRecent = recentCandles.Max(c => c.High);
// Check if we have any candles before proceeding
if (recentCandles == null || !recentCandles.Any())
{
await LogWarning(
$"No recent candles available for position {position.Identifier}. Using current candle data instead.");
// Fallback to current candle if available
if (currentCandle != null)
{
recentCandles = new List<Candle> { currentCandle };
}
else
{
await LogWarning(
$"No candle data available for position {position.Identifier}. Cannot determine stop loss/take profit hit.");
Logger.LogError(
"No candle data available for position {PositionId}. Cannot determine stop loss/take profit hit.",
position.Identifier);
return;
}
}
var minPriceRecent = recentCandles.Min(c => c.Low);
var maxPriceRecent = recentCandles.Max(c => c.High);
bool wasStopLossHit = false;
bool wasTakeProfitHit = false;