English Русский Deutsch 日本語 Português
preview
Validación cruzada simétrica combinatoria en MQL5

Validación cruzada simétrica combinatoria en MQL5

MetaTrader 5Sistemas comerciales | 9 abril 2024, 17:29
262 0
Francis Dube
Francis Dube

Introducción

A veces, cuando creamos una estrategia automatizada, comenzamos describiendo reglas basadas en indicadores arbitrarios que deben mejorarse de alguna manera. Este proceso implica la ejecución de varias pruebas con distintos valores de los parámetros de los indicadores seleccionados. De esta forma, podemos encontrar los valores de los indicadores que maximizan el beneficio o cualquier otro indicador que nos interese. El problema aquí es que introducimos un cierto desplazamiento optimista debido al ruido reinante en las series temporales financieras. Este fenómeno se denomina ajuste o sobreajuste (overfitting).

Aunque el ajuste no puede evitarse, el grado de ajuste puede cambiar de una estrategia a otra. Por lo tanto, nos resultaría útil poder determinar su alcance. La Validación cruzada simétrica combinatoria (Combinatorially Symmetrical Cross Validation, CSCV) es un método presentado en el artículo de investigación "The Probability of Backtest Overfitting", por David H. Bailey y otros. La validación puede usarse para evaluar el grado de ajuste al optimizar los parámetros de la estrategia.

En este artículo, demostraremos la implementación de CSCV en MQL5 y mostraremos con un ejemplo cómo se puede aplicar a un asesor.


El método CSCV

En esta sección, describiremos el método CSCV paso a paso, empezando por los aspectos preliminares relativos a los datos que deben recogerse según los criterios de rendimiento seleccionados.

El método CSCV puede aplicarse a diversos ámbitos más allá del desarrollo y el análisis de estrategias, pero en este artículo nos ceñiremos al contexto de la optimización de estrategias. Es decir, tenemos una estrategia definida por un conjunto de parámetros que hay que afinar ejecutando múltiples pruebas con diferentes ajustes de parámetros.

Antes de iniciar cualquier cálculo, tendremos que decidir qué criterios de rendimiento utilizaremos para evaluar la estrategia. El método CSCV es flexible porque puede usarse cualquier medida de rendimiento, desde el simple beneficio hasta medidas basadas en coeficientes.

Los criterios de rendimiento seleccionados también determinarán los datos de referencia que se usarán en los cálculos. Se trata de datos de origen detallados que se recopilarán durante todas las pruebas. Por ejemplo, si decidimos usar el ratio de Sharpe como medida de rendimiento, necesitaríamos obtener el rendimiento por barras cada vez que realicemos la prueba. Si utilizáramos el beneficio simple, necesitaríamos el beneficio o la pérdida en barras. Deberemos comprobar que la cantidad de datos recopilados para cada ejecución sea constante. De este modo, nos aseguramos de disponer de una medida para cada punto de datos relevante en todas las pruebas.

  1. El primer paso comenzará con la recogida de datos durante la optimización, cuando se prueban distintas opciones de parámetros. 
  2. Una vez finalizada la optimización, combinaremos todos los datos recogidos en las pruebas en una matriz. Cada fila de esta matriz contendrá todos los valores de rendimiento de la barra que se usarán para calcular algunas métricas de rendimiento comercial para la ejecución de prueba correspondiente.
  3. La matriz tendrá tantas filas como combinaciones de parámetros se hayan probado, mientras que el número de columnas será igual al número de columnas que comprenda todo el periodo de pruebas. A continuación, estas columnas se dividirán en cualquier número par de conjuntos, digamos N conjuntos.
  4. Estos conjuntos serán submatrices que se utilizarán para formar combinaciones de grupos de tamaño N/2. Así, se crearán un total de N combinaciones tomadas N/2 cada vez, es decir, N C n/2 . Partiendo de cada una de estas combinaciones, crearemos un In-Sample-Set (ISS) combinando las N/2 submatrices, así como el Out-Of-Sample-Set (OOSS) correspondiente a partir de las submatrices restantes no incluidas en el ISS.
  5. Para cada fila de las matrices ISS y OOSS, calcularemos la métrica de rendimiento correspondiente y nos centraremos en la fila de la matriz ISS con el mejor rendimiento. Esta será la configuración óptima de los parámetros. La fila correspondiente de la matriz OOSS se utilizará para calcular el rango relativo contando el número de pruebas de parámetros fuera de la muestra con un rendimiento inferior al obtenido utilizando la configuración óptima de parámetros, y representando este número como una fracción de todos los conjuntos de parámetros probados.
  6. Recorriendo todas las combinaciones, acumularemos el número de valores de rango relativo inferiores o iguales a 0,5. Este será el número de configuraciones de parámetros fuera de la muestra cuyo rendimiento resulta inferior al observado utilizando el conjunto óptimo de parámetros. Una vez procesadas todas las combinaciones, este número se presentará como una fracción de todas las combinaciones + 1, lo cual representará la Probabilidad de Sobreajuste del Backtest (PBO).

 A continuación se visualizarán los pasos que acabamos de describir cuando N = 4.

