128 lines
3.8 KiB
C#
128 lines
3.8 KiB
C#
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<LightSignal> Signals { get; set; }
|
|
|
|
public EmaTrendIndicatorBase(string name, int period) : base(name, IndicatorType.EmaTrend)
|
|
{
|
|
Signals = new List<LightSignal>();
|
|
Period = period;
|
|
}
|
|
|
|
public override List<LightSignal> Run(HashSet<Candle> 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs the indicator using pre-calculated EMA values for performance optimization.
|
|
/// </summary>
|
|
public override List<LightSignal> Run(HashSet<Candle> candles, IndicatorsResultBase preCalculatedValues)
|
|
{
|
|
if (candles.Count <= 2 * Period)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Use pre-calculated EMA values if available
|
|
List<EmaResult> 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Processes EMA trend signals based on price position relative to EMA line.
|
|
/// This method is shared between the regular Run() and optimized Run() methods.
|
|
/// </summary>
|
|
/// <param name="ema">List of EMA calculation results</param>
|
|
/// <param name="candles">Candles to process</param>
|
|
private void ProcessEmaTrendSignals(List<EmaResult> ema, HashSet<Candle> 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<Candle> 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);
|
|
}
|
|
}
|
|
} |