- Conceptos básicos del calendario
- Obtener la lista y las descripciones de los países disponibles
- Consultar tipos de eventos por país y moneda
- Obtener descripciones de eventos por ID
- Obtener registros de eventos por país o moneda
- Obtener registros de eventos de un tipo específico
- Leer registros de sucesos por ID
- Seguimiento de los cambios de eventos por país o moneda
- Seguimiento de los cambios de eventos por tipo
- Filtrar eventos por múltiples condiciones
- Transferir la base de datos de calendarios al probador
- Operar con el calendario
Seguimiento de los cambios de eventos por país o divisa
Como se menciona en la sección sobre conceptos básicos del calendario, la plataforma registra todos los cambios de eventos por algún medio interno. Cada estado se caracteriza por un identificador de cambios (change_id). Entre las funciones de MQL5 hay dos que permiten encontrar este identificador (en un punto arbitrario en el tiempo) y luego solicitar entradas de calendario cambiado más tarde. Una de estas funciones es CalendarValueLast, que se analizará en esta sección. El segundo, CalendarValueLastByEvent, se analizará en la próxima sección.
int CalendarValueLast(ulong &change_id, MqlCalendarValue &values[],
const string country = NULL, const string currency = NULL)
La función CalendarValueLast está diseñada para dos propósitos: obtener el último identificador de cambio de calendario conocido change_id y llenar el array values con los registros modificados desde la modificación anterior dada por el identificador pasado en el mismo change_id. En otras palabras: el parámetro change_id funciona como entrada y como salida. Por eso es una referencia y requiere que se especifique una variable.
Si introducimos change_id igual a 0 en la función, entonces la función llenará la variable con el identificador actual pero no llenará el array.
Opcionalmente, utilizando los parámetros country y currency, puede establecer registros de filtrado por país y divisa.
La función devuelve el número de elementos de calendario copiados. Dado que el array no está poblado en el primer modo de operación (change_id = 0), devolver 0 no es un error. También podemos obtener 0 si el calendario no se ha modificado desde el cambio especificado. Por lo tanto, para comprobar si hay un error, debe analizar _LastError.
Así que la forma habitual de utilizar la función es hacer un bucle a través del calendario en busca de cambios.
ulong change = 0;
|
Esto puede hacerse en un bucle, en un temporizador o en otros eventos.
Los identificadores aumentan constantemente, pero pueden desordenarse, es decir, saltar sobre varios valores.
Es importante tener en cuenta que cada entrada del calendario está siempre disponible en un único último estado: el historial de cambios no se proporciona en MQL5. Por regla general, esto no supone un problema, ya que el ciclo de vida de cada noticia es estándar: añadir a la base de datos con antelación suficiente y completar con datos relevantes en el momento del evento. Sin embargo, en la práctica pueden producirse varias desviaciones: editar la previsión, transferir el tiempo o revisar los valores. Es imposible averiguar exactamente a qué hora y qué se cambió en el registro a través de la API de MQL5 a partir del historial del calendario. Por lo tanto, aquellos sistemas de trading que tomen decisiones basadas en la situación momentánea requerirán guardar de forma independiente el historial de cambios e integrarlo en un Asesor Experto para ejecutarlo en el probador.
Utilizando la función CalendarValueLast podemos crear un servicio útil, CalendarChangeSaver.mq5, que comprobará si hay cambios en el calendario a los intervalos especificados y, si los hay, guardará los identificadores de cambio en el archivo junto con la hora actual del servidor. Esto permitirá seguir utilizando la información del archivo para realizar pruebas más realistas de los Asesores Expertos en el historial del calendario. Por supuesto, esto requerirá organizar la exportación/importación de toda la base de datos de calendarios, de lo que nos ocuparemos más adelante.
Vamos a proporcionar variables de entrada para especificar el nombre del archivo y el periodo entre sondeos (en milisegundos).
input string Filename = "calendar.chn";
|
Al principio del manejador OnStart, abrimos el archivo binario para escribir, o más bien para añadir (si ya existe). Aquí no se comprueba el formato de un archivo existente, por lo que deberá añadir protección al incrustarlo en una aplicación real.
void OnStart()
|
Aquí debemos hacer una pequeña digresión:
Cada vez que se modifica el calendario, deben escribirse en el archivo al menos un par de números enteros de 8 bytes: la hora actual (datetime) y el ID de la noticia (ulong), pero puede haber más de un registro modificado al mismo tiempo. Por lo tanto, además de la fecha, el número de registros modificados se empaqueta en el primer número. Esto tiene en cuenta que las fechas caben en 0x7FFFFFFFF y por lo tanto los 3 bytes superiores se dejan sin usar. Es en los dos bytes más significativos (con un desplazamiento a la izquierda de 48 bits) donde se coloca el número de identificadores que el servicio escribirá después de la marca de tiempo correspondiente. La macro PACK_DATETIME_COUNTER crea una fecha «extendida», y las otras dos, DATETIME y COUNTER, las necesitaremos más adelante cuando el archivo de cambios sea leído (por otro programa).
#define PACK_DATETIME_COUNTER(D,C) (D | (((ulong)(C)) << 48))
|
Volvamos ahora al código del servicio principal. En un bucle que se activa cada PeriodMsc milisegundos, solicitamos cambios utilizando CalendarValueLast. Si hay cambios, escribimos en un archivo la hora actual del servidor y el array de identificadores recibidos.
while(!IsStopped())
|
Para presentar cómodamente la información sobre cada evento de noticias, hemos escrito una función de ayuda Description.
string Description(const MqlCalendarValue &value)
|
Así, el registro mostrará no sólo el identificador, sino también el código de país, el título y la hora programada de la noticia.
Se da por sentado que el servicio debe funcionar durante bastante tiempo para recopilar información durante un periodo suficiente para la simulación (días, semanas, meses). Lamentablemente, al igual que ocurre con el libro de órdenes, la plataforma no proporciona un historial preparado del libro de órdenes ni de las ediciones del calendario, por lo que su recopilación se deja enteramente en manos del desarrollador de programas MQL.
Veamos el servicio en acción. En el siguiente fragmento del registro (para el periodo de tiempo de 2022.06.28, 15:30 - 16:00), algunos eventos de noticias se refieren a un futuro lejano (contienen los valores del campo prev_value, que es también el campo actual_value del evento actual del mismo nombre). Sin embargo, hay algo más importante: la hora real de una publicación de noticias puede diferir significativamente, a veces en varios minutos, de la prevista.
Requesting start ID...
|
Por supuesto, esto no es importante para todas las clases de estrategias de trading, sino sólo para las que operan rápidamente en el mercado. Para ellas, el archivo creado de ediciones de calendario puede proporcionar una simulación más precisa de Asesores Expertos de noticias. En el futuro hablaremos de cómo «conectar» el calendario con el probador, pero por ahora, mostraremos cómo leer el archivo recibido.
Utilizaremos el script CalendarChangeReader.mq5 para demostrar la funcionalidad comentada. En la práctica, el código fuente dado debe colocarse en el Asesor Experto.
Las variables de entrada permiten establecer el nombre del archivo que se va a leer y la fecha de inicio de la exploración. Si el servicio sigue funcionando (escribe el archivo), debe copiar el archivo con otro nombre o en otra carpeta (en el script de ejemplo, se cambia el nombre del archivo). Si el parámetro Start está en blanco, la lectura de los cambios de noticias comenzará desde el principio del día actual.
input string Filename = "calendar2.chn";
|
Se describe la estructura ChangeState para almacenar información sobre ediciones individuales.
struct ChangeState
|
Se utiliza en la clase ChangeFileReader, que realiza la mayor parte del trabajo de leer el archivo y proporcionar a la persona que llama los cambios que son apropiados para un momento determinado.
El manejador del archivo se pasa como parámetro al constructor, al igual que la hora de inicio de la prueba. La lectura de un archivo y el relleno de la estructura ChangeState para una edición de calendario se realiza en el método readState.
class ChangeFileReader
|
El método check lee el archivo hasta que aparezca la siguiente edición en el futuro. En este caso, todas las ediciones anteriores (por marcas de tiempo) desde la llamada anterior al método se colocan en el array de salida records.
bool check(datetime now, ulong &records[], const bool fastforward = false)
|
Así se utiliza la clase en OnStart.
void OnStart()
|
He aquí los resultados del script para los mismos cambios de calendario que fueron guardados por el servicio en el contexto del fragmento de registro anterior.
2022.06.28 15:31:00
|
Los mismos identificadores se reproducen en tiempo virtual con el mismo retraso que en línea, aunque aquí se aprecia el redondeo a 1 minuto, que se produjo porque fijamos un paso artificial de este tamaño en el bucle. En teoría, por razones de eficacia, podemos aplazar las comprobaciones hasta la hora almacenada en la estructura ChangeState current. El código fuente adjunto define el método getState para obtener este tiempo.