Eco-inspired Evolutionary Algorithm (ECO)
Table of Contents
Introduction
In this article, we will look at another optimization algorithm, another solution borrowed from nature. The Eco-inspired Evolutionary Algorithm is a metaheuristic optimization method that uses ecological concepts (habitat, species interactions, ecological succession) to model the search for solutions. It was proposed in the early 2010s as an extension of the ideas behind evolutionary algorithms, but with an emphasis on ecological processes.
Nature has always served as an inexhaustible source of inspiration for the development of computational models and paradigms. Evolutionary computation and swarm intelligence offer a wide range of optimization strategies based on the observation of natural processes: the evolution of species, the behavior of social groups, the dynamics of the immune system, food-search strategies, and ecological interactions among different populations.
However, until recently, such fundamental ecological concepts as habitats, ecological interactions, and ecological succession had remained virtually unexplored in the context of optimization. The Eco-inspired Evolutionary Algorithm, proposed by Rafael Stubs Parpinelli and Heitor Silvério Lopes in 2011, fills this gap by offering a fundamentally new approach to the design of cooperative search algorithms.
Algorithm Implementation
At the heart of ECO lies an ecological metaphor in which a multitude of populations of candidate solutions coexist, interact, and coevolve with one another:
| Ecological concept | Computational interpretation |
|---|---|
| Habitat | A cluster of populations located in the same region of the search space |
| Population | A group of candidate solutions evolving according to a common strategy |
| Centroid | The geometric center (average position) of the individuals in a population, defining its "habitat region" |
| Intra-habitat interactions | Mating between individuals of populations within a single habitat |
| Inter-habitat interactions | Migration of the best individuals between habitats |
| Ecological succession | An iterative process of habitat formation and the establishment of connections |
Multiple populations. Unlike classical evolutionary algorithms with a single population, ECO operates with multiple independent populations, each of which evolves according to its own search strategy. This provides a natural separation between intensification (exploitation) and diversification (exploration).
Dynamic Habitat Formation. At each iteration, the algorithm calculates the centroids of the populations and groups nearby populations into habitats based on a threshold distance "ρ". This allows the system to self-organize and adapt to the landscape of the objective function.
Two-level interaction. "Intra-habitat" interactions (mating) intensify local search in promising regions, while "inter-habitat" interactions (migrations) facilitate the exchange of genetic material between distant areas of the search space.
Coevolution. Populations are not isolated from one another — they form a single ecosystem in which the success of one population influences the evolution of neighboring ones. This creates a positive feedback loop that guides the search toward the global optimum.
Algorithm structure. The main ECO (ecological succession) loop consists of the following steps:
1. Initializing populations in random regions of the search space
2. WHILE the stopping criterion is not met:
2.1. Evolutionary period — each population evolves independently.
2.2. Calculating population centroids.
2.3. Forming habitats based on the proximity of centroids.
2.4. Intra-habitat interactions — mating between adjacent populations.
2.5. Inter-habitat interactions — migration of the best individuals between habitats.
3. Return the best solution found.
Imagine a natural ecosystem: several packs of animals inhabit a vast area. Each pack occupies its own territory — its habitat. Within a habitat, the packs interact with one another, exchanging individuals. Sometimes the strongest individuals migrate to distant habitats, carrying their genes with them. Over time, all the packs concentrate in areas with the best living conditions. This is precisely the model that the ECO algorithm reproduces in its search for optimal solutions.
An individual is a single candidate solution to a problem. For example, if we optimize two parameters of a trading strategy (the moving average period and the stop-loss level), then an individual is a specific pair of values (20, 50).
A population is a group of individuals that live near one another and evolve together. A population is characterized by its center — the average position of all individuals.
A habitat is a grouping of nearby populations. If the centers of two populations are located close to each other, they form a shared habitat and begin to exchange individuals.
Example: searching for a mountain peak. Suppose five groups of climbers are searching for the highest point in a mountain range. Each group starts from a random location:
Height
▲
│ ∧ ∧∧
│ ╱ ╲ ∧∧ ╱ ╲
│ ╱ ╲ ╱ ╲ ╱ ╲
│ ╱ ╲╱ ╲ ╱ ╲
│╱ ╲╱ ╲
└──────────────────────────────► Position
G1 G2 G3 G4 G5
G1–G5 — starting positions of the groups
- Step 1: Local search. Each group explores its surroundings, climbing higher.
- Step 2: Identifying habitats. Groups G1 and G2 turned out to be close together — they form a habitat. Groups G4 and G5 are also nearby — they form another habitat. Group G3 is alone for now.
- Step 3: Exchange within the habitat. G1 and G2 share information about the routes they have found. The best climber from G1 shows the way to a member of G2.
- Step 4: Migration between habitats. The best climber from Habitat 1 goes to Habitat 2, bringing knowledge about promising directions.
Result: after several iterations, all groups concentrate around the main peak.
Algorithm Structure. The algorithm operates in a loop, repeating five main steps:
Step 1: Population evolution. Each individual tries to improve its position in two ways:
- random exploration — displacement relative to a random neighbor,
- attraction to the leader — movement toward the best individual in the population.
Example for an individual at position (3, 4): the neighbor is at (5, 2), and the leader is at (7, 8). Random displacement: (3, 4) - (5, 2) = (-2, 2); multiply by a random coefficient. Attraction to the leader: (7, 8) - (3, 4) = (4, 4); multiply by a small coefficient. New position = current position + displacement + attraction
The best individual in the population remains unchanged — this ensures that the result achieved is preserved.
Step 2: Calculating centroids. A centroid is the “center of mass” of a population — the average position of all individuals. A population of 4 individuals:
5 ┤ ●(2,5)
4 ┤ ●(1,4)
3 ┤ ◉(2,3) ← centroid
2 ┤ ●(3,2)
1 ┤●(1,1)
└─────────────
0 1 2 3 4 5
Centroid = ((1+2+3+1)/4, (1+5+2+4)/4) = (1.75, 3.0) ≈ (2, 3). The centroid indicates where the population “lives” in the search space.
Step 3: Habitat formation. Populations whose centroids are close to one another are grouped into a habitat. “Closeness” is defined by the proximity threshold “ρ”: 5 populations with centroids:
Distances: P0–P1 = 1.5 (close); P0–P2 = 3.0 (far); P3–P4 = 4.0 (far). With proximity threshold “ρ” = 2.0: Habitat A: {P0, P1} — nearby populations; Habitat B: {P2} — single population; Habitat C: {P3} — single population; Habitat D: {P4} — single population.
Step 4: Mating within habitats. Populations within a single habitat exchange genetic material: Habitat A contains populations P0 and P1.
1. From P0, we select a strong individual (tournament selection). We take 3 random individuals and select the best one → Parent 1: (4, 6).
2. From P1, we select in the same way: → Parent 2: (8, 2).
3. Create an offspring (crossover): x: between 4 and 8 → randomly select 5.5; y: between 2 and 6 → randomly select 4.0 → Offspring: (5.5, 4.0). This would correspond to an arithmetic/intermediate crossover; however, in our implementation we use uniform crossover (for each coordinate, one of the parents is chosen at random), rather than the arithmetic mean. An offspring (5.5, 4.0) is possible only if the average was taken for "x."
4. The offspring replaces a weak individual in P1. This intensifies the search in promising regions.
Step 5: Migration between habitats. The best individuals travel between habitats, spreading good solutions:
Habitat A Habitat B
┌─────────────┐ ┌─────────────┐
│ P0: best │ │ P2 │
│ (3, 7) │ ─────────────► │ ? │
│ ★ │ migration │ ★ │
└─────────────┘ └─────────────┘
The best individual from P0 is copied to P2, replacing a random (non-best) individual. Migration prevents habitat isolation and accelerates the spread of the solutions found.
System dynamics. At the beginning of the run, the populations are scattered throughout the search space — there are many isolated habitats. As optimization proceeds, the populations converge toward the best regions — habitats grow larger:
Iteration 1: 6 habitats (exploration); Iteration 25: 3 habitats (transition); Iteration 50: 1 habitat (exploitation). This is an illustrative example; in practice, it depends on the function and the parameter "ρ."

