Algoritmo del duelista — Duelist Algorithm
Índice
Introducción
En el trading algorítmico, cada milisegundo supone una ganancia o una pérdida, por lo que la búsqueda de los parámetros óptimos para una estrategia de trading se convierte en una tarea de vital importancia. Los traders actuales recurren a métodos de inteligencia artificial y a algoritmos evolutivos para ajustar sus sistemas de trading.
Hoy analizaremos un nuevo enfoque de optimización: el Duelist Algorithm (algoritmo del duelista), inspirado en el antiguo arte de los duelos. El algoritmo fue desarrollado en 2015 por un grupo de científicos indonesios dirigido por Biyanto como alternativa a los algoritmos evolutivos tradicionales, con el objetivo de minimizar el carácter «ciego» de los operadores de mutación y cruce mediante un enfoque diferenciado para los ganadores y los perdedores. En este artículo analizaremos con detalle los fundamentos matemáticos del Duelist Algorithm, desarrollaremos una implementación en el lenguaje MQL5 y realizaremos un análisis comparativo con otros métodos de optimización poblacional.
Implementación del algoritmo
Imagine el entorno de trading como una arena en la que distintas estrategias compiten constantemente entre sí. Hay estrategias que triunfan en determinadas condiciones de mercado, mientras que otras fracasan. Pero ¿qué distingue a un tráder exitoso? La capacidad de aprender tanto de sus victorias como de sus derrotas, adaptando y perfeccionando constantemente su propio enfoque.
Precisamente este concepto encarna el Duelist Algorithm. A diferencia de los algoritmos genéticos clásicos, en los que todos los individuos se tratan por igual, el algoritmo del duelista reconoce una diferencia fundamental entre los ganadores y los perdedores. Los perdedores aprenden: analizan las estrategias de los ganadores y adoptan sus elementos exitosos, mientras que los ganadores tienden a innovar: experimentan con nuevos enfoques, sabiendo que su estrategia básica ya ha demostrado su eficacia. Los campeones actúan como mentores: las mejores estrategias se transmiten a la siguiente generación.

