preview
Алгоритм искусственного атома —  Artificial Atom Algorithm (A3)

Алгоритм искусственного атома — Artificial Atom Algorithm (A3)

MetaTrader 5Торговые системы |
99 0
Andrey Dik
Andrey Dik

Содержание

  1. Введение
  2. Реализация алгоритма
  3. Результаты тестов


Введение

В алгоритмическом трейдинге одной из ключевых задач является оптимизация параметров торговых стратегий. Трейдеры ежедневно сталкиваются с необходимостью настройки множества переменных: периодов индикаторов, уровней стоп-лоссов и тейк-профитов, размеров позиций, временных фильтров и десятков других параметров. Каждая комбинация этих параметров может кардинально изменить результативность стратегии, и мы продолжаем искать эффективные алгоритмы, которые готовы найти лучшее решение за приемлемое время.

В данной статье разберем еще один алгоритм оптимизации, а именно Artificial Atom Algorithm (A3) — это метаэвристический алгоритм оптимизации, вдохновленный химическими реакциями. Он был разработан турецкими учеными и впервые представлен научному сообществу в 2018 году.


Реализация алгоритма

В процессе работы над данным алгоритмом я столкнулся с рядом неясностей в авторском описании. Некоторые операторы представлены недостаточно подробно, что затрудняет их практическую реализацию. В связи с этим я приведу оригинальное описание алгоритма, однако, реализация была выполнена по моему собственному усмотрению — с опорой на личный опыт и структурные принципы, характерные для подобных алгоритмов. Это представляет собой существенный пробел в работе авторов, оставляющий разработчику широкое поле для интерпретации. Кроме того, в ходе работы были внесены изменения в базовый класс, которые также будут рассмотрены — после изложения основной реализации алгоритма.

Алгоритм моделирует взаимодействие атомов и электронов для поиска оптимального решения. Основные компоненты — это атомы, которые представляют потенциальные решения задачи. Электроны представляют собой переменные решения. Ковалентная связь — оператор для сохранения и репликации лучших решений, ионная связь — оператор для исследования пространства поиска и достижения глобального оптимума. 

Алгоритм начинает работу со случайной генерации набора атомов, затем оценивает качество каждого атома через целевую функцию, далее применяет операторы ковалентной и ионной связи для улучшения решений (как это делается, к сожалению, нет описания). Далее необходимо оценить эффект электронов, опять же неясно каким образом. Отсортировать электроны и атомы. Хорошо, мы можем отсортировать атомы (решения), но как можно сортировать электроны, загадка (это как попытаться отсортировать конечности у человека, руки с ногами, правые с левыми — неважно, отсортируем). Итеративно повторить процесс до достижения критерия останова. Что ж, как говорится, из песни слова не выкинешь, нужно попытаться собрать алгоритм из того, что есть (идеи авторов).

Окончательно представляю переработанный мной псевдокод алгоритма A3.

ИНИЦИАЛИЗАЦИЯ Входные параметры:

popSize — количество атомов (по умолчанию 10)
covalentRate — коэффициент ковалентной связи (по умолчанию 0.1)
rangeMin [], rangeMax [] — границы поиска для каждой переменной
rangeStep [] — шаг дискретизации

Предварительные вычисления:

Вычислить количество элитных атомов:
covalentCount = floor (popSize × covalentRate)
Ограничить: минимум 1, максимум (popSize - 1)

Создать популяцию из popSize атомов
Для каждого атома инициализировать:
Координаты c []
Локальное худшее решение cW []
Фитнес f, худший фитнес fW


ОСНОВНОЙ ЦИКЛ ОПТИМИЗАЦИИ
ШАГ 1: Первая итерация (если revision = false)
ДЛЯ каждого атома i от 0 до popSize-1:
    ДЛЯ каждой координаты j от 0 до coords-1:
        Сгенерировать случайное значение в диапазоне [rangeMin [j], rangeMax [j]]
        Применить дискретизацию согласно rangeStep [j]
        Сохранить в a [i].c [j]
    
