Directivas del preprocesador para el probador

En la sección sobre propiedades generales de los programas, primero nos familiarizamos con las directivas #property en los programas MQL. Luego nos encontramos con directivas pensadas para scripts, servicios e indicadores. También hay un grupo de directivas para el probador. Ya hemos mencionado algunas de ellas. Por ejemplo, tester_everytick_calculate afecta al cálculo de los indicadores.

En la siguiente tabla se enumeran todas las directivas del comprobador con sus explicaciones.

Directiva

Descripción

tester_indicator "string"

El nombre del indicador personalizado con el formato «nombre_indicador.ex5»

tester_file «string»

Nombre de archivo con el formato «nombre_archivo.extensión» con los datos iniciales necesarios para la prueba del programa.

tester_library «string»

Nombre de la biblioteca con una extensión como «library.ex5» o «library.dll».

tester_set «string»

Nombre de archivo con el formato «nombre_archivo.set» con los ajustes para los valores y rangos de optimización de los parámetros de entrada del programa.

tester_no_cache

Desactivación de la lectura de la caché existente de optimizaciones anteriores (archivos opt)

tester_everytick_calculate

Desactivación del modo de ahorro de recursos para el cálculo de indicadores en el comprobador

Las dos últimas directivas no tienen argumentos. Todos los demás esperan una cadena entre comillas dobles con el nombre de un archivo de un tipo u otro. También se deduce de esto que las directivas se pueden repetir con distintos archivos, es decir, se pueden incluir varios archivos de configuración o varios indicadores.

La directiva tester_indicator es necesaria para conectar al proceso de simulación aquellos indicadores que no se mencionan en el código fuente del programa sometido a prueba en forma de cadenas constantes (literales). Por regla general, el compilador puede determinar automáticamente el indicador necesario a partir de las llamadas a iCustom si su nombre se especifica explícitamente en el parámetro correspondiente, por ejemplo, iCustom(symbol, period, "indicator_name",...). Sin embargo, esto no siempre es así.

Digamos que estamos escribiendo un Asesor Experto universal que puede utilizar diferentes indicadores de media móvil, no sólo los estándar integrados. A continuación, podemos crear una variable de entrada para especificar el nombre del indicador por el usuario. Entonces, la llamada a iCustom se convertirá en iCustom(symbol, period, CustomIndicatorName,...), donde CustomIndicatorName es una variable de entrada del Asesor Experto, cuyo contenido se desconoce en el momento de la compilación. Además, en este caso es probable que el desarrollador aplique IndicatorCreate en lugar de iCustom, ya que también hay que configurar el número y los tipos de parámetros del indicador. En estos casos, para depurar el programa o demostrarlo con un indicador específico, debemos proporcionar el nombre al probador utilizando la directiva tester_indicator.

La necesidad de informar de los nombres de los indicadores en el código fuente limita considerablemente la capacidad de probar esos programas universales que pueden conectar varios indicadores en línea.

Sin la directiva tester_indicator, el terminal no podrá enviar al agente un indicador que no esté explícitamente declarado en el código fuente, por lo que el programa dependiente perderá parte o toda su funcionalidad.

La directiva tester_file permite especificar un archivo que se transferirá a los agentes y se colocará en el sandbox antes de realizar la simulación. El contenido y el tipo de archivo no están regulados. Por ejemplo, estos pueden ser los pesos de una red neuronal preentrenada, datos de Profundidad de Mercado recopilados previamente (porque el probador no puede reproducirlos), etc.

Tenga en cuenta que el archivo de la directiva tester_file sólo se lee si existía en tiempo de compilación. Si el código fuente se compiló cuando no existía el archivo correspondiente, su aparición en el futuro ya no servirá de nada: el programa compilado se enviará al agente sin archivo auxiliar. Por lo tanto, por ejemplo, si el archivo especificado en tester_file se genera en OnTesterInit, debe asegurarse de que el archivo con el nombre dado ya existía en el tiempo de compilación, aun cuando estuviera vacío. Se lo demostraremos a continuación.

Tenga en cuenta que el compilador no genera advertencias si el archivo especificado en la directiva tester_file no existe.

Los archivos conectados deben estar en el sandbox del terminal MQL5/Files/.

La directiva tester_library informa al probador de la necesidad de transferir la librería, que es un programa auxiliar que sólo puede funcionar en el contexto de otro programa MQL, a los agentes. Hablaremos de las bibliotecas en detalle en una sección aparte.

Las bibliotecas necesarias para la simulación se determinan automáticamente mediante las directivas #import del código fuente. Sin embargo, si alguna biblioteca es utilizada por un indicador externo, entonces esta propiedad debe estar habilitada. La biblioteca puede tener tanto la extensión dll como la extensión ex5.

