English Русский Português
preview
Desarrollamos un asesor experto multidivisas (Parte 23): Ordenando la cadena de etapas de optimización automática de proyectos (II)

Desarrollamos un asesor experto multidivisas (Parte 23): Ordenando la cadena de etapas de optimización automática de proyectos (II)

MetaTrader 5Probador |
196 0
Yuriy Bykov
Yuriy Bykov

Introducción

En una de las partes anteriores de la serie ya abordamos esta cuestión. Esta permitía elegir un vector más correcto de desarrollo ulterior del proyecto. En lugar de generar manualmente todas las tareas realizadas dentro de un proyecto en la base de datos de optimización, ahora tenemos una herramienta más cómoda: un script para crear un proyecto de optimización. En concreto, se trata más bien de una plantilla que puede adaptarse fácilmente para crear proyectos de optimización para distintas estrategias comerciales.

En la parte, conseguimos una solución bastante viable que nos permite ejecutar los proyectos de optimización creados con la exportación de los nuevos grupos de estrategias comerciales seleccionados directamente a la nueva base de datos. Esta base de datos se ha denominado base de datos de experto para distinguirla de la base de datos de optimización utilizada anteriormente (en las versiones completa y reducida). La base de datos del asesor experto puede ser utilizada por algún asesor experto que trabaje en la cuenta comercial, actualizando los ajustes de los sistemas comerciales utilizados sin necesidad de recompilación. Queda por comprobar si este mecanismo es correcto. No obstante, ya podemos decir que este enfoque simplifica el trabajo del transportador en su conjunto. Al fin y al cabo, antes habíamos previsto añadir algunas etapas más a las tres existentes del transportador:

  • Exportación de la biblioteca para obtener ExportedGroupsLibrary.mqh en la carpeta de datos (Stage4).
  • Copiado del archivo a la carpeta de trabajo (Stage5, Python o DLL) o mejora del paso anterior para exportar directamente a la carpeta de trabajo.
  • Compilación del asesor experto final (Stage6, Python).
  • Inicio del terminal con una nueva versión del asesor experto final.

Ahora esos pasos ya no son necesarios. Al mismo tiempo, nos hemos librado de una gran desventaja: sería imposible comprobar la corrección de este mecanismo de actualización automática de los ajustes en el simulador de estrategias. La presencia de un paso de recompilación resulta incompatible con el hecho de que el código del asesor experto compilado no pueda modificarse durante una única pasada del simulador.

Pero lo más importante es que intentaremos dar un paso importante hacia la simplificación del uso de todo el código escrito para optimizar estrategias aleatorias e intentaremos describir un algoritmo paso a paso con las acciones para lograrlo.


Trazando el camino

Empezaremos introduciendo cambios en la estructura de archivos del proyecto, cosa que deberíamos haber hecho tiempo atrás. Ahora están en una sola carpeta, lo que, por un lado, simplifica la transferencia y el uso de todo el código en un nuevo proyecto, aunque, por otro, en el proceso de desarrollo continuo tenemos varias carpetas de trabajo casi idénticas de proyectos para diferentes estrategias comerciales, cada una de las cuales debe actualizarse por separado. Por consiguiente, dividiremos todo el código en una parte de biblioteca, que será la misma para todos los proyectos, y una parte de proyecto, que contendrá código concreto para los distintos proyectos.

A continuación, implementaremos una comprobación para que si aparecen nuevos grupos de estrategias durante el funcionamiento del asesor experto final, este sea capaz de cargar correctamente los parámetros actualizados y continuar su trabajo. Como de costumbre, empezaremos por modelar el comportamiento deseado en un asesor experto que se ejecute en el simulador de estrategias. Si los resultados son satisfactorios allí, entonces podremos usarlo en los asesores expertos finales que ya no funcionan en el simulador.

¿Qué necesitaremos para ello? En la parte anterior, no hemos implementado el almacenamiento de información sobre las fechas de finalización del intervalo de optimización y de finalización de la ejecución del canal de optimización en la base de datos del asesor experto. Ahora necesitaremos esta información, de lo contrario, al realizar una pasada en el simulador, el asesor experto final no será capaz de entender si este grupo de estrategias ya se ha formado en una determinada fecha modelada, o no todavía.

También será necesario perfeccionar el asesor experto final para que pueda ejecutar su propia reinicialización cuando aparezcan nuevos grupos de estrategias en la base de datos del asesor experto. Ahora mismo, esa funcionalidad no existe. Aquí sería útil mostrar alguna información sobre el grupo actual de estrategias comerciales, para que podamos verificar visualmente que se ha cambiado con éxito de un grupo a otro. Sería más conveniente ver esta información directamente en el gráfico en el que se está ejecutando el asesor experto, pero, obviamente, podemos utilizar la muestra habitual en el diario de registro del terminal.

Y por último, mostraremos una descripción del algoritmo general de trabajo con las herramientas desarrolladas actualmente.

¡Manos a la obra!


Cambiamos a una estructura de archivos diferente

En todas las partes anteriores, el desarrollo se realizó en una única carpeta de trabajo del proyecto. Los archivos existentes se modificaban, y luego se añadían otros nuevos de vez en cuando. A veces, los archivos que habían perdido relevancia para el proyecto se eliminaban u "olvidaban". Para trabajar con una posible estrategia comercial (por ejemplo, la estrategia llamada SimpleVolumes usada como ejemplo en los artículos) este enfoque estaba bastante justificado. Pero al ampliar el mecanismo de optimización automática a otras estrategias comerciales, tuvimos que crear una copia completa de la carpeta de trabajo del proyecto y luego cambiar solo una pequeña parte de los archivos que contenía.

A medida que crecía el número de estrategias comerciales conectadas de esta forma (y conforme aumentaba el número de carpetas de trabajo diferentes), mantener actualizado el código de todas ellas requería cada vez más tiempo. Por ello, decidimos poner la parte del código, que debe ser el mismo para todas las carpetas de trabajo, en una carpeta de biblioteca separada situada en la carpeta MQL5/Include. Como nombre de la carpeta compartida para los archivos de la biblioteca elegimos Advisor, y le añadimos un componente único para que no entrara en conflicto con una posible carpeta existente con ese nombre. Ahora los archivos de la biblioteca estarán ubicados en la carpeta MQL5/Include/antekov/Advisor/.