Figura 1. Ilustración del funcionamiento del algoritmo DA_duelist
La visualización de las principales etapas del algoritmo muestra todos los aspectos clave del funcionamiento del algoritmo del duelista: población inicial —todos los duelistas comienzan con las mismas oportunidades—; determinación de los campeones —los mejores duelistas aparecen resaltados en color dorado—. Duelos: se muestra el proceso del duelo con la determinación del ganador y del perdedor. El aprendizaje y la innovación: dos caminos distintos de desarrollo para los perdedores y los ganadores. Entrenamiento de nuevos duelistas —cuando los campeones transmiten sus habilidades a la nueva generación— y eliminación: los peores duelistas se eliminan de la población.
La ilustración incluye además una flecha cíclica que muestra la naturaleza iterativa del algoritmo, el resultado final con el campeón ganador, así como una leyenda detallada con la explicación de todos los elementos y parámetros clave. La visualización ayuda a comprender rápidamente el concepto básico del algoritmo.
Ahora, tras un análisis detallado, escribiremos el pseudocódigo del algoritmo.
PARÁMETROS DE ENTRADA:
- popSize: tamaño de la población de duelistas
- luckCoefficient: coeficiente de suerte en los duelos
- learningProbability: probabilidad de aprendizaje del perdedor
- innovationProbability: probabilidad de innovación del ganador
- championsCount: número de campeones
INICIALIZACIÓN:
1. Comprobar y ajustar championsCount:
- Si < 1, establecerlo en 1
- Si >= popSize, establecerlo en popSize / 4
2. Crear arrays vacíos winners y losers
3. Crear un array champions de tamaño championsCount
CICLO PRINCIPAL (Moving):
SI es la primera iteración:
PARA cada duelista i desde 0 hasta popSize:
PARA cada coordenada j:
Inicializar con un valor aleatorio dentro de los límites permitidos
Establecer el indicador revision en true
SALIR de la función
EN CASO CONTRARIO (iteraciones posteriores):
1. EXPANSIÓN DE LA POBLACIÓN:
- Redimensionar el array de duelistas a (popSize + championsCount)
- Inicializar las estructuras de los nuevos duelistas
2. DETERMINACIÓN DE LOS CAMPEONES:
- Ordenar a los duelistas por fitness en orden descendente (ordenación de burbuja)
- Los primeros championsCount duelistas se convierten en campeones
3. ENTRENAMIENTO DE NUEVOS DUELISTAS:
PARA cada campeón i:
Llamar a TrainNewDuelist (i, popSize + i):
PARA cada coordenada c:
nuevo_duelista [c] = campeón [c] + GaussDistribution ()
Aplicar las restricciones de rango
4. EJECUCIÓN DE DUELOS:
Vaciar los arrays winners y losers
PARA cada duelista i desde championsCount hasta totalDuelists:
Seleccionar un oponente aleatorio opponent
SI opponent != i:
DetermineWinnerAndLoser(i, opponent):
A_Luck = fitness [A] * (luckCoef + random () * luckCoef)
B_Luck = fitness [B] * (luckCoef + random () * luckCoef)
SI (fitness [A] + A_Luck) >= (fitness [B] + B_Luck):
A: ganador; B: perdedor
EN CASO CONTRARIO:
B: ganador; A: perdedor
Añadir a los arrays winners y losers
5. PROCESO DE MEJORA:
a) Aprendizaje de los perdedores:
PARA cada pareja (perdedor, ganador):
LearningProcess (loser, winner):
PARA cada coordenada c:
SI random () < learningProbability:
loser [c] = winner [c]
b) Innovaciones de los ganadores:
PARA cada ganador:
InnovationProcess (winner):
PARA cada coordenada c:
SI random () < innovationProbability:
winner [c] = valor_aleatorio_en_el_rango
6. ELIMINACIÓN:
- Ordenar a todos los duelistas por fitness (aptitud) en orden descendente
- Conservar solo los primeros popSize duelistas
ACTUALIZACIÓN (Revision):
PARA cada duelista i:
SI fitness [i] > mejor_global:
Actualizar mejor_global = fitness [i]
Guardar las coordenadas de la mejor solución
REPETIR el ciclo principal hasta que se cumpla el criterio de parada
Cabe destacar las características clave de este algoritmo: los campeones no participan en los duelos, sino que entrenan a nuevos duelistas, y el elemento de azar, a través del coeficiente de suerte, hace que los duelos sean impredecibles. Enfoque diferenciado: los perdedores aprenden, mientras que los ganadores buscan soluciones innovadoras. La población se mantiene con un tamaño constante mediante la eliminación de los peores individuos. Ahora podemos pasar a la implementación del código del algoritmo DA_duelist.
Escribiremos la clase «C_AO_DA_duelist», que hereda de la clase «C_AO», inicializa un array de parámetros (params) y les asigna nombres y valores por defecto.
- SetParams () — modifica los valores de las variables internas (popSize, luckCoefficient, etc.) en función de los valores almacenados en el array «params».
- Init() — método de inicialización del algoritmo. Acepta arrays para definir los rangos y los incrementos de los parámetros, así como el número de épocas.
- Moving () — método que contiene la lógica principal del movimiento de los «duelistas» (búsqueda de los parámetros óptimos).
- Revision () — se encarga de analizar los resultados e introducir correcciones en el proceso.
Variables:
- luckCoefficient: coeficiente de suerte;
- learningProbability: probabilidad de aprendizaje;
- innovationProbability: probabilidad de innovación;
- championsCount: número de campeones;
- winners [] — array de índices de los «duelistas» ganadores;
- losers [] — array de índices de los «duelistas» perdedores;
- champions [] — array de índices de «campeones».
- La función DetermineWinnerAndLoser () determina el ganador y el perdedor en un «duelo»;
- LearningProcess () implementa el proceso de aprendizaje del perdedor utilizando al ganador;
- InnovationProcess () implementa el proceso de innovación para el ganador;
- TrainNewDuelist () entrena a un nuevo «duelista» basándose en el campeón.
//———————————————————————————————————————————————————————————————————— class C_AO_DA_duelist : public C_AO { public: //---------------------------------------------------------- ~C_AO_DA_duelist () { } C_AO_DA_duelist () { ao_name = "DA"; ao_desc = "Duelist Algorithm"; ao_link = "https://www.mql5.com/ru/articles/19093"; popSize = 50; // número de duelistas luckCoefficient = 0.01; // coeficiente de suerte learningProbability = 0.2; // probabilidad de aprendizaje para los perdedores innovationProbability = 0.1; // probabilidad de innovación para los ganadores championsCount = 5; // número de campeones ArrayResize (params, 5); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "luckCoefficient"; params [1].val = luckCoefficient; params [2].name = "learningProbability"; params [2].val = learningProbability; params [3].name = "innovationProbability"; params [3].val = innovationProbability; params [4].name = "championsCount"; params [4].val = championsCount; } void SetParams () { popSize = (int)params [0].val; luckCoefficient = params [1].val; learningProbability = params [2].val; innovationProbability = params [3].val; championsCount = (int)params [4].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //------------------------------------------------------------------ double luckCoefficient; // coeficiente de suerte double learningProbability; // probabilidad de aprendizaje double innovationProbability; // probabilidad de innovación int championsCount; // número de campeones private: //--------------------------------------------------------- int winners []; // índices de los ganadores int losers []; // índices de los perdedores int champions []; // índices de los campeones void DetermineWinnerAndLoser (int duelistA, int duelistB); void LearningProcess (int loserIndex, int winnerIndex); void InnovationProcess (int winnerIndex); void TrainNewDuelist (int championIndex, int newDuelistIndex); }; //————————————————————————————————————————————————————————————————————
El método «Init» de la clase «C_AO_DA_duelist» está destinado a preparar el algoritmo para su ejecución. Su función principal es inicializar los parámetros iniciales y las estructuras de datos antes de iniciar el funcionamiento.
El método comienza con la llamada a la inicialización estándar, que procesa los rangos y los pasos de los parámetros. Si esta inicialización estándar falla, el método devuelve «false», lo que indica que no es posible continuar. A continuación, el método ajusta el número de campeones: si es inferior a uno, lo establece en uno; si es igual o superior al tamaño de la población, lo reduce a una cuarta parte del tamaño total de la población. Luego se limpian los arrays de ganadores y perdedores y se redimensiona el array de campeones, para que queden vacíos y listos para la siguiente fase del algoritmo. Al final, el método devuelve «true», lo que indica que la inicialización se ha realizado correctamente.
//———————————————————————————————————————————————————————————————————— //--- Inicialización bool C_AO_DA_duelist::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ if (championsCount < 1) championsCount = 1; if (championsCount >= popSize) championsCount = popSize / 4; ArrayResize (winners, 0); ArrayResize (losers, 0); ArrayResize (champions, championsCount); return true; } //————————————————————————————————————————————————————————————————————
El método «DetermineWinnerAndLoser» de la clase «C_AO_DA_duelist» determina al ganador y al perdedor en un duelo entre dos individuos (duelistas) de la población; veamos el procedimiento.
- Cálculo de la «suerte». Para cada duelista (A y B) se calcula el valor de «suerte» (A_Luck y B_Luck). La «suerte» depende del valor base «f» del duelista (su «aptitud» o «fitness»), del coeficiente «luckCoefficient» y de un elemento aleatorio obtenido mediante «u.RNDprobab()». Se recurre a cierto grado de aleatoriedad para simular el efecto de la suerte.
- Determinación del ganador. Se compara la suma de la aptitud «f» del duelista y su «suerte». Quien tenga una suma mayor o igual se considera el ganador.
- Almacenamiento de los resultados. Los índices del ganador y del perdedor se añaden a los arrays correspondientes «winners» y «losers». Estos arrays se utilizan para el análisis posterior y la evolución de la población.
//———————————————————————————————————————————————————————————————————— //--- Determinación del ganador y del perdedor en un duelo void C_AO_DA_duelist::DetermineWinnerAndLoser (int duelistA, int duelistB) { // Algoritmo del documento double A_Luck = a [duelistA].f * (luckCoefficient + u.RNDprobab () * luckCoefficient); double B_Luck = a [duelistB].f * (luckCoefficient + u.RNDprobab () * luckCoefficient); if ((a [duelistA].f + A_Luck) >= (a [duelistB].f + B_Luck)) { ArrayResize (winners, ArraySize (winners) + 1); ArrayResize (losers, ArraySize (losers) + 1); winners [ArraySize (winners) - 1] = duelistA; losers [ArraySize (losers) - 1] = duelistB; } else { ArrayResize (winners, ArraySize (winners) + 1); ArrayResize (losers, ArraySize (losers) + 1); winners [ArraySize (winners) - 1] = duelistB; losers [ArraySize (losers) - 1] = duelistA; } } //————————————————————————————————————————————————————————————————————
El método «LearningProcess» de la clase «C_AO_DA_duelist» implementa un proceso de aprendizaje en el que el individuo que ha perdido el duelo «aprende» del ganador. El objetivo de este proceso es mejorar las características del perdedor, transmitiéndole parte de la estrategia del ganador. Veamos qué ocurre dentro del método:
Iteración por coordenadas. El bucle «for» recorre todas las «coordenadas» del individuo. El número de coordenadas viene determinado por la variable «coords».
Copia probabilística. Dentro del bucle se realiza una comprobación aleatoria mediante la función «u.RNDprobab()». Si el número aleatorio es menor que «learningProbability» (probabilidad de aprendizaje), se lleva a cabo la siguiente acción.
Copia de una característica. El perdedor (loserIndex) «toma prestado» el valor de la coordenada «c» del ganador (winnerIndex). Esto significa que la característica del individuo perdedor se actualiza, adoptando el valor de la característica correspondiente del ganador. En esencia, el perdedor copia parte de la estrategia del ganador.
En definitiva, este método simula la transmisión de conocimientos o estrategias de un individuo más exitoso a otro menos exitoso, lo que constituye la base del proceso evolutivo.
//———————————————————————————————————————————————————————————————————— //--- Proceso de aprendizaje del perdedor a partir del ganador void C_AO_DA_duelist::LearningProcess (int loserIndex, int winnerIndex) { for (int c = 0; c < coords; c++) { if (u.RNDprobab () < learningProbability) { // El perdedor copia parte de la estrategia del ganador a [loserIndex].c [c] = a [winnerIndex].c [c]; } } } //————————————————————————————————————————————————————————————————————
El método «InnovationProcess» de la clase «C_AO_DA_duelist» implementa el proceso de «innovación» o mutación para el individuo que ha ganado el duelo. Este proceso introduce cambios aleatorios en la estrategia del ganador, explorando nuevas posibilidades.
Iteración por coordenadas. El bucle «for» recorre todas las coordenadas «c» del individuo, de forma similar a «LearningProcess».
Mutación probabilística. Si el número aleatorio obtenido mediante «u.RNDprobab()» es menor que «innovationProbability», se produce una mutación. En otras palabras, con una probabilidad determinada (establecida por «innovationProbability»), el individuo intenta cambiar su estrategia.
Generación y corrección de un nuevo valor. El generador de números aleatorios «u.RNDfromCI()» crea para el parámetro «c» un valor aleatorio que se encuentra en el intervalo comprendido entre «rangeMin[c]» y «rangeMax[c]». El valor aleatorio obtenido se corrige mediante «u.SeInDiSp()». Esta función se encarga de ajustar el valor a un conjunto discreto de valores con un paso de «rangeStep[c]» dentro del rango especificado («rangeMin[c]», «rangeMax[c]»).
Como resultado, este método permite al ganador «experimentar» con nuevas estrategias, lo que potencialmente puede conducir a una mejora de las características del individuo y de su aptitud en el entorno. Es una parte importante del proceso evolutivo.
//———————————————————————————————————————————————————————————————————— //--- El proceso de innovación del ganador void C_AO_DA_duelist::InnovationProcess (int winnerIndex) { for (int c = 0; c < coords; c++) { if (u.RNDprobab () < innovationProbability) { // El ganador prueba una nueva técnica (mutación) a [winnerIndex].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [winnerIndex].c [c] = u.SeInDiSp (a [winnerIndex].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } //————————————————————————————————————————————————————————————————————
El método «TrainNewDuelist» de la clase «C_AO_DA_duelist» se encarga de crear un nuevo duelista (individuo) y de configurarlo inicialmente mediante el «entrenamiento» a partir del campeón actual. Se trata de un proceso de herencia, pero con elementos aleatorios, lo que permite introducir diversidad genética en la población.
Iteración por coordenadas. El bucle «for» recorre todas las características (coordenadas) «c» del nuevo duelista (newDuelistIndex) y ajusta cada una de ellas.
Cálculo de la desviación (deviation). Se calcula «deviation», que determina el rango de posibles variaciones con respecto a la dispersión de los valores admisibles.
Herencia con mutación (distribución de Gauss). A la característica «c» del campeón (championIndex) se le añade una desviación aleatoria obtenida mediante la función «u.GaussDistribution()». Esta función genera un número aleatorio a partir de una distribución normal (distribución de Gauss), lo que permite introducir variaciones aleatorias en las características heredadas. Los argumentos «0», «rangeMin [c]», «rangeMax [c]» y «8» definen los parámetros de la distribución normal: la media (0), los valores mínimo y máximo, y el parámetro que controla la amplitud de la distribución (8).
Ajuste a un valor discreto (SeInDiSp). El valor obtenido de la característica del nuevo duelista, tras la mutación, se ajusta a un conjunto discreto de valores mediante la función «u.SeInDiSp()». Esta función garantiza que los parámetros del nuevo duelista se encuentren dentro del rango permitido y se ajusten al paso «rangeStep [c]».
Como resultado, el nuevo duelista adquiere características similares a las del campeón, pero con variaciones aleatorias, lo que aporta diversidad a la población y permite explorar nuevas estrategias.
//———————————————————————————————————————————————————————————————————— //--- El campeón entrena a un nuevo duelista void C_AO_DA_duelist::TrainNewDuelist (int championIndex, int newDuelistIndex) { for (int c = 0; c < coords; c++) { // El nuevo duelista hereda las habilidades del campeón con pequeñas variaciones double deviation = (rangeMax [c] - rangeMin [c]) * 0.1; a [newDuelistIndex].c [c] = a [championIndex].c [c] + u.GaussDistribution (0, rangeMin [c], rangeMax [c], 8); a [newDuelistIndex].c [c] = u.SeInDiSp (a [newDuelistIndex].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
El método «Moving» de la clase «C_AO_DA_duelist» constituye el paso principal del algoritmo de evolución de los duelistas. Incluye la inicialización (al iniciarse por primera vez), la reproducción, la selección, los duelos, el aprendizaje (a partir de los resultados de los duelos), la innovación y el descarte. Este es el núcleo del proceso evolutivo que tiene lugar dentro del método:
Inicialización de la población (primera ejecución). Si se trata de la primera ejecución (revision es igual a false), se inicializa la población: para cada individuo «i» se establecen aleatoriamente los valores de todas las características «j», de acuerdo con el rango admisible y el paso. Tras la inicialización, «revision» se establece en «true» para evitar una nueva inicialización en ejecuciones posteriores.
Preparación para la reproducción. El tamaño del array «a» (que representa la población) se incrementa, lo que permite incorporar a los nuevos duelistas creados a partir de los campeones. Se inicializan los nuevos duelistas (se establecen sus estados iniciales).
Selección de campeones. La población se ordena según el parámetro «f» (aptitud) para seleccionar a los mejores individuos. Se determinan los campeones: los «championsCount» mejores individuos. Cada campeón utiliza el método «TrainNewDuelist» para crear un nuevo duelista que hereda las características del campeón.
Preparación para los duelos. Los arrays «winners» y «losers» se vacían para prepararse para los nuevos resultados de los duelos.
Ejecución de duelos. Cada individuo, salvo los campeones, se enfrenta a un oponente aleatorio (que tampoco es campeón). El resultado de cada duelo (quién ha ganado y quién ha perdido) se determina mediante «DetermineWinnerAndLoser».
Aprendizaje de los duelistas (Learning). Aquí tiene lugar el aprendizaje de los perdedores a partir de los ganadores. «LearningProcess» permite a los perdedores modificar sus parámetros, adoptando las mejores características de los ganadores.
Innovaciones de los ganadores (Innovation). Los mejores individuos utilizan el «InnovationProcess» para intentar cambiar su estrategia y explorar nuevas oportunidades.
Reordenación. Se vuelve a ordenar toda la población según el parámetro «f» para actualizar el orden de los individuos tras los cambios introducidos durante los duelos, el aprendizaje y las innovaciones.
Descarte de los peores. El tamaño del array «a» se restablece a «popSize», descartando los peores individuos para mantener constante el tamaño de la población.
Este ciclo representa un paso de la evolución en el que se producen la creación de descendencia, la selección de los mejores individuos, el aprendizaje, la actualización de estrategias y el mantenimiento del tamaño de la población. El método «Moving» lleva a cabo las operaciones evolutivas básicas: reproducción, mutación, selección y aprendizaje.
//———————————————————————————————————————————————————————————————————— //--- Paso principal del algoritmo void C_AO_DA_duelist::Moving () { // Inicialización de la población inicial if (!revision) { for (int i = 0; i < popSize; i++) { for (int j = 0; j < coords; j++) { a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]); a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } revision = true; return; } //------------------------------------------------------------------ // Ampliación temporal del array para los nuevos duelistas int totalDuelists = popSize + championsCount; ArrayResize (a, totalDuelists); // Inicialización de nuevos duelistas for (int i = popSize; i < totalDuelists; i++) { a [i].Init (coords); } // Ordenación para determinar los campeones (utilizamos la ordenación por burbuja) for (int i = 0; i < popSize - 1; i++) { for (int j = 0; j < popSize - i - 1; j++) { if (a [j].f < a [j + 1].f) { S_AO_Agent temp = a [j]; a [j] = a [j + 1]; a [j + 1] = temp; } } } // Determinamos los campeones for (int i = 0; i < championsCount; i++) { champions [i] = i; // El campeón entrena a un nuevo duelista TrainNewDuelist (i, popSize + i); } // Limpieza de los arrays de ganadores y perdedores ArrayResize (winners, 0); ArrayResize (losers, 0); // Ejecución de duelos (excluyendo a los campeones) for (int i = championsCount; i < totalDuelists; i++) { // Cada duelista se enfrenta a un adversario aleatorio int opponent = u.RNDintInRange (championsCount, totalDuelists - 1); if (opponent != i) { DetermineWinnerAndLoser (i, opponent); } } // Proceso de mejora de los duelistas int minCount = MathMin (ArraySize (winners), ArraySize (losers)); for (int i = 0; i < minCount; i++) { // Los perdedores aprenden de los ganadores LearningProcess (losers [i], winners [i]); } for (int i = 0; i < ArraySize (winners); i++) { // Los ganadores innovan InnovationProcess (winners [i]); } // Ordenación de todos los duelistas for (int i = 0; i < totalDuelists - 1; i++) { for (int j = 0; j < totalDuelists - i - 1; j++) { if (a [j].f < a [j + 1].f) { S_AO_Agent temp = a [j]; a [j] = a [j + 1]; a [j + 1] = temp; } } } // Eliminamos a los peores duelistas ArrayResize (a, popSize); } //————————————————————————————————————————————————————————————————————
El método «Revision» de la clase «C_AO_DA_duelist» sirve para actualizar la información sobre la mejor solución encontrada en la población actual. Su objetivo es seguir el óptimo global durante el proceso evolutivo.
Iteración sobre la población. El bucle «for» recorre cada individuo de la población (i de 0 a popSize).
Comparación de la aptitud. Dentro del bucle se comprueba si la aptitud «f» del individuo actual es mejor que la mejor aptitud actual «fB».
Actualización de la mejor solución. Si (a[i].f) es mejor que (fB), entonces (fB) se actualiza; es decir, (fB) almacena la mejor aptitud encontrada hasta ese momento. Además, se copian las características «c» del individuo actual en el array «cB». Esto significa que «cB» almacena los valores de las características del mejor individuo encontrado hasta el momento.
En definitiva, el método «Revision» analiza la población actual, busca el individuo con mayor aptitud y, si dicho individuo supera el «récord» actual, actualiza la información sobre el mejor individuo encontrado (su aptitud y sus propias características). Este método es necesario para realizar un seguimiento del progreso del algoritmo y encontrar el óptimo global.
//———————————————————————————————————————————————————————————————————— //--- Actualización de la mejor solución global void C_AO_DA_duelist::Revision () { // Actualizamos la mejor solución global for (int i = 0; i < popSize; i++) { if (a [i].f > fB) { fB = a [i].f; ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY); } } } //————————————————————————————————————————————————————————————————————
Resultados de las pruebas
En general, tras los estudios realizados, el algoritmo del duelista se comporta bastante bien.DA|Duelist Algorithm|100,0|0,01|0,9|0,1|2,0|
=============================
5 Hilly's; Func runs: 10000; result: 0.9278151663330798
25 Hilly's; Func runs: 10000; result: 0.5377820196319314
500 Hilly's; Func runs: 10000; result: 0.27792394907287765
=============================
5 Forest's; Func runs: 10000; result: 0.8695700230324329
25 Forest's; Func runs: 10000; result: 0.47535947112902815
500 Forest's; Func runs: 10000; result: 0.18193288697223736
=============================
5 Megacity's; Func runs: 10000; result: 0.6215384615384616
25 Megacity's; Func runs: 10000; result: 0.3356923076923076
500 Megacity's; Func runs: 10000; result: 0.11715384615384725
=============================
All score: 4.34477 (48.28%)
En la visualización del funcionamiento del algoritmo se aprecia una ligera dispersión de los valores, tanto en dimensiones pequeñas como grandes, lo que indica que el algoritmo posee buenas capacidades de búsqueda.

