Artificial Atom Algorithm (A3)
Contents
Introduction
In algorithmic trading, one of the key tasks is optimizing the parameters of trading strategies. Traders face the need to adjust numerous variables daily: indicator periods, stop loss and take profit levels, position sizes, time filters, and dozens of other parameters. Each combination of these parameters can dramatically change the strategy's performance, and we continue to search for effective algorithms that can find the best solution in a reasonable amount of time.
In this article, we will look at another optimization algorithm, namely the Artificial Atom Algorithm (A3), which is a metaheuristic optimization algorithm inspired by chemical reactions. It was developed by Turkish scientists and first presented to the scientific community in 2018.
Algorithm implementation
While working on this algorithm, I encountered a number of ambiguities in the original description. Some operators are not presented in sufficient detail, which makes their practical implementation difficult. In this regard, I will provide the original description of the algorithm, however, the implementation was carried out at my own discretion - based on personal experience and the structural principles characteristic of such algorithms. This represents a significant gap in the original work, leaving the developer with considerable room for interpretation. In addition, during the course of the work, changes were made to the base class, which will also be discussed after the presentation of the basic implementation of the algorithm.
The algorithm models the interaction of atoms and electrons to find the optimal solution. The basic components are atoms that represent potential solutions to the problem. Electrons represent decision variables. Covalent bonding is an operator for storing and replicating the best solutions, ionic bonding is an operator for exploring the search space and achieving a global optimum.
The algorithm starts by randomly generating a set of atoms, then evaluates the quality of each atom using the objective function, and then applies covalent and ionic bond operators to improve solutions (unfortunately, there is no description of how this is done). Next, it is necessary to evaluate the effect of electrons. Again, it is unclear how to do that. Electrons and atoms should be sorted. Okay, we can sort the atoms (solutions), but how we can sort the electrons is a mystery (it is like trying to sort a person's limbs, arms with legs, right with left - it does not matter). Repeat the process iteratively until the stopping criterion is reached. Well, as the saying goes, you have to work with what you have. We need to try to assemble an algorithm applying the authors' ideas.
I present here my final revised pseudocode of the A3 algorithm.
INITIALIZATION Inputs:
popSize — number of atoms (default 10)
covalentRate — covalent bond coefficient (default 0.1)
rangeMin [], rangeMax [] — search limits for each variable
rangeStep [] — discretization step
Preliminary calculations:
Calculate the number of elite atoms:
covalentCount = floor (popSize × covalentRate)
Limit: minimum 1, maximum (popSize - 1)
Create a population of popSize atoms
For each atom, initialize:
Coordinates c []
Local worst solution cW []
Fitness f, worst fitness fW
BASIC OPTIMIZATION LOOP
STEP 1: First iteration (if revision = false)
FOR each atom i from 0 to popSize-1:
FOR each coordinate j from 0 to coords-1:
Generate a random value in the range [rangeMin [j], rangeMax [j]]
Apply discretization according to rangeStep [j]
Save to a [i].c [j]
Set revision = true
End the iteration
STEP 2: Updating atom positions (Moving)
FOR each atom i from 0 to popSize-1:
IF i ≤ covalentCount (atom is elite):
FOR each coordinate c from 0 to coords-1:
IF random() < covalentRate (with 10% probability):
// Move towards the global best solution
step = random() × (cB [c] - a [i].c [c]) × covalentCount
a [i].c [c] = a [i].c [c] + step
OTHERWISE (with 90% probability):
// Generate via PowerDistribution around the best
a[i]. c[c] = PowerDistribution(center=cB [c], min=rangeMin [c], max=rangeMax [c], degree=20)
Apply discretization to a [i].c [c]
OTHERWISE (the atom is not elite):
FOR each coordinate c from 0 to coords-1:
// Select a random elite atom as a sample
ind = random_integer(0, covalentCount)
// Movement from local worst to the position of elite atom
direction = a [ind].c [c] - a [i].cW [c]
step = random() × direction × (1.0 - covalentCount)
a [i].c [c] = a [i].c [c] + step
Apply discretization to a [i].c [c]
STEP 3: Calculate fitness
FOR each atom i:
Calculate the fitness of a[i].f through the objective function
STEP 4: Memory update and sorting (Revision)
// Update local worst solutions
FOR each atom i from 0 to popSize-1:
IF a [i].f < a [i].fW:
a [i].fW = a [i].f
Copy a [i].c to a [i].cW
Update global worst fW
// Sort the population in descending order of fitness
Sort the array of atoms a[] by the field f (from best to worst)
// Update the global best
IF a [0].f > fB:
fB = a [0].f
Copy a [0].c to cB
// Update the global worst
IF a [popSize-1].f < fW:
fW = a [popSize-1].f
Copy a [popSize-1].c to cW
STEP 5: Check the stopping criterion
IF the maximum number of iterations has been reached:
Complete the algorithm
OTHERWISE:
Go to STEP 2
Now we can move on to practical implementation in code. Let's write a class that will represent the implementation of an artificial atomic system algorithm based on the metaphor of atoms and their bonds. This class inherits the basic capabilities of the C_AO class and extends them in the context of the A3 algorithm.
Parameter setting methods: the SetParams() function reads the current values from the parameters array and stores them in the corresponding variables. This provides flexibility in customization before running the algorithm. Initialization: the Init() method prepares the parameters and structure for execution, receiving the value ranges and steps of the variables, as well as the number of epochs. The main operations of the algorithm: the Moving() and Revision() methods implement the movement and update phases of the atoms within the system, which corresponds to the stages of searching for the optimal solution. Parameters and variables:
- popSize — atom population size (number of elements in the population).
- covalentRate — coefficient characterizing bond formation between atoms.
- covalentCount — internal number of atoms involved in forming bonds (used internally for calculations).
The general purpose of the class is to model the behavior of a system of atoms taking into account their connections, which is implemented through parameters and operating methods. The search and optimization logic are then implemented based on these concepts.
//———————————————————————————————————————————————————————————————————— class C_AO_A3 : public C_AO { public: //---------------------------------------------------------- ~C_AO_A3 () { } C_AO_A3 () { ao_name = "A3"; ao_desc = "Artificial Atom Algorithm"; ao_link = "https://www.mql5.com/en/articles/18958"; popSize = 10; // number of atoms (m) covalentRate = 0.1; // covalent bond coefficient (β) ArrayResize (params, 2); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "covalentRate"; params [1].val = covalentRate; } void SetParams () { popSize = (int)params [0].val; covalentRate = params [1].val; } bool Init (const double &rangeMinP [], // minimum values const double &rangeMaxP [], // maximum values const double &rangeStepP [], // step change const int epochsP = 0); // number of epochs void Moving (); void Revision (); //------------------------------------------------------------------ double covalentRate; // covalent bond coefficient (β) private: //--------------------------------------------------------- int covalentCount; // number of atoms for covalent bond }; //————————————————————————————————————————————————————————————————————
The Init() method is a key step in preparing the algorithm for operation. Its main task is to initialize all necessary parameters and structures before starting the optimization. The method takes four parameters as input:
- rangeMinP [] — minimum values for each variable that the algorithm will optimize.
- rangeMaxP [] — maximum values for each variable.
- rangeStepP [] — step of change for each variable. These steps are used to discretize and determine the search accuracy.
- epochsP — number of epochs (iterations) of the algorithm execution.
The StandardInit() method of the base class (C_AO), that performs the initialization steps common to all optimization algorithms, is called first. If StandardInit() succeeds, the method proceeds to calculate the parameters specific to the artificial atom algorithm. In particular, covalentCount is calculated—the number of atoms that will participate in covalent bonds. The calculation is made as the product of popSize (the total number of atoms in the population) and covalentRate (the covalent bond coefficient). The result is rounded down to the nearest integer MathFloor, the number of atoms should be an integer. Next, checks are applied to ensure that the value of covalentCount is logically correct. If all initialization steps are successfully completed, the method returns "true", signaling that the algorithm is ready to run.
Thus, the Init method ensures the complete readiness of the artificial atom algorithm for execution, initializing both the parameters common to optimization algorithms and those specific to a given model of "atoms" and "bonds".
/———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_A3::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ covalentCount = (int)MathFloor (popSize * covalentRate); if (covalentCount < 1) covalentCount = 1; if (covalentCount >= popSize) covalentCount = popSize - 1; return true; } //————————————————————————————————————————————————————————————————————
The Moving() method in the C_AO_A3 class is one of the main steps of the artificial atom algorithm, implementing the process of "moving" or changing the position of atoms in the search space. It models evolutionary changes in a population of atoms aimed at finding an optimal solution. At the beginning of each call of the Moving() method, the "revision" state is checked. This flag indicates whether the population has been initialized previously.
Initial initialization (if "revision" is 'false') means that the Moving() method is called for the first time or after a state reset, and the atom population has not yet been initialized. In this case, the following happens: a loop over all popSize (number of atoms) and over all coords (dimensions of the search space). For each atom and for each coordinate, its position is initialized to a random value within a given range. After the first call is initialized, "revision" is set to 'true' so that subsequent calls to Moving() will perform the main moving process rather than reinitializing. The method terminates because the task of the first call is completed.
The basic process of moving atoms (if revision is 'true'), once the population has been initialized, the Moving method starts updating the positions of the atoms, simulating their movement, where the atoms are divided into two groups depending on their 'i' index and the covalentCount value.
Group 1: Atoms involved in covalent bonding (i <= covalentCount). For each atom in this group and for each of its coordinates, a random number is generated, and if this number is less than the covalent bond coefficient, the atom changes its coordinate according to a formula that simulates an attraction or interaction with the current best solution cB[c], where cB is the best coordinates found in the population and takes into account covalentCount.
If the random number is greater than or equal to covalentRate, the atom changes its coordinate using the u.PowerDistribution function. The function generates a value biased towards cB[c] but with some random scatter, simulating a more random search or "scattering".
Regardless of how the coordinate has changed, it is processed again by the u.SeInDiSp function to bring it into the acceptable range and discrete grid.
Group 2: Atoms not involved in covalent bonding (i > covalentCount): for each atom in this group and for each of its coordinates, a random index ind is selected from the range from 0 to covalentCount. This means that the "unbound" atom will interact with one of the atoms involved in the covalent bond.
The atom coordinate is modified to simulate interaction or repulsion with the selected "bound" atom and its previous "best" position, and the coordinate is processed again by u.SeInDiSp to meet the constraints.
The Moving() method is the core of the search process. It implements stochastic rules for updating atomic positions, inspired by a physical model. Atoms involved in a "covalent bond" tend to move toward the best solution found, while other atoms explore space, interacting with covalently bonded atoms.
//———————————————————————————————————————————————————————————————————— //--- The main step of the algorithm void C_AO_A3::Moving () { // Initial population setup if (!revision) { for (int i = 0; i < popSize; i++) { for (int j = 0; j < coords; j++) { a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]); a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } revision = true; return; } //------------------------------------------------------------------ int ind = 0; for (int i = 0; i < popSize; i++) { if (i <= covalentCount) { for (int c = 0; c < coords; c++) { if (u.RNDprobab () < covalentRate) { a [i].c [c] = a [i].c [c] + u.RNDprobab () * (cB [c] - a [i].c [c]) * covalentCount;//(1.0 - covalentCount); } else { a [i].c [c] = u.PowerDistribution (cB [c], rangeMin [c], rangeMax [c], 20); } a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } else { for (int c = 0; c < coords; c++) { ind = u.RNDintInRange (0, covalentCount); a [i].c [c] = a [i].c [c] + u.RNDprobab () * (a [ind].c [c] - a [i].cW [c]) * (1.0 - covalentCount);//covalentCount; a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } } //————————————————————————————————————————————————————————————————————
The Revision() method in the C_AO_A3 class is responsible for updating information about the best and worst solutions found in the population of atoms at the current iteration of the algorithm. This is an important step, as these "best" and "worst" values (both population-wide and individual for each atom) are used to guide further search in the next step. Updating the individual worst local solution for each atom. The u.Sorting() function is called, sorting the entire population of 'a' atoms based on their 'f' fitness.
After sorting, atom a[0] represents the best atom in the current population, then fB is updated with the value of a[0].f. The a[0].c coordinates are copied to cB (globally best coordinates). The a [popSize - 1] atom represents the worst atom in the current population (after sorting). The fitness of a [popSize - 1].f is compared with fW (the globally worst fitness found so far). If a[popSize - 1].f is indeed worse, then fW is updated with the value of a[popSize - 1].f and the coordinates of a[popSize - 1].c are copied to cW (the globally worse coordinates).
The Revision method systematically monitors the optimization progress. It updates both the individual "best" states for each agent (atom) and the global "best" and "worst" states of the entire population.
//———————————————————————————————————————————————————————————————————— //--- Update the best and worst solutions void C_AO_A3::Revision () { // Update the local worst solution for (int i = 0; i < popSize; i++) { if (a [i].f < a [i].fW) { fW = a [i].f; ArrayCopy (a [i].cW, a [i].c, 0, 0, WHOLE_ARRAY); } } static S_AO_Agent aT []; ArrayResize (aT, popSize); u.Sorting (a, aT, popSize); if (a [0].f > fB) { fB = a [0].f; ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY); } if (a [popSize - 1].f < fW) { fW = a [popSize - 1].f; ArrayCopy (cW, a [popSize - 1].c, 0, 0, WHOLE_ARRAY); } } //————————————————————————————————————————————————————————————————————
The resulting implementation is quite compact. Now let's move on to the base class from which all optimization algorithms are inherited. As I said above, changes have occurred in it, and we will describe the class.
A simple structure for storing algorithm parameters. It allows us to dynamically manage the settings of any optimization algorithm.
//—————————————————————————————————————————————————————————————————————————————— struct S_AlgoParam { double val; string name; }; //——————————————————————————————————————————————————————————————————————————————
The S_AO_Agent structure is intended to represent a single "agent" that represents a potential solution to an optimization problem and moves through a multidimensional search space. The S_AO_Agent structure contains a number of fields that can be divided into several categories: 1. Coordinates (position in search space):
- c — current agent coordinates. This is the main array that stores the current agent position.
- cP — previous agent coordinates. Used to track the agent's movement.
- cB — best coordinates this particular agent has ever reached. This information helps the agent "remember" its most successful positions and return to them, or use them for further searches.
- cW — worst agent coordinates.
2. Fitness (solution quality):
- f — current fitness of the agent. This is the value of the objective function calculated for the current 'c' coordinates.
- fP — previous fitness of the agent. Used to track changes in fitness similar to cP.
- fB — best fitness ever achieved by this agent. Corresponds to cB coordinates.
- fW — worst fitness of the agent. Corresponds to the cW coordinates.
3. Supporting data: cnt — integer counter.
The structure also includes the public Init() method, which is used to initialize the agent. It takes one 'coords' integer parameter which specifies the dimensionality of the search space (i.e. the number of coordinates the agent should have).
When Init is called, the following actions occur: the coordinate arrays (c, cP, cB, cW) are dynamically resized to the specified 'coords'. This ensures that each agent has the correct number of dimensions to handle a particular problem. The fitness values (f, fP, fB, fW) are initialized, f, fP, fB are set to minus DBL_MAX (the smallest possible double value), and fW to DBL_MAX (the largest possible). This is standard practice when searching for maximum (for f, fP, fB then the current value will almost always be greater than the initial one), and for fW (worst) the largest value is set so that any first value found will be better (less). The cnt counter is initialized to zero.
Overall, S_AO_Agent provides a comprehensive data structure for each member of the population in an evolutionary and population optimization algorithm, allowing tracking both its current state and its history of success and failure.
//—————————————————————————————————————————————————————————————————————————————— struct S_AO_Agent { double c []; //coordinates double cP []; //previous coordinates double cB []; //best coordinates double cW []; //worst coordinates double f; //fitness double fP; //previous fitness double fB; //best fitness double fW; //worst fitness int cnt; //counter void Init (int coords) { ArrayResize (c, coords); ArrayResize (cP, coords); ArrayResize (cB, coords); ArrayResize (cW, coords); f = -DBL_MAX; fP = -DBL_MAX; fB = -DBL_MAX; fW = DBL_MAX; cnt = 0; } }; //——————————————————————————————————————————————————————————————————————————————
Let's look at the base C_AO class. General structure of the C_AO class. Public members. These are fields and methods accessible from outside the class.
Data describing the overall state of the best/worst solution:
- cB [] — array of coordinates of the best solution found by the entire algorithm (globally).
- cW [] — array of coordinates of the worst solution found by the entire algorithm (globally).
- fB — fitness value for the best global solution (corresponds to cB).
- fW — fitness value for the worst global solution (corresponds to cW).
Note that these are global best/worst solutions, unlike fB, fW inside S_AO_Agent which are agent-specific.
Population and parameter related data:
- a [] — S_AO_Agent object array. This is a "population" of agents, each of which is a potential solution.
- params [] — array of S_AlgoParam objects. This is where algorithm-specific parameters (e.g. influence coefficients, probabilities, etc.) are stored and can be configured.
- revision — flag indicating the need to revise and update the state.
- SetParams () — method for setting specific algorithm parameters; in the derived classes, it will fill the "params" array.
- Init () — main method for initializing the algorithm. It accepts search ranges (minimum, maximum, step) and number of epochs. Returns 'true' on successful initialization.
- Moving () — method that implements the logic of "movement" or "evolution" of agents in the search space. This is the core of the algorithm.
- Revision () — method for the "revision" or "update" phase of the algorithm state.
- Injection () — method for "injecting" a certain value into the coordinate of a specific agent.
Search space parameters, arrays storing:
- rangeMin [] — minimum values for each coordinate of the search space.
- rangeMax [] — maximum values for each coordinate of the search space.
- rangeStep [] — steps for each coordinate (useful for discrete spaces or to determine the granularity of the search).
Internal parameters of the algorithm:
- coords — number of dimensions (coordinates) in the search space.
- popSize — population size (number of agents) in the algorithm.
Auxiliary utilities: u — object of an auxiliary class containing common functions such as random number generation, mathematical operations, etc.
Standard initialization method (protected): StandardInit () is designed to perform a common, standard initialization part for all derived algorithms. It initializes the random number generator using the current system time as a seed, sets fB to negative infinity and fW to positive infinity for further tracking the best/worst global solution. It also sets the "revision" flag to 'false', checks and sets the number of coordinates (space dimension), resizes the internal arrays (rangeMin, rangeMax, rangeStep, cB, cW) according to the number of coordinates, resizes the 'a' agent array according to the popSize population size, where each agent calls its own Init method to initialize the agent's internal coordinate arrays. The method copies the input search ranges into its internal variables, returns 'true' on successful initialization, and 'false' in case of errors (for example, mismatch of the sizes of the input range arrays).
The C_AO class serves as a base class for concrete implementations of optimization algorithms. Each new algorithm (specific version of AO) will inherit from C_AO and override virtual methods (SetParams, Init, Moving, Revision, Injection) to implement its unique logic, while StandardInit helps avoid code duplication for common initialization steps.
//—————————————————————————————————————————————————————————————————————————————— class C_AO { public: //-------------------------------------------------------------------- C_AO () { } ~C_AO () { } double cB []; //best coordinates double cW []; //worst coordinates double fB; //FF of the best coordinates double fW; //FF of the worst coordinates S_AO_Agent a []; //agents S_AlgoParam params []; //algorithm parameters bool revision; virtual void SetParams () { } virtual bool Init (const double &rangeMinP [], //minimum search range const double &rangeMaxP [], //maximum search range const double &rangeStepP [], //step search const int epochsP = 0) //number of epochs { return false;} virtual void Moving () { } virtual void Revision () { } virtual void Injection (const int popPos, const int coordPos, const double value) { } string GetName () { return ao_name;} string GetDesc () { return ao_desc;} string GetLink () { return ao_link;} string GetParams () { string str = ""; for (int i = 0; i < ArraySize (params); i++) { str += (string)params [i].val + "|"; } return str; } protected: //----------------------------------------------------------------- string ao_name; //ao name; string ao_desc; //ao description string ao_link; //ao link double rangeMin []; //minimum search range double rangeMax []; //maximum search range double rangeStep []; //step search int coords; //coordinates number int popSize; //population size C_AO_Utilities u; //auxiliary functions bool StandardInit (const double &rangeMinP [], //minimum search range const double &rangeMaxP [], //maximum search range const double &rangeStepP []) //step search { int seed = (int)GetTickCount64 (); MathSrand (seed); //reset of the generator fB = -DBL_MAX; fW = DBL_MAX; revision = false; coords = ArraySize (rangeMinP); if (coords == 0 || coords != ArraySize (rangeMaxP) || coords != ArraySize (rangeStepP)) return false; ArrayResize (rangeMin, coords); ArrayResize (rangeMax, coords); ArrayResize (rangeStep, coords); ArrayResize (cB, coords); ArrayResize (cW, coords); ArrayResize (a, popSize); for (int i = 0; i < popSize; i++) a [i].Init (coords); ArrayCopy (rangeMin, rangeMinP, 0, 0, WHOLE_ARRAY); ArrayCopy (rangeMax, rangeMaxP, 0, 0, WHOLE_ARRAY); ArrayCopy (rangeStep, rangeStepP, 0, 0, WHOLE_ARRAY); return true; } }; //——————————————————————————————————————————————————————————————————————————————
Test results
Now let's see how the A3 algorithm works. Average results for a small population.
A3|Artificial Atom Algorithm|30.0|0.2|
=============================
5 Hilly's; Func runs: 10000; result: 0.7074464987418227
25 Hilly's; Func runs: 10000; result: 0.49461443027863367
500 Hilly's; Func runs: 10000; result: 0.27674325929370214
=============================
5 Forest's; Func runs: 10000; result: 0.8910275237236067
25 Forest's; Func runs: 10000; result: 0.43888040941642725
500 Forest's; Func runs: 10000; result: 0.17553299655770818
=============================
5 Megacity's; Func runs: 10000; result: 0.5323076923076923
25 Megacity's; Func runs: 10000; result: 0.3270769230769231
500 Megacity's; Func runs: 10000; result: 0.11430769230769337
=============================
All score: 3.95794 (43.98%)
The visualization shows a scatter of results for low-dimensional functions (green lines), although it is worth noting the good coverage of the space by solutions.