Figure 1. Illustration of how the ECO algorithm works
The illustration shows: on the left — the search space: 3 habitats (H₀, H₁, H₂) represented as colored regions, and 6 subpopulations (Sp₀–Sp₅) with agents (purple dots). The best individuals are highlighted in red. Dotted lines indicate adjacency between populations (dist ≤ ρ). Green arrows indicate mating within a habitat. Red arrows indicate inter-habitat migrations. The right-hand side shows the algorithm flow: 6 steps of the algorithm with formulas.
Let's write an implementation of the ECO algorithm.
The S_ECO_Coord structure. An auxiliary structure for storing a single centroid coordinate. The "centroids" array stores the centroids of all subpopulations in a flat representation of size [numPops × coords], where each element is one coordinate of one centroid.
//———————————————————————————————————————————————————————————————————— struct S_ECO_Coord { double v; }; //————————————————————————————————————————————————————————————————————
The C_AO_ECOi class, the main class of the algorithm, which inherits from the base class C_AO. This class implements an evolutionary algorithm inspired by ecological interactions. The algorithm divides the population into subpopulations that simulate individual ecosystems. These subpopulations interact with one another at both the local and global levels, which helps in the search for an optimal solution.
The SetParams method updates the internal parameters of the ECOi algorithm based on the values stored in the "params" array. This method is typically called after parameters have been changed externally.
//———————————————————————————————————————————————————————————————————— class C_AO_ECOi : public C_AO { public: ~C_AO_ECOi () { } C_AO_ECOi () { ao_name = "ECOi"; ao_desc = "Eco-inspired Evolutionary Algorithm"; ao_link = "https://www.mql5.com/en/articles/20608"; popSize = 100; // Total population size numPops = 50; // Number of subpopulations (N-POP) evoStep = 1; // Evolutionary period iterations (EVO-STEP) rho = 0.3; // Proximity threshold for habitat formation tournSize = 8; // Tournament selection size (T-SIZE) ArrayResize (params, 5); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "numPops"; params [1].val = numPops; params [2].name = "evoStep"; params [2].val = evoStep; params [3].name = "rho"; params [3].val = rho; params [4].name = "tournSize"; params [4].val = tournSize; } void SetParams () { popSize = (int)params [0].val; numPops = (int)params [1].val; evoStep = (int)params [2].val; rho = params [3].val; tournSize = (int)params [4].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP); void Moving (); void Revision (); //------------------------------------------------------------------ int numPops; // Number of subpopulations int evoStep; // Evolutionary period double rho; // Proximity threshold int tournSize; // Tournament size private: //————————————————————————————————————————————————————————— int evoCounter; // Counter for the evolutionary period int subPopSize; // Size of each subpopulation S_ECO_Coord centroids []; // [numPops * coords] — flattened centroid array int habitat []; // Habitat index for each subpopulation int numHabitats; bool adjacency []; // Adjacency matrix [numPops * numPops] — flattened int uniqueHab []; // Array of unique habitat indices double maxDist; // Maximum possible distance (for normalization) void CalculateCentroids (); void BuildAdjacencyMatrix (); void FormHabitats (); void IntraHabitatMating (); void InterHabitatMigration (); void EvolutivePeriod (); int TournamentSelect (int popIdx); void Crossover (int parent1, int parent2, int childIdx); double EuclideanDistanceNorm (int pop1, int pop2); int GetBestInPopulation (int popIdx); int GetRandomInPopulation (int popIdx, int excludeIdx); void BoundaryControl (int idx); int GetPopStart (int popIdx); int GetPopEnd (int popIdx); }; //————————————————————————————————————————————————————————————————————
The Init method initializes the algorithm before optimization starts. The method calls the base class StandardInit method to initialize common structures. The evolutionary period counter is reset, and numPops is adjusted if it exceeds popSize or is less than 1. subPopSize is calculated as popSize / numPops. Memory is allocated for the following arrays: centroids, habitat, adjacency, and uniqueHab. The maxDist value is calculated: the length of the search space diagonal used to normalize distances. Habitats are initialized: each subpopulation initially forms its own habitat.
//———————————————————————————————————————————————————————————————————— bool C_AO_ECOi::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ evoCounter = 0; // Ensure that numPops does not exceed popSize if (numPops > popSize) numPops = popSize; if (numPops < 1) numPops = 1; subPopSize = popSize / numPops; if (subPopSize < 1) subPopSize = 1; // Allocate arrays ArrayResize (centroids, numPops * coords); ArrayResize (habitat, numPops); ArrayResize (adjacency, numPops * numPops); ArrayResize (uniqueHab, numPops); // Calculate the maximum distance for normalization maxDist = 0.0; for (int c = 0; c < coords; c++) { double width = rangeMax [c] - rangeMin [c]; maxDist += width * width; } maxDist = MathSqrt (maxDist); if (maxDist < 1e-10) maxDist = 1.0; // Initialize habitats—each population is its own habitat for (int p = 0; p < numPops; p++) { habitat [p] = p; } numHabitats = numPops; return true; } //————————————————————————————————————————————————————————————————————
The Moving method. Purpose: the main method that executes a single step of the algorithm. It is called on every iteration and controls the search process. First iteration (no revision). The method initializes the entire population with random values that are normally distributed around random centers within the specified ranges.
Subsequent iterations: increments the evolutionary iteration counter (evoCounter). Calls the EvolutivePeriod method to perform local search within each subpopulation. After the evolutionary period ends (when evoStep is reached): CalculateCentroids is called to determine the centroids of each subpopulation; BuildAdjacencyMatrix is called to construct an adjacency matrix based on the distances between the centroids; FormHabitats is called to form habitats based on the adjacency matrix; IntraHabitatMating is called to exchange genetic information (crossover) between neighboring populations within a single habitat; and InterHabitatMigration is called to carry out "great migrations" (the transfer of the best individuals between different habitats).
The evolutionary iteration counter (evoCounter) is reset to zero.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::Moving () { //------------------------------------------------------------------ // First iteration: random initialization of all populations if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { // Initialize with a normal distribution around a random center (Box-Muller) double center = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [i].c [c] = u.GaussDistribution (center, rangeMin [c], rangeMax [c], 2.0); a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } //------------------------------------------------------------------ evoCounter++; //================================================================== // Evolutionary Period: simple evolution for each subpopulation // Using ABC-inspired local search (employed bee phase) //================================================================== EvolutivePeriod (); //================================================================== // After evoStep iterations, perform ecological interactions //================================================================== if (evoCounter >= evoStep) { // Step 5: Calculate the centroids for each population (Eq. 1) CalculateCentroids (); // Step 6: Build the adjacency matrix and form habitats BuildAdjacencyMatrix (); FormHabitats (); // Step 7–8: Intra-habitat interactions (mating) IntraHabitatMating (); // Step 9–10: Inter-habitat interactions (great migrations) InterHabitatMigration (); evoCounter = 0; } } //————————————————————————————————————————————————————————————————————
The EvolutivePeriod method. Purpose: evolutionary period — local search within each subpopulation. It implements an ABC-like strategy (the "employed bee" phase). The method iterates through each individual in the overall population and randomly selects one of its coordinates to modify, determines which subpopulation the individual belongs to, and then selects another random individual from the same subpopulation. Generates a new position for the current individual using a formula similar to ABC: x_new = x_i + phi * (x_i - x_k), where "phi" is a random coefficient. Applies the new position to the individual and performs boundary control (BoundaryControl).
Note: greedy selection (comparison with the previous value) is not performed — the fitness is recomputed by external code after Moving.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::EvolutivePeriod () { // Simple evolution: Each individual explores its neighborhood // Inspired by the ABC employed bee phase for (int i = 0; i < popSize; i++) { // Select a random dimension to modify int dim = u.RNDminusOne (coords); // Determine which subpopulation this individual belongs to int popIdx = i / subPopSize; if (popIdx >= numPops) popIdx = numPops - 1; // Select a different individual at random from the same subpopulation int otherIdx = GetRandomInPopulation (popIdx, i); // Generate a new position using an ABC-style equation: // x_new = x_i + phi * (x_i - x_k) double phi = u.RNDfromCI (-1.0, 1.0); double newVal = a [i].c [dim] + phi * (a [i].c [dim] - a [otherIdx].c [dim]); // Apply the new value a [i].c [dim] = newVal; BoundaryControl (i); } } //————————————————————————————————————————————————————————————————————
The CalculateCentroids method. Purpose: calculating the centroids (geometric centers) for each subpopulation. Formula (Equation 1 from the article): C = Σxi / Pop. The logic for each subpopulation is as follows: the centroid coordinates are initialized to zero; the coordinates of all individuals in the given subpopulation are summed; then the resulting sum is divided by the number of individuals in the subpopulation to obtain the average value (centroid).
Result: a filled "centroids" array.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::CalculateCentroids () { // Calculate the centroid for each subpopulation // C = sum(xi) / Pop (Equation 1 from the article) for (int p = 0; p < numPops; p++) { int startIdx = GetPopStart (p); int endIdx = GetPopEnd (p); int count = endIdx - startIdx; if (count < 1) count = 1; // Initialize the centroid to zero for (int c = 0; c < coords; c++) { centroids [p * coords + c].v = 0.0; } // Sum all positions for (int i = startIdx; i < endIdx; i++) { for (int c = 0; c < coords; c++) { centroids [p * coords + c].v += a [i].c [c]; } } // Divide by the population count for (int c = 0; c < coords; c++) { centroids [p * coords + c].v /= count; } } } //————————————————————————————————————————————————————————————————————
The BuildAdjacencyMatrix method. An adjacency matrix for the subpopulations is constructed based on the distance between their centroids. Adjacency criterion: two subpopulations are adjacent if the normalized Euclidean distance between their centroids is ≤ ρ (rho).
The method logic for each pair of subpopulations is as follows: the normalized Euclidean distance between their centroids is calculated using the EuclideanDistanceNorm method; if the distance is less than or equal to the specified "rho" threshold, the corresponding element of the adjacency matrix is set to 'true' (meaning they are adjacent); otherwise, it is set to 'false'. Result: a filled "adjacency" matrix.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::BuildAdjacencyMatrix () { // Build an adjacency matrix based on normalized Euclidean distance // Two populations are adjacent if the distance is less than or equal to rho for (int i = 0; i < numPops; i++) { for (int j = 0; j < numPops; j++) { if (i == j) { adjacency [i * numPops + j] = false; } else { double dist = EuclideanDistanceNorm (i, j); adjacency [i * numPops + j] = (dist <= rho); } } } } //————————————————————————————————————————————————————————————————————
The EuclideanDistanceNorm method calculates the normalized Euclidean distance between the centroids of two subpopulations. Parameters: pop1, pop2 — subpopulation indices. The method returns a distance in the range [0, 1], where 1 is the maximum possible distance (the diagonal of the search space).
Formula: dist = sqrt(Σ(c1[i] - c2[i])²) / maxDist. The calculated distance is divided by the maximum possible distance (maxDist) for normalization.
//———————————————————————————————————————————————————————————————————— double C_AO_ECOi::EuclideanDistanceNorm (int pop1, int pop2) { // Calculate the normalized Euclidean distance between two population centroids double dist = 0.0; for (int c = 0; c < coords; c++) { double diff = centroids [pop1 * coords + c].v - centroids [pop2 * coords + c].v; dist += diff * diff; } dist = MathSqrt (dist); // Normalize by the maximum possible distance return dist / maxDist; } //————————————————————————————————————————————————————————————————————
The FormHabitats method. Formation of habitats — groups of adjacent subpopulations. Algorithm: Union-Find (set union) based on an adjacency matrix.
Initially, each subpopulation is considered a separate habitat. Using the adjacency matrix, subpopulations that are adjacent (within a distance of rho from one another) are grouped into a single habitat. This is done by sequentially merging habitats. After all habitats have been formed, a list of unique habitat indices (uniqueHab) is compiled, and their total number (numHabitats) is computed.
Result: habitat — the habitat index for each subpopulation; uniqueHab — a list of unique habitat indices; and numHabitats — the number of habitats.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::FormHabitats () { // Form habitats using the union-find approach on an adjacency matrix // Initially, each population is its own habitat for (int p = 0; p < numPops; p++) { habitat [p] = p; } // Merge adjacent populations into the same habitat for (int i = 0; i < numPops; i++) { for (int j = i + 1; j < numPops; j++) { if (adjacency [i * numPops + j]) { // Merge habitats: all populations in j's habitat are merged into i's habitat int oldHab = habitat [j]; int newHab = habitat [i]; if (oldHab != newHab) { for (int p = 0; p < numPops; p++) { if (habitat [p] == oldHab) { habitat [p] = newHab; } } } } } } // Collect unique habitat indices numHabitats = 0; for (int p = 0; p < numPops; p++) { int h = habitat [p]; bool found = false; for (int k = 0; k < numHabitats; k++) { if (uniqueHab [k] == h) { found = true; break; } } if (!found) { uniqueHab [numHabitats] = h; numHabitats++; } } } //————————————————————————————————————————————————————————————————————
The IntraHabitatMating method. Intra-habitat mating — the exchange of genetic material between adjacent subpopulations within the same habitat. Ecological analogy: reproductive links between populations within a single habitat. All pairs of subpopulations are examined. If two subpopulations are located in the same habitat and are adjacent (according to the adjacency matrix), then two parents are selected from these two subpopulations using tournament selection (TournamentSelect). A random individual is selected from one of the subpopulations (excluding the best individual) to be replaced by an offspring. Crossover is performed on two parents to produce an offspring. The offspring replaces the selected individual in the target subpopulation.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::IntraHabitatMating () { // Mating between adjacent populations within the same habitat // For each pair of adjacent populations: perform tournament selection of parents, crossover, and replace a random individual for (int p = 0; p < numPops; p++) { for (int q = p + 1; q < numPops; q++) { // Check if they are in the same habitat and adjacent to each other if (habitat [p] == habitat [q] && adjacency [p * numPops + q]) { // Tournament selection from population p int parent1 = TournamentSelect (p); // Tournament selection from population q int parent2 = TournamentSelect (q); // Select a random individual (not the best) from population q to replace int bestQ = GetBestInPopulation (q); int childIdx = GetRandomInPopulation (q, bestQ); // Perform crossover—the child replaces the selected individual Crossover (parent1, parent2, childIdx); } } } } //————————————————————————————————————————————————————————————————————
The InterHabitatMigration method. Purpose: inter-habitat migration — transferring best solutions between habitats. Ecological analogy: The Great Migrations — the best individuals migrate to other habitats. For each habitat, a random subpopulation is selected from that habitat, the best individual in that subpopulation is identified (GetBestInPopulation), another random habitat (different from the original) is selected, and then a random subpopulation is selected from the target habitat. A random individual is selected from the target subpopulation (excluding the best one) to be replaced. The best individual from the source subpopulation is copied to the selected position, replacing the individual in the target subpopulation.
Note: The fitness of the migrated agent will be recalculated by external code.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::InterHabitatMigration () { // Great migrations: the best individual from one habitat migrates to another // For each habitat, select a random population and migrate the best individual to another habitat if (numHabitats < 2) return; // For each habitat, perform migration for (int hIdx = 0; hIdx < numHabitats; hIdx++) { int srcHab = uniqueHab [hIdx]; // Find populations in this habitat int popsInHab []; ArrayResize (popsInHab, 0); for (int p = 0; p < numPops; p++) { if (habitat [p] == srcHab) { int size = ArraySize (popsInHab); ArrayResize (popsInHab, size + 1); popsInHab [size] = p; } } if (ArraySize (popsInHab) == 0) continue; // Select a random population from this habitat int srcPopIdx = popsInHab [u.RNDminusOne (ArraySize (popsInHab))]; // Get the best individual from this population int bestIdx = GetBestInPopulation (srcPopIdx); // Select a random destination habitat (different from the source) int destHabIdx = u.RNDminusOne (numHabitats); int attempts = 0; while (uniqueHab [destHabIdx] == srcHab && attempts < numHabitats) { destHabIdx = (destHabIdx + 1) % numHabitats; attempts++; } if (uniqueHab [destHabIdx] == srcHab) continue; // No other habitat available int destHab = uniqueHab [destHabIdx]; // Find populations in the destination habitat int popsInDestHab []; ArrayResize (popsInDestHab, 0); for (int p = 0; p < numPops; p++) { if (habitat [p] == destHab) { int size = ArraySize (popsInDestHab); ArrayResize (popsInDestHab, size + 1); popsInDestHab [size] = p; } } if (ArraySize (popsInDestHab) == 0) continue; // Select a random population in the destination habitat int destPopIdx = popsInDestHab [u.RNDminusOne (ArraySize (popsInDestHab))]; // Select a random individual (not the best one) in the destination population to replace int bestDest = GetBestInPopulation (destPopIdx); int targetIdx = GetRandomInPopulation (destPopIdx, bestDest); // Copy the best individual to the target position (migration) for (int c = 0; c < coords; c++) { a [targetIdx].c [c] = a [bestIdx].c [c]; } // Note: fitness will be recalculated externally after Moving(). } } //————————————————————————————————————————————————————————————————————
The TournamentSelect method. Tournament selection is the selection of the agent with the best fitness from a random sample.
Parameter: "popIdx" is the subpopulation index. The method performs tournament selection within a single subpopulation. Randomly selects "tournSize" individuals from the specified subpopulation. Returns the index of the individual with the best fitness (the highest "f" value) among the selected individuals.
//———————————————————————————————————————————————————————————————————— int C_AO_ECOi::TournamentSelect (int popIdx) { // Tournament selection within a subpopulation int startIdx = GetPopStart (popIdx); int endIdx = GetPopEnd (popIdx); int popCount = endIdx - startIdx; if (popCount <= 0) return startIdx; int bestIdx = startIdx + u.RNDminusOne (popCount); double bestFit = a [bestIdx].f; for (int t = 1; t < tournSize && t < popCount; t++) { int idx = startIdx + u.RNDminusOne (popCount); if (a [idx].f > bestFit) { bestFit = a [idx].f; bestIdx = idx; } } return bestIdx; } //————————————————————————————————————————————————————————————————————
The Crossover method. Purpose: uniform crossover of two parents. The method performs uniform crossover between two parents to create an offspring. For each coordinate of the offspring, the corresponding coordinate from the first parent is selected with a probability of 0.5. Otherwise, a coordinate from the second parent is selected. After an offspring is created, boundary control (BoundaryControl) is performed.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::Crossover (int parent1, int parent2, int childIdx) { // Uniform crossover between two parents for (int c = 0; c < coords; c++) { if (u.RNDbool ()) { a [childIdx].c [c] = a [parent1].c [c]; } else { a [childIdx].c [c] = a [parent2].c [c]; } } BoundaryControl (childIdx); } //————————————————————————————————————————————————————————————————————
The GetBestInPopulation method. Description: Finds and returns the index of the best individual in the specified subpopulation. Iterates through all individuals in a given subpopulation and finds the one with the highest fitness value (f).
//———————————————————————————————————————————————————————————————————— int C_AO_ECOi::GetBestInPopulation (int popIdx) { int startIdx = GetPopStart (popIdx); int endIdx = GetPopEnd (popIdx); int bestIdx = startIdx; double bestFit = a [startIdx].f; for (int i = startIdx + 1; i < endIdx; i++) { if (a [i].f > bestFit) { bestFit = a [i].f; bestIdx = i; } } return bestIdx; } //————————————————————————————————————————————————————————————————————
The GetRandomInPopulation method. Purpose: Select a random agent from a subpopulation, excluding the specified index. Parameters: popIdx — the subpopulation index; excludeIdx — the index of the agent to be excluded. The method returns the index of a random individual from the specified subpopulation, with the option to exclude a specific individual.
Selects a random individual from the subpopulation; if the selected individual matches the one to be excluded (excludeIdx), a new selection is made (with a limit on the number of attempts) to avoid returning the excluded individual.
//———————————————————————————————————————————————————————————————————— int C_AO_ECOi::GetRandomInPopulation (int popIdx, int excludeIdx) { int startIdx = GetPopStart (popIdx); int endIdx = GetPopEnd (popIdx); int popCount = endIdx - startIdx; if (popCount <= 1) return startIdx; int idx = startIdx + u.RNDminusOne (popCount); int attempts = 0; while (idx == excludeIdx && attempts < 10) { idx = startIdx + u.RNDminusOne (popCount); attempts++; } return idx; } //————————————————————————————————————————————————————————————————————
The GetPopStart method. Calculating the boundaries of a subpopulation in the common array of agents. Returns the index of the first agent in the subpopulation.
//———————————————————————————————————————————————————————————————————— int C_AO_ECOi::GetPopStart (int popIdx) { int start = popIdx * subPopSize; if (start >= popSize) start = popSize - 1; return start; } //————————————————————————————————————————————————————————————————————
The GetPopEnd method. Returns the index following the last agent in the subpopulation. For the last subpopulation, end = popSize (it receives the “remaining” agents).
//———————————————————————————————————————————————————————————————————— int C_AO_ECOi::GetPopEnd (int popIdx) { int end = (popIdx + 1) * subPopSize; if (end > popSize) end = popSize; if (popIdx == numPops - 1) end = popSize; // The last population gets the remaining agents return end; } //————————————————————————————————————————————————————————————————————
The BoundaryControl method. Purpose: boundary control — returning the agent's coordinates to the valid range. For each coordinate of an individual, if the coordinate falls outside the allowable range (min, max), the reflecting boundary mechanism is applied (reflection from the boundary); if reflection does not help (the coordinate is still outside the range after several attempts), a random value within the allowable range is set.
The result is further processed using the SeInDiSp method, which adjusts the value taking the step (rangeStep) into account.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::BoundaryControl (int idx) { for (int c = 0; c < coords; c++) { double val = a [idx].c [c]; double min = rangeMin [c]; double max = rangeMax [c]; // Reflection boundary handling int iter = 0; while ((val < min || val > max) && iter < 10) { if (val < min) val = min + (min - val); if (val > max) val = max - (val - max); iter++; } // If reflection fails, use a random position if (val < min || val > max) { val = u.RNDfromCI (min, max); } a [idx].c [c] = u.SeInDiSp (val, min, max, rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
The Revision method finds the global best individual from the entire population and updates the previously found best individual (if the current one is better). Iterates over all individuals in the overall population. Compares the fitness of the current best individual (fB) with the fitness of the individual found in this iteration.
If a new best individual is found, its fitness and coordinates are saved.
//———————————————————————————————————————————————————————————————————— void C_AO_ECOi::Revision () { // Find and update the global best int bestIdx = 0; double bestFit = a [0].f; for (int i = 1; i < popSize; i++) { if (a [i].f > bestFit) { bestFit = a [i].f; bestIdx = i; } } if (bestFit > fB) { fB = bestFit; ArrayCopy (cB, a [bestIdx].c, 0, 0, coords); } } //————————————————————————————————————————————————————————————————————
Test Results
The ECOi algorithm (Eco-inspired Evolutionary Algorithm) achieved a score of 48% on the test function set. This score is right on the threshold for the top-optimization-algorithms table — just short of a stronger ranking position.
The algorithm employs a variety of mechanisms: ABC-like local search, habitat formation, intra-habitat mating with tournament selection and crossover, and inter-habitat migrations. This diversity strikes a balance between exploitation and exploration.
The habitat concept allows subpopulations to be dynamically grouped based on the proximity of their centroids, which, in theory, should facilitate the effective exchange of information between promising regions.
Adaptive population structure. The division into subpopulations with dynamic habitat formation creates a natural niche structure that is potentially useful for multimodal problems.
ECOi|Eco-inspired Evolutionary Algorithm|100.0|50.0|1.0|0.3|8.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.8454991049506774
25 Hilly's; Func runs: 10000; result: 0.5348563688609891
500 Hilly's; Func runs: 10000; result: 0.29261342860799167
=============================
5 Forest's; Func runs: 10000; result: 0.8820344476464197
25 Forest's; Func runs: 10000; result: 0.46479689499361054
500 Forest's; Func runs: 10000; result: 0.19211609956835546
=============================
5 Megacity's; Func runs: 10000; result: 0.6553846153846152
25 Megacity's; Func runs: 10000; result: 0.348
500 Megacity's; Func runs: 10000; result: 0.12630769230769343
=============================
Overall score: 4.34161 (48.24%)
Visualization of the ECO algorithm running on our test functions and, as an example, on standard test functions that can be selected from the list provided in the program.

ECOi on the Hilly test function

ECOi on the Forest test function

ECOi on the Megacity test function

ECOi on the Shaffer test function

ECOi on the GoldsteinPrice test function
The ECO algorithm is included for reference in the ranking table of the best population-based optimization methods.
| Description | Hilly | Hilly Final | Forest | Forest Final | Megacity (discrete) | Megacity Final | Final Result | % of MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||
| dingo_optimization_algorithm_M | 0.47968 | 0.45367 | 0.46369 | 1.39704 | 0.94145 | 0.87909 | 0.91454 | 2.73508 | 0.78615 | 0.86061 | 0.84805 | 2.49481 | 6.627 | 73.63 |
| across neighbourhood search | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| code lock algorithm (joo) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| animal migration optimization M | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| (P+O) evolution strategies | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| comet tail algorithm (joo) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| time evolution travel algorithm (joo) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| stochastic diffusion search M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| billiards optimization algorithm M | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| archery algorithm M | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| evolution of social groups (joo) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| simulated isotropic annealing (joo) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| extremal_optimization_M | 0.76166 | 0.77242 | 0.31747 | 1.85155 | 0.99999 | 0.76751 | 0.23527 | 2.00277 | 0.74769 | 0.53969 | 0.14249 | 1.42987 | 5.284 | 58.71 |
| biogeography based optimization | 0.94912 | 0.69456 | 0.35031 | 1.99399 | 0.93820 | 0.67365 | 0.25682 | 1.86867 | 0.74615 | 0.48277 | 0.17369 | 1.40261 | 5.265 | 58.50 |
| artificial cooperative search | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| dialectical algorithm | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| black hole algorithm M | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| anarchy society optimization | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| royal flush optimization (joo) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| atomic orbital search M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| turtle shell evolution algorithm (joo) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| backtracking_search_algorithm | 0.97309 | 0.54534 | 0.29098 | 1.80941 | 0.99999 | 0.58543 | 0.21747 | 1.80289 | 0.84769 | 0.36953 | 0.12978 | 1.34700 | 4.959 | 55.10 |
| differential evolution | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| successful restaurateur algorithm (joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| bonobo_optimizer | 0.77565 | 0.63805 | 0.32908 | 1.74278 | 0.88088 | 0.76344 | 0.25573 | 1.90005 | 0.61077 | 0.49846 | 0.14246 | 1.25169 | 4.895 | 54.38 |
| chemical reaction optimisation | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| blood inheritance optimization (joo) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| dream_optimization_algorithm | 0.85556 | 0.70085 | 0.37280 | 1.92921 | 0.73421 | 0.48905 | 0.24147 | 1.46473 | 0.77231 | 0.47354 | 0.18561 | 1.43146 | 4.825 | 53.62 |
| bird swarm algorithm | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| dolphin_echolocation_algorithm | 0.75995 | 0.67572 | 0.34171 | 1.77738 | 0.89582 | 0.64223 | 0.23941 | 1.77746 | 0.61538 | 0.44031 | 0.15115 | 1.20684 | 4.762 | 52.91 |
| harmony search | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| saplings sowing and growing | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| bacterial chemotaxis optimization M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| african buffalo optimization | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| (PO) evolution strategies | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| fractal-based algorithm | 0.79000 | 0.65134 | 0.28965 | 1.73099 | 0.87158 | 0.56823 | 0.18877 | 1.62858 | 0.61077 | 0.46062 | 0.12398 | 1.19537 | 4.555 | 50.61 |
| tabu search M | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| brain storm optimization | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| whale optimization algorithm M | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| artificial electric field algorithm | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| artificial ecosystem-based optimization algorithm | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| camel algorithm M | 0.78684 | 0.56042 | 0.35133 | 1.69859 | 0.82772 | 0.56041 | 0.24336 | 1.63149 | 0.64846 | 0.33092 | 0.13418 | 1.11356 | 4.444 | 49.37 |
| ant colony optimization M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| covariance_matrix_adaptation_evolution_strategy | 0.76258 | 0.72089 | 0.00000 | 1.48347 | 0.82056 | 0.79616 | 0.00000 | 1.61672 | 0.75846 | 0.49077 | 0.00000 | 1.24923 | 4.349 | 48.33 |
| duelist_algorithm | 0.92782 | 0.53778 | 0.27792 | 1.74352 | 0.86957 | 0.47536 | 0.18193 | 1.52686 | 0.62153 | 0.33569 | 0.11715 | 1.07437 | 4.345 | 48.28 |
| eco-inspired_evolutionary_algorithm | 0.84549 | 0.53485 | 0.29261 | 1.67295 | 0.88203 | 0.46479 | 0.19211 | 1.53893 | 0.65538 | 0.34800 | 0.12630 | 1.12968 | 4.342 | 48.24 |
| random walk | 0.48754 | 0.32159 | 0.25781 | 1.06694 | 0.37554 | 0.21944 | 0.15877 | 0.75375 | 0.27969 | 0.14917 | 0.09847 | 0.52734 | 2.348 | 26.09 |
Conclusions
A possible reason why the results were not as high as expected is the excessive complexity of the algorithm. The many interacting operators (evolutionary period, centroid calculation, habitat formation, mating, and migrations) create significant computational overhead. With a limited fitness-function evaluation budget, some resources are spent on "organizational" operations rather than on the actual search.
Weak local search. The ABC-like formula `x_new = x + φ·(x - x_other)` modifies only one dimension per iteration. For functions with strong interparameter dependencies, this may not be sufficiently efficient.
Loss of good solutions. During crossover and migration, the offspring replaces a random agent (not the worst one), which can lead to the loss of fairly good solutions. The absence of greedy selection during the evolutionary period may also slow convergence.
Sensitivity to parameters. The proximity threshold ρ has a critical influence on habitat formation. If the value is suboptimal, either all populations merge into a single habitat (resulting in a loss of diversity), or they remain isolated (resulting in a loss of information exchange).
Rare ecological interactions. Mating and migrations occur only every evoStep iterations. If this period is too long, useful information spreads slowly; if it is too short, the populations do not have time to converge locally.
The ECO algorithm demonstrates an interesting approach to optimization inspired by ecological concepts. A result of 48% is quite acceptable, especially given the originality of the idea. However, the complexity of the architecture, with its many operators, did not translate into proportionally high efficiency. Perhaps simplifying the algorithm somewhat (fewer operators, but more aggressive ones) or intensifying local search could improve the results. It is also worth noting that the ecological metaphor is better suited to problems with an explicit multimodal structure, where the habitat concept can fully demonstrate its advantages.

Figure 2. Color-coded ranking of algorithms based on the corresponding tests

Figure 3. Histogram of algorithm test results (on a scale from 0 to 100; the higher the score, the better, where 100 is the maximum possible theoretical result; the archive contains a script for calculating the ranking table)
Pros and cons of the ECO algorithm:
Pros:
- Good convergence on high- and medium-dimensional functions (not the worst results in the table).
Cons:
- Poor convergence on low-dimensional problems.
- Many external parameters.
- Many different solution-modification operators, which complicates debugging and tuning the algorithm.
An archive containing the latest versions of the algorithm code is attached to this article. The author of this article is not responsible for the absolute accuracy of the descriptions of canonical algorithms; many of them have been modified to improve search capabilities. The conclusions and judgments presented in the articles are based on the results of the experiments conducted.
Programs used in the article
| # | Name | Type | Description |
|---|---|---|---|
| 1 | #C_AO.mqh | Include file | Parent class for population-based optimization algorithms |
| 2 | #C_AO_enum.mqh | Include file | Enumeration of population-based optimization algorithms |
| 3 | TestFunctions.mqh | Include file | Test function library |
| 4 | TestStandFunctions.mqh | Include file | Library of test bench functions |
| 5 | Utilities.mqh | Include file | Utility functions library |
| 6 | CalculationTestResults.mqh | Include file | Script for calculating results for the comparison table |
| 7 | Testing AOs.mq5 | Script | A unified test bench for all population-based optimization algorithms |
| 8 | Simple use of population optimization algorithms.mq5 | Script | A simple example of using population-based optimization algorithms without visualization |
| 9 | Test_AO_ECOi.mq5 | Script | Test bench for ECOi |
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/20608
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Building Your Personal Expert Advisor (Part 5): Risk Management IV—Basket Risk and Strategy-Specific Sizing
From Basic to Intermediate: Classes (II)
Features of Experts Advisors
Neural Networks in Trading: Generalizing Time Series Without Data-Specific Dependence (Conclusion)
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use