using Managing.Core; using Managing.Domain.Shared.Rules; using Managing.Domain.Strategies.Base; using Skender.Stock.Indicators; using static Managing.Common.Enums; using Candle = Managing.Domain.Candles.Candle; namespace Managing.Domain.Strategies; public class RSIDivergenceStrategy : Strategy { public List Signals { get; set; } public TradeDirection Direction { get; set; } private const int UpperBand = 70; private const int LowerBand = 30; public RSIDivergenceStrategy(string name, int period) : base(name, StrategyType.RsiDivergence) { Period = period; Signals = new List(); } /// /// Get RSI signals /// /// public override List Run() { if (!Period.HasValue || Candles.Count <= Period) { return null; } var ticker = Candles.First().Ticker; try { var rsiResult = Candles.TakeLast(10 * Period.Value).GetRsi(Period.Value).ToList(); var candlesRsi = MapRsiToCandle(rsiResult, Candles.TakeLast(10 * Period.Value)); if (candlesRsi.Count(c => c.Rsi > 0) == 0) return null; GetLongSignals(candlesRsi); GetShortSignals(candlesRsi); return Signals; } catch (RuleException) { return null; } } public override StrategiesResultBase GetStrategyValues() { return new StrategiesResultBase() { Rsi = Candles.GetRsi(Period.Value).ToList() }; } private void GetLongSignals(List candlesRsi) { // Set the low and high for first candle var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0); var highPrices = new List(); var lowPrices = new List(); var highRsi = new List(); var lowRsi = new List(); highPrices.Add(firstCandleRsi); lowPrices.Add(firstCandleRsi); highRsi.Add(firstCandleRsi); lowRsi.Add(firstCandleRsi); var previousCandle = firstCandleRsi; // For a long foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1)) { // If price go down if (previousCandle.Close > currentCandle.Close) { // because the last price is upper than the current highPrices.AddItem(previousCandle); // Check if rsi is higher than the last lowest if (currentCandle.Rsi > lowRsi.TakeLast(Period.Value).Min(r => r.Rsi)) { // If new higher high, we set it if (currentCandle.Rsi > highRsi.Last().Rsi) highRsi.AddItem(currentCandle); if (currentCandle.Rsi > lowRsi.Last().Rsi) lowRsi.AddItem(currentCandle); // Price go down but RSI go up if (currentCandle.Close < lowPrices.TakeLast(Period.Value).Min(p => p.Close)) { AddSignal(currentCandle, TradeDirection.Long); } } else { // No divergence, price go down, rsi go down lowRsi.AddItem(currentCandle); } lowPrices.AddItem(currentCandle); } else { // Price go up, so we have to update if price is a new higher high than previous candle // Normally always true if (previousCandle.Close < currentCandle.Close) highPrices.AddItem(currentCandle); //15-15-12-14-17 // If rsi is lower low or not set if (currentCandle.Rsi < lowRsi.Last().Rsi || lowRsi.Last().Rsi == 0) lowRsi.AddItem(currentCandle); // Price going up, so if its a new high we set it if (currentCandle.Rsi > highRsi.Last().Rsi) highRsi.AddItem(currentCandle); } previousCandle = currentCandle; } } private void GetShortSignals(List candlesRsi) { // Set the low and high for first candle var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0); var signals = new List(); var highPrices = new List(); var lowPrices = new List(); var highRsi = new List(); var lowRsi = new List(); highPrices.Add(firstCandleRsi); lowPrices.Add(firstCandleRsi); highRsi.Add(firstCandleRsi); lowRsi.Add(firstCandleRsi); var previousCandle = firstCandleRsi; // For a short foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1)) { // If price go up if (previousCandle.Close < currentCandle.Close) { // because the last price is lower than the current lowPrices.AddItem(previousCandle); // Check if rsi is lower than the last high if (currentCandle.Rsi < highRsi.TakeLast(Period.Value).Max(r => r.Rsi)) { // If new lower low, we set it if (currentCandle.Rsi < lowRsi.Last().Rsi) lowRsi.AddItem(currentCandle); if (currentCandle.Rsi < highRsi.Last().Rsi) highRsi.AddItem(currentCandle); // Price go up but RSI go down if (currentCandle.Close > highPrices.TakeLast(Period.Value).Max(p => p.Close)) { AddSignal(currentCandle, TradeDirection.Short); } } else { // No divergence, price go up, rsi go up highRsi.AddItem(currentCandle); } highPrices.AddItem(currentCandle); } else { // Price go down, so we have to update if price is a new lower low than previous candle if (previousCandle.Close > currentCandle.Close) lowPrices.AddItem(currentCandle); // If rsi is higher high or not set if (currentCandle.Rsi > highRsi.Last().Rsi || highRsi.Last().Rsi == 0) highRsi.AddItem(currentCandle); // Price going down, so if its a new low we set it if (currentCandle.Rsi < lowRsi.Last().Rsi) lowRsi.AddItem(currentCandle); } previousCandle = currentCandle; } } private void AddSignal(CandleRsi candleSignal, TradeDirection direction) { var signal = new Signal(MiscExtensions.ParseEnum(candleSignal.Ticker), direction, Confidence.Low, candleSignal, candleSignal.Date, candleSignal.Exchange, Type, SignalType); if (Signals.Count(s => s.Identifier == signal.Identifier) < 1) { var lastCandleOnPeriod = Candles.TakeLast(Period.Value).ToList(); var signalsOnPeriod = Signals.Where(s => s.Date >= lastCandleOnPeriod[0].Date).ToList(); if (signalsOnPeriod.Count == 1) signal.SetConfidence(Confidence.Medium); if (signalsOnPeriod.Count >= 2) signal.SetConfidence(Confidence.High); Signals.AddItem(signal); } } private List MapRsiToCandle(IReadOnlyCollection rsiResult, IEnumerable candles) { return candles.Select(c => new CandleRsi() { Close = c.Close, Rsi = rsiResult.Find(c.Date).Rsi.GetValueOrDefault(), Date = c.Date, Ticker = c.Ticker, Exchange = c.Exchange }).ToList(); } private class CandleRsi : Candle { public double Rsi { get; set; } } }