From da48ee37ba36f30ac2e6bc95af1006a905051fd7 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Thu, 17 Jul 2025 15:27:08 +0700 Subject: [PATCH] Update genetic algo to select more indicators --- src/Managing.Application/GeneticService.cs | 78 +++++++++++++++++----- src/Managing.Bootstrap/WorkersBootstrap.cs | 1 + 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src/Managing.Application/GeneticService.cs b/src/Managing.Application/GeneticService.cs index dad691c..3e9dbe3 100644 --- a/src/Managing.Application/GeneticService.cs +++ b/src/Managing.Application/GeneticService.cs @@ -307,7 +307,7 @@ public class GeneticService : IGeneticService } // Create fitness function first - var fitness = new TradingBotFitness(_backtester, request); + var fitness = new TradingBotFitness(_backtester, request, _logger); // Create genetic algorithm with better configuration var ga = new GeneticAlgorithm( @@ -496,14 +496,16 @@ public class TradingBotChromosome : ChromosomeBase private readonly List _eligibleIndicators; private readonly double _maxTakeProfit; private readonly Random _random = new Random(); + private int[]? _indicatorSelectionPattern; // Gene structure: - // 0-2: Trading parameters (takeProfit, stopLoss, cooldownPeriod, maxLossStreak) - // 3-6: Indicator selection (up to 4 indicators) - // 7+: Indicator parameters (period, fastPeriods, etc.) + // 0-3: Trading parameters (takeProfit, stopLoss, cooldownPeriod, maxLossStreak) + // 4: Loopback period + // 5-4+N: Indicator selection (N = number of eligible indicators) + // 5+N+: Indicator parameters (period, fastPeriods, etc.) public TradingBotChromosome(List eligibleIndicators, double maxTakeProfit) - : base(4 + 1 + 4 + + : base(4 + 1 + eligibleIndicators.Count + eligibleIndicators.Count * 8) // Trading params + loopback + indicator selection + indicator params { _eligibleIndicators = eligibleIndicators; @@ -535,16 +537,22 @@ public class TradingBotChromosome : ChromosomeBase // LoopbackPeriod gene (always between 5 and 20) return new Gene(GetRandomIntInRange((5, 20))); } - else if (geneIndex < 9) + else if (geneIndex < 5 + _eligibleIndicators.Count) { // Indicator selection (0 = not selected, 1 = selected) - return new Gene(_random.Next(2)); + // Generate a random combination of up to 4 indicators + if (_indicatorSelectionPattern == null) + { + GenerateIndicatorSelectionPattern(); + } + + return new Gene(_indicatorSelectionPattern![geneIndex - 5]); } else { // Indicator parameters - var indicatorIndex = (geneIndex - 9) / 8; - var paramIndex = (geneIndex - 9) % 8; + var indicatorIndex = (geneIndex - (5 + _eligibleIndicators.Count)) / 8; + var paramIndex = (geneIndex - (5 + _eligibleIndicators.Count)) % 8; if (indicatorIndex < _eligibleIndicators.Count) { @@ -596,6 +604,11 @@ public class TradingBotChromosome : ChromosomeBase { var clone = new TradingBotChromosome(_eligibleIndicators, _maxTakeProfit); clone.ReplaceGenes(0, GetGenes()); + // Copy the selection pattern to maintain consistency + if (_indicatorSelectionPattern != null) + { + clone._indicatorSelectionPattern = (int[])_indicatorSelectionPattern.Clone(); + } return clone; } @@ -604,9 +617,10 @@ public class TradingBotChromosome : ChromosomeBase var selected = new List(); var genes = GetGenes(); - for (int i = 0; i < 4; i++) // Check first 4 indicator slots + // Check all indicator selection slots (genes 5 to 5+N-1 where N is number of eligible indicators) + for (int i = 0; i < _eligibleIndicators.Count; i++) { - if (genes[5 + i].Value.ToString() == "1" && i < _eligibleIndicators.Count) + if (genes[5 + i].Value.ToString() == "1") { var indicator = new GeneticIndicator { @@ -614,7 +628,7 @@ public class TradingBotChromosome : ChromosomeBase }; // Add parameters for this indicator - var baseIndex = 9 + i * 8; + var baseIndex = 5 + _eligibleIndicators.Count + i * 8; var paramName = GetParameterName(0); // period if (paramName != null && HasParameter(_eligibleIndicators[i], paramName)) { @@ -675,11 +689,8 @@ public class TradingBotChromosome : ChromosomeBase var genes = GetGenes(); var selectedIndicators = GetSelectedIndicators(); - // Ensure we have at least one indicator - if (!selectedIndicators.Any()) - { - selectedIndicators.Add(new GeneticIndicator { Type = _eligibleIndicators[0] }); - } + // The selection pattern ensures at least one indicator is always selected + // No need for fallback logic // Get take profit from chromosome (gene 0) var takeProfit = Convert.ToDouble(genes[0].Value); @@ -792,6 +803,26 @@ public class TradingBotChromosome : ChromosomeBase return GeneticService.IndicatorParamMapping.ContainsKey(indicator) && GeneticService.IndicatorParamMapping[indicator].Contains(paramName); } + + private void GenerateIndicatorSelectionPattern() + { + // Generate a random combination of up to 4 indicators + var selectedCount = _random.Next(1, Math.Min(5, _eligibleIndicators.Count + 1)); // 1 to 4 indicators + var selectedIndices = new HashSet(); + + // Randomly select indices + while (selectedIndices.Count < selectedCount) + { + selectedIndices.Add(_random.Next(_eligibleIndicators.Count)); + } + + // Create the selection pattern + _indicatorSelectionPattern = new int[_eligibleIndicators.Count]; + for (int i = 0; i < _eligibleIndicators.Count; i++) + { + _indicatorSelectionPattern[i] = selectedIndices.Contains(i) ? 1 : 0; + } + } } /// @@ -818,11 +849,13 @@ public class TradingBotFitness : IFitness private readonly IBacktester _backtester; private readonly GeneticRequest _request; private GeneticAlgorithm _geneticAlgorithm; + private readonly ILogger _logger; - public TradingBotFitness(IBacktester backtester, GeneticRequest request) + public TradingBotFitness(IBacktester backtester, GeneticRequest request, ILogger logger) { _backtester = backtester; _request = request; + _logger = logger; } public void SetGeneticAlgorithm(GeneticAlgorithm geneticAlgorithm) @@ -843,6 +876,15 @@ public class TradingBotFitness : IFitness // Get current generation number (default to 0 if not available) var currentGeneration = _geneticAlgorithm?.GenerationsNumber ?? 0; + // Log the indicators being tested (only in first few generations to avoid spam) + if (currentGeneration < 5) + { + var selectedIndicators = tradingBotChromosome.GetSelectedIndicators(); + var indicatorNames = string.Join(", ", selectedIndicators.Select(i => i.Type.ToString())); + _logger.LogDebug("Generation {Generation}: Testing indicators: {Indicators}", + currentGeneration, indicatorNames); + } + // Run backtest var backtest = _backtester.RunTradingBotBacktest( config, diff --git a/src/Managing.Bootstrap/WorkersBootstrap.cs b/src/Managing.Bootstrap/WorkersBootstrap.cs index b6e198c..348baf8 100644 --- a/src/Managing.Bootstrap/WorkersBootstrap.cs +++ b/src/Managing.Bootstrap/WorkersBootstrap.cs @@ -71,6 +71,7 @@ public static class WorkersBootstrap services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddTransient, OpenPositionCommandHandler>(); services.AddTransient, ClosePositionCommandHandler>();