using Managing.Domain.Strategies; using Managing.Domain.Strategies.Context; using Managing.Domain.Strategies.Signals; using Managing.Domain.Strategies.Trends; using Newtonsoft.Json; using static Managing.Common.Enums; namespace Managing.Domain.Scenarios; public static class ScenarioHelpers { /// /// Compares two lists of indicators and returns a list of changes (added, removed, modified). /// /// The previous list of indicators /// The new list of indicators /// A list of change descriptions public static List CompareIndicators(List oldIndicators, List newIndicators) { var changes = new List(); // Create dictionaries for easier comparison using Type as key var oldIndicatorDict = oldIndicators.ToDictionary(i => i.Type, i => i); var newIndicatorDict = newIndicators.ToDictionary(i => i.Type, i => i); // Find removed indicators var removedTypes = oldIndicatorDict.Keys.Except(newIndicatorDict.Keys); foreach (var removedType in removedTypes) { var indicator = oldIndicatorDict[removedType]; changes.Add($"➖ **Removed Indicator:** {removedType} ({indicator.GetType().Name})"); } // Find added indicators var addedTypes = newIndicatorDict.Keys.Except(oldIndicatorDict.Keys); foreach (var addedType in addedTypes) { var indicator = newIndicatorDict[addedType]; changes.Add($"➕ **Added Indicator:** {addedType} ({indicator.GetType().Name})"); } // Find modified indicators (same type but potentially different configuration) var commonTypes = oldIndicatorDict.Keys.Intersect(newIndicatorDict.Keys); foreach (var commonType in commonTypes) { var oldIndicator = oldIndicatorDict[commonType]; var newIndicator = newIndicatorDict[commonType]; // Compare indicators by serializing them (simple way to detect configuration changes) var oldSerialized = JsonConvert.SerializeObject(oldIndicator, Formatting.None); var newSerialized = JsonConvert.SerializeObject(newIndicator, Formatting.None); if (oldSerialized != newSerialized) { changes.Add($"🔄 **Modified Indicator:** {commonType} ({newIndicator.GetType().Name})"); } } // Add summary if there are changes if (changes.Any()) { var summary = $"📊 **Indicator Changes:** {addedTypes.Count()} added, {removedTypes.Count()} removed, {commonTypes.Count(c => JsonConvert.SerializeObject(oldIndicatorDict[c]) != JsonConvert.SerializeObject(newIndicatorDict[c]))} modified"; changes.Insert(0, summary); } return changes; } public static IIndicator BuildIndicator(LightIndicator indicator) { IIndicator result = indicator.Type switch { IndicatorType.StDev => new StDevContext(indicator.Name, indicator.Period.Value), IndicatorType.RsiDivergence => new RsiDivergenceIndicatorBase(indicator.Name, indicator.Period.Value), IndicatorType.RsiDivergenceConfirm => new RsiDivergenceConfirmIndicatorBase(indicator.Name, indicator.Period.Value), IndicatorType.MacdCross => new MacdCrossIndicatorBase(indicator.Name, indicator.FastPeriods.Value, indicator.SlowPeriods.Value, indicator.SignalPeriods.Value), IndicatorType.EmaCross => new EmaCrossIndicatorBase(indicator.Name, indicator.Period.Value), IndicatorType.DualEmaCross => new DualEmaCrossIndicatorBase(indicator.Name, indicator.FastPeriods.Value, indicator.SlowPeriods.Value), IndicatorType.ThreeWhiteSoldiers => new ThreeWhiteSoldiersIndicatorBase(indicator.Name, indicator.Period.Value), IndicatorType.SuperTrend => new SuperTrendIndicatorBase(indicator.Name, indicator.Period.Value, indicator.Multiplier.Value), IndicatorType.ChandelierExit => new ChandelierExitIndicatorBase(indicator.Name, indicator.Period.Value, indicator.Multiplier.Value), IndicatorType.EmaTrend => new EmaTrendIndicatorBase(indicator.Name, indicator.Period.Value), IndicatorType.StochRsiTrend => new StochRsiTrendIndicatorBase(indicator.Name, indicator.Period.Value, indicator.StochPeriods.Value, indicator.SignalPeriods.Value, indicator.SmoothPeriods.Value), IndicatorType.StochasticCross => new StochasticCrossIndicator(indicator.Name, indicator.StochPeriods.Value, indicator.SignalPeriods.Value, indicator.SmoothPeriods.Value, indicator.KFactor ?? 3.0, indicator.DFactor ?? 2.0), IndicatorType.Stc => new StcIndicatorBase(indicator.Name, indicator.CyclePeriods.Value, indicator.FastPeriods.Value, indicator.SlowPeriods.Value), IndicatorType.LaggingStc => new LaggingSTC(indicator.Name, indicator.CyclePeriods.Value, indicator.FastPeriods.Value, indicator.SlowPeriods.Value), IndicatorType.SuperTrendCrossEma => new SuperTrendCrossEma(indicator.Name, indicator.Period.Value, indicator.Multiplier.Value), IndicatorType.BollingerBandsPercentBMomentumBreakout => new BollingerBandsPercentBMomentumBreakout( indicator.Name, indicator.Period.Value, indicator.StDev.Value), IndicatorType.BollingerBandsVolatilityProtection => new BollingerBandsVolatilityProtection(indicator.Name, indicator.Period.Value, indicator.StDev.Value), IndicatorType.IchimokuKumoTrend => new IchimokuKumoTrend(indicator.Name, indicator.TenkanPeriods ?? 9, indicator.KijunPeriods ?? 26, indicator.SenkouBPeriods ?? 52, indicator.OffsetPeriods ?? 26, indicator.SenkouOffset, indicator.ChikouOffset), _ => throw new NotImplementedException(), }; result.SignalType = GetSignalType(indicator.Type); return result; } /// /// Converts a full Indicator to a LightIndicator /// public static LightIndicator BaseToLight(IndicatorBase indicatorBase) { return new LightIndicator(indicatorBase.Name, indicatorBase.Type) { MinimumHistory = indicatorBase.MinimumHistory, Period = indicatorBase.Period, FastPeriods = indicatorBase.FastPeriods, SlowPeriods = indicatorBase.SlowPeriods, SignalPeriods = indicatorBase.SignalPeriods, Multiplier = indicatorBase.Multiplier, StDev = indicatorBase.StDev, SmoothPeriods = indicatorBase.SmoothPeriods, StochPeriods = indicatorBase.StochPeriods, CyclePeriods = indicatorBase.CyclePeriods, KFactor = indicatorBase.KFactor, DFactor = indicatorBase.DFactor, TenkanPeriods = indicatorBase.TenkanPeriods, KijunPeriods = indicatorBase.KijunPeriods, SenkouBPeriods = indicatorBase.SenkouBPeriods, OffsetPeriods = indicatorBase.OffsetPeriods, SenkouOffset = indicatorBase.SenkouOffset, ChikouOffset = indicatorBase.ChikouOffset }; } public static IIndicator BuildIndicator( IndicatorType type, string name, int? period = null, int? fastPeriods = null, int? slowPeriods = null, int? signalPeriods = null, double? multiplier = null, double? stdev = null, int? stochPeriods = null, int? smoothPeriods = null, int? cyclePeriods = null, double? kFactor = null, double? dFactor = null, int? tenkanPeriods = null, int? kijunPeriods = null, int? senkouBPeriods = null, int? offsetPeriods = null, int? senkouOffset = null, int? chikouOffset = null) { IIndicator indicator = new IndicatorBase(name, type); switch (type) { case IndicatorType.RsiDivergence: case IndicatorType.RsiDivergenceConfirm: case IndicatorType.EmaTrend: case IndicatorType.EmaCross: case IndicatorType.StDev: if (!period.HasValue) { throw new Exception($"Missing period for {indicator.Type} strategy type"); } else { indicator.Period = period.Value; } break; case IndicatorType.MacdCross: if (!fastPeriods.HasValue || !slowPeriods.HasValue || !signalPeriods.HasValue) { throw new Exception( $"Missing fastPeriods or slowPeriods or signalPeriods, for {indicator.Type} strategy type"); } else { indicator.FastPeriods = fastPeriods; indicator.SlowPeriods = slowPeriods; indicator.SignalPeriods = signalPeriods; } break; case IndicatorType.DualEmaCross: if (!fastPeriods.HasValue || !slowPeriods.HasValue) { throw new Exception( $"Missing fastPeriods or slowPeriods for {indicator.Type} strategy type"); } else { indicator.FastPeriods = fastPeriods; indicator.SlowPeriods = slowPeriods; } break; case IndicatorType.ThreeWhiteSoldiers: break; case IndicatorType.SuperTrend: case IndicatorType.SuperTrendCrossEma: case IndicatorType.ChandelierExit: if (!period.HasValue || !multiplier.HasValue) { throw new Exception($"Missing period or multiplier, for {indicator.Type} strategy type"); } else { indicator.Period = period; indicator.Multiplier = multiplier; } break; case IndicatorType.StochRsiTrend: if (!period.HasValue || !stochPeriods.HasValue || !signalPeriods.HasValue || !smoothPeriods.HasValue) { throw new Exception( $"Missing period, stochPeriods, signalPeriods, smoothPeriods for {indicator.Type} strategy type"); } else { indicator.Period = period; indicator.StochPeriods = stochPeriods; indicator.SignalPeriods = signalPeriods; indicator.SmoothPeriods = smoothPeriods; } break; case IndicatorType.StochasticCross: if (!stochPeriods.HasValue || !signalPeriods.HasValue || !smoothPeriods.HasValue) { throw new Exception( $"Missing stochPeriods, signalPeriods, smoothPeriods for {indicator.Type} strategy type"); } else { indicator.StochPeriods = stochPeriods; indicator.SignalPeriods = signalPeriods; indicator.SmoothPeriods = smoothPeriods; // Set default values for optional parameters indicator.KFactor = kFactor ?? 3.0; indicator.DFactor = dFactor ?? 2.0; // Validate kFactor and dFactor are greater than 0 if (indicator.KFactor <= 0) { throw new Exception($"kFactor must be greater than 0 for {indicator.Type} strategy type"); } if (indicator.DFactor <= 0) { throw new Exception($"dFactor must be greater than 0 for {indicator.Type} strategy type"); } } break; case IndicatorType.BollingerBandsPercentBMomentumBreakout: case IndicatorType.BollingerBandsVolatilityProtection: if (!period.HasValue || !stdev.HasValue) { throw new Exception($"Missing period or stdev for {indicator.Type} strategy type"); } else { ((IndicatorBase)indicator).Period = period; ((IndicatorBase)indicator).StDev = stdev; } break; case IndicatorType.IchimokuKumoTrend: if (!tenkanPeriods.HasValue || !kijunPeriods.HasValue || !senkouBPeriods.HasValue || !offsetPeriods.HasValue) { throw new Exception($"Missing Ichimoku parameters for {indicator.Type} strategy type"); } else { ((IndicatorBase)indicator).TenkanPeriods = tenkanPeriods; ((IndicatorBase)indicator).KijunPeriods = kijunPeriods; ((IndicatorBase)indicator).SenkouBPeriods = senkouBPeriods; ((IndicatorBase)indicator).OffsetPeriods = offsetPeriods; ((IndicatorBase)indicator).SenkouOffset = senkouOffset; ((IndicatorBase)indicator).ChikouOffset = chikouOffset; } break; case IndicatorType.Stc: case IndicatorType.LaggingStc: if (!fastPeriods.HasValue || !slowPeriods.HasValue || !cyclePeriods.HasValue) { throw new Exception( $"Missing fastPeriods or slowPeriods or cyclePeriods, for {indicator.Type} strategy type"); } else { indicator.FastPeriods = fastPeriods; indicator.SlowPeriods = slowPeriods; indicator.CyclePeriods = cyclePeriods; } break; default: break; } return indicator; } public static SignalType GetSignalType(IndicatorType type) { return type switch { IndicatorType.RsiDivergence => SignalType.Signal, IndicatorType.RsiDivergenceConfirm => SignalType.Signal, IndicatorType.MacdCross => SignalType.Signal, IndicatorType.EmaCross => SignalType.Signal, IndicatorType.DualEmaCross => SignalType.Signal, IndicatorType.ThreeWhiteSoldiers => SignalType.Signal, IndicatorType.SuperTrend => SignalType.Trend, IndicatorType.ChandelierExit => SignalType.Signal, IndicatorType.EmaTrend => SignalType.Trend, IndicatorType.Composite => SignalType.Signal, IndicatorType.StochRsiTrend => SignalType.Trend, IndicatorType.StochasticCross => SignalType.Signal, IndicatorType.Stc => SignalType.Signal, IndicatorType.StDev => SignalType.Context, IndicatorType.LaggingStc => SignalType.Signal, IndicatorType.SuperTrendCrossEma => SignalType.Signal, IndicatorType.BollingerBandsPercentBMomentumBreakout => SignalType.Signal, IndicatorType.BollingerBandsVolatilityProtection => SignalType.Context, IndicatorType.IchimokuKumoTrend => SignalType.Trend, _ => throw new NotImplementedException(), }; } }