Parameter optimization is the most common source of EA overfitting. Genetic algorithms (GA) outperform brute-force grid search by exploring the parameter space intelligently. Below is a production-grade GA optimizer adapted for MQL5.
1. Chromosome Representation
Each parameter set is a chromosome. Encode continuous and discrete parameters:
```cpp
struct SChromosome {
double params[10]; // up to 10 parameters
double fitness; // fitness score (Sharpe, profit factor, etc.)
int generation;
};
```
2. Population Initialization with Sobol Sequence
Better than pure random: use quasi-random Sobol for uniform coverage:
```cpp
void InitPopulation(SChromosome &pop[], int size, double &min[], double &max[]) {
for(int i = 0; i < size; i++) {
for(int p = 0; p < ParamCount(); p++) {
double r = SobolSequence(i + 1, p + 1); // quasi-random
pop[i].params[p] = min[p] + r * (max[p] - min[p]);
}
}
}
```
3. Fitness Function with Walk-Forward Penalty
Avoid overfitting by testing on out-of-sample period:
```cpp
double ComputeFitness(SChromosome &ch, int inSampleStart, int outSampleStart) {
double inSamplePF = BacktestEA(ch.params, inSampleStart, outSampleStart);
double outSamplePF = BacktestEA(ch.params, outSampleStart, TimeCurrent());
// Penalty if out-of-sample performance deviates significantly
double ratio = (inSamplePF > 0) ? outSamplePF / inSamplePF : 0;
double fitness = inSamplePF * MathMin(1.0, ratio); // reward consistency
return fitness;
}
```
4. Crossover and Mutation Operators
Simulated Binary Crossover (SBX) for continuous parameters:
```cpp
void Crossover(SChromosome &p1, SChromosome &p2, SChromosome &c1, SChromosome &c2, double eta) {
for(int i = 0; i < ParamCount(); i++) {
double u = MathRand() / 32767.0;
double beta = (u <= 0.5) ? pow(2*u, 1/(eta+1)) : pow(1/(2*(1-u)), 1/(eta+1));
c1.params[i] = 0.5 * ((1+beta)*p1.params[i] + (1-beta)*p2.params[i]);
c2.params[i] = 0.5 * ((1-beta)*p1.params[i] + (1+beta)*p2.params[i]);
}
}
// Polynomial mutation
void Mutate(SChromosome &c, double &min[], double &max[], double eta_m, double prob) {
for(int i = 0; i < ParamCount(); i++) {
if(MathRand()/32767.0 < prob) {
double r = MathRand()/32767.0;
double delta = (r <= 0.5) ? pow(2*r, 1/(eta_m+1)) - 1 : 1 - pow(2*(1-r), 1/(eta_m+1));
c.params[i] += delta * (max[i] - min[i]);
c.params[i] = MathMax(min[i], MathMin(max[i], c.params[i]));
}
}
}
```
5. Complete GA Loop
```cpp
void RunGA(int generations, int popSize) {
SChromosome population[];
InitPopulation(population, popSize, minParams, maxParams);
for(int gen = 0; gen < generations; gen++) {
// Evaluate fitness
for(int i = 0; i < popSize; i++)
population[i].fitness = ComputeFitness(population[i], inSampleStart, outSampleStart);
// Elitism: keep top 10%
SortByFitness(population);
// Generate offspring via tournament selection + crossover + mutation
SChromosome newPop[];
for(int i = 0; i < popSize * 0.9; i++) {
SChromosome parent1 = TournamentSelect(population);
SChromosome parent2 = TournamentSelect(population);
SChromosome child1, child2;
Crossover(parent1, parent2, child1, child2, 15);
Mutate(child1, minParams, maxParams, 20, 0.1);
newPop[i] = child1;
}
// Combine elites and offspring
ArrayCopy(population, newPop, popSize * 0.1, 0);
}
}
```
6. Walk-Forward Validation Protocol
Reference: Aronson, David. "Evidence-Based Technical Analysis." Wiley, 2006; MQL5 Documentation, "Genetic Algorithm for Optimization."