Algoritmo de mercado bursátil de Intercambio — Exchange Market Algorithm (EMA)
Contenido
Introducción
En el campo de la optimización numérica, se desarrollan continuamente nuevos algoritmos que buscan resolver de manera eficaz problemas complejos en condiciones de incertidumbre y multidimensionalidad. Entre ellos, un lugar especial lo ocupan las metaheurísticas de población, que, al simular procesos naturales o sociales, demuestran una capacidad impresionante para encontrar óptimos globales.
Sin embargo, no todos los algoritmos nuevos son capaces de alcanzar niveles de rendimiento competitivos. Este artículo está dedicado a un análisis detallado del algoritmo del mercado bursátil (Exchange Market Algorithm, EMA), un representante de la clase de métodos antes mencionada, inspirado en el comportamiento de los tráders en el mercado de valores. El algoritmo simula el proceso de negociación de acciones, donde los participantes del mercado con distintos niveles de éxito emplean distintas estrategias para maximizar sus beneficios.
Implementación del algoritmo
El algoritmo comienza por formar una población de participantes del mercado (tráders), donde todos los participantes se dividen en tres grupos iguales. A cada tráder se le asigna aleatoriamente una posición inicial en el espacio de búsqueda que representa su cartera de acciones actual. Estas posiciones se distribuyen de manera uniforme en todo el rango de valores aceptables.
Tras una evaluación inicial del éxito de cada tráder (cálculo del valor de la función objetivo), toda la población se ordena en orden descendente según su éxito y se divide en tres grupos:
- El primer grupo es la élite. Estos son los participantes del mercado más exitosos, quienes han encontrado las posiciones más rentables. Una característica importante: los tráders de élite mantienen sus posiciones sin cambios durante toda la sesión de negociación, sirviendo de ejemplo para los demás.
- El segundo grupo es la clase media. La clase media incluye a participantes moderadamente exitosos que buscan mejorar su posición aprendiendo de la élite, pero que también están dispuestos a asumir riesgos moderados.
- El tercer grupo es el de los principiantes. Estos son los participantes menos exitosos, dispuestos a asumir grandes riesgos en un intento por mejorar radicalmente su situación.
Cada iteración del algoritmo representa una sesión de negociación que consta de dos fases correspondientes a distintos estados del mercado.
Fase 1: Mercado equilibrado (negociación estable)
En condiciones de mercado estables, los tráders usan estrategias conservadoras basadas en la imitación de los participantes más exitosos.
Comportamiento de la clase media. Cada tráder del segundo grupo selecciona un mentor al azar de entre la élite y, gradualmente, va desplazando su posición en su dirección. La magnitud de este desplazamiento viene determinada por el coeficiente de absorción (r1 = 1,5), que disminuye de forma adaptativa a medida que avanza el algoritmo. El movimiento no es determinista; para simular la incertidumbre de las decisiones del mercado, se usa un multiplicador aleatorio.
Comportamiento de los principiantes. Los tráders del tercer grupo están influenciados tanto por la élite como por la clase media. Cada principiante elige un mentor del primer y segundo grupo, y su nueva posición se forma usando una combinación de movimientos en la dirección de ambos mentores. Esto refleja un intento de aprender simultáneamente de los más exitosos y de aquellos que han mejorado recientemente su posición.
Fase 2: Mercado volátil (negociación volátil)
En condiciones de mercado volátiles, los tráders adoptan estrategias más arriesgadas para buscar nuevas oportunidades.
Comportamiento exploratorio de la clase media. Los tráders del segundo grupo usan una estrategia dual. Existe un 50% de probabilidades de que puedan copiar por completo la posición de la mejor solución global (el tráder bursátil más exitoso de la historia). En otros casos, exploran la zona que rodea el centro de masas del grupo de élite, añadiendo ruido aleatorio controlado a esa posición. El nivel de ruido está determinado por el factor de riesgo (riskAlpha = 0,3) y disminuye con el tiempo.
Comportamiento agresivo de los principiantes. El tercer grupo usa las estrategias más arriesgadas:
- Con una probabilidad igual al factor de riesgo (30%), el tráder olvida por completo su posición actual y comienza de nuevo desde un punto aleatorio;
- En el 35% de los casos, se realiza una búsqueda amplia alrededor de la posición actual con un radio amplio;
- En los casos restantes, se usa el aprendizaje por oposición: el tráder se mueve en la dirección opuesta al centro de las peores decisiones, tratando de alejarse de las zonas de bajo rendimiento.
La característica clave de EMA es su adaptabilidad. A medida que se ejecutan las iteraciones se calcula el coeficiente de atenuación basándose en la función sigmoide. Este coeficiente garantiza una transición fluida desde la exploración (al inicio de la ejecución) hasta la explotación de las buenas soluciones encontradas (al final).
Una vez finalizadas ambas fases, se produce un paso importante: las posiciones de todos los tráders, salvo las de la élite, se actualizan según los nuevos valores calculados. La élite mantiene su posición, lo cual garantiza la preservación de las mejores soluciones encontradas.
A continuación, se evalúa nuevamente el valor de la función objetivo para todos los tráders, se reordena la población y se determina una nueva solución óptima a nivel global. Ahora veamos cómo se ven las fórmulas del algoritmo en la imagen de abajo.

