
Operar con noticias de manera sencilla (Parte 4): Mejora del rendimiento
Introducción
En el artículo anterior, repasamos los procesos de implementación de operaciones basadas en el impacto de los acontecimientos noticiosos. Tuvimos éxito en esta misión, pero una desventaja clave del último código del artículo era su velocidad de backtesting, que es relativamente lenta. Esto se debe principalmente al acceso frecuente a la base de datos en memoria durante la comprobación retrospectiva de la estrategia. Para resolver este problema, reduciremos el número de veces que se accede a la base de datos durante el procedimiento de comprobación retrospectiva. Obtendremos toda la información que necesitamos de la base de datos en la memoria para el día actual, lo que significa que, en teoría, solo accederemos a la base de datos una vez al día.
Otro método que utilizaremos para mejorar el rendimiento es agrupar los eventos de noticias en función de sus horas, lo que significa que para cada hora del día tendremos una matriz que almacenará la información de los eventos solo para una hora específica. Cuando necesitemos la información del evento para la hora actual, si la hay, utilizaremos una instrucción switch para acceder a la matriz que contiene la información del evento para la hora relevante para la hora actual. Estos métodos reducirán drásticamente el tiempo de ejecución del experto, especialmente cuando se producen muchos acontecimientos noticiosos en un día o una hora concretos. En este artículo, codificaremos los componentes básicos para implementar estas soluciones en artículos posteriores y evitar así tener un solo artículo muy largo.
Clase de variables temporales
Anteriormente, esta clase se utilizaba para declarar una matriz con un tamaño fijo de 2000 índices que almacenaría los tiempos de las velas y se utilizaría en la clase Propiedades de las velas para comprobar si el tiempo de la vela almacenada es igual al tiempo de la vela actual, con el fin de identificar si se ha formado una nueva vela o no. En esta ocasión, ampliaremos esta clase para declarar enumeraciones para el tiempo y funciones que convertirán valores enteros en cualquiera de las enumeraciones declaradas, con el fin de disponer de un método controlado para tratar los segundos, minutos y horas en otras clases que incluirán esta clase.
La distribución de la clase es la siguiente:
//+------------------------------------------------------------------+ //| NewsTrading | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/kaaiblo | //+------------------------------------------------------------------+ //--- Enumeration For Hours in a Day enum HOURLY { H1=1,//01 H2=2,//02 H3=3,//03 H4=4,//04 H5=5,//05 H6=6,//06 H7=7,//07 H8=8,//08 H9=9,//09 H10=10,//10 H11=11,//11 H12=12,//12 H13=13,//13 H14=14,//14 H15=15,//15 H16=16,//16 H17=17,//17 H18=18,//18 H19=19,//19 H20=20,//20 H21=21,//21 H22=22,//22 H23=23,//23 H24=0//00 }; //--- Enumeration For Minutes in an Hour enum MINUTELY { M0,//00 M1,//01 M2,//02 M3,//03 M4,//04 M5,//05 M6,//06 M7,//07 M8,//08 M9,//09 M10,//10 M11,//11 M12,//12 M13,//13 M14,//14 M15,//15 M16,//16 M17,//17 M18,//18 M19,//19 M20,//20 M21,//21 M22,//22 M23,//23 M24,//24 M25,//25 M26,//26 M27,//27 M28,//28 M29,//29 M30,//30 M31,//31 M32,//32 M33,//33 M34,//34 M35,//35 M36,//36 M37,//37 M38,//38 M39,//39 M40,//40 M41,//41 M42,//42 M43,//43 M44,//44 M45,//45 M46,//46 M47,//47 M48,//48 M49,//49 M50,//50 M51,//51 M52,//52 M53,//53 M54,//54 M55,//55 M56,//56 M57,//57 M58,//58 M59//59 }; //--- Enumeration For Seconds Pre-event datetime enum PRESECONDLY { Pre_S30=30//30 }; //--- Enumeration For Seconds in a Minute enum SECONDLY { S0,//00 S1,//01 S2,//02 S3,//03 S4,//04 S5,//05 S6,//06 S7,//07 S8,//08 S9,//09 S10,//10 S11,//11 S12,//12 S13,//13 S14,//14 S15,//15 S16,//16 S17,//17 S18,//18 S19,//19 S20,//20 S21,//21 S22,//22 S23,//23 S24,//24 S25,//25 S26,//26 S27,//27 S28,//28 S29,//29 S30,//30 S31,//31 S32,//32 S33,//33 S34,//34 S35,//35 S36,//36 S37,//37 S38,//38 S39,//39 S40,//40 S41,//41 S42,//42 S43,//43 S44,//44 S45,//45 S46,//46 S47,//47 S48,//48 S49,//49 S50,//50 S51,//51 S52,//52 S53,//53 S54,//54 S55,//55 S56,//56 S57,//57 S58,//58 S59//59 }; //+------------------------------------------------------------------+ //|TimeVariables class | //+------------------------------------------------------------------+ class CTimeVariables { private: //--- Array to store candlestick times datetime CandleTime[2000]; public: CTimeVariables(void); //--- Set datetime value for an array index void SetTime(uint index,datetime time); //--- Get datetime value for an array index datetime GetTime(uint index); //--- Convert Integer to the Enumeration HOURLY HOURLY Hourly(uint Hour); //--- Convert Integer to the Enumeration MINUTELY MINUTELY Minutely(uint Minute); //--- Convert Integer to the Enumeration SECONDLY SECONDLY Secondly(uint Second); }; //+------------------------------------------------------------------+ //|Constructor | //+------------------------------------------------------------------+ CTimeVariables::CTimeVariables() { //--- Set default datetime values for all indexes in array CandleTime for(uint i=0; i<CandleTime.Size(); i++) { CandleTime[i]=D'1970.01.01'; } } //+------------------------------------------------------------------+ //|Set datetime value for an array index in array CandleTime | //+------------------------------------------------------------------+ void CTimeVariables::SetTime(uint index,datetime time) { if(index>=0&&index<CandleTime.Size()) { CandleTime[index] = time; } } //+------------------------------------------------------------------+ //|Get the datetime value for an array index in array CandleTime | //+------------------------------------------------------------------+ datetime CTimeVariables::GetTime(uint index) { return (index>=0&&index<CandleTime.Size())?CandleTime[index]:datetime(0); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration HOURLY | //+------------------------------------------------------------------+ HOURLY CTimeVariables::Hourly(uint Hour) { return (Hour>23)?HOURLY(0):HOURLY(Hour); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration MINUTELY | //+------------------------------------------------------------------+ MINUTELY CTimeVariables::Minutely(uint Minute) { return (Minute>59)?MINUTELY(0):MINUTELY(Minute); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration SECONDLY | //+------------------------------------------------------------------+ SECONDLY CTimeVariables::Secondly(uint Second) { return (Second>59)?SECONDLY(0):SECONDLY(Second); } //+------------------------------------------------------------------+
El código siguiente define una enumeración denominada HOURLY. Una enumeración (enum) es un tipo de datos definido por el usuario que consiste en un conjunto de constantes enteras con nombre. Se utiliza a menudo cuando se desea representar un conjunto específico de valores con nombres significativos, lo que hace que el código sea más legible.
enum HOURLY { H1=1, // Represents Hour 01 H2=2, // Represents Hour 02 H3=3, // Represents Hour 03 H4=4, // Represents Hour 04 H5=5, // Represents Hour 05 H6=6, // Represents Hour 06 H7=7, // Represents Hour 07 H8=8, // Represents Hour 08 H9=9, // Represents Hour 09 H10=10, // Represents Hour 10 H11=11, // Represents Hour 11 H12=12, // Represents Hour 12 H13=13, // Represents Hour 13 H14=14, // Represents Hour 14 H15=15, // Represents Hour 15 H16=16, // Represents Hour 16 H17=17, // Represents Hour 17 H18=18, // Represents Hour 18 H19=19, // Represents Hour 19 H20=20, // Represents Hour 20 H21=21, // Represents Hour 21 H22=22, // Represents Hour 22 H23=23, // Represents Hour 23 H24=0 // Represents Hour 00 (Midnight) };
Valores:
Cada valor de la enumeración corresponde a una hora específica del día, comenzando por H1=1 para la primera hora, H2=2, y así sucesivamente, hasta H23=23 para la vigésimo tercera hora. H24=0 se utiliza para representar la medianoche (00:00 horas). Puedes utilizar nombres como H1, H2, etc., en tu código para que resulte más intuitivo al trabajar con datos relacionados con el tiempo. En lugar de utilizar números sin procesar para las horas, prefiero utilizar valores POR HORA.
Ejemplo de uso:
HOURLY current_hour = H10; // Setting the current hour to 10:00 AM if (current_hour == H10) { Print("The current hour is 10:00 AM"); }
En el ejemplo anterior, a current_hour se le asigna el valor H10, y comprueba si la hora actual es H10, imprimiendo un mensaje en consecuencia. Esto hace que el código sea más fácil de leer y comprender, especialmente cuando se trabaja con innumerables operaciones basadas en el tiempo.
Enumeración para minutos - MINUTELY.
Esta enumeración define constantes para cada minuto de una hora, desde M0 (00) hasta M59 (59), ya que hay 59 minutos en cada hora.
enum MINUTELY
{
M0, M1, M2, M3, ..., M59
};
En lugar de trabajar con números enteros sin formato (por ejemplo, 0, 1, 2), utilizamos etiquetas significativas como M0, M1, etc.
Ejemplo:
- M0 representa el minuto 00.
- M30 representa el minuto 30.
Enumeración de segundos previos al evento - PRESECONDLY.
Esta enumeración define los segundos en relación con un tiempo anterior al evento. Aquí, Pre_S30 se utiliza específicamente para referirse a los 30 segundos previos a que ocurra un evento noticioso. Esto se utilizará como valor fijo para introducir el evento de noticias de antemano. Por ejemplo, si hay una noticia a las 14:00, solo intentaremos entrar en una operación a las 13:59:30. Esto significa que la opción anterior de entrar en una operación 5 segundos antes del evento ya no será una opción.
Hay pros y contras en este cambio que he hecho.
Desventaja principal:
- Menos personalización: esto es una desventaja, ya que el usuario/operador puede querer abrir operaciones solo 5 segundos antes de un evento debido a la volatilidad ocasional que se produce antes de que ocurra un evento de gran impacto, lo que significa que su stop loss podría activarse simplemente en función de la antelación con la que entre en la operación antes de que ocurra el evento. Por lo tanto, unos pocos segundos pueden afectar drásticamente a su rentabilidad cuando se trata del trading de noticias.
Ventaja principal:
- Horario fijo: esto es una ventaja, ya que podemos configurar el experto para que solo compruebe si entrar en una operación en intervalos de 30 segundos, lo que mejorará drásticamente la velocidad de las pruebas retrospectivas. La razón por la que esto reduce el tiempo de ejecución de las pruebas retrospectivas es simplemente porque no tenemos que utilizar los recursos del ordenador con tanta frecuencia. En el caso de que queramos entrar en una operación 5 segundos antes del evento, el experto tiene que comprobar cada 5 o 1 segundo si es el momento adecuado para realizar la operación, lo que consume los recursos del ordenador y hace que funcione más lento en la prueba retrospectiva.
Otro factor que debe tenerse en cuenta es la liquidez. Cuanto más nos acercamos a un acontecimiento noticioso de gran impacto, más ilíquido se vuelve el activo afectado. Lo que significa que, en general, entrar en una operación es menos favorable.
Estos son los factores que hacen que no sea favorable entrar en una operación con activos/mercados ilíquidos:
- Deslizamiento (Slippage): Los precios del activo o símbolo cambian con tanta frecuencia que el precio al que el usuario/operador deseaba entrar/salir del mercado ya no está disponible y se utiliza un precio menos favorable/esperado. La imprevisibilidad es la principal desventaja del deslizamiento.
- Diferenciales (Spreads): Los diferenciales amplios son más comunes, lo que limita el potencial de rentabilidad del operador.
- Fuera de cotización/no disponible: Las operaciones pueden cancelarse por completo, lo que significa que el operador pierde la posibilidad de beneficiarse de un movimiento potencialmente lucrativo en el activo/mercado.
Al introducir 30 segundos antes del evento noticioso, tenemos más probabilidades de evitar la iliquidez en comparación con introducir 5 segundos antes o cualquier segundo después de los 30 segundos.
enum PRESECONDLY { Pre_S30 = 30 // 30 seconds before an event };
Enumeración para segundos - SECONDLY.
Al igual que MINUTELY, esta enumeración representa cada segundo de un minuto, desde S0 (00 segundos) hasta S59 (59 segundos).
enum SECONDLY
{
S0, S1, S2, ..., S59
};
Ejemplo:
- S0 representa el segundo 00.
- S30 representa el segundo 30.
La siguiente función convierte un entero (Hour) en el valor correspondiente de la enumeración HOURLY. Garantiza que el valor pasado (en la variable Hour) sea válido (entre 0 y 23) y, a continuación, devuelve el valor correspondiente de la enumeración HOURLY.
- Tipo de retorno: HOURLY: esto significa que la función devuelve un valor de la enumeración HOURLY, que hemos comentado anteriormente. Esta enumeración contiene valores correspondientes a las 24 horas del día, donde H1=1, H2=2, ..., H23=23 y H24=0.
- Nombre de la función: Hourly. Es un método que pertenece a esta clase CTimeVariables, tal y como indica el operador de resolución de ámbito (::). Por lo tanto, forma parte de la clase CTimeVariables.
- Parámetro:
- uint Hour: Este es un entero sin signo que representa una hora y se pasa como parámetro a la función.
HOURLY CTimeVariables::Hourly(uint Hour) { return (Hour>23)?HOURLY(0):HOURLY(Hour); }
Lógica dentro de la función:
Esta línea utiliza el operador ternario (? :), que es una forma abreviada de una instrucción if-else. El operador ternario comprueba una condición y devuelve uno de dos valores en función de si la condición es verdadera o falsa.
Condición: (Hour > 23)
- Esto comprueba si el valor de la hora supera 23. Dado que las horas válidas van de 0 a 23 (24 horas en un día), cualquier valor superior a 23 no es válido.
- Si la hora es superior a 23 (hora no válida), la función devolverá HOURLY(0), que corresponde a H24=0 (medianoche o 00:00).
Si es falso: HOURLY(Hour)
- Si Hour está dentro del rango válido (0 a 23), convierte el entero Hour en su valor correspondiente en la enumeración HOURLY. Por ejemplo, si Hour = 10, devuelve HOURLY(10), que corresponde a H10.
return (Hour>23)?HOURLY(0):HOURLY(Hour);
Ejemplo:
- Si llamas a Hourly(10), la función devolverá HOURLY(10) (que es el valor enumerado para las 10:00 a. m.).
- Si llamas a Hourly(25), dado que 25 no es una hora válida, la función devolverá HOURLY(0) (que corresponde a las 00:00 o medianoche).
Puntos clave:
- Manejo de horas no válidas: La función garantiza que si el valor de la hora está fuera del rango válido (mayor que 23), se establece por defecto en HOURLY(0), que equivale a la medianoche.
- Conversión: La función convierte de manera eficiente una hora entera en un valor de enumeración HOURLY, lo que facilita su uso en la lógica basada en el tiempo.
Función de utilidad para convertir un número entero a MINUTOS (MINUTELY).
Esta función convierte un número entero (de 0 a 59) en un valor de enumeración MINUTELY. Si el número entero introducido supera 59 (un minuto no válido), la función establece por defecto M0 (minuto 0).
MINUTELY CTimeVariables::Minutely(uint Minute) { return (Minute>59)?MINUTELY(0):MINUTELY(Minute); }
Si el minuto supera los 59 segundos, devuelve M0 como medida de seguridad para evitar errores.
Función de utilidad para convertir un número entero en SEGUNDOS (SECONDLY).
Esta función realiza una tarea similar para los segundos. Convierte un número entero (de 0 a 59) en un valor de enumeración SECONDLY. Si el número entero supera 59, se establece por defecto en S0 (segundo 0).
SECONDLY CTimeVariables::Secondly(uint Second) { return (Second>59)?SECONDLY(0):SECONDLY(Second); }
Si el segundo supera 59, devuelve S0 para gestionar correctamente las entradas no válidas.
Puntos clave:
Precisión temporal: La clase TimeVariables simplifica la gestión del tiempo en los sistemas de negociación, facilitando el manejo de la precisión al minuto y al segundo, lo cual es esencial para las estrategias de negociación basadas en noticias.
Flexibilidad: Al utilizar enumeraciones, los desarrolladores pueden ajustar fácilmente los tiempos de ejecución de las operaciones en función de los acontecimientos noticiosos sin tener que codificar manualmente el tiempo para cada escenario.
Aplicación en el mundo real: En el comercio de noticias, ser capaz de actuar ante oportunidades sensibles al tiempo, como la volatilidad del mercado causada por publicaciones económicas, puede mejorar drásticamente el rendimiento comercial.
Predicciones y perspectivas:
Las diferentes condiciones del mercado influirán en la utilidad de la clase TimeVariables. Por ejemplo:
- En mercados volátiles, especialmente durante acontecimientos importantes (como los anuncios del NFP o del banco central), la precisión al segundo se vuelve fundamental, ya que pueden producirse grandes movimientos de precios en milisegundos.
- En condiciones de baja volatilidad, utilizar una precisión a nivel de minutos puede ser suficiente para realizar operaciones, ya que los movimientos de precios tienden a ser más lentos.
Reducción del acceso a la base de datos
En el artículo anterior, utilizamos la base de datos en memoria para almacenar la información del evento que ayudará a nuestro experto a saber cuándo abrir una operación en función de la hora del evento. A continuación se muestra el proceso para acceder a la base de datos.
- Al comienzo de cada nuevo día, cargamos todas las noticias que ocurrirán o han ocurrido durante el día actual, idealmente. Estos datos se almacenarán en una matriz estructurada en la que el programa mostrará los objetos del evento en el gráfico, indicando la hora del evento, el nombre, etc.
- Cuando se ha producido o se está produciendo un evento, el experto volverá a acceder a la base de datos en memoria para recuperar la información del siguiente evento que se produzca ese mismo día, si lo hubiera.
El proceso anterior no es el más adecuado, ya que si hay varios eventos con diferentes horarios en el mismo día, se accederá con frecuencia a la base de datos para obtener la información del siguiente evento. Una solución sencilla sería utilizar la misma matriz de estructura que se actualiza cada día y recorrerla hasta encontrar una fecha coincidente entre la fecha del evento y la fecha actual, de modo que podamos abrir la operación en el momento adecuado y no tengamos que acceder a la base de datos más de una vez solo para obtener la información del siguiente evento.
Esta sencilla solución mejorará sin duda la velocidad operativa del experto, pero surge otro problema cuando iteramos a través de la misma matriz de estructura que comprobamos en busca de fechas coincidentes, ya que podríamos tener fechas de eventos que ya han ocurrido y la comprobación de estas fechas, que son redundantes, limitará la velocidad operativa del experto. Lo mismo puede decirse de las fechas de eventos que ocurrirán mucho más tarde en el día que la fecha actual.
Por ejemplo, supongamos que tenemos los siguientes tiempos en nuestra matriz a continuación.
time[0] = '14:00' time[1] = '14:15' time[2] = '14:30' time[3] = '14:45' time[4] = '14:50' time[5] = '14:55' time[6] = '15:00' time[7] = '15:05' time[8] = '15:10' time[9] = '15:15' time[10] = '15:30' time[11] = '15:45' time[12] = '15:55'
Si la hora actual es las 11:00 a. m., no tiene sentido comprobar si la hora 14:00 p. m. coincide, y repetir constantemente este proceso con cada nuevo tick en el gráfico cuando las horas de los eventos aún no se han alcanzado afectará aún más a la velocidad operativa del experto. Teniendo esto en cuenta, una solución sencilla a este problema sería, como mínimo, separar la matriz en diferentes horas del día y que el experto solo iterara a través de la matriz con la hora que coincida con la hora actual. De esta manera, el experto ahorrará tiempo al comprobar únicamente si la hora del evento se encuentra, como mínimo, dentro de la misma hora de la fecha actual.
Estudio del caso:
Veamos un ejemplo real para destacar cómo esta optimización puede mejorar el rendimiento:
- Escenario: Un operador utiliza un EA que supervisa las principales noticias, como las de EE. UU. Nóminas no agrícolas (NFP), previstas para las 14:30. El EA está programado para colocar una orden de compra o venta stop en función del resultado de las noticias. Sin optimización, el EA accede continuamente a la base de datos cada segundo durante todo el día, comprobando si hay un evento próximo. Cuando se publique el NFP, es posible que el EA experimente un retraso en su reacción a las noticias debido al exceso de consultas a la base de datos, lo que reducirá sus posibilidades de detectar los puntos de entrada óptimos para las operaciones.
- Solución con reducción del acceso a la base de datos: En lugar de acceder continuamente a la base de datos, el EA carga todos los eventos del día a las 00:00 y los segmenta por horas. Cuando se acerca la hora 14:00, el EA solo comprueba los eventos dentro de la matriz de la hora 14:00 y omite cualquier comprobación de eventos fuera de esta ventana de tiempo. Cuando llegan las 14:30, el EA está listo para reaccionar inmediatamente a la publicación del NFP sin ningún retraso, colocando la operación en el momento adecuado.
Crearemos una nueva carpeta en el proyecto llamada TimeSeries. Esta carpeta contendrá las dos clases que crearán las matrices para cada hora del día y recuperarán los valores de estas matrices para cada hora del día.
Clase por hora
El código siguiente define una clase CTimeByHour, diseñada para gestionar y recuperar datos de tiempo y eventos para cada hora del día. La clase utiliza varios componentes, como estructuras, matrices y el concepto de programación orientada a objetos (Object-Oriented Programming, OOP). El objetivo de esta clase es crear objetos de matriz para cada hora del día cuando hay 24 horas, lo que significa que declararemos 24 objetos de matriz. Estos objetos de matriz almacenarán la variable entera Hour, la variable entera Minute y la variable de estructura de calendario myEData (esta variable almacenará toda la información del evento para la hora y el minuto específicos en que ocurrirá dentro del día). La estructura declarada TimeDate almacenará la hora y los minutos de una fecha, mientras que la matriz myTimeData almacenará datos en paralelo con la matriz de estructura myEvents.
//+------------------------------------------------------------------+ //| TimeByHour.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #include <Object.mqh> #include <Arrays\ArrayObj.mqh> #include "../TimeVariables.mqh" #include "../CommonVariables.mqh" #include "../TimeManagement.mqh" //--- Structure to store time data in Hour and Minute struct TimeDate { int Hour; int Minute; } myTimeData[]; //--- Structure array to store event data in parallel with myTimeData array Calendar myEvents[]; //+------------------------------------------------------------------+ //|TimeByHour class | //+------------------------------------------------------------------+ class CTimeByHour:public CObject { private: //--- classes' object declarations CTimeManagement CTime; CTimeVariables CTV; protected: //--- class constructor with parameters CTimeByHour(HOURLY myHour,MINUTELY myMinute,Calendar &myEventData): //--- Assign integer variables Hour and Minute with time data from myHour and myMinute respectively Hour(int(myHour)),Minute(int(myMinute)) { //--- Assign variable myEData with event info from variable myEventData myEData = myEventData; } virtual void myTime(Calendar &myNews[]); //--- Array object declarations for each hour of the day CArrayObj *myH1,*myH2,*myH3,...,*myH24; //--- Integer variables to store time data in hour and minute format int Hour; int Minute; //--- Calendar structure variable to store event info Calendar myEData; public: //--- class constructor without parameters CTimeByHour(void) { } //--- Array object variable CArrayObj *getmyTime; //--- Retrieve array object for an individual hour CObject *getTime(HOURLY myHour) { switch(myHour) { case H1: //--- retrieve array obj for 01 Hour return myH1; break; case H2: //--- retrieve array obj for 02 Hour return myH2; break; case H3: //--- retrieve array obj for 03 Hour return myH3; break; // ... default: //--- retrieve array obj for 24|00 Hour return myH24; break; } } //--- class pointer variable CTimeByHour *myClass; //--- class destructor ~CTimeByHour(void) { //--- delete all pointer variables delete getmyTime; delete myClass; delete myH1; delete myH2; delete myH3; // ... } //--- Function to retrieve timedata and calendar info for a specific hour of the day via parameters passed by reference void GetDataForHour(HOURLY myHour,TimeDate &TimeData[],Calendar &Events[]) { //--- Clean arrays ArrayRemove(TimeData,0,WHOLE_ARRAY); ArrayRemove(Events,0,WHOLE_ARRAY); //--- retrieve array object for the specific hour getmyTime = getTime(myHour); // Iterate through all the items in the list. for(int i=0; i<getmyTime.Total(); i++) { // Access class obj via array obj index myClass = getmyTime.At(i); //Re-adjust arrays' sizes ArrayResize(TimeData,i+1,i+2); ArrayResize(Events,i+1,i+2); //--- Assign values to arrays' index TimeData[i].Hour = myClass.Hour; TimeData[i].Minute = myClass.Minute; Events[i] = myClass.myEData; } } }; //+------------------------------------------------------------------+
Estructuras:
- TimeDate: Una estructura para almacenar datos de tiempo, con campos enteros para la hora y los minutos.
- myTimeData[]: Una matriz de estructuras TimeDate para almacenar los datos de tiempo de varias horas.
struct TimeDate { int Hour; int Minute; } myTimeData[];
- myEvents[]: Una matriz de tipo Calendario, destinada a almacenar datos de eventos en paralelo con myTimeData.
Calendario myEvents[];
Declaración de clase CTimeByHour:
- CTimeByHour: Una clase que extiende CObject. Esta clase gestiona datos de tiempo y eventos por hora.
class CTimeByHour:public CObject
Miembros privados:
- CTimeManagement and CTimeVariables: Estos son objetos de clases personalizadas (CTimeManagement, CTimeVariables) incluidas desde TimeManagement.mqh y TimeVariables.mqh, que gestionan datos y variables relacionados con el tiempo.
private:
CTimeManagement CTime;
CTimeVariables CTV;
Constructor:
- Este es un constructor parametrizado para la clase. Inicializa dos variables enteras (Hour y Minute) utilizando las enumeraciones HOURLY y MINUTELY, y asigna la información del evento (myEventData) a myEData.
protected: CTimeByHour(HOURLY myHour, MINUTELY myMinute, Calendar &myEventData): Hour(int(myHour)), Minute(int(myMinute)) { myEData = myEventData; }
Miembros de datos:
- myH1 ... myH24: Punteros a objetos CArrayObj, cada uno correspondiente a una hora específica (del 01 al 24) del día. Cada CArrayObj contiene una matriz de objetos para una hora específica.
- Hour y Minute: Variables enteras para almacenar la hora.
- myEData: Un objeto Calendario que almacena información sobre eventos.
CArrayObj *myH1, *myH2, ..., *myH24; int Hour; int Minute; Calendar myEData;
Métodos públicos:
- CTimeByHour(void): Un constructor predeterminado que no inicializa nada.
- getmyTime: Un puntero a un objeto matriz que contiene los datos de tiempo para una hora específica.
public: CTimeByHour(void) {} CArrayObj *getmyTime;
Recuperar objeto de matriz para una hora:
- getTime(HOURLY myHour): Un método que utiliza una instrucción switch para recuperar el objeto CArrayObj adecuado para una hora específica del día basándose en la enumeración HOURLY. Cada caso corresponde a una hora (por ejemplo, H1, H2, ... H24).
CObject *getTime(HOURLY myHour) { switch(myHour) { case H1: return myH1; case H2: return myH2; ... case H24: return myH24; } }
Destructor:
- ~CTimeByHour(void): El destructor limpia la memoria asignada dinámicamente llamando a delete en los punteros CArrayObj (myH1 ... myH24) y otros punteros de clase.
~CTimeByHour(void) { delete getmyTime; delete myClass; delete myH1, myH2, ..., myH24; }
Obtener datos para una hora específica:
- GetDataForHour: Este método recupera datos de tiempo y eventos para una hora específica (myHour).
- ArrayRemove: Borra las matrices (TimeData[], Events[]).
- getmyTime = getTime(myHour): Obtiene el objeto matriz para la hora especificada.
- Bucle «for»: Recorre todos los elementos del CArrayObj recuperado (es decir, los datos de tiempo y eventos de cada entrada).
- ArrayResize: Cambia dinámicamente el tamaño de las matrices (TimeData[], Events[]) para que se ajusten a los nuevos datos.
- myClass: Se refiere al objeto actual que se está procesando en la matriz.
Para cada objeto, el método asigna la hora, los minutos y myEData al índice correspondiente en las matrices TimeData[] y Events[].
void GetDataForHour(HOURLY myHour, TimeDate &TimeData[], Calendar &Events[]) { ArrayRemove(TimeData, 0, WHOLE_ARRAY); ArrayRemove(Events, 0, WHOLE_ARRAY); getmyTime = getTime(myHour); for(int i = 0; i < getmyTime.Total(); i++) { myClass = getmyTime.At(i); ArrayResize(TimeData, i + 1); ArrayResize(Events, i + 1); TimeData[i].Hour = myClass.Hour; TimeData[i].Minute = myClass.Minute; Events[i] = myClass.myEData; } }
Clase por día y hora
Esta clase se encargará de asignar valores a los objetos de matriz declarados anteriormente en el archivo de encabezado TimeByHour, así como de recuperar estos valores y ordenarlos por la hora y los minutos específicos almacenados en el objeto de matriz correspondiente. El código comienza importando otros archivos: TimeByHour.mqh, que maneja datos de tiempo a nivel de hora, y CommonVariables.mqh, que contiene constantes y variables compartidas. La clase CTimeByDay hereda de CTimeByHour. Esta clase maneja datos de tiempo por día y permite la interacción con datos de tiempo específicos por hora gestionados por CTimeByHour.
//+------------------------------------------------------------------+ //| TimeByDay.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #include "TimeByHour.mqh" #include "../CommonVariables.mqh" //+------------------------------------------------------------------+ //|TimeByDay class | //+------------------------------------------------------------------+ class CTimeByDay:private CTimeByHour { private: //--- Function to clear all array objects void Clear(); //--- Function to clean array dates in accordance to the current minute void DatePerMinute(TimeDate &TData[],Calendar &EData[],MINUTELY min,TimeDate &TimeData[],Calendar &EventData[]) { //--- Iterate through all the idexes in TData array for(uint i=0;i<TData.Size();i++) { //--- Check if Minutes match if(TData[i].Minute==int(min)) { //--- Resize arrays ArrayResize(TimeData,TimeData.Size()+1,TimeData.Size()+2); ArrayResize(EventData,EventData.Size()+1,EventData.Size()+2); //--- Assign data from each array to the other TimeData[TimeData.Size()-1] = TData[i]; EventData[EventData.Size()-1] = EData[i]; } } } public: //--- Function to set time for array objects based on calendar array myNews void SetmyTime(Calendar &myNews[]) { //--- Clear previous data stored in array objects Clear(); //--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,0,WHOLE_ARRAY); ArrayRemove(myEvents,0,WHOLE_ARRAY); //--- Set new values to each array object accordingly myTime(myNews); } //--- Function to get time for the specific hour and minute for news events void GetmyTime(HOURLY myHour,MINUTELY myMinute,TimeDate &TimeData[],Calendar &Events[]) { //--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,0,WHOLE_ARRAY); ArrayRemove(myEvents,0,WHOLE_ARRAY); //--- Declare temporary arrays to get news data for a specific hour TimeDate myTData[]; Calendar myData[]; //--- Get Data for the specific hour of the day GetDataForHour(myHour,myTData,myData); //--- Filter the Data for a specific Minute of the hour DatePerMinute(myTData,myData,myMinute,TimeData,Events); //--- Clear data from the temporary array variables ArrayRemove(myTData,0,WHOLE_ARRAY); ArrayRemove(myData,0,WHOLE_ARRAY); } public: //--- Class constructor CTimeByDay(void) { //--- Initialize array objects myH1 = new CArrayObj(); myH2 = new CArrayObj(); myH3 = new CArrayObj(); //... } //--- Class destructor ~CTimeByDay(void) { } }; //+------------------------------------------------------------------+ //|Add data to Array Objects for each Hour of the day | //+------------------------------------------------------------------+ void CTimeByHour::myTime(Calendar &myNews[]) { //--- Iterate through myNews calendar array for(uint i=0;i<myNews.Size();i++) { //--- Assign datetime from myNews calendar array datetime Date = datetime(myNews[i].EventDate); //--- Assign HOURLY Enumeration value from datetime variable Date HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date)); //--- Assign MINUTELY Enumeration value from datetime variable Date MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date)); //--- Switch statement to identify each value scenario for myHour switch(myHour) { case H1: //--- add array obj values for 01 Hour myH1.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; case H2: //--- add array obj values for 02 Hour myH2.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; case H3: //--- add array obj values for 03 Hour myH3.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; //... default: //--- add array obj values for 24|00 Hour myH24.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; } } } //+------------------------------------------------------------------+ //|Clear Data in Array Objects | //+------------------------------------------------------------------+ void CTimeByDay::Clear(void) { //--- Empty all array objects myH1.Clear(); myH2.Clear(); myH3.Clear(); //... } //+------------------------------------------------------------------+
Funciones privadas:
- Esta función se utiliza para borrar (o restablecer) todos los objetos de matriz que almacenan datos horarios.
void Clear();
La siguiente función filtra los datos de tiempo (TData[]) y los datos de eventos del calendario (EData[]) para conservar solo las entradas que coinciden con un minuto específico (representado por el argumento min).
- Itera a través de TData[] y, para cada elemento, comprueba si el minuto coincide con "min". Si coincide, se redimensionan las matrices TimeData[] y EventData[] y se copian en ellas los datos correspondientes de TData[] y EData[].
void DatePerMinute(TimeDate &TData[], Calendar &EData[], MINUTELY min, TimeDate &TimeData[], Calendar &EventData[]);
Funciones públicas:
La siguiente función restablece y asigna nuevos datos de hora y eventos para el día. Utiliza el método myTime de CTimeByHour, que procesa datos de tiempo a nivel de hora basándose en los eventos de noticias pasados en myNews[].
- En primer lugar, borra los datos de tiempo y eventos almacenados anteriormente utilizando Clear().
- A continuación, elimina todos los datos de las matrices paralelas (myTimeData y myEvents) y establece nuevos valores utilizando la función myTime() heredada de CTimeByHour.
void SetmyTime(Calendar &myNews[]);
La siguiente función recupera los datos de tiempo para una hora (myHour) y un minuto (myMinute) específicos.
- Primero borra las matrices myTimeData y myEvents.
- Las matrices temporales myTData[] y myData[] se declaran para almacenar datos de tiempo y eventos.
- Se llama a GetDataForHour() para rellenar las matrices temporales con datos para la hora especificada.
- Los datos se filtran aún más para el minuto específico utilizando DatePerMinute().
void GetmyTime(HOURLY myHour, MINUTELY myMinute, TimeDate &TimeData[], Calendar &Events[]);
Constructor:
- El constructor inicializa objetos de matriz para cada hora del día (del 1 al 24). Estos objetos de matriz almacenarán datos de tiempo y eventos para horas específicas.
CTimeByDay(void) { // Initialize array objects for each hour myH1 = new CArrayObj(); myH2 = new CArrayObj(); // (Initializes for all 24 hours) }
CTimeByHour::myTime()
Esta función se define en CTimeByHour y es heredada por CTimeByDay. Procesa la matriz myNews[] y asocia los eventos con horas específicas del día.
- Para cada evento en myNews[], extrae la hora y los minutos del evento.
- Utiliza una instrucción switch para decidir qué objeto de matriz horaria (por ejemplo, myH1, myH2, etc.) debe almacenar los datos de hora y evento.
- Cada evento temporal se añade como un objeto CTimeByHour al objeto matriz correspondiente.
for(uint i=0; i<myNews.Size(); i++) { datetime Date = datetime(myNews[i].EventDate); HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date)); MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date)); // Switch case to handle different hours }
Función Clear()
- Esta función borra todos los objetos de la matriz horaria (myH1, myH2, etc.), restableciendo esencialmente los datos horarios almacenados para cada hora.
void CTimeByDay::Clear(void) { // Clears all array objects myH1.Clear(); myH2.Clear(); // (Clears all 24 hours) }
Conclusión
En este artículo se han mostrado los métodos para mejorar el rendimiento del Experto, dividiendo los tiempos de los eventos en matrices separadas para cada hora del día y reduciendo la frecuencia con la que se accede a la base de datos del calendario en memoria. Al estructurar los valores de tiempo como enumeraciones (MINUTELY, SECONDLY), el código se vuelve más fácil de gestionar e interpretar, lo que reduce el riesgo de errores lógicos.
Las enumeraciones MINUTELY, SECONDLY y PRESECONDLY representan los minutos, segundos y tiempo previo al evento, respectivamente, lo que proporciona una mejor legibilidad y control sobre los intervalos de tiempo. Las funciones de conversión facilitan el trabajo con números enteros como entradas, asegurando que se conviertan en valores enumerados significativos. La clase CTimeByHour proporciona un mecanismo para almacenar, recuperar y gestionar datos de tiempo y eventos para cada hora del día. Estos métodos se implementarán en artículos posteriores.
Puntos clave:
- Acceso eficiente a la base de datos: los eventos se cargan solo una vez al día y se almacenan en la memoria, lo que reduce las consultas innecesarias a la base de datos.
- Segmentación temporal: al dividir los eventos en intervalos de una hora, el EA solo comprueba los eventos relevantes, lo que mejora la velocidad y reduce la carga de la CPU.
- Escalabilidad: El método propuesto es escalable para días con muchos eventos, lo que garantiza un rendimiento constante a lo largo de la jornada bursátil.
- Mayor capacidad de respuesta: al centrarse en los acontecimientos que se producen en el marco temporal pertinente, el EA puede reaccionar más rápidamente a los acontecimientos del mercado, lo cual es fundamental para las estrategias basadas en noticias.
Gracias por tu tiempo, espero poder ofrecerte más información útil en el próximo artículo. :)
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15878





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
Hola,
Gracias por este artículo, ¿podría ayudarme a añadir un filtro, (filter.csv) que contiene sólo las noticias que me gustaría negociar?
Hola,
Gracias por este artículo, ¿podría ayudarme a añadir un filtro, (filter.csv) que contiene sólo las noticias que me gustaría negociar?
Hola Hamid Rabia, Gracias por su interés en este artículo. Este tema de la filtración de noticias será cubierto en los próximos artículos, me gustaría que usted sea paciente.