Una vez transferidos todos los archivos, empezamos a sistematizarlos. Decidimos organizar todos los archivos en subcarpetas que reflejaran algún propósito común para los archivos que contenían. Esto requirió cierto trabajo con las directivas para conectar algunos archivos a otros archivos, ya que sus ubicaciones relativas cambiaron. Pero al final, logramos compilar con éxito ambos asesores expertos y todos los archivos de biblioteca por separado.

Este es el aspecto de la estructura de archivos tras la modificación:

Fig. 1. Estructura de los archivos de la biblioteca Advisor

Como podemos ver, hemos separado varios grupos de archivos colocándolos en las siguientes subcarpetas:

  • Base. Clases básicas de las que heredan otras clases del proyecto.
  • Database. Archivos para trabajar con todo tipo de bases de datos utilizadas por los asesores de proyectos.
  • Experts. Archivos con partes comunes de asesores usados de distintos tipos.
  • Optimization. Clases responsables del funcionamiento de la optimización automática.
  • Strategies. Ejemplos de estrategias comerciales usadas para demostrar el trabajo del proyecto.
  • Utils. Utilidades auxiliares, macros para reducir el código.
  • Virtual. Clases para crear diversos objetos unidos usando el sistema de órdenes y posiciones comerciales virtuales.

De todas las subcarpetas anteriores, solo destaca una que merece la pena mencionar. Esta es la carpeta Experts. Si comparamos la composición de los archivos de la fig. 1 con la composición de archivos de la parte anterior, podemos observar que solo esta carpeta contiene archivos que antes no estaban allí. Al principio se podría pensar que simplemente hemos renombrado parcialmente los archivos de los asesores expertos usados y los hemos movido aquí, pero debemos considerar que su extensión no es *.mq5, sino *.mqh. Pero antes de examinarlos con más detalle, echemos un vistazo a lo que queda en la carpeta de proyectos individuales para cierta estrategia comercial.

Haremos una carpeta separada dentro de MQL5/Experts/, que podemos nombrar como deseemos. Contendrá los archivos de todos los asesores expertos usados:

Fig. 2. Estructura de los archivos del proyecto que utiliza la biblioteca Advisor

La finalidad de estos archivos es la siguiente:

  • CreateProject.mq5 — asesor para crear un proyecto de optimización automática en la base de datos de optimización. Cada proyecto de la base de datos se representa en tres fases que constan de una o varias actividades. Cada trabajo consta de una o varias tareas de optimización realizadas por los asesores expertos de las etapas.

  • HistoryReceiverExpert.mq5 — asesor experto para reproducir el historial de operaciones previamente guardado. Hace tiempo que no lo usamos, ya que solo se hizo con el fin de comprobar la repetibilidad de los resultados al cambiar de bróker. No es necesario para que funcione la optimización automática, por lo que podemos eliminarlo si así lo deseamos.

  • Optimización.mq5 — asesor experto que lanza las tareas del proyecto de optimización automática. Al proceso mismo de ejecución secuencial de tales tareas lo denominamos pipeline de optimización automática.

  • SimpleVolumes.mq5 — asesor experto de resumen que combina muchas instancias individuales de estrategias comerciales del tipo SimpleVolumes. Tomará información sobre la composición de estas instancias de la base de datos del experto. El asesor experto de la tercera etapa del proceso de optimización automática introducirá dicha información en la base de datos del asesor experto.

  • Stage1.mq5 — asesor experto de la primera etapa del proceso de optimización automática. Realizará la optimización de una única instancia de una estrategia comercial.

  • Stage2.mq5 — asesor experto de la segunda etapa del proceso de optimización automática. El proceso de optimización selecciona, a partir de muchas instancias individuales buenas obtenidas en la primera etapa, un grupo de un pequeño número de instancias (normalmente 8 o 16) que obtiene los mejores resultados en cuanto al beneficio normalizado cuando trabajan juntas.

  • Stage3.mq5 — asesor experto de la tercera etapa del proceso de optimización automática. Combina todos los grupos obtenidos en la segunda etapa, normaliza los tamaños de las posiciones y guarda el grupo final con un factor de escala en la base de datos del asesor experto indicada en los ajustes.

Entre los archivos de asesor experto de la lista, solo CreateProject.mq5 ha mantenido su contenido. Todos los demás asesores expertos contienen básicamente solo el comando para incluir el archivo mqh correspondiente de la biblioteca Advisor, que se encuentran en la carpeta MQL5/Include/antekov/Advisor/Experts. Además, SimpleVolumes.mq5 y HistoryReceiverExpert.mq5 usan el mismo archivo de inclusión Expert.mqh. Como la práctica ha demostrado, no necesitamos escribir código diferente para la segunda y tercera etapa de asesores expertos para distintas estrategias comerciales. Y para el asesor experto de la primera etapa toda la diferencia se reduce únicamente a los distintos parámetros de entrada y a la composición de la cadena de inicialización necesaria a partir de sus valores. Todo lo demás también será igual.

Por consiguiente, solo CreateProject.mq5 requerirá una modificación más sustancial por ahora al cambiar a un proyecto con una estrategia comercial diferente. En el futuro, intentaremos extraer también una parte común.

Ahora vamos a ver qué cambios debemos introducir en los archivos de la biblioteca para implementar la actualización automática de la asesor experto final.


Fechas de finalización

Recordemos que en la última parte lanzamos cuatro proyectos de optimización casi idénticos. La diferencia entre ellos era la fecha de finalización del intervalo de optimización de las estrategias comerciales de instancia única. La composición de los instrumentos comerciales, los marcos temporales y otros parámetros no difirieron. Como resultado, aparecieron las siguientes entradas en la tabla strategy_groups de la base de datos del asesor experto:

Como hemos añadido la fecha final del intervalo de optimización al nombre del grupo, podemos usar esta información para ver a qué grupo corresponde cada fecha de finalización. Pero tiene que ser algo que un asesor experto pueda entender. Específicamente hemos introducido en esta tabla dos campos para guardar estas fechas que se deben rellenar al crear los registros, e incluso hemos preparado un lugar en el código donde esto debe hacerse:

