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

@@ -314,6 +314,8 @@ public class BacktestController : BaseController
request.Generations, request.Generations,
request.MutationRate, request.MutationRate,
request.SelectionMethod, request.SelectionMethod,
request.CrossoverMethod,
request.MutationMethod,
request.ElitismPercentage, request.ElitismPercentage,
request.MaxTakeProfit, request.MaxTakeProfit,
request.EligibleIndicators); request.EligibleIndicators);

View File

@@ -52,6 +52,16 @@ public class RunGeneticRequest
/// </summary> /// </summary>
public GeneticSelectionMethod SelectionMethod { get; set; } public GeneticSelectionMethod SelectionMethod { get; set; }
/// <summary>
/// The crossover method to use
/// </summary>
public GeneticCrossoverMethod CrossoverMethod { get; set; }
/// <summary>
/// The mutation method to use
/// </summary>
public GeneticMutationMethod MutationMethod { get; set; }
/// <summary> /// <summary>
/// The percentage of elite individuals to preserve (1-50) /// The percentage of elite individuals to preserve (1-50)
/// </summary> /// </summary>

View File

@@ -22,6 +22,8 @@ public interface IGeneticService
/// <param name="generations">The number of generations to evolve</param> /// <param name="generations">The number of generations to evolve</param>
/// <param name="mutationRate">The mutation rate (0.0 - 1.0)</param> /// <param name="mutationRate">The mutation rate (0.0 - 1.0)</param>
/// <param name="selectionMethod">The selection method to use</param> /// <param name="selectionMethod">The selection method to use</param>
/// <param name="crossoverMethod">The crossover method to use</param>
/// <param name="mutationMethod">The mutation method to use</param>
/// <param name="elitismPercentage">The percentage of elite individuals to preserve</param> /// <param name="elitismPercentage">The percentage of elite individuals to preserve</param>
/// <param name="maxTakeProfit">The maximum take profit percentage</param> /// <param name="maxTakeProfit">The maximum take profit percentage</param>
/// <param name="eligibleIndicators">The list of eligible indicators</param> /// <param name="eligibleIndicators">The list of eligible indicators</param>
@@ -37,6 +39,8 @@ public interface IGeneticService
int generations, int generations,
double mutationRate, double mutationRate,
GeneticSelectionMethod selectionMethod, GeneticSelectionMethod selectionMethod,
GeneticCrossoverMethod crossoverMethod,
GeneticMutationMethod mutationMethod,
int elitismPercentage, int elitismPercentage,
double maxTakeProfit, double maxTakeProfit,
List<IndicatorType> eligibleIndicators); List<IndicatorType> eligibleIndicators);

View File

@@ -24,4 +24,5 @@ public interface IMessengerService
Task SendNewTopFundingRate(FundingRate newRate); Task SendNewTopFundingRate(FundingRate newRate);
Task SendFundingRateUpdate(FundingRate oldRate, FundingRate newRate); Task SendFundingRateUpdate(FundingRate oldRate, FundingRate newRate);
Task SendBacktestNotification(Backtest backtest); Task SendBacktestNotification(Backtest backtest);
Task SendGeneticAlgorithmNotification(GeneticRequest request, double bestFitness, object? bestChromosome);
} }

View File

