preview
Алгоритм биржевого рынка — Exchange Market Algorithm (EMA)

Алгоритм биржевого рынка — Exchange Market Algorithm (EMA)

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


Содержание

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


Введение

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

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


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

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

После начальной оценки успешности каждого трейдера (вычисления значения целевой функции), вся популяция сортируется по убыванию успешности и делится на три группы:

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

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

Фаза 1: Сбалансированный рынок (стабильная торговля) 


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

Поведение среднего класса. Каждый трейдер из второй группы выбирает случайного наставника из элиты и постепенно смещает свою позицию в его направлении. Степень этого смещения определяется коэффициентом поглощения (r1 = 1.5), который адаптивно уменьшается по мере прогресса алгоритма. Движение происходит не детерминированно — используется случайный множитель, имитирующий неопределенность рыночных решений. 

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

Фаза 2: Колеблющийся рынок (волатильная торговля)


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

Исследовательское поведение среднего класса. Трейдеры второй группы применяют двойную стратегию. С вероятностью 50% они могут полностью скопировать позицию глобально лучшего решения (самого успешного трейдера за всю историю). В остальных случаях они исследуют область вокруг центра масс элитной группы, добавляя контролируемый случайный шум к этой позиции. Величина шума определяется фактором риска (riskAlpha = 0.3) и уменьшается со временем.

Агрессивное поведение начинающих. Третья группа применяет наиболее рискованные стратегии:

  • с вероятностью, равной фактору риска (30%), трейдер полностью забывает свою текущую позицию и начинает заново со случайной точки;
  • в 35% случаев проводится широкий поиск вокруг текущей позиции с большим радиусом;
  • в оставшихся случаях применяется оппозиционное обучение: трейдер движется в направлении, противоположном центру худших решений, пытаясь уйти от неудачных областей.

Ключевой особенностью EMA является его адаптивность. По мере выполнения итераций вычисляется коэффициент затухания на основе сигмоидной функции. Этот коэффициент обеспечивает плавный переход от исследования (в начале работы) к эксплуатации найденных хороших решений (в конце).

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

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

ema_illustracion

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

1. Инициализация

  • Создать популяцию из N = 60 трейдеров (N кратно 3)
  • Задать параметры: r₁ = 1.5, r₂ = 0.8, α = 0.3 (фактор риска)
  • Инициализировать каждого трейдера случайной позицией
  • Определить размер группы: m = N/3 = 20

2. Основной цикл

Для каждой итерации t = 1, 2, ..., T выполнить:

2.1 Ранжирование

  • Оценить фитнес всех трейдеров
  • Отсортировать по убыванию фитнеса
  • Разделить на группы:
    • G₁ = {1, ..., m} — элита
    • G₂ = {m+1, ..., 2m} — средний класс
    • G₃ = {2m+1, ..., N} — начинающие

2.2 Адаптация параметров

  • progress = t / T
  • decay = 1 / (1 + exp(-10(progress - 0.5)))
  • r₁ᵃᵈᵃᵖᵗ = r₁ × (1 - decay × 0.5)
  • r₂ᵃᵈᵃᵖᵗ = r₂ × (1 - decay × 0.3)

2.3 Сбалансированный рынок

  • G₁: позиции сохраняются
  • G₂: для каждого i ∈ G₂
    • выбрать случайного лидера k ∈ G₁
    • xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ - xᵢ)
  • G₃: для каждого i ∈ G₃
    • выбрать лидера k₁ ∈ G₁ и k₂ ∈ G₂
    • xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ₁ - xᵢ) + rand() × r₂ᵃᵈᵃᵖᵗ × (xₖ₂ - xᵢ)

2.4 Колеблющийся рынок

  • G₂: для каждого i ∈ G₂
    • если rand() < 0.5: xᵢ = xᵇᵉˢᵗ
    • иначе: xᵢ = центр(G₁) + шум × α × (1 - decay × 0.5)
  • G₃: для каждого i ∈ G₃
    • если rand() < α: xᵢ = случайная_позиция()
    • иначе если rand() < 0.5 + α/2: xᵢ = xᵢ + большое_возмущение
    • иначе: xᵢ = 2xᵢ - центр(худшие) + малый_шум

2.5 Обновление

  • Применить новые позиции для G₂ и G₃
  • Обновить глобально лучшее решение

3. Возврат результата