La directiva tester_set funciona con archivos set con ajustes del programa MQL. El archivo especificado en la directiva estará disponible en el menú contextual del probador y permitirá al usuario aplicar rápidamente los ajustes.

Si se especifica el nombre sin una ruta, el archivo set debe estar en el mismo directorio que el Asesor Experto. Esto es algo inesperado, porque el directorio por defecto para los archivos de set es Presets, y ahí es donde se guardan mediante comandos desde la interfaz del terminal. Para conectar el archivo set desde el directorio dado, debe especificarlo explícitamente en la directiva y escribir delante de él una barra, que indica la ruta absoluta dentro de la carpeta MQL5.

#property tester_set "/Presets/xyz.set"

Cuando no hay barra oblicua inicial, la ruta es relativa al lugar en el que se colocó el texto de origen.

Inmediatamente después de añadir el archivo y recompilar el programa, debe volver a seleccionar el Asesor Experto en el probador; de lo contrario, el archivo no se recogerá.

Si especifica el nombre y el número de versión del Asesor Experto como «<expert_name> _<number> .set» en el nombre del archivo set, éste se añadirá automáticamente al menú de descarga de versiones de parámetros con el número de versión <number>. Por ejemplo, el nombre «MACD Sample_4.set» significa que se trata de un archivo set para el Asesor Experto «MACD Sample.mq5» con número de versión 4.

Los interesados pueden estudiar el formato de los archivos set: para ello, guarde manualmente los ajustes de simulación/optimización en el probador de estrategias y, a continuación, abra el archivo creado de este modo en un editor de texto.

Veamos ahora la directiva tester_no_cache. Al realizar la optimización, el probador de estrategias guarda todos los resultados de las pasadas realizadas en la caché de optimización (archivos con la extensión opt), en la que se almacena el resultado de la prueba para cada conjunto de parámetros de entrada. Esto permite, cuando se vuelve a optimizar con los mismos parámetros, obtener resultados listos sin necesidad de volver a calcular y sin perder tiempo.

Sin embargo, para algunas tareas, como los cálculos matemáticos, puede ser necesario realizar cálculos independientemente de la presencia de resultados listos en la caché de optimización. En este caso, en el código fuente, debe incluir la propiedad tester_no_cache. Al mismo tiempo, los propios resultados de las pruebas se seguirán almacenando en la memoria caché para que pueda ver todos los datos sobre las pasadas completadas en el probador de estrategias.

La directiva tester_everytick_calculate está diseñada para activar el modo de cálculo del indicador en cada tick en el probador.

Por defecto, los indicadores se calculan en el probador sólo cuando se accede a ellos para obtener datos, es decir, cuando se solicitan los valores de los búferes de indicadores. Esto permite acelerar considerablemente la simulación y la optimización si no es necesario obtener los valores de los indicadores en cada tick.

Sin embargo, algunos programas pueden requerir que los indicadores se vuelvan a calcular en cada tick. Es en estos casos cuando resulta útil la propiedad tester_everytick_calculate.

Los indicadores del probador de estrategias también se ven obligados a calcularse en cada tick en los siguientes casos:

  • al realizar simulación en modo visual;
  • si existen las funciones EventChartCustom, OnChartEvent o OnTimer en el indicador.

Esta propiedad sólo se aplica a las operaciones del probador de estrategias. En el terminal, los indicadores se calculan siempre en cada tick entrante.

De hecho, la directiva se ha utilizado en el Asesor Experto FrameTransfer.mq5:

#property tester_set "FrameTransfer.set"

Simplemente no nos centramos en ello. El archivo «FrameTransfer.set» se encuentra junto al código fuente. En el mismo Asesor Experto, también necesitamos otra directiva de la tabla anterior:

#property tester_no_cache

Además, veamos un ejemplo de directiva tester_file. Anteriormente, en la sección sobre ajuste automático de los parámetros del Asesor Experto a la hora de optimizar, introdujimos BandOsMApro.mq5, en el que fue necesario introducir varios parámetros sombra para pasar rangos de optimización a nuestro código fuente que se ejecuta en los agentes.

La directiva tester_file nos permitirá deshacernos de estos parámetros adicionales. Llamemos a la nueva versión BandOsMAprofile.mq5.

Ya que estamos familiarizados con la directiva tester_set, vamos a añadir a la nueva versión el archivo /Presets/MQL5Book/BandOsMA.set anteriormente mencionado.

#property tester_set "/Presets/MQL5Book/BandOsMA.set"

La información sobre el rango y el paso de los periodos de cambio de FastOsMA y SlowOsMA se guardará en el archivo BandOsMAprofile.csv en lugar de tres parámetros de entrada adicionales FastShadow4Optimization, SlowShadow4Optimization, StepsShadow4Optimization.

