Simulación de estrategias comerciales

La idea del trading automatizado es bastante atractiva con el hecho de que el robot de trading trabaja sin descanso 24 horas al día y siete días a la semana. El robot no sabe nada de cansancio, dudas y miedo, ni tampoco de problemas psicológicos. Sólo basta con formalizar las reglas de trading e implementarlas en forma de algoritmos, y su robot está listo a trabajar sin parar. Pero antes, es necesario asegurarse del cumplimiento de dos condiciones importantes:

  • el Asesor Experto realiza las operaciones comerciales de acuerdo con las reglas del sistema de trading;
  • la estrategia de trading que ha sido implementada en el Asesor Experto muestra la ganancia a base de los datos históricos.

Para recibir respuestas a estas preguntas, se utiliza el Probador de Estrategias que forma parte integrante del Terminal de Cliente MetaTrader 5.

En este apartado vamos a analizar todas las particularidades de la simulación y optimización de los programas en el Probador de Estrategias:

 

 

Limitaciones de memoria y espacio en el disco en MQL5 Cloud Network

Al ejecutar la optimización en MQL5 Cloud Network, existe un límite: el asesor experto probado no puede escribir más de 4GB de datos en el disco y utilizar más de 4GB de RAM. Si se excede el límite, el agente de red no podrá completar los cálculos correctamente y usted no obtendrá el resultado de la prueba. En este caso, además, se le cobrará el tiempo ya empleado en los cálculos.

Si necesita obtener información de cada pasada de optimización, utilice el envío de frames sin escribir en el disco. En otras palabras, al realizar cálculos en MQL5 Cloud Network no utilice operaciones de archivo en los asesores durante la optimización, puede utilizar esta comprobación:.

   int handle=INVALID_HANDLE;
   bool file_operations_allowed=true;
   if(MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_FORWARD))
      file_operations_allowed=false;
 
   if(file_operations_allowed)
     {
      ...
      handle=FileOpen(...);
      ...
     }

 

Limitaciones del funcionamiento de las funciones en el Probador de Estrategias #

En el Probador del terminal de cliente existen limitaciones para el trabajo de algunas funciones.

Funciones Comment(), Print() y PrintFormat() #

Con el fin de aumentar la velocidad de operación durante la optimización de los parámetros del EA, las funciones Comment(), Print() y PrintFormat() no se ejecutan. La excepción es el uso de estas funciones dentro del manejador OnInit(). Eso permite facilitar la búsqueda de las causas de los errores cuando surgen.

Funciones Alert(), MessageBox(), PlaySound(), SendFTP, SendMail(), SendNotification(), WebRequest() #

Las funciones de interacción con el "mundo externo" Alert(), MessageBox(), PlaySound(), SendFTP(), SendMail(), SendNotification() y WebRequest() no se ejecutan en el Probador.

 

Modos de generación de ticks #

Un Asesor Experto escrito en el lenguaje MQL5 es un programa que se inicia cada vez como respuesta a una acción externa - un evento. Para cada evento predefinido el EA dispone de una función correspondiente a este evento - manejador de eventos.  

El evento más importante para un EA es el cambio del precio - NewTick. Por esta razón, para la simulación de los EAs es necesario generar las secuencias de ticks. En el Probador del Terminal de Cliente MetaTrader 5 hay 3 modos de generación de ticks:

  • Todos los ticks
  • Precios OHLC de las barras de un minuto (1 Minute OHLC)
  • Sólo precios de apertura

El modo "Todos los ticks" es el modo básico y el más detallado de generación de los ticks, los dos restantes es una simplificación del modo principal y serán descritos en comparación con el modo "Todos los ticks". Vamos a analizar los tres modos para comprender la diferencia entre ellos.

Todos los ticks

El historial de cotizaciones de los instrumentos financieros se traspasa del servidor comercial al Terminal de Cliente MetaTrader 5 en forma de los bloques de barras de un minuto bien comprimidos. Usted puede encontrar la información detallada sobre cómo se hace la solicitud y construcción de los períodos necesarios en el apartado de la ayuda Organización de acceso a los datos.

El elemento mínimo del historial de precios es una barra de un minuto desde la que se puede conseguir la información sobre los valores de cuatro precios:

  • Open - precio de apertura de la barra de un minuto;
  • High - el máximo alcanzado durante esta barra de un minuto;
  • Low - el mínimo alcanzado durante esta barra de un minuto;
  • Close - precio de cierre de la barra.

La nueva barra de un minuto no se abre en el momento cuando se empieza un nuevo minuto (el número de segundos llega a ser 0), sino cuando llega un tick nuevo, es decir, cuando el precio se cambia por lo menos en un punto. En la imagen se muestra la primera barra minutera de nueva semana de trading con fecha y hora de apertura 2011.01.10. 00:00. La diferencia de precios entre el viernes y el lunes que vemos en el gráfico es un hecho corriente, puesto que incluso durante los días de descanso las cotizaciones de divisas van cambiando en respuesta a las noticias que llegan.

new_bar_of_week

Respecto a esta barra minutera sólo sabemos que fue abierta el 10 de Enero de 2011 a las 00:00, pero no sabemos nada de los segundos. Esto podía pasar a las 00:00:12 o 00:00:36 (pasados 12 o 36 segundos desde el inicio de la nueva jornada), o cualquier otro momento dentro de este minuto. Pero lo que sabemos exactamente es que en el momento de apertura de la nueva barra minutera el precio Open para EURUSD se encontraba en 1.28940.

Igualmente, tampoco sabemos con una precisión de un segundo cuándo ha llegado el tick correspondiente al precio de cierre de la barra en cuestión. Lo único que sabemos es que se trata del último precio en esta barra minutera que ha sido apuntado como el precio Close. Para este minuto este precio es 1.28958. El tiempo de aparición de los precios High y Low tampoco se sabe. Pero sabemos que el precio máximo y el mínimo ha alcanzado sin duda alguna los niveles 1.28958 y 1.28940, respectivamente.

Para probar la estrategia comercial nos hace falta una secuencia de ticks sobre la que va a emularse el trabajo del EA. De esta manera, para cada barra de un minuto sabemos 4 puntos de control sobre los que podemos decir con total seguridad que el precio ha estado ahí. Si una barra tiene sólo 4 ticks, esta información será suficiente para la simulación, pero normalmente el volumen de tick es superior a 4. Eso significa que hace falta generar los puntos de control adicionales para los ticks que han llegado entre los precios Open, High, Low y Close. El principio de generación de los ticks en el modo "Todos los ticks" se describe en el artículo Algoritmo de generación de los ticks en el Probador de Estrategias del terminal MetaTrader 5 la ilustración desde el cual se muestra más abajo.

ideal_white_MQL5_2

Durante la simulación el modo "Todos los ticks" la función OnTick() del EA va a llamarse en cada punto de control, siendo cada punto de control un tick desde la secuencia generada. El EA va a recibir la hora y el precio del tick modelado igualmente como durante el trabajo en tiempo real.