Вернуть найденное лучшее решение xᵇᵉˢᵗ

Теперь можем написать класс "C_AO_EMA", который будет являться наследником класса "C_AO" и реализует наш алгоритм оптимизации EMA. Основные компоненты: popSize — размер популяции (количество агентов), r1, r2 — коэффициенты поглощения для разных групп агентов, riskAlpha — фактор риска. Далее инициализируются параметры для настройки алгоритма и присваиваются значения по умолчанию.

Функции:

  • SetParams () — обновляет значения параметров алгоритма из массива "params", а также выполняет проверку на валидность этих параметров, чтобы убедиться, что они находятся в допустимых пределах.
  • Init () — инициализирует алгоритм перед его использованием, принимает параметры, такие как границы поиска (rangeMinP, rangeMaxP), шаг изменения параметров (rangeStepP) и количество эпох (epochsP).
  • Moving () — отвечает за перемещение агентов в пространстве поиска и представляет основную логику алгоритма EMA для обновления позиций агентов.
  • Revision() — выполняет пересмотр (revision) стратегий агентов включает в себя механизмы для улучшения и изменения текущих позиций агентов.
  • GetDecayRate() — возвращает коэффициент затухания (decay rate).

Переменные:

  • r1 — коэффициент поглощения для группы агентов. Более высокая величина означает, что агенты этой группы сильнее "притягиваются" к другим агентам или к "лучшим" решениям.
  • r2 — коэффициент поглощения для другой группы агентов.
  • riskAlpha — фактор риска, он определяет, насколько сильно агенты подвержены риску при принятии решений.
  • groupSize — размер каждой из трех групп, на которые делится популяция (popSize / 3).
  • currentEpoch — номер текущей итерации алгоритма.
  • totalEpochs — общее количество итераций, которые будут выполнены алгоритмом.
  • tempPop — временный массив для хранения новых позиций агентов при их перемещении. Тип "S_AO_Agent" определяет структуру отдельного агента (решения).

Класс "C_AO_EMA" предоставляет реализацию алгоритма EMA для численной оптимизации. Алгоритм разделяет популяцию на группы с разными стратегиями (определяемыми коэффициентами r1 и r2) и использует фактор риска (riskAlpha) для балансировки между исследованием пространства поиска и использованием найденных "хороших" решений. Функции "Moving ()" и "Revision ()" содержат основную логику оптимизации, "Init ()" инициализирует алгоритм, а "SetParams ()" позволяет изменять значения параметров.

//————————————————————————————————————————————————————————————————————
class C_AO_EMA : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_EMA () { }
  C_AO_EMA ()
  {
    ao_name = "EMA";
    ao_desc = "Exchange Market Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/18605";

    popSize   = 60;     // Размер популяции
    r1        = 1.5;    // Коэффициент поглощения для группы 2
    r2        = 0.8;    // Коэффициент поглощения для группы 3
    riskAlpha = 0.3;    // Фактор риска

    ArrayResize (params, 4);

    params [0].name = "popSize";   params [0].val = popSize;
    params [1].name = "r1";        params [1].val = r1;
    params [2].name = "r2";        params [2].val = r2;
    params [3].name = "riskAlpha"; params [3].val = riskAlpha;
  }

  void SetParams ()
  {
    popSize   = (int)params [0].val;
    r1        = params      [1].val;
    r2        = params      [2].val;
    riskAlpha = params      [3].val;

    // Проверка корректности параметров
    if (popSize < 6) popSize = 6;
    if (popSize % 3 != 0) popSize = ((popSize / 3) + 1) * 3; // Кратно 3
    if (r1 < 0.0) r1 = 0.0;
    if (r2 < 0.0) r2 = 0.0;
    if (riskAlpha < 0.0) riskAlpha = 0.0;
    if (riskAlpha > 1.0) riskAlpha = 1.0;
  }

  bool Init (const double &rangeMinP  [],
             const double &rangeMaxP  [],
             const double &rangeStepP [],
             const int     epochsP = 0);

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double r1;         // Коэффициент поглощения 1
  double r2;         // Коэффициент поглощения 2
  double riskAlpha;  // Фактор риска

  private: //---------------------------------------------------------
  int    groupSize;     // размер каждой группы (popSize/3)
  int    currentEpoch;  // текущая эпоха
  int    totalEpochs;   // общее количество эпох

  S_AO_Agent tempPop []; // временная популяция для хранения новых позиций

  double GetDecayRate   ();
};
//————————————————————————————————————————————————————————————————————

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

