using Managing.Core; using Managing.Domain.Candles; using Managing.Domain.Indicators; using Managing.Domain.Shared.Rules; using Managing.Domain.Strategies.Base; using Skender.Stock.Indicators; using static Managing.Common.Enums; namespace Managing.Domain.Strategies.Trends; public class EmaTrendIndicatorBase : EmaBaseIndicatorBase { public List Signals { get; set; } public EmaTrendIndicatorBase(string name, int period) : base(name, IndicatorType.EmaTrend) { Signals = new List(); Period = period; } public override List Run(HashSet candles) { if (candles.Count <= 2 * Period) { return null; } try { var ema = candles.GetEma(Period.Value).ToList(); if (ema.Count == 0) return null; ProcessEmaTrendSignals(ema, candles); return Signals.ToList(); } catch (RuleException) { return null; } } /// /// Runs the indicator using pre-calculated EMA values for performance optimization. /// public override List Run(HashSet candles, IndicatorsResultBase preCalculatedValues) { if (candles.Count <= 2 * Period) { return null; } try { // Use pre-calculated EMA values if available List ema = null; if (preCalculatedValues?.Ema != null && preCalculatedValues.Ema.Any()) { // Filter pre-calculated EMA values to match the candles we're processing ema = preCalculatedValues.Ema .Where(e => candles.Any(c => c.Date == e.Date)) .ToList(); } // If no pre-calculated values or they don't match, fall back to regular calculation if (ema == null || !ema.Any()) { return Run(candles); } ProcessEmaTrendSignals(ema, candles); return Signals.ToList(); } catch (RuleException) { return null; } } /// /// Processes EMA trend signals based on price position relative to EMA line. /// This method is shared between the regular Run() and optimized Run() methods. /// /// List of EMA calculation results /// Candles to process private void ProcessEmaTrendSignals(List ema, HashSet candles) { var emaCandles = MapEmaToCandle(ema, candles.TakeLast(Period.Value)); if (emaCandles.Count == 0) return; var previousCandle = emaCandles[0]; foreach (var currentCandle in emaCandles.Skip(1)) { if (currentCandle.Close > (decimal)currentCandle.Ema) { AddSignal(currentCandle, TradeDirection.Long, Confidence.None); } else { AddSignal(currentCandle, TradeDirection.Short, Confidence.None); } previousCandle = currentCandle; } } public override IndicatorsResultBase GetIndicatorValues(HashSet candles) { return new IndicatorsResultBase() { Ema = candles.GetEma(Period.Value).ToList() }; } public void AddSignal(CandleEma candleSignal, TradeDirection direction, Confidence confidence) { var signal = new LightSignal(candleSignal.Ticker, direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, Type, SignalType, Name); if (!Signals.Any(s => s.Identifier == signal.Identifier)) { Signals.AddItem(signal); } } }