@@ -21,6 +21,7 @@ public class GeneticService : IGeneticService
private readonly IGeneticRepository _geneticRepository; private readonly IGeneticRepository _geneticRepository;
private readonly IBacktester _backtester; private readonly IBacktester _backtester;
private readonly ILogger<GeneticService> _logger; private readonly ILogger<GeneticService> _logger;
private readonly IMessengerService _messengerService;
// Predefined parameter ranges for each indicator (matching backtestGenetic.tsx) // Predefined parameter ranges for each indicator (matching backtestGenetic.tsx)
public static readonly Dictionary<string, (double min, double max)> ParameterRanges = new() public static readonly Dictionary<string, (double min, double max)> ParameterRanges = new()
@@ -186,11 +187,13 @@ public class GeneticService : IGeneticService
public GeneticService( public GeneticService(
IGeneticRepository geneticRepository, IGeneticRepository geneticRepository,
IBacktester backtester, IBacktester backtester,
ILogger<GeneticService> logger) ILogger<GeneticService> logger,
IMessengerService messengerService)
{ {
_geneticRepository = geneticRepository; _geneticRepository = geneticRepository;
_backtester = backtester; _backtester = backtester;
_logger = logger; _logger = logger;
_messengerService = messengerService;
} }
public GeneticRequest CreateGeneticRequest( public GeneticRequest CreateGeneticRequest(
@@ -204,6 +207,8 @@ public class GeneticService : IGeneticService
int generations, int generations,
double mutationRate, double mutationRate,
GeneticSelectionMethod selectionMethod, GeneticSelectionMethod selectionMethod,
GeneticCrossoverMethod crossoverMethod,
GeneticMutationMethod mutationMethod,
int elitismPercentage, int elitismPercentage,
double maxTakeProfit, double maxTakeProfit,
List<IndicatorType> eligibleIndicators) List<IndicatorType> eligibleIndicators)
@@ -220,6 +225,8 @@ public class GeneticService : IGeneticService
Generations = generations, Generations = generations,
MutationRate = mutationRate, MutationRate = mutationRate,
SelectionMethod = selectionMethod, SelectionMethod = selectionMethod,
CrossoverMethod = crossoverMethod,
MutationMethod = mutationMethod,
ElitismPercentage = elitismPercentage, ElitismPercentage = elitismPercentage,
MaxTakeProfit = maxTakeProfit, MaxTakeProfit = maxTakeProfit,
EligibleIndicators = eligibleIndicators, EligibleIndicators = eligibleIndicators,
@@ -307,8 +314,8 @@ public class GeneticService : IGeneticService
population, population,
fitness, fitness,
GetSelection(request.SelectionMethod), GetSelection(request.SelectionMethod),
new UniformCrossover(), GetCrossover(request.CrossoverMethod),
GetMutation(request.MutationRate)) GetMutation(request.MutationMethod))
{ {
Termination = new GenerationNumberTermination(request.Generations), Termination = new GenerationNumberTermination(request.Generations),
MutationProbability = (float)request.MutationRate, MutationProbability = (float)request.MutationRate,
@@ -330,9 +337,7 @@ public class GeneticService : IGeneticService
{ {
generationCount = ga.GenerationsNumber; generationCount = ga.GenerationsNumber;
// Update progress every 5 generations // Update progress every generation
if (generationCount % 5 == 0)
{
var bestFitness = ga.BestChromosome?.Fitness ?? 0; var bestFitness = ga.BestChromosome?.Fitness ?? 0;
request.CurrentGeneration = generationCount; request.CurrentGeneration = generationCount;
request.BestFitnessSoFar = bestFitness; request.BestFitnessSoFar = bestFitness;
@@ -350,7 +355,6 @@ public class GeneticService : IGeneticService
} }
UpdateGeneticRequest(request); UpdateGeneticRequest(request);
}
// Check for cancellation // Check for cancellation
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
@@ -403,6 +407,16 @@ public class GeneticService : IGeneticService
UpdateGeneticRequest(request); 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 return new GeneticAlgorithmResult
{ {
BestFitness = bestFitness, BestFitness = bestFitness,
@@ -429,16 +443,48 @@ public class GeneticService : IGeneticService
{ {
return selectionMethod switch return selectionMethod switch
{ {
GeneticSelectionMethod.Tournament => new TournamentSelection(), GeneticSelectionMethod.Elite => new EliteSelection(),
GeneticSelectionMethod.Roulette => new RouletteWheelSelection(), 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() _ => 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) private string BuildBacktestConfigMessage(Backtest backtest)
{ {
var config = backtest.Config; var config = backtest.Config;
@@ -263,4 +276,42 @@ public class MessengerService : IMessengerService
return message; 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;
}
} }

View File

@@ -422,8 +422,43 @@ public static class Enums
/// </summary> /// </summary>
public enum GeneticSelectionMethod public enum GeneticSelectionMethod
{ {
Tournament, Elite,
Roulette, Roulette,
FitnessWeighted StochasticUniversalSampling,
Tournament,
Truncation
}
/// <summary>
/// Crossover methods for genetic algorithm optimization
/// </summary>
public enum GeneticCrossoverMethod
{
AlternatingPosition,
CutAndSplice,
Cycle,
OnePoint,
OrderBased,
Ordered,
PartiallyMapped,
PositionBased,
ThreeParent,
TwoPoint,
Uniform,
VotingRecombination
}
/// <summary>
/// Mutation methods for genetic algorithm optimization
/// </summary>
public enum GeneticMutationMethod
{
Displacement,
FlipBit,
Insertion,
PartialShuffle,
ReverseSequence,
Twors,
Uniform
} }
} }

View File

@@ -112,6 +112,18 @@ public class GeneticRequest
[Required] [Required]
public GeneticSelectionMethod SelectionMethod { get; set; } = GeneticSelectionMethod.Tournament; public GeneticSelectionMethod SelectionMethod { get; set; } = GeneticSelectionMethod.Tournament;
/// <summary>
/// The crossover method to use
/// </summary>
[Required]
public GeneticCrossoverMethod CrossoverMethod { get; set; } = GeneticCrossoverMethod.Uniform;
/// <summary>
/// The mutation method to use
/// </summary>
[Required]
public GeneticMutationMethod MutationMethod { get; set; } = GeneticMutationMethod.Uniform;
/// <summary> /// <summary>
/// The percentage of elite individuals to preserve (1-50) /// The percentage of elite individuals to preserve (1-50)
/// </summary> /// </summary>

View File

@@ -20,6 +20,8 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
public int Generations { get; set; } public int Generations { get; set; }
public double MutationRate { get; set; } public double MutationRate { get; set; }
public GeneticSelectionMethod SelectionMethod { get; set; } public GeneticSelectionMethod SelectionMethod { get; set; }
public GeneticCrossoverMethod CrossoverMethod { get; set; }
public GeneticMutationMethod MutationMethod { get; set; }
public int ElitismPercentage { get; set; } public int ElitismPercentage { get; set; }
public double MaxTakeProfit { get; set; } public double MaxTakeProfit { get; set; }
public List<IndicatorType> EligibleIndicators { get; set; } = new(); public List<IndicatorType> EligibleIndicators { get; set; } = new();

View File

@@ -209,13 +209,18 @@ public static class MongoMappers
Generations = dto.Generations, Generations = dto.Generations,
MutationRate = dto.MutationRate, MutationRate = dto.MutationRate,
SelectionMethod = dto.SelectionMethod, SelectionMethod = dto.SelectionMethod,
CrossoverMethod = dto.CrossoverMethod,
MutationMethod = dto.MutationMethod,
ElitismPercentage = dto.ElitismPercentage, ElitismPercentage = dto.ElitismPercentage,
MaxTakeProfit = dto.MaxTakeProfit, MaxTakeProfit = dto.MaxTakeProfit,
EligibleIndicators = dto.EligibleIndicators, EligibleIndicators = dto.EligibleIndicators,
BestFitness = dto.BestFitness, BestFitness = dto.BestFitness,
BestIndividual = dto.BestIndividual, BestIndividual = dto.BestIndividual,
ErrorMessage = dto.ErrorMessage, ErrorMessage = dto.ErrorMessage,
ProgressInfo = dto.ProgressInfo ProgressInfo = dto.ProgressInfo,
BestChromosome = dto.BestChromosome,
BestFitnessSoFar = dto.BestFitnessSoFar,
CurrentGeneration = dto.CurrentGeneration
}; };
} }
@@ -239,13 +244,18 @@ public static class MongoMappers
Generations = geneticRequest.Generations, Generations = geneticRequest.Generations,
MutationRate = geneticRequest.MutationRate, MutationRate = geneticRequest.MutationRate,
SelectionMethod = geneticRequest.SelectionMethod, SelectionMethod = geneticRequest.SelectionMethod,
CrossoverMethod = geneticRequest.CrossoverMethod,
MutationMethod = geneticRequest.MutationMethod,
ElitismPercentage = geneticRequest.ElitismPercentage, ElitismPercentage = geneticRequest.ElitismPercentage,
MaxTakeProfit = geneticRequest.MaxTakeProfit, MaxTakeProfit = geneticRequest.MaxTakeProfit,
EligibleIndicators = geneticRequest.EligibleIndicators, EligibleIndicators = geneticRequest.EligibleIndicators,
BestFitness = geneticRequest.BestFitness, BestFitness = geneticRequest.BestFitness,
BestIndividual = geneticRequest.BestIndividual, BestIndividual = geneticRequest.BestIndividual,
ErrorMessage = geneticRequest.ErrorMessage, ErrorMessage = geneticRequest.ErrorMessage,
ProgressInfo = geneticRequest.ProgressInfo ProgressInfo = geneticRequest.ProgressInfo,
BestChromosome = geneticRequest.BestChromosome,
BestFitnessSoFar = geneticRequest.BestFitnessSoFar,
CurrentGeneration = geneticRequest.CurrentGeneration
}; };
} }