Установить revision = true
Завершить итерацию

ШАГ 2: Обновление позиций атомов (Moving)
ДЛЯ каждого атома i от 0 до popSize-1:
    
    ЕСЛИ i ≤ covalentCount (атом является элитным):
        ДЛЯ каждой координаты c от 0 до coords-1:
            
            ЕСЛИ random() < covalentRate (с вероятностью 10%):
                // Движение к глобально лучшему решению
                step = random() × (cB [c] - a [i].c [c]) × covalentCount
                a [i].c [c] = a [i].c [c] + step
            
            ИНАЧЕ (с вероятностью 90%):
                // Генерация через PowerDistribution вокруг лучшего
                a[i]. c[c] = PowerDistribution(центр=cB [c], min=rangeMin [c], max=rangeMax [c], степень=20)
            Применить дискретизацию к a [i].c [c]
    
    ИНАЧЕ (атом не является элитным):
        ДЛЯ каждой координаты c от 0 до coords-1:
            // Выбрать случайный элитный атом как образец
            ind = случайное_целое(0, covalentCount)
            
            // Движение от локального худшего к позиции элитного атома
            direction = a [ind].c [c] - a [i].cW [c]
            step = random() × direction × (1.0 - covalentCount)
            a [i].c [c] = a [i].c [c] + step
            Применить дискретизацию к a [i].c [c]

ШАГ 3: Вычисление фитнеса
ДЛЯ каждого атома i:
    Вычислить фитнес a [i].f через целевую функцию

ШАГ 4: Обновление памяти и сортировка (Revision)
// Обновление локальных худших решений
ДЛЯ каждого атома i от 0 до popSize-1:
    ЕСЛИ a [i].f < a [i].fW:
        a [i].fW = a [i].f
        Скопировать a [i].c в a [i].cW
        Обновить глобальное худшее fW

// Сортировка популяции по убыванию фитнеса
Отсортировать массив атомов a [] по полю f (от лучшего к худшему)

// Обновление глобального лучшего
ЕСЛИ a [0].f > fB:
    fB = a [0].f
    Скопировать a [0].c в cB

// Обновление глобального худшего
ЕСЛИ a [popSize-1].f < fW:
    fW = a [popSize-1].f
    Скопировать a [popSize-1].c в cW

ШАГ 5: Проверка критерия останова
ЕСЛИ достигнуто максимальное количество итераций:
    Завершить алгоритм
ИНАЧЕ:
    Перейти к ШАГ 2

Теперь можем перейти к практической реализации в коде. Напишем класс, который будет представлять собой реализацию алгоритма искусственной атомной системы, основанного на метафоре атомов и их связей. Этот класс наследует базовые возможности от класса "C_AO" и расширяет их в контексте алгоритма A3.

Методы установки параметров: функция "SetParams ()" считывает текущие значения из массива параметров и сохраняет их в соответствующие переменные. Это обеспечивает гибкость настройки перед запуском алгоритма. Инициализация: метод "Init ()" подготавливает параметры и структуру для начала работы, получая диапазоны значений и шаги изменения переменных, а также число эпох. Основные операции алгоритма: методы Moving () и Revision () реализуют процессы перемещения и ревизии атомов внутри системы, что соответствует этапам поиска оптимального решения. Параметры и переменные:

  • popSize — размер популяции атомов (количество элементов в сообществе).
  • covalentRate — коэффициент, характеризующий уравнение связей между атомами.
  • covalentCount — внутреннее число атомов, участвующих в формировании связей (используется внутри класса для расчетов).

Общий смысл класса — моделировать поведение системы атомов с учетом их связей, что реализуется через параметры и методы работы, далее реализуются логика поиска и оптимизации на основе этих концепций.

//————————————————————————————————————————————————————————————————————
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/ru/articles/18958";

    popSize      = 10;    // количество атомов (m)
    covalentRate = 0.1;   // коэффициент ковалентной связи (β)

    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  [],  // минимальные значения
             const double &rangeMaxP  [],  // максимальные значения
             const double &rangeStepP [],  // шаг изменения
             const int     epochsP = 0);   // количество эпох

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double covalentRate;       // коэффициент ковалентной связи (β)

  private: //---------------------------------------------------------
  int    covalentCount;      // количество атомов для ковалентной связи
};
//————————————————————————————————————————————————————————————————————

Метод "Init ()" является ключевым шагом в подготовке алгоритма к работе. Его основная задача — выполнить инициализацию всех необходимых параметров и структур перед началом оптимизационного процесса. Метод принимает на вход четыре параметра:

  1. rangeMinP [] — минимальные значения для каждой переменной, которую алгоритм будет оптимизировать.
  2. rangeMaxP [] — максимальные значения для каждой переменной.
  3. rangeStepP [] — шаг изменения для каждой переменной. Эти шаги используются для дискретизации и определения точности поиска.
  4. epochsP — количество эпох (итераций) выполнения алгоритма.

Первым делом вызывается "StandardInit ()" — метод базового класса (C_AO), который выполняет общие для всех алгоритмов оптимизации действия по инициализации. Если "StandardInit ()" успешно завершился, метод приступает к расчетам специфичных для алгоритма искусственного атома параметров. В частности, вычисляется "covalentCount" — количество атомов, которые будут участвовать в ковалентных связях. Расчет производится как произведение "popSize" (общее количество атомов в популяции) на "covalentRate" (коэффициент ковалентной связи). Результат округляется до ближайшего целого вниз "MathFloor", количество атомов должно быть целым числом. Далее применяются проверки для обеспечения логически корректного значения "covalentCount". В случае успешного выполнения всех инициализационных шагов, метод возвращает "true", сигнализируя о готовности алгоритма к запуску.

Таким образом, метод "Init" обеспечивает полную готовность алгоритма искусственного атома к выполнению, инициализируя как общие для оптимизационных алгоритмов параметры, так и специфические для данной модели "атомов" и "связей".

/————————————————————————————————————————————————————————————————————
//--- Инициализация
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;
}
//————————————————————————————————————————————————————————————————————

Метод "Moving ()"  в классе "C_AO_A3" является одним из основных шагов алгоритма искусственного атома, реализуя процесс "движения" или изменения положения атомов в пространстве поиска. Он моделирует эволюционные изменения в популяции атомов, направленные на поиск оптимального решения. В начале каждого вызова метода "Moving ()" выполняется проверка состояния "revision". Этот флаг указывает, была ли популяция инициализирована ранее.

Начальная инициализация (если "revision" равен "false") означает, что метод "Moving ()" вызывается впервые или после сброса состояния, и популяция атомов еще не была инициализирована. В этом случае происходит следующее: цикл по всем "popSize" (количеству атомов) и по всем "coords" (измерениям пространства поиска). Для каждого атома и для каждой координаты его положение инициализируется случайным значением в пределах заданного диапазона. После инициализации первого вызова, "revision" устанавливается в  "true", чтобы при последующих вызовах "Moving ()" выполнялся уже основной процесс перемещения, а не повторная инициализация. Метод завершается, так как задача первого вызова выполнена.

Основной процесс перемещения атомов (если "revision" равен "true"), когда популяция уже инициализирована, метод "Moving" приступает к обновлению положений атомов, имитируя их движение, когда атомы делятся на две группы в зависимости от их индекса "i" и значения "covalentCount". 

Группа 1: Атомы, участвующие в ковалентной связи (i <= covalentCount). Для каждого атома в этой группе и для каждой его координаты генерируется случайное число, и если это число меньше коэффициента ковалентной связи, атом изменяет свою координату по формуле, которая имитирует притяжение или взаимодействие с текущим наилучшим решением "cB [c]", где "cB" является лучшими найденными координатами в популяции и учитывает "covalentCount".

