262 lines
11 KiB
C#
262 lines
11 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// Compares two lists of indicators and returns a list of changes (added, removed, modified).
|
||
/// </summary>
|
||
/// <param name="oldIndicators">The previous list of indicators</param>
|
||
/// <param name="newIndicators">The new list of indicators</param>
|
||
/// <returns>A list of change descriptions</returns>
|
||
public static List<string> CompareIndicators(List<LightIndicator> oldIndicators, List<LightIndicator> newIndicators)
|
||
{
|
||
var changes = new List<string>();
|
||
|
||
// 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.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),
|
||
_ => throw new NotImplementedException(),
|
||
};
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Converts a full Indicator to a LightIndicator
|
||
/// </summary>
|
||
public static LightIndicator BaseToLight(IndicatorBase indicatorBase)
|
||
{
|
||
return new LightIndicator(indicatorBase.Name, indicatorBase.Type)
|
||
{
|
||
SignalType = indicatorBase.SignalType,
|
||
MinimumHistory = indicatorBase.MinimumHistory,
|
||
Period = indicatorBase.Period,
|
||
FastPeriods = indicatorBase.FastPeriods,
|
||
SlowPeriods = indicatorBase.SlowPeriods,
|
||
SignalPeriods = indicatorBase.SignalPeriods,
|
||
Multiplier = indicatorBase.Multiplier,
|
||
SmoothPeriods = indicatorBase.SmoothPeriods,
|
||
StochPeriods = indicatorBase.StochPeriods,
|
||
CyclePeriods = indicatorBase.CyclePeriods
|
||
};
|
||
}
|
||
|
||
public static IIndicator BuildIndicator(
|
||
IndicatorType type,
|
||
string name,
|
||
int? period = null,
|
||
int? fastPeriods = null,
|
||
int? slowPeriods = null,
|
||
int? signalPeriods = null,
|
||
double? multiplier = null,
|
||
int? stochPeriods = null,
|
||
int? smoothPeriods = null,
|
||
int? cyclePeriods = 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.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.Stc => SignalType.Signal,
|
||
IndicatorType.StDev => SignalType.Context,
|
||
IndicatorType.LaggingStc => SignalType.Signal,
|
||
IndicatorType.SuperTrendCrossEma => SignalType.Signal,
|
||
_ => throw new NotImplementedException(),
|
||
};
|
||
}
|
||
} |