DA_duelist en la función de prueba Hilly

DA_duelist en la función de prueba Forest

DA_duelist en la función de prueba Megacity
Según los resultados de las pruebas, el algoritmo del duelista ocupa el puesto 42 en la clasificación general de algoritmos de optimización.
| N.º | AO | Description | Hilly | Hilly Final | Forest | Forest Final | Megacity (discrete) | Megacity Final | Final Result | % de MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||||
| 1 | ANS | across neighbourhood search | 0,94948 | 0,84776 | 0,43857 | 2,23581 | 1,00000 | 0,92334 | 0,39988 | 2,32323 | 0,70923 | 0,63477 | 0,23091 | 1,57491 | 6,134 | 68,15 |
| 2 | CLA | code lock algorithm (joo) | 0,95345 | 0,87107 | 0,37590 | 2,20042 | 0,98942 | 0,91709 | 0,31642 | 2,22294 | 0,79692 | 0,69385 | 0,19303 | 1,68380 | 6,107 | 67,86 |
| 3 | AMOm | animal migration ptimization M | 0,90358 | 0,84317 | 0,46284 | 2,20959 | 0,99001 | 0,92436 | 0,46598 | 2,38034 | 0,56769 | 0,59132 | 0,23773 | 1,39675 | 5,987 | 66,52 |
| 4 | (P+O)ES | (P+O) evolution strategies | 0,92256 | 0,88101 | 0,40021 | 2,20379 | 0,97750 | 0,87490 | 0,31945 | 2,17185 | 0,67385 | 0,62985 | 0,18634 | 1,49003 | 5,866 | 65,17 |
| 5 | CTA | comet tail algorithm (joo) | 0,95346 | 0,86319 | 0,27770 | 2,09435 | 0,99794 | 0,85740 | 0,33949 | 2,19484 | 0,88769 | 0,56431 | 0,10512 | 1,55712 | 5,846 | 64,96 |
| 6 | TETA | time evolution travel algorithm (joo) | 0,91362 | 0,82349 | 0,31990 | 2,05701 | 0,97096 | 0,89532 | 0,29324 | 2,15952 | 0,73462 | 0,68569 | 0,16021 | 1,58052 | 5,797 | 64,41 |
| 7 | SDSm | stochastic diffusion search M | 0,93066 | 0,85445 | 0,39476 | 2,17988 | 0,99983 | 0,89244 | 0,19619 | 2,08846 | 0,72333 | 0,61100 | 0,10670 | 1,44103 | 5,709 | 63,44 |
| 8 | BOAm | billiards optimization algorithm M | 0,95757 | 0,82599 | 0,25235 | 2,03590 | 1,00000 | 0,90036 | 0,30502 | 2,20538 | 0,73538 | 0,52523 | 0,09563 | 1,35625 | 5 598 | 62,19 |
| 9 | AAm | archery algorithm M | 0,91744 | 0,70876 | 0,42160 | 2,04780 | 0,92527 | 0,75802 | 0,35328 | 2,03657 | 0,67385 | 0,55200 | 0,23738 | 1,46323 | 5,548 | 61,64 |
| 10 | ESG | evolution of social groups (joo) | 0,99906 | 0,79654 | 0,35056 | 2,14616 | 1,00000 | 0,82863 | 0,13102 | 1,95965 | 0,82333 | 0,55300 | 0,04725 | 1,42358 | 5 529 | 61,44 |
| 11 | SIA | simulated isotropic annealing (joo) | 0,95784 | 0,84264 | 0,41465 | 2,21513 | 0,98239 | 0,79586 | 0,20507 | 1,98332 | 0,68667 | 0,49300 | 0,09053 | 1,27020 | 5.469 | 60,76 |
| 12 | EOm | extremal_optimization_M | 0,76166 | 0,77242 | 0,31747 | 1,85155 | 0,99999 | 0,76751 | 0,23527 | 2,00277 | 0,74769 | 0,53969 | 0,14249 | 1,42987 | 5.284 | 58,71 |
| 13 | BBO | biogeography based optimization | 0,94912 | 0,69456 | 0,35031 | 1,99399 | 0,93820 | 0,67365 | 0,25682 | 1,86867 | 0,74615 | 0,48277 | 0,17369 | 1,40261 | 5,265 | 58,50 |
| 14 | ACS | artificial cooperative search | 0,75547 | 0,74744 | 0,30407 | 1,80698 | 1,00000 | 0,88861 | 0,22413 | 2,11274 | 0,69077 | 0,48185 | 0,13322 | 1,30583 | 5,226 | 58,06 |
| 15 | DA | dialectical algorithm | 0,86183 | 0,70033 | 0,33724 | 1,89940 | 0,98163 | 0,72772 | 0,28718 | 1,99653 | 0,70308 | 0,45292 | 0,16367 | 1,31967 | 5,216 | 57,95 |
| 16 | BHAm | black hole algorithm M | 0,75236 | 0,76675 | 0,34583 | 1,86493 | 0,93593 | 0,80152 | 0,27177 | 2,00923 | 0,65077 | 0,51646 | 0,15472 | 1,32195 | 5,196 | 57,73 |
| 17 | ASO | anarchy society optimization | 0,84872 | 0,74646 | 0,31465 | 1,90983 | 0,96148 | 0,79150 | 0,23803 | 1,99101 | 0,57077 | 0,54062 | 0,16614 | 1,27752 | 5,178 | 57,54 |
| 18 | RFO | royal flush optimization (joo) | 0,83361 | 0,73742 | 0,34629 | 1,91733 | 0,89424 | 0,73824 | 0,24098 | 1,87346 | 0,63154 | 0,50292 | 0,16421 | 1,29867 | 5,089 | 56,55 |
| 19 | AOSm | atomic orbital search M | 0,80232 | 0,70449 | 0,31021 | 1,81702 | 0,85660 | 0,69451 | 0,21996 | 1,77107 | 0,74615 | 0,52862 | 0,14358 | 1,41835 | 5,006 | 55,63 |
| 20 | TSEA | turtle shell evolution algorithm (joo) | 0,96798 | 0,64480 | 0,29672 | 1,90949 | 0,99449 | 0,61981 | 0,22708 | 1,84139 | 0,69077 | 0,42646 | 0,13598 | 1,25322 | 5,004 | 55,60 |
| 21 | BSA | backtracking_search_algorithm | 0,97309 | 0,54534 | 0,29098 | 1,80941 | 0,99999 | 0,58543 | 0,21747 | 1,80289 | 0,84769 | 0,36953 | 0,12978 | 1,34700 | 4,959 | 55,10 |
| 22 | DE | differential evolution | 0,95044 | 0,61674 | 0,30308 | 1,87026 | 0,95317 | 0,78896 | 0,16652 | 1,90865 | 0,78667 | 0,36033 | 0,02953 | 1,17653 | 4,955 | 55,06 |
| 23 | SRA | successful restaurateur algorithm (joo) | 0,96883 | 0,63455 | 0,29217 | 1,89555 | 0,94637 | 0,55506 | 0,19124 | 1,69267 | 0,74923 | 0,44031 | 0,12526 | 1,31480 | 4,903 | 54,48 |
| 24 | CRO | chemical reaction optimisation | 0,94629 | 0,66112 | 0,29853 | 1,90593 | 0,87906 | 0,58422 | 0,21146 | 1,67473 | 0,75846 | 0,42646 | 0,12686 | 1,31178 | 4,892 | 54,36 |
| 25 | BIO | blood inheritance optimization (joo) | 0,81568 | 0,65336 | 0,30877 | 1,77781 | 0,89937 | 0,65319 | 0,21760 | 1,77016 | 0,67846 | 0,47631 | 0,13902 | 1,29378 | 4.842 | 53,80 |
| 26 | BSA | bird swarm algorithm | 0,89306 | 0,64900 | 0,26250 | 1,80455 | 0,92420 | 0,71121 | 0,24939 | 1,88479 | 0,69385 | 0,32615 | 0,10012 | 1,12012 | 4.809 | 53,44 |
| 27 | DEA | dolphin_echolocation_algorithm | 0,75995 | 0,67572 | 0,34171 | 1,77738 | 0,89582 | 0,64223 | 0,23941 | 1,77746 | 0,61538 | 0,44031 | 0,15115 | 1,20684 | 4.762 | 52,91 |
| 28 | HS | harmony search | 0,86509 | 0,68782 | 0,32527 | 1,87818 | 0,99999 | 0,68002 | 0,09590 | 1,77592 | 0,62000 | 0,42267 | 0,05458 | 1,09725 | 4.751 | 52,79 |
| 29 | SSG | saplings sowing and growing | 0,77839 | 0,64925 | 0,39543 | 1,82308 | 0,85973 | 0,62467 | 0,17429 | 1,65869 | 0,64667 | 0,44133 | 0,10598 | 1,19398 | 4 676 | 51,95 |
| 30 | BCOm | bacterial chemotaxis optimization M | 0,75953 | 0,62268 | 0,31483 | 1,69704 | 0,89378 | 0,61339 | 0,22542 | 1,73259 | 0,65385 | 0,42092 | 0,14435 | 1,21912 | 4 649 | 51,65 |
| 31 | ABO | african buffalo optimization | 0,83337 | 0,62247 | 0,29964 | 1,75548 | 0,92170 | 0,58618 | 0,19723 | 1,70511 | 0,61000 | 0,43154 | 0,13225 | 1,17378 | 4.634 | 51,49 |
| 32 | (PO)ES | (PO) evolution strategies | 0,79025 | 0,62647 | 0,42935 | 1,84606 | 0,87616 | 0,60943 | 0,19591 | 1,68151 | 0,59000 | 0,37933 | 0,11322 | 1,08255 | 4.610 | 51,22 |
| 33 | FBA | fractal-based Algorithm | 0,79000 | 0,65134 | 0,28965 | 1,73099 | 0,87158 | 0,56823 | 0,18877 | 1,62858 | 0,61077 | 0,46062 | 0,12398 | 1,19537 | 4.555 | 50,61 |
| 34 | TSm | tabu search M | 0,87795 | 0,61431 | 0,29104 | 1,78330 | 0,92885 | 0,51844 | 0,19054 | 1,63783 | 0,61077 | 0,38215 | 0,12157 | 1,11449 | 4,536 | 50,40 |
| 35 | BSO | brain storm optimization | 0,93736 | 0,57616 | 0,29688 | 1,81041 | 0,93131 | 0,55866 | 0,23537 | 1,72534 | 0,55231 | 0,29077 | 0,11914 | 0,96222 | 4,498 | 49,98 |
| 36 | WOAm | wale optimization algorithm M | 0,84521 | 0,56298 | 0,26263 | 1,67081 | 0,93100 | 0,52278 | 0,16365 | 1,61743 | 0,66308 | 0,41138 | 0,11357 | 1,18803 | 4,476 | 49,74 |
| 37 | AEFA | artificial electric field algorithm | 0,87700 | 0,61753 | 0,25235 | 1,74688 | 0,92729 | 0,72698 | 0,18064 | 1,83490 | 0,66615 | 0,11631 | 0,09508 | 0,87754 | 4,459 | 49,55 |
| 38 | AEO | artificial ecosystem-based optimization algorithm | 0,91380 | 0,46713 | 0,26470 | 1,64563 | 0,90223 | 0,43705 | 0,21400 | 1,55327 | 0,66154 | 0,30800 | 0,28563 | 1,25517 | 4,454 | 49,49 |
| 39 | CAm | camel algorithm M | 0,78684 | 0,56042 | 0,35133 | 1,69859 | 0,82772 | 0,56041 | 0,24336 | 1,63149 | 0,64846 | 0,33092 | 0,13418 | 1,11356 | 4,444 | 49,37 |
| 40 | ACOm | ant colony optimization M | 0,88190 | 0,66127 | 0,30377 | 1,84693 | 0,85873 | 0,58680 | 0,15051 | 1,59604 | 0,59667 | 0,37333 | 0,02472 | 0,99472 | 4,438 | 49,31 |
| 41 | CMAES | covariance_matrix_adaptation_evolution_strategy | 0,76258 | 0,72089 | 0,00000 | 1,48347 | 0,82056 | 0,79616 | 0,00000 | 1,61672 | 0,75846 | 0,49077 | 0,00000 | 1,24923 | 4,349 | 48,33 |
| 42 | DA_duelist | duelist_algorithm | 0,92782 | 0,53778 | 0,27792 | 1,74352 | 0,86957 | 0,47536 | 0,18193 | 1,52686 | 0,62153 | 0,33569 | 0,11715 | 1,07437 | 4,345 | 48,28 |
| 43 | 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 |
| 44 | 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 |
| 45 | 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 |
| 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 del duelista demuestra resultados notables en el ámbito de la optimización metaheurística y se sitúa entre los 45 mejores algoritmos poblacionales. Aunque no aspira al título de campeón absoluto entre los métodos de optimización, su principal ventaja es la velocidad.
Estrategia de aprendizaje adaptativo: en el contexto de la optimización de sistemas de trading, esto significa que las configuraciones de parámetros fallidas adoptan rápidamente los patrones exitosos, lo cual resulta especialmente valioso al trabajar con series financieras no estacionarias.
Equilibrio entre la explotación y la exploración: los ganadores no se duermen en los laureles, sino que siguen buscando mejoras a través de la innovación. Esto es de vital importancia para las estrategias de trading, que deben adaptarse a las condiciones cambiantes del mercado.
El elemento de aleatoriedad controlada —el coeficiente de suerte— modela de forma natural la incertidumbre del mercado, en el que incluso una buena estrategia puede arrojar temporalmente malos resultados debido a la volatilidad a corto plazo.
La estructura jerárquica con campeones garantiza la conservación de las mejores soluciones y su difusión, lo que, en el ámbito del trading, equivale a conservar estrategias contrastadas a lo largo del tiempo y, al mismo tiempo, buscar nuevas oportunidades.
El «Duelist Algorithm» es una solución de calidad que, a través de la metáfora de las artes marciales, aporta al mundo de la optimización una idea sencilla pero eficaz: aprender de los más fuertes, experimentar desde una posición de fuerza y transmitir los conocimientos a la siguiente generación.

