Files
managing-apps/src/Managing.Application/ManageBot/BotService.cs
2025-06-03 01:01:17 +07:00

316 lines
12 KiB
C#

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<TradingBot> _tradingBotLogger;
private readonly ITradingService _tradingService;
private readonly IMoneyManagementService _moneyManagementService;
private readonly IUserService _userService;
private ConcurrentDictionary<string, BotTaskWrapper> _botTasks =
new ConcurrentDictionary<string, BotTaskWrapper>();
public BotService(IBotRepository botRepository, IExchangeService exchangeService,
IMessengerService messengerService, IAccountService accountService, ILogger<TradingBot> 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<ITradingBot> 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<BotBackup> 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<TradingBotBackup>(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<TradingBotBackup>(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<string> 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<bool> 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<string> 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);
}
}
}