Optimización móvil continua (Parte 4): Programa de control de la optimización (optimizador automático)

Andrey Azatskiy | 10 abril, 2020

Introducción

Prosiguiendo el estudio de la optimización móvil continua, presentamos al lector el programa creado para implementar la optimiación automática programada. En los artículos anteriores, hemos descrito con detalle la implementación de este programa tanto por la parte del terminal, como por la parte de la biblioteca usada para trabajar con los informes de optimización creados. El lector podrá familiarizarse con el material siguiendo los enlaces:

  1. Optimización móvil continua (Parte 1): Mecanismo de trabajo con los informes de optimización
  2. Optimización móvil continua (Parte 2): Mecanismo de creación de informes de optimización para cualquier robot
  3. Optimización móvil continua (Parte 3): Método de adaptación del robot al optimizador automático

El presente artículo tiene como misión mostrar una panorámica general del producto creado, sirviendo al mismo tiempo de instrucciones de uso. Asimismo, el optimizador automático prevé la posibilidad de ampliar su funcionalidad: el algoritmo de optimización que describiremos en el presente artículo puede ser fácilmente sustituido por su propio algoritmo de optimización, lo que deja un amplio margen para la creatividad. La propia estructura interna del programa creado se descubrirá en posteriores descripciones. El objetivo principal de este artículo es precisamente describir el mecanismo de trabajo y las posibilidades de la aplicación obtenida, dado que los colegas que han usado esta aplicación no siempre la han dominado sobre la marcha. Aunque, como ya mostraremos en el artículo, el propio optimizador automático es bastante sencillo en cuanto a su uso y ajustes. Por consiguiente, podemos ver el presente artículo como unas instrucciones de uso de esta aplicación, en las que se habla sobre todas las posibles trampas y detalles en sus ajustes.


Describiendo el funcionamiento del optimizador automático

Para comenzar a analizar el funcionamiento del propio programa creado, primero debemos determinar el objetivo de este proyecto. Teniendo en cuenta que hemos decidido utilizar un enfoque científico del trading y que hemos comenzado a crear algoritmos comerciales programados con precisión (no importa que tratemos con robots indicadores o con robots basados en el uso de modelos de lógica difusa o redes neuronales, todos ellos son algoritmos programados que ejecutan tareas claramente establecidas), también deberemos formalizar el enfoque sobre el proceso de selección de los resultados. Dicho de otra forma, si durante el proceso de trading nos abstraemos de las casualidades, el proceso de preparación para las transacciones también deberá ser automatizado. De lo contrario, podría resultar que seleccionemos al azar los resultados que nos gusten de entre la masa de parámetros de optimización obtenidos, lo cual se asemeja más a la adivinación que al comercio sistemático. La idea planteada es el primero de los motivos que han impulsado al autor a crear esta aplicación. El siguiente sería la posibilidad de poner a prueba los algoritmos mediante su optimización: la propia optimización móvil continua, que se muestra visualmente en el gráfico de abajo.   


La optimización móvil continua alterna las pasadas de optimización históricas (amarillas) y en tiempo real (verdes) en el intervalo temporal establecido. Por ejemplo, disponemos de una historia de 10 años. Hemos decidido que el intervalo de optimización debe constar de un intervalo igual a 1 año, mientras que el intervalo en tiempo real debe constar de 3 meses. Como resultado, obtenemos para una pasada de optimización + un test en tiempo real con un intervalo de 1,25 años (1 año + 1 trimestre). En el diagrama mostrado, cada línea caracteriza precisamente el intervalo temporal mencionado. 

A continuación, reproduciremos un proceso monótono:

  1. Realizamos una optimización en el intervalo histórico elegido (1 año).
  2. Seleccionamos según la metodología fijada los mejores parámetros de los obtenidos en el intervalo optimizado.
  3. Nos desplazamos al intervalo en tiempo real y realizamos un test, retornamos al primer paso, pero ya en el periodo desplazado. Y así, hasta que lleguemos hasta el periodo temporal actual.
  4. Tras recopilar los resultados de la pasada de prueba de cada trimestre, obtenemos una valoración final de la viabilidad del algoritmo.
  5. Iniciamos la última pasada de optimización (que ya no tiene test en tiempo real).

