From 9c4b5b7877b8bbf06079a5366cf8369b45658bdc Mon Sep 17 00:00:00 2001 From: cryptooda Date: Fri, 18 Jul 2025 05:15:51 +0700 Subject: [PATCH] Update genetics --- src/Managing.Application/GeneticService.cs | 105 ++++++++++++++++----- 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/src/Managing.Application/GeneticService.cs b/src/Managing.Application/GeneticService.cs index 3e9dbe3..4dd4381 100644 --- a/src/Managing.Application/GeneticService.cs +++ b/src/Managing.Application/GeneticService.cs @@ -319,7 +319,12 @@ public class GeneticService : IGeneticService { Termination = new GenerationNumberTermination(request.Generations), MutationProbability = (float)request.MutationRate, - CrossoverProbability = 0.7f // Fixed crossover rate as in frontend + CrossoverProbability = 0.7f, // Fixed crossover rate as in frontend + TaskExecutor = new ParallelTaskExecutor + { + MinThreads = 4, + MaxThreads = Environment.ProcessorCount + } }; // Set the genetic algorithm reference in the fitness function @@ -511,11 +516,8 @@ public class TradingBotChromosome : ChromosomeBase _eligibleIndicators = eligibleIndicators; _maxTakeProfit = maxTakeProfit; - // Initialize all genes - for (int i = 0; i < Length; i++) - { - ReplaceGene(i, GenerateGene(i)); - } + // Initialize genes with proper constraint handling + InitializeGenesWithConstraints(); } public override Gene GenerateGene(int geneIndex) @@ -526,7 +528,7 @@ public class TradingBotChromosome : ChromosomeBase return geneIndex switch { 0 => new Gene(GetRandomInRange((0.9, _maxTakeProfit))), // Take profit (0.9% to max TP) - 1 => new Gene(GetRandomInRange(GeneticService.ParameterRanges["stopLoss"])), // Stop loss + 1 => new Gene(GetRandomInRange(GeneticService.ParameterRanges["stopLoss"])), // Stop loss (will be constrained during initialization) 2 => new Gene(GetRandomIntInRange(GeneticService.ParameterRanges["cooldownPeriod"])), // Cooldown period 3 => new Gene(GetRandomIntInRange(GeneticService.ParameterRanges["maxLossStreak"])), // Max loss streak _ => new Gene(0) @@ -695,20 +697,15 @@ public class TradingBotChromosome : ChromosomeBase // Get take profit from chromosome (gene 0) var takeProfit = Convert.ToDouble(genes[0].Value); - // Calculate stop loss based on 1.1:1 risk-reward ratio (gene 1) + // Get stop loss from chromosome (gene 1) and enforce constraints at phenotype level var stopLoss = Convert.ToDouble(genes[1].Value); - - // Ensure minimum 1.1:1 risk-reward ratio and minimum 0.2% to cover fees - var minStopLossForRR = takeProfit / 1.1; - var minStopLossForFees = 0.2; // Minimum 0.2% to cover trading fees - var minStopLoss = Math.Max(minStopLossForRR, minStopLossForFees); + + // Enforce 1.1:1 risk-reward ratio constraint at phenotype level + var minStopLoss = Math.Max(0.2, takeProfit / 1.1); // Minimum 0.2% to cover fees var maxStopLoss = takeProfit - 0.1; // Ensure SL is less than TP with some buffer - - // Adjust stop loss if it doesn't meet the constraints - if (stopLoss > maxStopLoss || stopLoss < minStopLoss) - { - stopLoss = GetRandomInRange((minStopLoss, maxStopLoss)); - } + + // Clamp stop loss to valid range (this maintains genotype-phenotype mapping) + stopLoss = Math.Max(minStopLoss, Math.Min(maxStopLoss, stopLoss)); // Get loopback period from gene 4 var loopbackPeriod = Convert.ToInt32(genes[4].Value); @@ -823,6 +820,30 @@ public class TradingBotChromosome : ChromosomeBase _indicatorSelectionPattern[i] = selectedIndices.Contains(i) ? 1 : 0; } } + + /// + /// Initializes genes with proper constraint handling for take profit and stop loss + /// + private void InitializeGenesWithConstraints() + { + // Generate take profit first (gene 0) + var takeProfit = GetRandomInRange((0.9, _maxTakeProfit)); + ReplaceGene(0, new Gene(takeProfit)); + + // Generate stop loss with constraints based on take profit (gene 1) + var minStopLoss = Math.Max(0.2, takeProfit / 1.1); // Minimum 0.2% to cover fees + var maxStopLoss = takeProfit - 0.1; // Ensure SL is less than TP with some buffer + var stopLoss = GetRandomInRange((minStopLoss, maxStopLoss)); + ReplaceGene(1, new Gene(stopLoss)); + + // Initialize remaining genes normally + for (int i = 2; i < Length; i++) + { + ReplaceGene(i, GenerateGene(i)); + } + } + + } /// @@ -917,8 +938,48 @@ public class TradingBotFitness : IFitness if (backtest == null || backtest.Statistics == null) return 0.1; - // Use the comprehensive backtest score directly as fitness - // The BacktestScorer already includes all important metrics with proper weighting - return backtest.Score; + // Calculate base fitness from backtest score + var baseFitness = backtest.Score; + + // Apply constraint penalty if needed (this will guide the GA toward valid solutions) + var constraintPenalty = CalculateConstraintPenalty(config); + + // Return penalized fitness (constraint violations reduce fitness) + return baseFitness * constraintPenalty; + } + + /// + /// Calculates a penalty factor for constraint violations (1.0 = no penalty, < 1.0 = penalty) + /// + private double CalculateConstraintPenalty(TradingBotConfig config) + { + var takeProfit = Convert.ToDouble(config.MoneyManagement.TakeProfit); + var stopLoss = Convert.ToDouble(config.MoneyManagement.StopLoss); + + // Check 1.1:1 risk-reward ratio constraint + var minStopLoss = Math.Max(0.2, takeProfit / 1.1); + var maxStopLoss = takeProfit - 0.1; + + // If stop loss is within valid range, no penalty + if (stopLoss >= minStopLoss && stopLoss <= maxStopLoss) + { + return 1.0; + } + + // Calculate penalty based on how far outside the valid range the stop loss is + var violation = 0.0; + if (stopLoss < minStopLoss) + { + violation = (minStopLoss - stopLoss) / minStopLoss; + } + else if (stopLoss > maxStopLoss) + { + violation = (stopLoss - maxStopLoss) / maxStopLoss; + } + + // Apply penalty: 0.5 for severe violations, 0.8 for minor violations + var penalty = Math.Max(0.5, 1.0 - (violation * 0.5)); + + return penalty; } } \ No newline at end of file