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(),
};
}
}