La ilustración contiene todas las fórmulas clave de actualización de posiciones para cada grupo en ambos modos de mercado. Según la descripción y basándonos en las fórmulas presentadas, generaremos un pseudocódigo para el algoritmo EMA.
1. Inicialización
- Creamos una población de N = 60 tráders (N es un múltiplo de 3).
- Establecemos los parámetros: r₁ = 1,5, r₂ = 0,8, α = 0,3 (factor de riesgo)
- Inicializamos cada tráder con una posición aleatoria.
- Determinamos el tamaño del grupo: m = N/3 = 20
2. Ciclo principal
Para cada iteración t = 1, 2, ..., T, se ejecuta:
2.1 Clasificación
- Evaluamos la idoneidad de todos los tráders
- Ordenamos según el nivel de aptitud descendente
- Dividimos en grupos:
- G₁ = {1, ..., m} — élite
- G₂ = {m+1, ..., 2m} — clase media
- G₃ = {2m+1, ..., N} — principiantes
2.2 Adaptación de parámetros
- 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 Mercado equilibrado
- G₁: se mantienen las posiciones
- G₂: para cada i ∈ G₂
- Se selecciona un líder aleatorio k ∈ G₁
- xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ - xᵢ)
- G₃: para cada i ∈ G₃
- Se selecciona un líder k₁ ∈ G₁ y k₂ ∈ G₂
- xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ₁ - xᵢ) + rand() × r₂ᵃᵈᵃᵖᵗ × (xₖ₂ - xᵢ)
2.4 Mercado fluctuante
- G₂: para cada i ∈ G₂
- Si rand() < 0.5: xᵢ = xᵇᵉˢᵗ
- En caso contrario: xᵢ = centro(G₁) + ruido × α × (1 - decay × 0,5)
- G₃: para cada i ∈ G₃
- si rand() < α: xᵢ = posición_aleatoria()
- De lo contrario, si rand() < 0,5 + α/2: xᵢ = xᵢ + gran_perturbación
- de lo contrario: xᵢ = 2xᵢ - centro(peor) + ruido_pequeño
2.5 Actualización
- Aplicamos las nuevas posiciones para G₂ y G₃
- Actualizamos globalmente la mejor solución
3. Retorno del resultado
Devolvemos la mejor solución encontrada xᵇᵉˢᵗ
Ahora podemos escribir la clase "C_AO_EMA", que será descendiente de la clase "C_AO", y también implementar nuestro algoritmo de optimización EMA. Componentes principales: popSize es el tamaño de la población (número de agentes), r1, r2 son coeficientes de absorción para diferentes grupos de agentes, riskAlpha es el factor de riesgo. A continuación, inicializamos los parámetros para configurar el algoritmo y asignamos los valores predeterminados.
Funciones:
- SetParams() — actualiza los valores de los parámetros del algoritmo desde el array "params" y también realiza una comprobación de validación de estos parámetros para garantizar que estén dentro de rangos aceptables.
- Init() — inicializa el algoritmo antes de usarlo, acepta parámetros como límites de búsqueda (rangeMinP, rangeMaxP), el paso de parámetro (rangeStepP) y el número de épocas (epochsP).
- Moving() — se encarga de mover los agentes en el espacio de búsqueda y representa la lógica principal del algoritmo EMA para actualizar las posiciones de los agentes.
- Revision() — realiza una revisión de las estrategias de los agentes e incluye mecanismos para mejorar y cambiar las posiciones actuales de los agentes.
- GetDecayRate() — retorna el coeficiente de atenuación (decay rate).
Variables:
- r1 — coeficiente de absorción para un grupo de agentes. Un valor más alto indica que los agentes de este grupo se sienten más "atraídos" por otros agentes o por soluciones "mejores".
- r2 — coeficiente de absorción para otro grupo de agentes.
- riskAlpha — factor de riesgo que determina a qué nivel de riesgo están expuestos los agentes al tomar decisiones.
- groupSize — tamaño de cada uno de los tres grupos en los que se divide la población (popSize / 3).
- currentEpoch — número de la iteración actual del algoritmo.
- totalEpochs — número total de iteraciones que realizará el algoritmo.
- tempPop — array temporal para almacenar las nuevas posiciones de los agentes a medida que se mueven. El tipo "S_AO_Agent" define la estructura de un único agente (decisión).
La clase "C_AO_EMA" proporciona una implementación del algoritmo EMA para la optimización numérica. El algoritmo divide la población en grupos con diferentes estrategias (definidas por los coeficientes r1 y r2) y usa un factor de riesgo (riskAlfa) para equilibrar la exploración del espacio de búsqueda con el uso de las soluciones "buenas" encontradas. Las funciones "Moving()" y "Revision()" contienen la lógica principal de optimización, "Init()" inicializa el algoritmo y "SetParams()" permite cambiar los valores de los parámetros.
//———————————————————————————————————————————————————————————————————— 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 (); }; //————————————————————————————————————————————————————————————————————
El método de inicialización "Init" tiene como objetivo preparar el algoritmo para su ejecución. Requiere arrays de valores mínimos, valores máximos y pasos para los parámetros de búsqueda, así como el número de épocas (iteraciones). En primer lugar, se llama a la función de inicialización estándar, que se encarga de configurar los rangos de búsqueda y comprobar su corrección. Si devuelve un resultado negativo, se finaliza la inicialización del método.
A continuación, se calcula el tamaño de cada grupo de agentes, que se obtiene dividiendo el tamaño total de la población entre 3. Luego se inicializa el contador de épocas actual y se almacena el número total de épocas para las que está diseñado el algoritmo. Posteriormente, se asigna memoria para una población temporal: un array de agentes que tiene el mismo tamaño que la población principal. En el ciclo, cada agente de este array temporal se inicializa con las coordenadas especificadas en el array "coords".
Cuando todos estos pasos se completan con éxito, el valor devuelto es "true", lo que significa que el algoritmo está preparado correctamente para su ejecución.
//———————————————————————————————————————————————————————————————————— //--- Инициализация 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; } //————————————————————————————————————————————————————————————————————
El método "Moving" es la función principal que implementa la evolución de los agentes en el algoritmo EMA. Este se encarga de cambiar las posiciones de los agentes en el espacio de búsqueda. Inicialización (se realiza solo una vez):
- Si se trata de la primera ejecución del algoritmo, entonces se produce una inicialización aleatoria de las coordenadas de todos los agentes dentro de los rangos indicados (rangeMin, rangeMax).
- Las coordenadas de cada agente se ajustan considerando el paso de discretización (rangeStep).
- La variable "revision" se establece en verdadero para que las llamadas posteriores a este método no repitan la inicialización.
Actualización de época y factor de atenuación:
- El contador de época actual (currentEpoch) se incrementa.
- Se calcula el decayRate (coeficiente de atenuación), el cual disminuye con cada época, influyendo en el comportamiento adaptativo del algoritmo.
Copiado de la población actual. Las coordenadas y los valores de la función objetivo "f" de todos los agentes de la población actual "a2" se copian a la población temporal "tempPop": esto se hace para que los cambios en "tempPop" no afecten a "a" hasta que se completen todos los cálculos en la iteración actual.
FASE 1: Mercado equilibrado (operadores absorbentes). Esta fase modela el comportamiento de los agentes que "absorben" información de los mejores agentes. La población se divide en tres grupos:
- Grupo 1 (Élite): estos agentes (con índices de 0 a groupSize - 1) no cambian en esta etapa, son los mejores agentes y sus posiciones se mantienen.
- Grupo 2 (Operador absorbente 1): los agentes de este grupo (con índices desde "groupSize" hasta "2*groupSize - 1") actualizan sus posiciones.
- Para cada agente de este grupo, se selecciona aleatoriamente un "líder" del Grupo 1.
- Las coordenadas del agente del Grupo 2 se desplazan hacia las coordenadas del líder seleccionado del Grupo 1. El desplazamiento depende del coeficiente adaptativo "r1", que disminuye considerando "decayRate".
- Las coordenadas se ajustan según el rango y el paso.
- Grupo 3 (Operador absorbente 2): los agentes de este grupo (con índices desde "2 * groupSize" hasta "popSize - 1") también actualizan sus posiciones, pero de forma más activa.
- Para cada agente de este grupo, se seleccionan aleatoriamente dos líderes: uno del Grupo 1 y otro del Grupo 2.
- Las coordenadas del agente del Grupo 3 se desplazan hacia ambos líderes seleccionados usando el mismo coeficiente adaptativo "r1".
- Las coordenadas se ajustan según el rango y el paso.
FASE 2: Mercado fluctuante (operadores de búsqueda). Esta fase modela el comportamiento exploratorio de los agentes, permitiéndoles buscar nuevas áreas en el espacio.
- Grupo 2 (Operador de búsqueda 1 - Riesgo moderado):
- Se actualiza cada coordenada del agente de este grupo.
- Existe un 50% de probabilidad de que la coordenada sea reemplazada completamente por el valor de "cB".
- De lo contrario, la coordenada se desplaza en relación con el "centro de élite" (el valor promedio de las coordenadas de los agentes del Grupo 1) con la adición de ruido. El ruido depende de "riskAlpha", el rango de coordenadas y "decayRate".
- Las coordenadas se ajustan según el rango y el paso.
- Grupo 3 (Operador de búsqueda 2 - Alto riesgo):
- Se actualiza cada coordenada del agente de este grupo.
- Con una probabilidad "riskAlpha", la coordenada se reinicializa completamente de forma aleatoria dentro del rango. Esto simula una exploración ampllia y de alto riesgo del espacio de búsqueda.
- De lo contrario, si el segundo número aleatorio es menor que "0,5 + riskAlpha / 2,0", se produce una "búsqueda amplia": la coordenada se desplaza una cantidad aleatoria dentro de un cierto radio, dependiendo de "riskAlpha" y "decayRate".
- En caso contrario, se aplica el "aprendizaje por oposición": la coordenada se desplaza en la dirección opuesta. Luego se añade un poco de ruido aleatorio.
- Las coordenadas se ajustan según el rango y el paso.
Copiado de cambios. Finalmente, después de todos los cálculos, las coordenadas actualizadas de los agentes de "tempPop" (para los Grupos 2 y 3) se copian de nuevo a la población principal "a". Las coordenadas del Grupo 1 permanecen sin cambios (no se copiaron inicialmente).
En general, el método "Moving" implementa un proceso iterativo en el que los agentes de la población cambian de posición "aprendiendo" de los mejores agentes y explorando el espacio de búsqueda con diferentes grados de riesgo.
//———————————————————————————————————————————————————————————————————— //--- Основной цикл алгоритма 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); } } //————————————————————————————————————————————————————————————————————
El método "GetDecayRate" se usa para calcular la tasa de atenuación, que controla el grado en que cambia el comportamiento del algoritmo durante su ejecución. Primero comprueba si el número total de épocas "totalEpochs" es "0" o negativo, y luego retorna cero para evitar la división por cero y garantizar un funcionamiento seguro. A continuación, calcula el progreso de la búsqueda como una proporción de las épocas pasadas en relación con el número total de "progress". El valor "progress" aumenta de "0" a "1" a medida que avanza el algoritmo.
Utilizando "progress", el método retorna un valor basado en la función sigmoide de forma exponencial. Esta función ofrece una transición suave del factor de atenuación de "0" a "1", de modo que al comienzo de la búsqueda (cuando "progress" está cerca de 0) el valor está cerca de "0", y al final (cuando "progress" se acerca a 1) se acerca a 1. Esta transición no lineal ayuda a equilibrar la exploración del espacio y la explotación de las soluciones ya encontradas.
Específicamente, se usa una función sigmoide de la forma: 1 / (1 + exp(-10 * (progress - 0.5))). Este formato ofrece una transición más brusca cerca de la mitad del proceso "progress ≈ 0,5", lo que contribuye a un cambio más dinámico en el coeficiente durante el funcionamiento del algoritmo.
//———————————————————————————————————————————————————————————————————— //--- Получение коэффициента затухания 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))); } //————————————————————————————————————————————————————————————————————
En el contexto del algoritmo EMA, el método de "Revision" se encarga de actualizar las mejores soluciones encontradas (óptimo global) en cada iteración. Se crea un array estático temporal "aT" de tipo "S_AO_Agent". "static" significa que este array se creará la primera vez que se llame a la función y conservará su estado entre llamadas. "ArrayResize(aT, popSize)"; redimensiona este array temporal para que pueda acomodar "popSize" (el tamaño de la población) de agentes. Este array se utilizará para la clasificación. La función "Sorting" se llama desde el objeto auxiliar "u". Esta función ordena la población principal de agentes "a" según su aptitud (el valor de la función objetivo "f").
Importante: después de esta operación, el array "a" se ordenará de tal manera que el agente con el mejor valor de la función objetivo "f" estará en primer lugar (índice "0"). En los algoritmos de minimización, será el agente con el valor "f" más pequeño; en los algoritmos de maximización, el que tenga el valor "f" más grande. Las coordenadas del mejor agente de la población actual (que ahora está en "a[0]" después de la clasificación) se copian en el array "cB", que almacena las coordenadas de la mejor solución encontrada por el algoritmo hasta el momento. El valor de la función objetivo del mejor agente se asigna a la variable "fB". Esta variable almacena el valor de la función objetivo correspondiente a las mejores coordenadas "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; } //————————————————————————————————————————————————————————————————————
Resultados de las pruebas
Querríamos obtener mejores resultados después de pasar la prueba, aunque el algoritmo funciona y tiene ciertas capacidades de búsqueda.
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%)
La visualización muestra una dispersión en los resultados para funciones de baja dimensionalidad.