A3 on the Hilly test function

A3 on the Forest test function

A3 on the Megacity test function
Our rating table presents the A3 algorithm for informational purposes only.
| # | AO | 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) | ||||||||
| 1 | ANS | 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 |
| 2 | CLA | 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 |
| 3 | AMOm | animal migration ptimization 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 |
| 4 | (P+O)ES | (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 |
| 5 | CTA | 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 |
| 6 | TETA | 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 |
| 7 | SDSm | 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 |
| 8 | BOAm | 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 |
| 9 | AAm | 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 |
| 10 | ESG | 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 |
| 11 | SIA | 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 |
| 12 | EOm | 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 |
| 13 | BBO | 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 |
| 14 | ACS | 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 |
| 15 | DA | 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 |
| 16 | BHAm | 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 |
| 17 | ASO | 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 |
| 18 | RFO | 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 |
| 19 | AOSm | 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 |
| 20 | TSEA | 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 |
| 21 | BSA | 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 |
| 22 | DE | 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 |
| 23 | SRA | 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 |
| 24 | CRO | chemical reaction optimization | 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 |
| 25 | BIO | 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 |
| 26 | BSA | 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 |
| 27 | DEA | 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 |
| 28 | HS | 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 |
| 29 | SSG | 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 |
| 30 | BCOm | 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 |
| 31 | ABO | 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 |
| 32 | (PO)ES | (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 |
| 33 | FBA | 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 |
| 34 | TSm | 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 |
| 35 | BSO | 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 |
| 36 | WOAm | wale 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 |
| 37 | AEFA | 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 |
| 38 | AEO | 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 |
| 39 | CAm | 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 |
| 40 | ACOm | 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 |
| 41 | CMAES | 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 |
| 42 | BFO-GA | bacterial foraging optimization - ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| 43 | SOA | simple optimization algorithm | 0.91520 | 0.46976 | 0.27089 | 1.65585 | 0.89675 | 0.37401 | 0.16984 | 1.44060 | 0.69538 | 0.28031 | 0.10852 | 1.08422 | 4.181 | 46.45 |
| 44 | ABHA | artificial bee hive algorithm | 0.84131 | 0.54227 | 0.26304 | 1.64663 | 0.87858 | 0.47779 | 0.17181 | 1.52818 | 0.50923 | 0.33877 | 0.10397 | 0.95197 | 4.127 | 45.85 |
| 45 | ACMO | atmospheric cloud model optimization | 0.90321 | 0.48546 | 0.30403 | 1.69270 | 0.80268 | 0.37857 | 0.19178 | 1.37303 | 0.62308 | 0.24400 | 0.10795 | 0.97503 | 4.041 | 44.90 |
| A3 | artificial_atom_algorithm | 0.70744 | 0.49461 | 0.27674 | 1.47879 | 0.89102 | 0.43888 | 0.17553 | 1.50543 | 0.53230 | 0.32707 | 0.11431 | 0.97368 | 3.958 | 43.98 | |
| RW | 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 | |
Summary
The article presented the implementation of the artificial atom algorithm A3 in the MQL5 language for solving problems of optimizing trading strategies. Despite the incompleteness of the theoretical description by the authors in the original work, we managed to create a functional practical implementation. The tests conducted showed that A3 demonstrates a good balance between computational speed and solution quality. Just two adjustable parameters significantly simplify the process of customizing the algorithm for a specific task, which is critical for traders who are not optimization specialists.
A3 algorithm may occupy a certain niche among optimization methods. The artificial atom algorithm demonstrates that even relatively simple metaheuristics can be quite effective if properly adapted to a specific subject area. The combination of conceptual simplicity, computational efficiency, and sufficient solution quality makes A3 a good tool in the algorithmic trader's arsenal.
The source code for the algorithm and examples of its application are available in the appendix to the article, allowing those interested to independently evaluate the efficiency of the method and experiment with it.

Figure 1. Color gradation of algorithms across the corresponding tests

Figure 2. Histogram of algorithm testing results (scale from 0 to 100, the higher the better, where 100 is the maximum possible theoretical result, in the archive there is a script for calculating the rating table)
A3 pros and cons:
Pros:
- Fast.
- Few parameters.
Cons:
- Low convergence accuracy.
An archive with the latest versions of the algorithm code is attached to the article. The author of the article is not responsible for the absolute accuracy in the description of canonical algorithms. Changes have been made to many of them to improve search capabilities. The conclusions and judgments presented in the articles are based on the results of the experiments.
Programs used in the article
| # | Name | Type | Description |
|---|---|---|---|
| 1 | #C_AO.mqh | Include | Parent class of population optimization algorithms |
| 2 | #C_AO_enum.mqh | Include | Enumeration of population optimization algorithms |
| 3 | TestFunctions.mqh | Include | Library of test functions |
| 4 | TestStandFunctions.mqh | Include | Test stand function library |
| 5 | Utilities.mqh | Include | Library of auxiliary functions |
| 6 | CalculationTestResults.mqh | Include | Script for calculating results in the comparison table |
| 7 | Testing AOs.mq5 | Script | The unified test stand for all population optimization algorithms |
| 8 | Simple use of population optimization algorithms.mq5 | Script | A simple example of using population optimization algorithms without visualization |
| 9 | Test_AO_A3.mq5 | Script | A3 test stand |
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/18958
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.
Interactive Supply and Demand Zone Manager in MQL5 (Part II): Event-Driven Architecture and Persistent Lifecycle Logging
Quantum Neural Network in MQL5 (Part III): A Virtual Quantum Processor Based on Qubits
From Cloud to Complex: The Vietoris-Rips Filtration in MQL5
MQL5 Wizard Techniques you should know (Part 98): Using an Unscented Kalman Filter and a Capsule Network in a Custom Signal Class
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use