#define SETTINGS_FILE "BandOsMAprofile.csv"
#property tester_file SETTINGS_FILE
   
const string SettingsFile = SETTINGS_FILE;

El ajuste de sombra FastSlowCombo4Optimization sigue siendo necesario para una enumeración completa de las combinaciones de periodos permitidas.

input group "A U X I L I A R Y"
sinput int FastSlowCombo4Optimization = 0;   // (reserved for optimization)

Recordemos que encontramos su rango de optimización en la función Iterate. La primera vez la llamamos en OnTesterInit con una enumeración completa de combinaciones de periodos rápidos y lentos.

Básicamente, podríamos almacenar todas las combinaciones válidas en el array de estructuras PairOfPeriods y escribirla en un archivo binario para su transmisión a los agentes. A continuación, en los agentes, nuestro Asesor Experto podría leer el array listo del archivo y por el índice FastSlowCombo4Optimization extraer el par correspondiente de FastOsMA y SlowOsMA del array.

En lugar de ello, nos centraremos en un cambio mínimo en la lógica de funcionamiento del programa: seguiremos restaurando un par de periodos debido a la segunda llamada Iterate en el manejador OnInit. Esta vez obtendremos el rango y el paso de enumeración de los valores del periodo no de los parámetros de sombra, sino del archivo CSV.

He aquí los cambios en OnTesterInit:

int OnTesterInit()
{
   ...
        // check if the file already exists before compiling
        // - if not, the tester will not be able to send it to agents
         const bool preExisted = FileIsExist(SettingsFile);
         
         // write the settings to a file for transfer to copy programs on agents
         int handle = FileOpen(SettingsFileFILE_WRITE | FILE_CSV | FILE_ANSI",");
         FileWrite(handle"FastOsMA"start1step1stop1);
         FileWrite(handle"SlowOsMA"start2step2stop2);
         FileClose(handle);
         
         if(!preExisted)
         {
            PrintFormat("Required file %s is missing. It has been just created."
               " Please restart again.",
               SettingsFile);
            ChartClose();
            return INIT_FAILED;
         }
   ...
   return INIT_SUCCEEDED;
}

Nótese que hemos hecho el manejador OnTesterInit con el tipo de retorno int, lo que hace posible cancelar la optimización si el archivo no existe. Sin embargo, en cualquier caso, los datos reales se escriben en el archivo, por lo que si no existía, ahora se crea, y el posterior inicio de la optimización será sin duda un éxito.

Si desea omitir este paso, puede crear previamente un archivo MQL5/Files/BandOsMAprofile.csv vacío.

El manejador OnInit se ha modificado como sigue:

int OnInit()
{
   if(FastOsMA >= SlowOsMAreturn INIT_PARAMETERS_INCORRECT;
   
   PairOfPeriods p = {FastOsMASlowOsMA}; // default initial parameters
   int handle = FileOpen(SettingsFileFILE_READ | FILE_TXT | FILE_ANSI);
   
   // during optimization, a file with shadow parameters is needed
   if(MQLInfoInteger(MQL_OPTIMIZATION) && handle == INVALID_HANDLE)
   {
      return INIT_PARAMETERS_INCORRECT;
   }
   
   if(handle != INVALID_HANDLE)
   {
      if(FastSlowCombo4Optimization != -1)
      {
         // if there is a shadow copy, read the period values from it
         const string line1 = FileReadString(handle);
         string settings[];
         if(StringSplit(line1, ',', settings) == 4)
         {
            int FastStart = (int)StringToInteger(settings[1]);
            int FastStep = (int)StringToInteger(settings[2]);
            int FastStop = (int)StringToInteger(settings[3]);
            const string line2 = FileReadString(handle);
            if(StringSplit(line2, ',', settings) == 4)
            {
               int SlowStart = (int)StringToInteger(settings[1]);
               int SlowStep = (int)StringToInteger(settings[2]);
               int SlowStop = (int)StringToInteger(settings[3]);
               p = Iterate(FastStartFastStopFastStep,
                  SlowStartSlowStopSlowStepFastSlowCombo4Optimization);
               PrintFormat("MA periods are restored from shadow: FastOsMA=%d SlowOsMA=%d",
                  p.fastp.slow);
            }
         }
      }
      FileClose(handle);
   }

Al ejecutar pruebas individuales tras la optimización, veremos valores de periodo descodificados en el registro FastOsMA y SlowOsMA basados en el valor optimizado FastSlowCombo4Optimization. En el futuro, podemos sustituir estos valores en los parámetros del periodo y eliminar el archivo csv. También hemos previsto que el archivo no se tenga en cuenta si FastSlowCombo4Optimization se establece en -1.