Update genetic algo to select more indicators

This commit is contained in:
2025-07-17 15:27:08 +07:00
parent 3ba06f94be
commit da48ee37ba
2 changed files with 61 additions and 18 deletions

View File

@@ -307,7 +307,7 @@ public class GeneticService : IGeneticService
}
// Create fitness function first
var fitness = new TradingBotFitness(_backtester, request);
var fitness = new TradingBotFitness(_backtester, request, _logger);
// Create genetic algorithm with better configuration
var ga = new GeneticAlgorithm(
@@ -496,14 +496,16 @@ public class TradingBotChromosome : ChromosomeBase
private readonly List<IndicatorType> _eligibleIndicators;
private readonly double _maxTakeProfit;
private readonly Random _random = new Random();
private int[]? _indicatorSelectionPattern;
// Gene structure:
// 0-2: Trading parameters (takeProfit, stopLoss, cooldownPeriod, maxLossStreak)
// 3-6: Indicator selection (up to 4 indicators)
// 7+: Indicator parameters (period, fastPeriods, etc.)
// 0-3: Trading parameters (takeProfit, stopLoss, cooldownPeriod, maxLossStreak)
// 4: Loopback period
// 5-4+N: Indicator selection (N = number of eligible indicators)
// 5+N+: Indicator parameters (period, fastPeriods, etc.)
public TradingBotChromosome(List<IndicatorType> eligibleIndicators, double maxTakeProfit)
: base(4 + 1 + 4 +
: base(4 + 1 + eligibleIndicators.Count +
eligibleIndicators.Count * 8) // Trading params + loopback + indicator selection + indicator params
{
_eligibleIndicators = eligibleIndicators;
@@ -535,16 +537,22 @@ public class TradingBotChromosome : ChromosomeBase
// LoopbackPeriod gene (always between 5 and 20)
return new Gene(GetRandomIntInRange((5, 20)));
}
else if (geneIndex < 9)
else if (geneIndex < 5 + _eligibleIndicators.Count)
{
// Indicator selection (0 = not selected, 1 = selected)
return new Gene(_random.Next(2));
// Generate a random combination of up to 4 indicators
if (_indicatorSelectionPattern == null)
{
GenerateIndicatorSelectionPattern();
}
return new Gene(_indicatorSelectionPattern![geneIndex - 5]);
}
else
{
// Indicator parameters
var indicatorIndex = (geneIndex - 9) / 8;
var paramIndex = (geneIndex - 9) % 8;
var indicatorIndex = (geneIndex - (5 + _eligibleIndicators.Count)) / 8;
var paramIndex = (geneIndex - (5 + _eligibleIndicators.Count)) % 8;
if (indicatorIndex < _eligibleIndicators.Count)
{
@@ -596,6 +604,11 @@ public class TradingBotChromosome : ChromosomeBase
{
var clone = new TradingBotChromosome(_eligibleIndicators, _maxTakeProfit);
clone.ReplaceGenes(0, GetGenes());
// Copy the selection pattern to maintain consistency
if (_indicatorSelectionPattern != null)
{
clone._indicatorSelectionPattern = (int[])_indicatorSelectionPattern.Clone();
}
return clone;
}
@@ -604,9 +617,10 @@ public class TradingBotChromosome : ChromosomeBase
var selected = new List<GeneticIndicator>();
var genes = GetGenes();
for (int i = 0; i < 4; i++) // Check first 4 indicator slots
// Check all indicator selection slots (genes 5 to 5+N-1 where N is number of eligible indicators)
for (int i = 0; i < _eligibleIndicators.Count; i++)
{
if (genes[5 + i].Value.ToString() == "1" && i < _eligibleIndicators.Count)
if (genes[5 + i].Value.ToString() == "1")
{
var indicator = new GeneticIndicator
{
@@ -614,7 +628,7 @@ public class TradingBotChromosome : ChromosomeBase
};
// Add parameters for this indicator
var baseIndex = 9 + i * 8;
var baseIndex = 5 + _eligibleIndicators.Count + i * 8;
var paramName = GetParameterName(0); // period
if (paramName != null && HasParameter(_eligibleIndicators[i], paramName))
{
@@ -675,11 +689,8 @@ public class TradingBotChromosome : ChromosomeBase
var genes = GetGenes();
var selectedIndicators = GetSelectedIndicators();
// Ensure we have at least one indicator
if (!selectedIndicators.Any())
{
selectedIndicators.Add(new GeneticIndicator { Type = _eligibleIndicators[0] });
}
// The selection pattern ensures at least one indicator is always selected
// No need for fallback logic
// Get take profit from chromosome (gene 0)
var takeProfit = Convert.ToDouble(genes[0].Value);
@@ -792,6 +803,26 @@ public class TradingBotChromosome : ChromosomeBase
return GeneticService.IndicatorParamMapping.ContainsKey(indicator) &&
GeneticService.IndicatorParamMapping[indicator].Contains(paramName);
}
private void GenerateIndicatorSelectionPattern()
{
// Generate a random combination of up to 4 indicators
var selectedCount = _random.Next(1, Math.Min(5, _eligibleIndicators.Count + 1)); // 1 to 4 indicators
var selectedIndices = new HashSet<int>();
// Randomly select indices
while (selectedIndices.Count < selectedCount)
{
selectedIndices.Add(_random.Next(_eligibleIndicators.Count));
}
// Create the selection pattern
_indicatorSelectionPattern = new int[_eligibleIndicators.Count];
for (int i = 0; i < _eligibleIndicators.Count; i++)
{
_indicatorSelectionPattern[i] = selectedIndices.Contains(i) ? 1 : 0;
}
}
}
/// <summary>
@@ -818,11 +849,13 @@ public class TradingBotFitness : IFitness
private readonly IBacktester _backtester;
private readonly GeneticRequest _request;
private GeneticAlgorithm _geneticAlgorithm;
private readonly ILogger<GeneticService> _logger;
public TradingBotFitness(IBacktester backtester, GeneticRequest request)
public TradingBotFitness(IBacktester backtester, GeneticRequest request, ILogger<GeneticService> logger)
{
_backtester = backtester;
_request = request;
_logger = logger;
}
public void SetGeneticAlgorithm(GeneticAlgorithm geneticAlgorithm)
@@ -843,6 +876,15 @@ public class TradingBotFitness : IFitness
// Get current generation number (default to 0 if not available)
var currentGeneration = _geneticAlgorithm?.GenerationsNumber ?? 0;
// Log the indicators being tested (only in first few generations to avoid spam)
if (currentGeneration < 5)
{
var selectedIndicators = tradingBotChromosome.GetSelectedIndicators();
var indicatorNames = string.Join(", ", selectedIndicators.Select(i => i.Type.ToString()));
_logger.LogDebug("Generation {Generation}: Testing indicators: {Indicators}",
currentGeneration, indicatorNames);
}
// Run backtest
var backtest = _backtester.RunTradingBotBacktest(
config,