De esta forma, obtendremos una metodología para probar la viabilidad de nuestros algoritmos usando la optimización, sin embargo, este mismo proceso nos obliga a realizar la reoptimización de nuestros algoritmos cada vez que termina el tiempo reservado al periodo en tiempo real. Dicho de otra forma, establecemos un cierto intervalo temporal para la reoptimización de nuestro algoritmo, fijamos la metodología de selección de parámetros y realizamos primero este procedimiento con la historia, y luego más tarde, cada vez que finalice el tiempo reservado al comercio. 

En primer lugar, esta metodología de optimización nos permite fijar con precisión una lógica de optimización, posibilitando la obtención de un resultado independiente de la intervención humana.

En segundo lugar, realizando la optimización de nuevos activos o para nuevos algoritmos con esta metodología, obtenemos una panorámica completa de las pruebas de estrés del algoritmo, que luego podremos mantener. Si el método de selección de parámetros y los parámetros optimizados son fijados tanto en el primer inicio de la optimización en tiempo real como en los siguientes, conseguiremos una prueba de estrés que se mantendrá constante, y podremos detectar a tiempo si el mercado se muestra más receptivo a esta estrategia.

En tercer lugar, obtenemos en un intervalo temporal bastante pequeño un gran número de ventanas de optimización, lo que mejora el análisis de la fiabilidad de las pruebas realizadas. Por ejemplo, en un intervalo de 2 años, con la división descrita anteriormente del periodo en tiempo real (1 trimestre) y el periodo de optimización (1 año), obtenemos 4 pruebas de estrés y 1 optimización final.


Ajustes del inicio de la optimización

Una vez aclarado el proceso realizado por la aplicación, vamos a describir su método de uso. Debemos decir que este ciclo de artículos supone un cierto desarrollo lógico de los artículos sobre la interfaz gráfica para el control del proceso de optimización:

  1. Gestión de la optimización (Parte I)
  2. Gestión de la optimización (Parte II)

En ellos se describe un método que sirve para iniciar una optimización o una prueba en el terminal como un proceso controlable. Este mismo método se usa en el presente ciclo de artículos, sin embargo, una de las principales diferencias respecto al método pasado es que, en esta ocasión, el proceso controlable no se ha organizado como una adición al terminal, sino como un programa independiente. Este enfoque es necesario para que sea posible usar todos los terminales instalados en una computadora. Para aquellos que no se hayan familiarizado con los artículos mencionados, aclararemos que en estos se creó una expansión que se iniciaba desde el terminal en marcha. Esta expansión podía implicar a todos los terminales instalados en la computadora, aparte de aquel en el que estaba iniciada. Este programa también tiene acceso a todos los terminales instalados en la computadora, sin embargo, funciona solo con un terminal al mismo tiempo, y puede iniciar cualquiera de los terminales.

Un aspecto importante es el hecho de que, para que funcione bien el optimizador automático, antes del inicio, el terminal seleccionado debe estar cerrado. 

La aplicación obtenida funciona según el esquema siguiente:

  1. Durante el inicio del proceso de turno, ya sea una optimización o una prueba, la aplicación inicia el terminal, delegando en el terminal el proceso completo de optimización. Al finalizar la simulación o la optimización, el terminal se cierra por sí mismo.
  2. El robot, al finalizar la simulación u optimización, genera por sí mismo un informe con los resultados de optimización descargados. El lector tiene más información sobre el proceso mencionado en los artículos anteriores de la presente serie.
  3. El optimizador automático, sabiendo dónde se encuentra el informe, lo lee y lo procesa. Al finalizar el procesamiento, o bien se inicia una nueva etapa de optimización y simulación, o bien la propia optimización finaliza y los resultados se muestran en la pestaña "Result". 

Más adelante se describirá con mayor detalle el mecanismo de optimización implementado; en esta parte del presente ciclo de artículos, tenemos la tarea de describir el proceso de funcionamiento del programa, dando el menor número de detalles. Debemos recordar de nuevo que el propio lector podrá escribir la parte del código que responde de la propia optimización, según sus necesidades. El optimizador automático es el programa de control que inicia los procesos de optimización, mientras que la lógica del funcionamiento de estos procesos puede ser diversa. Aquí podemos ver una captura de pantalla de la pestaña principal, con información más concreta.


Si nos fijamos en la captura, el primer cuadro combinado (lista desplegable), nombrado "Select Optimiser" y ubicado en la zona destacada en color verde, precisamente supone la selección del tipo de optimización implementada en el programa. 

Como podemos ver, la interfaz gráfica de la aplicación obtenida se divide en 2 pestañas principales. La pestaña Settings es la principal, precisamente por ella comienza el trabajo de la aplicación cuando debemos iniciar o pausar la optimización, y precisamente a esta pestaña se dedica la presente parte del artículo. La segunda pestaña, "Result", es el lugar donde se muestran los resultados de la optimización realizada; hablaremos de ella más tarde.

En primer lugar, al iniciar el optimizador automático, debemos seleccionar el terminal utilizado. La elección del terminal se realiza según el mismo principio que en la anterior interfaz gráfica para el inicio de la optimización. En otras palabras, el optimizador automático encontrará por sí mismo todos los terminales instalados en la computadora, sin embargo, lo hará solo en el caso de que usen la instalación estándar, y no la portátil. 

Como podemos ver en la captura, la pestaña "Settings" se divide en 4 partes. Los límites de cada parte de la pantalla pueden arrastrarse, cosa especialmente cómoda si debemos trabajar con un algoritmo con un gran número de parámetros de entrada.

Asimismo, merece la pena recordar que, debido al formato de texto de los archivos set, no podemos distinguir los formatos de los parámetros originales de los algoritmos. Por ejemplo, todos los parámetros enum se representan como int. Por eso, hemos decidido representar en la segunda parte de la pantalla la lista de parámetros en forma de líneas. Si al lector le resulta incómodo configurar los pasos de la optimización y otros parámetros del robot antes de la prueba directamente en el optimizador automático, podrá realizar todos los ajustes en el terminal. A continuación, después de cambiar las pestañas en el simulador o al cerrar el terminal, los ajustes se registrarán en el archivo buscado, y todo lo que necesitará hacer es seleccionar el algoritmo necesario en el optimizador automático: este estará cargado ya con los ajustes que usted ha realizado.

Justo antes del inicio de la optimización o la simulación del optimizador automático, debemos configurar los campos siguientes:

  1. Selección del experto.
  2. Marcamos los parámetros a optimizar y seleccionamos el intervalo (aquí todo es igual que en el terminal).
  3. Seleccionamos aunque sea un solo criterio de clasificación: partiendo precisamente de los parámetros de clasificación elegidos, se clasificarán los datos descargados por el robot; el mejor resultado de los obtenidos se iniciará en el intervalo en tiempo real (podemos no indicarlo, en el caso de que iniciemos una simulación).
  4. Elegimos las fechas de la optimización móvil (o las fechas de las simulaciones, en el caso de que iniciemos una simulación, y no una optimización).   
  5. Debemos elegir el optimizador necesario, si realizamos la optimización (lista desplegable "Select Optimiser:"). 
  6. Debemos indicar Asset name, es decir, el nombre del activo con el que se va a iniciar la operación solicitada. En caso de error, el terminal no podrá iniciar el proceso de simulación u optimización.
  7. Para especificar de forma adicional el nombre de la pasada de optimización guardada, se establece "Directory prefix". El asunto es que, al finalizar la optimización, se crea una carpeta con los resultados de la optimización en un directorio interior especial del programa. Esta carpeta se nombra de la forma siguiente: "{Directory prefix} {Selected optimiser} {Expert name} {Asset name}". Precisamente estos nombres vemos en la lista desplegable "Optimisation:", de donde podremos descargarlos para su posterior análisis y visualización.
  8. La lista desplegable con los parámetros "Rewrite" o "Append" también es opcional. Esta indica la acción que debe realizar el optimizador automático si encuentra entre los resultados guardados alguno que tenga un nombre similar (la creación del nombre se describe en el punto 7 de la presente enumeración). Si elegimos el punto "Rewrite", todos los archivos serán reescritos por los nuevos; si elegimos el punto "Append", se reescribirán solo las fechas de optimización que coincidan con otras. Si en la lista actual de intervalos temporales y en la lista de intervalos anteriormente guardados se encuentra el mismo intervalo, los resultados anteriormente guardados serán reescritos por los nuevamente recibidos, y los intervalos temporales que no han sido anteriormente guardados serán simplemente añadidos a los ya registrados.

Al finalizar los ajustes mencionados, solo quedará pulsar el botón "Start/Stop", y el proceso dará comienzo. Si pulsamos de nuevo sobre este botón cuando el proceso de optimización está en marcha, este se interrumpirá. Durante todo el proceso de optimización, su estado se representará en ProgressBar y la marca de texto que se encuentran ubicadas en la base de la ventana del optimizador automático. Al finalizar el proceso de optimización, se cargarán los resultados obtenidos en la pestaña "Result", y ProgressBar será reseteada a la posición inicial. Sin embargo, si en lugar de la optimización iniciamos la simulación pulsando el botón "Start / Stop", el terminal no se cerrará automáticamente. Esto se ha hecho para mayor comodidad del usuario, que podrá estudiar los adatos que le interesen el tiempo que necesite. Una vez termine de analizar la información buscada, para continuar trabajando con el optimizador automático, tendrá que cerrar el terminal manualmente, de la forma habitual. Recordemos de nuevo: para que la aplicación descrita funcione correctamente, será necesario que el terminal esté siempre cerrado, ya que, en esencia, la aplicaicón es un gestor de optimizaciones, y por consiguiente, deberá tener la posibilidad de controlar el terminal de forma independiente.

Asimismo, antes de iniciar la optimización, deberemos configurar el propio optimizador. No es un requisito obligatorio, pero la estructura del controlador de optimizaciones obtenido es tal que permite al mismo tiempo crear optimizadores de usuario e indicar para cada uno de ellos los ajustes a través de la pulsación del botón "GUI" ubicado directamente junto al cuadro combinado de selección del optimizador. Los ajustes del optimizador implementado son los siguientes:

  1. Test on ticks indica el modo de simulación de datos con las pruebas históricas y en tiempo real. El modo de optimización se indica en el primer cuarto de la pantalla "Settings", mientras que el modo de simulación se indica en los ajustes del optimizador. Si está marcado, las simulaciones se realizarán con los ticks, sin embargo, si no está marcado, las simulaciones se realizarán usando el método OHLC 1 minute. 
  2. Replace real dates to setted indica que hay que sustituir las fechas reales de comienzo y finalización de las optimizaciones por las transmitidas. Lo que ocurre es que, como se indica en el tercer artículo del presente ciclo, la hora de comienzo y finalización de las optimizaciones se guarda usando el gráfico de minutos, y en ocasiones, si no hay transacciones o si es un día festivo, las fechas de comienzo o finalización de la optimización podría diferenciarse de las establecidas. Marcando la casilla, simplemente estamos añadiendo fechas más habituales, para no tener que adivinar en consecuencia a qué intervalo temporal pertenece la pasada de optimización analizada. No obstante, si tenemos ganas de ver las fechas reales de las transacciones, podremos no marcar la casilla en el parámetro descrito.
  3. Use different shift for tick test, como se mencionó en el segundo artículo del ciclo, podemos añadir el deslizamiento y el desplazamiento a los datos descargados. Si realizamos la simulación con ticks, a veces resulta razonable desactivar o reducir el deslizamiento. Por eso, hemos introducido este punto, que se activa solo cuando el ajuste "Test on ticks" ha sido marcado como activo. Para que ello, deberemos indicar los parámetros del algoritmo que son responsables de establecer la comisión y el deslizamiento, y también asignarles un nuevo valor. Por ejemplo, si indicamos el parámetro del robot encargado del deslizamiento y le asignamos un valor igual a 0, quitaremos de la descarga el deslizamiento en el modo de simulación con ticks. Después de indicar el parámetro y su valor, para guardar este parámetro, deberemos añadirlo al recuadro pulsando el botón "Add".

No es necesario guardar los ajustes introducidos (como podemos ver, no hay botón de guardado), estos se guardan automáticamente después de cambiar este o aquel parámetro. Antes de iniciar la optimización, debemos cerrar esta ventana, para no cambiar casualmente los parámetros durante la optimización. 


