English Русский Português
preview
Algoritmo del duelista — Duelist Algorithm

Algoritmo del duelista — Duelist Algorithm

MetaTrader 5Trading |
55 2
Andrey Dik
Andrey Dik

Índice

  1. Introducción
  2. Implementación del algoritmo
  3. Resultados de las pruebas


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.

illustration

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) &gt;= (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».
Métodos privados:
  • 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.

  1. 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.
  2. 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.
  3. 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.

    Hilly

    DA_duelist en la función de prueba Hilly

    Forest

    DA_duelist en la función de prueba Forest

    Megacity

    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.

    tab

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

    gráfico

    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:

    1. Es rápido.
    2. Tiene poca dispersión de los resultados en las funciones de prueba.

    Desventajas:

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

    Archivos adjuntos |
    DAvduelistk.ZIP (330.07 KB)
    Александр Бельмецов
    Александр Бельмецов | 15 ago 2025 en 04:00
    MetaQuotes:

    Se ha publicado el artículo «El algoritmo del duelista — Duelist Algorithm»:

    Autor: Andrey Dik

    Los duelistas que luchan en el ring. El mercado no es un ring, sino un terreno. A veces se lucha en el aire, otras veces hasta la cintura en un pantano. Sobre el hielo ganará el patinador, pero en el pantano... ¿Y al final, qué tipo de guerrero resultará?
    lynxntech
    lynxntech | 15 ago 2025 en 04:21
    Александр Бельмецов #:
    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.

    Redes neuronales en el trading: modelo de consultas temporales (TQNet) Redes neuronales en el trading: modelo de consultas temporales (TQNet)
    El framework TQNet abre nuevas posibilidades en el modelado y la previsión de series temporales financieras, combinando modularidad, flexibilidad y un alto rendimiento. En el artículo se expone la posibilidad de implementar mecanismos complejos para trabajar con correlaciones globales, incluyendo métodos avanzados de inicialización de parámetros.
    Implementación de módulos prácticos de otros lenguajes en MQL5 (Parte 02): Desarrollo de la biblioteca REQUESTS inspirada en requests de Python Implementación de módulos prácticos de otros lenguajes en MQL5 (Parte 02): Desarrollo de la biblioteca REQUESTS inspirada en requests de Python
    En este artículo, implementamos un un módulo similar a requests, como el que ofrece Python para facilitar el envío y la recepción de solicitudes web en MetaTrader 5 utilizando MQL5.
    Algoritmo de optimización basado en los sueños — Dream Optimization Algorithm (DOA) Algoritmo de optimización basado en los sueños — Dream Optimization Algorithm (DOA)
    Un algoritmo de optimización poblacional inspirado en un fenómeno controvertido y poco estudiado: el fenómeno de los sueños humanos. Grupos de agentes con distintas «memorias», modulación cosenoidal del movimiento y una distribución de fases inusual de 99/1: descubra cómo estas características influyen en la eficacia de la optimización de sus estrategias de trading.
    Redes neuronales en el trading: descomposición en lugar de escalado (Final) Redes neuronales en el trading: descomposición en lugar de escalado (Final)
    Le proponemos familiarizarse con el algoritmo para descomponer una serie temporal en capas semánticas y construir a partir de ellas un modelo eficiente. Mostramos paso a paso la arquitectura, la implementación práctica en MQL5/OpenCL y las pruebas reales sobre datos históricos de mercado.