English Русский 中文 Deutsch 日本語 Português
preview
Desarrollo de un factor de calidad para los EAs

Desarrollo de un factor de calidad para los EAs

MetaTrader 5Ejemplos | 30 octubre 2023, 17:02
341 0
Ricardo Rodrigues Lucca
Ricardo Rodrigues Lucca

Introducción

En este artículo, te explicaremos cómo desarrollar un factor de calidad que tu Asesor Experto (EA) pueda devolver en el simulador de estrategias. Como se puede ver en la Figura 1 a continuación, el valor de "OnTester result" se devolvió como 1.0639375, como ejemplo de la calidad del sistema que se ejecutó. A lo largo de este artículo, aprenderás dos posibles enfoques para calcular la calidad de los sistemas y también verás cómo imprimir ambos valores en el registro, ya que solo podemos devolver uno de ellos.

Figura 1: Mostrando el campo "OnTester result" resaltado.


Arranque de un modelo de negocio o construcción de un EA

Antes de abordar el factor de calidad del sistema, es necesario establecer un sistema básico que se utilizará en las pruebas. Optamos por un sistema sencillo: haremos un sorteo de un número aleatorio y, si el número es par, entraremos en una posición de compra; de lo contrario, entraremos en una posición de venta, ya que el número es impar.

Para realizar el sorteo, utilizaremos la función MathRand(), que proporcionará un número entre 0 (cero) y 32767.Además, para hacer que el sistema sea más equilibrado, agregaremos dos reglas complementarias. Así, con estas 3 reglas, intentaremos garantizar un sistema más confiable. Mira:

  • Cuando no estás posicionado, debemos generar un número al azar;
    • Si el número es 0 (cero) o 32767, no haremos nada;
    • Si el número es par, compraremos la cantidad correspondiente al tamaño mínimo del lote del activo;
    • Si el número es impar, venderemos la cantidad correspondiente al tamaño mínimo del lote del activo;
  • Cuando estamos posicionados, moveremos el stop a favor en cada nueva vela que supere la anterior en la dirección del movimiento;
    • El stop utilizado se basará en el indicador ATR con un período de 1, normalizado con un EMA de 8. Además, se colocará en el extremo más alejado de las dos velas utilizadas para el análisis;
  • Si la hora está fuera del intervalo de las 11:00 a las 16:00, no estaremos autorizados a abrir una posición y, a las 16:30, la posición deberá ser cerrada obligatoriamente.

El código utilizado para desarrollar estas reglas se puede ver a continuación.

//--- Indicador utilizado para stop ATR(1) con EMA(8)...
int ind_atr = iATR(_Symbol, PERIOD_CURRENT, 1);
int ind_ema = iMA(_Symbol, PERIOD_CURRENT, 8, 0, MODE_EMA, ind_atr);
//--- Definimos la variable para decir que tenemos una transacción...
bool tem_tick = false;
//--- Variable auxiliar para abrir una posición.
#include<Trade/Trade.mqh>
#include<Trade/SymbolInfo.mqh>
CTrade negocios;
CSymbolInfo info;
//--- Definimos en OnInit() el uso del temporizador cada segundo
//--- e iniciamos CTrade.
int OnInit()
  {
//--- Ponemos el rellenado para dejar la orden pendiente hasta que se
//--- ejecute totalmente.
   negocios.SetTypeFilling(ORDER_FILLING_RETURN);
//--- Dejemos la desviación fijada, no se usa en B3
   negocios.SetDeviationInPoints(5);
//--- Definimos el símbolo de CSymbolInfo...
   info.Name(_Symbol);
//--- Creamos el temporizador...
   EventSetTimer(1);
//--- Definimos la base del número aleatorio para que tengamos pruebas iguales...
   MathSrand(0xDEAD);
   return(INIT_SUCCEEDED);
  }
//--- Como hemos definido un temporizador, lo destruiremos en OnDeInit()
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//--- La función OnTick solo nos informará de que tenemos una nueva transacción.
void OnTick()
  {
   tem_tick = true;
  }
//+------------------------------------------------------------------+
//| Función principal del asesor                                     |
//+------------------------------------------------------------------+
void OnTimer()
  {
   MqlRates cotacao[];
   bool fechar_tudo = false;
   bool negocios_autorizados = false;
//--- ¿Tenemos una nueva transacción?
   if(tem_tick == false)
      return ;
//--- Copiamos la información de las 3 velas más recientes para comprobarla....
   if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 3, cotacao) != 3)
      return ;
//--- ¿Tenemos una nueva vela desde la última comprobación?
   if(tem_vela_nova(cotacao[2]) == false)
      return ;
//--- Recupera los datos de la ventana comercial y de cierre...
   negocios_autorizados = esta_na_janela_de_negocios(cotacao[2], fechar_tudo);
//--- Si vamos a cerrar todo, si hay una posición la cerraremos...
   if(fechar_tudo)
     {
      negocios.PositionClose(_Symbol);
      return ;
     }
//--- Si no es para cerrar todo, moveremos el stop si hay una posición...
   if(arruma_stop_em_posicoes(cotacao))
      return ;
   if (negocios_autorizados == false) // ¿estamos fuera de la ventana comercial?
      return ;
//--- ¡Estamos en la ventana comercial, intentaremos abrir una posición!
   int sorteio = MathRand();
//--- Regla de entrada 1.1
   if(sorteio == 0 || sorteio == 32767)
      return ;
   if(MathMod(sorteio, 2) == 0)  // Regla de sorteo 1.2 -- número par compra
     {
     negocios.Buy(info.LotsMin(), _Symbol);
     }
   else // Regla de sorteo 1.3 -- número impar venta
     {
     negocios.Sell(info.LotsMin(), _Symbol);
     }
  }
//--- Comprobando si hay una vela nueva...
bool tem_vela_nova(const MqlRates &rate)
  {
   static datetime vela_anterior = 0;
   datetime vela_atual = rate.time;
   if(vela_atual != vela_anterior) // ¿es una hora diferente a la guardada?
     {
      vela_anterior = vela_atual;
      return true;
     }
   return false;
  }
//--- Comprobamos si la hora se encuentra en el periodo comercial de cierre de posiciones...
bool esta_na_janela_de_negocios(const MqlRates &rate, bool &close_positions)
  {
   MqlDateTime mdt;
   bool ret = false;
   close_positions = true;
   if(TimeToStruct(rate.time, mdt))
     {
      if(mdt.hour >= 11 && mdt.hour < 16)
        {
         ret = true;
         close_positions = false;
        }
      else
        {
         if(mdt.hour == 16)
            close_positions = (mdt.min >= 30);
        }
     }
   return ret;
  }
//---
bool arruma_stop_em_posicoes(const MqlRates &cotacoes[])
  {
   if(PositionsTotal()) // ¿hay una posición?
     {
      double offset[1] = { 0 };
      if(CopyBuffer(ind_ema, 0, 1, 1, offset) == 1 // ¿La EMA se ha copiado con éxito?
         && PositionSelect(_Symbol))  // ¡selecione la posición existente!
        {
         ENUM_POSITION_TYPE tipo = (ENUM_POSITION_TYPE) PositionGetInteger(POSITION_TYPE);
         double SL = PositionGetDouble(POSITION_SL);
         double TP = info.NormalizePrice(PositionGetDouble(POSITION_TP));
         if(tipo == POSITION_TYPE_BUY)
           {
            if (cotacoes[1].high > cotacoes[0].high)
               {
                  double sl = MathMin(cotacoes[0].low, cotacoes[1].low) - offset[0];
                  info.NormalizePrice(sl);
                  if (sl > SL)
                     {
                        negocios.PositionModify(_Symbol, sl, TP);
                     }
               }
           }
         else // tipo == POSITION_TYPE_SELL
           {
           if (cotacoes[1].low < cotacoes[0].low)
               {
                  double sl = MathMax(cotacoes[0].high, cotacoes[1].high) + offset[0];
                  info.NormalizePrice(sl);
                  if (SL == 0 || (sl > 0 && sl < SL))
                     {
                        negocios.PositionModify(_Symbol, sl, TP);
                     }
               }
           }
        }
      return true;
     }
   // no había ninguna posición
   return false;
  }

Vamos a comentar brevemente el código anterior.Utilizaremos la media calculada a partir del ATR para definir el tamaño de las paradas que se colocarán en los extremos de las velas cuando encontremos una vela que haya superado a la anterior. Esto ocurre en la función "arruma_stop_em_posicoes". Siempre que se devuelva un valor verdadero, hay una posición y no debemos avanzar en el código principal en "OnTimer". Utilizo esta función en lugar de "OnTick" porque no necesito que una función larga se ejecute en cada operación realizada, sino solo en cada nueva vela del período definido. Sin embargo, en "OnTick", una variable se configura como verdadera para indicar una operación anterior. Esto es necesario para evitar que el simulador de estrategias haga pausas cuando el mercado no está abierto, ya que ejecutaría la función incluso si no hubiera operaciones anteriores.

Mira, hasta ahora todo ha seguido estrictamente lo definido, incluyendo las dos ventanas definidas. La primera es la ventana de apertura de operaciones, que está entre las 11:00 y las 16:00 horas, y la segunda, la de gestión, permite que el algoritmo administre la operación abierta moviendo las órdenes de stop hasta las 16:30 horas, momento en que debe cerrar todas las operaciones del día.

Ten en cuenta que si operas este EA ahora, el "OnTester result" será cero, como se puede ver en la Figura 2, ya que no hemos proporcionado la función de cálculo para ese valor.

Figura 2: EA ejecutado en USDJPY en H1 en el modo OHLC de 1 minuto entre las fechas del 01-01-2023 al 19-05-2023.


Acerca del factor de calidad

Para configurar el valor de "OnTester result", necesitamos definir una función llamada "OnTester" que devuelva un valor double. ¡Así de simple! Con esto, obtenemos el resultado de la Figura 3 utilizando el código que se encuentra a continuación.

EA ejecutado en USDJPY en H1 en modo OHLC de 1 minuto entre las fechas del 01-01-2023 al 19-05-2023.

Figura 3: EA ejecutado en USDJPY en H1 en el modo OHLC de 1 minuto entre las fechas del 01-01-2023 al 19-05-2023.

El código a continuación se coloca al final del código anterior. En él, calculamos la relación promedio entre el riesgo y el rendimiento de las operaciones. Esta relación se expresa comúnmente como el rendimiento obtenido, dado que el riesgo se considera constante en 1. Por lo tanto, podemos interpretar la relación riesgo/rendimiento como 1:1.23 o, también, como 1.23 y otro ejemplo, 0.43. En el primer ejemplo, por cada dólar en riesgo ganamos 1.23, mientras que en el segundo, por cada dólar en riesgo perdemos 0.43. Por lo tanto, cuando el rendimiento es 1 o cercano a él, significa que estamos empatados, y por encima de 1 significa que estamos ganando.

Dado que las estadísticas no proporcionan el valor promedio ganado o perdido, utilizamos el valor bruto normalizado por la cantidad de operaciones en cada lado (compra o venta). Al recuperar los valores de las operaciones realizadas, sumamos 1 para su uso, de esta manera, si no hay operaciones con beneficios o pérdidas, el programa no se cerrará debido a una división por cero durante el cálculo. Además, para evitar mostrar una gran cantidad de dígitos como se mostraba anteriormente en la Figura 1, que tenía más de 5 dígitos, utilizamos la función "NormalizeDouble" para mostrar el resultado con solo 2 dígitos.

double OnTester()
  {
//--- Beneficio Medio
   double lucro_medio=TesterStatistics(STAT_GROSS_PROFIT)/(TesterStatistics(STAT_PROFIT_TRADES)+1);
//--- Pérdidas Medias
   double prejuizo_medio=-TesterStatistics(STAT_GROSS_LOSS)/(TesterStatistics(STAT_LOSS_TRADES)+1); 
//--- Cálculo del riesgo: rentabilidad que será obtenida
   double rr_medio = lucro_medio / prejuizo_medio;
//---
   return NormalizeDouble(rr_medio, 2);
  }

La función "OnTester" debe estar presente en cada código de EA para que el valor se muestre en el informe. Para reducir este trabajo de copiar varias líneas de código, aislaremos la función en un archivo separado, de esta manera, solo necesitaremos copiar una línea cada vez. Como se puede ver:

#include "ARTICLE_METRICS.mq5"

¡Así tendremos un código más conciso! En el archivo mencionado, la función se definirá mediante una definición. Esto es necesario para permitir que, si deseas utilizar el "include", puedas cambiar el nombre de la función a incluir sin complicaciones, evitando posibles errores de duplicación en caso de que la función "OnTester" ya esté definida. De esta manera, podemos considerarlo como un mecanismo para dar preferencia a "OnTester", que se insertaría directamente en el código del EA. Si queremos utilizarlo mediante "include", simplemente comentamos la función "OnTester" en el código del EA y comentamos la definición correspondiente. Volveremos a esto antes de que termine el artículo.

El archivo "ARTICLE_METRICS.mq5" se vería inicialmente así:

//--- Calcula el Riesgo: rendimiento medio de las operaciones
double rr_medio()
  {
//--- Beneficio Medio
   double lucro_medio=TesterStatistics(STAT_GROSS_PROFIT)/(TesterStatistics(STAT_PROFIT_TRADES)+1);
//--- Pérdidas Medias
   double prejuizo_medio=-TesterStatistics(STAT_GROSS_LOSS)/(TesterStatistics(STAT_LOSS_TRADES)+1); 
//--- Cálculo del riesgo: rentabilidad que será obtenida
   double rr_medio = lucro_medio / prejuizo_medio;
//---
   return NormalizeDouble(rr_medio, 2);
  }

//+------------------------------------------------------------------+
//| OnTester                                                         |
//+------------------------------------------------------------------+
#ifndef SQN_TESTER_ON_TESTER
#define SQN_TESTER_ON_TESTER OnTester
#endif
double SQN_TESTER_ON_TESTER()
  {
   return rr_medio();
  }

Ten en cuenta que el nombre correcto del archivo debería tener la extensión "mqh". Sin embargo, como planeo mantener el archivo en el directorio de EAs, dejé la extensión de código a propósito. Todo depende de tu elección.


Primera versión del cálculo de calidad

Nuestra primera versión del cálculo de calidad que vamos a mostrar se basa en el enfoque creado por Sunny Harris llamado CPC Index. Este cálculo utiliza tres métricas que se multiplican entre sí: riesgo:retorno promedio, tasa de éxito y factor de beneficio. Sin embargo, vamos a modificarlo para no usar el factor de beneficio y en su lugar usar el valor más bajo entre el factor de beneficio y el factor de recuperación. Aunque, si consideramos la diferencia entre los dos, deberíamos optar por el factor de recuperación, prefiero dejarlo así porque ya mejora el cálculo.

El fragmento de código siguiente implementa el enfoque mencionado en el párrafo anterior. Solo tienes que llamarlo en la función "OnTester". Ten en cuenta que aquí no se ha sumado 1 a la cantidad de operaciones porque el valor proporcionado es el total y esperamos que haya al menos 1 operación que evaluar.

//--- Calcula el CPC Index de Sunny Harris
double CPCIndex()
  {
   double taxa_acerto=TesterStatistics(STAT_PROFIT_TRADES)/TesterStatistics(STAT_TRADES);
   double fator=MathMin(TesterStatistics(STAT_PROFIT_FACTOR), TesterStatistics(STAT_RECOVERY_FACTOR));
   return NormalizeDouble(fator * taxa_acerto * rr_medio(), 5);
  }


Segunda versión del cálculo de calidad

El segundo factor de calidad que abordaremos se denomina Índice de Calidad del Sistema (SQN, por sus siglas en inglés) y fue creado por Van Tharp. Realizaremos el cálculo para las operaciones realizadas mes a mes y obtendremos un promedio simple de todos los meses de la simulación. El SQN difiere del enfoque explicado en la sección anterior, ya que busca enfatizar la estabilidad del sistema de negociación.

Así que, una característica importante del SQN es que penaliza a los sistemas que pueden tener altibajos. De esta manera, si el sistema tiene una serie de operaciones pequeñas y una grande, será penalizado. Esto significa que si tenemos un sistema con pequeñas pérdidas y grandes ganancias, lo penalizaremos. También será penalizado lo contrario, pequeñas ganancias y grandes pérdidas. Este último es lo peor para quienes operan.

Recuerda que operar es una carrera a largo plazo ¡y siempre céntrate en la estabilidad de seguir tu sistema! No en el dinero que aparece al final del mes, ya que esto puede tener una gran variabilidad.

//--- desviación estándar de las operaciones realizadas según el resultado en dinero
double dp_por_negocio(uint primeiro_negocio, uint ultimo_negocio,
                      double media_dos_resultados, double quantidade_negocios)
  {
   ulong ticket=0;
   double dp=0.0;
   for(uint i=primeiro_negocio; i < ultimo_negocio; i++)
     {
      //--- try to get deals ticket
      if((ticket=HistoryDealGetTicket(i))>0)
        {
         //--- get deals properties
         double profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);
         //--- create price object
         if(profit!=0)
           {
            dp += MathPow(profit - media_dos_resultados, 2.0);
           }
        }
     }
   return MathSqrt(dp / quantidade_negocios);
  }

//--- Calcula el System Quality Number, SQN, de Van Tharp
double sqn(uint primeiro_negocio, uint ultimo_negocio,
           double lucro_acumulado, double quantidade_negocios)
  {
   double lucro_medio = lucro_acumulado / quantidade_negocios;
   double dp = dp_por_negocio(primeiro_negocio, ultimo_negocio,
                              lucro_medio, quantidade_negocios);
   if(dp == 0.0)
     {
      // Como la desviación estándar ha retornado un valor cero que no esperábamos
      // lo cambiaremos por el beneficio_medio ya que no hay desviación, lo cual
      // acercará el sistema al resultado 1.
      dp = lucro_medio;
     }
//--- El número de operaciones aquí estará limitado a 100 para que el resultado
//--- no sea maximizado en función de un gran número de operaciones.
   double res = (lucro_medio / dp) * MathSqrt(MathMin(100, quantidade_negocios));
   return NormalizeDouble(res, 2);
  }

//--- retorna si se ha encontrado un nuevo mes
bool eh_um_novo_mes(datetime timestamp, int &mes_anterior)
  {
   MqlDateTime mdt;
   TimeToStruct(timestamp, mdt);
   if(mes_anterior < 0)
     {
      mes_anterior=mdt.mon;
     }
   if(mes_anterior != mdt.mon)
     {
      mes_anterior = mdt.mon;
      return true;
     }
   return false;
  }

//--- SQN Mensual
double sqn_mes(void)
  {
   double sqn_acumulado = 0.0;
   double lucro_acumulado = 0.0;
   double quantidade_negocios = 0.0;
   int sqn_n = 0;
   int mes = -1;
   uint primeiro_negocio = 0;
   uint total_negocios;
//--- solicitando la historia comercial
   if(HistorySelect(0,TimeCurrent()) == false)
      return 0.0;
   total_negocios = HistoryDealsTotal();
//--- para cada transacción, calculamos la media de cada mes
   for(uint i=primeiro_negocio; i < total_negocios; i++)
     {
      ulong    ticket=0;
      //--- Seleccionamos el ticket deseado para pegar las informaciones
      if((ticket=HistoryDealGetTicket(i))>0)
        {
         datetime time = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
         double   lucro = HistoryDealGetDouble(ticket,DEAL_PROFIT);
         if(lucro == 0)
           {
            //--- Si no hay resultado, pasaremos a la siguiente transacción.
            continue;
           }
         if(eh_um_novo_mes(time, mes))
           {
            //--- Si tenemos transacciones calculamos el sqn, de lo contrario será cero...
            if(quantidade_negocios>0)
              {
               sqn_acumulado += sqn(primeiro_negocio, i, lucro_acumulado,
                                    quantidade_negocios);
              }
            //--- ¡El número calculado de sqns se actualiza siempre!
            sqn_n++;
            primeiro_negocio=i;
            lucro_acumulado = 0.0;
            quantidade_negocios = 0;
           }
         lucro_acumulado += lucro;
         quantidade_negocios++;
        }
     }
//--- al salir del for puede que tengamos alguna consecuencia pendiente
   if(quantidade_negocios>0)
     {
      sqn_acumulado += sqn(primeiro_negocio, total_negocios,
                           lucro_acumulado, quantidade_negocios);
      sqn_n++;
     }
//--- tomamos la media simple de los sqns
   return NormalizeDouble(sqn_acumulado / sqn_n, 2);
  }

Vamos a explicar el código de abajo hacia arriba:

La primera función calcula la desviación estándar del conjunto de operaciones. Aquí, seguimos la recomendación de Van Tharp, que enfatiza la inclusión de todas las operaciones en el cálculo de la desviación estándar. Sin embargo, en la fórmula final que se encuentra en la función siguiente, limitamos el número de operaciones a 100. Esto se hace para evitar que el resultado se distorsione debido a la cantidad de operaciones, haciéndolo más práctico y significativo.

Finalmente, tenemos la función "sqn_mes" que verifica si estamos en un nuevo mes y acumula algunos datos necesarios para las funciones anteriores. Al final de esta función, se calcula el promedio de los SQN mensuales para el período en que se realizó la simulación. Esta breve explicación tiene como objetivo brindar una visión general del código y el propósito de cada función. Siguiendo este enfoque, es posible comprender mejor el cálculo del SQN.

La función OnTester puede imprimir los tres valores y se puede consultar en la pestaña del probador o guardar en un archivo, o incluso podemos devolver un valor multiplicado por otro para que aparezca en el informe, como se puede ver a continuación.
double SQN_TESTER_ON_TESTER()
  {
   PrintFormat("%G,%G,%G", rr_medio(), CPCIndex(), sqn_mes());
   return NormalizeDouble(sqn_mes() * CPCIndex(), 5);
  }


Antes de concluir

Antes de concluir este artículo, volvamos al tema del "include" y cómo evitar el error de duplicación. Supongamos que tengas un código de EA con la función "OnTester" y desees poner el "inlcude" del archivo mencionado. Se vería algo así como a continuación (ignora el contenido del "OnTester" de este ejemplo).

//+------------------------------------------------------------------+
double OnTester()
  {
   return __LINE__;
  }
//+------------------------------------------------------------------+
#include "ARTICLE_METRICS.mq5"

Este código resultará en un error de función duplicada porque tanto el EA en su código como el archivo de inclusión tienen una función con el mismo nombre, "OnTester". Sin embargo, podemos utilizar dos definiciones para cambiar el nombre de una de ellas y simular un mecanismo para habilitar o deshabilitar cuál función debería ser utilizada. Mira el ejemplo a continuación.

//+------------------------------------------------------------------+
#define OnTester disable
//#define SQN_TESTER_ON_TESTER disable
double OnTester()
  {
   return __LINE__;
  }
#undef OnTester
//+------------------------------------------------------------------+
#include "ARTICLE_METRICS.mq5"

En este nuevo formato, no tendremos un error de función duplicada porque la definición cambiará el nombre de la función en el código del EA de "OnTester" a "disable". Ahora, si comentamos la primera definición y descomentamos la segunda, el resultado será que la función dentro del archivo ARTICLE_METRICS se cambiará al nombre "disable", mientras que la función en el archivo del EA seguirá llamándose "OnTester".

Esta aproximación parece ser una manera sencilla de alternar entre ambas funciones que no involucra comentar múltiples líneas de código. Aunque puede ser un poco más intrusiva, creo que puede ser considerada por el usuario. Otra cosa a tener en cuenta sería si necesitas mantener la función junto con el EA, dado que ya existe una en un archivo incluido, lo que podría volverse confuso.


Conclusión

Llegamos al final de este artículo, en el que presentamos un modelo de EA que opera de manera aleatoria para servir como ejemplo en la demostración de cálculos de factor de calidad. Por lo tanto, abordamos 2 posibles cálculos: Van Tharp y Sunny Harris.Además, presentamos un factor introductorio utilizando la relación riesgo y rendimiento. También hemos demostrado cómo la utilización de "includes" puede facilitar alternar entre diferentes funciones disponibles.

¿Tienes alguna pregunta o encontraste algún error? ¡Comenta en el tema del artículo! Los códigos mencionados tanto del EA como del archivo de métricas están en el archivo adjunto para tu estudio.

¿Utilizas alguna otra métrica de calidad? ¡Compártela comentando aquí en el tema del artículo para que la conozcamos! Muchas gracias por leer este artículo.


Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/11373

Archivos adjuntos |
ARTICLE.zip (4.73 KB)
ARTICLE_METRICS.mq5 (10.33 KB)
ARTICLE_MT5.mq5 (11.87 KB)
Desarrollo de un sistema de repetición — Simulación de mercado (Parte 19): Ajustes necesarios Desarrollo de un sistema de repetición — Simulación de mercado (Parte 19): Ajustes necesarios
Lo que vamos a hacer aquí es preparar el terreno para que, cuando sea necesario agregar nuevas funciones al código, esto se haga de manera fluida y sencilla. El código actual aún no puede cubrir o manejar algunas cosas que serán necesarias para un progreso significativo. Necesitamos que todo se construya de manera que el esfuerzo de implementar algunas cosas sea lo más mínimo posible. Si esto se hace adecuadamente, tendremos la posibilidad de tener un sistema realmente muy versátil. Capaz de adaptarse muy fácilmente a cualquier situación que deba ser cubierta.
Teoría de categorías en MQL5 (Parte 12): Orden Teoría de categorías en MQL5 (Parte 12): Orden
El artículo forma parte de una serie sobre la implementación de grafos utilizando la teoría de categorías en MQL5 y está dedicado a la relación de orden (Order Theory). Hoy analizaremos dos tipos básicos de orden y exploraremos cómo los conceptos de relación de orden pueden respaldar conjuntos monoides en las decisiones comerciales.
Desarrollando un cliente MQTT para MetaTrader 5: metodología de TDD Desarrollando un cliente MQTT para MetaTrader 5: metodología de TDD
El presente artículo representa el primer intento de desarrollar un cliente MQTT nativo para MQL5. El MQTT es un protocolo de comunicación "publicación-suscripción". Es ligero, abierto, simple y está diseñado para implementarse con facilidad, lo cual permite su uso en muchas situaciones.
Teoría de categorías en MQL5 (Parte 11): Grafos Teoría de categorías en MQL5 (Parte 11): Grafos
El presente artículo continúa la serie sobre la implementación de la teoría de categorías en MQL5. Aquí veremos cómo podemos integrar la teoría de grafos con los monoides y otras estructuras de datos al desarrollar una estrategia de cierre del sistema comercial.