using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using cAlgo; using cAlgo.API; using cAlgo.Modular; using Icarus.Optimiser.Core.Genetic; using Icarus.Optimiser.Modules; using Icarus.Utility; using Module = cAlgo.Modular.Module; namespace Icarus.Optimiser.Core.Modular { public class ModularChromosomeOperator : IChromosomeOperator { public Dictionary> BestPerformingModuleConfigs = new Dictionary>(); private List ModuleDefinitions { get; set; } private readonly Random m_Random = new Random(); public double DiversityFactor { get; set; } // Betwen 0 and 1, 1 meaning search through 100% of the search space, 0.1 is 10%. private readonly List m_ExitOnlyModules; private readonly List m_EssentialExitModules; public ModularChromosomeOperator() { var modules = new List { typeof(TrailingStopExitModule), // Only used as an exit module typeof(RsiModule), typeof(OutsideBollingerBands), typeof(OutsideDonchianChannel), typeof(OutsideFractalChaosBands), typeof(OutsideKeltnerChannels), typeof(MaCrossoverModule), typeof(FisherCrossoverModule), typeof(IchimokuFilterModule), typeof(IchimokuCrossoverUnderCloudModule), typeof(IchimokuCrossoverAboveCloudModule), typeof(WeeklyOpenFilterModule), typeof(WeeklyProjectionFilterModule), typeof(WeeklyProjectionCrossoverModule), typeof(MonthlyOpenFilterModule), typeof(MonthlyProjectionFilterModule), typeof(MonthlyProjectionCrossoverModule), typeof(MaFilterModule), typeof(HeikinAshiFilterModule), typeof(HeikinAshiCrossoverModule), typeof(FisherRsiModule), typeof(WickCandleModule), typeof(EngulfingBarModule), typeof(PriceActionMarubozu), typeof(HydraCurrencyStrength), typeof(IndecisionModule) }; ModuleDefinitions = modules.Select(ModuleDefinition.FromType).ToList(); m_ExitOnlyModules = ModuleDefinitions.Where(x => x.ModuleType.GetCustomAttributes(typeof(ExitModuleOnlyAttribute), true).Any()).ToList(); m_EssentialExitModules = ModuleDefinitions.Where(x => x.ModuleType.GetCustomAttributes(typeof(EssentialExitModuleAttribute), true).Any()).ToList(); } public List GetSeeds() { return new List { new ModularChromosome { EntryModules = new List { new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(RsiModule)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("periods", 1, 250), Name = "periods", Value = 116, }, new ParameterConfig { Interpolator = new EnumInterpolator("rsiMaType", typeof(MovingAverageType), "Simple", "Exponential"), Name = "rsiMaType", Value = MovingAverageType.Simple, }, new ParameterConfig { Interpolator = new IntInterpolator("oversold", 1, 60), Name = "oversold", Value = 37, }, new ParameterConfig { Interpolator = new IntInterpolator("overbought", 40, 100), Name = "overbought", Value = 95, } } } }, new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(OutsideBollingerBands)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("periods", 1, 50), Name = "periods", Value = 43, }, new ParameterConfig { Interpolator = new DoubleInterpolator("standardDeviations", 1, 3, 0.2), Name = "standardDeviations", Value = 2.4, }, new ParameterConfig { Interpolator = new EnumInterpolator("maType", typeof(MovingAverageType), "Simple", "Exponential"), Name = "maType", Value = MovingAverageType.Simple, } } } } }, ExitModules = new List { new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(IchimokuCrossoverAboveCloudModule)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("ichimokuTenkanSenPeriods", 1, 200), Name = "ichimokuTenkanSenPeriods", Value = 101, }, new ParameterConfig { Interpolator = new IntInterpolator("ichimokuKijunSenPeriods", 1, 200), Name = "ichimokuKijunSenPeriods", Value = 134, }, new ParameterConfig { Interpolator = new IntInterpolator("ichimokuSenkouSpanBPeriods", 1, 200), Name = "ichimokuSenkouSpanBPeriods", Value = 111, } } } }, new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(TrailingStopExitModule)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("stopLossPips", 30, 70), Name = "stopLossPips", Value = 33, } } } } } }, new ModularChromosome { EntryModules = new List { new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(RsiModule)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("periods", 1, 250), Name = "periods", Value = 92, }, new ParameterConfig { Interpolator = new EnumInterpolator("rsiMaType", typeof(MovingAverageType), "Simple", "Exponential"), Name = "rsiMaType", Value = MovingAverageType.Simple, }, new ParameterConfig { Interpolator = new IntInterpolator("oversold", 1, 60), Name = "oversold", Value = 36, }, new ParameterConfig { Interpolator = new IntInterpolator("overbought", 40, 100), Name = "overbought", Value = 99, } } } }, new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(OutsideBollingerBands)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("periods", 1, 50), Name = "periods", Value = 42, }, new ParameterConfig { Interpolator = new DoubleInterpolator("standardDeviations", 1, 3, 0.2), Name = "standardDeviations", Value = 2.8, }, new ParameterConfig { Interpolator = new EnumInterpolator("maType", typeof(MovingAverageType), "Simple", "Exponential"), Name = "maType", Value = MovingAverageType.Simple, } } } } }, ExitModules = new List { new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(HeikinAshiCrossoverModule)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("periods", 1, 200), Name = "periods", Value = 27, }, new ParameterConfig { Interpolator = new EnumInterpolator("maType", typeof(MovingAverageType), "Simple", "Exponential"), Name = "maType", Value = MovingAverageType.Simple, } } } }, new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(TrailingStopExitModule)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("stopLossPips", 30, 70), Name = "stopLossPips", Value = 33, } } } }, new ModuleConfig { ModuleDefinition = ModuleDefinitions.Single(x => x.ModuleType == typeof(FisherRsiModule)), RunConfig = new RunConfig { Parameters = new List { new ParameterConfig { Interpolator = new IntInterpolator("periods", 1, 200), Name = "periods", Value = 82, }, new ParameterConfig { Interpolator = new IntInterpolator("weightedPeriods", 1, 200), Name = "weightedPeriods", Value = 163, }, new ParameterConfig { Interpolator = new DoubleInterpolator("threshold", 0, 0.9, 0.1), Name = "threshold", Value = 0, } } } } } }, new ModularChromosome { EntryModules = GenerateSeedModules(false), ExitModules = GenerateSeedModules(true) } }; } public ModularChromosome GenerateNewChromosome() { return new ModularChromosome { EntryModules = GenerateRandomModules(false), ExitModules = GenerateRandomModules(true) }; } private List GenerateRandomModules(bool isExit) { var modulesToChooseFrom = ModuleDefinitions.Concat(ModuleDefinitions); // Include every module twice, let it use two of the same if its wants to if (!isExit) { modulesToChooseFrom = modulesToChooseFrom.Except(m_ExitOnlyModules); } Queue modules = new Queue(modulesToChooseFrom.Shuffle()); var chosenModules = Enumerable.Range(0, m_Random.Next(1, Math.Min(5, modules.Count))).Select(x => modules.Dequeue()).ToList(); // Stop more than five being used at once EnsureEssentialModulesAreChosen(chosenModules, isExit); return chosenModules.Select(GenerateConfig).ToList(); } private List GenerateSeedModules(bool isExit) { var modulesToChooseFrom = ModuleDefinitions; // Include every module twice, let it use two of the same if its wants to if (!isExit) { modulesToChooseFrom = modulesToChooseFrom.Except(m_ExitOnlyModules).ToList(); } Queue modules = new Queue(modulesToChooseFrom.Shuffle()); var chosenModules = Enumerable.Range(0, Math.Min(5, modules.Count)).Select(x => modules.Dequeue()).ToList(); EnsureEssentialModulesAreChosen(chosenModules, isExit); return chosenModules.Select(GenerateSeedConfig).ToList(); } public ModularChromosome Mutate(ModularChromosome chromosome) { ModularChromosome newChromosome = new ModularChromosome(); newChromosome.EntryModules = chromosome.EntryModules; newChromosome.ExitModules = chromosome.ExitModules; if (m_Random.NextDouble() > 0.5) { // Change entry modules if (m_Random.NextDouble() > 0.5) { // Change whole list newChromosome.EntryModules = GenerateRandomModules(false); } else { // Change individuals newChromosome.EntryModules = RandomiseModuleConfigs(chromosome.EntryModules, false); } } if (m_Random.NextDouble() > 0.5) { // Change exit modules if (m_Random.NextDouble() > 0.5) { // Change whole list newChromosome.ExitModules = GenerateRandomModules(true); } else { // Change individuals newChromosome.ExitModules = RandomiseModuleConfigs(chromosome.ExitModules, true); } } return newChromosome; } private List RandomiseModuleConfigs(List moduleConfigs, bool isExit) { if ((new StackTrace()).GetFrames().Length > 200) { Debugger.Break(); } var chosenModules = moduleConfigs.Select(x => { if (m_Random.NextDouble() > 0.5) { // Mutate this module return x.ModuleDefinition; } // Replace this module var modulesToChooseFrom = ModuleDefinitions; // Include every module twice, let it use two of the same if its wants to if (!isExit) { modulesToChooseFrom = modulesToChooseFrom.Except(m_ExitOnlyModules).ToList(); } return modulesToChooseFrom.Shuffle().First(); }).Distinct().ToList(); EnsureEssentialModulesAreChosen(chosenModules, isExit); return chosenModules.Select(GenerateConfig).ToList(); } private void EnsureEssentialModulesAreChosen(List chosenModules, bool isExit) { if (!isExit) { return; } foreach (var essentialExitModule in m_EssentialExitModules) { if (!chosenModules.Contains(essentialExitModule)) { chosenModules.Add(essentialExitModule); } } } public ModularChromosome CrossOver(ModularChromosome source1, ModularChromosome source2) { if ((new StackTrace()).GetFrames().Length > 200) { Debugger.Break(); } ModularChromosome newChromosome = new ModularChromosome(); if (m_Random.NextDouble() > 0.5) { // Merge entry modules if (m_Random.NextDouble() > 0.5) { // Take all newChromosome.EntryModules = source2.EntryModules; } else { // Take some var numChosenModules = (int) Math.Round((double)(source1.EntryModules.Count + source2.EntryModules.Count) / 2); newChromosome.EntryModules = source1.EntryModules.Concat(source2.EntryModules).Shuffle().Take(numChosenModules).Distinct().ToList(); } } else { // Take all from 1 newChromosome.EntryModules = source1.EntryModules; } if (m_Random.NextDouble() > 0.5) { // Merge exit modules if (m_Random.NextDouble() > 0.5) { // Take all newChromosome.ExitModules = source2.ExitModules; } else { // Take some var numChosenModules = (int)Math.Round((double)(source1.ExitModules.Count + source2.ExitModules.Count) / 2); newChromosome.ExitModules = source1.ExitModules.Concat(source2.ExitModules).Shuffle().Take(numChosenModules).Distinct().ToList(); foreach (var essentialExitModule in m_EssentialExitModules) { if (!newChromosome.ExitModules.Any(x => x.ModuleDefinition.Equals(essentialExitModule))) { newChromosome.ExitModules.Add(GenerateConfig(essentialExitModule)); } } } } else { // Take all from 1 newChromosome.ExitModules = source1.ExitModules; } return newChromosome; } private ModuleConfig GenerateConfig(ModuleDefinition moduleDefinition) { ModuleConfig successfulConfig = null; List bestConfigsForThisModule; if (BestPerformingModuleConfigs.TryGetValue(moduleDefinition.ModuleType, out bestConfigsForThisModule)) { successfulConfig = bestConfigsForThisModule.Shuffle().First().Clone(); } if (m_Random.NextDouble() > 0.8 && successfulConfig != null) // 20% of the time reuse a config from the best performing configs for this module { return successfulConfig; } ModuleConfig config = new ModuleConfig(); config.ModuleDefinition = moduleDefinition; config.RunConfig = new RunConfig(); foreach (var parameter in moduleDefinition.ModuleParameters) { var center = successfulConfig != null ? successfulConfig.RunConfig.Parameters.Single(x => x.Name.Equals(parameter.Name)).Value : parameter.Interpolator.CenterValue; config.RunConfig.Parameters.Add(new ParameterConfig { Name = parameter.Name, Interpolator = parameter.Interpolator, Value = parameter.Interpolator.GetRandomValue(center, DiversityFactor) }); } return config; } private static ModuleConfig GenerateSeedConfig(ModuleDefinition moduleDefinition) { ModuleConfig config = new ModuleConfig(); config.ModuleDefinition = moduleDefinition; config.RunConfig = new RunConfig(); foreach (var parameter in moduleDefinition.ModuleParameters) { config.RunConfig.Parameters.Add(new ParameterConfig { Name = parameter.Name, Interpolator = parameter.Interpolator, Value = parameter.DefaultValue }); } return config; } } }