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