Если случайное число больше или равно "covalentRate", атом изменяет свою координату с использованием функции "u.PowerDistribution". Эта функция генерирует значение, смещенное к "cB [c]", но с некоторым случайным разбросом, имитируя больший случайный поиск или "рассеивание".

Независимо от того, как изменилась координата, она снова обрабатывается функцией "u.SeInDiSp" для приведения к допустимому диапазону и дискретной сетке. 

Группа 2: Атомы, не участвующие в ковалентной связи (i > covalentCount): для каждого атома в этой группе и для каждой его координаты выбирается случайный индекс "ind" из диапазона от "0" до "covalentCount". Это означает, что "несвязанный" атом будет взаимодействовать с одним из атомов, участвующих в ковалентной связи.

Координата атома изменяется, имитируя взаимодействие или отталкивание от выбранного "связанного" атома и своего предыдущего "лучшего" положения, и координата снова обрабатывается "u.SeInDiSp" для соответствия ограничениям.

Метод "Moving ()" является ядром поискового процесса. Он реализует стохастические правила обновления положений атомов, вдохновленные физической моделью. Атомы, участвующие в "ковалентной связи", стремятся к лучшему найденному решению, тогда как другие атомы исследуют пространство, взаимодействуя с ковалентно связанными атомами.

//————————————————————————————————————————————————————————————————————
//--- Основной шаг алгоритма
void C_AO_A3::Moving ()
{
  // Начальная инициализация популяции
  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]);
      }
    }
  }
}
//————————————————————————————————————————————————————————————————————

Метод "Revision ()" в классе "C_AO_A3" отвечает за обновление информации о лучшем и худшем найденных решениях в популяции атомов на текущей итерации алгоритма. Это важный этап, так как эти "лучшие" и "худшие" значения (как общие для всей популяции, так и индивидуальные для каждого атома) используются для направления дальнейшего поиска в следующем шаге движения. Обновление индивидуального худшего локального решения для каждого атома. Вызывается функция "u.Sorting ()", сортирует всю популяцию атомов "a" на основе их приспособленности "f".

После сортировки, атом "a [0]" представляет собой лучший атом в текущей популяции, тогда "fB" обновляется значением "a [0].f". Координаты "a [0].c" копируются в "cB" (глобально лучшие координаты). Атом "a [popSize - 1]" представляет собой наихудший атом в текущей популяции (после сортировки). Происходит сравнение приспособленности "a [popSize - 1].f" с "fW" (глобально худшей приспособленностью, найденной за всё время). Если "a [popSize - 1].f"  действительно хуже, то "fW" обновляется значением "a [popSize - 1].f" и координаты "a [popSize - 1].c" копируются в "cW" (глобально худшие координаты).

Метод "Revision" систематически отслеживает прогресс оптимизации. Он обновляет как индивидуальные "лучшие" состояния для каждого агента (атома), так и глобальные "лучшее" и "худшее" состояния всей популяции. 

//————————————————————————————————————————————————————————————————————
//--- Обновление лучшего и худшего решений
void C_AO_A3::Revision ()
{
  // Обновляем локальное худшее решение
  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);
  }
}
//————————————————————————————————————————————————————————————————————

Решение получилось очень компактное. Теперь перейдем к базовому классу, от которого наследуются все алгоритмы оптимизации, как уже сказал выше, в нем произошли изменения, опишем класс.

Простая структура для хранения параметров алгоритма. Позволяет динамически управлять настройками любого алгоритма оптимизации.

//——————————————————————————————————————————————————————————————————————————————
struct S_AlgoParam
{
    double val;
    string name;
};
//——————————————————————————————————————————————————————————————————————————————

Структура "S_AO_Agent" предназначена для представления отдельного "агента", который представляет собой потенциальное решение задачи оптимизации и перемещается в многомерном пространстве поиска. Структура "S_AO_Agent" содержит ряд полей, которые можно разделить на несколько категорий: 1. Координаты (положение в пространстве поиска):

  • c — текущие координаты агента. Это основной массив, который хранит текущее положение агента.
  • cP — предыдущие координаты агента. Используется для отслеживания движения агента.
  • cB — лучшие координаты, которые когда-либо достигал этот конкретный агент. Эта информация помогает агенту "помнить" свои самые успешные положения и возвращаться к ним, или использовать их для дальнейшего поиска.
  • cW — худшие координаты агента.

2. Приспособленность (качество решения):

  • f — текущая приспособленность агента. Это значение целевой функции, рассчитанное для текущих координат "c".
  • fP — предыдущая приспособленность агента. Аналогично "cP", используется для отслеживания изменения приспособленности.
  • fB — лучшая приспособленность, которая когда-либо была достигнута этим агентом. Соответствует координатам "cB".
  • fW — худшая приспособленность агента. Соответствует координатам "cW".

3. Вспомогательные данные: cnt — целочисленный счетчик. 

Структура также включает публичный метод "Init ()", который служит для инициализации агента. Он принимает один целочисленный параметр "coords", указывающий размерность пространства поиска (то есть количество координат, которые должен иметь агент).

При вызове "Init" происходят следующие действия: массивы координат (c, cP, cB, cW) динамически изменяют свой размер до указанного "coords". Это гарантирует, что каждый агент имеет правильное количество измерений для работы с конкретной проблемной задачей. Значения приспособленности (f, fP, fB, fW) инициализируются, "f", "fP", "fB" устанавливаются в минус "DBL_MAX" (наименьшее возможное значение "double"), а "fW" — в "DBL_MAX" (наибольшее возможное). Это стандартная практика при поиске максимума (для f, fP, fB тогда текущее значение будет почти всегда больше начального), а для "fW" (худшее) ставится самое большое значение, чтобы любое первое найденное значение было лучше (меньше) него. Счетчик "cnt" инициализируется нулем.

В целом, "S_AO_Agent" предоставляет комплексную структуру данных для каждого участника популяции в эволюционном и популяционном алгоритме оптимизации, позволяя отслеживать как его текущее состояние, так и его историю успеха и неудачи.

//——————————————————————————————————————————————————————————————————————————————
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;
    }
};
//——————————————————————————————————————————————————————————————————————————————

Разберем базовый класс "C_AO". Общая структура класса  C_AO. Публичные члены (public). Это поля и методы, доступные извне класса. 

Данные, описывающие общее состояние лучшего/худшего решения:

  • cB [] — массив координат лучшего решения, найденного всем алгоритмом (глобально).
  • cW [] — массив координат худшего решения, найденного всем алгоритмом (глобально).
  • fB — значение приспособленности для лучшего глобального решения (соответствует "cB").
  • fW — значение приспособленности для худшего глобального решения (соответствует "cW").

Обратите внимание, что это глобальные лучшие/худшие решения, в отличие от "fB", "fW" внутри "S_AO_Agent", которые относятся к конкретному агенту. 

Данные, связанные с популяцией и параметрами:

  • a [] — массив объектов "S_AO_Agent". Это "популяция" агентов, каждый из которых является потенциальным решением.
  • params [] — массив объектов "S_AlgoParam". Здесь хранятся специфичные для данного алгоритма параметры (например, коэффициенты влияния, вероятности и т.д.), которые могут быть настроены.
  • revision — флаг, указывающий на необходимость пересмотра и обновления состояния. 
