Add progression and new method of selection/mutation/crossover

This commit is contained in:
2025-07-11 23:38:22 +07:00
parent b21b0a342c
commit cebbeff887
13 changed files with 444 additions and 69 deletions

View File

@@ -21,6 +21,7 @@ public class GeneticService : IGeneticService
private readonly IGeneticRepository _geneticRepository;
private readonly IBacktester _backtester;
private readonly ILogger<GeneticService> _logger;
private readonly IMessengerService _messengerService;
// Predefined parameter ranges for each indicator (matching backtestGenetic.tsx)
public static readonly Dictionary<string, (double min, double max)> ParameterRanges = new()
@@ -186,11 +187,13 @@ public class GeneticService : IGeneticService
public GeneticService(
IGeneticRepository geneticRepository,
IBacktester backtester,
ILogger<GeneticService> logger)
ILogger<GeneticService> logger,
IMessengerService messengerService)
{
_geneticRepository = geneticRepository;
_backtester = backtester;
_logger = logger;
_messengerService = messengerService;
}
public GeneticRequest CreateGeneticRequest(
@@ -204,6 +207,8 @@ public class GeneticService : IGeneticService
int generations,
double mutationRate,
GeneticSelectionMethod selectionMethod,
GeneticCrossoverMethod crossoverMethod,
GeneticMutationMethod mutationMethod,
int elitismPercentage,
double maxTakeProfit,
List<IndicatorType> eligibleIndicators)
@@ -220,6 +225,8 @@ public class GeneticService : IGeneticService
Generations = generations,
MutationRate = mutationRate,
SelectionMethod = selectionMethod,
CrossoverMethod = crossoverMethod,
MutationMethod = mutationMethod,
ElitismPercentage = elitismPercentage,
MaxTakeProfit = maxTakeProfit,
EligibleIndicators = eligibleIndicators,
@@ -307,8 +314,8 @@ public class GeneticService : IGeneticService
population,
fitness,
GetSelection(request.SelectionMethod),
new UniformCrossover(),
GetMutation(request.MutationRate))
GetCrossover(request.CrossoverMethod),
GetMutation(request.MutationMethod))
{
Termination = new GenerationNumberTermination(request.Generations),
MutationProbability = (float)request.MutationRate,
@@ -330,28 +337,25 @@ public class GeneticService : IGeneticService
{
generationCount = ga.GenerationsNumber;
// Update progress every 5 generations
if (generationCount % 5 == 0)
// Update progress every generation
var bestFitness = ga.BestChromosome?.Fitness ?? 0;
request.CurrentGeneration = generationCount;
request.BestFitnessSoFar = bestFitness;
if (ga.BestChromosome is TradingBotChromosome bestChromosome)
{
var bestFitness = ga.BestChromosome?.Fitness ?? 0;
request.CurrentGeneration = generationCount;
request.BestFitnessSoFar = bestFitness;
if (ga.BestChromosome is TradingBotChromosome bestChromosome)
var genes = bestChromosome.GetGenes();
var geneValues = genes.Select(g =>
{
var genes = bestChromosome.GetGenes();
var geneValues = genes.Select(g =>
{
if (g.Value is double doubleValue) return doubleValue;
if (g.Value is int intValue) return (double)intValue;
return Convert.ToDouble(g.Value.ToString());
}).ToArray();
request.BestChromosome = JsonSerializer.Serialize(geneValues);
}
UpdateGeneticRequest(request);
if (g.Value is double doubleValue) return doubleValue;
if (g.Value is int intValue) return (double)intValue;
return Convert.ToDouble(g.Value.ToString());
}).ToArray();
request.BestChromosome = JsonSerializer.Serialize(geneValues);
}
UpdateGeneticRequest(request);
// Check for cancellation
if (cancellationToken.IsCancellationRequested)
{
@@ -403,6 +407,16 @@ public class GeneticService : IGeneticService
UpdateGeneticRequest(request);
// Send notification about the completed genetic algorithm
try
{
await _messengerService.SendGeneticAlgorithmNotification(request, bestFitness, bestChromosome);
}
catch (Exception notificationEx)
{
_logger.LogWarning(notificationEx, "Failed to send genetic algorithm notification for request {RequestId}", request.RequestId);
}
return new GeneticAlgorithmResult
{
BestFitness = bestFitness,
@@ -429,16 +443,48 @@ public class GeneticService : IGeneticService
{
return selectionMethod switch
{
GeneticSelectionMethod.Tournament => new TournamentSelection(),
GeneticSelectionMethod.Elite => new EliteSelection(),
GeneticSelectionMethod.Roulette => new RouletteWheelSelection(),
GeneticSelectionMethod.FitnessWeighted => new RankSelection(), // Use rank selection as approximation
GeneticSelectionMethod.StochasticUniversalSampling => new StochasticUniversalSamplingSelection(),
GeneticSelectionMethod.Tournament => new TournamentSelection(),
GeneticSelectionMethod.Truncation => new TruncationSelection(),
_ => new TournamentSelection()
};
}
private IMutation GetMutation(double mutationRate)
private ICrossover GetCrossover(GeneticCrossoverMethod crossoverMethod)
{
return new UniformMutation(true);
return crossoverMethod switch
{
GeneticCrossoverMethod.AlternatingPosition => new AlternatingPositionCrossover(),
GeneticCrossoverMethod.CutAndSplice => new CutAndSpliceCrossover(),
GeneticCrossoverMethod.Cycle => new CycleCrossover(),
GeneticCrossoverMethod.OnePoint => new OnePointCrossover(),
GeneticCrossoverMethod.OrderBased => new OrderBasedCrossover(),
GeneticCrossoverMethod.Ordered => new OrderedCrossover(),
GeneticCrossoverMethod.PartiallyMapped => new PartiallyMappedCrossover(),
GeneticCrossoverMethod.PositionBased => new PositionBasedCrossover(),
GeneticCrossoverMethod.ThreeParent => new ThreeParentCrossover(),
GeneticCrossoverMethod.TwoPoint => new TwoPointCrossover(),
GeneticCrossoverMethod.Uniform => new UniformCrossover(),
GeneticCrossoverMethod.VotingRecombination => new VotingRecombinationCrossover(),
_ => new UniformCrossover()
};
}
private IMutation GetMutation(GeneticMutationMethod mutationMethod)
{
return mutationMethod switch
{
GeneticMutationMethod.Displacement => new DisplacementMutation(),
GeneticMutationMethod.FlipBit => new FlipBitMutation(),
GeneticMutationMethod.Insertion => new InsertionMutation(),
GeneticMutationMethod.PartialShuffle => new PartialShuffleMutation(),
GeneticMutationMethod.ReverseSequence => new ReverseSequenceMutation(),
GeneticMutationMethod.Twors => new TworsMutation(),
GeneticMutationMethod.Uniform => new UniformMutation(true),
_ => new UniformMutation(true)
};
}
}

View File

@@ -176,6 +176,19 @@ public class MessengerService : IMessengerService
}
}
public async Task SendGeneticAlgorithmNotification(GeneticRequest request, double bestFitness, object? bestChromosome)
{
try
{
var message = BuildGeneticAlgorithmMessage(request, bestFitness, bestChromosome);
await _webhookService.SendMessage(message, "2775292276");
}
catch (Exception e)
{
Console.WriteLine($"Failed to send genetic algorithm notification: {e.Message}");
}
}
private string BuildBacktestConfigMessage(Backtest backtest)
{
var config = backtest.Config;
@@ -263,4 +276,42 @@ public class MessengerService : IMessengerService
return message;
}
private string BuildGeneticAlgorithmMessage(GeneticRequest request, double bestFitness, object? bestChromosome)
{
var duration = request.CompletedAt.HasValue
? request.CompletedAt.Value - request.CreatedAt
: TimeSpan.Zero;
var indicators = request.EligibleIndicators.Any()
? string.Join(", ", request.EligibleIndicators.Select(i => i.ToString()))
: "N/A";
if (request.EligibleIndicators.Count > 5)
{
indicators += $" (+{request.EligibleIndicators.Count - 5} more)";
}
var message = $"🧬 Genetic Algorithm Completed! 🧬\n\n" +
$"🔹 Symbol: {request.Ticker}\n" +
$"⏱️ Timeframe: {request.Timeframe}\n" +
$"👥 Population: {request.PopulationSize}\n" +
$"🔄 Generations: {request.Generations}\n" +
$"🎯 Selection: {request.SelectionMethod}\n" +
$"🔀 Crossover: {request.CrossoverMethod}\n" +
$"🧬 Mutation: {request.MutationMethod}\n" +
$"🧩 Indicators: {indicators}\n" +
$"📅 Period: {request.StartDate:yyyy-MM-dd} to {request.EndDate:yyyy-MM-dd}\n" +
$"📈 Results:\n" +
$"⭐ Best Fitness: {bestFitness:F4}\n" +
$"⏱️ Duration: {duration.TotalMinutes:F1} minutes\n" +
$"🆔 Request ID: {request.RequestId[..8]}...";
if (request.BestFitnessSoFar.HasValue && request.BestFitnessSoFar.Value > 0)
{
message += $"\n🏆 Best Fitness So Far: {request.BestFitnessSoFar.Value:F4}";
}
return message;
}
}