using System.Collections.Concurrent; using Managing.Application.Abstractions; using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Services; using Managing.Application.Bots; using Managing.Domain.Bots; using Managing.Domain.Users; using Managing.Domain.Workflows; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using static Managing.Common.Enums; namespace Managing.Application.ManageBot { public class BotService : IBotService { private readonly IBotRepository _botRepository; private readonly IExchangeService _exchangeService; private readonly IMessengerService _messengerService; private readonly IAccountService _accountService; private readonly ILogger _tradingBotLogger; private readonly ITradingService _tradingService; private readonly IMoneyManagementService _moneyManagementService; private readonly IUserService _userService; private ConcurrentDictionary _botTasks = new ConcurrentDictionary(); public BotService(IBotRepository botRepository, IExchangeService exchangeService, IMessengerService messengerService, IAccountService accountService, ILogger tradingBotLogger, ITradingService tradingService, IMoneyManagementService moneyManagementService, IUserService userService) { _botRepository = botRepository; _exchangeService = exchangeService; _messengerService = messengerService; _accountService = accountService; _tradingBotLogger = tradingBotLogger; _tradingService = tradingService; _moneyManagementService = moneyManagementService; _userService = userService; } public BotBackup GetBotBackup(string identifier) { return _botRepository.GetBots().FirstOrDefault(b => b.Identifier == identifier); } public void SaveOrUpdateBotBackup(User user, string identifier, BotType botType, BotStatus status, string data) { var backup = GetBotBackup(identifier); if (backup != null) { backup.LastStatus = status; backup.Data = data; _botRepository.UpdateBackupBot(backup); } else { var botBackup = new BotBackup { LastStatus = status, User = user, Identifier = identifier, BotType = botType, Data = data }; _botRepository.InsertBotAsync(botBackup); } } public class BotTaskWrapper { public Task Task { get; private set; } public Type BotType { get; private set; } public object BotInstance { get; private set; } // Add this line public BotTaskWrapper(Task task, Type botType, object botInstance) // Update constructor { Task = task; BotType = botType; BotInstance = botInstance; // Set the bot instance } } public void AddSimpleBotToCache(IBot bot) { var botTask = new BotTaskWrapper(Task.Run(() => bot.Start()), bot.GetType(), bot); // Pass bot as the instance _botTasks.AddOrUpdate(bot.Identifier, botTask, (key, existingVal) => botTask); } public void AddTradingBotToCache(ITradingBot bot) { var botTask = new BotTaskWrapper(Task.Run(() => bot.Start()), bot.GetType(), bot); _botTasks.AddOrUpdate(bot.Identifier, botTask, (key, existingVal) => botTask); } public List GetActiveBots() { var bots = _botTasks.Values .Where(wrapper => typeof(ITradingBot).IsAssignableFrom(wrapper.BotType)) .Select(wrapper => wrapper.BotInstance as ITradingBot) .Where(bot => bot != null) .ToList(); return bots; } public IEnumerable GetSavedBots() { return _botRepository.GetBots(); } public void StartBotFromBackup(BotBackup backupBot) { object bot = null; Task botTask = null; switch (backupBot.BotType) { case BotType.ScalpingBot: var scalpingBotData = JsonConvert.DeserializeObject(backupBot.Data); var scalpingMoneyManagement = _moneyManagementService.GetMoneyMangement(scalpingBotData.MoneyManagement.Name).Result; // Create config from backup data var scalpingConfig = new TradingBotConfig { AccountName = scalpingBotData.AccountName, MoneyManagement = scalpingMoneyManagement, Ticker = scalpingBotData.Ticker, ScenarioName = scalpingBotData.ScenarioName, Timeframe = scalpingBotData.Timeframe, IsForWatchingOnly = scalpingBotData.IsForWatchingOnly, BotTradingBalance = scalpingBotData.BotTradingBalance, BotType = scalpingBotData.BotType, Name = scalpingBotData.Name, CooldownPeriod = scalpingBotData.CooldownPeriod, MaxLossStreak = scalpingBotData.MaxLossStreak, MaxPositionTimeHours = scalpingBotData.MaxPositionTimeHours == 0m ? null : scalpingBotData.MaxPositionTimeHours, FlipOnlyWhenInProfit = scalpingBotData.FlipOnlyWhenInProfit, IsForBacktest = false, FlipPosition = false }; bot = CreateScalpingBot(scalpingConfig); botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot)); break; case BotType.FlippingBot: var flippingBotData = JsonConvert.DeserializeObject(backupBot.Data); var flippingMoneyManagement = _moneyManagementService.GetMoneyMangement(flippingBotData.MoneyManagement.Name).Result; // Create config from backup data var flippingConfig = new TradingBotConfig { AccountName = flippingBotData.AccountName, MoneyManagement = flippingMoneyManagement, Ticker = flippingBotData.Ticker, ScenarioName = flippingBotData.ScenarioName, Timeframe = flippingBotData.Timeframe, IsForWatchingOnly = flippingBotData.IsForWatchingOnly, BotTradingBalance = flippingBotData.BotTradingBalance, BotType = flippingBotData.BotType, Name = flippingBotData.Name, CooldownPeriod = flippingBotData.CooldownPeriod, MaxLossStreak = flippingBotData.MaxLossStreak, MaxPositionTimeHours = flippingBotData.MaxPositionTimeHours == 0m ? null : flippingBotData.MaxPositionTimeHours, FlipOnlyWhenInProfit = flippingBotData.FlipOnlyWhenInProfit, IsForBacktest = false, FlipPosition = true }; bot = CreateFlippingBot(flippingConfig); botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot)); break; } if (bot != null && botTask != null) { var botWrapper = new BotTaskWrapper(botTask, bot.GetType(), bot); _botTasks.AddOrUpdate(backupBot.Identifier, botWrapper, (key, existingVal) => botWrapper); } } private void InitBot(ITradingBot bot, BotBackup backupBot) { var user = _userService.GetUser(backupBot.User.Name); bot.User = user; // Config is already set correctly from backup data, so we only need to restore signals, positions, etc. bot.LoadBackup(backupBot); bot.Start(); } public IBot CreateSimpleBot(string botName, Workflow workflow) { return new SimpleBot(botName, _tradingBotLogger, workflow, this); } public async Task StopBot(string identifier) { if (_botTasks.TryGetValue(identifier, out var botWrapper)) { if (botWrapper.BotInstance is IBot bot) { await Task.Run(() => bot.Stop()); // Assuming Stop is an asynchronous process wrapped in Task.Run for synchronous methods return bot.GetStatus(); } } return BotStatus.Down.ToString(); } public async Task DeleteBot(string identifier) { if (_botTasks.TryRemove(identifier, out var botWrapper)) { try { if (botWrapper.BotInstance is IBot bot) { await Task.Run(() => bot.Stop()); // Assuming Stop is an asynchronous process wrapped in Task.Run for synchronous methods } await _botRepository.DeleteBotBackup(identifier); return true; } catch (Exception e) { Console.WriteLine(e); return false; } } return false; } public Task RestartBot(string identifier) { if (_botTasks.TryGetValue(identifier, out var botWrapper)) { if (botWrapper.BotInstance is IBot bot) { bot.Restart(); return Task.FromResult(bot.GetStatus()); } } return Task.FromResult(BotStatus.Down.ToString()); } public void ToggleIsForWatchingOnly(string identifier) { if (_botTasks.TryGetValue(identifier, out var botTaskWrapper) && botTaskWrapper.BotInstance is ITradingBot tradingBot) { tradingBot.ToggleIsForWatchOnly().Wait(); } } public ITradingBot CreateScalpingBot(TradingBotConfig config) { return new ScalpingBot( _exchangeService, _tradingBotLogger, _tradingService, _accountService, _messengerService, this, config); } public ITradingBot CreateBacktestScalpingBot(TradingBotConfig config) { config.IsForBacktest = true; return new ScalpingBot( _exchangeService, _tradingBotLogger, _tradingService, _accountService, _messengerService, this, config); } public ITradingBot CreateFlippingBot(TradingBotConfig config) { return new FlippingBot( _exchangeService, _tradingBotLogger, _tradingService, _accountService, _messengerService, this, config); } public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config) { config.IsForBacktest = true; return new FlippingBot( _exchangeService, _tradingBotLogger, _tradingService, _accountService, _messengerService, this, config); } } }