View File

@@ -3771,6 +3771,8 @@ export interface GeneticRequest {
generations: number; generations: number;
mutationRate: number; mutationRate: number;
selectionMethod: GeneticSelectionMethod; selectionMethod: GeneticSelectionMethod;
crossoverMethod: GeneticCrossoverMethod;
mutationMethod: GeneticMutationMethod;
elitismPercentage: number; elitismPercentage: number;
maxTakeProfit: number; maxTakeProfit: number;
eligibleIndicators: IndicatorType[]; eligibleIndicators: IndicatorType[];
@@ -3793,9 +3795,36 @@ export enum GeneticRequestStatus {
} }
export enum GeneticSelectionMethod { export enum GeneticSelectionMethod {
Tournament = "Tournament", Elite = "Elite",
Roulette = "Roulette", Roulette = "Roulette",
FitnessWeighted = "FitnessWeighted", StochasticUniversalSampling = "StochasticUniversalSampling",
Tournament = "Tournament",
Truncation = "Truncation",
}
export enum GeneticCrossoverMethod {
AlternatingPosition = "AlternatingPosition",
CutAndSplice = "CutAndSplice",
Cycle = "Cycle",
OnePoint = "OnePoint",
OrderBased = "OrderBased",
Ordered = "Ordered",
PartiallyMapped = "PartiallyMapped",
PositionBased = "PositionBased",
ThreeParent = "ThreeParent",
TwoPoint = "TwoPoint",
Uniform = "Uniform",
VotingRecombination = "VotingRecombination",
}
export enum GeneticMutationMethod {
Displacement = "Displacement",
FlipBit = "FlipBit",
Insertion = "Insertion",
PartialShuffle = "PartialShuffle",
ReverseSequence = "ReverseSequence",
Twors = "Twors",
Uniform = "Uniform",
} }
export interface RunGeneticRequest { export interface RunGeneticRequest {
@@ -3808,6 +3837,8 @@ export interface RunGeneticRequest {
generations?: number; generations?: number;
mutationRate?: number; mutationRate?: number;
selectionMethod?: GeneticSelectionMethod; selectionMethod?: GeneticSelectionMethod;
crossoverMethod?: GeneticCrossoverMethod;
mutationMethod?: GeneticMutationMethod;
elitismPercentage?: number; elitismPercentage?: number;
maxTakeProfit?: number; maxTakeProfit?: number;
eligibleIndicators?: IndicatorType[] | null; eligibleIndicators?: IndicatorType[] | null;

View File

@@ -674,6 +674,8 @@ export interface GeneticRequest {
generations: number; generations: number;
mutationRate: number; mutationRate: number;
selectionMethod: GeneticSelectionMethod; selectionMethod: GeneticSelectionMethod;
crossoverMethod: GeneticCrossoverMethod;
mutationMethod: GeneticMutationMethod;
elitismPercentage: number; elitismPercentage: number;
maxTakeProfit: number; maxTakeProfit: number;
eligibleIndicators: IndicatorType[]; eligibleIndicators: IndicatorType[];
@@ -696,9 +698,36 @@ export enum GeneticRequestStatus {
} }
export enum GeneticSelectionMethod { export enum GeneticSelectionMethod {
Tournament = "Tournament", Elite = "Elite",
Roulette = "Roulette", Roulette = "Roulette",
FitnessWeighted = "FitnessWeighted", StochasticUniversalSampling = "StochasticUniversalSampling",
Tournament = "Tournament",
Truncation = "Truncation",
}
export enum GeneticCrossoverMethod {
AlternatingPosition = "AlternatingPosition",
CutAndSplice = "CutAndSplice",
Cycle = "Cycle",
OnePoint = "OnePoint",
OrderBased = "OrderBased",
Ordered = "Ordered",
PartiallyMapped = "PartiallyMapped",
PositionBased = "PositionBased",
ThreeParent = "ThreeParent",
TwoPoint = "TwoPoint",
Uniform = "Uniform",
VotingRecombination = "VotingRecombination",
}
export enum GeneticMutationMethod {
Displacement = "Displacement",
FlipBit = "FlipBit",
Insertion = "Insertion",
PartialShuffle = "PartialShuffle",
ReverseSequence = "ReverseSequence",
Twors = "Twors",
Uniform = "Uniform",
} }
export interface RunGeneticRequest { export interface RunGeneticRequest {
@@ -711,6 +740,8 @@ export interface RunGeneticRequest {
generations?: number; generations?: number;
mutationRate?: number; mutationRate?: number;
selectionMethod?: GeneticSelectionMethod; selectionMethod?: GeneticSelectionMethod;
crossoverMethod?: GeneticCrossoverMethod;
mutationMethod?: GeneticMutationMethod;
elitismPercentage?: number; elitismPercentage?: number;
maxTakeProfit?: number; maxTakeProfit?: number;
eligibleIndicators?: IndicatorType[] | null; eligibleIndicators?: IndicatorType[] | null;

View File

@@ -6,6 +6,8 @@ import useApiUrlStore from '../../app/store/apiStore'
import { import {
type Backtest, type Backtest,
BacktestClient, BacktestClient,
GeneticCrossoverMethod,
GeneticMutationMethod,
type GeneticRequest, type GeneticRequest,
GeneticSelectionMethod, GeneticSelectionMethod,
IndicatorType, IndicatorType,
@@ -52,6 +54,8 @@ interface GeneticBundleFormData {
generations: number generations: number
mutationRate: number mutationRate: number
selectionMethod: GeneticSelectionMethod selectionMethod: GeneticSelectionMethod
crossoverMethod: GeneticCrossoverMethod
mutationMethod: GeneticMutationMethod
elitismPercentage: number elitismPercentage: number
maxTakeProfit: number maxTakeProfit: number
eligibleIndicators: IndicatorType[] eligibleIndicators: IndicatorType[]
@@ -70,6 +74,7 @@ const BacktestGeneticBundle: React.FC = () => {
const [isViewModalOpen, setIsViewModalOpen] = useState(false) const [isViewModalOpen, setIsViewModalOpen] = useState(false)
const [backtests, setBacktests] = useState<Backtest[]>([]) const [backtests, setBacktests] = useState<Backtest[]>([])
const [isLoadingBacktests, setIsLoadingBacktests] = useState(false) const [isLoadingBacktests, setIsLoadingBacktests] = useState(false)
const [isFormCollapsed, setIsFormCollapsed] = useState(false)
// Form setup // Form setup
const {register, handleSubmit, watch, setValue, formState: {errors}} = useForm<GeneticBundleFormData>({ const {register, handleSubmit, watch, setValue, formState: {errors}} = useForm<GeneticBundleFormData>({
@@ -83,6 +88,8 @@ const BacktestGeneticBundle: React.FC = () => {
generations: 5, generations: 5,
mutationRate: 0.3, mutationRate: 0.3,
selectionMethod: GeneticSelectionMethod.Tournament, selectionMethod: GeneticSelectionMethod.Tournament,
crossoverMethod: GeneticCrossoverMethod.Uniform,
mutationMethod: GeneticMutationMethod.Uniform,
elitismPercentage: 10, elitismPercentage: 10,
maxTakeProfit: 2.0, maxTakeProfit: 2.0,
eligibleIndicators: ALL_INDICATORS, eligibleIndicators: ALL_INDICATORS,
@@ -139,6 +146,8 @@ const BacktestGeneticBundle: React.FC = () => {
generations: data.generations, generations: data.generations,
mutationRate: data.mutationRate, mutationRate: data.mutationRate,
selectionMethod: data.selectionMethod, selectionMethod: data.selectionMethod,
crossoverMethod: data.crossoverMethod,
mutationMethod: data.mutationMethod,
elitismPercentage: data.elitismPercentage, elitismPercentage: data.elitismPercentage,
maxTakeProfit: data.maxTakeProfit, maxTakeProfit: data.maxTakeProfit,
eligibleIndicators: selectedIndicators, eligibleIndicators: selectedIndicators,
@@ -161,6 +170,8 @@ const BacktestGeneticBundle: React.FC = () => {
setValue('generations', 5) setValue('generations', 5)
setValue('mutationRate', 0.3) setValue('mutationRate', 0.3)
setValue('selectionMethod', GeneticSelectionMethod.Tournament) setValue('selectionMethod', GeneticSelectionMethod.Tournament)
setValue('crossoverMethod', GeneticCrossoverMethod.Uniform)
setValue('mutationMethod', GeneticMutationMethod.Uniform)
setValue('elitismPercentage', 10) setValue('elitismPercentage', 10)
setValue('maxTakeProfit', 2.0) setValue('maxTakeProfit', 2.0)
setSelectedIndicators(ALL_INDICATORS) setSelectedIndicators(ALL_INDICATORS)
@@ -228,13 +239,6 @@ const BacktestGeneticBundle: React.FC = () => {
// Table columns for genetic requests // Table columns for genetic requests
const geneticRequestsColumns = useMemo(() => [ const geneticRequestsColumns = useMemo(() => [
{
Header: 'ID',
accessor: 'requestId',
Cell: ({value}: { value: string }) => (
<span className="font-mono text-xs">{value.slice(0, 8)}...</span>
),
},
{ {
Header: 'Ticker', Header: 'Ticker',
accessor: 'ticker', accessor: 'ticker',
@@ -243,6 +247,49 @@ const BacktestGeneticBundle: React.FC = () => {
Header: 'Timeframe', Header: 'Timeframe',
accessor: 'timeframe', accessor: 'timeframe',
}, },
{
Header: 'Progress',
accessor: 'currentGeneration',
Cell: ({value, row}: { value: number, row: any }) => {
const generations = row.original.generations
const currentGen = value || 0
const percentage = generations > 0 ? Math.round((currentGen / generations) * 100) : 0
return (
<div className="flex flex-col gap-1">
<div className="text-xs">
{currentGen} / {generations}
</div>
<progress
className="progress progress-primary w-full h-2"
value={percentage}
max="100"
/>
</div>
)
},
},
{
Header: 'Selection',
accessor: 'selectionMethod',
Cell: ({value}: { value: string }) => (
<span className="text-xs">{value}</span>
),
},
{
Header: 'Crossover',
accessor: 'crossoverMethod',
Cell: ({value}: { value: string }) => (
<span className="text-xs">{value}</span>
),
},
{
Header: 'Mutation',
accessor: 'mutationMethod',
Cell: ({value}: { value: string }) => (
<span className="text-xs">{value}</span>
),
},
{ {
Header: 'Status', Header: 'Status',
accessor: 'status', accessor: 'status',
@@ -252,20 +299,6 @@ const BacktestGeneticBundle: React.FC = () => {
</span> </span>
), ),
}, },
{
Header: 'Created',
accessor: 'createdAt',
Cell: ({value}: { value: string }) => (
<span>{new Date(value).toLocaleDateString()}</span>
),
},
{
Header: 'Completed',
accessor: 'completedAt',
Cell: ({value}: { value: string | null }) => (
<span>{value ? new Date(value).toLocaleDateString() : '-'}</span>
),
},
{ {
Header: 'Actions', Header: 'Actions',
accessor: 'actions', accessor: 'actions',
@@ -301,14 +334,52 @@ const BacktestGeneticBundle: React.FC = () => {
<div className="space-y-6"> <div className="space-y-6">
<div className="card bg-base-100 shadow-xl"> <div className="card bg-base-100 shadow-xl">
<div className="card-body"> <div className="card-body">
<div className="flex justify-between items-center mb-4">
<div>
<h2 className="card-title">Genetic Algorithm Bundle</h2> <h2 className="card-title">Genetic Algorithm Bundle</h2>
<p className="text-sm text-gray-600 mb-4"> <p className="text-sm text-gray-600">
Create a genetic algorithm request that will be processed in the background. Create a genetic algorithm request that will be processed in the background.
The algorithm will optimize trading parameters and indicator combinations. The algorithm will optimize trading parameters and indicator combinations.
</p> </p>
</div>
<button
type="button"
onClick={() => setIsFormCollapsed(!isFormCollapsed)}
className="btn btn-sm btn-outline"
>
{isFormCollapsed ? 'Show Form' : 'Hide Form'}
</button>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4"> <form onSubmit={handleSubmit(onSubmit)} className={`space-y-4 ${isFormCollapsed ? 'hidden' : ''}`}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> {isFormCollapsed && (
<div className="space-y-4">
<div className="bg-base-200 p-4 rounded-lg">
<h4 className="font-semibold mb-2">Current Settings</h4>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-sm">
<div><strong>Ticker:</strong> {formValues.ticker}</div>
<div><strong>Timeframe:</strong> {formValues.timeframe}</div>
<div><strong>Population:</strong> {formValues.populationSize}</div>
<div><strong>Generations:</strong> {formValues.generations}</div>
<div><strong>Selection:</strong> {formValues.selectionMethod}</div>
<div><strong>Crossover:</strong> {formValues.crossoverMethod}</div>
<div><strong>Mutation:</strong> {formValues.mutationMethod}</div>
<div><strong>Indicators:</strong> {selectedIndicators.length}</div>
</div>
</div>
<div className="flex justify-center">
<button
type="button"
onClick={handleSubmit(onSubmit)}
disabled={isSubmitting}
className="btn btn-primary"
>
{isSubmitting ? 'Creating...' : 'Create Request with Current Settings'}
</button>
</div>
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="form-control"> <div className="form-control">
<label className="label"> <label className="label">
<span className="label-text">Ticker</span> <span className="label-text">Ticker</span>
@@ -418,9 +489,52 @@ const BacktestGeneticBundle: React.FC = () => {
className="select select-bordered w-full" className="select select-bordered w-full"
{...register('selectionMethod')} {...register('selectionMethod')}
> >
<option value={GeneticSelectionMethod.Tournament}>Tournament Selection</option> <option value={GeneticSelectionMethod.Elite}>Elite Selection</option>
<option value={GeneticSelectionMethod.Roulette}>Roulette Wheel</option> <option value={GeneticSelectionMethod.Roulette}>Roulette Wheel</option>
<option value={GeneticSelectionMethod.FitnessWeighted}>Fitness Weighted</option> <option value={GeneticSelectionMethod.StochasticUniversalSampling}>Stochastic Universal Sampling</option>
<option value={GeneticSelectionMethod.Tournament}>Tournament Selection</option>
<option value={GeneticSelectionMethod.Truncation}>Truncation Selection</option>
</select>
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Crossover Method</span>
</label>
<select
className="select select-bordered w-full"
{...register('crossoverMethod')}
>
<option value={GeneticCrossoverMethod.AlternatingPosition}>Alternating Position (AP)</option>
<option value={GeneticCrossoverMethod.CutAndSplice}>Cut and Splice</option>
<option value={GeneticCrossoverMethod.Cycle}>Cycle (CX)</option>
<option value={GeneticCrossoverMethod.OnePoint}>One-Point (C1)</option>
<option value={GeneticCrossoverMethod.OrderBased}>Order-based (OX2)</option>
<option value={GeneticCrossoverMethod.Ordered}>Ordered (OX1)</option>
<option value={GeneticCrossoverMethod.PartiallyMapped}>Partially Mapped (PMX)</option>
<option value={GeneticCrossoverMethod.PositionBased}>Position-based (POS)</option>
<option value={GeneticCrossoverMethod.ThreeParent}>Three Parent</option>
<option value={GeneticCrossoverMethod.TwoPoint}>Two-Point (C2)</option>
<option value={GeneticCrossoverMethod.Uniform}>Uniform</option>
<option value={GeneticCrossoverMethod.VotingRecombination}>Voting Recombination (VR)</option>
</select>
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Mutation Method</span>
</label>
<select
className="select select-bordered w-full"
{...register('mutationMethod')}
>
<option value={GeneticMutationMethod.Displacement}>Displacement</option>
<option value={GeneticMutationMethod.FlipBit}>Flip Bit</option>
<option value={GeneticMutationMethod.Insertion}>Insertion</option>
<option value={GeneticMutationMethod.PartialShuffle}>Partial Shuffle (PSM)</option>
<option value={GeneticMutationMethod.ReverseSequence}>Reverse Sequence (RSM)</option>
<option value={GeneticMutationMethod.Twors}>Twors</option>
<option value={GeneticMutationMethod.Uniform}>Uniform</option>
</select> </select>
</div> </div>
@@ -535,6 +649,15 @@ const BacktestGeneticBundle: React.FC = () => {
<div> <div>
<strong>Timeframe:</strong> {selectedRequest.timeframe} <strong>Timeframe:</strong> {selectedRequest.timeframe}
</div> </div>
<div>
<strong>Selection Method:</strong> {selectedRequest.selectionMethod}
</div>
<div>
<strong>Crossover Method:</strong> {selectedRequest.crossoverMethod}
</div>
<div>
<strong>Mutation Method:</strong> {selectedRequest.mutationMethod}
</div>
<div> <div>
<strong>Status:</strong> <strong>Status:</strong>
<span className={`badge ${getStatusBadgeColor(selectedRequest.status)} ml-2`}> <span className={`badge ${getStatusBadgeColor(selectedRequest.status)} ml-2`}>
@@ -549,6 +672,23 @@ const BacktestGeneticBundle: React.FC = () => {
<strong>Completed:</strong> {new Date(selectedRequest.completedAt).toLocaleString()} <strong>Completed:</strong> {new Date(selectedRequest.completedAt).toLocaleString()}
</div> </div>
)} )}
<div>
<strong>Progress:</strong> {selectedRequest.currentGeneration || 0} / {selectedRequest.generations}
{selectedRequest.currentGeneration && selectedRequest.generations > 0 && (
<div className="mt-1">
<progress
className="progress progress-primary w-full h-2"
value={Math.round(((selectedRequest.currentGeneration || 0) / selectedRequest.generations) * 100)}
max="100"
/>
</div>
)}
</div>
{selectedRequest.bestFitnessSoFar && (
<div>
<strong>Best Fitness:</strong> {selectedRequest.bestFitnessSoFar.toFixed(4)}
</div>
)}
</div> </div>
<div className="mb-6"> <div className="mb-6">