Importante: el modo de simulación "Todos los ticks" es el más preciso pero también es el más duradero. Para una valoración primaria de la mayoría de las estrategias comerciales generalmente es suficiente utilizar alguno de otros dos modos de prueba.

1 minute OHLC

El modo de simulación "Todos los ticks" es el más preciso de los tres, pero al mismo tiempo es el más lento. La función OnTick() se inicia para cada tick, y el volumen de tick puede ser bastante grande. Para las estrategias a las que no les importa en qué secuencia de ticks se desarrollaba el precio en el transcurso de cada barra existe el modo de modelación más rápido y más aproximado - "1 minute OHLC".

En el modo "1 minute OHLC" la secuencia de ticks se construye sólo a base de los precios OHLC de las barras de un minuto, en este caso el número de puntos de control generados se reduce sustancialmente. Por consiguiente, se reduce la duración de la prueba. El inicio de la función OnTick() se hace en todos los puntos de control que se construyen a base de los precios OHLC de las barras minuteras.

La renuncia a generar los ticks intermedios adicionales entre los precios Open, High, Low y Close hace que aparece una determinación fuerte en el desarrollo del precio a partir del momento en el que ha sido determinado el precio Open. Esto da posibilidades para crear el "Grial de simulación" que muestra durante la simulación un bonito gráfico ascendiente. Puede encontrar un ejemplo de este Grial en Code Base - Grr-al.

graal_OHLC

En la imagen podemos ver un gráfico muy atractivo de simulación de este EA. ¿Cómo ha salido así? Para una barra minutera se saben 4 precios. Y es sabido con seguridad que el precio Open va primero, y el último es el precio Close. Entre ellos hay precios High y Low, el orden de su aparición no se sabe, pero se sabe que el precio High es mayor o igual al precio Open (el precio Low es menor o igual al precio Open).

Basta con averiguar el momento de llegada del precio Open y luego analizar el siguiente tick con el fin de determinar qué es lo que tenemos delante, ¿High o Low? Si el precio es más bajo que el precio Open, eso significa que tenemos delante de nosotros el precio Low - entonces hacemos la compra en este tick. El siguiente tick va a corresponder al precio High en el que cerramos la compra y abrimos la venta. El siguiente tick es el último - es el precio Close, cerramos la venta en este tick.

Si después del precio ha llegado un tick con el precio que es más alto que el precio de apertura, entonces la secuencia de transacciones es inversa. Vamos a procesar en este modo tramposo la barra de un minuto y esperamos la siguiente. Mientras probamos este EA en los datos históricos, todo va perfectamente. Pero cuando lo hagamos en tiempo real, el cuento de hadas se desvanece -la línea del balance sigue siendo recta pero va hacia abajo. Para desvelar el truco, sólo hay que probar este EA en el modo "Todos los ticks".

Importante: si los resultados de simulación del EA con el uso de los modos aproximados ("1 minute OHLC" y "Sólo precios de apertura") salen muy buenos, haga la prueba obligatoriamente en el modo "Todos los ticks".

Sólo precios de apertura

En este modo se generan los ticks sobre los precios OHLC del período (timeframe) seleccionado para la simulación. En este caso la función OnTick() del EA se inicia sólo al principio de la barra para el precio Open. Gracias a esta particularidad los niveles stop y las órdenes pendientes pueden iniciarse por el precio diferente al especificado (sobre todo durante la simulación en los períodos mayores). A cambio de eso, tenemos la posibilidad de llevar a cabo la simulación estimativa del EA de forma bastante rápida.

La excepción durante la simulación de ticks en el modo "Sólo precios de apertura" son los períodos W1 y MN1: para estos períodos los ticks se generan para los precios OHLC de cada día, y no para los precios OHLC de la semana y mes, respectivamente.

Por ejemplo, se hace la prueba del EA para EURUSD H1 en el modo "Sólo precios de apertura". En este caso, el número total de los ticks (puntos de control) no va a superar 4*número de barras de una hora que están dentro del intervalo de simulación. Pero la llamada al manejador OnTick() se hace sólo en el momento de la apertura de la barra de una hora. En los demás ticks ("invisibles" para el EA) se hacen las comprobaciones necesarias para la simulación correcta:

  • cálculo de requerimientos de margen;
  • accionamiento de Stop Loss y Take Profit;
  • accionamiento de órdenes pendientes;
  • eliminación de órdenes pendientes caducadas.

Si no hay posiciones abiertas u órdenes pendientes, entonces tampoco hay necesidad en estas comprobaciones para los ticks invisibles, y el aumento de la velocidad puede llegar a ser bastante importante. El modo "Sólo precios de apertura" conviene muy bien para probar las estrategias que realizan las transacciones sólo en la apertura de la barra, y no utilizan las órdenes pendientes ni tampoco las órdenes StopLoss,. TakeProfit. Para el tipo de estas estrategias se mantiene toda la precisión de simulación necesaria.

Como ejemplo de un EA para el que no importa el modo de simulación vamos a mostrar el EA Moving Average de la entrega estándar. La lógica de este EA está construida de tal manera que todas las decisiones se toman en la apertura de la barra y las transacciones se realizan enseguida, sin utilizar las órdenes pendientes. Vamos a arrancar la prueba del EA para EURUSD H1 en el intervalo desde 2010.01.09 hasta 2010.31.12, y compararemos los gráficos. En la imagen se muestran los gráficos del balance desde el informe del Probador para los tres modos.

El gráfico de simulación del EA Moving Average.mq5 desde la entrega estándar no depende del modo de simulación

Como puede ver, los gráficos de diferentes modos de simulación son absolutamente idénticos para el EA Moving Average desde la entrega estándar.

Existen ciertas limitaciones de la aplicación del modo "Sólo precios de apertura":

  • No se puede utilizar el modo de trading "Retraso aleatorio";
  • En el EA que se prueba no es posible acceder a los datos del inferior período que se utiliza para la simulación/optimización. Por ejemplo, si para la simulación/optimización se utiliza el período H1, Usted puede acceder a los datos del H2, H3, H4, etc., y no a los del M30, M20, M10, etc. Aparte de eso, los períodos mayores a los que se accede tienen que ser múltiplos del período de simulación. Por ejemplo, durante la simulación en el período M20 no se puede acceder al período M30, pero se puede dirigirse al H1. Estas limitaciones están condicionadas a la imposibilidad de obtener los datos de los períodos inferiores y no múltiplos desde las barras que se generan durante la simulación/optimización.
  • Las limitaciones del acceso a los datos de otros períodos se extienden también a otros símbolos cuyos datos utiliza el EA. No obstante, en este caso la limitación para cada símbolo depende del primer período al que se ha accedido durante la simulación/optimización. Por ejemplo, la simulación se lleva a cabo para el símbolo y período EURUSD H1, el EA se ha dirigido por primera vez al símbolo GBPUSD M20. En esta situación el EA puede utilizar en adelante los datos del EURUSD H1, H2, etc., así como los del GBPUSD M20, H1, H2, etc.

Importante: el modo "Sólo precios de apertura" es el más rápido por el tiempo que dura la simulación, pero no conviene para todas las estrategias de trading. Se debe seleccionar el modo de simulación necesario en función de las particularidades del sistema de trading que se utiliza.

Al final de esta sección que trata sobre los modos de modelación vamos a mostrar la comparación visual de diferentes modos de generación de ticks para EURUSD para dos barras de M15 en el intervalo 2011.01.11 21:00:00 - 2011.01.11 21:30:00. Los ticks ha sido escritos en archivos diferentes, utilizando el EA WriteTicksFromTester.mq5. La terminación de los nombres de estos archivos se establecen en los parámetros input filenamEveryTick, filenameOHLC y filenameOpenPrice.

EA_inputs_in_tester

Para obtener tres archivos con tres secuencias de ticks (para cada uno de los modos "Todos los ticks", "OHLC en barras minuteras" y "Sólo precios de apertura") el EA ha sido arrancado tres veces en los modos correspondientes en repasos únicos. Luego, utilizando el indicador TicksFromTester.mq5, los datos han sido proyectados al gráfico desde estos tres archivos. El código del indicador va adjunto al artículo.

three_tick_series

Por defecto, todas las operaciones con archivos en el lenguaje MQL5 se realizan dentro de los límites de una "sandbox de archivos", y durante la simulación el EA tiene disponible sólo su propia "sandbox de archivos". Para que el indicador y el EA puedan trabajar durante la simulación con los archivos de la misma carpeta, se utiliza la bandera FILE_COMMON. Ejemplo del código del EA:

//--- abrimos el archivo
   file=FileOpen(filename,FILE_WRITE|FILE_CSV|FILE_COMMON,";");
//--- comprobamos el éxito de la operación
   if(file==INVALID_HANDLE)
     {
      PrintFormat("No se ha podido abrir el archivo %s para la escritura. Código del error=%d",filename,GetLastError());
      return;
     }
   else
     {
      //--- avisamos sobre el guardado en la carpeta compartida de todos los terminales de cliente y sobre su ubicación
      PrintFormat("El archivo será guardado en la carpeta %s",TerminalInfoString(TERMINAL_COMMONDATA_PATH));
     }

En el indicador para la lectura de datos también se ha utilizado la bandera FILE_COMMON, lo que ha permitido evitar el traspaso manual de los archivos necesarios de una carpeta a otra.

//--- abrimos el archivo
   int file=FileOpen(fname,FILE_READ|FILE_CSV|FILE_COMMON,";");
//--- comprobamos el éxito de la operación
   if(file==INVALID_HANDLE)
     {
      PrintFormat("No se ha podido abrir el archivo %s para la lectura. Código del error=%d",fname,GetLastError());
      return;
     }
   else
     {
      //--- indicaremos la ubicación de la carpeta compartida de todos los terminales de cliente
      PrintFormat("El archivo será leído desde la carpeta %s",TerminalInfoString(TERMINAL_COMMONDATA_PATH));
     }

Modelación de spreads #

La diferencia entre los precios Bid y Ask se llama spread. El spread no se modela durante la simulación, sino se coge de los datos históricos. If the spread is less than or equal to zero in the historical data, then the last known (at the moment of generation) spread  of is used by testing agent.

En el Probador un spread siempre se considera como flotante. Es decir, SymbolInfoInteger(symbol, SYMBOL_SPREAD_FLOAT) siempre devuelve true.

Además, en los datos históricos se guardan los valores de los volúmenes de ticks y de los volúmenes comerciales. Para almacenar y obtener los datos, se utiliza una estructura especial MqlRates:

struct MqlRates
  {
   datetime time;         // fecha/hora de apertura de la barra
   double   open;         // precio de apertura Open
   double   high;         // precio máximo High
   double   low;          // precio mínimo Low
   double   close;        // precio de cierre Close
   long     tick_volume;  // volumen de ticks
   int      spread;       // spread
   long     real_volume;  // volumen de bolsa
  };

Uso de ticks reales durante la simulación #

La simulación y optimización con ticks reales se aproxima al máximo a las condiciones reales. En lugar de ticks generados basados en los datos de minuto, se utilizan los ticks reales almacenados por el bróker. Dichos ticks provienen de la bolsa y los proveedores de liquidez.

Con objeto de garantizar una precisión mayor, también se utilizan las barras de minuto. En ellas se verifican y corrigen los datos de los ticks. De esta forma, también se evita la divergencia de los gráficos en el simulador y en el terminal del cliente.

El simulador comprueba la correspondencia entre los datos de los ticks y los parámetros de las barras de minuto, es decir: el tick no deberá exceder los precios High/Low de la barra; el tick que abre y cierra el minuto deberá coincidir con los precios Open/Close de la barra. También se compara el volumen. Si se detecta una disparidad, se descartarán los ticks correspondientes a esta barra de minutos. En lugar de ellos, se utilizarán los ticks generados (como en el modo "Cada tick").

Si en la historia del símbolo existe una barra de minuto sin datos de ticks para la misma, el simulador generará ticks en el modo "Todos los ticks". Esto permitirá dibujar correctamente el gráfico en el simulador, en el caso de que los datos de ticks del bróker estén incompletos.

Si en la hitoria del símbolo no existe una barra de minuto, pero sí hay datos de ticks apropiados para ese minuto, los datos pueden ser utilizados en el simulador. Por ejemplo, las barras de los símbolos de la bolsa se forman de acuerdo con los precios Last. Si desde el servidor llegan solo ticks con precios Bid/Ask pero sin precio Last, la barra no se formará. El simulador usará estos datos de ticks, ya que no contradicen las de minuto.

Los datos de ticks pueden diferenciarse de las barras de minuto por varios motivos. Por ejemplo, debido a desconexiones u otros fallos al transferirse al terminal de cliente los datos desde la fuente. Durante las simulaciones, los datos de minuto se consideran más fiables.

Al simular con ticks reales, deberá tener en cuenta los siguientes aspectos:

  • Al realizar la simulación, los datos de minuto del símbolo se sincronizarán junto con los datos de los ticks.
  • Los ticks se almacenan en la caché del símbolo en el simulador de estrategias. El tamaño de la caché es inferior a los 128 000 ticks. Cuando llegan los nuevos ticks, los datos más antiguos de los mismos se expulsarán de la caché. Entre tanto, la función CopyTicks nos ayudará a obtener los ticks fuera de la caché (esto sucede en las simulaciones con ticks reales). En este caso, los datos se solicitarán a partir de la base de ticks del simulador, que se corresponde totalmente con la base análoga del terminal de cliente. En dicha base no se realiza ninguna corrección de las barras de minuto, por ello, los ticks en la misma pueden diferenciarse de los ticks en la caché.

Variables globales del Terminal de Cliente #

Durante la simulación, las variables globales del Terminal de Cliente se emulan también, pero no están relacionadas de ninguna manera con auténticas variables globales del terminal que se puede ver en el terminal utilizando el botón F3. Eso quiere decir que todas las operaciones con las variables globales del terminal durante la simulación se realizan fuera del mismo (en el agente de pruebas).