Figura 2. Gradación cromática de los algoritmos según las pruebas correspondientes

Figura 3. Histograma de los resultados de las pruebas de los algoritmos (en una escala de 0 a 100; cuanto mayor sea el valor, mejor, donde 100 es el resultado teórico máximo posible; en el archivo comprimido se incluye el script para calcular la tabla de clasificación)
Ventajas e inconvenientes del algoritmo DA_duelist:
Ventajas:
- Es rápido.
- Tiene poca dispersión de los resultados en las funciones de prueba.
Desventajas:
- Tiene baja precisión de convergencia.
Adjuntamos al artículo un archivo comprimido con las versiones actualizadas de los códigos de los algoritmos. El autor del artículo no se hace responsable de la exactitud absoluta en la descripción de los algoritmos canónicos, ya que en muchos de ellos se han introducido modificaciones 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 utilizados en el artículo
| # | Nombre | Tipo | Descripción |
|---|---|---|---|
| 1 | #C_AO.mqh | Archivo de inclusión | Clase padre de los algoritmos de optimización poblacional |
| 2 | #C_AO_enum.mqh | Archivo de inclusión | Enumeración de algoritmos de optimización poblacional |
| 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 de la tabla comparativa |
| 7 | Testing AOs.mq5 | Script | Banco de pruebas unificado para todos los algoritmos de optimización poblacional |
| 8 | Simple use of population optimization algorithms.mq5 | Script | Ejemplo sencillo de uso de algoritmos de optimización poblacional sin visualización |
| 9 | Test_AO_DA_duelist.mq5 | Script | Banco de pruebas para DA_duelist |
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/19093
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.
Redes neuronales en el trading: modelo de consultas temporales (TQNet)
Implementación de módulos prácticos de otros lenguajes en MQL5 (Parte 02): Desarrollo de la biblioteca REQUESTS inspirada en requests de Python
Algoritmo de optimización basado en los sueños — Dream Optimization Algorithm (DOA)
Redes neuronales en el trading: descomposición en lugar de escalado (Final)
- 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
Se ha publicado el artículo «El algoritmo del duelista — Duelist Algorithm»:
Autor: Andrey Dik
Duelistas que se enfrentan en el ring. El mercado no es un ring, sino un terreno. A veces en el sol, otras veces metidos hasta la cintura en un pantano. Sobre el hielo ganará el patinador artístico, pero en el pantano... ¿Y al final, qué tipo de guerrero saldrá de ahí?
Podéis buscarlo vosotros mismos en Internet, no se lo ha inventado el autor del hilo.