Виртуальные методы (требующие реализации в классах-наследниках). Эти методы объявлены как "virtual", они могут быть переопределены в классах-наследниках для реализации специфической логики конкретной версии алгоритма.
  • SetParams () — метод для установки специфических параметров алгоритма, в наследниках он будет заполнять массив "params".
  • Init () — основной метод инициализации алгоритма. Принимает диапазоны поиска (минимум, максимум, шаг) и количество эпох. Возвращает "true"  при успешной инициализации.
  • Moving () — метод, реализующий логику "перемещения" или "эволюции" агентов в пространстве поиска. Это ядро алгоритма.
  • Revision () — метод для фазы "пересмотра" или "обновления" состояния алгоритма.
  • Injection () — метод для "инъекции" определенного значения в координату конкретного агента. 

Параметры пространства поиска, массивы хранящие:

  • rangeMin [] — минимальные значения для каждой координаты пространства поиска.
  • rangeMax [] — максимальные значения для каждой координаты пространства поиска.
  • rangeStep [] — шаги для каждой координаты (полезно для дискретных пространств или для определения гранулярности поиска).

Внутренние параметры алгоритма:

  • coords — количество измерений (координат) в пространстве поиска.
  • popSize — размер популяции (количество агентов) в алгоритме.

Вспомогательные утилиты: "u" — объект вспомогательного класса, содержащего общие функции, такие как генерация случайных чисел, математические операции и т.п.

Метод стандартной инициализации (protected): "StandardInit ()" — метод предназначен для выполнения общей, стандартной части инициализации для всех алгоритмов-наследников, инициализирует генератор случайных чисел, используя текущее системное время в качестве начального зерна, устанавливает "fB" в отрицательную бесконечность и "fW" в положительную бесконечность для дальнейшего отслеживания лучшего/худшего глобального решения. Также устанавливает флаг "revision" в "false", проверяет и устанавливает количество координат (размерность пространства), изменяет размер внутренних массивов (rangeMin, rangeMax, rangeStep, cB, cW) в соответствии с количеством координат, изменяет размер массива агентов "a" в соответствии с размером популяции "popSize", где для каждого агента вызывается его собственный метод "Init" для инициализации внутренних массивов координат агента. Метод копирует входные диапазоны поиска в свои внутренние переменные, возвращает "true" при успешной инициализации, и "false" в случае ошибок (например, несовпадение размеров входных массивов диапазонов).

Класс "C_AO" служит базовым классом для конкретных реализаций алгоритмов оптимизации. Каждый новый алгоритм (конкретная версия AO) будет наследовать от "C_AO" и переопределять виртуальные методы (SetParams, Init, Moving, Revision, Injection) для реализации своей уникальной логики, а "StandardInit" помогает избежать дублирования кода для общих шагов инициализации.

//——————————————————————————————————————————————————————————————————————————————
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;
  }
};
//——————————————————————————————————————————————————————————————————————————————


Результаты тестов

Теперь посмотрим, как работает алгоритм A3. Средние результаты при небольшой популяции.

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%)

На визуализации заметен разброс результатов на функциях малой размерности (зеленые линии), хотя необходимо отметить хорошее покрытие пространства решениями.

Hilly

A3 на тестовой функции Hilly

Forest

A3 на тестовой функции Forest

Megacity

A3 на тестовой функции Megacity

В нашей рейтинговой таблице алгоритм A3 представлен ознакомительно.

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


Выводы

В данной статье была представлена реализация алгоритма искусственного атома A3 на языке MQL5 для решения задач оптимизации торговых стратегий. Несмотря на неполноту теоретического описания авторами в оригинальной работе, удалось создать работоспособную практическую реализацию. Проведенное тестирование показало, что A3 демонстрирует хорошее соотношение между скоростью работы и качеством получаемых решений. Всего два настраиваемых параметра существенно упрощают процесс настройки алгоритма под конкретную задачу, что критически важно для трейдеров, не являющихся специалистами в области оптимизации. 

Алгоритм A3 может занимать определенную нишу среди методов оптимизации. Алгоритм искусственного атома демонстрирует, что даже относительно простые метаэвристики могут быть весьма эффективными при правильной адаптации под конкретную предметную область. Сочетание концептуальной простоты, вычислительной эффективности и достаточного качества решений делает A3 хорошим инструментом в арсенале алгоритмического трейдера.

Исходный код алгоритма и примеры его применения доступны в приложении к статье, что позволит желающим самостоятельно оценить эффективность метода и поэкспериментировать.

tab

Рисунок 1. Цветовая градация алгоритмов по соответствующим тестам

chart

Рисунок 2. Гистограмма результатов тестирования алгоритмов (по шкале от 0 до 100, чем больше, тем лучше, где 100 — максимально возможный теоретический результат, в архиве скрипт для расчета рейтинговой таблицы)

Плюсы и минусы алгоритма A3:

Плюсы:

  1. Быстрый.
  2. Небольшое количество параметров.

Минусы:

  1. Невысокая точность сходимости.

К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.


Программы, используемые в статье

# Имя Тип Описание
1 #C_AO.mqh
Включаемый файл
Родительский класс популяционных алгоритмов оптимизации
2 #C_AO_enum.mqh
Включаемый файл
Перечисление популяционных алгоритмов оптимизации
3 TestFunctions.mqh
Включаемый файл
Библиотека тестовых функций
4
TestStandFunctions.mqh
Включаемый файл
Библиотека функций тестового стенда
5
Utilities.mqh
Включаемый файл
Библиотека вспомогательных функций
6
CalculationTestResults.mqh
Включаемый файл
Скрипт для расчета результатов в сравнительную таблицу
7
Testing AOs.mq5
Скрипт Единый испытательный стенд для всех популяционных алгоритмов оптимизации
8
Simple use of population optimization algorithms.mq5
Скрипт
Простой пример использования популяционных алгоритмов оптимизации без визуализации
9
Test_AO_A3.mq5
Скрипт Испытательный стенд для A3
Прикрепленные файлы |
A3.ZIP (326.77 KB)
Построение модели для ограничения диапазона сигналов по тренду (Часть 9): Советник с несколькими стратегиями (III) Построение модели для ограничения диапазона сигналов по тренду (Часть 9): Советник с несколькими стратегиями (III)
Добро пожаловать в третью часть серии статьей о трендах! Сегодня мы углубимся в использование дивергенции как стратегии определения оптимальных точек входа в рамках преобладающего дневного тренда. Мы также представим специальный механизм фиксации прибыли, аналогичный скользящему стоп-лоссу, но с уникальными усовершенствованиями. Кроме того, мы обновим советник Trend Constraint до более продвинутой версии, включив в него новое условие исполнения сделки в дополнение к существующим. Также мы продолжим изучать практическое применение MQL5 в разработке алгоритмов.
Разработка инструментария для анализа движения цен (Часть 5): Советник Volatility Navigator Разработка инструментария для анализа движения цен (Часть 5): Советник Volatility Navigator
Определить направление рынка может быть просто, но вот понять, когда входить на рынок, - гораздо более сложная задача. В этой статье серии "Разработка инструментария для анализа движения цен" я представлю еще один инструмент, который определяет точки входа и уровни стоп-лосса/тейк-профита. Для достижения этой цели использовался язык программирования MQL5.
Сингулярный спектральный анализ на MQL5 Сингулярный спектральный анализ на MQL5
Данная статья предназначена в качестве руководства для тех, кто не знаком с концепцией сингулярного спектрального анализа и хочет получить достаточно знаний, чтобы иметь возможность применять встроенные инструменты, доступные на MQL5.
Теория графов: Алгоритм Дейкстры в трейдинге Теория графов: Алгоритм Дейкстры в трейдинге
Алгоритм Дейкстры — классическое решение по поиску кратчайшего пути в теории графов, которое позволяет оптимизировать торговые стратегии путем моделирования рыночных сетей. Трейдеры могут использовать его для поиска наиболее эффективных маршрутов в данных свечного графика.