Visualización de las matrices de datos

Submatrices

Conjuntos dentro y fuera de muestra

Combinaciones

En la siguiente sección veremos cómo implementar en código los pasos descritos. Veremos primero el método CSCV principal, y dejaremos el código relacionado con la recogida de datos para el ejemplo que se mostrará al final del artículo.


Implementación de CSCV en MQL5

La clase Ccsvc contenida en CSCV.mqh encapsulará el algoritmo CSCV. CSCV.mqh comenzará incluyendo subfunciones de la biblioteca estándar MQL5 Mathematics.

//+------------------------------------------------------------------+
//|                                                         CSCV.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#include 

El puntero de función criterio definirá un tipo de función utilizado para calcular la métrica de rendimiento dado un array como entrada.

#include 
typedef double (*Criterion)(const double &data[]); // function pointer for performance criterion

Ccscv solo tiene un método con el que los usuarios deben familiarizarse. Podrá llamarse después de inicializar un ejemplar de la clase. Este método CalculateProbabilty() retornará el valor de PBO si tiene éxito. Si se detecta un error, el método retornará -1. A continuación describiremos sus parámetros de entrada:

//+------------------------------------------------------------------+
//| combinatorially symmetric cross validation class                 |
//+------------------------------------------------------------------+
class Cscv
  {
   ulong             m_perfmeasures;         //granular performance measures
   ulong             m_trials;               //number of parameter trials
   ulong             m_combinations;         //number of combinations

   ulong  m_indices[],           //array tracks combinations
          m_lengths[],           //points to number measures for each combination
          m_flags  [];           //tracks processing of combinations
   double m_data   [],           //intermediary holding performance measures for current trial
          is_perf  [],           //in sample performance data
          oos_perf [];           //out of sample performance data


public:
                     Cscv(void);                   //constructor
                    ~Cscv(void);                  //destructor

   double            CalculateProbability(const ulong blocks, const matrix &in_data,const Criterion criterion, const bool maximize_criterion);
  };
  • El primer parámetro de entrada será blocks. Este se corresponderá con el número de conjuntos (N conjuntos) en que se dividirán las columnas de la matriz.
  • in_data es una matriz con un número de filas igual al número total de opciones de parámetros probadas durante la optimización y un número de columnas igual al número de columnas que comprenden toda la historia seleccionada para la optimización.
  • criterion — puntero a la función que se utilizará para calcular la métrica de rendimiento seleccionada. La función deberá devolver un valor de tipo double y aceptar una matriz de tipo double como datos de entrada.
  •  maximize_criterion está relacionado con el criterio en el sentido de que permite especificar si la mejor de las métricas de rendimiento seleccionadas viene determinada por el valor máximo o mínimo. Por ejemplo, si se utilizamos la reducción como criterio de rendimiento, será mejor utilizar el valor más pequeño, por lo que maximise_criterion deberá ser false.
double Cscv::CalculateProbability(const ulong blocks, const matrix &in_data,const Criterion criterion, const bool maximize_criterion)
  {
//---get characteristics of matrix
   m_perfmeasures = in_data.Cols();
   m_trials = in_data.Rows();
   m_combinations=blocks/2*2;
//---check inputs
   if(m_combinations<4)
      m_combinations = 4;
//---memory allocation
   if(ArrayResize(m_indices,int(m_combinations))< int(m_combinations)||
      ArrayResize(m_lengths,int(m_combinations))< int(m_combinations)||
      ArrayResize(m_flags,int(m_combinations))<int(m_combinations)   ||
      ArrayResize(m_data,int(m_perfmeasures))<int(m_perfmeasures)    ||
      ArrayResize(is_perf,int(m_trials))<int(m_trials)               ||
      ArrayResize(oos_perf,int(m_trials))<int(m_trials))
     {
      Print("Memory allocation error ", GetLastError());
      return -1.0;
     }
//---

En ComputeProbability, empezaremos obteniendo el número de columnas y filas de la matriz in_data, y comprobando los bloques para asegurarnos de que es un número par. Obtener el tamaño de la matriz de entrada será necesario para determinar el tamaño de los búferes internos del ejemplar.

   int is_best_index ;               //row index of oos_best parameter combination
   double oos_best, rel_rank ;   //oos_best performance and relative rank values
//---
   ulong istart = 0 ;
   for(ulong i=0 ; i // Block starts here 
      m_lengths[i] = (m_perfmeasures - istart) / (m_combinations-i) ; // It contains this many cases
      istart += m_lengths[i] ;       // Next block
     }
//---
   ulong num_less =0;                    // Will count the number of time OOS of oos_best <= median OOS, for prob
   for(ulong i=0; i if (i 2 )        // Identify the IS set
         m_flags[i]=1;
      else
         m_flags[i]=0;               // corresponding OOS set
     }
//---

Una vez asignada con éxito la memoria para los búferes internos, empezaremos a preparar la partición de las columnas según m_combinations. El array m_indices se rellenará con los índices de columna iniciales para una partición concreta, mientras que m_lengths contendrá el número correspondiente de columnas contenidas en cada una. num_less permitirá contar cuántas veces el rendimiento de la mejor prueba de la muestra resulta inferior al rendimiento de las demás pruebas fuera de la muestra. m_flags será un array entero cuyos valores podrán contener 1 o 0. Esto ayudará a identificar los subconjuntos etiquetados como dentro y fuera de la muestra, a la vez que se enumerarán todas las combinaciones posibles.

ulong ncombo;
   for(ncombo=0; ; ncombo++)
     {
      //--- in sample performance calculated in this loop
      for(ulong isys=0; isys int  n=0;
         for(ulong ic=0; ic if (m_flags[ic])
              {
               for(ulong i=m_indices[ic]; i //--- out of sample performance calculated here 
      for(ulong isys=0; isys int  n=0;
         for(ulong ic=0; ic if (!m_flags[ic])
              {
               for(ulong i=m_indices[ic]; i  

En este punto, se iniciará el ciclo principal, que probará todas las combinaciones de conjuntos dentro y fuera de la muestra. Se usarán dos ciclos internos para calcular el rendimiento modelizado dentro y fuera de la muestra llamando a la función criterion y almacenando este valor en los arrays is_perf y oos_perf, respectivamente.

//--- get the oos_best performing in sample index
      is_best_index = maximize_criterion?ArrayMaximum(is_perf):ArrayMinimum(is_perf);
      //--- corresponding oos performance
      oos_best = oos_perf[is_best_index];

El índice del mejor valor de rendimiento en el array is_perf se calculará según maximise_criterion. El correspondiente valor de rendimiento fuera de la muestra se almacenará en la variable oos_best.

//--- count oos results less than oos_best
      int count=0;
      for(ulong isys=0; isys if (isys == ulong(is_best_index) || (maximize_criterion && oos_best>=oos_perf[isys]) || (!maximize_criterion && oos_best<=oos_perf[isys]))
            ++count;
        }

Vamos a recorrer el array oos_perf y a contar cuántas veces el valor oos_best ha sido igual o mejor.

//--- calculate the relative rank
      rel_rank = double (count)/double (m_trials+1);
      //--- cumulate num_less
      if(rel_rank<=0.5)
         ++num_less;

El recuento se utilizará para calcular la clasificación relativa. Por último, num_less se sumará si el rango relativo calculado es inferior a 0,5.

//---move calculation on to new combination updating flags array along the way
      int n=0;
      ulong iradix;
      for(iradix=0; iradix 1 ; iradix++)
        {
         if(m_flags[iradix]==1)
           {
            ++n;
            if(m_flags[iradix+1]==0)
              {
               m_flags[iradix]=0;
               m_flags[iradix+1]=0;
               for(ulong i=0; i if (--n>0)
                     m_flags[i]=1;
                  else
                     m_flags[i]=0;
                 }
               break;
              }
           }
        }

El último ciclo interno se utilizará para pasar a los siguientes conjuntos de datos dentro y fuera de la muestra.

if(iradix == m_combinations-1)
        {
         ++ncombo;
         break;
        }
     }
//--- final result
   return double(num_less)/double(ncombo);
  }


El último bloque if determinará cuándo salir del ciclo externo principal antes de retornar el valor final de PBO dividiendo num_less por ncombo.

Antes de ver un ejemplo de aplicación de la clase Ccscv, deberemos entender lo que este algoritmo dice sobre una estrategia en particular.


Interpretación de los resultados

El algoritmo CSCV que hemos implementado genera una métrica, la PBO. David Bailey y sus coautores señalan que la PBO define la probabilidad de que el conjunto de parámetros que ha ofrecido el mejor rendimiento durante la optimización en un conjunto de datos de muestra logre un rendimiento inferior a la mediana de los resultados de rendimiento utilizando conjuntos de parámetros subóptimos en un conjunto de datos fuera de la muestra.

Cuanto mayor sea el valor, mayor será el grado de ajuste. En otras palabras, existe una alta probabilidad de que una estrategia resulte ineficaz si se aplica fuera de la muestra. La PBO ideal debe ser inferior a 0,1.

El valor de PBO alcanzado dependerá principalmente de la variedad de conjuntos de parámetros probados durante la optimización. Así, deberemos asegurarnos de que los conjuntos de parámetros elegidos sean representativos de los que pueden aplicarse en el mundo real. Incluir intencionadamente combinaciones de parámetros que probablemente no se seleccionarán, o que estén dominadas por combinaciones que se encuentran cerca o lejos de ser óptimas solo distorsionará el resultado final.


Ejemplo

En esta sección, mostraremos la aplicación de la clase Ccscv para el asesor. Modificaremos el asesor Moving Average estándar para incluir el cálculo de PBO. Para aplicar eficazmente el método CSCV, utilizaremos el para recopilar datos de barras. Una vez finalizada la optimización, los datos de cada pasada se recogerán en una matriz. Esto significa que al menos el y el () deberán añadirse al código del asesor. Por último, el asesor seleccionado deberá someterse a una optimización completa utilizando la opción de algoritmo completo lento en el simulador de estrategias.  

//+------------------------------------------------------------------+
//|                                    MovingAverage_CSCV_DemoEA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include 
#include 
#include 

Empezaremos por incluir los archivos CSCV.mqh y Returns.mqh, que contienen la definición de la clase CReturns. CReturns nos servirá para reunir las rentabilidades de las barras con las que podremos calcular el ratio de Sharpe, la rentabilidad media o la rentabilidad total. Podemos utilizar cualquiera de estos parámetros como criterio para determinar el rendimiento óptimo. Como hemos mencionado al principio de este artículo, la métrica de rendimiento elegida no importa, puedes utilizar cualquier métrica.

sinput uint  NumBlocks          = 4;


Hemos añadido un nuevo parámetro no optimizable llamado NumBlocks, que determinará el número de particiones que utilizará el algoritmo CSCV. Más adelante veremos que el cambio de este parámetro afectará a la PBO.  

CReturns colrets;
ulong numrows,numcolumns;

Ahora declararemos un ejemplar de CReturns de forma global. Aquí también se declararán numrows y numcolumns, que utilizaremos para inicializar la matriz.

//+------------------------------------------------------------------+
//| TesterInit function                                              |
//+------------------------------------------------------------------+
void OnTesterInit()
  {
   numrows=1;
//---
   string name="MaximumRisk";
   bool enable;
   double par1,par1_start,par1_step,par1_stop;
   ParameterGetRange(name,enable,par1,par1_start,par1_step,par1_stop);
   if(enable)
      numrows*=ulong((par1_stop-par1_start)/par1_step)+1;

//---
   name="DecreaseFactor";
   double par2,par2_start,par2_step,par2_stop;
   ParameterGetRange(name,enable,par2,par2_start,par2_step,par2_stop);
   if(enable)
      numrows*=ulong((par2_stop-par2_start)/par2_step)+1;

//---
   name="MovingPeriod";
   long par3,par3_start,par3_step,par3_stop;
   ParameterGetRange(name,enable,par3,par3_start,par3_step,par3_stop);
   if(enable)
      numrows*=ulong((par3_stop-par3_start)/par3_step)+1;

//---
   name="MovingShift";
   long par4,par4_start,par4_step,par4_stop;
   ParameterGetRange(name,enable,par4,par4_start,par4_step,par4_stop);
   if(enable)
      numrows*=ulong((par4_stop-par4_start)/par4_step)+1;
  }

Vamos a añadir el manejador OnTesterInit(), en el que contaremos el número de conjuntos de parámetros a probar.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   colrets.OnNewTick();
//---
   if(SelectPosition())
      CheckForClose();
   else
      CheckForOpen();
//---
  }

En el manejador de eventos OnTick(), llamaremos al método OnNewtick() de CReturns.

//+------------------------------------------------------------------+
//| Tester function                                                  |
//+------------------------------------------------------------------+
double OnTester()
  {
//---
   double ret=0.0;
   double array[];
//---
   if(colrets.GetReturns(ENUM_RETURNS_ALL_BARS,array))
     {
      //---
      ret = MathSum(array);
      if(!FrameAdd(IntegerToString(MA_MAGIC),long(MA_MAGIC),double(array.Size()),array))
        {
         Print("Could not add frame ", GetLastError());
         return 0;
        }
      //---
     }
//---return
   return(ret);
  }

Dentro de OnTester(), reuniremos un array de retornos utilizando nuestra ejemplar CReturns declarado globalmente. Por último, añadiremos estos datos al marco usando la llamada FrameAdd().

//+------------------------------------------------------------------+
//| TesterDeinit function                                            |
//+------------------------------------------------------------------+
void OnTesterDeinit()
  {
//---prob value
   numcolumns = 0;
   double probability=-1;
   int count_frames=0;
   matrix data_matrix=matrix::Zeros(numrows,1);
   vector addvector=vector::Zeros(1);
   Cscv cscv;
//---calculate
   if(FrameFilter(IntegerToString(MA_MAGIC),long(MA_MAGIC)))
     {
      //---
      ulong pass;
      string frame_name;
      long frame_id;
      double passed_value;
      double passed_data[];
      //---
      while(FrameNext(pass,frame_name,frame_id,passed_value,passed_data))
        {
         //---
         if(!numcolumns)
           {
            numcolumns=ulong(passed_value);
            addvector.Resize(numcolumns);
            data_matrix.Resize(numrows,numcolumns);
           }
         //---
         if(addvector.Assign(passed_data))
           {
            data_matrix.Row(addvector,pass);
            count_frames++;
           }
         //---
        }
     }
   else
      Print("Error retrieving frames ", GetLastError());
//---results
   probability = cscv.CalculateProbability(NumBlocks,data_matrix,MathSum,true);
//---output results
   Print("cols ",data_matrix.Cols()," rows ",data_matrix.Rows());
   Print("Number of passes processed: ", count_frames, " Probability: ",probability);
//---
  }

Es precisamente en OnTesterDeinit() donde encontraremos la mayor parte de las adiciones realizadas al asesor. Aquí declararemos un ejemplar de Ccscv junto con las variables de tipo matriz y vector. Luego recorreremos en un ciclo todos los marcos y transmitiremos sus datos a la matriz. El vector se utilizará como intermediario para añadir una nueva fila de datos para cada marco.

Antes de mostrar los resultados en la pestaña Expertos del terminal, llamaremos al método CalculateProbability() de Ccscv. En este ejemplo, hemos transmitido MathSum() al método, lo cual significa que los ingresos totales se utilizarán para determinar el conjunto óptimo de parámetros. La salida también ofrecerá una indicación del número de marcos procesados para confirmar que se han capturado todos los datos.

Aquí están algunos resultados de la ejecución de nuestro asesor modificado con diferentes configuraciones en marcos temporales distintos. El resultado de la PBO se muestra en la pestaña Expertos del terminal.

MovingAverage_CSCV_DemoEA (EURUSD,H1)   Number of passes processed: 23520 Probability: 0.3333333333333333
NumBlocks
Marco temporal
Probabilidad de ajuste de las pruebas de la historia (PBO)
4
W1
0,3333
4
D1
0,6666
4
H12
0,6666
8
W1
0,2
8
D1
0,8
8
H12
0,6
16
W1
0,4444
16
D1
0,8888
16
H12
0,6666

El mejor resultado que hemos obtenido es 0,2. Los otros han sido mucho peores. Esto indica una alta probabilidad de que el asesor muestre un rendimiento pobre cuando se aplique a cualquier conjunto de datos fuera de la muestra. También veremos que los malos resultados de la PBO persistirán en distintos marcos temporales. La modificación del número de secciones utilizadas en el análisis no ha mejorado la mala puntuación inicial.

Configuración del simulador de estrategias comerciales


Parámetros de entrada seleccionados


Conclusión

Hoy hemos mostrado la aplicación del método de validación cruzada simétrica combinatoria para evaluar el ajuste después de un procedimiento de optimización. Comparado con el uso de permutaciones de Monte Carlo para cuantificar el ajuste, la CSCV es relativamente rápida. El método también hace un uso eficiente de los datos históricos disponibles. No obstante, existen escollos potenciales que debemos tener en cuenta. La fiabilidad de este método dependerá exclusivamente de los datos utilizados.

En particular, se comprobará el grado de variación de los parámetros. Utilizar menos variaciones de los parámetros podría llevar a subestimar el ajuste. Al mismo tiempo, la inclusión de un gran número de combinaciones de parámetros poco realistas podría dar lugar a sobreestimaciones. También debemos considerar el plazo elegido para el periodo de optimización. Esto puede afectar a la elección de los parámetros aplicados a la estrategia. Se sobreentiende que el valor final de la PBO puede variar de vez en cuando. Al realizar las pruebas, deberemos considerar el mayor número posible de configuraciones de parámetros.

Una desventaja notable de esta prueba es que no puede aplicarse fácilmente a los asesores cuyo código fuente no esté disponible. En teoría, se podrían realizar backtests independientes para cada posible configuración de los parámetros, pero esto requeriría tanto trabajo como utilizar los métodos de Monte Carlo.
 
En el artículo original figura una descripción detallada de la CSCV y la interpretación de la PBO. Encontrará el enlace en el segundo párrafo de este artículo. A continuación se adjunta el código fuente de todos los programas mencionados en el artículo.

Nombre del fichero
Descripción
Mql5\Include\Returns.mqh
Define una clase CReturns para recopilar los datos de rentabilidad o equidad en tiempo real.
Mql5\Include\CSCV.mqh
Contiene la definición de la clase Ccscv, que implementa la validación cruzada simétrica combinatoria.
Mql5\Experts\MovingAverage_CSCV_DemoEA.mq5
Asesor Moving Average modificado que demuestra la aplicación de la clase Ccscv.


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/13743

Archivos adjuntos |
CSCV.mqh (6.9 KB)
Returns.mqh (9.58 KB)
Mql5.zip (7.37 KB)
Patrones de diseño en MQL5 (Parte I): Patrones de creación (Creational Patterns) Patrones de diseño en MQL5 (Parte I): Patrones de creación (Creational Patterns)
Existen métodos que pueden usarse para resolver problemas típicos. Una vez entendemos cómo utilizar estas técnicas una vez, podemos escribir programas de forma eficaz y aplicar el concepto DRY (No te repitas, en inglés, don't repeat yourself). En este contexto, resultan muy útiles los patrones de diseño que pueden aportar soluciones a problemas bien descritos y recurrentes.
Algoritmos de optimización de la población: Algoritmo de gotas de agua inteligentes (Intelligent Water Drops, IWD) Algoritmos de optimización de la población: Algoritmo de gotas de agua inteligentes (Intelligent Water Drops, IWD)
El artículo analiza un interesante algoritmo, las gotas de agua inteligentes, IWD, presente en la naturaleza inanimada, que simula el proceso de formación del cauce de un río. Las ideas de este algoritmo han permitido mejorar significativamente el anterior líder de la clasificación, el SDS, y el nuevo líder (SDSm modificado); como de costumbre, se puede encontrar en el archivo del artículo.
Patrones de diseño en MQL5 (Parte 2): Patrones estructurales Patrones de diseño en MQL5 (Parte 2): Patrones estructurales
En este artículo, seguiremos estudiando los patrones de diseño que permiten a los desarrolladores crear aplicaciones extensibles y fiables no solo en MQL5, sino también en otros lenguajes de programación. Esta vez hablaremos de un tipo diferente: los patrones estructurales. Asimismo, aprenderemos a diseñar sistemas usando las clases disponibles para formar estructuras mayores.
Desarrollo de un sistema de repetición (Parte 41): Inicio de la segunda fase (II) Desarrollo de un sistema de repetición (Parte 41): Inicio de la segunda fase (II)
Si hasta ahora todo te ha parecido correcto, significa que no estás pensando realmente a largo plazo. Donde empiezas a desarrollar aplicaciones y, con el tiempo, ya no necesitas programar nuevas aplicaciones. Solo tienes que conseguir que trabajen juntos. Veamos cómo terminar de montar el indicador del ratón.