Далее происходит расчет размера каждой группы агентов, которая делится пополам по количеству элементов (деление общего размера популяции на 3). Инициализируется счетчик текущей эпохи и сохраняется общее количество эпох, на которые рассчитан алгоритм. После этого выделяется память под временную популяцию — массив агентов, который имеет такой же размер, как основная популяция. В цикле каждый агент в этом временном массиве инициализируется с помощью координат, которые задаются в массиве "coords".

Когда все эти шаги выполнены успешно, возвращается значение "true", что означает успешную подготовку к выполнению алгоритма.

//————————————————————————————————————————————————————————————————————
//--- Инициализация
bool C_AO_EMA::Init (const double &rangeMinP  [],
                     const double &rangeMaxP  [],
                     const double &rangeStepP [],
                     const int epochsP = 0)
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //------------------------------------------------------------------
  groupSize    = popSize / 3;
  currentEpoch = 0;
  totalEpochs  = epochsP;

  // Инициализация временной популяции
  ArrayResize (tempPop, popSize);
  for (int i = 0; i < popSize; i++)
  {
    tempPop [i].Init (coords);
  }

  return true;
}
//————————————————————————————————————————————————————————————————————

Метод "Moving" — это основная функция, реализующая эволюцию агентов в алгоритме EMA. Она отвечает за изменение позиций агентов в пространстве поиска. Начальная инициализация (выполняется только один раз):

  • Если это первый запуск алгоритма, то происходит случайная инициализация координат всех агентов в пределах заданных диапазонов (rangeMin, rangeMax).
  • Координаты каждого агента корректируются с учетом шага дискретизации (rangeStep).
  • Переменная "revision" устанавливается в истину, чтобы при следующих вызовах этого метода инициализация не повторялась.

Обновление эпохи и коэффициента затухания:

  • Счетчик текущей эпохи (currentEpoch) увеличивается.
  • Вычисляется "decayRate" (коэффициент затухания), который уменьшается с каждой эпохой, влияя на адаптивное поведение алгоритма.

Копирование текущей популяции. Координаты и значения целевой функции "f" всех агентов из текущей популяции "a2 копируются во временную популяцию "tempPop", что делается для того, чтобы изменения в "tempPop" не влияли на "a" до завершения всех расчетов в текущей итерации.

ФАЗА 1: Сбалансированный рынок (Поглощающие операторы). Эта фаза моделирует поведение агентов, которые "поглощают" информацию от лучших агентов. Популяция делится на три группы:

  • Группа 1 (Элита): эти агенты (с индексами от 0 до groupSize - 1) не изменяются на этом этапе, это лучшие агенты, и их позиции сохраняются.
  • Группа 2 (Поглощающий оператор 1): агенты этой группы (с индексами от "groupSize" до "2*groupSize - 1") обновляют свои позиции.
    • Для каждого агента из этой группы случайно выбирается "лидер" из Группы 1.
    • Координаты агента из Группы 2 смещаются в сторону координат выбранного лидера из Группы 1. Смещение зависит от адаптивного коэффициента "r1", который уменьшается с учетом "decayRate".
    • Координаты корректируются по диапазонам и шагу.
  • Группа 3 (Поглощающий оператор 2): агенты этой группы (с индексами от "2 * groupSize" до "popSize - 1") также обновляют свои позиции, но более активно.
    • Для каждого агента из этой группы случайно выбираются два лидера: один из Группы 1 и один из Группы 2.
    • Координаты агента из Группы 3 смещаются в сторону обоих выбранных лидеров, используя тот же адаптивный коэффициент "r1".
    • Координаты корректируются по диапазонам и шагу.