Cálculo de indicadores durante la simulación #

En el modo de tiempo real, los valores de los indicadores se calculan en cada tick.

En el simulador de estrategias, los indicadores se calculan solo al recurrir a los mismos, es decir, solo en el momento cuando se solicitan los valores de los búferes de indicador. La excepción son los indicadores personalizados con #property tester_everytick_calculate configurado; en dicho caso, el recálculo se realiza en cada tick.

En el modo de simulación visual, todos los indicadores se recalculan incondicionalmente al llegar un nuevo tick, para que así se muestren correctamente en el gráfico de simulación visual.

El cálculo del indicador en cada tick se realiza una vez, y todas las posteriores solicitudes de datos del indicador antes de que llegue un nuevo tick no provocan un nuevo cálculo. Por consiguiente, si el temporizador se ha habilitado en el EA usando la función EventSetTimer(), entonces antes de cada llamada del manejador OnTimer(), se solicitarán los datos del indicador desde el último tick. Si el indicador aún no se ha calculado en el último tick, se calcularán los valores del indicador. Si los datos ya han sido preparados, estos se ofrecerán sin realizar un nuevo recálculo.

Por ello, todos los cálculos de los indicadores se realizan de la forma más económica posible: si el indicador ya se ha calculado en un momento determinado, los datos del mismo se ofrecen tal cual, el indicador no se recalcula.

Carga del historial durante la simulación #

El historial para el instrumento a probar se sincroniza y se descarga por el terminal desde el servidor comercial antes del inicio del proceso de simulación. En este caso, el terminal descarga desde el servidor comercial por primera vez todo el historial disponible para el instrumento a probar para luego ya no volver a este asunto. A continuación, se descargan sólo los datos nuevos.

El agente de pruebas recibe del Terminal de Cliente el historial para el instrumento a probar justamente después de que haya sido iniciado el proceso de simulación. Si durante el proceso de simulación se utilizan los datos de otros instrumentos (por ejemplo, si se trata de un EA de múltiples divisas), entonces en este caso el agente de pruebas solicita al Terminal de Cliente el historial necesario durante la primera invocación. Si los datos históricos se encuentran dentro del terminal, se pasan enseguida a los agentes de pruebas. Si no hay datos necesarios, el terminal los solicitará y descargará desde el servidor, y luego los pasará a los agentes de pruebas.

También se realiza el acceso a los instrumentos adicionales en el caso cuando se calcula el precio del tipo de cambio cruzado durante las operaciones de trading. Por ejemplo, durante la simulación de la estrategia sobre EURCHF con la moneda del depósito en dólares de los EE.UU. el agente de pruebas solicita al Terminal de Cliente el historial para EURUSD y USDCHF antes de procesar la primera operación de comercial, aunque la estrategia no supone la invocación directa a estos instrumentos financieros.

Antes de empezar a probar una estrategia de múltiples divisas, se recomienda descargar previamente todos los datos históricos necesarios al Terminal de Cliente. Esto permitirá evitar las demoras durante la simulación/optimización relacionadas con la descarga complementaria de datos que faltan. Por ejemplo, puede descargar el historial si abre los gráficos correspondientes y los desplaza hacia el inicio del historial. Puede encontrar un ejemplo de la descarga forzosa del historial al Terminal de Cliente en el apartado Organización de acceso a los datos de la documentación sobre MQL5.

Los agentes de pruebas en su lugar reciben el historial en forma comprimida desde terminal. Durante la simulación repetida el Probador ya no vuelve a descargar el historial desde el terminal, puesto que quedan los datos después del arranque anterior del Probador.

  • El terminal descarga el historial desde el servidor comercial sólo una vez cuando el agente se dirige al terminal a por el historial para el símbolo a probar. El historial se descarga en forma comprimida con el fin de ahorrar el tráfico.
  • Los ticks no se mandan por la red, sino se generan por los agentes de pruebas.

Simulación en múltiples divisas #

El Probador permite llevar a cabo la simulación sobre el historial de estrategias que tradean utilizando varios instrumentos financieros. A estos EAs se les llaman condicionalmente de múltiples divisas, porque desde el principio en la plataformas anteriores la simulación se realizaba sólo para un instrumento financiero. Mientras que en el Probador del terminal MetaTrader 5 se puede simular el trading con todos los instrumentos disponibles.

El historial para los instrumentos utilizados se descarga por el Probador desde el Terminal de Cliente (¡no desde el servidor comercial!) de forma automática cuando se le llama al instrumento en cuestión por primera vez.

El agente de pruebas descarga sólo el historial que falta con pequeña reserva con el fin de asegurar los datos históricos necesarios para el cálculo de los indicadores en el momento de simulación. El volumen mínimo del historial a descargar desde el servidor comercial para los períodos D1 e inferiores es de un año. De esta manera, si se inicia la simulación en el intervalo 2010.11.01-2010.12.01 (simulación en el intervalo de un mes) con el período M15 (cada barra es igual a 15 minutos), entonces se le solicitará al terminal el historial para el instrumento durante el año 2010 entero. Para los períodos Weekly será solicitado el historial de 100 barras, lo que supone aproximadamente dos años (hay 52 semanas en el año). Para la simulación sobre el período mensual Monthly el agente solicitará el historial de 8 años (12 meses * 8 años = 96 meses).

Si por alguna razón resulta imposible conseguir antes del inicio de la prueba el número de barras necesario para realizar la simulación, entonces la fecha del inicio de la simulación será acercada automáticamente hacia el presente para alcanzar esta reserva necesaria.

Durante la simulación se emula también la "Observación del Mercado" desde la cual se puede obtener la información sobre los instrumentos. Por defecto, al comienzo de la simulación la "Observación del Mercado" del Probador contiene sólo un símbolo, para el que ha sido iniciada la simulación. Todos los símbolos necesarios se conectan a la "Observación del Mercado" del Probador (¡no del Terminal!) de forma automática en cuanto se hace la llamada a ellos.

Antes de empezar la simulación de un EA de múltiples divisas, hay que seleccionar los instrumentos necesarios para esta simulación en la "Observación del Mercado" del terminal y bajar los datos necesarios en la profundidad necesaria. Al llamar a un símbolo "ajeno" por primera vez, se ejecuta automáticamente la sincronización para este símbolo entre el agente de pruebas y el Terminal de Cliente. Un símbolo "ajeno" es aquél que se diferencia del símbolo sobre el que ha sido iniciada la simulación.

La llamada a los datos del símbolo ajeno se hace en las siguientes ocasiones:

  • llamada a las series temporales para el par símbolo/período utilizando las funciones:

En el momento cuando se hace la primera invocación a un símbolo ajeno, el proceso de simulación se detiene y se realiza la descarga adicional de datos históricos que faltan para el par símbolo/período desde el terminal al agente de pruebas. Al mismo tiempo se activa el proceso de generación de la secuencia de ticks para este símbolo.

