
Алгоритм биржевого рынка — Exchange Market Algorithm (EMA)
Содержание
Введение
В области численной оптимизации непрерывно разрабатываются новые алгоритмы, стремящиеся эффективно решать сложные задачи в условиях неопределенности и многомерности. Среди них особое место занимают популяционные метаэвристики, которые, имитируя природные или социальные процессы, демонстрируют впечатляющие способности к поиску глобальных оптимумов.
Однако не каждый новый алгоритм способен достичь конкурентного уровня производительности. Данная статья посвящена подробному анализу алгоритма Exchange Market Algorithm (EMA)— представителя вышеупомянутого класса методов, который вдохновлен поведением трейдеров на фондовом рынке. Алгоритм моделирует процесс торговли акциями, где участники рынка с разным уровнем успеха применяют различные стратегии для максимизации прибыли.
Реализация алгоритма
Алгоритм начинает работу с формирования популяции из участников рынка (трейдеров), все участники будут разделены на три равные группы. Каждому трейдеру случайным образом назначается начальная позиция в пространстве поиска, представляющая его текущий портфель акций. Эти позиции равномерно распределены по всему допустимому диапазону значений.
После начальной оценки успешности каждого трейдера (вычисления значения целевой функции), вся популяция сортируется по убыванию успешности и делится на три группы:
- первая группа — элита. Это наиболее успешные участники рынка, которые нашли самые прибыльные позиции. Важная особенность: элитные трейдеры сохраняют свои позиции неизменными в течение всей торговой сессии, служа примером для остальных.
- вторая группа — средний класс. Умеренно успешные участники, которые стремятся улучшить свои позиции, учась у элиты, но при этом готовы к умеренному риску.
- третья группа — начинающие. Наименее успешные участники, готовые к высокому риску в попытках кардинально улучшить свое положение.
Каждая итерация алгоритма представляет собой торговую сессию, состоящую из двух фаз, соответствующих разным состояниям рынка.
Фаза 1: Сбалансированный рынок (стабильная торговля)
В условиях стабильного рынка трейдеры применяют консервативные стратегии, основанные на подражании более успешным участникам.
Поведение среднего класса. Каждый трейдер из второй группы выбирает случайного наставника из элиты и постепенно смещает свою позицию в его направлении. Степень этого смещения определяется коэффициентом поглощения (r1 = 1.5), который адаптивно уменьшается по мере прогресса алгоритма. Движение происходит не детерминированно — используется случайный множитель, имитирующий неопределенность рыночных решений.
Поведение начинающих. Трейдеры третьей группы находятся под влиянием как элиты, так и среднего класса. Каждый начинающий выбирает по одному наставнику из первой и второй групп, и его новая позиция формируется как комбинация движений в направлении обоих наставников. Это отражает попытку учиться одновременно у самых успешных и у тех, кто недавно улучшил свои позиции.
Фаза 2: Колеблющийся рынок (волатильная торговля)
В условиях нестабильного рынка трейдеры применяют более рискованные стратегии поиска новых возможностей.
Исследовательское поведение среднего класса. Трейдеры второй группы применяют двойную стратегию. С вероятностью 50% они могут полностью скопировать позицию глобально лучшего решения (самого успешного трейдера за всю историю). В остальных случаях они исследуют область вокруг центра масс элитной группы, добавляя контролируемый случайный шум к этой позиции. Величина шума определяется фактором риска (riskAlpha = 0.3) и уменьшается со временем.
Агрессивное поведение начинающих. Третья группа применяет наиболее рискованные стратегии:
- с вероятностью, равной фактору риска (30%), трейдер полностью забывает свою текущую позицию и начинает заново со случайной точки;
- в 35% случаев проводится широкий поиск вокруг текущей позиции с большим радиусом;
- в оставшихся случаях применяется оппозиционное обучение: трейдер движется в направлении, противоположном центру худших решений, пытаясь уйти от неудачных областей.
Ключевой особенностью EMA является его адаптивность. По мере выполнения итераций вычисляется коэффициент затухания на основе сигмоидной функции. Этот коэффициент обеспечивает плавный переход от исследования (в начале работы) к эксплуатации найденных хороших решений (в конце).
После выполнения обеих фаз происходит важный этап: позиции всех трейдеров, кроме элиты, обновляются согласно вычисленным новым значениям. Элита сохраняет свои позиции, что обеспечивает сохранение лучших найденных решений.
Затем для всех трейдеров вычисляется новая успешность (значение целевой функции), популяция снова сортируется, и определяется новое глобально лучшее решение. Теперь посмотрим, как выглядят формулы алгоритма на представленной ниже картинке.
Иллюстрация содержит все ключевые формулы обновления позиций для каждой группы в обоих режимах рынка. Согласно описанию и на основе представленных формул, сформируем псевдокод алгоритма 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%)
На визуализации заметен разброс в результатах на функциях малой размерности.
EMA на тестовой функции Hilly
EMA на тестовой функции Forest
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 останется базовым алгоритмом с ограниченным потенциалом для решения сложных оптимизационных задач, что и объясняет его отсутствие в рейтингах топовых метаэвристик, однако, представленные идеи, заложенные в алгоритм, достаточно интересны и перспективны для нашей копилки идей на дальнейшую доработку.
Рисунок 2. Цветовая градация алгоритмов по соответствующим тестам
Рисунок 3. Гистограмма результатов тестирования алгоритмов (по шкале от 0 до 100, чем больше, тем лучше, где 100 — максимально возможный теоретический результат, в архиве скрипт для расчета рейтинговой таблицы)
Плюсы и минусы алгоритма EMA:
Плюсы:
- простая реализация
Минусы:
- низкая точность сходимости
К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.
Программы, используемые в статье
# | Имя | Тип | Описание |
---|---|---|---|
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 |





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования