English Русский Português
preview
Desarrollamos un asesor experto multidivisas (Parte 29): Mejoras en el proceso de optimización automatizada

Desarrollamos un asesor experto multidivisas (Parte 29): Mejoras en el proceso de optimización automatizada

MetaTrader 5 — Probador |
26 0
Yuriy Bykov
Yuriy Bykov

Introducción

En las tres partes anteriores, nos hemos desviado ligeramente de la línea principal de desarrollo y hemos centrado nuestros esfuerzos en crear herramientas auxiliares que nos resultarán útiles de una forma u otra en el futuro.

Por ejemplo, en la parte 28, hemos ampliado las funciones de gestión de capital de nuestro EA multidivisa mediante el desarrollo y la implementación de un nuevo módulo de software: el gestor de cierre. Permitía realizar un seguimiento de las ganancias o pérdidas totales en relación con un saldo base actualizado dinámicamente y reiniciar todas las estrategias de negociación cuando se alcanzaban los valores establecidos. Entre los planes de futuro figuraba ampliar su funcionalidad añadiendo la posibilidad de aplicar un seguimiento dinámico al beneficio total y llevar todo el conjunto de posiciones abiertas al punto de equilibrio.

En la parte 27, creamos otro componente: un cuadro de diálogo que ocupa toda el área del gráfico del terminal y permite mostrar texto de varias líneas con opciones flexibles de configuración de la fuente y compatibilidad con el desplazamiento. Esta herramienta hizo que la visualización de la información resultara más práctica y clara. Hemos añadido este componente a la biblioteca Adwizard como herramienta para mostrar distintos tipos de información durante la ejecución del EA.

En general, hemos consolidado el enfoque adoptado anteriormente, que consiste en una clara división de todo el código del programa en una parte de biblioteca (repositorio Adwizard) y una o varias partes del proyecto (repositorios SimpleCandles y SymbolsInformer).

Ahora volvamos a los resultados obtenidos en la parte 25. Allí alcanzamos un hito importante en el desarrollo de nuestro EA multidivisa, tras haber completado la creación de un sistema universal de optimización automática y haber integrado con éxito en él una nueva estrategia de trading denominada SimpleCandles. Todo el proceso —desde la creación de un proyecto de optimización en la base de datos y la puesta en marcha de una optimización en varias etapas en un clúster de agentes hasta la generación de un EA final listo para su uso— estuvo totalmente automatizado.

Sin embargo, desde la publicación de ese artículo, hemos encontrado una serie de dificultades o inconvenientes a la hora de utilizar este sistema. Esto no es de extrañar, ya que se trataba únicamente de la primera iteración del desarrollo del sistema en su conjunto. Veamos, pues, algunas mejoras que nos permitirán utilizar la optimización automática de forma más eficaz.


Planificación del proceso

Recordemos en qué consiste la cadena propuesta para desarrollar el asesor experto (EA) de trading definitivo. En la primera etapa, queremos llevar a cabo varios procesos de optimización de una misma estrategia de trading para diferentes símbolos, marcos temporales y otros parámetros en el probador de estrategias de MetaTrader 5. A partir de los resultados de la optimización, seleccionaremos un buen número de resultados prometedores para cada símbolo (si los hay, claro está). Llamémoslos instancias individuales de una estrategia de trading.

En la segunda etapa, llevaremos a cabo una optimización para identificar los mejores grupos formados por un pequeño número de instancias individuales de estrategias de trading. Es decir, de entre miles de instancias, seleccionaremos para cada símbolo un grupo de entre 8 y 16 instancias que hayan mostrado los mejores resultados al trabajar juntas. Durante la tercera etapa, combinaremos estos mejores grupos para cargarlos y utilizarlos en el EA definitivo.

Tras un largo proceso de desarrollo, todas las acciones mencionadas se han automatizado en la medida de lo posible. Ahora tenemos que especificar manualmente los parámetros para generar un proyecto de optimización; es decir, básicamente, un escenario general según el cual se llevará a cabo la optimización automática. Una vez finalizada la optimización, habrá que realizar varios pasos para poner en marcha el EA definitivo en la cuenta de trading. Sin embargo, el tiempo dedicado actualmente a las operaciones manuales (apenas unos minutos) es insignificante comparado con el tiempo durante el cual una cadena automatizada de optimización puede funcionar sin necesidad de intervenir en el proceso (horas, días o incluso semanas). Esta es la herramienta con la que empezamos a trabajar.

El primer inconveniente surgió cuando, tras terminar de probar la integración de una nueva estrategia, decidimos pasar a otra nueva. Resultó que, a pesar de la división declarada del código en dos partes independientes (la parte de biblioteca y la parte del proyecto), aún persistían algunas conexiones entre ellas. Veamos dónde se producen e intentemos eliminarlas.

Otra dificultad era que las tareas individuales ejecutadas dentro de la cadena de optimización del proyecto podían tardar bastante tiempo. Esta duración depende directamente de la extensión del intervalo histórico sobre el que se realizan todas las optimizaciones. Si queremos realizar una optimización sobre un periodo histórico de, por ejemplo, 5 años, el proceso llevará mucho más tiempo que una optimización sobre un periodo de 3 meses. Sin embargo, como ha demostrado la práctica, durante la optimización genética es posible encontrar buenas combinaciones de parámetros de la estrategia mucho antes del final previsto del proceso. Por lo tanto, es posible reducir el tiempo total de la cadena deteniendo los procesos de optimización tras un periodo de tiempo determinado. Añadamos a nuestro sistema la posibilidad de especificar un límite de tiempo para completar cada tarea de optimización.

Por último, vamos a facilitar un poco el seguimiento del proceso de optimización automática mostrando información más detallada sobre la tarea actual, en lugar de mostrar solo su ID.

Para mayor claridad, repasemos todo el proceso paso a paso hasta crear el EA definitivo, deteniéndonos para realizar las correcciones que consideremos oportunas.


Creación de una base de datos

En primer lugar, debemos clonar los dos repositorios siguientes en MQL5/Shared Projects: el repositorio de la biblioteca Adwizard y el repositorio del proyecto. En este artículo encontraremos información detallada sobre cómo clonar en nuestro ordenador los repositorios alojados en MQL5 Algo Forge.

También podemos crear nuestro propio repositorio para el proyecto utilizando el repositorio SimpleCandles como plantilla. Hemos hablado de cómo hacerlo en los artículos dedicados a la transición a una nueva estrategia. Esto será necesario si queremos implementar alguna de nuestras propias estrategias de trading. Seguiremos utilizando la estrategia SimpleCandles y el repositorio del proyecto del mismo nombre. Contiene un proyecto para crear un asesor experto (EA) multidivisa final utilizando esta estrategia de trading.

Cuando ambos repositorios se encuentren en la carpeta de trabajo del terminal, en MQL5/Shared Projects, compilaremos el archivo del EA para crear un proyecto de optimización automática, SimpleCandles/Optimization/CreateProject.mq5, y arrastraremos la versión compilada que aparece en el Navegador del terminal a cualquier gráfico. En el cuadro de diálogo de parámetros de entrada, iremos a la pestaña «Parámetros de entrada» y veremos lo siguiente:

En el parámetro del archivo de base de datos de optimización, hemos especificado el nombre de la base de datos que utilizamos en artículos anteriores. Dado que tenemos previsto realizar algunos cambios, entre ellos algunos que afectan a la estructura de la base de datos de optimización, no podemos seguir utilizando la base de datos antigua. Es necesario crear una nueva.

Es muy sencillo: solo tenemos que especificar otro nombre para la base de datos en este parámetro de entrada. Si no existe ningún archivo con este nombre, el EA de creación de proyectos creará automáticamente una base de datos vacía con este nombre y la estructura de tablas necesaria. Para evitar tener que cambiar el valor predeterminado del nombre la próxima vez que ejecutemos este EA, cambiémoslo por uno nuevo en el archivo fuente del EA:

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
sinput group "::: Database"
sinput string fileName_  = "article.17607.db.sqlite"; // - Optimization database file

sinput group "::: Project parameters - Basic"
sinput string  projectName_ = "SimpleCandles";        // - Name
sinput string  projectVersion_ = "1.00";              // - Version
sinput string  symbols_ = "GBPUSD,EURUSD,EURGBP";     // - Symbols
sinput string  timeframes_ = "H1,M30";                // - Timeframes
...

Además, resulta más cómodo modificar los valores de otros parámetros en el código fuente de este EA que en el cuadro de diálogo de configuración de parámetros al iniciar el EA. Esta es una etapa crucial en la que debemos analizar detenidamente el escenario general de la cadena automatizada de optimización, registrarlo en los parámetros de entrada del EA de creación del proyecto y, a continuación, ejecutarlo una vez. En la práctica, lo ejecutamos más de una vez, ya que resulta muy difícil determinar todos los parámetros de una sola vez. Por lo tanto, procedemos por iteraciones: fijamos los valores de los parámetros en el código fuente, creamos un proyecto, ejecutamos la optimización automática y, si vemos que hay que cambiar algo, detenemos el proceso y volvemos al principio.

¿Qué podría salir mal? Por ejemplo, hemos elegido un intervalo temporal demasiado largo para las pruebas y, según unas estimaciones aproximadas, todo el proceso durará varias semanas. En este caso, si no queremos esperar tanto tiempo, podemos intentar acortar el periodo de prueba o, por ejemplo, reducir el número de símbolos diferentes para los que se repetirán las pasadas de optimización. Para ello, modificamos los parámetros de entrada y volvemos a crear el proyecto. Incluso podemos eliminar previamente el archivo de la base de datos para que el proyecto recién creado sea el único que figure en la nueva base de datos y no se añada a una ya existente.

En general, modificar los valores predeterminados de estos parámetros en la parte del proyecto es aceptable y está bastante justificado: cada proyecto requiere una configuración específica, que debe establecerse en el propio proyecto.

El resultado de este paso es la creación de un archivo de base de datos con el nombre especificado en la carpeta de datos común de los terminales. La base de datos creada contiene información sobre la configuración del proyecto de optimización automática (en la tabla «projects»), que consta de un conjunto de etapas (tabla «stages»). Las etapas se componen de trabajos (tabla «jobs»), y dentro de un trabajo puede haber una o varias tareas de optimización (tabla «tasks»). Estas relaciones se representan en la figura mediante flechas:

Todas las tareas del proyecto se encuentran en el estado «Queued» (en cola), lo que significa que están pendientes de ejecución. Ahora podemos pasar al siguiente paso: poner en marcha la cadena automatizada de optimización, en la que todas las tareas creadas se convertirán en ejecuciones independientes de optimización de los EA de cada etapa en el probador de estrategias de MetaTrader 5.


Puesta en marcha de la cadena de optimización

Para ello, compilaremos el archivo del EA de optimización automática SimpleCandles/Optimization/Optimization.mq5 y arrastraremos la versión compilada que aparece en el Navegador del terminal a cualquier gráfico. En el cuadro de diálogo de parámetros de entrada, en la pestaña «Parámetros de entrada», veremos lo siguiente:

Aquí volvemos a ver que los valores predeterminados de los parámetros corresponden al nombre del archivo de base de datos anterior. Por supuesto, podemos sustituirlo ahora manualmente por un nuevo nombre, article.17607.db.sqlite, pero la experiencia nos ha demostrado que esto resulta poco práctico. El motivo es que, al configurar la cadena, lo más probable es que tengamos que realizar primero varias ejecuciones de prueba. Y en cada una de ellas tendremos que cambiar manualmente el nombre del archivo de la base de datos por el que corresponda en ese momento.

Sin embargo, si intentamos hacer lo descrito anteriormente, es decir, cambiar el valor del nombre en el código, veremos que estos cambios deben realizarse no en la parte del proyecto, sino en la parte de biblioteca. En la parte del proyecto, el archivo del EA de optimización (SimpleCandles/Optimization/Optimization.mq5) se limita a incluir el archivo de biblioteca, de modo que, al compilar, obtenemos un archivo ejecutable en la carpeta del proyecto:

#include "../Include/Adwizard/Experts/Optimization.mqh"

No contiene nada más, por lo que el valor utilizado de forma predeterminada se encuentra en el archivo de biblioteca incluido. Solucionemos esto de la siguiente manera: en el archivo del proyecto, antes de incluir el archivo de biblioteca, declaramos constantes con los valores predeterminados necesarios para los parámetros y, al mismo tiempo, corregimos la ruta al archivo incluido eliminando el nombre de la carpeta Include:

// Constants with default parameters for the project:
// - File with the main database
#define OPT_FILEMNAME "article.17607.db.sqlite"

// - Path to the Python interpreter
#define OPT_PYTHONPATH "C:\\Python\\Python312\\python.exe"

#include "../../Adwizard/Experts/Optimization.mqh"

En la parte de biblioteca (Adwizard/Experts/Optimization.mqh), tendremos en cuenta que estas constantes podrían no existir. En ese caso, las declaramos asignándoles valores vacíos. A continuación, utilizamos sus valores en los parámetros de entrada:

// Create constants for default parameters,
// if they are not defined in the project part
#ifndef OPT_FILEMNAME
#define OPT_FILEMNAME ""
#endif

#ifndef OPT_PYTHONPATH
#define OPT_PYTHONPATH ""
#endif

sinput string fileName_    = OPT_FILEMNAME;  // - File with the main database
sinput string pythonPath_  = OPT_PYTHONPATH; // - Path to the Python interpreter

Ahora podemos especificar estos parámetros en el código de la parte del proyecto sin modificar la parte de biblioteca. Es decir, la parte de biblioteca será la misma para los distintos proyectos.


Limitación del tiempo de ejecución de las tareas

Como ya se ha mencionado, otra dificultad radicaba en que las tareas individuales ejecutadas dentro de la cadena de optimización del proyecto podían prolongarse bastante. Para establecer un límite de tiempo en la ejecución de una tarea de optimización concreta, tendremos que realizar cambios en varios lugares.

En primer lugar, debemos añadir parámetros de entrada al archivo del EA de creación del proyecto para poder especificar el tiempo máximo de ejecución deseado para las tareas. Estos cambios afectan a la parte del proyecto. En segundo lugar, en la tabla «tasks» de la base de datos, tenemos que añadir un campo para almacenar este valor. En tercer lugar, el código del EA de optimización debe permitir utilizar el valor del tiempo máximo de ejecución. Estos cambios se llevarán a cabo en la parte de biblioteca.

Añadamos dos parámetros al archivo SimpleCandles/Optimization/CreateProject.mq5 para especificar el tiempo máximo de ejecución de las tareas de optimización en la primera y la segunda etapa.

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
sinput group "::: Database"
sinput string fileName_  = "article.17607.db.sqlite"; // - Optimization database file

sinput group "::: Project parameters - Basic"
sinput string  projectName_ = "SimpleCandles";        // - Name
sinput string  projectVersion_ = "1.00";              // - Version
sinput string  symbols_ = "GBPUSD,EURUSD,EURGBP";     // - Symbols
sinput string  timeframes_ = "H1,M30";                // - Timeframes

sinput group "::: Project parameters - Optimization interval"
sinput datetime fromDate_ = D'2023-09-01';            // - Start date
sinput datetime toDate_ = D'2024-01-01';              // - End date

sinput group "::: Project parameters - Account"
sinput string   mainSymbol_ = "GBPUSD";               // - Main symbol
sinput int      deposit_ = 10000;                     // - Initial deposit

sinput group "::: Stage 1. Search"
sinput string   stage1ExpertName_ = "Stage1.ex5";     // - Stage EA
sinput string   stage1Criterions_ = "6,6,6";          // - Optimization criteria for tasks
sinput long     stage1MaxDuration_ = 20;              // - Max duration of tasks (с)

sinput group "::: Stage 2. Grouping"
sinput string   stage2ExpertName_ = "Stage2.ex5";     // - Stage EA
sinput string   stage2Criterion_  = "6";              // - Optimization criterion for tasks
sinput long     stage2MaxDuration_ = 20;              // - Max duration of tasks (с)
//sinput bool     stage2UseClusters_= false;          // - Use clustering?
sinput double   stage2MinCustomOntester_ = 500;       // - Min norm. profit
sinput uint     stage2MinTrades_  = 20;               // - Min number of trades
sinput double   stage2MinSharpeRatio_ = 0.7;          // - Min Sharpe coeff.
sinput uint     stage2Count_      = 8;                // - Number of strategies in the group (1 - 16)

sinput group "::: Stage 3. Final"
sinput string   stage3ExpertName_ = "Stage3.ex5";     // - Stage EA
sinput ulong    stage3Magic_      = 27183;            // - Magic
sinput bool     stage3Tester_     = true;             // - For the tester?

No podemos especificar una duración máxima para la tercera etapa, ya que esta utiliza una pasada única del probador de estrategias en lugar de una optimización. Solo una vez finalizada esta pasada se calculan los volúmenes normalizados de las posiciones abiertas y se genera la línea de inicialización del EA final. Si interrumpimos esta pasada antes de tiempo, no podremos obtener sus resultados. En la primera y la segunda etapa, detener el proceso de optimización antes de tiempo solo supondrá una reducción del número total de pasadas realizadas. Si el número de pasadas realizadas sigue siendo suficientemente alto, no hay de qué preocuparse.

Para añadir un nuevo campo a la estructura de la tabla «tasks», solo tenemos que añadir una consulta SQL al archivo de definición de la estructura de la base de datos de optimización db.opt.schema.sql:

-- Table: tasks
DROP TABLE IF EXISTS tasks;

CREATE TABLE tasks (
    id_task                INTEGER  PRIMARY KEY AUTOINCREMENT,
    id_job                 INTEGER  NOT NULL
                                    REFERENCES jobs (id_job) ON DELETE CASCADE
                                                             ON UPDATE CASCADE,
    optimization_criterion INTEGER  DEFAULT (7) 
                                    NOT NULL,
    start_date             DATETIME,
    finish_date            DATETIME,
    max_duration           INTEGER  NOT NULL
                                    DEFAULT 0,
    status                 TEXT     NOT NULL
                                    DEFAULT Queued
                                    CHECK (status IN ('Queued', 'Process', 'Done') ) 
);

Ahora pasemos a editar la parte de biblioteca. Básicamente, tendremos que realizar cambios en dos archivos. En primer lugar, en el archivo de la clase de tareas de optimización Adwizard/Optimization/OptimizerTask.mqh, añadiremos otro campo para la duración máxima a la estructura destinada a leer los parámetros de la tarea desde la base de datos:

//+------------------------------------------------------------------+
//| Optimization task class                                          |
//+------------------------------------------------------------------+
class COptimizerTask {
protected:
   // ...

public:
   // Data structure for reading a single row of a query result 
   struct params {
      string         expert;
      int            optimization;
      string         from_date;
      string         to_date;
      int            forward_mode;
      string         forward_date;
      double         deposit;
      string         symbol;
      string         period;
      string         tester_inputs;
      ulong          id_task;
      int            optimization_criterion;
      long           max_duration;
   } m_params;

   // ...
};

Lo añadiremos a la consulta para obtener la información de la tarea de la base de datos:

//+------------------------------------------------------------------+
//| Get the next optimization task from the queue                    |
//+------------------------------------------------------------------+
void COptimizerTask::Load(ulong p_id) {
// Save task ID
   m_id = p_id;

// Request to get optimization task from queue by ID
   string query = StringFormat(
                     "SELECT s.expert,"
                     "       s.optimization,"
                     "       s.from_date,"
                     "       s.to_date,"
                     "       s.forward_mode,"
                     "       s.forward_date,"
                     "       s.deposit,"
                     "       j.symbol,"
                     "       j.period,"
                     "       j.tester_inputs,"
                     "       t.id_task,"
                     "       t.optimization_criterion,"
                     "       t.max_duration"
                     "  FROM tasks t"
                     "       JOIN"
                     "       jobs j ON t.id_job = j.id_job"
                     "       JOIN"
                     "       stages s ON j.id_stage = s.id_stage"
                     " WHERE t.id_task=%I64u;", m_id);

// Open the database
   if(DB::Connect(m_fileName)) {
      // Execute the request
      int request = DatabasePrepare(DB::Id(), query);

      // ...
   }
}

En el método para comprobar que la tarea ha finalizado, añadiremos un bloque de código que, si se especifica un valor distinto de cero para la duración máxima de la tarea, obtendrá de la base de datos el tiempo transcurrido desde el inicio de la tarea y lo comparará con el máximo especificado. Si la duración actual ya ha superado el máximo, se invoca el método de parada forzada de la tarea:

//+------------------------------------------------------------------+
//| Task completed?                                                  |
//+------------------------------------------------------------------+
bool COptimizerTask::IsDone() {
// If there is no current task, then everything is done
   if(m_id == 0) {
      return true;
   }

// Result
   bool res = false;

// If this is the EA optimization task
   if(m_type == TASK_TYPE_EX5) {
      // Check if the strategy tester has finished its work
      res |= MTTESTER::IsReady();

      // If the tester is running and the maximum duration is specified, then
      if(!res && m_params.max_duration > 0) {
         // Request to get the elapsed execution time of the current task
         string query = StringFormat("SELECT unixepoch(datetime()) - unixepoch(start_date) AS duration"
                                     "  FROM tasks"
                                     " WHERE id_task=%I64u;", m_id);
         
         // Get the execution time in seconds
         DB::Connect(m_fileName);
         long duration = StringToInteger(DB::GetValue(query));
         DB::Close();

         // If the execution time is greater than the maximum allowed, 
         if(duration > m_params.max_duration) {
            // Stop the task
            Stop();
         }
      }

      // If this is a task to run a Python program, then
   } else if(m_type == TASK_TYPE_PY) {
      // ...
      }
   } else {
      res = true;
   }

   return res;
}

En Adwizard/Optimization/OptimizationProject.mqh, añadiremos el paso del parámetro de duración máxima a los métodos de creación de tareas:

//+------------------------------------------------------------------+
//| Optimization project class                                       |
//+------------------------------------------------------------------+
class COptimizationProject {
public:
   // ...
   
   // Add new tasks to the database for the specified optimization criteria
   COptimizationProject* AddTasks(string p_criterions, long p_maxDuration = 0);
   COptimizationProject* AddTasks(string &p_criterions[], long p_maxDuration = 0);

   // ...
};

//+------------------------------------------------------------------+
//| Add new tasks to the database for the specified                  |
//| optimization criteria in one string                              |
//+------------------------------------------------------------------+
COptimizationProject* COptimizationProject::AddTasks(string p_criterions, long p_maxDuration) {
// Array for optimization criteria
   string criterions[];
   StringReplace(p_criterions, ";", ",");
   StringSplit(p_criterions, ',', criterions);

   return AddTasks(criterions, p_maxDuration);
}

//+------------------------------------------------------------------+
//| Add new tasks to the database for the specified                  |
//| optimization criteria in the array                               |
//+------------------------------------------------------------------+
COptimizationProject* COptimizationProject::AddTasks(string &p_criterions[], long p_maxDuration) {
// For each job of the current stage
   FOREACH_AS(m_stage.jobs, m_job) {
      // For each optimization criterion
      FOREACH(p_criterions) {
         // Create a new task object for the job
         m_task = new COptimizationTask(0, m_job, (int) p_criterions[i], p_maxDuration);

         // Insert it into the optimization database
         m_task.Insert();

         // Add it to the array of all tasks
         APPEND(m_tasks, m_task);

         // Add it to the current job's tasks array
         APPEND(m_job.tasks, m_task);
      }
   }

   return &this;
}

En el archivo Adwizard/Optimization/OptimizationTask.mqh, lo único que tenemos que hacer es añadir un campo adicional a la clase COptimizationTask y asegurarnos de que se inicialice en el constructor y en el método encargado de insertar datos en la tabla «tasks» de la base de datos:

//+------------------------------------------------------------------+
//| Optimization task class                                          |
//+------------------------------------------------------------------+
class COptimizationTask {
public:
   ulong             id_task;       // task ID
   ulong             id_job;        // job ID
   int               optimization;  // Optimization criterion
   long              maxDuration;   // Max duration
   string            status;        // Task status

   COptimizationJob* job;           // The job for the task will be launched for

   // Constructor
                     COptimizationTask(ulong p_taskId = 0, COptimizationJob* p_job = NULL,
                     int p_optimization = 6,
                     long p_maxDuration = 0,
                     string p_status = "Done");

   // Create a task in the database
   void              Insert();
};

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
COptimizationTask::COptimizationTask(ulong p_taskId = 0,
                                     COptimizationJob* p_job = NULL,
                                     int p_optimization = 6,
                                     long p_maxDuration = 0,
                                     string p_status = "Done") :
   id_task(p_taskId),
   job(p_job),
   id_job(!!p_job ? p_job.id_job : 0),
   optimization(p_optimization),
   maxDuration(p_maxDuration),
   status(p_status) {}

//+------------------------------------------------------------------+
//| Create a task in the database                                    |
//+------------------------------------------------------------------+
void COptimizationTask::Insert() {
   string query = StringFormat("INSERT INTO tasks "
                               " VALUES (NULL,%I64u,%d,NULL,NULL,%I64d,'%s');",
                               id_job, optimization, maxDuration, status);

   id_task = DB::Insert(query);
   PrintFormat(__FUNCTION__" | %s -> %I64u", query, id_task);
}
//+------------------------------------------------------------------+

Ahora podemos compilar dos asesores expertos: uno para crear proyectos de optimización en la base de datos, especificando el tiempo máximo de ejecución de las tareas de optimización en las dos primeras etapas de la cadena (CreateProject.ex5), y otro para poner en marcha la cadena (Optimization.ex5).


Mostrar datos durante la optimización

Dado que antes nos resultaba más importante garantizar el correcto funcionamiento del EA de la cadena automatizada de optimización, la visualización de la información del proceso era una cuestión secundaria. Utilizábamos la función estándar Comment(), que permite mostrar un texto breve en la esquina superior izquierda del gráfico en el que se ejecuta el EA. Lo único que se mostraba era el ID de la tarea de optimización actual. Ahora que el funcionamiento principal del EA de optimización está más o menos definido, podemos pasar a cuestiones menos importantes. Además, ahora disponemos de un componente que permite mostrar texto con mayor flexibilidad en el gráfico del EA: la clase CConsoleDialog. Como recordaremos, un objeto de esta clase nos permite crear un cuadro de diálogo que se expande hasta cubrir todo el gráfico, con botones para contraerlo y cerrarlo, y que muestra texto de varias líneas con desplazamiento y escala ajustable. Vamos a utilizarlo.

Para ello, tenemos que añadir lo siguiente. En el archivo de biblioteca incluido en el EA de optimización (Adwizard/Experts/Optimization.mqh), debemos crear un puntero global a un objeto de esta clase y, en la función de inicialización, crear el propio objeto de diálogo y llamar a su método de ejecución:

CConsoleDialog *dialog;                      // Dialog for displaying text with data

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
// If the database file is not specified, then exit
   if(fileName_ == "") {
      PrintFormat(__FUNCTION__" | ERROR: Set const OPT_FILEMNAME with filename of DB in project", 0);
      return INIT_FAILED;
   }

// Create an optimizer
   optimizer = new COptimizer(fileName_, pythonPath_);

// Create and launch a dialog to display information
   dialog = new CConsoleDialog();
   dialog.Create(__FILE__);
   dialog.Run();

// Create the timer and start its handler
   EventSetTimer(2);
   OnTimer();

   return(INIT_SUCCEEDED);
}

En el controlador del temporizador, enviamos al objeto de diálogo el texto actualizado que proporciona el optimizador:

//+------------------------------------------------------------------+
//| Expert timer function                                            |
//+------------------------------------------------------------------+
void OnTimer() {
   
// Start the optimizer handling
   optimizer.Process();

   dialog.Text(optimizer.Text());
}

Añadamos una función de gestión de eventos del gráfico, OnChartEvent(), que pase los eventos al objeto de diálogo para garantizar la interacción con el usuario:

//+------------------------------------------------------------------+
//| Event handling                                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID
                  const long & lparam,  // event parameter of the long type
                  const double & dparam, // event parameter of the double type
                  const string & sparam) { // event parameter of the string type

   if(!!dialog && !IsStopped()) {
      dialog.ChartEvent(id, lparam, dparam, sparam);
   }
}

Asegurémonos de eliminar el objeto de diálogo creado cuando el EA termine de ejecutarse y de volver a dibujar el gráfico:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   PrintFormat(__FUNCTION__" | Reason: %d", reason);
   EventKillTimer();

// Remove the optimizer
   if(!!optimizer) {
      delete optimizer;
   }   
   
// Remove the dialog
   if(!!dialog) {
      dialog.Destroy();
      delete dialog;
      ChartRedraw();
   }
}

Ahora tenemos que generar el texto deseado que se mostrará en pantalla durante la optimización. Implementemos esto en el método Text() del objeto optimizador en Adwizard/Optimization/Optimizer.mqh:

//+------------------------------------------------------------------+
//| Information about the current optimization state                 |
//+------------------------------------------------------------------+
string COptimizer::Text(void) {
   string text = "";

   // Get the number of projects with different statuses
   DB::Connect(m_fileName);
   int process_projects_count = (int) DB::GetValue("SELECT count(status) FROM projects WHERE status = 'Process'");
   int queued_projects_count = (int) DB::GetValue("SELECT count(status) FROM projects WHERE status = 'Queued'");
   int done_projects_count = (int) DB::GetValue("SELECT count(status) FROM projects WHERE status = 'Done'");
   int total_projects_count = process_projects_count + queued_projects_count + done_projects_count;
   DB::Close();

   // Add this to the message text
   text += StringFormat("DB: %s | %d Projects (Process: %d, Queued: %d, Done: %d)\n",
                        m_fileName,
                        total_projects_count,
                        process_projects_count,
                        queued_projects_count,
                        done_projects_count
                       );

   // If there is an active project 
   if(process_projects_count > 0) {
      // Add information about the current task to the message text
      text += m_task.Text();

      // And the total number of tasks in the queue
      if(m_totalTasks)
         text += StringFormat(
                    "Total tasks in queue: %d\n",
                    m_totalTasks);
   }

   return text;
}

Obtendremos información sobre la tarea actual llamando al método Text() de la clase COptimizerTask. Obtengamos la información sobre el proyecto, la etapa y el trabajo a los que pertenece la tarea de optimización actual en el archivo Adwizard/Optimization/OptimizerTask.mqh. También calcularemos el tiempo transcurrido desde que se inició la tarea y el tiempo restante hasta su finalización, si se especifica el tiempo máximo permitido para completar la tarea:

//+------------------------------------------------------------------+
//| Current task info                                                |
//+------------------------------------------------------------------+
string COptimizerTask::Text() {
   string text = "";

   // If there is an active task
   if(m_params.id_task) {
      DB::Connect(m_fileName);

      // Add information about the project
      text += StringFormat("═════════════════════════════════════════════════════════════════════════\n"
                           "PROJECT: %s v. %s\n%s\n\n",
                           DB::GetValue("SELECT name FROM projects WHERE status = 'Process' LIMIT 1"),
                           DB::GetValue("SELECT version FROM projects WHERE status = 'Process'  LIMIT 1"),
                           DB::GetValue("SELECT description FROM projects WHERE status = 'Process'  LIMIT 1")
                          );

      // Request to get all information about the task
      string query = "SELECT s.name, s.expert, s.from_date, s.to_date, "
                     "  j.symbol, j.period, t.optimization_criterion, t.start_date, "
                     "  time(max_duration, 'unixepoch') AS max_duration,"
                     "  time(unixepoch('now') - unixepoch(t.start_date), 'unixepoch') AS elapsed_time,"
                     "  time(MAX(0, max_duration - (unixepoch('now') - unixepoch(t.start_date))), 'unixepoch') AS remaining_time"
                     "  FROM stages s"
                     "       JOIN"
                     "       projects p ON s.id_project = p.id_project AND"
                     "                     p.status = 'Process' AND"
                     "                     s.expert IS NOT NULL"
                     "     JOIN jobs j ON j.id_stage = s.id_stage"
                     "     JOIN tasks t ON t.id_job = j.id_job AND t.status = 'Process';";

      // Execute the request
      int request = DatabasePrepare(DB::Id(), query);

      struct Row {
         string stage_name;
         string expert_name;
         string from_date;
         string to_date;
         string symbol;
         string timeframe;
         int optimization_criterion;
         string start_date;
         string max_duration;
         string elapsed_time;
         string remainig_time;
      } row;

      // If there is no error
      if(request != INVALID_HANDLE) {

         // Read data from the first line of the result and add it to the text
         if(DatabaseReadBind(request, row)) {
            text += StringFormat("TASK #%I64u:\n"
                                 " %10.10s │ %14.14s │ %-23s │ %6.6s │ %-3.3s │ %15.15s │ %-10.10s │ %-10.10s\n"
                                 "────────────┼────────────────┼─────────────────────────┼────────┼─────┼─────────────────┼────────────┼─────────────\n"
                                 " %10.10s │ %14.14s │ %s - %s │ %6.6s │ %-3.3s │ %15.15s │ %-10.10s │ %-10.10s \n\n"
                                 "═════════════════════════════════════════════════════════════════════════\n",
                                 m_id,
                                 "Stage",
                                 "Expert",
                                 "Testing period",
                                 "Symbol",
                                 "TF",
                                 "Criterion",
                                 "Max Durat.",
                                 "Remaining",
                                 row.stage_name,
                                 row.expert_name,
                                 row.from_date,
                                 row.to_date,
                                 row.symbol,
                                 row.timeframe,
                                 s_criterionNames[row.optimization_criterion],
                                 m_params.max_duration ? row.max_duration : "Unlimited",
                                 row.remainig_time
                                );
         }
      }
      DatabaseFinalize(request);

      DB::Close();
   }

   return text;
}

Organizaremos los datos obtenidos sobre la tarea en forma de tabla de texto. En este momento, ya se han completado las modificaciones necesarias para garantizar esta funcionalidad, por lo que podemos pasar a las pruebas.


Pruebas

Ejecutemos el EA de optimización SimpleCandles/Optimization/Optimization.ex5. Veremos algo como esto:

Para esta captura de pantalla, hemos establecido el tiempo máximo de ejecución permitido para una sola tarea de optimización en 3 minutos (180 segundos). Llevaremos a cabo optimizaciones en tres símbolos y dos marcos temporales sobre un periodo de cuatro meses.

En la primera etapa, para cada combinación de símbolo y marco temporal, ejecutaremos la optimización tres veces. Por lo tanto, en total tendremos que completar 2 símbolos * 3 marcos temporales * 3 optimizaciones = 18 tareas en la primera etapa.

En la segunda etapa, realizaremos una ejecución de optimización para seleccionar un buen grupo para cada combinación de símbolo y marco temporal. Por lo tanto, en la segunda etapa tendremos que completar 2 símbolos * 3 marcos temporales = 6 tareas.

En la tercera etapa se realiza una última tarea, por lo que el número total de tareas es 18 + 6 + 1 = 25. Establecemos un tiempo máximo para todas las tareas salvo la última. Si estimamos, a grandes rasgos, que el tiempo de ejecución de la última tarea es de 3 minutos, podemos calcular cuánto durará todo el proceso de la cadena automatizada de optimización: 25 * 3 = 75 minutos = 1 hora y 15 minutos. Esto es, por supuesto, mucho menos que días o semanas. Pero probemos con una opción aún más extrema.

Limitaremos el tiempo máximo de ejecución a solo 20 segundos. En este caso, la duración total del proceso de optimización automática se reducirá aún más y será de unos 25 × 20 segundos = 500 segundos = 8 minutos y 20 segundos.

Tras este breve periodo de tiempo, todas las tareas de optimización habrán finalizado. En el terminal MetaTrader 5, donde hemos ejecutado este proceso, podemos ver los resultados de la última tarea de la tercera etapa, que consistió en una pasada única del asesor experto (EA) que combina 2 símbolos * 3 marcos temporales * 8 instancias por grupo = 48 instancias individuales de la estrategia de trading SimpleCandles:

Estos resultados aún no se han normalizado, lo que significa que la reducción del capital durante estos cuatro meses fue inferior a 500 USD, frente a un máximo previsto de 1 000 USD (10 %). Por lo tanto, podemos aumentar el volumen de las posiciones abiertas aproximadamente al doble sin superar una reducción del capital del 10 % durante este periodo. Esta operación ya se ha completado al final de la tercera etapa, y la información sobre el tamaño de las posiciones, teniendo en cuenta esta normalización, ya se ha guardado en la base de datos independiente del EA final.

Además, en la tercera etapa no se utilizó ni un gestor de riesgos de cierre ni un gestor de cierre.

Según la convención adoptada anteriormente, el nombre del archivo de la base de datos del EA final se genera siguiendo un algoritmo estrictamente definido a partir del nombre del proyecto, el número mágico especificado en los parámetros del proyecto y el sufijo «test». En nuestro caso concreto, una vez finalizado el proceso de optimización automática, se creó un archivo llamado SimpleCandles-27183.test.db.sqlite en la carpeta de datos común de los terminales.

Echemos un vistazo al contenido de la base de datos del EA final. Contiene cuatro tablas, pero por ahora solo nos interesan las dos últimas:

La tabla strategy_groups contiene una entrada correspondiente al grupo de estrategias creado con el identificador id_group=1. Si volvemos a ejecutar la cadena automatizada de optimización para el mismo proyecto, se añadirán nuevas filas con ID diferentes a esta tabla. Necesitamos el valor del identificador del grupo de estrategias para especificarlo en los parámetros del EA final. En la tabla strategies, hemos añadido instancias individuales de estrategias de trading que pertenecen a un grupo de estrategias específico. Las tablas restantes serán utilizadas por el EA final cuando opere en la cuenta de trading.

Compilemos el archivo SimpleCandles/SimpleCandles.mq5 del EA final y ejecutemos las pruebas con el ID del grupo de estrategias existente, sin actualización automática, con el gestor de riesgos y el gestor de cierre desactivados, y con el número mágico 27183:

Obtendremos los siguientes resultados:

Ahora, la reducción del capital ya se acerca al valor máximo previsto especificado en la configuración del EA final y, en consecuencia, el beneficio ha aumentado aproximadamente al doble, en proporción al aumento del tamaño de las posiciones.



Conclusión

El trabajo realizado en este artículo ha mejorado considerablemente nuestra cadena automatizada de optimización y ha facilitado su uso. No hemos añadido nuevas funcionalidades a la lógica de negociación, sino que nos hemos centrado en resolver los problemas prácticos con los que nos hemos encontrado al utilizar el sistema.

Hemos seguido separando la parte de biblioteca de la parte del proyecto. Ahora, para crear un nuevo proyecto basado en una estrategia de negociación diferente, basta con describir sus parámetros en la parte del proyecto, sin necesidad de realizar cambios en el núcleo común. Este proceso aún no ha concluido y continuará en el futuro.

La implementación de un mecanismo para limitar el tiempo de ejecución de las tareas de optimización nos proporcionó una herramienta muy eficaz para controlar la duración de toda la cadena. Ahora podemos equilibrar de forma flexible la profundidad de la optimización y el tiempo dedicado, deteniendo los procesos en las dos primeras etapas una vez que se alcanza un límite razonable, lo cual es fundamental para la realización de pruebas rápidas y el desarrollo iterativo.

La integración del componente CConsoleDialog para mostrar información detallada sobre la optimización transformó la supervisión del proceso, pasando del seguimiento de los ID de las tareas a una supervisión cómoda y visual. Ahora podemos ver en tiempo real en qué etapa, con qué símbolo y en qué marco temporal está funcionando la cadena, cuánto tiempo queda para que finalice la tarea actual y cuál es el progreso general.

De este modo, se demostró con éxito en la práctica todo el ciclo: desde la creación de una base de datos del proyecto y la puesta en marcha de la cadena hasta la obtención de un EA final con parámetros normalizados, listo para las pruebas. Una ejecución extrema con límites de tiempo muy ajustados demostró claramente la viabilidad de este enfoque: incluso con duraciones muy limitadas para las tareas individuales, el sistema es capaz de completar un ciclo de optimización completo y ofrecer un resultado funcional. Esto abre posibilidades para crear prototipos rápidamente y probar nuevas estrategias de trading.

¡Gracias por su atención! ¡Hasta pronto!


Advertencia importante

Todos los resultados presentados en este artículo y en todos los artículos anteriores de la serie se basan únicamente en datos de pruebas históricas y no constituyen garantía alguna de obtener beneficios en el futuro. El trabajo realizado en el marco de este proyecto es de carácter investigativo. Cualquier persona puede utilizar todos los resultados publicados bajo su propia responsabilidad.


Contenido del archivo
#
Nombre
VersiónDescripciónCambios recientes
 SimpleCandles Carpeta de trabajo del proyecto
(dentro de MQL5/Shared Projects)
 
1SimpleCandles.mq5
1.03
Asesor experto (EA) final para el funcionamiento en paralelo de varios grupos de estrategias modelo. Los parámetros se obtendrán de la biblioteca de grupos integrada.
Parte 29
 └ Optimization
 Carpeta de los asesores expertos (EA) de optimización del proyecto 
2CreateProject.mq51.05EA para crear un proyecto con etapas, trabajos y tareas de optimización.
Parte 29
3Optimization.mq51.03
EA para la optimización automática de proyectos
Parte 29
4Stage1.mq51.02
EA para la optimización de una instancia individual de una estrategia de trading (etapa 1)
Parte 25
5Stage2.mq51.01
EA para la optimización de un grupo de instancias de estrategias de trading (etapa 2)
Parte 25
6Stage3.mq51.01
El EA que guarda un grupo de estrategias generado y normalizado en una base de datos de EA con un nombre determinado.Parte 25
 └ Strategies Carpeta de estrategias del proyecto
Parte 25
7SimpleCandlesStrategy.mqh
1.03
Clase de la estrategia de trading SimpleCandles
Parte 29
 Adwizard Carpeta de la biblioteca Adwizard
(dentro de MQL5/Shared Projects)
 
 └ Base
 Clases base de las que heredan otras clases del proyecto 
8Advisor.mqh1.04Clase base del asesor experto (EA)Parte 10
9Factorable.mqh
1.06
Clase base de los objetos creados a partir de una cadena
Parte 28
10FactorableCreator.mqh
1.00Clase de creadores que asocian nombres y constructores estáticos de las clases descendientes de CFactorableParte 24
11Interface.mqh1.01
Clase base para visualizar diversos objetos
Parte 4
12Receiver.mqh
1.04Clase base para convertir volúmenes abiertos en posiciones de mercado
Parte 12
13Strategy.mqh
1.04
Clase base de las estrategias de trading
Parte 10
 └ Database
 Archivos para gestionar todos los tipos de bases de datos utilizados por los asesores expertos (EA) del proyecto
 
14Database.mqh1.13Clase para gestionar la base de datosParte 29
15db.adv.schema.sql1.00
Estructura de la base de datos del EA finalParte 22
16db.cut.schema.sql
1.00Estructura de la base de datos de optimización truncada
Parte 22
17db.opt.schema.sql
1.06Estructura de la base de datos de optimización
Parte 29
18Storage.mqh1.01
Clase para gestionar el almacenamiento clave-valor del EA final en la base de datos del EA
Parte 23
 └ Experts
 Archivos con partes comunes de los EA de distintos tipos utilizados
 
19Expert.mqh1.24El archivo de biblioteca para el EA final. Los parámetros del grupo se pueden extraer de la base de datos del EA
Parte 28
20Optimization.mqh1.06Archivo de biblioteca para el EA que gestiona el lanzamiento de tareas de optimización
Parte 29
21Stage1.mqh
1.19Archivo de biblioteca para el EA que optimiza una instancia individual de una estrategia de trading (etapa 1)
Parte 23
22Stage2.mqh1.04Archivo de biblioteca para el EA que optimiza un grupo de instancias de estrategias de trading (etapa 2)Parte 23
23Stage3.mqh
1.04Archivo de biblioteca para el EA que guarda un grupo de estrategias generado y normalizado en una base de datos de EA con un nombre determinado.Parte 23
 └ Optimization
 Clases encargadas de la optimización automática
 
24OptimizationJob.mqh1.00Clase de trabajo de una etapa del proyecto de optimización
Parte 25
25OptimizationProject.mqh1.00Clase de proyecto de optimizaciónParte 25
26OptimizationStage.mqh1.00Clase de etapa del proyecto de optimizaciónParte 25
27OptimizationTask.mqh1.01Clase de tarea de optimización (creación)Parte 29
28Optimizer.mqh
1.04Clase para el gestor de optimización automática del proyecto
Parte 29
29OptimizerTask.mqh
1.06
Clase de tarea de optimización (cadena)
Parte 29
 └ Strategies Ejemplos de estrategias de trading utilizadas para mostrar cómo funciona el proyecto
 
24HistoryStrategy.mqh
1.00Clase de estrategia de trading para reproducir el historial de operaciones
Parte 16
25SimpleVolumesStrategy.mqh
1.11
Clase de estrategia de trading que utiliza volúmenes de ticks
Parte 22
 └ Utils
 Utilidades auxiliares, macros para reducir el código

26ConsoleDialog.mqh1.01Clase para mostrar datos de texto en un gráficoParte 28
26ExpertHistory.mqh1.00Clase para exportar el historial de operaciones a un archivoParte 16
27Macros.mqh1.07Macros útiles para operaciones con matricesParte 26
28MTTester.mqh—
Archivo de la biblioteca MultiTester para trabajar con el probador de estrategias
Parte 28
29NewBarEvent.mqh1.00Clase para detectar una nueva barra de un símbolo concretoParte 8
30SymbolsMonitor.mqh1.01Clase para obtener información sobre instrumentos de negociación (símbolos)Parte 28
 └ Virtual
 Clases para crear diversos objetos que tienen en común el uso de un sistema de órdenes y posiciones de negociación virtuales

31Money.mqh1.01Clase básica de gestión monetaria
Parte 12
32TesterHandler.mqh1.07Clase para gestionar los eventos de optimizaciónParte 23
33VirtualAdvisor.mqh1.12Clase del EA que gestiona posiciones virtuales (órdenes)Parte 28
34VirtualChartOrder.mqh1.02Clase de posición virtual con representación gráficaParte 28
35VirtualCloseManager.mqh1.00Clase del gestor de cierreParte 28
36VirtualHistoryAdvisor.mqh1.00Clase del EA que reproduce el historial de operacionesParte 16
37VirtualInterface.mqh1.00Clase de la interfaz gráfica de usuario del EAParte 4
38VirtualOrder.mqh1.09Clase de órdenes y posiciones virtualesParte 22
39VirtualReceiver.mqh1.04Clase para convertir volúmenes abiertos en posiciones de mercado (receptor)Parte 23
40VirtualRiskManager.mqh1.06Clase de gestión de riesgos (gestor de riesgos)Parte 28
41VirtualStrategy.mqh1.09Clase de una estrategia de trading con posiciones virtualesParte 23
42VirtualStrategyGroup.mqh1.04Clase de grupos de estrategias de tradingParte 28
43VirtualSymbolReceiver.mqh1.00Clase de receptor de símbolosParte 3
 Common/Files Carpeta de datos común de los terminales de MetaTrader 5 
44article.17607.db.sqlite—Base de datos de optimización
Parte 29
45SimpleCandles-27183.test.db.sqlite
—
Base de datos del EA final
Parte 29

El código fuente también está disponible en SimpleCandles y Adwizard

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/17607

Archivos adjuntos |
MQL5.zip (2157.63 KB)
Características del Wizard MQL5 que debe conocer (Parte 81): Uso de patrones de Ichimoku y ADX-Wilder con aprendizaje por inferencia mediante β-VAE Características del Wizard MQL5 que debe conocer (Parte 81): Uso de patrones de Ichimoku y ADX-Wilder con aprendizaje por inferencia mediante β-VAE
Este artículo da continuidad a la «Parte 80», en la que examinamos la combinación de Ichimoku y ADX en un framework de aprendizaje por refuerzo. Ahora centraremos nuestra atención en el aprendizaje por inferencia. Como ya hemos comentado, Ichimoku y ADX son complementarios; sin embargo, vamos a repasar las conclusiones del último artículo en relación con el uso de la cadena de procesamiento. Para nuestro aprendizaje por inferencia, utilizaremos el algoritmo beta de un autocodificador variacional (VAE). Además, seguiremos utilizando una implementación de una clase de señal personalizada diseñada para integrarse con el Asistente de MQL5.
Creación de sistemas de trading basados en IA en MQL5 (Parte 3): Actualización a una interfaz de usuario orientada a un único chat con desplazamiento Creación de sistemas de trading basados en IA en MQL5 (Parte 3): Actualización a una interfaz de usuario orientada a un único chat con desplazamiento
En este artículo, actualizaremos el programa en MQL5 integrado con ChatGPT para dotarlo de una interfaz de usuario orientada a un único chat con desplazamiento, mejorando la visualización del historial de conversaciones mediante marcas de tiempo y desplazamiento dinámico. El sistema se basa en el análisis de JSON para gestionar mensajes de varios turnos y admite modos de barra de desplazamiento personalizables y efectos al pasar el cursor para mejorar la interacción con el usuario.
Características del Wizard MQL5 que debe conocer (Parte 82): Uso de patrones del TRIX y del WPR con aprendizaje por refuerzo mediante una red Q profunda (DQN) Características del Wizard MQL5 que debe conocer (Parte 82): Uso de patrones del TRIX y del WPR con aprendizaje por refuerzo mediante una red Q profunda (DQN)
En el último artículo, analizamos la combinación de Ichimoku y ADX en el framework del aprendizaje por inferencia. En este artículo retomamos el tema del aprendizaje por refuerzo cuando se utiliza junto con un par de indicadores que ya analizamos en la «Parte 68». El indicador TRIX y el rango porcentual de Williams. El algoritmo que usaremos en este análisis será la red Q profunda (DQN) con regresión por cuantiles. Como es habitual, presentamos este enfoque como una clase de señal personalizada diseñada para su implementación con el Asistente de MQL5.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 43): Probabilidad de las velas japonesas y rupturas Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 43): Probabilidad de las velas japonesas y rupturas
Mejore su análisis de mercado con el EA «Candlestick Probability», nativo de MQL5, una herramienta ligera que transforma las barras de precios sin procesar en información sobre probabilidades en tiempo real y específica para cada instrumento. Clasifica los patrones pinbar, envolvente y doji al cierre de la barra, utiliza un filtrado basado en el ATR y ofrece confirmación opcional de las rupturas. El EA calcula los porcentajes brutos y ponderados por volumen de continuación del precio en la dirección indicada por el patrón, lo que le ayuda a comprender el resultado típico de cada patrón en símbolos y marcos temporales concretos. Los marcadores en el gráfico, un panel de control compacto y los botones interactivos facilitan la validación y permiten centrarse en lo esencial. Exporte registros CSV detallados para realizar pruebas sin conexión. Úselos para elaborar perfiles de probabilidad, optimizar estrategias y convertir el reconocimiento de patrones en una ventaja cuantificable.