Para cada instrumento se genera su propia secuencia de ticks de acuerdo con el modo de generación de ticks seleccionado. Aparte de eso, se puede solicitar el historial para los símbolos necesarios de forma explícita mediante la llamada a la función SymbolSelect() en el manejador OnInit(). En este caso el historial será descargado en el acto, antes que empiece a probar su EA.

De esta manera, para llevar a cabo la simulación de múltiples divisas en el Terminal de Cliente MetaTrader 5, no hace falta hacer ningunos esfuerzos adicionales. Bastará con abrir los gráficos de los instrumentos correspondientes en el Terminal de Cliente. El historial de los símbolos necesarios se descargará automáticamente desde el servidor comercial con la condición si dispone de estos datos.

Modelación de la hora en el Probador #

Durante la simulación la hora local TimeLocal() siempre es iguala a la hora del servidor TimeTradeServer(). En su lugar, la hora del servidor siempre es iguala la hora que corresponde a la hora GMT - TimeGMT(). Así, durante la simulación todas estas funciones muestran la misma hora.

La falta de diferencia entre GMT, la hora local y de servidor en el Probador está hecha a propósito debido a que la conexión con el servidor no siempre puede ser permanente. Mientras que los resultados de la simulación tienen que ser iguales, independientemente de que si hay conexión o no. La información sobre la hora de servidor no se guarda de forma local, sino se coge en el servidor.

Objetos gráficos durante la simulación #

La construcción de los objetos gráficos no se hace durante la simulación/optimización. De esta manera, el EA obtiene los valores cero cuando se dirige a las propiedades del objeto creado durante la simulación/optimización.

Esta limitación no concierne a la simulación en modo visual.

Función OnTimer() en el Probador #

En MQL5 se puede procesar los eventos del temporizador. La llamada al manejador OnTimer() se realiza independientemente del modo de simulación. Eso significa que si la simulación ha sido iniciada en el modo "Sólo los precios de apertura" sobre el período H4 y dentro del EA está instalado un temporizador con la llamada cada segundo, entonces durante la apertura de cada barra H4 el manejador OnTick() será llamado una vez, y 14400 veces (3600 segundos * 4 horas) durante la barra será llamado el manejador OnTimer(). En cuánto va a aumentarse el tiempo de simulación depende de la lógica del EA.

Hemos escrito un simple EA sin operaciones comerciales para comprobar la dependencia del tiempo de simulación de la periodicidad del temporizador.

//--- input parameters
input int      timer=1;              // valor del temporizador, segundos
input bool     timer_switch_on=true; // temporizador activado
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- iniciamos el temporizador si timer_switch_on==true
   if(timer_switch_on)
     {
      EventSetTimer(timer);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- detenemos el temporizador
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
// no hacemos nada, el cuerpo del manejador está vacío
  }
//+------------------------------------------------------------------+

Se ha medido el tiempo de simulación con diferentes valores del parámetro timer (periodicidad del evento Timer). A base de los datos obtenidos se ha construido el gráfico de dependencia del tiempo de simulación T del valor de periodicidad Period.

Time_Dependence

Aquí se ve muy bien, cuanto más bajo sea el parámetro timer en el momento de inicialización del temporizador por la función EventSetTimer(timer), es menor el período (Period) entre las llamadas al manejador OnTimer(), y es mayor el tiempo de simulación T durante las mismas condiciones restantes.

Función Sleep() en el Probador #

La función Sleep() permite parar temporalmente la ejecución del programa mql5 en el EA o script durante el trabajo en el gráfico. Esto puede ser útil cuando se solicitan algunos datos que en el momento de la solicitud aún no están listos y hace falta esperar hasta que estén disponibles. Puede encontrar un ejemplo detallado del uso de la función Sleep() en el apartado Organización de acceso a los datos.

Pero en el Probador las llamadas a la función Sleep() no retrasan el proceso de simulación. Cuando se llama a la función Sleep(), "se reproducen" los ticks generados dentro del margen del retraso especificado, como resultado de lo cual pueden accionarse las órdenes pendientes, stops, etc. Después de la llamada a la función Sleep(), el tiempo modelado en el Probador se aumenta al intervalo especificado en el parámetro de la función Sleep.

Si como resultado de la ejecución de la función Sleep() el tiempo actual en el Probador ha salido fuera del período de simulación, verá el mensaje del error "ciclo infinito en Sleep". En caso de este error, los resultados de la simulación no se omiten. Todos los cálculos se realizan íntegramente (número de transacciones, reducciones, etc.).

La función Sleep() no va a funcionar en OnDeinit(), ya que tras su llamada el tiempo de simulación sí o sí saldrá fuera del intervalo de simulación.

Esquema del uso de la función Sleep() en el Probador del terminal MetaTrader 5.

Uso del Probador para las tareas de optimización en los cálculos matemáticos #

En el terminal MetaTrader 5 se puede utilizar el Probador no sólo para probar las estrategias comerciales, sino también para los cálculos matemáticos. Para eso hay que seleccionar el modo correspondiente en las opciones:

math_calculations

Al elegir el modo "Cálculos matemáticos", será realizado un repaso en "vacío" del agente de simulación. El repaso en "vacío" significa que no se realizará la generación de los ticks ni tampoco va a cargarse el historial. Durante este repaso sólo serán llamadas las funciones OnInit(), OnTester() y OnDeinit().

Si la fecha de finalización de la prueba es menor o igual a la fecha de su inicio, esto también va a significar la simulación en el modo "Cálculos matemáticos".

Durante el uso del Probador para la solución de tareas matemáticas, la descarga del historial y la generación de los ticks no se hace.

Una tarea matemática muy típica a resolver en el Probador del MetaTrader 5 es la búsqueda del extremo de la función de muchas variables. Para su solución hace falta:

  • Colocar el bloque de cálculos del valor de la función de muchas variables en OnTester(), y devolver el valor calculado a través de return(valor_de_la_función);
  • Pasar los parámetros de la función al área global del programa en forma de las variables input;

Compilamos el EA, abrimos la ventana "Probador". En la pestaña "Parámetros de entrada" marcamos los parámetros de entrada necesarios y establecemos para ellos los límites en el espacio de los valores y el paso para el repaso.

Seleccionamos el tipo de optimización: "Lenta" (repaso completo de parámetros) o "Rápida" (algoritmo genético). Es mejor seleccionar la optimización rápida para la simple búsqueda del extremo de la función. Pero si hace falta calcular los valores en todo el espacio de las variables, mejor conviene la optimización lenta.

Seleccionamos el modo "Cálculos matemáticos" e iniciamos el proceso de optimización haciendo clic en el botón "Empezar". Hay que recordar que durante la optimización siempre se busca el máximo local del valor de la función OnTester. Para la búsqueda del mínimo local se puede devolver de la función OnTester el valor inverso al valor calculado de la función:

return(1/valor_de_la_función);

En este caso Usted mismo debe comprobar que el valor_de_la_función no sea igual a cero, ya que de lo contrario se puede recibir un error crítico de división por cero. Hay otra opción que es más conveniente y que no altera los resultados de la optimización, ha sido ofrecido por los lectores del artículo:

return(-valor_de_la_función);

Aquí no hace falta comprobar si el valor_de_la_función es igual a cero, y la misma superficie de los resultados de la optimización en representación 3D tiene la misma forma pero reflejada de espejo respecto a la inicial.

Como ejemplo vamos a coger la función sink():

sink_formula

El código del EA para la búsqueda del extremo de esta función vamos a colocar en OnTester():

//+------------------------------------------------------------------+
//|                                                         Sink.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- input parameters
input double   x=-3.0; // start=-3, step=0.05, stop=3
input double   y=-3.0; // start=-3, step=0.05, stop=3
//+------------------------------------------------------------------+
//| Tester function                                                  |
//+------------------------------------------------------------------+
double OnTester()
  {
//---
   double sink=MathSin(x*x+y*y);
//---
   return(sink);
  }
//+------------------------------------------------------------------+

Hagamos la optimización y mostremos los resultados de la optimización como un gráfico 2D.

Resultados de la optimización completa de la función sink(x*x+y*y) en forma del gráfico en 2D.

Cuanto mejor sea el valor para el par de parámetros establecido (x, y), más denso será el color. Tal como se esperaba partiendo de la vista de la fórmula de la función sink(), sus valores forman unos círculos concéntricos con el centro en el punto (0,0). Para la función sink() no existe un extremos absoluto. Esto se ve muy bien cuando vemos los resultados de optimización en el modo 3D:

3d

Sincronización de las barras durante la simulación en el modo "Sólo precios de apertura" #

El Probador en el terminal MetaTrader 5 permite probar también así llamados Asesores Expertos "de múltiples divisas". Un EA de múltiples divisas es un Asesor Experto que opera con dos o más símbolos.

La simulación de las estrategias que tradean con varios instrumentos impone al Probador unos requerimientos técnicos adicionales:

  • generación de ticks para estos instrumentos;
  • cálculo de valores de los indicadores para estos instrumentos;
  • cálculo de requerimientos del margen para estos instrumentos;
  • sincronización de secuencias de ticks generadas para todos los instrumentos con los que se tradea.

El Probador genera y reproduce una secuencia de ticks para cada instrumento en función del modo de trading seleccionado. En este caso la nueva barraen cada instrumento se abre independientemente de cómo se ha abierto la barra en otro instrumento. Eso significa que durante la simulación de un EA de múltiples divisas puede surgir la situación (suele pasar con bastante frecuencia) cuando en un instrumento la barra ya se ha abierto y en el otro todavía no. De esta manera, durante la simulación pasa lo mismo que pasa en la vida real.

Esta auténtica modelación del desarrollo del historial en el Probador no causa preguntas hasta que utilizamos los modos de simulación "Todos los ticks" y "1 minute OHLC". Durante el uso de estos modos, dentro de los límites de una vela se genera una cantidad de ticks suficiente para esperar el momento de sincronización de las barras de diferentes símbolos. ¿Pero cómo vamos a probar las estrategias de múltiples divisas en el modo "Sólo precios de apertura" si se requiere la sincronización obligatoria de las barras para los instrumentos en los que tradeamos? Pues, en este modo el EA es llamado sólo en el tick que correspondiente a la hora de apretura de la barra.

Lo explicaremos en un ejemplo: si probamos nuestro EA sobre el símbolo EURUSD, y en EURUSD ha sido abierta una nueva vela, será muy fácil enterarnos de eso. Es que durante la simulación en el modo "Sólo precios de apertura", el evento NewTick corresponde al momento de apertura de la barra en el período de la prueba. Pero no existe garantía alguna de que la nueva vela se ha abierto para el símbolo GBPUSD que se utiliza en el EA.

En condiciones normales bastará con finalizar el trabajo de la función OnTick() y comprobar la aparición de la nueva barra para GBPUSD durante el siguiente tick. Pero durante la simulación en el modo "Sólo precios de apertura" no habrá ningún otro tick, y a lo mejor puede formarse la impresión que este modo no vale para probar los EAs de múltiples divisas. Pero eso no es así. No olvide que el Probador en MetaTrader 5 se comporta igual como en la vida real. Se puede esperar el momento cuando para el otro símbolo se abrirá una nueva barra mediante la función Sleep()!

Este es código del EA Synchronize_Bars_Use_Sleep.mq5 que muestra el ejemplo de sincronización de las barras durante la simulación en el modo "Sólo precios de apertura":

//+------------------------------------------------------------------+
//|                                   Synchronize_Bars_Use_Sleep.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- input parameters
input string   other_symbol="USDJPY";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- confrontamos el símbolo actual
   if(_Symbol==other_symbol)
     {
      PrintFormat("¡Hace falta especificar otro símbolo o iniciar la simulación sobre otro símbolo!");
      //--- finalizamos la prueba forzosamente
      return(INIT_PARAMETERS_INCORRECT);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- variable estática para guardar la hora de apertura de la última barra
   static datetime last_bar_time=0;
//---  indicio de que la hora de apertura de la última barra de diferentes símbolos está sincronizada
   static bool synchonized=false;
//--- si la variable estática aún no está inicializada
   if(last_bar_time==0)
     {
      //--- es la primera llamada, apuntaos la hora de apertura y salimos
      last_bar_time=(datetime)SeriesInfoInteger(_Symbol,Period(),SERIES_LASTBAR_DATE);
      PrintFormat("Hemos inicializado la variable last_bar_time con el valor %s",TimeToString(last_bar_time));
     }
//--- obtenemos la hora de apertura de la última barra para nuestro símbolo
   datetime curr_time=(datetime)SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);
//--- si la hora de apertura de la barra actual no coincide con la que se guarda en last_bar_time
   if(curr_time!=last_bar_time)
     {
      //--- recordamos la hora de apertura de la nueva barra en la variable estática
      last_bar_time=curr_time;
      //--- la sincronización ha sido violada, mostramos la bandera false
      synchonized=false;
      //--- mostramos el mensaje sobre este evento
      PrintFormat("Para el símbolo %s se ha abierto nueva barra a las %s",_Symbol,TimeToString(TimeCurrent()));
     }
//--- aquí vamos a guardar la hora de apertura de la barras para el símbolo ajeno
   datetime other_time;
//--- ciclo, hasta que la hora de apertura de la última barra para el otro símbolo coincida con curr_time
   while(!(curr_time==(other_time=(datetime)SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE)) && !synchonized))
     {
      PrintFormat("Esperaremos 5 segundos..");
      //--- esperaremos 5 segundos y volveremos a solicitar SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE)
      Sleep(5000);
     }
//--- la hora de apertura de la barra ahora es igual para los dos símbolos
   synchonized=true;
   PrintFormat("La hora de apertura de la última barra para nuestro símbolo %s: %s",_Symbol,TimeToString(last_bar_time));
   PrintFormat("La hora de apertura de la última barra para el símbolo %s: %s",other_symbol,TimeToString(other_time));
//--- TimeCurrent() no vale, utilizamos TimeTradeServer() para
   Print("Las barras han sido sincronizadas a las ",TimeToString(TimeTradeServer(),TIME_DATE|TIME_SECONDS));
  }
