using Managing.Core; using Managing.Domain.Candles; using Managing.Domain.MoneyManagements; using Managing.Domain.Strategies; using Managing.Domain.Trades; using static Managing.Common.Enums; namespace Managing.Domain.Shared.Helpers; public static class TradingBox { public static Signal GetSignal(HashSet newCandles, HashSet strategies, HashSet previousSignal) { var signalOnCandles = new HashSet(); foreach (var strategy in strategies) { strategy.UpdateCandles(newCandles); var signals = strategy.Run(); if (signals == null || signals.Count == 0) continue; foreach (var signal in signals.Where(s => s.Date == newCandles.Last().Date)) { if (previousSignal.SingleOrDefault(s => s.Identifier == signal.Identifier) == null) { if (previousSignal.Count == 0 || previousSignal.Last().Date < signal.Date) { signalOnCandles.Add(signal); } } } } if (signalOnCandles.Count != strategies.Count) return null; var data = newCandles.First(); return ComputeSignals(strategies, signalOnCandles, MiscExtensions.ParseEnum(data.Ticker), data.Timeframe); } public static Signal ComputeSignals(HashSet strategies, HashSet signalOnCandles, Ticker ticker, Timeframe timeframe) { Signal signal = null; if (strategies.Count > 1) { var trendSignal = signalOnCandles.Where(s => s.SignalType == SignalType.Trend).ToList(); var signals = signalOnCandles.Where(s => s.SignalType == SignalType.Signal).ToList(); var contextStrategiesCount = strategies.Count(s => s.SignalType == SignalType.Context); var validContext = true; if (contextStrategiesCount > 0 && signalOnCandles.Count(s => s.SignalType == SignalType.Context) != contextStrategiesCount) { validContext = false; } if (signals.All(s => s.Direction == TradeDirection.Long) && trendSignal.All(t => t.Direction == TradeDirection.Long) && validContext) { signal = new Signal( ticker, TradeDirection.Long, Confidence.High, signals.Last().Candle, signals.Last().Date, signals.Last().Exchange, timeframe, StrategyType.Composite, SignalType.Signal); } else if (signals.All(s => s.Direction == TradeDirection.Short) && trendSignal.All(t => t.Direction == TradeDirection.Short) && validContext) { signal = new Signal( ticker, TradeDirection.Short, Confidence.High, signals.Last().Candle, signals.Last().Date, signals.Last().Exchange, timeframe, StrategyType.Composite, SignalType.Signal); } } else { // Only one strategy, we just add the single signal to the bot signal = signalOnCandles.Single(); } return signal; } public static MoneyManagement GetBestMoneyManagement(List candles, List positions, MoneyManagement originMoneyManagement) { // Foreach positions, identitify the price when the position is open // Then, foreach candles, get the maximum price before the next position // Then, identify the lowest price before the maximum price // Base on that, return the best StopLoss and TakeProfit to use and build a var moneyManagement = new MoneyManagement(); var stoplossPercentage = new List(); var takeProfitsPercentage = new List(); if (positions.Count == 0) return null; for (var i = 0; i < positions.Count; i++) { var position = positions[i]; var nextPosition = i + 1 < positions.Count ? positions[i + 1] : null; var (stopLoss, takeProfit) = GetBestSltpForPosition(candles, position, nextPosition); stoplossPercentage.Add(stopLoss); takeProfitsPercentage.Add(takeProfit); } moneyManagement.StopLoss = stoplossPercentage.Average(); moneyManagement.TakeProfit = takeProfitsPercentage.Average(); moneyManagement.BalanceAtRisk = originMoneyManagement.BalanceAtRisk * 100; moneyManagement.Timeframe = originMoneyManagement.Timeframe; moneyManagement.Leverage = originMoneyManagement.Leverage; moneyManagement.Name = "Optimized"; return moneyManagement; } public static (decimal Stoploss, decimal TakeProfit) GetBestSltpForPosition(List candles, Position position, Position nextPosition) { var stopLoss = 0M; var takeProfit = 0M; var candlesBeforeNextPosition = candles.Where(c => c.Date >= position.Date && c.Date <= (nextPosition == null ? candles.Last().Date : nextPosition.Date)) .ToList(); if (position.OriginDirection == TradeDirection.Long) { var maxPrice = candlesBeforeNextPosition.Max(c => c.High); var minPrice = candlesBeforeNextPosition.TakeWhile(c => c.High <= maxPrice).Min(c => c.Low); stopLoss = GetPercentageFromEntry(position.Open.Price, minPrice); takeProfit = GetPercentageFromEntry(position.Open.Price, maxPrice); } else if (position.OriginDirection == TradeDirection.Short) { var minPrice = candlesBeforeNextPosition.Min(c => c.Low); var maxPrice = candlesBeforeNextPosition.TakeWhile(c => c.Low >= minPrice).Max(c => c.High); stopLoss = GetPercentageFromEntry(position.Open.Price, maxPrice); takeProfit = GetPercentageFromEntry(position.Open.Price, minPrice); } return (stopLoss, takeProfit); } private static decimal GetPercentageFromEntry(decimal entry, decimal price) { return Math.Abs(100 - ((100 * price) / entry)); } public static ProfitAndLoss GetProfitAndLoss(Position position, decimal quantity, decimal price, decimal leverage) { var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-quantity, price) }; var pnl = new ProfitAndLoss(orders, position.OriginDirection); // Apply leverage on the realized pnl pnl.Realized = pnl.Realized * leverage; return pnl; } }