//+------------------------------------------------------------------+
//| Export an array of strategies to the specified EA database       |
//| as a new group of strategies                                     |
//+------------------------------------------------------------------+
void CTesterHandler::Export(CStrategy* &p_strategies[], string p_groupName, string p_advFileName) {
// Connect to the required EA database
   if(DB::Connect(p_advFileName, DB_TYPE_ADV)) {
      string fromDate = "";   // Start date of the optimization interval
      string toDate = "";     // End date of the optimization interval

      // Create an entry for a new strategy group
      string query = StringFormat("INSERT INTO strategy_groups VALUES(NULL, '%s', '%s', '%s', NULL)"
                                  " RETURNING rowid;",
                                  p_groupName, fromDate, toDate);
      ulong groupId = DB::Insert(query);

      // ...
   }
}

Las fechas de inicio y finalización del intervalo de optimización se almacenan en la base de datos en la tabla de stages. Por ello, podemos obtenerlos desde allí ejecutando la consulta SQL correspondiente en este lugar del código. Pero este enfoque no ha resultado el más óptimo, porque ya hemos escrito un código que ejecuta una consulta SQL para obtener información sobre estas fechas. Esto sucedía en el asesor de optimización automática. Tenía que recuperar información de la base de datos sobre la siguiente tarea de optimización. En esta información se deben incluir sin duda las fechas que necesitamos. Vamos a aprovecharnos de ello. 

Necesitaremos crear un objeto de la clase COptimizerTask, transmitiendo a su constructor el nombre de la base de datos de optimización. El nombre lo tenemos en el campo estático de la clase CTesterHandler::s_fileName. Mientras que el otro campo estático CTesterHandler::s_idTask tiene el ID de la tarea de optimización actual. Lo transmitiremos al método de carga de los datos de la tarea de optimización. A continuación, las fechas necesarias pueden obtenerse de los campos correspondientes de la estructura m_params del objeto de tarea.

//+------------------------------------------------------------------+
//| Export an array of strategies to the specified EA database       |
//| as a new group of strategies                                     |
//+------------------------------------------------------------------+
void CTesterHandler::Export(CStrategy* &p_strategies[], string p_groupName, string p_advFileName) {
// Create an optimization task object
   COptimizerTask task(s_fileName);
// Load the data of the current optimization task into it
   task.Load(CTesterHandler::s_idTask);

// Connect to the required EA database
   if(DB::Connect(p_advFileName, DB_TYPE_ADV)) {
      string fromDate = task.m_params.from_date; // Start date of the optimization interval
      string toDate = task.m_params.to_date;     // End date of the optimization interval

      // Create an entry for a new strategy group
      string query = StringFormat("INSERT INTO strategy_groups VALUES(NULL, '%s', '%s', '%s', NULL)"
                                  " RETURNING rowid;",
                                  p_groupName, fromDate, toDate);
      ulong groupId = DB::Insert(query);

      // ...
   }
}

Guardaremos los cambios realizados en el archivo TesterHandler.mqh en la subcarpeta Virtual de la biblioteca.

Crearemos nuevamente varios proyectos utilizando el asesor CreateProject.ex5. Para acelerar el proceso, haremos que el intervalo de optimización sea pequeño (4 meses). Las fechas de inicio y finalización del intervalo de optimización de cada proyecto posterior se adelantarán un mes. Como resultado, obtendremos lo siguiente:

Como podemos ver, ahora cada grupo de la base de datos del asesor experto contiene la fecha final del intervalo de optimización. Tenga en cuenta que esta fecha se tomará del intervalo de la tarea de la tercera etapa. Para que todo sea correcto, las fechas de los intervalos de las tres etapas deberán ser las mismas. Esto se posibilita en el asesor de creación de proyectos.


Modificación de la asesor experto final

Antes de empezar a implementar la actualización automática del grupo de estrategias usadas en el asesor experto final, vamos a echar un vistazo a los cambios causados por la transición a la nueva estructura de los archivos del proyecto. Como ya hemos señalado, la asesor experto definitiva se presenta ahora en dos archivos. El archivo principal se encuentra en la carpeta del proyecto y se llama SimpleVolumes.mq5. Aquí tenemos su código completo:

//+------------------------------------------------------------------+
//|                                                SimpleVolumes.mq5 |
//|                                 Copyright 2024-2025, Yuriy Bykov |
//|                            https://www.mql5.com/en/users/antekov |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024-2025, Yuriy Bykov"
#property link      "https://www.mql5.com/en/articles/16913"
#property description "The final EA, combining multiple instances of trading strategies:"
#property description " "
#property description "Strategies open a market or pending order when,"
#property description "the candle tick volume exceeds the average volume in the direction of the current candle."
#property description "If orders have not yet turned into positions, they are deleted at expiration time."
#property description "Open positions are closed only by SL or TP."
#property version "1.22"

#include <antekov/Advisor/Experts/Expert.mqh>

//+------------------------------------------------------------------+

En este código, esencialmente solo hay un comando para importar el archivo de biblioteca del asesor experto final. Este es precisamente uno de esos casos cuando lo más importante no es lo que es, sino lo que no es. Comparémoslo con el código del segundo asesor experto HistoryReceiverExpert.mq5:

//+------------------------------------------------------------------+
//|                                        HistoryReceiverExpert.mq5 |
//|                                 Copyright 2024-2025, Yuriy Bykov |
//|                            https://www.mql5.com/en/users/antekov |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024-2025, Yuriy Bykov"
#property link      "https://www.mql5.com/en/articles/16913"
#property description "The EA opens a market or pending order when,"
#property description "the candle tick volume exceeds the average volume in the direction of the current candle."
#property description "If orders have not yet turned into positions, they are deleted at expiration time."
#property description "Open positions are closed only by SL or TP."
#property version "1.01"

//+------------------------------------------------------------------+
//| Declare the name of the final EA.                                |
//| During the compilation, the function of generating               |
//| the initialization string from the current file will be used     |
//+------------------------------------------------------------------+
#define  __NAME__ MQLInfoString(MQL_PROGRAM_NAME)

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "::: Testing the deal history"
input string historyFileName_    = "SimpleVolumesExpert.1.19 [2021.01.01 - 2022.12.30]"
                                   " [10000, 34518, 1294, 3.75].history.csv";    // File with history

//+------------------------------------------------------------------+
//| Function for generating the strategy initialization string       |
//| from the inputs                                                  |
//+------------------------------------------------------------------+
string GetStrategyParams() {
   return StringFormat("class CHistoryStrategy(\"%s\")\n", historyFileName_);
}

#include <antekov/Advisor/Experts/Expert.mqh>

//+------------------------------------------------------------------+

Resaltaremos a color los tres bloques que no están en el archivo SimpleVolumes.mq5. Su presencia se tiene en cuenta en el archivo de biblioteca del asesor experto final Experts/Expert.mqh de la forma siguiente: si no se especifica ninguna constante con el nombre del asesor experto final, se declarará la función de formación de la cadena de inicialización, que la obtendrá de la base de datos del asesor experto. Si se indica un nombre, dicha función deberá declararse en el archivo anterior en el que se inserta este archivo de biblioteca.

// If the constant with the name of the final EA is not specified, then
#ifndef __NAME__
// Set it equal to the name of the EA file 
#define  __NAME__ MQLInfoString(MQL_PROGRAM_NAME)

//+------------------------------------------------------------------+
//| Function for generating the strategy initialization string       |
//| from the default inputs (if no name was specified).              | 
//| Import the initialization string from the EA database            |
//| by the strategy group ID                                         |
//+------------------------------------------------------------------+
string GetStrategyParams() {
// Take the initialization string from the new library for the selected group
// (from the EA database)
   string strategiesParams = CVirtualAdvisor::Import(
                                CVirtualAdvisor::FileName(__NAME__, magic_),
                                groupId_
                             );

// If the strategy group from the library is not specified, then we interrupt the operation
   if(strategiesParams == NULL && useAutoUpdate_) {
      strategiesParams = "";
   }

   return strategiesParams;
}
#endif

Además, en el archivo de biblioteca Experts/Expert.mqh, en la función de inicialización del asesor experto, se utilizará una de las posibles variantes de la función de formación de la cadena de inicialización:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
// ...

// Initialization string with strategy parameter sets
   string strategiesParams = NULL;

// Take the initialization string from the new library for the selected group
// (from the EA database)
   strategiesParams = GetStrategyParams();

// If the strategy group from the library is not specified, then we interrupt the operation
   if(strategiesParams == NULL) {
      return INIT_FAILED;
   }

// ...

// Successful initialization
   return(INIT_SUCCEEDED);
}

Así, si lo deseamos, podemos crear un asesor experto final que no usará la carga de la cadena de inicialización desde la base de datos del asesor experto. Para ello, bastará con declarar la constante __NAME__ y la función con la signatura en el archivo *.mq5. 

string GetStrategyParams()

Ahora, podemos iniciar la actualización automática.


Actualización automática

La primera variante de aplicación de la actualización automática puede que no sea la más bonita, pero servirá para empezar. Lo importante es que funcione. Los cambios necesarios en el archivo de biblioteca de la asesor experto final constan de dos partes.

En primer lugar, hemos cambiado ligeramente la composición de los parámetros de entrada eliminando la enumeración con el número de grupo de la antigua biblioteca y sustituyéndola por el identificador de grupo de la base de datos del asesor experto y añadiendo un parámetro lógico que permite realizar la actualización automática:

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "::: Use a strategy group"
sinput int        groupId_       = 0;     // - ID of the group from the new library (0 - last)
sinput bool       useAutoUpdate_ = true;  // - Use auto update?

input group "::: Money management"
sinput double expectedDrawdown_  = 10;    // - Maximum risk (%)
sinput double fixedBalance_      = 10000; // - Used deposit (0 - use all) in the account currency
input  double scale_             = 1.00;  // - Group scaling multiplier

// ...

En segundo lugar, hemos añadido el siguiente código a la nueva función de procesamiento de ticks, después de la línea resaltada:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   expert.Tick();

// If both are executed at the same time:
   if(groupId_ == 0                       // - no specific group ID specified
         && useAutoUpdate_                // - auto update enabled
         && IsNewBar(Symbol(), PERIOD_D1) // - a new day has arrived
         && expert.CheckUpdate()          // - a new group of strategies discovered
     ) {
      // Save the current EA state
      expert.Save();

      // Delete the EA object
      delete expert;

      // Call the EA initialization function to load a new strategy group 
      OnInit();
   }
}

Así, la actualización automática solo funcionará si los parámetros de entrada groupId_=0 y useAutoUpdate_=true. Si indicamos algún identificador de grupo distinto de cero, será el que se utilice durante todo el intervalo de prueba. En este caso, no habrá ninguna restricción sobre cuándo el asesor experto final puede ejecutar operaciones.

Si activamos la actualización automática, el asesor experto resultante solo ejecutará operaciones después de la fecha de finalización del intervalo de optimización del grupo más antiguo existente en la base de datos del asesor experto. Dicho mecanismo se implementará en el nuevo método de la clase CVirtualAdvisor::CheckUpdate():

//+------------------------------------------------------------------+
//| Check the presence of a new strategy group in the EA database    |
//+------------------------------------------------------------------+
bool CVirtualAdvisor::CheckUpdate() {
// Request to get strategies of a given group or the last group
   string query = StringFormat("SELECT MAX(id_group) FROM strategy_groups"
                               " WHERE to_date <= '%s'",
                               TimeToString(TimeCurrent(), TIME_DATE));

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

      // If there is no error
      if(request != INVALID_HANDLE) {
         // Data structure for reading a single string of a query result 
         struct Row {
            int      groupId;
         } row;

         // Read data from the first result string
         while(DatabaseReadBind(request, row)) {
            // Remember the strategy group ID
            // in the static property of the EA class
            return s_groupId < row.groupId;
         }
      } else {
         // Report an error if necessary
         PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
      }

      // Close the EA database
      DB::Close();
   }

   return false;
}

En este método, recuperaremos el mayor ID de grupo de la base de datos del asesor experto para el que la fecha de finalización del intervalo de optimización no sea superior a la fecha actual. Así, aunque un nuevo registro de grupo ya esté físicamente presente en la base de datos, pero el momento de su aparición (>= momento del final del intervalo de optimización) esté situado en el futuro en relación con el tiempo simulado actual del simulador de estrategias, no se recuperará como resultado de la consulta SQL usada.

Durante la inicialización, el asesor experto almacena el ID del grupo de estrategias cargado en un campo estático de la clase CVirtualAdvisor::s_groupId. Por consiguiente, podremos detectar la aparición de un nuevo grupo comparando el identificador que acabamos de recibir de la base de datos del asesor experto con el identificador del grupo cargado anteriormente. Si el primero es mayor, habrá surgido un nuevo grupo. 

En el método de obtención de la cadena de inicialización de la base de datos del asesor experto, que ya interactúa directamente con la base de datos, utilizaremos la misma condición para la fecha de finalización del intervalo de prueba del grupo cuando está activada la actualización automática:

//+------------------------------------------------------------------+
//| Get the strategy group initialization string                     |
//| from the EA database with the given ID                           |
//+------------------------------------------------------------------+
string CVirtualAdvisor::Import(string p_fileName, int p_groupId = 0) {
   string params[];   // Array for strategy initialization strings

// Request to get strategies of a given group or the last group
   string query = StringFormat("SELECT id_group, params "
                               "  FROM strategies"
                               " WHERE id_group = %s;",
                               (p_groupId > 0 ? (string) p_groupId 
                                : "(SELECT MAX(id_group) FROM strategy_groups WHERE to_date <= '"
                                + TimeToString(TimeCurrent(), TIME_DATE) +
                                "')"));

// Open the EA database
   if(DB::Connect(p_fileName, DB_TYPE_ADV)) {
      // ...
   }

// Strategy group initialization string
   string groupParams = NULL;

// Total number of strategies in the group
   int totalStrategies = ArraySize(params);

// If there are strategies, then
   if(totalStrategies > 0) {
      // Concatenate their initialization strings with commas
      JOIN(params, groupParams, ",");

      // Create a strategy group initialization string
      groupParams = StringFormat("class CVirtualStrategyGroup([%s], %.5f)",
                                 groupParams,
                                 totalStrategies);
   }

// Return the strategy group initialization string
   return groupParams;
}

La última cosa que merece la pena mencionar aquí es la adición del método de carga del estado del asesor experto después de cambiar a un nuevo grupo de estrategias. La cuestión es que las nuevas estrategias de un nuevo grupo no encontrarán su configuración en la base de datos del asesor experto, porque el método Save() no ha sido llamado para ellas todavía, e informará de un error de carga. Pero este error deberá ignorarse.

Y una adición más está relacionada con la necesidad de cerrar las posiciones virtuales de las estrategias antiguas inmediatamente después de cargar las nuevas. Para ello, será necesario y suficiente que todos los símbolos utilizados por las estrategias antiguas creen objetos receptores simbólicos. Estos objetos corregirán los volúmenes de las posiciones abiertas en el siguiente tick. Si no se hace así, la corrección del volumen solo se producirá cuando las nuevas estrategias abran posiciones virtuales. Y si alguno de los símbolos usados anteriormente ya no se utiliza en las nuevas estrategias, las posiciones sobre él permanecerán abiertas.