//+------------------------------------------------------------------+

Fíjense en la última línea del EA que nos muestra la hora actual a la que ha sido determinado el hecho de sincronización:

   Print("Las barras han sido sincronizadas a las ",TimeToString(TimeTradeServer(),TIME_SECONDS));

Para mostrar la hora actual hemos utilizado la función TimeTradeServer(), en vez de la TimeCurrent(). Es que la función TimeCurrent() devuelve la hora del último tick que no se ha cambiado de ninguna manera tras el uso de Sleep(). Inicie el EA en el modo "Sólo precios de apertura" y verá los mensajes sobre la sincronización de las barras.

Synchronize_Bars_Use_Sleep_EA

Utilice la función TimeTradeServer() en lugar de la TimeCurrent() si necesita obtener la hora de servidor actual y no la hora de llegada del último tick.

Hay otra forma de sincronizar las barras - utilizando el temporizador. El ejemplo de tal EA Synchronize_Bars_Use_OnTimer.mq5 se adjunta al artículo.

Función IndicatorRelease() en el Probador #

Después de la finalización de la simulación, se abre automáticamente el gráfico del instrumento en el cual se muestran las transacciones realizadas y los indicadores que se han utilizado en el EA. Esto ayuda comprobar de forma visual los momentos de entrada y salida, así como compararlos con los valores de los indicadores.

Importante: los indicadores mostrados en el gráfico abierto automáticamente tras finalizarse la simulación se calculan de nuevo ya después de completarse la prueba. Incluso si estos indicadores han sido utilizados en el EA a probar.

Pero en algunas ocasiones el programador puede necesitar ocultar la información sobre los indicadores utilizados en el algoritmo de trading. Por ejemplo, el código del EA se alquila o se vende como archivo ejecutable sin proporcionar el código fuente. Para este propósito valdrá la función IndicatorRelease().

Si en la carpeta /profiles/templates del Terminal de Cliente hay una plantilla que se llama tester.tpl, precisamente esta plantilla será aplicada al gráfico que se abre. Si no hay esta plantilla, se aplica la plantilla predeterminada (default.tpl).

Desde el principio la función IndicatorRelease() está destinada para liberar la parte de cálculo del indicador, en caso de que ya no es necesario. Esto permite ahorrar la memoria y los recursos de la CPU, porque cada tick activa el cálculo del indicador. Su segundo cometido consiste en prohibir la visualización del indicador en el gráfico de simulación tras finalizarse el repaso único.

Para prohibir la visualización del indicador en el gráfico después de la simulación, invoque la función IndicatorRelease() con el handle del indicador en el manejador OnDeinit(). La función OnDeinit() siempre se invoca después de la finalización y antes de visualización del gráfico de simulación.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   bool hidden=IndicatorRelease(handle_ind);
   if(hidden) Print("IndicatorRelease() ejecutada con éxito");
   else Print("IndicatorRelease() ha devuelto false. Código del error ",GetLastError());
  }

Para prohibir la visualización del indicador en el gráfico después de la simulación, utilice la función IndicatorRelease() en el manejador OnDeinit().

Procesamiento de eventos en el Probador #

La presencia del manejador OnTick() en el EA no es obligatoria para que se pueda pobarlo sobre los datos históricos en el Probador del terminal MetaTrader 5. Será suficiente que en el EA haya por lo menos una función-manejador listadas más abajo:

  • OnTick() - manejador del evento de la llegada de un nuevo tick;
  • OnTrade() - manejador de un evento comercial;
  • OnTimer() - manejador del evento de la llegada de una señal de temporizador;
  • OnChartEvent() - manejador de los eventos del usuario.

Durante la simulación, en el Probador se puede procesar los eventos de usuario utilizando la función OnChartEvent(), pero en los indicadores esta función no se invoca en el Probador. Incluso si el indicador dispone del manejador OnChartEvent() y este indicador se utiliza en el EA, este indicador no va a recibir ningunos eventos personalizados.

Durante la simulación el indicador puede generar los eventos personalizados utilizando la función EventChartCustom(), y el EA puede procesar este evento en OnChartEvent().

Aparte de los eventos arriba mencionados en el Probador de Estrategias se generan los eventos especiales relacionados con el proceso de simulación y optimización:

  • Tester - este evento se genera cuando se finaliza la simulación del EA a base de los datos históricos. El procesamiento del evento Tester se hace por la función OnTester(). Se puede utilizar esta función sólo en los EAs durante la simulación, y en primer lugar está destinada para calcular un valor que se utiliza como criterio Custom max durante la optimización genética de los parámetros de entrada.
  • TesterInit - este evento se genera cuando se inicia el proceso de optimización en el Probador de Estrategias antes del primer repaso. El procesamiento del evento TesterInit se realiza por la función OnTesterInit(). El EA que dispone de este manejador se carga automáticamente, al iniciarse la optimización, en un gráfico nuevo del terminal con el símbolo y período especificados en el Probador, y recibe el evento TesterInit. La función está destinada para inicializar el EA antes del inicio de la optimización para el posterior procesamiento de los resultados de la optimización.
  • TesterPass - este evento se genera cuando llega un nuevo frame de datos. El procesamiento del evento TesterPass se realiza por la función OnTesterPass(). El EA con este manejador se carga automáticamente en un gráfico nuevo del terminal con el símbolo/período especificados para la simulación, y recibe durante la optimización el evento TesterPass cuando llegue un frame. La función está destinada para el procesamiento dinámico de los resultados de la optimización directamente "al vuelo", sin esperar su finalización. La agregación de los frames se realiza por la función FrameAdd(), que puede ser invocada cuando se finaliza el repaso único en el manejador OnTester().
  • TesterDeinit - este evento se genera cuando se termina el proceso de optimización del EA en el Probador de Estrategias. El procesamiento del evento TesterDeinit se realiza por la función OnTesterDeinit(). El EA con este manejador se carga automáticamente en el gráfico al iniciarse la optimización y recibe el evento TesterDeinit tras su finalización. Esta función está destinada para el procesamiento final de todos los resultados de la optimización.

Agentes de pruebas #

En el Terminal de Cliente MetaTrader 5 la simulación se realiza utilizando los agentes de pruebas. Los agentes locales se crean y se conectan de forma automática. Por defecto, el número de los agentes locales corresponde al número de núcleos que tiene el ordenador.

Cada agente de pruebas dispone de su propia copia de variables globales que no está relacionada de ninguna manera con el Terminal de Cliente. El mismo terminal desempeña el papel del operador que reparte las tareas para los agentes locales y remotos. Después de ejecutar la tarea de turno relacionada con la simulación de un EA con parámetros establecidos, el agente devuelve el resultado al terminal. Durante la prueba única se utiliza sólo un agente.

El agente guarda el historial que recibe del terminal en las carpetas separadas que llevan el nombre del instrumento. Es decir, el historial para EURUSD se guarda en la carpeta con el nombre EURUSD. Aparte de eso el historial de los instrumentos se separa según las fuentes. La estructura de almacenamiento del historial es la siguiente:

carpeta_del_probador\Agent-IPaddress-Port\bases\nombre_de_la_fuente\history\nombre_del_instrumento

Por ejemplo, el historial para EURUSD del servidor MetaQuotes-Demo se puede guardar en la carpeta_del_probador\Agent-127.0.0.1-3000\bases\MetaQuotes-Demo\EURUSD.

Después de haberse finalizado el proceso de simulación, el agente local se encuentra durante cinco minutos en el modo de espera de la siguiente tarea para no perder tiempo con el arranque en caso de las siguientes llamadas. Y sólo transcurrido este plazo de espera, el agente local finaliza su trabajo y se descarga de la memoria del ordenador.

En caso de la finalización anticipada de la simulación por parte del usuario (el botón "Cancelar"), así como en caso del cierre del terminal, todos los agentes locales finalizan su trabajo y se descargan de la memoria del ordenador.

Intercambio de datos entre el Terminal y el Agente #

Cuando se inicia el proceso de simulación, el terminal se prepara para enviar al agente unos bloques de parámetros:

  • Parámetros de entrada de simulación (modo de modelación, intervalo de simulación, instrumento, criterio de optimización, etc.)
  • Lista de instrumentos seleccionados en "Observación del Mercado"
  • Especificación del instrumento a probar (tamaño del contrato, desviaciones permitidas del mercado para colocar StopLoss y Takeprofit, etc.)
  • EA a probar y los valores de sus parámetros de entrada
  • Información sobre los archivos adicionales (bibliotecas, indicadores, archivos de datos - #property tester_...)

tester_indicator

string

Nombre del indicador personalizado en el formato "nombre_del_indicador.ex5". Los indicadores necesarios para la simulación se determinan automáticamente desde la llamada de la función iCustom(), si el parámetro correspondiente ha sido establecido con una constante literal. Para los demás casos (el uso de la función IndicatorCreate() o el uso de una cadena no constante en el parámetro que establece el nombre del indicador) hace falta esta propiedad

tester_file

string

Nombre del archivo para el Probador con extensión, encerrado entre dobles comillas (como constante literal). El archivo especificado será pasado al Probador. Siempre hay que especificar los archivos de entrada para la simulación, en caso de que haya necesidad de ellos

tester_library

string

Nombre de la biblioteca con extensión encerrado entre dobles comillas. Una biblioteca puede tener la extensión dll o ex5. Las bibliotecas necesarias para la simulación se determinan automáticamente. Sin embargo, si alguna biblioteca se utiliza por un indicador personalizado, esta propiedad es necesaria

Para cada bloque de parámetros se crea una huella digital en forma del hash MD5 que se envía al agente. El hash MD5 es único para cada conjunto de datos, siendo su volumen muchas veces menor que el volumen de información a base de la cual éste ha sido calculado.

El agente recibe los hashs de los bloques y los compara con los que ya tiene almacenados. Si el agente no dispone de la huella de este bloque de parámetros, o el hash recibido se diferencia del existente, el agente solicita el bloque de parámetros en sí. De esta manera, se reduce el volumen de tráfico entre el terminal y el agente.

Después de realizar la simulación, el agente devuelve al terminal todos los resultados de la prueba que se muestran en las pestañas "Resultados de simulación" y "Resultados de optimización": beneficio obtenido, número de transacciones, ratio de Sharpe, resultado de la función OnTester(), etc.

Durante la optimización, el terminal reparte entre los agentes las tareas para realizar la prueba utilizando pequeños paquetes de datos. Cada paquete contiene unas cuantas tareas (cada tarea supone una prueba única con el conjunto de parámetros de entrada). Esto reduce el tiempo de intercambio entre el terminal y el agente.

Los agentes nunca guardan en el disco duro los archivos EX5 recibidos del terminal (EA, indicadores, bibliotecas, etc.) por motivos de seguridad. Se hace para que no se pueda utilizar los datos recibidos en el ordenador con el agente instalado. Todos los demás datos, incluyendo DLL, se guardan en la zona protegida (sandbox). En los agentes remotos no se puede probar los EAs con el uso de las DLL.

El terminal deposita los resultados de simulación en una caché de resultados especial (caché resultante) para un acceso rápido a ellos cuando surja esta necesidad. Para cada conjunto de parámetros el terminal busca en la caché resultante los resultados ya listos de los arranques anteriores con el fin de evitar los arranques repetitivos. Si el resultado con este conjunto de parámetros no ha sido encontrado, el agente recibe la orden para empezar la prueba.

Todo el tráfico entre el terminal y el agente se codifica.

Los ticks no se mandan por la red, sino se generan por los agentes de pruebas.

Uso de la carpeta compartida de todos los Terminales de Cliente #

Todos los agentes de pruebas están aislados uno del otro y del Terminal de Cliente también: cada agente tiene su propia carpeta donde se guardan todos los logs del agente. Además de eso, durante la simulación todas las operaciones con los archivos se hacen en la carpeta nombre_del_agente/MQL5/Files. No obstante, se puede organizar la interacción entre los agentes locales y el terminal a través de la carpeta compartida de todos los terminales de cliente si durante la apertura del archivo indicamos la bandera FILE_COMMON:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- carpeta compartida de todos los Terminales de Cliente
   common_folder=TerminalInfoString(TERMINAL_COMMONDATA_PATH);
//--- mostraremos el nombre de esta carpeta
   PrintFormat("Abrimos el archivo en la carpeta compartida de todos los Terminales de Cliente %s", common_folder);
//--- abrimos el archivo en la carpeta compartida (tenemos la bandera FILE_COMMON)
   handle=FileOpen(filename,FILE_WRITE|FILE_READ|FILE_COMMON);
  ... siguientes acciones
//---
   return(INIT_SUCCEEDED);
  }

Uso de la DLL #

Para acelerar el proceso de la simulación, se puede utilizar no sólo los agentes locales, sino también los agentes remotos. Pero hay ciertas limitaciones para los agentes remotos. En primer lugar, los agentes remotos no muestran en sus logs los resultados de ejecución de la función Print(), los mensajes sobre la apertura y el cierre de posiciones. En el log se muestra el mínimo de información para que los EAs escritos de forma incorrecta no sobrellenen con sus mensajes el disco duro del ordenador en el que trabaja el agente remoto.

La segunda limitación consiste en prohibición del uso de DLL durante la simulación de los EAs. Las llamadas a las bibliotecas DLL están completamente prohibidas en los agentes remotos por motivos de seguridad. Para los agentes locales las llamadas dll dentro de los EAs que se prueban están permitidas sólo en el caso si el permiso correspondiente ha sido concedido por medio de la opción "Permitir importación de DLL".

La opción "Permitir importación de DLL" en los programas mql5

Importante: si utiliza los EAs (scripts, indicadores) recibidos desde fuera que exigen que les permita las llamadas a DLL, Usted debe comprender todo el riesgo que toma sobre sí en caso de permitir el uso de esta opción en los ajustes del terminal. Y eso no depende de forma del uso del EA -para la simulación o para su arranque en el gráfico.