ФАЗА 2: Колеблющийся рынок (Поисковые операторы). Эта фаза моделирует исследовательское поведение агентов, позволяя им искать новые области в пространстве.

  • Группа 2 (Поисковый оператор 1 - умеренный риск):
    • Каждая координата агента из этой группы обновляется.
    • С вероятностью 50% координата полностью заменяется на значение из "cB".
    • В противном случае, координата смещается относительно "центра элиты" (среднее значение координат агентов Группы 1) с добавлением шума. Шум зависит от "riskAlpha", диапазона координат и "decayRate".
    • Координаты корректируются по диапазонам и шагу.
  • Группа 3 (Поисковый оператор 2 - высокий риск):
    • Каждая координата агента из этой группы обновляется.
    • С вероятностью "riskAlpha" координата полностью реинициализируется случайным образом в пределах диапазона. Это моделирует очень рискованное, широкомасштабное исследование.
    • Иначе, если второе случайное число меньше "0.5 + riskAlpha / 2.0", происходит "широкий поиск": координата смещается на случайную величину в определенном радиусе, зависящем от "riskAlpha" и "decayRate".
    • В противном случае, применяется "оппозиционное обучение": координата смещается в противоположном направлении. Добавляется небольшой случайный шум.
    • Координаты корректируются по диапазонам и шагу.

Копирование изменений. Наконец, после всех расчетов, обновленные координаты агентов из "tempPop" (для Групп 2 и 3) копируются обратно в основную популяцию "a". Координаты Группы 1 остаются неизменными (они изначально не были скопированы обратно).

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

//————————————————————————————————————————————————————————————————————
//--- Основной цикл алгоритма
void C_AO_EMA::Moving ()
{
  // Начальная инициализация
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    revision = true;
    return;
  }

  currentEpoch++;
  double decayRate = GetDecayRate ();

  // Копирование текущей популяции во временную
  for (int i = 0; i < popSize; i++)
  {
    ArrayCopy (tempPop [i].c, a [i].c, 0, 0, WHOLE_ARRAY);
    tempPop [i].f = a [i].f;
  }

  // ФАЗА 1: Сбалансированный рынок (Поглощающие операторы)

  // Группа 1 (элита) - не изменяется
  // Индексы: 0 ... groupSize-1

  // Группа 2 - поглощающий оператор 1
  // Индексы: groupSize ... 2*groupSize-1

  double adaptiveR1 = r1 * (1.0 - decayRate * 0.5);

  for (int i = groupSize; i < 2 * groupSize; i++)
  {
    // Каждый агент группы 2 выбирает случайного лидера из группы 1
    int leaderIdx = u.RNDminusOne (groupSize);

    for (int c = 0; c < coords; c++)
    {
      tempPop [i].c [c] = a [i].c [c] + u.RNDprobab () * adaptiveR1 * (a [leaderIdx].c [c] - a [i].c [c]);
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Группа 3 - поглощающий оператор 2
  // Индексы: 2*groupSize ... popSize-1

  adaptiveR1 = r1 * (1.0 - decayRate * 0.3);

  for (int i = 2 * groupSize; i < popSize; i++)
  {
    // Выбор лидеров из групп 1 и 2
    int leader1Idx = u.RNDminusOne (groupSize);
    int leader2Idx = groupSize + u.RNDminusOne (groupSize);

    for (int c = 0; c < coords; c++)
    {
      tempPop [i].c [c] = a [i].c [c] +
                          u.RNDprobab () * adaptiveR1 * (a [leader1Idx].c [c] - a [i].c [c]) +
                          u.RNDprobab () * adaptiveR1 * (a [leader2Idx].c [c] - a [i].c [c]);

      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // ФАЗА 2: Колеблющийся рынок (Поисковые операторы)

  // Группа 2 - поисковый оператор 1 (умеренный риск)
  for (int i = groupSize; i < 2 * groupSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      double range = rangeMax [c] - rangeMin [c];

      if (u.RNDprobab () < 0.5)
      {
        tempPop [i].c [c] = cB [c];// tempPop [i].c [c] + delta;
      }
      else
      {
        // Поиск вокруг центра элиты
        double eliteCenter = 0.0;
        for (int j = 0; j < groupSize; j++)
        {
          eliteCenter += a [j].c [c];
        }
        eliteCenter /= (double)groupSize;

        double noise = riskAlpha * range * u.RNDfromCI (-0.5, 0.5) * (1.0 - decayRate * 0.5);
        tempPop [i].c [c] = eliteCenter + noise;
      }

      // Проверка границ
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Группа 3 - поисковый оператор 2 (высокий риск)
  for (int i = 2 * groupSize; i < popSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      double range = rangeMax [c] - rangeMin [c];

      if (u.RNDprobab () < riskAlpha)
      {
        // Полная реинициализация
        tempPop [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
      }
      else
        if (u.RNDprobab () < 0.5 + riskAlpha / 2.0)
        {
          // Широкий поиск
          double searchRadius = 2.0 * riskAlpha * range * (1.0 - decayRate * 0.3);
          double delta = u.RNDfromCI (-searchRadius, searchRadius);

          tempPop [i].c [c] = tempPop [i].c [c] + delta;
        }
        else
        {
          // Оппозиционное обучение
          double worstCenter = 0.0;
          int worstCount = groupSize / 2;
          for (int j = popSize - worstCount; j < popSize; j++)
          {
            worstCenter += a [j].c [c];
          }
          worstCenter /= (double)worstCount;

          // Движение в противоположном направлении от худших
          tempPop [i].c [c] = 2.0 * tempPop [i].c [c] - worstCenter;

          // Добавляем небольшой шум
          double noise = riskAlpha * range * u.RNDfromCI (-0.1, 0.1);
          tempPop [i].c [c] += noise;
        }

      // Проверка границ
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Копирование из временной популяции в основную (кроме группы 1)
  for (int i = groupSize; i < popSize; i++)
  {
    ArrayCopy (a [i].c, tempPop [i].c, 0, 0, WHOLE_ARRAY);
  }
}
//————————————————————————————————————————————————————————————————————

Метод "GetDecayRate"  предназначен для вычисления коэффициента затухания, который управляет степенью изменения поведения алгоритма в ходе выполнения. Он сначала проверяет, если общее число эпох "totalEpochs" равно "0" или отрицательно, то возвращает ноль, чтобы избежать деления на ноль и обеспечить безопасную работу. Далее рассчитывает прогресс выполнения поиска в виде доли прошедших эпох относительно общего числа "progress". Значение "progress" растет от "0" до "1" по мере прохождения алгоритма.

Используя "progress", метод возвращает значение, основанное на сигмоидной функции экспоненциальной формы. Эта функция обеспечивает плавный переход коэффициента затухания от "0" к "1", делая так, чтобы в начале поиска (когда "progress" близко к 0) значение было близко к "0", а к концу (когда "progress" приближается к 1) — к 1. Такой нелинейный переход помогает сбалансировать баланс между исследованием пространства и эксплуатацией уже найденных решений.

Конкретно, используется сигмоидная функция вида: 1 / (1 + exp(-10 * (progress - 0.5))). Этот формат обеспечивает более резкий переход около середины процесса "progress ≈ 0.5", что способствует более динамическому изменению коэффициента во время работы алгоритма.

//————————————————————————————————————————————————————————————————————
//--- Получение коэффициента затухания
double C_AO_EMA::GetDecayRate ()
{
  if (totalEpochs <= 0) return 0.0;

  // Нелинейное затухание для лучшего баланса эксплуатации/исследования
  double progress = (double)currentEpoch / (double)totalEpochs;

  // Использование сигмоидной функции для плавного перехода
  return 1.0 / (1.0 + MathExp (-10.0 * (progress - 0.5)));
}
//————————————————————————————————————————————————————————————————————

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

Важно: после этой операции массив "a" будет отсортирован таким образом, что агент с наилучшим значением целевой функции "f" окажется на первом месте (индекс "0"). В алгоритмах минимизации это будет агент с наименьшим "f", в алгоритмах максимизации — с наибольшим "f". Координаты лучшего агента текущей популяции (который теперь находится в "a [0]" после сортировки) копируются в массив "cB", который хранит координаты лучшего решения, найденного алгоритмом на данный момент. Значение целевой функции лучшего агента присваивается переменной "fB". Эта переменная хранит значение целевой функции, соответствующее лучшим координатам "cB".

//————————————————————————————————————————————————————————————————————
//--- Обновление лучших решений
void C_AO_EMA::Revision ()
{
  static S_AO_Agent aT []; ArrayResize (aT, popSize);
  u.Sorting (a, aT, popSize);
  ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY);
  fB = a [0].f;
}
//————————————————————————————————————————————————————————————————————


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

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

EMA|Exchange Market Algorithm|60.0|1.5|0.8|0.3|
=============================
5 Hilly's; Func runs: 10000; result: 0.6706604188712635
25 Hilly's; Func runs: 10000; result: 0.42759923501764946
500 Hilly's; Func runs: 10000; result: 0.252217676777693
=============================
5 Forest's; Func runs: 10000; result: 0.7419215403847332
25 Forest's; Func runs: 10000; result: 0.38137087707323236
500 Forest's; Func runs: 10000; result: 0.19454127467011006
=============================
5 Megacity's; Func runs: 10000; result: 0.38769230769230767
25 Megacity's; Func runs: 10000; result: 0.21323076923076928
500 Megacity's; Func runs: 10000; result: 0.09672307692307769
=============================
All score: 3.36596 (37.40%)

На визуализации заметен разброс в результатах на функциях малой размерности.

Hilly

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

Forest

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

Megacity

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

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

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 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
13 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
14 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
15 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
16 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
17 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
18 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
19 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
20 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
21 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
22 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
23 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
24 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
25 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
26 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
27 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
28 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
29 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
30 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
31 (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
32 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
33 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
34 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
35 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
36 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
37 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
38 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
39 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
40 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
41 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
42 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
43 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
44 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
45 ADAMm adaptive moment estimation M 0,88635 0,44766 0,26613 1,60014 0,84497 0,38493 0,16889 1,39880 0,66154 0,27046 0,10594 1,03794 4,037 44,85
EMA exchange_market_algorithm 0,67066 0,42759 0,25221 1,35046 0,74192 0,38137 0,19454 1,31783 0,38769 0,21323 0,09672 0,69764 3,366 37,40
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


Выводы

Представленный алгоритм EMA, хотя и обладает базовыми поисковыми способностями, не демонстрирует достаточной производительности для вхождения в топ-45 рейтинговой таблицы популяционных алгоритмов оптимизации. Его монолитная структура и отсутствие явных механизмов для преодоления общеизвестных проблем, связанных с застреванием в локальных экстремумах, указывают на ряд потенциальных недостатков. 

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

tab

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

chart

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

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

Плюсы:

  1. простая реализация

Минусы:

  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_EMA.mq5
Скрипт Испытательный стенд для EMA

Прикрепленные файлы |
EMA.ZIP (310.01 KB)
Связь торговых роботов MetaTrader 5 с внешними брокерами через API и Python Связь торговых роботов MetaTrader 5 с внешними брокерами через API и Python
В настоящей статье мы обсудим реализацию MQL5 в партнерстве с Python для выполнения связанных с брокером операций. Представьте, что у вас есть постоянно работающий советник (EA), размещенный на VPS и совершающий сделки от вашего имени. В какой-то момент способность советника управлять средствами становится первостепенной. Она включает в себя такие операции, как пополнение вашего торгового счета и инициирование вывода средств. В данном обсуждении мы прольем свет на преимущества и практическую реализацию этих функций, обеспечивающих плавную интеграцию управления средствами в вашу торговую стратегию. Следите за обновлениями!
Создание торговой панели администратора на MQL5 (Часть VI): Панель управления торговлей (II) Создание торговой панели администратора на MQL5 (Часть VI): Панель управления торговлей (II)
В этой статье мы улучшим панель управления торговлей нашей многофункциональной панели администратора. Мы представим мощную вспомогательную функцию, которая упрощает код, улучшая его читаемость, удобство обслуживания и эффективность. Мы также продемонстрируем, как легко интегрировать дополнительные кнопки и улучшить интерфейс для решения более широкого спектра торговых задач. Независимо от того, управляете ли вы позициями, корректируете ордера или упрощаете взаимодействие с пользователем, это руководство поможет вам разработать надежную и удобную панель управления торговлей.
Нейросети в трейдинге: Вероятностное прогнозирование временных рядов (Энкодер) Нейросети в трейдинге: Вероятностное прогнозирование временных рядов (Энкодер)
Предлагаем познакомиться с новым подходом, который объединяет классические методы и современные нейросети для анализа временных рядов. В статье подробно раскрыта архитектура и принципы работы модели K²VAE.
Разработка инструментария для анализа движения цен (Часть 2): Скрипт аналитических комментариев Разработка инструментария для анализа движения цен (Часть 2): Скрипт аналитических комментариев
В продолжение нашей работы по упрощению взаимодействия с поведением цены мы рады представить еще один инструмент, который может значительно улучшить ваш анализ рынка и помочь вам принимать обоснованные решения. Этот инструмент отображает ключевые технические индикаторы, такие как цены предыдущего дня, значимые уровни поддержки и сопротивления, а также торговый объем, автоматически генерируя визуальные подсказки на графике.