Trabajando con los resultados de optimización obtenidos

Después de iniciar la optimización, lo único que se requerirá será no interferir en dicho proceso. Asimismo, el experto deberá ser eliminado del gráfico antes de que el optimizador lo detenga, de lo contrario, el optimizador automático considerará -debido a que las fechas no coinciden- que ha sucedido un error durante el proceso de optimización. Una vez el proceso haya finalizado y el terminal haya sido cerrado, el optimizador cargará el informe de optimización en la pestaña "Results", donde tendremos la posibilidad de evaluar el trabajo. La estructura de esta pestaña se representa en la captura de pantalla:

 

Como podemos ver, la pestaña con los resultados de optimización también está dividida en fracciones, marcadas con números para hablar de ellas con mayor facilidad. La primera parte de esta pestaña contiene las pasadas de optimización, divididas entre las pestañas (Selected pass y Optimisations). La primera pestaña de este contenedor, llamada "Selected pass", contiene las pasadas de optimización seleccionadas. Estas pasadas se dividen entre las dos pestañas ("Forward" y "History"). Para ver más detalles, podemos fijarnos en un ejemplo que muestra cómo se distribuyen los parámetros de optimización entre estas pestañas. Por ejemplo, en la primera de las ventanas de optimización, vemos las siguientes fechas:

  1. 01.01.2012 - 07.12.2012 - History
  2. 10.12.2012 - 07.03.2012 - Forward

Por consiguiente, la optimización se realizará en un intervalo temporal histórico. A continuación, se seleccionarán los mejores parámetros con el uso del filtrado y la clasificación, como se hizo en el capítulo anterior. Al finalizar el proceso de selección, se realizan 2 simulaciones: en la ventana histórica y en la ventana en tiempo real. Acto seguido, los resultados de las simulaciones de los datos de los mejores parámetros se introducen en la pestaña "Selected pass", dividiéndose entre las pestañas "History" y "Forwars", de acuerdo con las ventanas de los intervalos temporales histórico y en tiempo real. Las propias pasadas de optimización se introducen en la pestaña "Optimisations", donde se puede ver la lista completa de optimizaciones de cualquiera de los intervalos históricos seleccionados, como en el terminal. Visualizando con mayor detalle la pestaña con las optimizaciones.


Como podemos ver, la estructura del propio recuadro (primer parte de la pestaña "Optimisations") es similar a la estructura de las pestañas "Forward" e "History". Sin embargo, en este recuadro se representan todas las pasadas de optimización del intervalo temporal solicitado. Podomos seleccionar el intervalo temporal en la lista desplegable "Optimisation dtes". Al seleccionar un nuevo intervalo temporal, se actualiza todo el recuadro con las pasadas de optimización. La segunda parte de la pestaña descrita es similar a la tercera parte de la pestaña "Settings", mencionada en el capítulo anterior del presente artículo, y todos los cambios en esta esfera se sincronizan con la misma esfera en la pestaña "Settings".

Asimismo, como podemos ver en la captura mostrada, la pestaña "Optimisations", al igual que la pestaña "Selected pass", contiene el botón "Save to (*.csv)". Al pulsar este botón con la pestaña "Selected pass" seleccionada, se crea un archivo (*.csv) con la lista de pasadas de optimización históricas y en tiempo real. Si hemos elegido la pestaña "Optimisations", al pulsar este botón se descargará la información sobre todas las optimizaciones realizadas en el archivo (*csv) correspondiente. Los otros botones, "Sort" y "Filter", están disponibles en la pestaña "Optimisations": su tarea consiste en ofrecer las posibilidades de filtrado y clasificación de las pasadas de optimización obtenidas de acuerdo con los ajustes indicados en la segunda parte de la pestaña "Optimisations". En la práctica, esta opción no es necesaria como tal, ya que el propio optimizador automático usa precisamente el mismo mecanismo. No obstante, si usted no está de acuerdo con los resultados obtenidos y considera que puede seleccionar otros más adecuados que los indicados anteriormente mediante el cambio de filtros, esta funcionalidad le resultará útil. 