//+------------------------------------------------------------------+
//| Load status                                                      |
//+------------------------------------------------------------------+
bool CVirtualAdvisor::Load() {
   bool res = true;
   ulong groupId = 0;

// Load status if:
   if(true
// file exists
         && FileIsExist(m_fileName, FILE_COMMON)
// currently, there is no optimization
         && !MQLInfoInteger(MQL_OPTIMIZATION)
// and there is no testing at the moment or there is a visual test at the moment
         && (!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE))
     ) {
      // If the connection to the EA database is established
      if(CStorage::Connect(m_fileName)) {
         // If the last modified time is loaded and less than the current time
         if(CStorage::Get("CVirtualReceiver::s_lastChangeTime", m_lastSaveTime)
               && m_lastSaveTime <= TimeCurrent()) {

            PrintFormat(__FUNCTION__" | LAST SAVE at %s",
                        TimeToString(m_lastSaveTime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));

            // If the saved strategy group ID is loaded
            if(CStorage::Get("CVirtualAdvisor::s_groupId", groupId)) {
               // Load all strategies ignoring possible errors
               FOREACH(m_strategies, {
                  res &= ((CVirtualStrategy*) m_strategies[i]).Load();
               });

               if(groupId != s_groupId) {
                  // Actions when launching an EA with a new group of strategies.
                  PrintFormat(__FUNCTION__" | UPDATE Group ID: %I64u -> %I64u", s_groupId, groupId);
                  
                  // Reset a possible error flag when loading strategies
                  res = true;
                  
                  string symbols[]; // Array for symbol names
                  
                  // Get the list of all symbols used by the previous group
                  CStorage::GetSymbols(symbols);
                  
                  // For all symbols, create a symbolic receiver.
                  // This is necessary for the correct closing of virtual positions 
                  // of the old strategy group immediately after loading the new one
                  FOREACH(symbols, m_receiver[symbols[i]]);
               }

               // ...
            }
         } else {
            // If the last modified time is not found or is in the future,
            // then start work from scratch
            PrintFormat(__FUNCTION__" | NO LAST SAVE [%s] - Clear Storage",
                        TimeToString(m_lastSaveTime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
            CStorage::Clear();
            m_lastSaveTime = 0;
         }

         // Close the connection
         CStorage::Close();
      }
   }

   return res;
}

Después de eso, podemos empezar a comprobar si la carga automática de los nuevos grupos funciona. Por desgracia, el éxito no ha sido inmediato, ya que hemos tenido que corregir los errores que han aparecido. Por ejemplo, hemos descubierto que el asesor experto cae en un ciclo infinito si la base de datos del asesor experto de repente resulta estar vacía. O que la prueba no se inicia si la fecha de inicio de la prueba se fija al menos un día antes de la fecha de aparición del primer grupo de estrategias. Pero al final hemos corregido todos los errores encontrados.

Veamos ahora el algoritmo de uso de la biblioteca creada en general y los resultados de la comprobación de la actualización automática.


Algoritmo de uso de la biblioteca Advisor

En esta ocasión describiremos el algoritmo de uso de la biblioteca Advisor para optimizar automáticamente la estrategia del modelo SimpleVolumes, incluida en la biblioteca y ejecutada en el simulador del asesor experto final.

  1. Instalamos la biblioteca en Include (fig. 1).

  2. Creamos una carpeta de proyecto y transferimos a ella los archivos de asesor experto (fig. 2).

  3. Realizamos los cambios en los archivos de asesor experto de la primera fase y en el archivo de asesor experto de la creación del proyecto. Si utilizamos la estrategia de modelos, no será necesario realizar cambios, ya que están actualizados. Compilamos todos los asesores expertos en la carpeta del proyecto.

  4. Ejecutamos el asesor experto de creación del proyectos, estableciendo los valores de los parámetros deseados (podemos dejar los valores por defecto).
    Como resultado, debería aparecer una base de datos de optimización llena de tareas para el proyecto en la carpeta de datos compartidos del terminal. En la descripción del proyecto, podemos especificar cualquier cosa, como las fechas de inicio y finalización del intervalo de optimización. Esto sería por ahora solo la creación de una tarea de inicio del transportador. El propio inicio correrá a cargo de otro asesor experto.

  5. Si lo desea, podrá repetir el punto anterior tantas veces como desee, cambiando los parámetros. De este modo, por ejemplo, podrá crear varios proyectos a la vez para su optimización automática en distintos intervalos de tiempo.

  6. Ejecutamos el asesor experto de optimización y esperamos. El tiempo necesario para completar todos los proyectos añadidos a la base de datos de optimización dependerá de su número, así como del número de símbolos y marcos temporales de los proyectos, de la duración del intervalo de tiempo de prueba/optimización y de la complejidad de la estrategia comercial aplicada. También este tiempo dependerá del número de agentes de prueba que participen en la optimización.
    La salida será un archivo con la base de datos del asesor experto en una carpeta común. Su nombre procederá de la configuración.
    La base de datos del asesor experto contendrá los grupos de estrategias guardados.

  7. Vamos a ejecutar el asesor experto final. Resulte esencial que su nombre y su número mágico coincidan con los especificados durante la optimización. De lo contrario, creará una base de datos de asesor experto vacía y esperará a que aparezca algo en ella. Si el asesor experto final encuentra su base, intentará cargar el grupo de estrategias con el identificador especificado o el último grupo añadido si el identificador es 0. Si establecemos el parámetro de actualización automática, el asesor experto comprobará una vez al día si ha aparecido un nuevo grupo de estrategias disponibles según la fecha en la base de datos del asesor experto. Si ha aparecido, sustituirá al grupo utilizado anteriormente.


Probamos la actualización automática

Así, una vez finalizada la optimización de todos los proyectos añadidos a la base de datos con diferentes fechas de finalización, disponemos de una base de datos de asesores expertos con varios grupos de estrategias de diferente composición. También diferirán en la fecha de finalización del intervalo de optimización. Y tenemos un asesor experto final que puede tomar un nuevo grupo de estrategias de la base a medida que lo probemos, cuando el tiempo actual simulado excede el tiempo final del intervalo de optimización para este nuevo grupo.

Cabe señalar que el guardado y la carga de los parámetros del asesor experto solo funciona cuando el asesor experto se ejecuta en un gráfico o en el modo de prueba visual, por lo que deberemos utilizar el modo visual para comprobar la actualización automática en el simulador.

Vamos a ejecutar el asesor experto final con un identificador de grupo específico groupId_=1. En este caso, independientemente del valor del parámetro useAutoUpdate_, solo se usará este grupo. Hemos realizado la optimización en el intervalo 2022.09.01 - 2023.01.01, así que ejecutaremos el simulador a partir de la fecha 2022.09.01 (periodo principal), y a partir de la fecha 2023.01.01, iniciaremos el periodo forward hasta 2024.01.01.

Periodo principal 2022.09.01 - 2023.01.01

Periodo forward 2023.01.01 - 2024.01.01

Fig. 3. Resultados del asesor experto final con los parámetros groupId_=1 en el intervalo 2022.09.01 - 2024.01.01

Como podemos ver, el asesor experto muestra buenos resultados en el periodo principal, que coincide con el intervalo de optimización; en el periodo forward, en cambio, el panorama es bastante diferente. La caída es mucho mayor y la curva de fondos no experimenta una subida pronunciada. Bueno, querríamos haber visto algo más bonito, pero este resultado no es inesperado. Al fin y al cabo, al realizar la optimización, hemos utilizado un intervalo muy pequeño, pocos símbolos y pocos marcos temporales. Por consiguiente, nos encontramos con que en la sección conocida los parámetros estaban demasiado bien ajustados solo para esta corta sección. Y en un lugar desconocido, el asesor experto no ha logrado rendir.

Por interés, vamos a ver si se observa un patrón similar para otro grupo de estrategias comerciales. Así, ejecutaremos el asesor experto final con el identificador de grupo groupId_=3. Este grupo se ha optimizado en el intervalo 2022.11.01 - 2023.03.01, así que iniciaremos el simulador a partir de la fecha 2022.11.01 (periodo principal), y a partir de la fecha 2023.03.01, iniciaremos también el periodo forward hasta 2024.01.01.

Periodo central 2022.11.01 - 2023.03.01

Periodo forward 2023.03.01 - 2024.01.01

Fig. 4. Resultados del asesor experto final con parámetros groupId_=3 en el intervalo 2022.11.01 - 2024.01.01

Sí, los resultados han sido los mismos que para el primer grupo. Para ambos grupos, existe una gran reducción en mayo-junio. Podríamos pensar que este es un mal periodo para la estrategia. Pero si tomamos el grupo que ha sido optimizado en este rango, vemos que aquí también estos parámetros de estrategias del grupo se han recogido con éxito. En este se ve el mismo crecimiento suave y hermoso del gráfico. 

Si ejecutamos el asesor experto final a partir de la fecha 2023.01.01 con los parámetros groupId_=0, useAutoUpdate=false, obtendremos el mismo resultado que en el periodo forward para el primer grupo, porque en este caso se cargará el primer grupo (ya "existe" en la fecha de inicio de la pasada). Sin embargo, no será sustituido por grupos con horas de aparición más tardías debido a que las actualizaciones automáticas están desactivadas.

Por último, ejecutaremos el asesor experto final en el intervalo 2023.01.01 - 2024.01.01 con actualización automática especificando los parámetros groupId_=0, useAutoUpdate=true.

Fig. 5. Resultados del asesor experto final con los parámetros groupId_=0, useAutoUpdate=true en el intervalo 2023.01.01 - 2024.01.01

Los resultados comerciales en sí no son interesantes, ya que hemos utilizado un periodo de optimización muy corto (solo 4 meses) para reducir el tiempo de optimización automática. Ahora solo queríamos demostrar la funcionalidad del mecanismo de actualización automática de los grupos de estrategias usados. Y esto, a juzgar por las entradas en el diario de registro y el cierre automático de las posiciones a principios de cada mes, funciona según lo previsto: 

SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:00   CVirtualReceiver::Get | OK, Strategy orders: 3 from 144 total
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:00   CVirtualStrategyGroup::CVirtualStrategyGroup | Scale = 2.44, total strategies = 1
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:00   CVirtualStrategyGroup::CVirtualStrategyGroup | Scale = 48.00, total groups = 48
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:00   CVirtualStrategyGroup::CVirtualStrategyGroup | Scale = 1.00, total groups = 1
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:00   CVirtualRiskManager::UpdateBaseLevels | DAILY UPDATE: Balance = 0.00 | Equity = 0.00 | Level = 0.00 | depoPart = 0.10 = 0.10 * 1.00 * 1.00
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:00   CVirtualAdvisor::Load | LAST SAVE at 2023.01.31 20:32:00
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:00   CVirtualAdvisor::Load | UPDATE Group ID: 1 -> 2
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:00:59   CSymbolNewBarEvent::IsNewBar | Register new event handler for GBPUSD PERIOD_D1
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:01:00   CSymbolNewBarEvent::IsNewBar | Register new event handler for EURUSD PERIOD_D1
SimpleVolumes (GBPUSD,H1)       2023.02.01 00:01:00   CSymbolNewBarEvent::IsNewBar | Register new event handler for EURGBP PERIOD_D1


Conclusión

Vamos a resumir un poco las conclusiones. Finalmente hemos organizado todo el código reutilizable en la carpeta Include como una biblioteca Advisor. Ahora podremos conectarlo a proyectos que trabajen con distintas estrategias comerciales. Y las actualizaciones posteriores de la biblioteca se propagarán automáticamente a todos los proyectos en los que se use.

Cada vez resulta más fácil crear y ejecutar un proyecto de optimización automática. Ahora también hemos simplificado el mecanismo de aplicación de los resultados de la optimización en el asesor experto final. Bastará con especificar el nombre requerido de la base de datos del asesor experto en los ajustes de la tercera etapa de optimización, y los resultados irán a un lugar desde donde el asesor experto final podrá recuperarlos.

Sin embargo, aún debemos considerar algunas cosas. Una de ellas es el procesamiento del algoritmo para añadir un nuevo tipo de estrategia comercial e incluir grupos que contengan diferentes tipos de estrategias comerciales en el asesor experto final. Pero eso será en otra ocasión.

Gracias por su atención, ¡hasta pronto!


Advertencia importante

Todos los resultados expuestos 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 ofrecen ninguna garantía de lograr beneficios en el futuro. El trabajo de este proyecto es de carácter exploratorio. Todos los resultados publicados pueden ser usados por cualquiera bajo su propia responsabilidad.


Contenido del archivo

#
 Nombre
Versión  Descripción  Cambios recientes
  MQL5/Experts/Article.16913   Carpeta de trabajo del proyecto  
1 CreateProject.mq5 1.01
Script asesor para crear un proyecto con etapas, actividades y tareas de optimización.
Parte 23
2 HistoryReceiverExpert.mq5 1.01
Asesor experto para reproducir la historia de transacciones con el gestor de riesgos
Parte 23
3 Optimization.mq5
1.00 Asesor experto para la optimización automática de proyectos  Parte 23
4 SimpleVolumesExpert.mq5
1.22 Asesor experto final para el funcionamiento en paralelo de varios grupos de estrategias modelo. Los parámetros se tomarán de la biblioteca de grupos incorporada.
Parte 23
5 Stage1.mq5 1.22  Asesor experto para optimizar una única instancia de una estrategia comercial (Etapa 1)
Parte 23
6 Stage2.mq5
1.00 Asesor experto para optimizar un grupo de instancias de estrategias comerciales (Etapa 2)
Parte 23
Stage3.mq5
1.00 Asesor experto que guarda un grupo normalizado formado de estrategias usando como base los datos del asesor experto con el nombre especificado.
Parte 23
  MQL5/Include/antekov/Advisor/Base
  Clases básicas de las que heredan otras clases del proyecto  
8 Advisor.mqh 1.04. Clase básica del experto Parte 10
9 Factorable.mqh
1.05
Clase básica de objetos creados a partir de una cadena (string)
Parte 22
10 Interface.mqh 1.01
Clase básica de visualización de diversos objetos
Parte 4
11 Receptor.mqh
1.04.  Clase básica de transferencia de volúmenes abiertos a posiciones de mercado
Parte 12
12 Strategy.mqh
1.04.
Clase básica de estrategia comercial
Parte 10
  MQL5/Include/antekov/Advisor/Database
  Archivos para trabajar con todo tipo de bases de datos utilizadas por los asesores de proyectos  
13 Database.mqh 1.10 Clase para trabajar con la base de datos Parte 22
14 db.adv.schema.sql 1.00
Esquema de la base de datos del asesor final Parte 22
15 db.cut.schema.sql
1.00 Esquema de una base de datos de optimización truncada
Parte 22
16 db.opt.schema.sql
1.05  Esquema de la base de datos de optimización
Parte 22
17 Storage.mqh   1.01
Clase de trabajo con almacenamiento Key-Value para el asesor experto final en la base de datos del asesor experto.
Parte 23
  MQL5/Include/antekov/Advisor/Experts
  Archivos con partes comunes de asesores usados de distintos tipos
 
18 Expert.mqh  1.22 Archivo de biblioteca para el asesor final. Los parámetros del grupo pueden tomarse de la base de datos del asesor experto
Parte 23
19 Optimization.mqh  1.04. Archivo de biblioteca para el asesor experto que controla el inicio de las tareas de optimización
Parte 23
20 Stage1.mqh
1.19 Archivo de biblioteca del asesor experto para optimizar una única instancia de una estrategia comercial (Etapa 1)
Parte 23
21 Stage2.mqh 1.04. Archivo de biblioteca del asesor experto para optimizar un grupo de instancias de estrategias comerciales (Etapa 2)   Parte 23
22 Stage3.mqh
1.04. Archivo de biblioteca para el asesor experto que guarda el grupo normalizado generado de estrategias en la base de datos del asesor experto con un nombre especificado. Parte 23
  MQL5/Include/antekov/Advisor/Optimization
  Clases responsables del trabajo de optimización automática  
23 Optimizer.mqh
1.03  Clase para el gestor de optimización automática de proyectos
Parte 22
24 OptimizerTask.mqh
1.03
Clase para la tarea de optimización
Parte 22
  MQL5/Include/antekov/Advisor/Strategies    Ejemplos de estrategias comerciales usadas para demostrar el trabajo del proyecto
 
25 HistoryStrategy.mqh 
1.00 Clase de estrategia comercial para reproducir la historia de transacciones
Parte 16
26 SimpleVolumesStrategy.mqh
1.11
Clase de estrategia comercial con uso de volúmenes de ticks
Parte 22
  MQL5/Include/antekov/Advisor/Utils
  Utilidades auxiliares, macros para la reducción del código
 
27 ExpertHistory.mqh 1.00 Clase para exportar la historia de transacciones a un archivo Parte 16
28 Macros.mqh 1.05 Macros útiles para operaciones con arrays Parte 22
29 NewBarEvent.mqh 1.00  Clase de definición de una nueva barra para un símbolo específico  Parte 8
30 SymbolsMonitor.mqh  1.00 Clase de obtención de información sobre instrumentos comerciales (símbolos) Parte 21
  MQL5/Include/antekov/Advisor/Virtual
  Clases para crear diversos objetos unidos mediante un sistema de órdenes y posiciones comerciales virtuales
 
31 Money.mqh 1.01  Clase básica de gestión de capital
Parte 12
32 TesterHandler.mqh  1.07 Clase para gestionar los eventos de optimización  Parte 23
33 VirtualAdvisor.mqh  1.10  Clase de asesor experto para trabajar con posiciones (órdenes) virtuales Parte 23
34 VirtualChartOrder.mqh  1.01  Clase de posición virtual gráfica Parte 18
35 VirtualFactory.mqh 1.04.  Clase de fábrica de objetos  Parte 16
36 VirtualHistoryAdvisor.mqh 1.00  Clase experta para reproducir la historia de transacciones  Parte 16
37 VirtualInterface.mqh  1.00  Clase de GUI del asesor  Parte 4
38 VirtualOrder.mqh 1.09  Clase de órdenes y posiciones virtuales  Parte 22
39 VirtualReceiver.mqh 1.04. Clase de transferencia de los volúmenes abiertos a las posiciones de mercado (receptor)  Parte 23
40 VirtualRiskManager.mqh  1.04. Clase de gestión de riesgos (gestor de riesgos)  Parte 23
41 VirtualStrategy.mqh 1.09  Clase de estrategia comercial con posiciones virtuales  Parte 23
42 VirtualStrategyGroup.mqh  1.02  Clase de grupo o grupos de estrategias comerciales Parte 23
43 VirtualSymbolReceiver.mqh  1.00 Clase de receptor simbólico  Parte 3
  MQL5/Common/Files   Carpeta común de terminales   
44 SimpleVolumes-27183.test.db.sqlite - Base de datos de expertos con grupos de estrategias añadidos  

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

Archivos adjuntos |
MQL5.zip (421.44 KB)
Simulación de mercado (Parte 16): Sockets (X) Simulación de mercado (Parte 16): Sockets (X)
Estamos a punto de concluir este desafío. Sin embargo, antes de pasar al siguiente, quiero que tú, querido lector, procures comprender estos dos artículos, tanto este como el anterior. Así podrás entender realmente el próximo artículo, en el que abordaré exclusivamente la parte referente a la programación en MQL5. Aunque en él también procuraré que sea fácil de entender. Si no comprendes estos dos últimos artículos, con toda seguridad tendrás grandes dificultades para entender el siguiente. El motivo es simple: los contenidos se van acumulando. Cuantas más cosas haya que hacer, más cosas será necesario crear y comprender para alcanzar el objetivo.
Añadimos un LLM personalizado a un robot comercial (Parte 5): Desarrollar y probar la estrategia de negociación con LLMs (IV) - Probar la estrategia de trading Añadimos un LLM personalizado a un robot comercial (Parte 5): Desarrollar y probar la estrategia de negociación con LLMs (IV) - Probar la estrategia de trading
Con el rápido desarrollo de la inteligencia artificial en la actualidad, los modelos de lenguaje (LLM) son una parte importante de la inteligencia artificial, por lo que debemos pensar en cómo integrar potentes LLM en nuestro trading algorítmico. Para la mayoría de las personas, resulta difícil ajustar estos potentes modelos según sus necesidades, implementarlos localmente y luego aplicarlos al comercio algorítmico. Esta serie de artículos adoptará un enfoque paso a paso para lograr este objetivo.
Automatización de estrategias de trading en MQL5 (Parte 4): Creación de un sistema de recuperación de zonas multinivel Automatización de estrategias de trading en MQL5 (Parte 4): Creación de un sistema de recuperación de zonas multinivel
En este artículo, desarrollamos un sistema de recuperación de zonas multinivel en MQL5 que utiliza el RSI para generar señales de trading. Cada instancia de señal se añade dinámicamente a una estructura de matriz, lo que permite al sistema gestionar múltiples señales simultáneamente dentro de la lógica de recuperación de zona. Mediante este enfoque, demostramos cómo manejar de manera efectiva escenarios complejos de gestión comercial, manteniendo al mismo tiempo un diseño de código escalable y robusto.
Simulación de mercado (Parte 15): Sockets (IX) Simulación de mercado (Parte 15): Sockets (IX)
En este artículo, explicaré una de las posibles soluciones a lo que he estado intentando mostrar. Es decir, cómo permitir que un usuario de Excel realice una acción en MetaTrader 5 sin enviar órdenes ni abrir o cerrar una posición. La idea es que el usuario utilice Excel para realizar un análisis fundamental de algún símbolo. Y que, usando únicamente Excel, pueda indicar a un Asesor Experto que se esté ejecutando en MetaTrader 5 que debe abrir o cerrar una posición determinada.