using Managing.Application.Abstractions; using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Services; using Managing.Core; using Managing.Core.FixedSizedQueue; using Managing.Domain.Accounts; using Managing.Domain.Backtests; using Managing.Domain.Candles; using Managing.Domain.MoneyManagements; using Managing.Domain.Scenarios; using Managing.Domain.Shared.Helpers; using Managing.Domain.Strategies; using Managing.Domain.Strategies.Base; using Managing.Domain.Workflows; using Microsoft.Extensions.Logging; using static Managing.Common.Enums; namespace Managing.Application.Backtesting { public class Backtester : IBacktester { private readonly IBacktestRepository _backtestRepository; private readonly ILogger _logger; private readonly IExchangeService _exchangeService; private readonly IBotFactory _botFactory; private readonly IScenarioService _scenarioService; public Backtester( IExchangeService exchangeService, IBotFactory botFactory, IBacktestRepository backtestRepository, ILogger logger, IScenarioService scenarioService) { _exchangeService = exchangeService; _botFactory = botFactory; _backtestRepository = backtestRepository; _logger = logger; _scenarioService = scenarioService; } public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false) { var simplebot = _botFactory.CreateSimpleBot("scenario", workflow); Backtest result = null; if (save) { _backtestRepository.InsertBacktest(result); } return result; } public Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker, Scenario scenario, Timeframe timeframe, double days, decimal balance, bool isForWatchingOnly = false, bool save = false, List initialCandles = null) { var scalpingBot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, isForWatchingOnly); scalpingBot.LoadScenario(scenario.Name); var candles = initialCandles ?? GetCandles(account, ticker, timeframe, days); var result = GetBacktestingResult(ticker, scenario, timeframe, scalpingBot, candles, balance, account, moneyManagement); if (save) { _backtestRepository.InsertBacktest(result); } return result; } private List GetCandles(Account account, Ticker ticker, Timeframe timeframe, double days) { var candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker, DateTime.Now.AddDays(Convert.ToDouble(days)), timeframe).Result; if (candles == null || candles.Count == 0) throw new Exception($"No candles for {ticker} on {account.Exchange}"); return candles; } public Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker, Scenario scenario, Timeframe timeframe, double days, decimal balance, bool isForWatchingOnly = false, bool save = false, List initialCandles = null) { var flippingBot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, false); flippingBot.LoadScenario(scenario.Name); var candles = initialCandles ?? GetCandles(account, ticker, timeframe, days); var result = GetBacktestingResult(ticker, scenario, timeframe, flippingBot, candles, balance, account, moneyManagement); if (save) { _backtestRepository.InsertBacktest(result); } return result; } public Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List candles, decimal balance) { var ticker = MiscExtensions.ParseEnum(candles.FirstOrDefault().Ticker); var bot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, false); bot.LoadScenario(scenario.Name); var result = GetBacktestingResult(ticker, scenario, timeframe, bot, candles, balance, account, moneyManagement); return result; } public Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List candles, decimal balance) { var ticker = MiscExtensions.ParseEnum(candles.FirstOrDefault().Ticker); var bot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, false); bot.LoadScenario(scenario.Name); var result = GetBacktestingResult(ticker, scenario, timeframe, bot, candles, balance, account, moneyManagement); return result; } private Backtest GetBacktestingResult( Ticker ticker, Scenario scenario, Timeframe timeframe, ITradingBot bot, List candles, decimal balance, Account account, MoneyManagement moneyManagement) { if (candles == null || candles.Count == 0) { throw new Exception("No candle to backtest"); } bot.WalletBalances.Add(candles.FirstOrDefault().Date, balance); foreach (var candle in candles) { bot.OptimizedCandles.Enqueue(candle); bot.Candles.Add(candle); bot.Run(); } bot.Candles = new HashSet(candles); // bot.UpdateStrategiesValues(); var strategies = _scenarioService.GetStrategies(); var strategiesValues = GetStrategiesValues(strategies, candles); var finalPnl = bot.GetProfitAndLoss(); var winRate = bot.GetWinRate(); var optimizedMoneyManagement = TradingBox.GetBestMoneyManagement(candles, bot.Positions, moneyManagement); var stats = TradingHelpers.GetStatistics(bot.WalletBalances); var growthPercentage = TradingHelpers.GetGrowthFromInitalBalance(balance, finalPnl); var hodlPercentage = TradingHelpers.GetHodlPercentage(candles[0], candles.Last()); var result = new Backtest(ticker, scenario.Name, bot.Positions, bot.Signals.ToList(), timeframe, candles, bot.BotType, account.Name) { FinalPnl = finalPnl, WinRate = winRate, GrowthPercentage = growthPercentage, HodlPercentage = hodlPercentage, Fees = bot.GetTotalFees(), WalletBalances = bot.WalletBalances.ToList(), Statistics = stats, OptimizedMoneyManagement = optimizedMoneyManagement, MoneyManagement = moneyManagement, StrategiesValues = strategiesValues }; return result; } private Dictionary GetStrategiesValues(IEnumerable strategies, List candles) { var strategiesValues = new Dictionary(); var fixedCandles = new FixedSizeQueue(10000); foreach (var candle in candles) { fixedCandles.Enqueue(candle); } foreach (var strategy in strategies) { try { var s = ScenarioHelpers.BuildStrategy(strategy, 10000); s.Candles = fixedCandles; strategiesValues[strategy.Type] = s.GetStrategyValues(); } catch (Exception e) { Console.WriteLine(e); } } return strategiesValues; } public IEnumerable GetBacktests() { return _backtestRepository.GetBacktests(); } public bool DeleteBacktest(string id) { try { _backtestRepository.DeleteBacktestById(id); return true; } catch (Exception ex) { _logger.LogError(ex.Message); return false; } } public bool DeleteBacktests() { try { _backtestRepository.DeleteAllBacktests(); //_backtestRepository.DropCollection(); return true; } catch (Exception ex) { _logger.LogError(ex.Message); return false; } } } }