EMA en la función de prueba de Hilly

EMA en la función de prueba Forest

EMA en la función de prueba Megacity
Tras las pruebas realizadas, el algoritmo EMA se presentará en nuestra tabla de clasificación de algoritmos de optimización de poblaciones únicamente con fines informativos.
| № | 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 optimization 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 | búsqueda de orbitales atómicos 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 | |
Conclusiones
El algoritmo EMA presentado, si bien posee capacidades de búsqueda básicas, no demuestra un rendimiento suficiente para entrar en el top 45 de la tabla de clasificación de algoritmos de optimización de población. Su estructura monolítica y la falta de mecanismos explícitos para superar los conocidos problemas de atascamiento en óptimos locales apuntan a una serie de deficiencias potenciales.
Sin mejoras significativas en su funcionalidad y control de comportamiento adaptativo, el EMA seguirá siendo un algoritmo básico con potencial limitado para resolver problemas de optimización complejos, lo que explica su ausencia en los primeros puestos de las clasificaciones de metaheurísticas. Sin embargo, las ideas presentadas en el algoritmo son bastante interesantes y prometedoras para nuestro conjunto de ideas para su desarrollo futuro.

Figura 2. Clasificación por colores de los algoritmos según las pruebas respectivas

Figura 3. Histograma de los resultados de las pruebas de algoritmos (en una escala de 0 a 100, cuanto más mejor, donde 100 es el máximo resultado teórico posible, el script para calcular la tabla de puntuación está en el archivo)
Ventajas y desventajas del algoritmo EMA:
Ventajas:
- implementación sencilla
Desventajas:
- baja precisión de convergencia
Adjuntamos al artículo un archivo con las versiones actuales de los códigos de los algoritmos. El autor de este artículo no se responsabiliza de la exactitud absoluta de la descripción de los algoritmos canónicos, muchos de ellos han sido modificados para mejorar las capacidades de búsqueda. Las conclusiones y juicios presentados en los artículos se basan en los resultados de los experimentos realizados.
Programas usados en el artículo
| # | Nombre | Tipo | Descripción |
|---|---|---|---|
| 1 | #C_AO.mqh | Archivo de inclusión | Clase padre de algoritmos de optimización basados en la población |
| 2 | #C_AO_enum.mqh | Archivo de inclusión | Enumeración de los algoritmos de optimización basados en la población |
| 3 | TestFunctions.mqh | Archivo de inclusión | Biblioteca de funciones de prueba |
| 4 | TestStandFunctions.mqh | Archivo de inclusión | Biblioteca de funciones del banco de pruebas |
| 5 | Utilities.mqh | Archivo de inclusión | Biblioteca de funciones auxiliares |
| 6 | CalculationTestResults.mqh | Archivo de inclusión | Script para calcular los resultados en una tabla comparativa |
| 7 | Testing AOs.mq5 | Script | Banco de pruebas único para todos los algoritmos de optimización basados en la población |
| 8 | Simple use of population optimization algorithms.mq5 | Script | Ejemplo sencillo de utilización de algoritmos de optimización basados en la población sin visualización |
| 9 | Test_AO_EMA.mq5 | Script | Banco de pruebas para EMA |
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/18605
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Exploramos modelos de regresión para inferencia causal y operaciones bursátiles
Formulación de un Asesor Experto Multipar Dinámico (Parte 3): Estrategias de reversión a la media y de impulso
Particularidades del trabajo con números del tipo double en MQL4
Introducción a MQL5 (Parte 18): Introducción al patrón de onda de Wolfe
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso