Русский Português
preview
Artificial Atom Algorithm (A3)

Artificial Atom Algorithm (A3)

MetaTrader 5Trading systems |
426 0
Andrey Dik
Andrey Dik

Contents

  1. Introduction
  2. Algorithm implementation
  3. Test results


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:

  1. rangeMinP [] — minimum values for each variable that the algorithm will optimize.
  2. rangeMaxP [] — maximum values for each variable.
  3. rangeStepP [] — step of change for each variable. These steps are used to discretize and determine the search accuracy.
  4. 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. 
Virtual methods (requiring implementation in derived classes). These methods are declared as "virtual" and can be overridden in derived classes to implement the specific logic of a particular version of the algorithm.
  • 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.

Hilly

A3 on the Hilly test function

Forest

A3 on the Forest test function

Megacity

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.

tab

Figure 1. Color gradation of algorithms across the corresponding tests

chart

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:

  1. Fast.
  2. Few parameters.

Cons:

  1. 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

Attached files |
A3.zip (258.97 KB)
Interactive Supply and Demand Zone Manager in MQL5 (Part II): Event-Driven Architecture and Persistent Lifecycle Logging Interactive Supply and Demand Zone Manager in MQL5 (Part II): Event-Driven Architecture and Persistent Lifecycle Logging
This article advances the stateful supply and demand zone framework for MetaTrader 5 by replacing polling with an event-driven model based on OnChartEvent(). We split synchronization into dedicated handlers for creation, modification, and deletion, and separate market logic in OnTick() from user interactions in OnChartEvent(). A persistent, append-only CSV logger records all lifecycle events, improving responsiveness, state consistency, and recoverable history for downstream analysis.
Quantum Neural Network in MQL5 (Part III): A Virtual Quantum Processor Based on Qubits Quantum Neural Network in MQL5 (Part III): A Virtual Quantum Processor Based on Qubits
The article focuses on creating a trading system with a real quantum simulator instead of mathematical analogies. The system uses 3 virtual qubits, quantum gates and superposition principles to analyze markets. It is implemented as a trading EA for MetaTrader 5 in MQL5. The main achievement is the transition from simulation to real quantum principles of financial information processing.
From Cloud to Complex: The Vietoris-Rips Filtration in MQL5 From Cloud to Complex: The Vietoris-Rips Filtration in MQL5
We turn a price-embedded point cloud into a Vietoris–Rips filtration and its boundary matrix. The article enumerates vertices, edges, and triangles with filtration values, sorts them in entry order, and builds O(1) vertex/edge lookups. You get MQL5 classes CTDARips and CTDABoundary and a sparse Z/2 boundary suitable for the next-step persistence reduction.
MQL5 Wizard Techniques you should know (Part 98): Using an Unscented Kalman Filter and a Capsule Network in a Custom Signal Class MQL5 Wizard Techniques you should know (Part 98): Using an Unscented Kalman Filter and a Capsule Network in a Custom Signal Class
This article presents 'CSignalUKFCapsNet', as a custom class coded in MQL5. This class is meant to be used with the MQL5 Wizard when assembling an Expert Advisor and when selected in the Wizard it defines the Expert Advisor's entry signals. In building this custom class, we brought together the algorithm Unscented Kalman Filter and the Capsule Neural Network. Our algorithm is showcased with four operation modes, and the coding of this as a custom class for the MQL5 Wizard, allows testing with various Trailing Stop methods and Money Management systems.