Ambos recuadros son interactivos. Al realizar un solo clic sobre una línea del recuadro, la segunda y la tercera parte de la pestaña "Result" actualizarán sus valores para la pasada de optimización seleccionada. Un doble clic sobre alguna de las líneas iniciará el test en el simulador. En este caso, además, el terminal no se cerrará automáticamente al finalizar la simulación, por consiguiente, después de estudiar los datos buscados en el informe de optimización del terminal y analizar el gráfico de la simulación, deberemos cerrarlo manualmente para que el optimizador automático tenga posteriormente la posibilidad de trabajar con el terminal. Antes de iniciar el test, podemos también ajustar ciertos parámetros del simulador, en la segunda 2 parte de la pestaña "Results":

  

La fecha de inicio y finalización de la simulación se actualizan automáticamente de acuerdo con el intervalo temporal seleccionado. No obstante, si el usuario así lo desea, podrá cambiarla clicando dos veces sobre la línea elegida. Asimismo, es posible seleccionar el retraso de ejecución y el tipo de simulación (ticks, OHLC y otros). Por defecto, tenemos las variantes "Sin retrasos" y "Cada tick". 

La tercera parte de esta pestaña representa tanto los indicadores estadísticos de las transacciones en la pasada de optimización elegida (pestañas Daily PL y Max PL/DD), como los parámetros del robot para la pasada seleccionada (pestaña Bot params). En este caso, además, la pestaña "Max PL/DD" no muestra el valor final de beneficio/pérdidas, muestra el beneficio sumado (se suman solo las transacciones rentables) y las pérdidas sumadas (se suman solo las transacciones con pérdidas). El beneficio y la reducción máxima que había en el momento de finalizar las transacciones se representa en el recuadro con los resultados de las optimizaciones y las simulaciones. La pestaña Daily PL representa el beneficio medio diario y las pérdidas medias diarias, todo de una forma semejante al informe que se encuentra en el terminal.

Otro ejemplo sencillo del algoritmo de trabajo con el optimizador automático

Tras analizar cómo usar el optimizador automático, vamos a mostrar un ejemplo acabado de un robot adaptado al funcionamiento del optimizador automático. En el artículo №3 del presente ciclo, ya analizamos una plantilla para escribir un algoritmo para la tarea planteada; en este artículo, adaptaremos nuestra plantilla a un algoritmo escrito en el estilo C. En primer lugar, analizamos el propio algoritmo; según la tradición, tomamos un algoritmo sencillo de 2 medias móviles, con una única excepción: el cierre de posiciones se realizará o bien con un stop loss fijo, o bien con un take profit fijo. Para llamar la atención sobre puntos concretos, quitaremos del código adjunto la implementación de las funciones que describen la lógica del experto obtenido: este no es el objetivo del presente ejemplo.

+//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
+//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;

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

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not anabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
+//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
+//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

+//+------------------------------------------------------------------+
//| Calculate stop                                                   |
+//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

+//+------------------------------------------------------------------+
//| Calculate take                                                   |
+//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

+//+------------------------------------------------------------------+
//| Open position according to direction                             |
+//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...
  }

+//+------------------------------------------------------------------+
//| Get direction                                                    |
+//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

