English Русский 中文 Deutsch 日本語
preview
Operar con noticias de manera sencilla (Parte 4): Mejora del rendimiento

Operar con noticias de manera sencilla (Parte 4): Mejora del rendimiento

MetaTrader 5Sistemas comerciales | 20 junio 2025, 08:31
61 2
Kabelo Frans Mampa
Kabelo Frans Mampa

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.

Liquidez

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 es verdadero: HOURLY(0)
  • 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.

Carpeta TimeSeries



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

Archivos adjuntos |
NewsTrading_Part4.zip (591.45 KB)
Hamid Rabia
Hamid Rabia | 5 nov 2024 en 12:58

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?

Kabelo Frans Mampa
Kabelo Frans Mampa | 5 nov 2024 en 16:27
Hamid Rabia #:

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.

Algoritmo de búsqueda orbital atómica - Atomic Orbital Search (AOS) Modificación Algoritmo de búsqueda orbital atómica - Atomic Orbital Search (AOS) Modificación
En la segunda parte del artículo, seguiremos desarrollando una versión modificada del algoritmo AOS (Atomic Orbital Search), centrándonos en operadores específicos para mejorar su eficacia y adaptabilidad. Tras analizar los fundamentos y la mecánica del algoritmo, discutiremos ideas para mejorar el rendimiento y la capacidad de analizar espacios de soluciones complejos, proponiendo nuevos enfoques para ampliar su funcionalidad como herramienta de optimización.
Redes neuronales en el trading: Optimización del Transformer para la previsión de series temporales (LSEAttention) Redes neuronales en el trading: Optimización del Transformer para la previsión de series temporales (LSEAttention)
El framework LSEAttention ofrece formas de mejorar la arquitectura del Transformer, y se ha diseñado específicamente para la previsión a largo plazo de series temporales multidimensionales. Los enfoques propuestos por los autores del método resuelven los problemas de colapso de entropía e inestabilidad de aprendizaje característicos del Transformer vainilla.
Redes neuronales en el trading: Mejora de la eficiencia del Transformer mediante la reducción de la nitidez (SAMformer) Redes neuronales en el trading: Mejora de la eficiencia del Transformer mediante la reducción de la nitidez (SAMformer)
El entrenamiento de los modelos de Transformer requiere grandes cantidades de datos y suele ser difícil debido a la escasa capacidad de generalización de los modelos en muestras pequeñas. El framework SAMformer ayuda a resolver este problema evitando los mínimos locales malos, mejorando la eficacia de los modelos incluso con muestras de entrenamiento limitadas.
El análisis volumétrico de redes neuronales como clave de las tendencias futuras El análisis volumétrico de redes neuronales como clave de las tendencias futuras
Este artículo explora la posibilidad de mejorar la previsión de los precios usando como base el análisis comercial volumétrico mediante la integración de los principios del análisis técnico con la arquitectura de redes neuronales LSTM. Prestaremos especial atención a la detección e interpretación de volúmenes anómalos, el uso de clusterización y la generación y definición de características basadas en el volumen en el contexto del aprendizaje automático.