Update front
This commit is contained in:
@@ -291,6 +291,94 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
return Math.min(formValues.maxTakeProfit || 4, PARAMETER_RANGES.takeProfit.max)
|
return Math.min(formValues.maxTakeProfit || 4, PARAMETER_RANGES.takeProfit.max)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate total number of backtests that will be run
|
||||||
|
const calculateTotalBacktests = (): number => {
|
||||||
|
const populationSize = formValues.populationSize || GENETIC_CONFIG.populationSize
|
||||||
|
const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
|
||||||
|
|
||||||
|
// Each phase uses the full generation count
|
||||||
|
const phase1Generations = totalGenerations
|
||||||
|
const phase2Generations = totalGenerations
|
||||||
|
const phase3Generations = totalGenerations
|
||||||
|
const phase4Generations = totalGenerations
|
||||||
|
|
||||||
|
// For Phase 1, we test each eligible indicator with the full population size for each generation
|
||||||
|
const phase1Backtests = phase1Generations * populationSize * eligibleIndicators.length
|
||||||
|
|
||||||
|
// For subsequent phases, we use combinations of the best indicators from Phase 1
|
||||||
|
const maxBestIndicators = Math.min(8, eligibleIndicators.length)
|
||||||
|
|
||||||
|
// Calculate combinations for each phase
|
||||||
|
const phase2Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) / 2) // C(n,2)
|
||||||
|
const phase3Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) / 6) // C(n,3)
|
||||||
|
const phase4Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) * (maxBestIndicators - 3) / 24) // C(n,4)
|
||||||
|
|
||||||
|
const phase2Backtests = phase2Generations * phase2Combinations
|
||||||
|
const phase3Backtests = phase3Generations * phase3Combinations
|
||||||
|
const phase4Backtests = phase4Generations * phase4Combinations
|
||||||
|
|
||||||
|
return phase1Backtests + phase2Backtests + phase3Backtests + phase4Backtests
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to calculate progress accurately
|
||||||
|
const calculateProgress = () => {
|
||||||
|
if (totalIndividuals === 0) return { completed: 0, total: 1, percentage: 0 }
|
||||||
|
|
||||||
|
const populationSize = geneticConfig.populationSize
|
||||||
|
const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
|
||||||
|
|
||||||
|
let completedBacktests = 0
|
||||||
|
let totalBacktests = 0
|
||||||
|
|
||||||
|
// Phase 1: Each generation tests populationSize * eligibleIndicators.length
|
||||||
|
const phase1Backtests = totalGenerations * populationSize * eligibleIndicators.length
|
||||||
|
totalBacktests += phase1Backtests
|
||||||
|
|
||||||
|
if (currentPhase === 'phase1') {
|
||||||
|
completedBacktests = (currentGeneration - 1) * populationSize + currentIndividual
|
||||||
|
} else {
|
||||||
|
completedBacktests += phase1Backtests
|
||||||
|
|
||||||
|
// Phase 2: Use best indicators (max 8)
|
||||||
|
const maxBestIndicators = Math.min(8, eligibleIndicators.length)
|
||||||
|
const phase2Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) / 2)
|
||||||
|
const phase2Backtests = totalGenerations * phase2Combinations
|
||||||
|
totalBacktests += phase2Backtests
|
||||||
|
|
||||||
|
if (currentPhase === 'phase2') {
|
||||||
|
completedBacktests += (currentGeneration - totalGenerations - 1) * populationSize + currentIndividual
|
||||||
|
} else {
|
||||||
|
completedBacktests += phase2Backtests
|
||||||
|
|
||||||
|
// Phase 3: 3-indicator combinations
|
||||||
|
const phase3Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) / 6)
|
||||||
|
const phase3Backtests = totalGenerations * phase3Combinations
|
||||||
|
totalBacktests += phase3Backtests
|
||||||
|
|
||||||
|
if (currentPhase === 'phase3') {
|
||||||
|
completedBacktests += (currentGeneration - totalGenerations * 2 - 1) * populationSize + currentIndividual
|
||||||
|
} else {
|
||||||
|
completedBacktests += phase3Backtests
|
||||||
|
|
||||||
|
// Phase 4: 4-indicator combinations
|
||||||
|
const phase4Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) * (maxBestIndicators - 3) / 24)
|
||||||
|
const phase4Backtests = totalGenerations * phase4Combinations
|
||||||
|
totalBacktests += phase4Backtests
|
||||||
|
|
||||||
|
if (currentPhase === 'phase4') {
|
||||||
|
completedBacktests += (currentGeneration - totalGenerations * 3 - 1) * populationSize + currentIndividual
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
completed: Math.min(completedBacktests, totalBacktests),
|
||||||
|
total: totalBacktests,
|
||||||
|
percentage: Math.round((completedBacktests / totalBacktests) * 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a random indicator with bias towards CustomScenario defaults
|
// Create a random indicator with bias towards CustomScenario defaults
|
||||||
const createRandomIndicator = (eligibleIndicators: IndicatorType[]): GeneticIndicator => {
|
const createRandomIndicator = (eligibleIndicators: IndicatorType[]): GeneticIndicator => {
|
||||||
const type = eligibleIndicators[Math.floor(Math.random() * eligibleIndicators.length)]
|
const type = eligibleIndicators[Math.floor(Math.random() * eligibleIndicators.length)]
|
||||||
@@ -378,6 +466,53 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
return uniqueIndicators
|
return uniqueIndicators
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a population for Phase 1 that ensures every eligible indicator is tested
|
||||||
|
const createPhase1Population = (populationSize: number, eligibleIndicators: IndicatorType[]): GeneticIndividual[] => {
|
||||||
|
const population: GeneticIndividual[] = []
|
||||||
|
|
||||||
|
// Create a full population where each individual uses a different eligible indicator
|
||||||
|
// If we have more population slots than indicators, we cycle through indicators
|
||||||
|
for (let i = 0; i < populationSize; i++) {
|
||||||
|
const indicatorType = eligibleIndicators[i % eligibleIndicators.length]
|
||||||
|
const indicator = createRandomIndicator([indicatorType]) // Force this specific indicator type
|
||||||
|
|
||||||
|
// Generate stop loss first
|
||||||
|
const minTP = 0.9;
|
||||||
|
const maxTP = getMaxTakeProfit();
|
||||||
|
const tp = getRandomInRange({ min: minTP, max: maxTP });
|
||||||
|
const minSL = 0.5;
|
||||||
|
const maxSL = Math.min(tp / 1.1, tp - 0.1);
|
||||||
|
const sl = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
|
||||||
|
|
||||||
|
const individual: GeneticIndividual = {
|
||||||
|
// Money Management Parameters
|
||||||
|
stopLoss: sl,
|
||||||
|
takeProfit: tp,
|
||||||
|
leverage: 1,
|
||||||
|
|
||||||
|
// Trading Bot Parameters
|
||||||
|
cooldownPeriod: getRandomIntInRange(PARAMETER_RANGES.cooldownPeriod),
|
||||||
|
maxLossStreak: getRandomIntInRange(PARAMETER_RANGES.maxLossStreak),
|
||||||
|
maxPositionTimeHours: 0, // Always 0 to prevent early position cutting
|
||||||
|
flipOnlyWhenInProfit: Math.random() > 0.5,
|
||||||
|
closeEarlyWhenProfitable: Math.random() > 0.5,
|
||||||
|
|
||||||
|
// Synth API Parameters (always false for performance)
|
||||||
|
useSynthApi: false,
|
||||||
|
useForPositionSizing: false,
|
||||||
|
useForSignalFiltering: false,
|
||||||
|
useForDynamicStopLoss: false,
|
||||||
|
|
||||||
|
// Indicator Configuration
|
||||||
|
indicators: [indicator],
|
||||||
|
}
|
||||||
|
|
||||||
|
population.push(individual)
|
||||||
|
}
|
||||||
|
|
||||||
|
return population
|
||||||
|
}
|
||||||
|
|
||||||
// Create a random individual with positive risk-reward ratio
|
// Create a random individual with positive risk-reward ratio
|
||||||
const createIndividual = (phase: 'phase1' | 'phase2' | 'phase3' | 'phase4' = 'phase1', bestIndicators: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}> = [], currentEligibleIndicators: IndicatorType[] = eligibleIndicators): GeneticIndividual => {
|
const createIndividual = (phase: 'phase1' | 'phase2' | 'phase3' | 'phase4' = 'phase1', bestIndicators: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}> = [], currentEligibleIndicators: IndicatorType[] = eligibleIndicators): GeneticIndividual => {
|
||||||
// Generate stop loss first
|
// Generate stop loss first
|
||||||
@@ -474,9 +609,17 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
const removeBadIndicators = (fitnessResults: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>) => {
|
const removeBadIndicators = (fitnessResults: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>) => {
|
||||||
const badIndicators = new Set<IndicatorType>()
|
const badIndicators = new Set<IndicatorType>()
|
||||||
|
|
||||||
// Find indicators with very poor performance (fitness < 0.1)
|
// Calculate the maximum possible fitness score for reference
|
||||||
|
// PnL Score (30%) + Win Rate Score (20%) + Risk-Reward Score (20%) + Consistency Score (10%) + Risk-Reward Bonus (10%) + Drawdown Score (10%)
|
||||||
|
// Maximum theoretical fitness: 0.3 + 0.2 + 0.4 + 0.1 + 0.02 + 0.1 = 1.12
|
||||||
|
const maxPossibleFitness = 1.12
|
||||||
|
|
||||||
|
// Set threshold to 10% of maximum possible fitness (0.112)
|
||||||
|
const badIndicatorThreshold = maxPossibleFitness * 0.1
|
||||||
|
|
||||||
|
// Find indicators with very poor performance
|
||||||
fitnessResults.forEach(result => {
|
fitnessResults.forEach(result => {
|
||||||
if (result.fitness < 0.1 && result.individual.indicators.length === 1) {
|
if (result.fitness < badIndicatorThreshold && result.individual.indicators.length === 1) {
|
||||||
const indicatorType = result.individual.indicators[0].type
|
const indicatorType = result.individual.indicators[0].type
|
||||||
badIndicators.add(indicatorType)
|
badIndicators.add(indicatorType)
|
||||||
}
|
}
|
||||||
@@ -487,8 +630,12 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
setEligibleIndicators(prev => prev.filter(indicator => !badIndicators.has(indicator)))
|
setEligibleIndicators(prev => prev.filter(indicator => !badIndicators.has(indicator)))
|
||||||
setRemovedIndicators(prev => new Set([...prev, ...badIndicators]))
|
setRemovedIndicators(prev => new Set([...prev, ...badIndicators]))
|
||||||
|
|
||||||
// Log removed indicators
|
// Log removed indicators with fitness scores for transparency
|
||||||
console.log(`Removed bad indicators: ${Array.from(badIndicators).join(', ')}`)
|
const removedWithScores = fitnessResults
|
||||||
|
.filter(result => badIndicators.has(result.individual.indicators[0]?.type))
|
||||||
|
.map(result => `${result.individual.indicators[0]?.type} (fitness: ${result.fitness.toFixed(3)})`)
|
||||||
|
|
||||||
|
console.log(`Removed bad indicators (threshold: ${badIndicatorThreshold.toFixed(3)}): ${removedWithScores.join(', ')}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,13 +655,19 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
const riskRewardRatio = individual.takeProfit / individual.stopLoss
|
const riskRewardRatio = individual.takeProfit / individual.stopLoss
|
||||||
const riskRewardBonus = Math.min(0.2, (riskRewardRatio - 1.1) * 0.1) // Bonus for R:R > 1.1
|
const riskRewardBonus = Math.min(0.2, (riskRewardRatio - 1.1) * 0.1) // Bonus for R:R > 1.1
|
||||||
|
|
||||||
// Weighted combination
|
// Drawdown score (normalized to 0-1, where lower drawdown is better)
|
||||||
|
// maxDrawdownPc is in percentage, so we normalize it
|
||||||
|
const maxDrawdownPc = Math.abs(stats.maxDrawdownPc || 0) // Use absolute value and default to 0
|
||||||
|
const drawdownScore = Math.max(0, 1 - (maxDrawdownPc / 50)) // Normalize: 0% drawdown = 1.0, 50% drawdown = 0.0
|
||||||
|
|
||||||
|
// Weighted combination with drawdown consideration
|
||||||
const fitness =
|
const fitness =
|
||||||
pnlScore * 0.35 +
|
pnlScore * 0.3 +
|
||||||
winRateScore * 0.25 +
|
winRateScore * 0.2 +
|
||||||
riskRewardScore * 0.2 +
|
riskRewardScore * 0.2 +
|
||||||
consistencyScore * 0.1 +
|
consistencyScore * 0.1 +
|
||||||
riskRewardBonus * 0.1
|
riskRewardBonus * 0.1 +
|
||||||
|
drawdownScore * 0.1 // Add drawdown score with a weight of 0.1
|
||||||
|
|
||||||
return Math.max(0, fitness)
|
return Math.max(0, fitness)
|
||||||
}
|
}
|
||||||
@@ -720,10 +873,30 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
setEligibleIndicators(ALL_INDICATORS)
|
setEligibleIndicators(ALL_INDICATORS)
|
||||||
setRemovedIndicators(new Set())
|
setRemovedIndicators(new Set())
|
||||||
|
|
||||||
const phase1Individuals = GENETIC_CONFIG.phase1Generations * geneticConfig.populationSize
|
// Use the full user-specified generation count for each phase
|
||||||
const phase2Individuals = GENETIC_CONFIG.phase2Generations * geneticConfig.populationSize
|
const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
|
||||||
const phase3Individuals = GENETIC_CONFIG.phase3Generations * geneticConfig.populationSize
|
|
||||||
const phase4Individuals = GENETIC_CONFIG.phase4Generations * geneticConfig.populationSize
|
// Each phase uses the full generation count
|
||||||
|
const phase1Generations = totalGenerations
|
||||||
|
const phase2Generations = totalGenerations
|
||||||
|
const phase3Generations = totalGenerations
|
||||||
|
const phase4Generations = totalGenerations
|
||||||
|
|
||||||
|
// For Phase 1, we test each eligible indicator with the full population size
|
||||||
|
const phase1Individuals = phase1Generations * geneticConfig.populationSize * eligibleIndicators.length
|
||||||
|
|
||||||
|
// For subsequent phases, we use combinations of the best indicators from Phase 1
|
||||||
|
// We'll assume we keep the top 8 indicators (as per the current logic)
|
||||||
|
const maxBestIndicators = Math.min(8, eligibleIndicators.length)
|
||||||
|
|
||||||
|
// Calculate combinations for each phase
|
||||||
|
const phase2Combinations = Math.min(geneticConfig.populationSize, maxBestIndicators * (maxBestIndicators - 1) / 2) // C(n,2)
|
||||||
|
const phase3Combinations = Math.min(geneticConfig.populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) / 6) // C(n,3)
|
||||||
|
const phase4Combinations = Math.min(geneticConfig.populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) * (maxBestIndicators - 3) / 24) // C(n,4)
|
||||||
|
|
||||||
|
const phase2Individuals = phase2Generations * phase2Combinations
|
||||||
|
const phase3Individuals = phase3Generations * phase3Combinations
|
||||||
|
const phase4Individuals = phase4Generations * phase4Combinations
|
||||||
const totalIndividuals = phase1Individuals + phase2Individuals + phase3Individuals + phase4Individuals
|
const totalIndividuals = phase1Individuals + phase2Individuals + phase3Individuals + phase4Individuals
|
||||||
setTotalIndividuals(totalIndividuals)
|
setTotalIndividuals(totalIndividuals)
|
||||||
|
|
||||||
@@ -738,10 +911,10 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
t.update('info', 'Phase 1: Testing single indicators...')
|
t.update('info', 'Phase 1: Testing single indicators...')
|
||||||
setCurrentPhase('phase1')
|
setCurrentPhase('phase1')
|
||||||
|
|
||||||
let population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase1'))
|
let population = createPhase1Population(geneticConfig.populationSize, eligibleIndicators)
|
||||||
const phase1Results: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}> = []
|
const phase1Results: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}> = []
|
||||||
|
|
||||||
for (let generation = 0; generation < GENETIC_CONFIG.phase1Generations; generation++) {
|
for (let generation = 0; generation < phase1Generations; generation++) {
|
||||||
setCurrentGeneration(generation + 1)
|
setCurrentGeneration(generation + 1)
|
||||||
|
|
||||||
// Evaluate fitness for individuals one by one for real-time updates
|
// Evaluate fitness for individuals one by one for real-time updates
|
||||||
@@ -798,7 +971,8 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
// Update progress toast
|
// Update progress toast
|
||||||
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
||||||
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
||||||
t.update('info', `Phase 1 - Progress: ${progress}% - Gen ${generation + 1}/${GENETIC_CONFIG.phase1Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
const testedIndicators = new Set(fitnessResults.map(r => r.individual.indicators[0]?.type)).size
|
||||||
|
t.update('info', `Phase 1 - Progress: ${progress}% - Gen ${generation + 1}/${phase1Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Tested: ${testedIndicators}/${eligibleIndicators.length} indicators - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fitness calculation error:', error)
|
console.error('Fitness calculation error:', error)
|
||||||
@@ -811,7 +985,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
fitnessResults.sort((a, b) => b.fitness - a.fitness)
|
fitnessResults.sort((a, b) => b.fitness - a.fitness)
|
||||||
|
|
||||||
// Store best single indicators for subsequent phases
|
// Store best single indicators for subsequent phases
|
||||||
if (generation === GENETIC_CONFIG.phase1Generations - 1) {
|
if (generation === phase1Generations - 1) {
|
||||||
const topSingleIndicators = fitnessResults
|
const topSingleIndicators = fitnessResults
|
||||||
.filter(result => result.fitness > 0)
|
.filter(result => result.fitness > 0)
|
||||||
.slice(0, Math.min(8, fitnessResults.length)) // Keep top 8 single indicators
|
.slice(0, Math.min(8, fitnessResults.length)) // Keep top 8 single indicators
|
||||||
@@ -821,97 +995,9 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
removeBadIndicators(fitnessResults)
|
removeBadIndicators(fitnessResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create next generation for phase 1
|
// For Phase 1, create a new population for the next generation
|
||||||
const newPopulation = []
|
// Each generation tests every eligible indicator with the full population size
|
||||||
const eliteCount = Math.max(1, Math.floor(geneticConfig.populationSize * (formValues.elitismPercentage || 15) / 100))
|
population = createPhase1Population(geneticConfig.populationSize, eligibleIndicators)
|
||||||
for (let i = 0; i < eliteCount; i++) {
|
|
||||||
newPopulation.push(fitnessResults[i].individual)
|
|
||||||
}
|
|
||||||
|
|
||||||
while (newPopulation.length < geneticConfig.populationSize) {
|
|
||||||
const parent1 = selectParent(fitnessResults)
|
|
||||||
const parent2 = selectParent(fitnessResults)
|
|
||||||
|
|
||||||
const child = {...parent1}
|
|
||||||
if (Math.random() < geneticConfig.crossoverRate) {
|
|
||||||
child.stopLoss = parent2.stopLoss
|
|
||||||
child.takeProfit = parent2.takeProfit
|
|
||||||
child.leverage = 1
|
|
||||||
child.cooldownPeriod = parent2.cooldownPeriod
|
|
||||||
child.indicators = parent2.indicators
|
|
||||||
|
|
||||||
// Validate risk-reward ratio after crossover
|
|
||||||
const minTakeProfit = child.stopLoss * 1.1
|
|
||||||
if (child.takeProfit < minTakeProfit) {
|
|
||||||
child.takeProfit = minTakeProfit + getRandomInRange({
|
|
||||||
min: 0,
|
|
||||||
max: getMaxTakeProfit() - minTakeProfit
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutation
|
|
||||||
if (Math.random() < geneticConfig.mutationRate) {
|
|
||||||
// Mutate stop loss
|
|
||||||
child.stopLoss = getRandomInRange(PARAMETER_RANGES.stopLoss);
|
|
||||||
// Adjust take profit to maintain minimum R:R ratio and respect max TP
|
|
||||||
const minTP = 0.9;
|
|
||||||
const maxTP = getMaxTakeProfit();
|
|
||||||
child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
|
|
||||||
const minSL = 0.5;
|
|
||||||
const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
|
|
||||||
child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
|
|
||||||
}
|
|
||||||
if (Math.random() < geneticConfig.mutationRate) {
|
|
||||||
// Mutate take profit while respecting minimum R:R ratio and max TP
|
|
||||||
const minTP = 0.9;
|
|
||||||
const maxTP = getMaxTakeProfit();
|
|
||||||
child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
|
|
||||||
const minSL = 0.5;
|
|
||||||
const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
|
|
||||||
child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
|
|
||||||
}
|
|
||||||
if (Math.random() < geneticConfig.mutationRate) {
|
|
||||||
// 50% chance to create completely new indicator, 50% chance to slightly modify existing
|
|
||||||
if (Math.random() < 0.5) {
|
|
||||||
child.indicators = [createRandomIndicator(eligibleIndicators)]
|
|
||||||
} else {
|
|
||||||
// Slight modification of existing indicator parameters
|
|
||||||
const existingIndicator = child.indicators[0]
|
|
||||||
const modifiedIndicator = {...existingIndicator}
|
|
||||||
|
|
||||||
// Randomly modify one parameter with small variation
|
|
||||||
const paramKeys = Object.keys(modifiedIndicator).filter(key =>
|
|
||||||
key !== 'type' && typeof modifiedIndicator[key as keyof GeneticIndicator] === 'number'
|
|
||||||
) as Array<keyof GeneticIndicator>
|
|
||||||
|
|
||||||
if (paramKeys.length > 0) {
|
|
||||||
const randomParam = paramKeys[Math.floor(Math.random() * paramKeys.length)]
|
|
||||||
const currentValue = modifiedIndicator[randomParam] as number
|
|
||||||
const defaultValue = DEFAULT_INDICATOR_VALUES[randomParam as keyof typeof DEFAULT_INDICATOR_VALUES]
|
|
||||||
|
|
||||||
// Add small random variation (±20% of the difference from default)
|
|
||||||
const variation = (currentValue - defaultValue) * 0.2 * (Math.random() - 0.5)
|
|
||||||
const newValue = Math.max(1, Math.round(currentValue + variation))
|
|
||||||
|
|
||||||
;(modifiedIndicator as any)[randomParam] = newValue
|
|
||||||
child.indicators = [modifiedIndicator]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce RR >= 1.1:1
|
|
||||||
if (child.takeProfit < child.stopLoss * 1.1) {
|
|
||||||
child.takeProfit = Math.min(getMaxTakeProfit(), child.stopLoss * 1.1);
|
|
||||||
}
|
|
||||||
if (child.stopLoss > child.takeProfit / 1.1) {
|
|
||||||
child.stopLoss = Math.max(PARAMETER_RANGES.stopLoss.min, child.takeProfit / 1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
newPopulation.push(child)
|
|
||||||
}
|
|
||||||
|
|
||||||
population = newPopulation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 2: Test 2-indicator combinations
|
// Phase 2: Test 2-indicator combinations
|
||||||
@@ -921,8 +1007,8 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
const bestIndicators = bestSingleIndicators
|
const bestIndicators = bestSingleIndicators
|
||||||
population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase2', bestIndicators))
|
population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase2', bestIndicators))
|
||||||
|
|
||||||
for (let generation = 0; generation < GENETIC_CONFIG.phase2Generations; generation++) {
|
for (let generation = 0; generation < phase2Generations; generation++) {
|
||||||
setCurrentGeneration(GENETIC_CONFIG.phase1Generations + generation + 1)
|
setCurrentGeneration(phase1Generations + generation + 1)
|
||||||
|
|
||||||
const fitnessResults = []
|
const fitnessResults = []
|
||||||
|
|
||||||
@@ -954,7 +1040,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
individual: result.individual,
|
individual: result.individual,
|
||||||
backtest: result.backtest,
|
backtest: result.backtest,
|
||||||
fitness: result.fitness,
|
fitness: result.fitness,
|
||||||
generation: GENETIC_CONFIG.phase1Generations + generation + 1,
|
generation: phase1Generations + generation + 1,
|
||||||
phase: 'phase2',
|
phase: 'phase2',
|
||||||
}]
|
}]
|
||||||
return newResults
|
return newResults
|
||||||
@@ -976,7 +1062,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
// Update progress toast
|
// Update progress toast
|
||||||
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
||||||
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
||||||
t.update('info', `Phase 2 - Progress: ${progress}% - Gen ${generation + 1}/${GENETIC_CONFIG.phase2Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
t.update('info', `Phase 2 - Progress: ${progress}% - Gen ${generation + 1}/${phase2Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fitness calculation error:', error)
|
console.error('Fitness calculation error:', error)
|
||||||
@@ -1096,8 +1182,8 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
|
|
||||||
population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase3', bestIndicators))
|
population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase3', bestIndicators))
|
||||||
|
|
||||||
for (let generation = 0; generation < GENETIC_CONFIG.phase3Generations; generation++) {
|
for (let generation = 0; generation < phase3Generations; generation++) {
|
||||||
setCurrentGeneration(GENETIC_CONFIG.phase1Generations + GENETIC_CONFIG.phase2Generations + generation + 1)
|
setCurrentGeneration(phase1Generations + phase2Generations + generation + 1)
|
||||||
|
|
||||||
const fitnessResults = []
|
const fitnessResults = []
|
||||||
|
|
||||||
@@ -1129,7 +1215,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
individual: result.individual,
|
individual: result.individual,
|
||||||
backtest: result.backtest,
|
backtest: result.backtest,
|
||||||
fitness: result.fitness,
|
fitness: result.fitness,
|
||||||
generation: GENETIC_CONFIG.phase1Generations + GENETIC_CONFIG.phase2Generations + generation + 1,
|
generation: phase1Generations + phase2Generations + generation + 1,
|
||||||
phase: 'phase3',
|
phase: 'phase3',
|
||||||
}]
|
}]
|
||||||
return newResults
|
return newResults
|
||||||
@@ -1151,7 +1237,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
// Update progress toast
|
// Update progress toast
|
||||||
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
||||||
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
||||||
t.update('info', `Phase 3 - Progress: ${progress}% - Gen ${generation + 1}/${GENETIC_CONFIG.phase3Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
t.update('info', `Phase 3 - Progress: ${progress}% - Gen ${generation + 1}/${phase3Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fitness calculation error:', error)
|
console.error('Fitness calculation error:', error)
|
||||||
@@ -1271,8 +1357,8 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
|
|
||||||
population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase4', bestIndicators))
|
population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase4', bestIndicators))
|
||||||
|
|
||||||
for (let generation = 0; generation < GENETIC_CONFIG.phase4Generations; generation++) {
|
for (let generation = 0; generation < phase4Generations; generation++) {
|
||||||
setCurrentGeneration(GENETIC_CONFIG.phase1Generations + GENETIC_CONFIG.phase2Generations + GENETIC_CONFIG.phase3Generations + generation + 1)
|
setCurrentGeneration(phase1Generations + phase2Generations + phase3Generations + generation + 1)
|
||||||
|
|
||||||
const fitnessResults = []
|
const fitnessResults = []
|
||||||
|
|
||||||
@@ -1304,7 +1390,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
individual: result.individual,
|
individual: result.individual,
|
||||||
backtest: result.backtest,
|
backtest: result.backtest,
|
||||||
fitness: result.fitness,
|
fitness: result.fitness,
|
||||||
generation: GENETIC_CONFIG.phase1Generations + GENETIC_CONFIG.phase2Generations + GENETIC_CONFIG.phase3Generations + generation + 1,
|
generation: phase1Generations + phase2Generations + phase3Generations + generation + 1,
|
||||||
phase: 'phase4',
|
phase: 'phase4',
|
||||||
}]
|
}]
|
||||||
return newResults
|
return newResults
|
||||||
@@ -1326,7 +1412,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
// Update progress toast
|
// Update progress toast
|
||||||
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
|
||||||
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
const avgTimeFormatted = formatTimeDuration(averageTimeMs)
|
||||||
t.update('info', `Phase 4 - Progress: ${progress}% - Gen ${generation + 1}/${GENETIC_CONFIG.phase4Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
t.update('info', `Phase 4 - Progress: ${progress}% - Gen ${generation + 1}/${phase4Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fitness calculation error:', error)
|
console.error('Fitness calculation error:', error)
|
||||||
@@ -1451,7 +1537,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [geneticConfig, runBacktestForIndividual, calculateFitnessScore])
|
}, [geneticConfig, runBacktestForIndividual, calculateFitnessScore])
|
||||||
|
|
||||||
// Prepare 3D plot data
|
// Prepare 3D plot data for Fitness vs Score vs Win Rate
|
||||||
const plotData = results.length > 0 ? [
|
const plotData = results.length > 0 ? [
|
||||||
{
|
{
|
||||||
type: 'scatter3d' as const,
|
type: 'scatter3d' as const,
|
||||||
@@ -1482,11 +1568,78 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
const riskRewardRatio = r.individual.takeProfit / r.individual.stopLoss
|
const riskRewardRatio = r.individual.takeProfit / r.individual.stopLoss
|
||||||
const riskRewardColor = riskRewardRatio >= 2 ? '🟢' : riskRewardRatio >= 1.5 ? '🟡' : '🔴'
|
const riskRewardColor = riskRewardRatio >= 2 ? '🟢' : riskRewardRatio >= 1.5 ? '🟡' : '🔴'
|
||||||
|
|
||||||
|
// Calculate drawdown color based on severity
|
||||||
|
const maxDrawdownPc = Math.abs(r.backtest?.statistics?.maxDrawdownPc || 0)
|
||||||
|
const drawdownColor = maxDrawdownPc <= 10 ? '🟢' : maxDrawdownPc <= 25 ? '🟡' : '🔴'
|
||||||
|
|
||||||
return `Phase: ${r.phase || 'Unknown'}<br>` +
|
return `Phase: ${r.phase || 'Unknown'}<br>` +
|
||||||
`Gen: ${r.generation}<br>` +
|
`Gen: ${r.generation}<br>` +
|
||||||
`Fitness: ${r.fitness.toFixed(2)}<br>` +
|
`Fitness: ${r.fitness.toFixed(2)}<br>` +
|
||||||
`PnL: $${r.backtest?.statistics?.totalPnL?.toFixed(2) || 'N/A'}<br>` +
|
`PnL: $${r.backtest?.statistics?.totalPnL?.toFixed(2) || 'N/A'}<br>` +
|
||||||
`Win Rate: ${r.backtest?.winRate?.toFixed(1) || 'N/A'}%<br>` +
|
`Win Rate: ${r.backtest?.winRate?.toFixed(1) || 'N/A'}%<br>` +
|
||||||
|
`Max Drawdown: ${drawdownColor} ${maxDrawdownPc.toFixed(1)}%<br>` +
|
||||||
|
`SL: ${r.individual.stopLoss.toFixed(1)}%<br>` +
|
||||||
|
`TP: ${r.individual.takeProfit.toFixed(1)}%<br>` +
|
||||||
|
`R/R: ${riskRewardColor} ${riskRewardRatio.toFixed(2)}<br>` +
|
||||||
|
`Leverage: ${r.individual.leverage}x<br>` +
|
||||||
|
`Cooldown: ${r.individual.cooldownPeriod} candles<br>` +
|
||||||
|
`Max Loss Streak: ${r.individual.maxLossStreak}<br>` +
|
||||||
|
`Max Time: ${r.individual.maxPositionTimeHours}h<br>` +
|
||||||
|
`Indicators: ${indicatorDetails}<br>` +
|
||||||
|
`Loopback: ${loopbackPeriod}`
|
||||||
|
}),
|
||||||
|
hovertemplate: '%{text}<extra></extra>',
|
||||||
|
},
|
||||||
|
] : []
|
||||||
|
|
||||||
|
// Prepare 3D plot data for TP% vs SL% vs PnL
|
||||||
|
const plotDataTPvsSL = results.length > 0 ? [
|
||||||
|
{
|
||||||
|
type: 'scatter3d' as const,
|
||||||
|
mode: 'markers' as const,
|
||||||
|
x: results.map(r => r.individual.takeProfit),
|
||||||
|
y: results.map(r => r.individual.stopLoss),
|
||||||
|
z: results.map(r => r.backtest?.statistics?.totalPnL || 0),
|
||||||
|
marker: {
|
||||||
|
size: 5,
|
||||||
|
color: results.map(r => {
|
||||||
|
const pnl = r.backtest?.statistics?.totalPnL || 0
|
||||||
|
return pnl > 0 ? pnl : 0 // Color by PnL (positive values only for better visualization)
|
||||||
|
}),
|
||||||
|
colorscale: 'RdYlGn' as const, // Red to Yellow to Green
|
||||||
|
opacity: 0.8,
|
||||||
|
colorbar: {
|
||||||
|
title: 'PnL ($)',
|
||||||
|
titleside: 'right',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
text: results.map(r => {
|
||||||
|
const loopbackPeriod = r.individual.indicators.length === 1
|
||||||
|
? LOOPBACK_CONFIG.singleIndicator
|
||||||
|
: '5-15'
|
||||||
|
|
||||||
|
// Format indicator parameters for display
|
||||||
|
const indicatorDetails = r.individual.indicators.map((indicator: GeneticIndicator, index: number) => {
|
||||||
|
let details = indicator.type.toString()
|
||||||
|
if (indicator.period) details += ` (${indicator.period})`
|
||||||
|
if (indicator.fastPeriods && indicator.slowPeriods) details += ` (${indicator.fastPeriods}/${indicator.slowPeriods})`
|
||||||
|
if (indicator.multiplier) details += ` (${indicator.multiplier.toFixed(1)}x)`
|
||||||
|
return details
|
||||||
|
}).join(', ')
|
||||||
|
|
||||||
|
const riskRewardRatio = r.individual.takeProfit / r.individual.stopLoss
|
||||||
|
const riskRewardColor = riskRewardRatio >= 2 ? '🟢' : riskRewardRatio >= 1.5 ? '🟡' : '🔴'
|
||||||
|
|
||||||
|
// Calculate drawdown color based on severity
|
||||||
|
const maxDrawdownPc = Math.abs(r.backtest?.statistics?.maxDrawdownPc || 0)
|
||||||
|
const drawdownColor = maxDrawdownPc <= 10 ? '🟢' : maxDrawdownPc <= 25 ? '🟡' : '🔴'
|
||||||
|
|
||||||
|
return `Phase: ${r.phase || 'Unknown'}<br>` +
|
||||||
|
`Gen: ${r.generation}<br>` +
|
||||||
|
`Fitness: ${r.fitness.toFixed(2)}<br>` +
|
||||||
|
`PnL: $${r.backtest?.statistics?.totalPnL?.toFixed(2) || 'N/A'}<br>` +
|
||||||
|
`Win Rate: ${r.backtest?.winRate?.toFixed(1) || 'N/A'}%<br>` +
|
||||||
|
`Max Drawdown: ${drawdownColor} ${maxDrawdownPc.toFixed(1)}%<br>` +
|
||||||
`SL: ${r.individual.stopLoss.toFixed(1)}%<br>` +
|
`SL: ${r.individual.stopLoss.toFixed(1)}%<br>` +
|
||||||
`TP: ${r.individual.takeProfit.toFixed(1)}%<br>` +
|
`TP: ${r.individual.takeProfit.toFixed(1)}%<br>` +
|
||||||
`R/R: ${riskRewardColor} ${riskRewardRatio.toFixed(2)}<br>` +
|
`R/R: ${riskRewardColor} ${riskRewardRatio.toFixed(2)}<br>` +
|
||||||
@@ -1528,20 +1681,31 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<h4 className="font-bold">Four-Phase Optimization Strategy:</h4>
|
<h4 className="font-bold">Four-Phase Optimization Strategy:</h4>
|
||||||
<p className="text-sm mt-2">
|
<p className="text-sm mt-2">
|
||||||
<strong>Phase 1:</strong> Tests individual indicators (3 generations) to identify the best performing single indicators.
|
<strong>Phase 1:</strong> Tests individual indicators using the full user-specified generation count. Each generation runs populationSize backtests. The algorithm cycles through eligible indicators to ensure comprehensive coverage, but the total backtests remain populationSize per generation.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
<strong>Phase 2:</strong> Tests 2-indicator combinations (2 generations) using the top 8 single indicators.
|
<strong>Phase 2:</strong> Tests 2-indicator combinations using the full user-specified generation count. Each generation runs populationSize backtests with combinations of the best indicators from Phase 1.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
<strong>Phase 3:</strong> Tests 3-indicator combinations (2 generations) using the top 8 single indicators.
|
<strong>Phase 3:</strong> Tests 3-indicator combinations using the full user-specified generation count. Each generation runs populationSize backtests with combinations of the best indicators from Phase 1.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
<strong>Phase 4:</strong> Tests 4-indicator combinations (2 generations) using the top 8 single indicators.
|
<strong>Phase 4:</strong> Tests 4-indicator combinations using the full user-specified generation count. Each generation runs populationSize backtests with combinations of the best indicators from Phase 1.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
<strong>No Duplicates:</strong> Each scenario contains unique indicator types to avoid redundancy.
|
<strong>No Duplicates:</strong> Each scenario contains unique indicator types to avoid redundancy.
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Population Size:</strong> For Phase 1, set population size to at least the number of indicators you want to test to ensure comprehensive coverage.
|
||||||
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Total Backtests:</strong> {calculateTotalBacktests()} backtests will be run.
|
||||||
|
{(() => {
|
||||||
|
const populationSize = formValues.populationSize || GENETIC_CONFIG.populationSize
|
||||||
|
const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
|
||||||
|
return ` (${totalGenerations}×${populationSize}×4 phases = ${totalGenerations * populationSize * 4} total)`
|
||||||
|
})()}.
|
||||||
|
</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
<strong>Parameter Optimization:</strong> Uses CustomScenario defaults as starting points with 70% bias towards proven values.
|
<strong>Parameter Optimization:</strong> Uses CustomScenario defaults as starting points with 70% bias towards proven values.
|
||||||
</p>
|
</p>
|
||||||
@@ -1565,6 +1729,34 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Fitness Function Components */}
|
||||||
|
<div className="alert alert-info mb-4">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold">Fitness Function Components:</h4>
|
||||||
|
<p className="text-sm mt-2">
|
||||||
|
<strong>PnL Score (30%):</strong> Normalized profit/loss performance
|
||||||
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Win Rate Score (20%):</strong> Percentage of winning trades
|
||||||
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Risk-Reward Score (20%):</strong> Ratio of winning to losing trades
|
||||||
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Consistency Score (10%):</strong> Stability of performance over time
|
||||||
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Risk-Reward Bonus (10%):</strong> Bonus for better R:R ratios (>1.1)
|
||||||
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Drawdown Score (10%):</strong> Penalty for maximum drawdown (0% = 1.0, 50% = 0.0)
|
||||||
|
</p>
|
||||||
|
<p className="text-sm mt-1">
|
||||||
|
<strong>Drawdown Legend:</strong> 🟢 ≤10% (Low Risk) | 🟡 10-25% (Medium Risk) | 🔴 >25% (High Risk)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Eligible Indicators Feature */}
|
{/* Eligible Indicators Feature */}
|
||||||
<div className="alert alert-info mb-4">
|
<div className="alert alert-info mb-4">
|
||||||
<div>
|
<div>
|
||||||
@@ -1573,7 +1765,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
<strong>Manual Selection:</strong> Choose which indicators to include in the optimization process.
|
<strong>Manual Selection:</strong> Choose which indicators to include in the optimization process.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
<strong>Automatic Removal:</strong> After Phase 1, indicators with fitness scores below 0.1 are automatically removed from consideration.
|
<strong>Automatic Removal:</strong> After Phase 1, indicators with fitness scores below the configured threshold (default: 10% of max fitness) are automatically removed from consideration.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm mt-1">
|
<p className="text-sm mt-1">
|
||||||
<strong>Smart Optimization:</strong> The algorithm focuses on promising indicators, improving convergence and reducing computation time.
|
<strong>Smart Optimization:</strong> The algorithm focuses on promising indicators, improving convergence and reducing computation time.
|
||||||
@@ -1722,6 +1914,8 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className="form-control">
|
<div className="form-control">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text">Eligible Indicators</span>
|
<span className="label-text">Eligible Indicators</span>
|
||||||
@@ -1766,7 +1960,7 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
disabled={isRunning}
|
disabled={isRunning}
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
>
|
>
|
||||||
{isRunning ? 'Running...' : 'Start Genetic Algorithm'}
|
{isRunning ? 'Running...' : `Start Genetic Algorithm (${calculateTotalBacktests()} backtests)`}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -1795,18 +1989,22 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex justify-between mb-2">
|
<div className="flex justify-between mb-2">
|
||||||
<span>Phase: {currentPhase === 'phase1' ? '1 - Single Indicators' : currentPhase === 'phase2' ? '2 - 2-Indicators' : currentPhase === 'phase3' ? '3 - 3-Indicators' : '4 - 4-Indicators'}</span>
|
<span>Phase: {currentPhase === 'phase1' ? '1 - Single Indicators' : currentPhase === 'phase2' ? '2 - 2-Indicators' : currentPhase === 'phase3' ? '3 - 3-Indicators' : '4 - 4-Indicators'}</span>
|
||||||
<span>Generation: {currentGeneration}/{GENETIC_CONFIG.phase1Generations + GENETIC_CONFIG.phase2Generations + GENETIC_CONFIG.phase3Generations + GENETIC_CONFIG.phase4Generations}</span>
|
<span>Generation: {currentGeneration}/{(formValues.generations || GENETIC_CONFIG.generations) * 4}</span>
|
||||||
<span>Individual: {currentIndividual}/{geneticConfig.populationSize}</span>
|
<span>Individual: {currentIndividual}/{geneticConfig.populationSize}</span>
|
||||||
<span>Best Fitness: {bestFitness.toFixed(2)}</span>
|
<span>Best Fitness: {bestFitness.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between mb-2">
|
<div className="flex justify-between mb-2">
|
||||||
<span>Overall Progress: {totalIndividuals > 0 ? Math.round(((currentGeneration - 1) * geneticConfig.populationSize + currentIndividual) / totalIndividuals * 100) : 0}%</span>
|
<span>Overall Progress: {calculateProgress().percentage}%</span>
|
||||||
<span>Results: {results.length}</span>
|
<span>Results: {results.length}</span>
|
||||||
<span>Best Single Indicators: {bestSingleIndicators.length}</span>
|
<span>Best Single Indicators: {bestSingleIndicators.length}</span>
|
||||||
|
{currentPhase === 'phase1' && (
|
||||||
|
<span>Indicators Tested: {new Set(results.filter(r => r.phase === 'phase1').map(r => r.individual.indicators[0]?.type)).size}/{eligibleIndicators.length}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between mb-2">
|
<div className="flex justify-between mb-2">
|
||||||
<span>Eligible Indicators: {eligibleIndicators.length}/{ALL_INDICATORS.length}</span>
|
<span>Eligible Indicators: {eligibleIndicators.length}/{ALL_INDICATORS.length}</span>
|
||||||
<span>Removed Indicators: {removedIndicators.size}</span>
|
<span>Removed Indicators: {removedIndicators.size}</span>
|
||||||
|
<span>Bad Indicator Threshold: 0.112 (10% of max fitness)</span>
|
||||||
{removedIndicators.size > 0 && (
|
{removedIndicators.size > 0 && (
|
||||||
<span className="text-warning">
|
<span className="text-warning">
|
||||||
Removed: {Array.from(removedIndicators).slice(0, 3).join(', ')}
|
Removed: {Array.from(removedIndicators).slice(0, 3).join(', ')}
|
||||||
@@ -1819,9 +2017,9 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
<span>Estimated Completion: {estimatedCompletion}</span>
|
<span>Estimated Completion: {estimatedCompletion}</span>
|
||||||
</div>
|
</div>
|
||||||
<progress
|
<progress
|
||||||
className="progress progress-primary w-full"
|
className="progress progress-info w-full"
|
||||||
value={(currentGeneration - 1) * geneticConfig.populationSize + currentIndividual}
|
value={calculateProgress().completed}
|
||||||
max={totalIndividuals}
|
max={calculateProgress().total}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1829,33 +2027,66 @@ const BacktestGenetic: React.FC = () => {
|
|||||||
{/* Results */}
|
{/* Results */}
|
||||||
{showResults && results.length > 0 && (
|
{showResults && results.length > 0 && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* 3D Visualization */}
|
{/* 3D Visualizations */}
|
||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
<div className="card-body">
|
{/* Fitness vs Score vs Win Rate */}
|
||||||
<h3 className="card-title">3D Optimization Results</h3>
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<Plot
|
<div className="card-body">
|
||||||
data={plotData}
|
<h3 className="card-title">Fitness vs Score vs Win Rate</h3>
|
||||||
layout={{
|
<Plot
|
||||||
title: {text: 'Fitness Score vs Score vs Win Rate'},
|
data={plotData}
|
||||||
scene: {
|
layout={{
|
||||||
xaxis: {title: {text: 'Fitness Score'}},
|
title: {text: 'Fitness Score vs Score vs Win Rate'},
|
||||||
yaxis: {title: {text: 'Score'}},
|
scene: {
|
||||||
zaxis: {title: {text: 'Win Rate (%)'}},
|
xaxis: {title: {text: 'Fitness Score'}},
|
||||||
},
|
yaxis: {title: {text: 'Score'}},
|
||||||
width: 1100,
|
zaxis: {title: {text: 'Win Rate (%)'}},
|
||||||
height: 800,
|
},
|
||||||
margin: {
|
width: 750,
|
||||||
b: 20,
|
height: 600,
|
||||||
l: 0,
|
margin: {
|
||||||
pad: 0,
|
b: 20,
|
||||||
r: 0,
|
l: 0,
|
||||||
t: 0,
|
pad: 0,
|
||||||
},
|
r: 0,
|
||||||
paper_bgcolor: '#121212',
|
t: 0,
|
||||||
plot_bgcolor: theme.secondary,
|
},
|
||||||
}}
|
paper_bgcolor: '#121212',
|
||||||
config={{displayModeBar: false}}
|
plot_bgcolor: theme.secondary,
|
||||||
/>
|
}}
|
||||||
|
config={{displayModeBar: false}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TP% vs SL% vs PnL */}
|
||||||
|
<div className="card bg-base-100 shadow-xl">
|
||||||
|
<div className="card-body">
|
||||||
|
<h3 className="card-title">Take Profit vs Stop Loss vs PnL</h3>
|
||||||
|
<Plot
|
||||||
|
data={plotDataTPvsSL}
|
||||||
|
layout={{
|
||||||
|
title: {text: 'Take Profit % vs Stop Loss % vs PnL'},
|
||||||
|
scene: {
|
||||||
|
xaxis: {title: {text: 'Take Profit (%)'}},
|
||||||
|
yaxis: {title: {text: 'Stop Loss (%)'}},
|
||||||
|
zaxis: {title: {text: 'PnL ($)'}},
|
||||||
|
},
|
||||||
|
width: 750,
|
||||||
|
height: 600,
|
||||||
|
margin: {
|
||||||
|
b: 20,
|
||||||
|
l: 0,
|
||||||
|
pad: 0,
|
||||||
|
r: 0,
|
||||||
|
t: 0,
|
||||||
|
},
|
||||||
|
paper_bgcolor: '#121212',
|
||||||
|
plot_bgcolor: theme.secondary,
|
||||||
|
}}
|
||||||
|
config={{displayModeBar: false}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user