+//+------------------------------------------------------------------+
//| Expert tick function                                             |
+//+------------------------------------------------------------------+
void OnTick()
  {
   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
+//+------------------------------------------------------------------+

Al fragmento mostrado del código del experto es en general básico. Es necesario a la hora de monitorear los cambios requeridos para que este experto esté disponible en el optimizador automático. 

Antes de analizar el código, debemos destacar que no hemos necesitado crear un robot funcional para esta tarea, y por eso, es más que probable que sufra pérdidas al usarse en las transacciones. Para que no sea posible iniciarlo en el comercio por error, en el experto existe una restricción. Para desactivarla (tras desactivarla, el experto podrá ser iniciado en el comercio), basta con comentar la definición TESTER_ONLY

En el método OnOnit, lo único que tenemos que hacer es realizar la instanciación de los indicadores de las medias móviles. En el método OnDeinit, por consiguiente, eliminamos los indicadores. Para determinar la dirección, se usa la enumeración Direction declarada en el código. La apertura de posiciones se realiza a través de la clase CTrade, enmascarada en la función open_position. La propia lógica está descrita en cuatro líneas de código en la llamada de retorno OnTick. Ahora, vamos a añadir la inclusión de la funcionalidad requerida en el experto.

+//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
+//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#include <History manager/AutoLoader.mqh> // Include CAutoUploader
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

// Comission and price shift (Article 2) 
input double _comission_ = 0; // Comission
input int _shift_ = 0; // Shift

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;
CAutoUploader * auto_optimiser; // Pointer to CAutoUploader class (Article 3)
CCCM _comission_manager_; // Comission manager (Article 2)

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

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not anabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }

   // Set Comission adn shift
   _comission_manager_.add(_Symbol,_comission_,_shift_);

   // Add robot params
   BotParams params[];
   APPEND_BOT_PARAM(ma_fast,params);
   APPEND_BOT_PARAM(ma_slow,params);
   APPEND_BOT_PARAM(_sl_,params);
   APPEND_BOT_PARAM(_tp_,params);
   APPEND_BOT_PARAM(_lot_,params);
   APPEND_BOT_PARAM(_comission_,params);
   APPEND_BOT_PARAM(_shift_,params);

   // Add Instance CAutoUploader class (Article3)
   auto_optimiser = new CAutoUploader(&_comission_manager_,"SimpleMAMutex",params);
//---
   return(INIT_SUCCEEDED);
  }
+//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
+//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);

   // Delete CAutoUploaderclass (Article 3)
   delete auto_optimiser; 
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

+//+------------------------------------------------------------------+
//| Calculate stop                                                   |
+//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

+//+------------------------------------------------------------------+
//| Calculate take                                                   |
+//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

+//+------------------------------------------------------------------+
//| Open position according to direction                             |
+//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...   
  }

+//+------------------------------------------------------------------+
//| Get direction                                                    |
+//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

+//+------------------------------------------------------------------+
//| Expert tick function                                             |
+//+------------------------------------------------------------------+
void OnTick()
  {
   auto_optimiser.OnTick(); // Save current date (Article 3)

   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
+//+------------------------------------------------------------------+

Todas las novedades implementadas en el código se han marcado en color verde. Vamos a analizarlas desplazándonos hacia abajo por el código mostrado. En primer lugar, incluimos el archivo de encabezado AutoLoader, descrito en el artículo №3. Este archivo contiene la clase CAutoUploader, cuya tarea consiste en descargar la historia de transacciones acumulada en el momento de la eliminación. Añadimos dentro de la llamada de retorno OnInit la comisión a la clase CCCM correspondiente, descrita en el artículo №2, y realizamos la instanciación de CAutoUploader, añadiendo de forma preliminar a la misma los parámetros del experto. Asimismo, en la llamada de retorno de OnDeinit, eliminamos el ejemplar de la clase CAutoUploader, realizando con ello la instanciación de la llamada del desctructor donde tiene lugar la descarga del informe de transacciones a un archivo xml (artículo №1). 

Toda la lógica del experto permanece intacta, salvo la llamada de retorno de OnTick, en la que se llama el método OnTick del ejemplar de clase CAutoUploader. Esta llamada es necesaria para guardar correctamente las fechas de comienzo y finalización de las simulaciones. En este caso, además, el funcionamiento de la clase CAutoUploader se limita solo al simulador, en su funcionamiento real, no realizará ninguna acción. 


Conclusión

En el presente artículo, se describe la funcionalidad de la aplicación creada: el gestor de optimizaciones. Como ya se ha dicho al inicio de este artículo, debemos entenderlo como unas instrucciones de uso del programa obtenido. Los aspectos técnicos de la implementación del programa obtenido se describirán en los próximos artículos. Al artículo se adjunta:

Para iniciar el programa del optimizador automático, deberemos compilarlo con la ayuda de Ide Visual Studio. Asimismo, debemos recordar que en el directorio MQL5/Include/Libraries tiene que encontrarse la biblioteca "ReportManager.dll", descrita en el primer artículo del presente ciclo. Además, es posible encontrarla en el proyecto adjunto, con el optimizador automático (también requiere de compilación).