192 lines
7.2 KiB
C#
192 lines
7.2 KiB
C#
using Managing.Core;
|
|
using Managing.Domain.Candles;
|
|
using Managing.Domain.Shared.Rules;
|
|
using Managing.Domain.Strategies.Base;
|
|
using Skender.Stock.Indicators;
|
|
using static Managing.Common.Enums;
|
|
|
|
namespace Managing.Domain.Strategies;
|
|
|
|
public class SuperTrendCrossEma : Strategy
|
|
{
|
|
public List<Signal> Signals { get; set; }
|
|
|
|
public SuperTrendCrossEma(string name, int period, double multiplier) : base(name, StrategyType.SuperTrendCrossEma)
|
|
{
|
|
Signals = new List<Signal>();
|
|
Period = period;
|
|
Multiplier = multiplier;
|
|
MinimumHistory = 100 + Period.Value;
|
|
}
|
|
|
|
public override List<Signal> Run()
|
|
{
|
|
// Validate sufficient historical data for all indicators
|
|
const int emaPeriod = 50;
|
|
const int adxPeriod = 14; // Standard ADX period
|
|
const int adxThreshold = 25; // Minimum ADX level to confirm a trend
|
|
|
|
int minimumRequiredHistory = Math.Max(Math.Max(emaPeriod, adxPeriod), Period.Value * 2); // Ensure enough data
|
|
if (Candles.Count < minimumRequiredHistory)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
// 1. Calculate indicators
|
|
var superTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value)
|
|
.Where(s => s.SuperTrend.HasValue)
|
|
.ToList();
|
|
|
|
var ema50 = Candles.GetEma(emaPeriod)
|
|
.Where(e => e.Ema.HasValue)
|
|
.ToList();
|
|
|
|
var adxResults = Candles.GetAdx(adxPeriod)
|
|
.Where(a => a.Adx.HasValue && a.Pdi.HasValue && a.Mdi.HasValue) // Ensure all values exist
|
|
.ToList();
|
|
|
|
// 2. Create merged dataset with price + indicators
|
|
var superTrendCandles = MapSuperTrendToCandle(superTrend, Candles.TakeLast(minimumRequiredHistory));
|
|
if (superTrendCandles.Count == 0)
|
|
return null;
|
|
|
|
// 3. Add EMA50 and ADX values to the CandleSuperTrend objects
|
|
foreach (var candle in superTrendCandles)
|
|
{
|
|
var emaValue = ema50.Find(e => e.Date == candle.Date)?.Ema;
|
|
var adxValue = adxResults.Find(a => a.Date == candle.Date);
|
|
|
|
if (emaValue.HasValue)
|
|
candle.Ema50 = emaValue.Value;
|
|
|
|
if (adxValue != null)
|
|
{
|
|
candle.Adx = (decimal)adxValue.Adx.Value;
|
|
candle.Pdi = (decimal)adxValue.Pdi.Value;
|
|
candle.Mdi = (decimal)adxValue.Mdi.Value;
|
|
}
|
|
}
|
|
|
|
// 4. Signal detection logic with ADX filter
|
|
for (int i = 1; i < superTrendCandles.Count; i++)
|
|
{
|
|
var current = superTrendCandles[i];
|
|
var previous = superTrendCandles[i - 1];
|
|
|
|
// Convert SuperTrend to double for comparison
|
|
double currentSuperTrend = (double)current.SuperTrend;
|
|
double previousSuperTrend = (double)previous.SuperTrend;
|
|
|
|
// Ensure ADX data exists
|
|
if (current.Adx < adxThreshold) // Only trade when ADX confirms trend strength
|
|
continue;
|
|
|
|
/* LONG SIGNAL CONDITIONS:
|
|
* 1. SuperTrend crosses above EMA50
|
|
* 2. Price > SuperTrend and > EMA50
|
|
* 3. Previous state shows SuperTrend < EMA50
|
|
* 4. ADX > threshold and +DI > -DI (bullish momentum)
|
|
*/
|
|
bool longCross = currentSuperTrend > current.Ema50 &&
|
|
previousSuperTrend < previous.Ema50;
|
|
|
|
bool longPricePosition = current.Close > (decimal)currentSuperTrend &&
|
|
current.Close > (decimal)current.Ema50;
|
|
|
|
bool adxBullish = current.Pdi > current.Mdi; // Bullish momentum confirmation
|
|
|
|
if (longCross && longPricePosition && adxBullish)
|
|
{
|
|
AddSignal(current, TradeDirection.Long, Confidence.Medium);
|
|
}
|
|
|
|
/* SHORT SIGNAL CONDITIONS:
|
|
* 1. SuperTrend crosses below EMA50
|
|
* 2. Price < SuperTrend and < EMA50
|
|
* 3. Previous state shows SuperTrend > EMA50
|
|
* 4. ADX > threshold and -DI > +DI (bearish momentum)
|
|
*/
|
|
bool shortCross = currentSuperTrend < current.Ema50 &&
|
|
previousSuperTrend > previous.Ema50;
|
|
|
|
bool shortPricePosition = current.Close < (decimal)currentSuperTrend &&
|
|
current.Close < (decimal)current.Ema50;
|
|
|
|
bool adxBearish = current.Mdi > current.Pdi; // Bearish momentum confirmation
|
|
|
|
if (shortCross && shortPricePosition && adxBearish)
|
|
{
|
|
AddSignal(current, TradeDirection.Short, Confidence.Medium);
|
|
}
|
|
}
|
|
|
|
return Signals.Where(s => s.Confidence != Confidence.None)
|
|
.OrderBy(s => s.Date)
|
|
.ToList();
|
|
}
|
|
catch (RuleException)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private List<CandleSuperTrend> MapSuperTrendToCandle(List<SuperTrendResult> superTrend, IEnumerable<Candle> candles)
|
|
{
|
|
var superTrends = new List<CandleSuperTrend>();
|
|
foreach (var candle in candles)
|
|
{
|
|
var currentSuperTrend = superTrend.Find(candle.Date);
|
|
if (currentSuperTrend != null)
|
|
{
|
|
superTrends.Add(new CandleSuperTrend()
|
|
{
|
|
Close = candle.Close,
|
|
Open = candle.Open,
|
|
Date = candle.Date,
|
|
Ticker = candle.Ticker,
|
|
Exchange = candle.Exchange,
|
|
SuperTrend = currentSuperTrend.SuperTrend.Value,
|
|
LowerBand = currentSuperTrend.LowerBand,
|
|
UpperBand = currentSuperTrend.UpperBand,
|
|
});
|
|
}
|
|
}
|
|
|
|
return superTrends;
|
|
}
|
|
|
|
public override StrategiesResultBase GetStrategyValues()
|
|
{
|
|
return new StrategiesResultBase()
|
|
{
|
|
SuperTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue)
|
|
.ToList()
|
|
};
|
|
}
|
|
|
|
|
|
private void AddSignal(CandleSuperTrend candleSignal, TradeDirection direction, Confidence confidence)
|
|
{
|
|
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence,
|
|
candleSignal, candleSignal.Date,
|
|
candleSignal.Exchange, Type, SignalType);
|
|
if (!Signals.Any(s => s.Identifier == signal.Identifier))
|
|
{
|
|
Signals.AddItem(signal);
|
|
}
|
|
}
|
|
|
|
|
|
private class CandleSuperTrend : Candle
|
|
{
|
|
public decimal SuperTrend { get; internal set; }
|
|
public decimal? LowerBand { get; internal set; }
|
|
public decimal? UpperBand { get; internal set; }
|
|
public double Ema50 { get; set; }
|
|
public decimal Adx { get; set; } // ADX value
|
|
public decimal Pdi { get; set; } // Positive Directional Indicator (+DI)
|
|
public decimal Mdi { get; set; } // Negative Directional Indicator (-DI)
|
|
}
|
|
} |