English Русский Deutsch 日本語 Português
preview
Operar con noticias de manera sencilla (Parte 2): Gestión de riesgos

Operar con noticias de manera sencilla (Parte 2): Gestión de riesgos

MetaTrader 5Ejemplos | 4 diciembre 2024, 10:20
60 0
Kabelo Frans Mampa
Kabelo Frans Mampa

Introducción

Un rápido repaso al artículo anterior de la serie "Operar con noticias de manera sencilla". En la primera parte, repasamos el concepto de horario de verano y las distintas versiones para los distintos países, que básicamente cambian sus husos horarios una hora por delante y por detrás durante un año fiscal. Esto cambiará los horarios comerciales de los corredores relacionados que utilizan el horario de verano. Se abordaron las razones para crear una base de datos y los beneficios. Se creó una base de datos para almacenar los eventos de noticias del Calendario Económico MQL5 con cambios posteriores en los datos de la hora del evento para reflejar el horario DST del corredor para realizar pruebas retrospectivas precisas en el futuro. En los archivos del proyecto, se proporcionó un script SQL de resultados en formato Excel para todos los eventos únicos accesibles a través del Calendario MQL5 para todos los diferentes países.

Sin embargo, en este artículo, haremos algunos cambios a nuestro código anterior en la parte 1. En primer lugar, al implementar la herencia en el código existente y en el nuevo código que está por venir, la anterior base de datos de noticias/calendario se renovará y se convertirá en algo más útil y práctico. Además, abordaremos la gestión de riesgos y crearemos diferentes perfiles de riesgo entre los que elegir para usuarios con diferentes apetitos de riesgo o preferencias.


Herencia

Tipos de herencia


¿Qué es la herencia?

Herencia

La herencia es un concepto fundamental en la programación orientada a objetos (POO) que permite a una nueva clase (llamada subclase o clase derivada) heredar propiedades y comportamientos (campos y métodos) de una clase existente (llamada superclase o clase base). Este mecanismo permite crear una nueva clase ampliando o modificando el comportamiento de una clase existente, lo que favorece la reutilización del código y la creación de una estructura de clases más lógica y jerárquica. 


¿Para qué sirve la herencia?

La herencia permite reutilizar el código existente. Al heredar de una clase base, una subclase puede aprovechar los métodos y campos existentes sin tener que reescribirlos. Esto reduce la redundancia y hace que el código sea más fácil de mantener. La herencia ayuda a organizar el código en una estructura jerárquica, que es más fácil de entender y gestionar. Las clases pueden agruparse en función de atributos y comportamientos compartidos, lo que permite una organización clara y lógica del código base. La herencia está estrechamente relacionada con el polimorfismo, que permite tratar objetos de clases diferentes como objetos de una superclase común. Esto resulta especialmente útil para implementar código flexible y extensible. Por ejemplo, una función puede operar sobre objetos de clases diferentes siempre que hereden de la misma clase base, lo que permite la vinculación dinámica de métodos y un código más generalizado.

La herencia permite ampliar la funcionalidad de las clases existentes. Las subclases pueden añadir nuevos métodos y campos o anular los existentes para introducir comportamientos específicos sin modificar la clase original. Esto promueve el principio de abierto/cerrado, según el cual las clases están abiertas a la ampliación, pero cerradas a la modificación. La herencia favorece el encapsulamiento al permitir que una subclase acceda a los miembros protegidos y públicos de una superclase, manteniendo privados los detalles de implementación. Esto garantiza una separación clara entre la interfaz y la implementación, mejorando la modularidad y reduciendo el riesgo de interacciones no deseadas.


¿Qué son los modificadores de acceso?

Los modificadores de acceso son palabras clave en lenguajes de programación orientados a objetos que establecen la accesibilidad de clases, métodos/funciones y otros miembros. Controlan la visibilidad y accesibilidad de estos elementos desde distintas partes del código, reforzando así la encapsulación y protegiendo la integridad de los datos.

Tipos de modificadores de acceso

  • Público
  • Privado
  • Protegido

1. Público

Propósito: Hacer que una clase, función o variable esté disponible para su uso en otras clases o programas.

2. Privado

Propósito: Restringir el acceso a los miembros de una clase, protegiendo así la integridad de los datos.

3. Protegido

Propósito: Permitir a las subclases heredar y acceder a los miembros mientras se restringe el acceso de los objetos de la clase.


Ejemplo de herencia relacionado con MQL5

Primero crearemos un diagrama de clases UML para el ejemplo, y para visualizar las clases y sus relaciones y atributos.

Diagrama de clases UML

Las clases UnitedStates y Switzerland tienen herencia singular de la clase NewsData:

class NewsData
  {
private://Properties are only accessible from this class
   string            Country;//Private variable
   struct EventDetails//Private structure
     {
      int            EventID;
      string         EventName;
      datetime       EventDate;
     };
protected:
   //-- Protected Array Only accessible from this class and its children
   EventDetails      News[];
   //-- Proctected virtual void Function(to be expanded on via child classes)
   virtual void      SetNews();
   //-- Protected Function Only accessible from this class and its children
   void              SetCountry(string myCountry) {Country=myCountry;}
public:
   void              GetNews()//Public function to display 'News' array details
     {
      PrintFormat("+---------- %s ----------+",Country);
      for(uint i=0;i<News.Size();i++)
        {
         Print("ID: ",News[i].EventID," Name: ",News[i].EventName," Date: ",News[i].EventDate);
        }
     }
                     NewsData(void) {}//Class constructor
                    ~NewsData(void) {ArrayFree(News);}//Class destructor
  };

//+------------------------------------------------------------------+
//|(Subclass/Child) for 'NewsData'                                   |
//+------------------------------------------------------------------+
class UnitedStates:private NewsData
//private inheritance from NewsData,
//'UnitedStates' class's objects and children
//will not have access to 'NewsData' class's properties
  {
private:
   virtual void      SetNews()//private Function only Accessible in 'UnitedStates' class
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);
      News[News.Size()-1].EventID = 1;
      News[News.Size()-1].EventName = "NFP(Non-Farm Payrolls)";
      News[News.Size()-1].EventDate = D'2024.01.03 14:00:00';
     }
public:
   void              myNews()//public Function accessible via class's object
     {
      SetCountry("United States");//Calling function from 'NewsData'
      GetNews();//Calling Function from private inherited class 'NewsData'
     }
                     UnitedStates(void) {SetNews();}//Class constructor
  };

//+------------------------------------------------------------------+
//|(Subclass/Child) for 'NewsData'                                   |
//+------------------------------------------------------------------+
class Switzerland: public NewsData
//public inheritance from NewsData
  {
public:
   virtual void      SetNews()//Public Function to set News data
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);//Adjusting News structure array's size
      News[News.Size()-1].EventID = 0;//Setting event id to '0'
      News[News.Size()-1].EventName = "Interest Rate Decision";//Assigning event name
      News[News.Size()-1].EventDate = D'2024.01.06 10:00:00';//Assigning event date
     }
                     Switzerland(void) {SetCountry("Switerland"); SetNews();}//Class construct
  };

En este ejemplo:

La clase (Parent/Base/Super) sería NewsData y cualquier declaración privada sólo será accesible por esta clase. Las declaraciones privadas serán inaccesibles para los objetos e hijos de la clase. Mientras que las declaraciones protegidas serán accesibles tanto a la clase como a sus hijos. Mientras que todas las declaraciones públicas serán accesibles para la clase, sus hijos y objetos.

Tabla de accesibilidad para NewsData:

Propiedades de la clase Clase Hijos Objetos
Variable: Country(Privado)
Estructura: EventDetails(Privado)
Variable: News(Protegido)
Función: SetNews(Protegido)
Función: SetCountry(Protegido)
Función: GetNews(Público)

Constructor: NewsData(Público)
Destructor: ~NewsData(Público)
class NewsData
  {
private://Properties are only accessible from this class
   string            Country;//Private variable
   struct EventDetails//Private structure
     {
      int            EventID;
      string         EventName;
      datetime       EventDate;
     };
protected:
   //-- Protected Array Only accessible from this class and its children
   EventDetails      News[];
   //-- Proctected virtual void Function(to be expanded on via child classes)
   virtual void      SetNews();
   //-- Protected Function Only accessible from this class and its children
   void              SetCountry(string myCountry) {Country=myCountry;}
public:
   void              GetNews()//Public function to display 'News' array details
     {
      PrintFormat("+---------- %s ----------+",Country);
      for(uint i=0;i<News.Size();i++)
        {
         Print("ID: ",News[i].EventID," Name: ",News[i].EventName," Date: ",News[i].EventDate);
        }
     }
                     NewsData(void) {}//Class constructor
                    ~NewsData(void) {ArrayFree(News);}//Class destructor
  };

Propiedades visibles del objeto NewsData:  

Propiedades visibles del objeto           Función pública del objeto


Resultado de la función GetNews en NewsData:

Salida de la función

La herencia se implementa con las dos clases restantes:

En la clase (Child/Sub/Derived) UnitedStates, hereda la clase padre (NewsData) de forma privada.
Es decir, que la clase sub (UnitedStates) puede acceder a las propiedades protegidas y públicas de la clase padre (NewsData), pero los hijos de la clase UnitedStates y sus objetos no tendrán acceso a ninguna propiedad de la clase padre (NewsData). Si el modificador de herencia (modificador de acceso) fuera protector, los hijos de la clase UnitedStates tendrían acceso tanto a las propiedades protectoras como públicas de la clase padre (NewsData), pero los objetos de la clase sub (UnitedStates) no tendrían acceso a las propiedades de la clase padre. .

Tabla de accesibilidad para UnitedStates:

Propiedades de la clase Clase Hijos Objetos
Variable de herencia (privada): Country(Privado)



Estructura de herencia (privada): EventDetails(Privado)



Variable de herencia (privada): News(Protegido)



Función de herencia (privada): SetNews(Protegido)



Función de herencia (privada): SetCountry(Protegido)



Función de herencia (privada): GetNews(Público)



Constructor de herencia (privado): NewsData(Público)



Destructor de herencia (privado): ~NewsData(Público)



Función: SetNews(Privado)


Función: myNews(Público)


Constructor: UnitedStates(Público)


class UnitedStates:private NewsData
//private inheritance from NewsData,
//'UnitedStates' class's objects and children
//will not have access to 'NewsData' class's properties
  {
private:
   virtual void      SetNews()//private Function only Accessible in 'UnitedStates' class
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);
      News[News.Size()-1].EventID = 1;
      News[News.Size()-1].EventName = "NFP(Non-Farm Payrolls)";
      News[News.Size()-1].EventDate = D'2024.01.03 14:00:00';
     }
public:
   void              myNews()//public Function accessible via class's object
     {
      SetCountry("United States");//Calling function from 'NewsData'
      GetNews();//Calling Function from private inherited class 'NewsData'
     }
                     UnitedStates(void) {SetNews();}//Class constructor
  };

Propiedades visibles del objeto UnitedStates:

Propiedades visibles del objeto        Función heredada privada de NewsData


Se produce un error de compilación al intentar acceder a la función GetNews que es heredada de forma privada de NewsData, esto impide que el objeto UnitedStates pueda acceder a la función.

Error de compilación

En la clase (Child/Sub/Derived) Switzerland.

El modificador de acceso a la herencia es público. Esto proporciona a los hijos de la clase sub (Switzerland) acceso a las propiedades públicas y de protección de la clase padre (NewsData), mientras que los objetos de la clase Switzerland sólo tienen acceso a las propiedades públicas de todas las clases relacionadas.

Tabla de accesibilidad para Switzerland:

Propiedades de la clase Clase Hijos Objetos
Variable de herencia (pública): Country(Privado)



Estructura de herencia (pública): EventDetails(Privado)



Variable de herencia (pública): News(Protegido)



Función de herencia (pública): SetNews(Protegido)



Función de herencia (pública): SetCountry(Protegido)



Función de herencia (pública): GetNews(Público)



Constructor de herencia (público): NewsData(Público)



Destructor de herencia (público): ~NewsData(Público)



Función: SetNews(Público)


Constructor: Switzerland(Pública)


class Switzerland: public NewsData
//public inheritance from NewsData
  {
public:
   virtual void      SetNews()//Public Function to set News data
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);//Adjusting News structure array's size
      News[News.Size()-1].EventID = 0;//Setting event id to '0'
      News[News.Size()-1].EventName = "Interest Rate Decision";//Assigning event name
      News[News.Size()-1].EventDate = D'2024.01.06 10:00:00';//Assigning event date
     }
                     Switzerland(void) {SetCountry("Switerland"); SetNews();}//Class construct
  };

Propiedades visibles del objeto Switzerland:

Propiedades visibles del objeto

Resultados:

Resultados de los objetos


Clases de horario de verano

En "Operar con noticias de manera sencilla (Parte 1)":

Diagrama de clases UML

Diagrama de clases UML (clases de horario de verano) Parte 1

Archivos del proyecto:

Clases sobre el horario de verano, parte 1

En el código anterior teníamos tres clases para el horario de verano, a saber:

  • CDaylightSavings_AU
  • CDaylightSavings_UK
  • CDaylightSavings_US


En la Parte 2:

Diagrama de clases UML

Diagrama de clases UML (Clases de horario de verano) Parte 2

Archivos del proyecto:

Clases sobre el cambio de horario de verano, parte 2

Tendremos las siguientes clases para el cambio de horario, a saber:

  • CDaylightSavings
  • CDaylightSavings_AU
  • CDaylightSavings_UK
  • CDaylightSavings_US


¿Por qué crear otra clase sobre el horario de verano?

En el código de las clases anteriores había mucha repetición entre las clases, que esencialmente era el mismo código escrito nuevamente para diferentes valores en una lista. En lugar de que esta repetición ocurra entre clases similares, colocaremos todos los puntos en común en una clase separada y heredaremos las características comunes en las diferentes clases de horario de verano.


¿Qué son las funciones virtuales?

En la programación orientada a objetos (POO), una función virtual es una función miembro de una clase base que puedes anular en una clase derivada. Cuando una función se declara como virtual, habilita el polimorfismo, lo que permite que la clase derivada proporcione una implementación específica de la función que se puede llamar a través de un puntero o referencia de la clase base.

¿Objetivo?

  • Polimorfismo: Las funciones virtuales permiten el polimorfismo dinámico (en tiempo de ejecución). Esto significa que el método que se ejecuta se determina en tiempo de ejecución en función del tipo real del objeto al que se hace referencia, en lugar del tipo de referencia o puntero.
  • Flexibilidad: Permiten un código más flexible y reutilizable al permitir que las clases derivadas modifiquen o amplíen el comportamiento de la clase base.
  • Desacoplamiento: Las funciones virtuales ayudan a desacoplar el código al separar la interfaz de la implementación, lo que hace más fácil cambiar la implementación sin afectar el código que utiliza la interfaz de la clase base.


CDaylightSavings Class

En esta clase se ponen todos los puntos en común de los códigos anteriores en una sola clase y declararemos funciones virtuales para inicializar las diferentes listas para el horario de verano correspondiente.

La clase CDaylightSavings tiene una herencia singular de la clase CObject.

La clase CDaylightSavings tiene inclusiones de clases:

  •  CArrayObj 
  •  CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+

#include <Object.mqh>

#include <Arrays\ArrayObj.mqh>
#include "../TimeManagement.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings class                                             |
//+------------------------------------------------------------------+
class CDaylightSavings: public CObject
  {

protected:
   CTimeManagement   Time;
                     CDaylightSavings(datetime startdate,datetime enddate);
   CObject           *List() { return savings;}//Gets the list of Daylightsavings time
   datetime          StartDate;
   datetime          EndDate;
   CArrayObj         *savings;
   CArrayObj         *getSavings;
   CDaylightSavings      *dayLight;
   virtual void      SetDaylightSavings_UK();//Initialize UK Daylight Savings Dates into List
   virtual void      SetDaylightSavings_US();//Initialize US Daylight Savings Dates into List
   virtual void      SetDaylightSavings_AU();//Initialize AU Daylight Savings Dates into List

public:
                     CDaylightSavings(void);
                    ~CDaylightSavings(void);
   bool              isDaylightSavings(datetime Date);//This function checks if a given date falls within Daylight Savings Time.
   bool              DaylightSavings(int Year,datetime &startDate,datetime &endDate);//Check if DaylightSavings Dates are available for a certain Year
   string            adjustDaylightSavings(datetime EventDate);//Will adjust the date's timezone depending on DaylightSavings
  };


//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings::CDaylightSavings(void)
  {
  }

//+------------------------------------------------------------------+
//|Initialize variables                                              |
//+------------------------------------------------------------------+
CDaylightSavings::CDaylightSavings(datetime startdate,datetime enddate)
  {
   StartDate = startdate;//Assign class's global variable StartDate value from parameter variable startdate
   EndDate = enddate;//Assign class's global variable EndDate value from parameter variable enddate
  }

//+------------------------------------------------------------------+
//|checks if a given date falls within Daylight Savings Time         |
//+------------------------------------------------------------------+
bool CDaylightSavings::isDaylightSavings(datetime Date)
  {
// Initialize a list to store daylight savings periods.
   getSavings = List();
// Iterate through all the periods in the list.
   for(int i=0; i<getSavings.Total(); i++)
     {
      // Access the current daylight savings period.
      dayLight = getSavings.At(i);
      // Check if the given date is within the current daylight savings period.
      if(Time.DateIsInRange(dayLight.StartDate,dayLight.EndDate,Date))
        {
         // If yes, return true indicating it is daylight savings time.
         return true;
        }
     }
// If no period matches, return false indicating it is not daylight savings time.
   return false;
  }

//+------------------------------------------------------------------+
//|Check if DaylightSavings Dates are available for a certain Year   |
//+------------------------------------------------------------------+
bool CDaylightSavings::DaylightSavings(int Year,datetime &startDate,datetime &endDate)
  {
// Initialize a list to store daylight savings periods.
   getSavings = List();
   bool startDateDetected=false,endDateDetected=false;
// Iterate through all the periods in the list.
   for(int i=0; i<getSavings.Total(); i++)
     {
      dayLight = getSavings.At(i);
      if(Year==Time.ReturnYear(dayLight.StartDate))//Check if a certain year's date is available within the DaylightSavings start dates in the List
        {
         startDate = dayLight.StartDate;
         startDateDetected = true;
        }
      if(Year==Time.ReturnYear(dayLight.EndDate))//Check if a certain year's date is available within the DaylightSavings end dates in the List
        {
         endDate = dayLight.EndDate;
         endDateDetected = true;
        }
      if(startDateDetected&&endDateDetected)//Check if both DaylightSavings start and end dates are found for a certain Year
        {
         return true;
        }
     }

   startDate = D'1970.01.01 00:00:00';//Set a default start date if no DaylightSaving date is found
   endDate = D'1970.01.01 00:00:00';//Set a default end date if no DaylightSaving date is found
   return false;
  }

//+------------------------------------------------------------------+
//|Will adjust the date's timezone depending on DaylightSavings      |
//+------------------------------------------------------------------+
string CDaylightSavings::adjustDaylightSavings(datetime EventDate)
  {
   if(isDaylightSavings(TimeTradeServer()))//Check if the current tradeserver time is already within the DaylightSavings Period
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         return TimeToString(EventDate);//normal event time
        }
      else
        {
         return TimeToString((datetime)(EventDate-Time.HoursS()));//event time minus an hour for DST
        }
     }
   else
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         return TimeToString((datetime)(Time.HoursS()+EventDate));//event time plus an hour for DST
        }
      else
        {
         return TimeToString(EventDate);//normal event time
        }
     }
  }

//+------------------------------------------------------------------+
//|Destructor                                                        |
//+------------------------------------------------------------------+
CDaylightSavings::~CDaylightSavings(void)
  {
   delete savings;//Delete CArrayObj Pointer
   delete dayLight;//Delete CDaylightSavings Pointer
   delete getSavings;//Delete CArrayObj Pointer
  }
//+------------------------------------------------------------------+


CDaylightSavings_AU Class

En esta clase ampliamos el void virtual SetDaylightSavings_AU y procedemos a añadir el horario de verano para Australia.

Las fechas del horario de verano australiano se hallan aquí.

La clase CDaylightSavings_AU tiene herencia multinivel de clases:

  • CDaylightSavings 
  • CObject

La clase CDaylightSavings_AU tiene herencia jerárquica de clases:

  • CDaylightSavings
  • CArrayObj
  • CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+

#include "DaylightSavings.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings_AU class                                          |
//+------------------------------------------------------------------+
class CDaylightSavings_AU: public CDaylightSavings
  {

public:

                     CDaylightSavings_AU(void);
  };

//+------------------------------------------------------------------+
//|Set Daylight Savings Schedule for Australia                       |
//+------------------------------------------------------------------+
void CDaylightSavings::SetDaylightSavings_AU()
  {
   savings = new CArrayObj();
//Daylight savings dates to readjust dates in the database for accurate testing in the strategy tester
   savings.Add(new CDaylightSavings(D'2006.10.29 03:00:00',D'2007.03.25 02:00:00'));
   savings.Add(new CDaylightSavings(D'2007.10.28 03:00:00',D'2008.04.06 02:00:00'));
   savings.Add(new CDaylightSavings(D'2008.10.05 03:00:00',D'2009.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2009.10.04 03:00:00',D'2010.04.04 02:00:00'));
   savings.Add(new CDaylightSavings(D'2010.10.03 03:00:00',D'2011.04.03 02:00:00'));
   savings.Add(new CDaylightSavings(D'2011.10.02 03:00:00',D'2012.04.01 02:00:00'));
   savings.Add(new CDaylightSavings(D'2012.10.07 03:00:00',D'2013.04.07 02:00:00'));
   savings.Add(new CDaylightSavings(D'2013.10.06 03:00:00',D'2014.04.06 02:00:00'));
   savings.Add(new CDaylightSavings(D'2014.10.05 03:00:00',D'2015.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2015.10.04 03:00:00',D'2016.04.03 02:00:00'));
   savings.Add(new CDaylightSavings(D'2016.10.02 03:00:00',D'2017.04.02 02:00:00'));
   savings.Add(new CDaylightSavings(D'2017.10.01 03:00:00',D'2018.04.01 02:00:00'));
   savings.Add(new CDaylightSavings(D'2018.10.07 03:00:00',D'2019.04.07 02:00:00'));
   savings.Add(new CDaylightSavings(D'2019.10.06 03:00:00',D'2020.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2020.10.04 03:00:00',D'2021.04.04 02:00:00'));
   savings.Add(new CDaylightSavings(D'2021.10.03 03:00:00',D'2022.04.03 02:00:00'));
   savings.Add(new CDaylightSavings(D'2022.10.02 03:00:00',D'2023.04.02 02:00:00'));
   savings.Add(new CDaylightSavings(D'2023.10.01 03:00:00',D'2024.04.07 02:00:00'));
   savings.Add(new CDaylightSavings(D'2024.10.06 03:00:00',D'2025.04.06 02:00:00'));
   savings.Add(new CDaylightSavings(D'2025.10.05 03:00:00',D'2026.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2026.10.04 03:00:00',D'2027.04.04 02:00:00'));
   savings.Add(new CDaylightSavings(D'2027.10.03 03:00:00',D'2028.04.02 02:00:00'));
   savings.Add(new CDaylightSavings(D'2028.10.01 03:00:00',D'2029.04.01 02:00:00'));
  }

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings_AU::CDaylightSavings_AU(void)
  {
   SetDaylightSavings_AU();
  }
//+------------------------------------------------------------------+


CDaylightSavings_UK Class

En esta clase ampliamos el void virtual SetDaylightSavings_UK y procedemos a añadir el horario de verano para Europa.

Las fechas del horario de verano del Reino Unido se hallan aquí.

La clase CDaylightSavings_UK tiene herencia multinivel de las clases:

  • CDaylightSavings 
  • CObject

La clase CDaylightSavings_UK tiene herencia jerárquica de las clases:

  • CDaylightSavings
  • CArrayObj
  • CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+

#include "DaylightSavings.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings_UK class                                          |
//+------------------------------------------------------------------+
class CDaylightSavings_UK: public CDaylightSavings
  {

public:

                     CDaylightSavings_UK(void);
  };

//+------------------------------------------------------------------+
//|Set Daylight Savings Schedule for Europe                          |
//+------------------------------------------------------------------+
void CDaylightSavings::SetDaylightSavings_UK()
  {
   savings = new CArrayObj();
//Daylight savings dates to readjust dates in the database for accurate testing in the strategy tester
   savings.Add(new CDaylightSavings(D'2007.03.25 02:00:00',D'2007.10.28 01:00:00'));
   savings.Add(new CDaylightSavings(D'2008.03.30 02:00:00',D'2008.10.26 01:00:00'));
   savings.Add(new CDaylightSavings(D'2009.03.29 02:00:00',D'2009.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2010.03.28 02:00:00',D'2010.10.31 01:00:00'));
   savings.Add(new CDaylightSavings(D'2011.03.27 02:00:00',D'2011.10.30 01:00:00'));
   savings.Add(new CDaylightSavings(D'2012.03.25 02:00:00',D'2012.10.28 01:00:00'));
   savings.Add(new CDaylightSavings(D'2013.03.31 02:00:00',D'2013.10.27 01:00:00'));
   savings.Add(new CDaylightSavings(D'2014.03.30 02:00:00',D'2014.10.26 01:00:00'));
   savings.Add(new CDaylightSavings(D'2015.03.29 02:00:00',D'2015.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2016.03.27 02:00:00',D'2016.10.30 01:00:00'));
   savings.Add(new CDaylightSavings(D'2017.03.26 02:00:00',D'2017.10.29 01:00:00'));
   savings.Add(new CDaylightSavings(D'2018.03.25 02:00:00',D'2018.10.28 01:00:00'));
   savings.Add(new CDaylightSavings(D'2019.03.31 02:00:00',D'2019.10.27 01:00:00'));
   savings.Add(new CDaylightSavings(D'2020.03.29 02:00:00',D'2020.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2021.03.28 02:00:00',D'2021.10.31 01:00:00'));
   savings.Add(new CDaylightSavings(D'2022.03.27 02:00:00',D'2022.10.30 01:00:00'));
   savings.Add(new CDaylightSavings(D'2023.03.26 02:00:00',D'2023.10.29 01:00:00'));
   savings.Add(new CDaylightSavings(D'2024.03.31 02:00:00',D'2024.10.27 01:00:00'));
   savings.Add(new CDaylightSavings(D'2025.03.30 02:00:00',D'2025.10.26 01:00:00'));
   savings.Add(new CDaylightSavings(D'2026.03.29 02:00:00',D'2026.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2027.03.28 02:00:00',D'2027.10.31 01:00:00'));
   savings.Add(new CDaylightSavings(D'2028.03.26 02:00:00',D'2028.10.29 01:00:00'));
   savings.Add(new CDaylightSavings(D'2029.03.25 02:00:00',D'2029.10.28 01:00:00'));
  }

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings_UK::CDaylightSavings_UK(void)
  {
   SetDaylightSavings_UK();
  }
//+------------------------------------------------------------------+


CDaylightSavings_US Class

En esta clase ampliamos el void virtual SetDaylightSavings_US y procedemos a añadir el horario de verano para Estados Unidos.

Las fechas del horario de verano de Estados Unidos se hallan aquí.

La clase CDaylightSavings_US tiene herencia multinivel de las clases:

  • CDaylightSavings 
  • CObject

La clase CDaylightSavings_US tiene herencia jerárquica de las clases:

  • CDaylightSavings
  • CArrayObj
  • CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+

#include "DaylightSavings.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings_US Class                                          |
//+------------------------------------------------------------------+
class CDaylightSavings_US: public CDaylightSavings
  {

public:

                     CDaylightSavings_US(void);
  };

//+------------------------------------------------------------------+
//|Set Daylight Savings Schedule for the United States               |
//+------------------------------------------------------------------+
void CDaylightSavings::SetDaylightSavings_US()
  {
   savings = new CArrayObj();
//Daylight savings dates to readjust dates in the database for accurate testing in the strategy tester
   savings.Add(new CDaylightSavings(D'2007.03.11 03:00:00',D'2007.11.04 01:00:00'));
   savings.Add(new CDaylightSavings(D'2008.03.09 03:00:00',D'2008.11.02 01:00:00'));
   savings.Add(new CDaylightSavings(D'2009.03.08 03:00:00',D'2009.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2010.03.14 03:00:00',D'2010.11.07 01:00:00'));
   savings.Add(new CDaylightSavings(D'2011.03.13 03:00:00',D'2011.11.06 01:00:00'));
   savings.Add(new CDaylightSavings(D'2012.03.11 03:00:00',D'2012.11.04 01:00:00'));
   savings.Add(new CDaylightSavings(D'2013.03.10 03:00:00',D'2013.11.03 01:00:00'));
   savings.Add(new CDaylightSavings(D'2014.03.09 03:00:00',D'2014.11.02 01:00:00'));
   savings.Add(new CDaylightSavings(D'2015.03.08 03:00:00',D'2015.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2016.03.13 03:00:00',D'2016.11.06 01:00:00'));
   savings.Add(new CDaylightSavings(D'2017.03.12 03:00:00',D'2017.11.05 01:00:00'));
   savings.Add(new CDaylightSavings(D'2018.03.11 03:00:00',D'2018.11.04 01:00:00'));
   savings.Add(new CDaylightSavings(D'2019.03.10 03:00:00',D'2019.11.03 01:00:00'));
   savings.Add(new CDaylightSavings(D'2020.03.08 03:00:00',D'2020.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2021.03.14 03:00:00',D'2021.11.07 01:00:00'));
   savings.Add(new CDaylightSavings(D'2022.03.13 03:00:00',D'2022.11.06 01:00:00'));
   savings.Add(new CDaylightSavings(D'2023.03.12 03:00:00',D'2023.11.05 01:00:00'));
   savings.Add(new CDaylightSavings(D'2024.03.10 03:00:00',D'2024.11.03 01:00:00'));
   savings.Add(new CDaylightSavings(D'2025.03.09 03:00:00',D'2025.11.02 01:00:00'));
   savings.Add(new CDaylightSavings(D'2026.03.08 03:00:00',D'2026.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2027.03.14 03:00:00',D'2027.11.07 01:00:00'));
   savings.Add(new CDaylightSavings(D'2028.03.12 03:00:00',D'2028.11.05 01:00:00'));
   savings.Add(new CDaylightSavings(D'2029.03.11 03:00:00',D'2029.11.04 01:00:00'));
  }

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings_US::CDaylightSavings_US(void)
  {
   SetDaylightSavings_US();
  }
//+------------------------------------------------------------------+


Propiedades de los símbolos de la clase

En esta clase estableceremos un símbolo del cual nos gustaría recuperar datos. Esta clase nos proporcionará un método fácil y rápido para obtener propiedades de símbolos dentro de otras clases y, por lo tanto, eliminar la redundancia en nuestro código.

Seleccionaremos una combinación de propiedades para recuperar la lista que se podría ampliar pero por el momento la lista queda de la siguiente manera:

  • Precio Ask
  • Precio Bid
  • Tamaño del contrato
  • Volumen mínimo
  • Volumen máximo
  • Paso de volumen
  • Límite de volumen
  • Spread
  • Nivel de Stops
  • Nivel de congelamiento
  • El tiempo del símbolo
  • Precio normalizado del símbolo
  • Dígitos del símbolo
  • Puntos del símbolo
  • Modo de comercio del símbolo
  • Suma del volumen de pedidos del símbolo
  • Suma del volumen de las posiciones del símbolo
  • Moneda base del símbolo
  • Beneficio en la moneda del símbolo
  • Margen en la moneda del símbolo
  • Estado personalizado del símbolo
  • Color de fondo del símbolo

La clase CSymbolProperties tiene inclusión de la clase CSymbolInfo.

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include <Trade/SymbolInfo.mqh>
//+------------------------------------------------------------------+
//|SymbolProperties class                                            |
//+------------------------------------------------------------------+
class CSymbolProperties
  {
private:
   double            ASK;//Store Ask Price
   double            BID;//Store Bid Price
   double            LOTSMIN;//Store Minimum Lotsize
   double            LOTSMAX;//Store Maximum Lotsize
   double            LOTSSTEP;//Store Lotsize Step
   double            LOTSLIMIT;//Store Lotsize Limit(Maximum sum of Volume)
   long              SPREAD;//Store Spread value
   long              STOPLEVEL;//Store Stop level
   long              FREEZELEVEL;//Store Freeze level
   long              TIME;//Store time
   long              DIGITS;//Store Digits
   double            POINT;//Store Point
   double            ORDERSVOLUME;//Store Orders volume
   double            POSITIONSVOLUME;//Store Positions volume
   long              CUSTOM;//Store if Symbol is Custom
   long              BACKGROUND_CLR;//Store Symbol's background color

protected:
   CSymbolInfo       CSymbol;//Creating class CSymbolInfo's Object
   bool              SetSymbolName(string SYMBOL)
     {
      //-- If Symbol's name was successfully set.
      if(!CSymbol.Name((SYMBOL==NULL)?Symbol():SYMBOL))
        {
         Print("Invalid Symbol: ",SYMBOL);
         return false;
        }
      return true;
     }

   //-- Retrieve Symbol's name
   string            GetSymbolName()
     {
      return CSymbol.Name();
     }

public:
                     CSymbolProperties(void);//Constructor
   double            Ask(string SYMBOL=NULL);//Retrieve Ask Price
   double            Bid(string SYMBOL=NULL);//Retrieve Bid Price
   double            ContractSize(string SYMBOL=NULL);//Retrieve Contract Size
   double            LotsMin(string SYMBOL=NULL);//Retrieve Min Volume
   double            LotsMax(string SYMBOL=NULL);//Retrieve Max Volume
   double            LotsStep(string SYMBOL=NULL);//Retrieve Volume Step
   double            LotsLimit(string SYMBOL=NULL);//Retrieve Volume Limit
   int               Spread(string SYMBOL=NULL);//Retrieve Spread
   int               StopLevel(string SYMBOL=NULL);//Retrieve Stop Level
   int               FreezeLevel(string SYMBOL=NULL);//Retrieve Freeze Level
   datetime          Time(string SYMBOL=NULL);//Retrieve Symbol's Time
   //-- Normalize Price
   double            NormalizePrice(const double price,string SYMBOL=NULL);
   int               Digits(string SYMBOL=NULL);//Retrieve Symbol's Digits
   double            Point(string SYMBOL=NULL);//Retrieve Symbol's Point
   ENUM_SYMBOL_TRADE_MODE TradeMode(string SYMBOL=NULL);//Retrieve Symbol's Trade Mode
   double            OrdersVolume(string SYMBOL=NULL);//Retrieve Symbol's Orders Volume
   double            PositionsVolume(string SYMBOL=NULL);//Retrieve Symbol's Positions Volume
   string            CurrencyBase(string SYMBOL=NULL);//Retrieve Symbol's Currency Base
   string            CurrencyProfit(string SYMBOL=NULL);//Retrieve Symbol's Currency Profit
   string            CurrencyMargin(string SYMBOL=NULL);//Retrieve Symbol's Currency Margin
   bool              Custom(string SYMBOL=NULL);//Retrieve Symbol's Custom status
   color             SymbolBackground(string SYMBOL=NULL,bool allow_black=false);//Retrieve Symbol's Background color
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
//Initializing Variables
CSymbolProperties::CSymbolProperties(void):ASK(0.0),BID(0.0),
   LOTSMIN(0.0),LOTSMAX(0.0),
   LOTSSTEP(0.0),LOTSLIMIT(0.0),DIGITS(0),
   SPREAD(0),STOPLEVEL(0),ORDERSVOLUME(0.0),
   FREEZELEVEL(0),TIME(0),POINT(0.0),POSITIONSVOLUME(0.0),
   CUSTOM(0),BACKGROUND_CLR(0)
  {
  }

//+------------------------------------------------------------------+
//|Retrieve Ask Price                                                |
//+------------------------------------------------------------------+
double CSymbolProperties::Ask(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_ASK,ASK))
        {
         return ASK;
        }
     }
   Print("Unable to retrieve Symbol's Ask Price");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Bid Price                                                |
//+------------------------------------------------------------------+
double CSymbolProperties::Bid(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_BID,BID))
        {
         return BID;
        }
     }
   Print("Unable to retrieve Symbol's Bid Price");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Contract Size                                            |
//+------------------------------------------------------------------+
double CSymbolProperties::ContractSize(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.ContractSize();
        }
     }
   Print("Unable to retrieve Symbol's Contract size");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Min Volume                                               |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsMin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MIN,LOTSMIN))
        {
         return LOTSMIN;
        }
     }
   Print("Unable to retrieve Symbol's LotsMin");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Max Volume                                               |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsMax(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MAX,LOTSMAX))
        {
         return LOTSMAX;
        }
     }
   Print("Unable to retrieve Symbol's LotsMax");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Volume Step                                              |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsStep(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_STEP,LOTSSTEP))
        {
         return LOTSSTEP;
        }
     }
   Print("Unable to retrieve Symbol's LotsStep");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Volume Limit                                             |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsLimit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_LIMIT,LOTSLIMIT))
        {
         return LOTSLIMIT;
        }
     }
   Print("Unable to retrieve Symbol's LotsLimit");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Spread                                                   |
//+------------------------------------------------------------------+
int CSymbolProperties::Spread(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_SPREAD,SPREAD))
        {
         return int(SPREAD);
        }
     }
   Print("Unable to retrieve Symbol's Spread");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Stop Level                                               |
//+------------------------------------------------------------------+
int CSymbolProperties::StopLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_STOPS_LEVEL,STOPLEVEL))
        {
         return int(STOPLEVEL);
        }
     }
   Print("Unable to retrieve Symbol's StopLevel");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Freeze Level                                             |
//+------------------------------------------------------------------+
int CSymbolProperties::FreezeLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_FREEZE_LEVEL,FREEZELEVEL))
        {
         return int(FREEZELEVEL);
        }
     }
   Print("Unable to retrieve Symbol's FreezeLevel");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Time                                            |
//+------------------------------------------------------------------+
datetime CSymbolProperties::Time(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TIME,TIME))
        {
         return datetime(TIME);
        }
     }
   Print("Unable to retrieve Symbol's Time");
   TIME=0;
   return datetime(TIME);
  }

//+------------------------------------------------------------------+
//|Normalize Price                                                   |
//+------------------------------------------------------------------+
double CSymbolProperties::NormalizePrice(const double price,string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh()&&CSymbol.RefreshRates())
        {
         return CSymbol.NormalizePrice(price);
        }
     }
   Print("Unable to Normalize Symbol's Price");
   return price;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Digits                                          |
//+------------------------------------------------------------------+
int CSymbolProperties::Digits(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_DIGITS,DIGITS))
        {
         return int(DIGITS);
        }
     }
   Print("Unable to retrieve Symbol's Digits");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Point                                           |
//+------------------------------------------------------------------+
double CSymbolProperties::Point(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_POINT,POINT))
        {
         return POINT;
        }
     }
   Print("Unable to retrieve Symbol's Point");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Trade Mode                                      |
//+------------------------------------------------------------------+
ENUM_SYMBOL_TRADE_MODE CSymbolProperties::TradeMode(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.TradeMode();
        }
     }
   Print("Unable to retrieve Symbol's TradeMode");
   return SYMBOL_TRADE_MODE_DISABLED;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Orders Volume                                   |
//+------------------------------------------------------------------+
double CSymbolProperties::OrdersVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<OrdersTotal(); i++)
        {
         if(OrderSelect(OrderGetTicket(i)))
           {
            if(OrderGetString(ORDER_SYMBOL)==GetSymbolName())
              {
               ORDERSVOLUME+=OrderGetDouble(ORDER_VOLUME_CURRENT);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's OrdersVolume");
      return 0.0;
     }
   return ORDERSVOLUME;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Positions Volume                                |
//+------------------------------------------------------------------+
double CSymbolProperties::PositionsVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         if(PositionGetTicket(i)>0)
           {
            if(PositionGetString(POSITION_SYMBOL)==GetSymbolName())
              {
               POSITIONSVOLUME+=PositionGetDouble(POSITION_VOLUME);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's PositionsVolume");
      return 0.0;
     }
   return POSITIONSVOLUME;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Currency Base                                   |
//+------------------------------------------------------------------+
string CSymbolProperties::CurrencyBase(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyBase();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyBase");
   return "";
  }
//+------------------------------------------------------------------+
//|Retrieve Symbol's Currency Profit                                 |
//+------------------------------------------------------------------+
string CSymbolProperties::CurrencyProfit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyProfit();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyProfit");
   return "";
  }
//+------------------------------------------------------------------+
//|Retrieve Symbol's Currency Margin                                 |
//+------------------------------------------------------------------+
string CSymbolProperties::CurrencyMargin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyMargin();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyMargin");
   return "";
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Custom status                                   |
//+------------------------------------------------------------------+
bool CSymbolProperties::Custom(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_CUSTOM,CUSTOM))
        {
         return bool(CUSTOM);
        }
     }
   Print("Unable to retrieve if Symbol is Custom");
   return false;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Background color                                |
//+------------------------------------------------------------------+
color CSymbolProperties::SymbolBackground(string SYMBOL=NULL,bool allow_black=false)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_BACKGROUND_COLOR,BACKGROUND_CLR))
        {
         /*Avoid any Symbol black background color */
         BACKGROUND_CLR = ((ColorToString(color(BACKGROUND_CLR))=="0,0,0"||
                            color(BACKGROUND_CLR)==clrBlack)&&!allow_black)?
                          long(StringToColor("236,236,236")):BACKGROUND_CLR;
         return color(BACKGROUND_CLR);
        }
     }
   Print("Unable to retrieve Symbol's Background color");
   return color(StringToColor("236,236,236"));//Retrieve a lightish gray color
  }
//+------------------------------------------------------------------+

En el constructor de la clase de abajo, inicializamos las variables que declaramos anteriormente como la variable doble ASK, y asignamos a ASK el valor 0.0 ya que aún no tenemos el precio Ask del símbolo.

//Initializing Variables
CSymbolProperties::CSymbolProperties(void):ASK(0.0),BID(0.0),
   LOTSMIN(0.0),LOTSMAX(0.0),
   LOTSSTEP(0.0),LOTSLIMIT(0.0),DIGITS(0),
   SPREAD(0),STOPLEVEL(0),ORDERSVOLUME(0.0),
   FREEZELEVEL(0),TIME(0),POINT(0.0),POSITIONSVOLUME(0.0),
   CUSTOM(0),BACKGROUND_CLR(0)
  {
  }

En el siguiente código seguimos un orden de pasos para obtener finalmente el precio de venta del símbolo.

Precio Ask

1. En primer lugar tenemos un parámetro opcional donde podemos decidir introducir o editar el nombre del símbolo en la variable SYMBOL. 

2. A continuación, establecemos el nombre del símbolo en el valor del parámetro. Si el valor del parámetro sigue siendo el valor por defecto de NULL asumiremos que queremos las propiedades del símbolo para el símbolo del gráfico actual (Symbol()).

3. Si no podemos hallar el nombre del símbolo, imprimiremos un mensaje de error para notificar al usuario que no se puede recuperar el precio de venta del símbolo y devolveremos 0.0.

4. Una vez que pudimos establecer el nombre del símbolo recuperaremos el precio de venta para ese símbolo específico y devolveremos el valor.

double CSymbolProperties::Ask(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))
     {
      if(CSymbol.InfoDouble(SYMBOL_ASK,ASK))
        {
         return ASK;
        }
     }
   Print("Unable to retrieve Symbol's Ask Price");
   return 0.0;
  }
   bool              SetSymbolName(string SYMBOL)
     {
      //-- If Symbol's name was successfully set.
      if(!CSymbol.Name((SYMBOL==NULL)?Symbol():SYMBOL))
        {
         Print("Invalid Symbol: ",SYMBOL);
         return false;
        }
      return true;
     }

La siguiente función recuperará el precio de oferta (Bid) del símbolo basándose en la variable SYMBOL.

Precio Bid

double CSymbolProperties::Bid(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_BID,BID))
        {
         return BID;
        }
     }
   Print("Unable to retrieve Symbol's Bid Price");
   return 0.0;
  }

La siguiente función recuperará el tamaño del contrato del símbolo. El tamaño del contrato de un símbolo afecta al operador, ya que un mayor tamaño del contrato aumenta el riesgo de las operaciones individuales. Mientras que un tamaño de contrato menor disminuirá el riesgo en las operaciones de un individuo.

Tamaño del contrato

double CSymbolProperties::ContractSize(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.ContractSize();
        }
     }
   Print("Unable to retrieve Symbol's Contract size");
   return 0.0;
  }

La siguiente función recuperará el volumen/tamaño de lote mínimo permitido del símbolo. Esto significa que el operador no puede abrir una posición con un tamaño de lote inferior al mínimo.

Volumen mínimo

double CSymbolProperties::LotsMin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MIN,LOTSMIN))
        {
         return LOTSMIN;
        }
     }
   Print("Unable to retrieve Symbol's LotsMin");
   return 0.0;
  }

La siguiente función recuperará el tamaño/volumen de lote máximo permitido del símbolo. Esto implica que el operador no podrá abrir una posición con un tamaño de lote/volumen superior al máximo, pero podrá abrir varias posiciones que podrían sumar un tamaño de lote/volumen superior al máximo en función del límite de volumen del corredor y del límite de órdenes de la cuenta.

Volumen máximo


double CSymbolProperties::LotsMax(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MAX,LOTSMAX))
        {
         return LOTSMAX;
        }
     }
   Print("Unable to retrieve Symbol's LotsMax");
   return 0.0;
  }

La siguiente función recuperará el paso del volumen/tamaño de lote del símbolo. Significa que el tamaño del lote debe tener un intervalo de este valor. Por ejemplo, si el paso de volumen es 1, el operador no puede seleccionar un tamaño de lote/volumen de 1,5. 

Paso de volumen


double CSymbolProperties::LotsStep(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_STEP,LOTSSTEP))
        {
         return LOTSSTEP;
        }
     }
   Print("Unable to retrieve Symbol's LotsStep");
   return 0.0;
  }

La siguiente función recuperará el límite de volumen/tamaño de lote del símbolo. Esta es la suma del volumen/tamaño de lote que se puede colocar antes de que se implementen restricciones en la cuenta del comerciante para el símbolo específico.

Límite de volumen
double CSymbolProperties::LotsLimit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_LIMIT,LOTSLIMIT))
        {
         return LOTSLIMIT;
        }
     }
   Print("Unable to retrieve Symbol's LotsLimit");
   return 0.0;
  }

La siguiente función recuperará el spread del símbolo. El diferencial (spread) afecta a los operadores ya que cuanto mayor sea el diferencial del símbolo menos rentable será para el operador, dependiendo del diferencial, una estrategia podría ser rentable o no, obviamente hay muchas circunstancias diferentes que podrían hacer que una estrategia no sea rentable, pero el diferencial del símbolo podría ser una razón importante. Los diferenciales son esencialmente una forma de ingreso para su corredor, se podría pensar que es el impuesto del corredor por ese símbolo.

Spread

int CSymbolProperties::Spread(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_SPREAD,SPREAD))
        {
         return int(SPREAD);
        }
     }
   Print("Unable to retrieve Symbol's Spread");
   return 0;
  }

La siguiente función recuperará el nivel de parada del símbolo. Se trata de una restricción de la distancia mínima entre un precio de apertura y el Stop Loss o Take Profit, así como de la distancia mínima entre el precio actual Ask o Bid y el precio de apertura de una orden.

Nivel de Stops

int CSymbolProperties::StopLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_STOPS_LEVEL,STOPLEVEL))
        {
         return int(STOPLEVEL);
        }
     }
   Print("Unable to retrieve Symbol's StopLevel");
   return 0;
  }

La siguiente función recuperará el nivel de congelación del símbolo. Esta es la distancia mínima que un precio debe moverse desde su precio de apertura para que se permita el cierre de la operación (cuando se le permite cerrar la operación específica con ganancias o pérdidas).

int CSymbolProperties::FreezeLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_FREEZE_LEVEL,FREEZELEVEL))
        {
         return int(FREEZELEVEL);
        }
     }
   Print("Unable to retrieve Symbol's FreezeLevel");
   return 0;
  }

La siguiente función recuperará la hora del símbolo.

Tiempo

datetime CSymbolProperties::Time(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TIME,TIME))
        {
         return datetime(TIME);
        }
     }
   Print("Unable to retrieve Symbol's Time");
   TIME=0;
   return datetime(TIME);
  }

La siguiente función intentará normalizar el precio de un símbolo específico.

Por ejemplo, si el precio de venta para EURUSD es 1.07735 e intenta abrir una operación de compra en un nivel de precio 1.077351. Es posible que aparezca un error como precio no válido, ya que el número de dígitos decimales es superior al permitido, por ejemplo 5 dígitos. Esta función tomará el precio con 6 dígitos y lo convertirá a 5 dígitos, normalizando así el precio.

double CSymbolProperties::NormalizePrice(const double price,string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh()&&CSymbol.RefreshRates())
        {
         return CSymbol.NormalizePrice(price);
        }
     }
   Print("Unable to Normalize Symbol's Price");
   return price;
  }

La siguiente función recuperará los dígitos del símbolo. Los dígitos se representan como los decimales del precio del símbolo.

Dígitos

Los decimales del símbolo son 3

int CSymbolProperties::Digits(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_DIGITS,DIGITS))
        {
         return int(DIGITS);
        }
     }
   Print("Unable to retrieve Symbol's Digits");
   return 0;
  }

La siguiente función recuperará el punto del símbolo.

double CSymbolProperties::Point(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_POINT,POINT))
        {
         return POINT;
        }
     }
   Print("Unable to retrieve Symbol's Point");
   return 0.0;
  }

La siguiente función recuperará el modo comercial del símbolo.

Modo de comercio

ENUM_SYMBOL_TRADE_MODE CSymbolProperties::TradeMode(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.TradeMode();
        }
     }
   Print("Unable to retrieve Symbol's TradeMode");
   return SYMBOL_TRADE_MODE_DISABLED;
  }

La siguiente función recuperará la suma del volumen/lotes de las órdenes del símbolo.

double CSymbolProperties::OrdersVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<OrdersTotal(); i++)
        {
         if(OrderSelect(OrderGetTicket(i)))
           {
            if(OrderGetString(ORDER_SYMBOL)==GetSymbolName())
              {
               ORDERSVOLUME+=OrderGetDouble(ORDER_VOLUME_CURRENT);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's OrdersVolume");
      return 0.0;
     }
   return ORDERSVOLUME;
  }

La siguiente función recuperará la suma del volumen/lotes de las posiciones del símbolo.

double CSymbolProperties::PositionsVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         if(PositionGetTicket(i)>0)
           {
            if(PositionGetString(POSITION_SYMBOL)==GetSymbolName())
              {
               POSITIONSVOLUME+=PositionGetDouble(POSITION_VOLUME);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's PositionsVolume");
      return 0.0;
     }
   return POSITIONSVOLUME;
  }

La siguiente función recuperará la moneda base del símbolo.

string CSymbolProperties::CurrencyBase(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyBase();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyBase");
   return "";
  }

La siguiente función recuperará la moneda de beneficio del símbolo.

Moneda de beneficio

string CSymbolProperties::CurrencyProfit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyProfit();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyProfit");
   return "";
  }

La siguiente función recuperará la moneda de margen del símbolo.

Moneda de margen

string CSymbolProperties::CurrencyMargin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyMargin();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyMargin");
   return "";
  }

La siguiente función recuperará un valor booleano para identificar si un símbolo es personalizado o no.

bool CSymbolProperties::Custom(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_CUSTOM,CUSTOM))
        {
         return bool(CUSTOM);
        }
     }
   Print("Unable to retrieve if Symbol is Custom");
   return false;
  }

La siguiente función recuperará el color de fondo del símbolo. Tiene un parámetro opcional allow_black que es falso por defecto, esto es porque usaremos el color de fondo del símbolo para establecer el color de fondo del gráfico más adelante y sí queremos recuperar un color negro ya que otros elementos de nuestro gráfico estarán en negro. Si permitiéramos el color negro, en función del formato de gráfico previsto, el gráfico resultaría ilegible.

Ejemplo de gráfico de fondo negro con nuestro nuevo formato de gráfico que se establecerá más adelante.

Fondo negro

color CSymbolProperties::SymbolBackground(string SYMBOL=NULL,bool allow_black=false)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_BACKGROUND_COLOR,BACKGROUND_CLR))
        {
        /*Avoid any Symbol black background color */
         BACKGROUND_CLR = ((ColorToString(color(BACKGROUND_CLR))=="0,0,0"||
                            color(BACKGROUND_CLR)==clrBlack)&&!allow_black)?
                          long(StringToColor("236,236,236")):BACKGROUND_CLR;
         return color(BACKGROUND_CLR);
        }
     }
   Print("Unable to retrieve Symbol's Background color");
   return color(StringToColor("236,236,236"));//Retrieve a lightish gray color
  }


Clase de gestión del tiempo

En esta clase, resaltaré las nuevas funciones agregadas a la funcionalidad de la clase. El propósito de esta clase es manipular y/o interactuar con datos de tiempo.

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|TimeManagement class                                              |
//+------------------------------------------------------------------+
class CTimeManagement
  {

private:

   MqlDateTime       today;//private variable
   MqlDateTime       timeFormat;//private variable

public:

   //-- Checks if a date is within two other dates
   bool              DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime);
   //-- Check if two dates(Start&End) are within CompareStart & CompareEnd
   bool              DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd);
   bool              DateisToday(datetime TimeRepresented);//Checks if a date is within the current day
   int               SecondsS(int multiple=1);//Returns seconds
   int               MinutesS(int multiple=1);//Returns Minutes in seconds
   int               HoursS(int multiple=1);//Returns Hours in seconds
   int               DaysS(int multiple=1);//Returns Days in seconds
   int               WeeksS(int multiple=1);//Returns Weeks in seconds
   int               MonthsS(int multiple=1);//Returns Months in seconds
   int               YearsS(int multiple=1);//Returns Years in seconds
   int               ReturnYear(datetime time);//Returns the Year for a specific date
   int               ReturnMonth(datetime time);//Returns the Month for a specific date
   int               ReturnDay(datetime time);//Returns the Day for a specific date
   //-- Will return a datetime type of a date with an subtraction offset in seconds
   datetime          TimeMinusOffset(datetime standardtime,int timeoffset);
   //-- Will return a datetime type of a date with an addition offset in seconds
   datetime          TimePlusOffset(datetime standardtime,int timeoffset);
  };

//+------------------------------------------------------------------+
//|Checks if a date is within two other dates                        |
//+------------------------------------------------------------------+
bool CTimeManagement::DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime)
  {
   return(FirstTime<=compareTime&&SecondTime>compareTime);
  }

//+------------------------------------------------------------------+
//|Check if two dates(Start&End) are within CompareStart & CompareEnd|
//+------------------------------------------------------------------+
bool CTimeManagement::DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd)
  {
   return(Start<=CompareStart&&CompareEnd<End);
  }

//+------------------------------------------------------------------+
//|Checks if a date is within the current day                        |
//+------------------------------------------------------------------+
bool CTimeManagement::DateisToday(datetime TimeRepresented)
  {
   MqlDateTime TiM;
   TimeToStruct(TimeRepresented,TiM);
   TimeCurrent(today);
   return(TiM.year==today.year&&TiM.mon==today.mon&&TiM.day==today.day);
  }

//+------------------------------------------------------------------+
//|Returns seconds                                                   |
//+------------------------------------------------------------------+
int CTimeManagement::SecondsS(int multiple=1)
  {
   return (1*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Minutes in seconds                                        |
//+------------------------------------------------------------------+
int CTimeManagement::MinutesS(int multiple=1)
  {
   return (SecondsS(60)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Hours in seconds                                          |
//+------------------------------------------------------------------+
int CTimeManagement::HoursS(int multiple=1)
  {
   return (MinutesS(60)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Days in seconds                                           |
//+------------------------------------------------------------------+
int CTimeManagement::DaysS(int multiple=1)
  {
   return (HoursS(24)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Weeks in seconds                                          |
//+------------------------------------------------------------------+
int CTimeManagement::WeeksS(int multiple=1)
  {
   return (DaysS(7)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Months in seconds                                         |
//+------------------------------------------------------------------+
int CTimeManagement::MonthsS(int multiple=1)
  {
   return (WeeksS(4)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Years in seconds                                          |
//+------------------------------------------------------------------+
int CTimeManagement::YearsS(int multiple=1)
  {
   return (MonthsS(12)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns the Year for a specific date                              |
//+------------------------------------------------------------------+
int CTimeManagement::ReturnYear(datetime time)
  {
   TimeToStruct(time,timeFormat);
   return timeFormat.year;
  }

//+------------------------------------------------------------------+
//|Returns the Month for a specific date                             |
//+------------------------------------------------------------------+
int CTimeManagement::ReturnMonth(datetime time)
  {
   TimeToStruct(time,timeFormat);
   return timeFormat.mon;
  }

//+------------------------------------------------------------------+
//|Returns the Day for a specific date                               |
//+------------------------------------------------------------------+
int CTimeManagement::ReturnDay(datetime time)
  {
   TimeToStruct(time,timeFormat);
   return timeFormat.day;
  }

//+------------------------------------------------------------------+
//|Will return a datetime type of a date with an subtraction offset  |
//|in seconds                                                        |
//+------------------------------------------------------------------+
datetime CTimeManagement::TimeMinusOffset(datetime standardtime,int timeoffset)
  {
   standardtime-=timeoffset;
   return standardtime;
  }

//+------------------------------------------------------------------+
//|Will return a datetime type of a date with an addition offset     | 
//|in seconds                                                        |
//+------------------------------------------------------------------+
datetime CTimeManagement::TimePlusOffset(datetime standardtime,int timeoffset)
  {
   standardtime+=timeoffset;
   return standardtime;
  }
//+------------------------------------------------------------------+


Clase de propiedades de gráfico

El propósito de las propiedades del gráfico es almacenar la configuración del gráfico antes de cambiar el diseño del gráfico. Una vez que se elimina el experto del gráfico, el destructor de la clase restaurará el estado de configuración del gráfico antes de los cambios que hubiéramos realizado en el gráfico.

Realizar cambios en el gráfico no es necesario para la funcionalidad de los expertos, pero es visualmente agradable tener un gráfico que no sea solo verde y negro (diseño de gráfico predeterminado) y puede ser difícil distinguir los precios del gráfico o los niveles comerciales una vez que se realizan las transacciones.

CChartProperties tiene una herencia singular de la clase CSymbolProperties.

CChartProperties tiene herencia jerárquica de clases:

  • CSymbolProperties
  • CSymbolInfo
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "SymbolProperties.mqh"
//+------------------------------------------------------------------+
//|ChartProperties class                                             |
//+------------------------------------------------------------------+
class CChartProperties : public CSymbolProperties
  {
private:
   struct ChartFormat
     {
      ulong             CHART_MODE;//Chart Candle Mode
      ulong             CHART_COLOR_BACKGROUND;//Chart Background Color
      ulong             CHART_COLOR_FOREGROUND;//Chart Foreground Color
      ulong             CHART_COLOR_CHART_LINE;//Chart Line Color
      ulong             CHART_COLOR_CANDLE_BEAR;//Chart Bear Candle Color
      ulong             CHART_COLOR_CHART_DOWN;//Chart Down Candle Color
      ulong             CHART_COLOR_CANDLE_BULL;//Chart Bull Candle Color
      ulong             CHART_COLOR_CHART_UP;//Chart Up Candle Color
      ulong             CHART_COLOR_ASK;//Chart Ask Color
      ulong             CHART_COLOR_BID;//Chart Bid Color
      ulong             CHART_COLOR_STOP_LEVEL;//Chart Stoplevel Color
      ulong             CHART_SHOW_PERIOD_SEP;//Chart Show Period Separator
      ulong             CHART_SCALE;//Chart Scale
      ulong             CHART_FOREGROUND;//Chart Show Foreground
      ulong             CHART_SHOW_ASK_LINE;//Chart Show Ask Line
      ulong             CHART_SHOW_BID_LINE;//Chart Show Bid Line
      ulong             CHART_SHOW_TRADE_LEVELS;//Chart Show Trade Levels
      ulong             CHART_SHOW_OHLC;//Chart Show Open-High-Low-Close
      ulong             CHART_SHOW_GRID;//Chart Show Grid
      ulong             CHART_SHOW_VOLUMES;//Chart Show Volumes
      ulong             CHART_AUTOSCROLL;//Chart Auto Scroll
      double            CHART_SHIFT_SIZE;//Chart Shift Size
      ulong             CHART_SHIFT;//Chart Shift
      ulong             CHART_SHOW_ONE_CLICK;//Chart One Click Trading
     };
   ulong             ChartConfig[65];//Array To Store Chart Properties
   void              ChartSet();//Apply Chart format
   void              ChartConfigure();//Set Chart Values
   ChartFormat       Chart;//Variable of type ChartFormat

public:
                     CChartProperties(void);//Constructor
                    ~CChartProperties(void);//Destructor
   void              ChartRefresh() {ChartConfigure();}
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CChartProperties::CChartProperties(void)//Class Constructor
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartGetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Storing Chart values into ChartConfig array
     }
   ChartConfigure();
  }

//+------------------------------------------------------------------+
//|Destructor                                                        |
//+------------------------------------------------------------------+
CChartProperties::~CChartProperties(void)
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartSetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Restoring Chart values from ChartConfig array
     }
  }

//+------------------------------------------------------------------+
//|Set Chart Properties                                              |
//+------------------------------------------------------------------+
void CChartProperties::ChartSet()
  {
   ChartSetInteger(0,CHART_MODE,Chart.CHART_MODE);//Set Chart Candle Mode
   ChartSetInteger(0,CHART_COLOR_BACKGROUND,Chart.CHART_COLOR_BACKGROUND);//Set Chart Background Color
   ChartSetInteger(0,CHART_COLOR_FOREGROUND,Chart.CHART_COLOR_FOREGROUND);//Set Chart Foreground Color
   ChartSetInteger(0,CHART_COLOR_CHART_LINE,Chart.CHART_COLOR_CHART_LINE);//Set Chart Line Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,Chart.CHART_COLOR_CANDLE_BEAR);//Set Chart Bear Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_DOWN,Chart.CHART_COLOR_CHART_DOWN);//Set Chart Down Candle Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,Chart.CHART_COLOR_CANDLE_BULL);//Set Chart Bull Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_UP,Chart.CHART_COLOR_CHART_UP);//Set Chart Up Candle Color
   ChartSetInteger(0,CHART_COLOR_ASK,Chart.CHART_COLOR_ASK);//Set Chart Ask Color
   ChartSetInteger(0,CHART_COLOR_BID,Chart.CHART_COLOR_BID);//Set Chart Bid Color
   ChartSetInteger(0,CHART_COLOR_STOP_LEVEL,Chart.CHART_COLOR_STOP_LEVEL);//Set Chart Stop Level Color
   ChartSetInteger(0,CHART_FOREGROUND,Chart.CHART_FOREGROUND);//Set if Chart is in Foreground Visibility
   ChartSetInteger(0,CHART_SHOW_ASK_LINE,Chart.CHART_SHOW_ASK_LINE);//Set Chart Ask Line Visibility
   ChartSetInteger(0,CHART_SHOW_BID_LINE,Chart.CHART_SHOW_BID_LINE);//Set Chart Bid Line Visibility
   ChartSetInteger(0,CHART_SHOW_PERIOD_SEP,Chart.CHART_SHOW_PERIOD_SEP);//Set Chart Period Separator Visibility
   ChartSetInteger(0,CHART_SHOW_TRADE_LEVELS,Chart.CHART_SHOW_TRADE_LEVELS);//Set Chart Trade Levels Visibility
   ChartSetInteger(0,CHART_SHOW_OHLC,Chart.CHART_SHOW_OHLC);//Set Chart Open-High-Low-Close Visibility
   ChartSetInteger(0,CHART_SHOW_GRID,Chart.CHART_SHOW_GRID);//Set Chart Grid Visibility
   ChartSetInteger(0,CHART_SHOW_VOLUMES,Chart.CHART_SHOW_VOLUMES);//Set Chart Volumes Visibility
   ChartSetInteger(0,CHART_SCALE,Chart.CHART_SCALE);//Set Chart Scale Value
   ChartSetInteger(0,CHART_AUTOSCROLL,Chart.CHART_AUTOSCROLL);//Set Chart Auto Scroll Option
   ChartSetDouble(0,CHART_SHIFT_SIZE,Chart.CHART_SHIFT_SIZE);//Set Chart Shift Size Value
   ChartSetInteger(0,CHART_SHIFT,Chart.CHART_SHIFT);//Set Chart Shift Option
   ChartSetInteger(0,CHART_SHOW_ONE_CLICK,Chart.CHART_SHOW_ONE_CLICK);//Set Chart One Click Trading
  }

//+------------------------------------------------------------------+
//|Initialize Chart Properties                                       |
//+------------------------------------------------------------------+
void CChartProperties::ChartConfigure(void)
  {
   Chart.CHART_MODE=(ulong)CHART_CANDLES;//Assigning Chart Mode of CHART_CANDLES
   Chart.CHART_COLOR_BACKGROUND=ulong(SymbolBackground());//Assigning Chart Background Color of Symbol's Background color
   Chart.CHART_COLOR_FOREGROUND=(ulong)clrBlack;//Assigning Chart Foreground Color of clrBalck(Black color)
   Chart.CHART_COLOR_CHART_LINE=(ulong)clrBlack;//Assigning Chart Line Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BEAR=(ulong)clrBlack;//Assigning Chart Bear Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CHART_DOWN=(ulong)clrBlack;//Assigning Chart Down Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BULL=(ulong)clrWhite;//Assigning Chart Bull Candle Color of clrWhite(White color)
   Chart.CHART_COLOR_CHART_UP=(ulong)clrBlack;//Assigning Chart Up Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_ASK=(ulong)clrBlack;//Assigning Chart Ask Color of clrBlack(Black color)
   Chart.CHART_COLOR_BID=(ulong)clrBlack;//Assigning Chart Bid Color of clrBlack(Black color)
   Chart.CHART_COLOR_STOP_LEVEL=(ulong)clrBlack;//Assigning Chart Stop Level Color of clrBlack(Black color)
   Chart.CHART_FOREGROUND=(ulong)false;//Assigning Chart Foreground Boolean Value of 'false'
   Chart.CHART_SHOW_ASK_LINE=(ulong)true;//Assigning Chart Ask Line Boolean Value of 'true'
   Chart.CHART_SHOW_BID_LINE=(ulong)true;//Assigning Chart Bid Line Boolean Value of 'true'
   Chart.CHART_SHOW_PERIOD_SEP=(ulong)true;//Assigning Chart Period Separator Boolean Value of 'true'
   Chart.CHART_SHOW_TRADE_LEVELS=(ulong)true;//Assigning Chart Trade Levels Boolean Value of 'true'
   Chart.CHART_SHOW_OHLC=(ulong)false;//Assigning Chart Open-High-Low-Close Boolean Value of 'false'
   Chart.CHART_SHOW_GRID=(ulong)false;//Assigning Chart Grid Boolean Value of 'false'
   Chart.CHART_SHOW_VOLUMES=(ulong)false;//Assigning Chart Volumes Boolean Value of 'false'
   Chart.CHART_SCALE=(ulong)3;//Assigning Chart Scale Boolean Value of '3'
   Chart.CHART_AUTOSCROLL=(ulong)true;//Assigning Chart Auto Scroll Boolean Value of 'true'
   Chart.CHART_SHIFT_SIZE=30;//Assigning Chart Shift Size Value of '30'
   Chart.CHART_SHIFT=(ulong)true;//Assigning Chart Shift Boolean Value of 'true'
   Chart.CHART_SHOW_ONE_CLICK=ulong(false);//Assigning Chart One Click Trading a value of 'false'
   ChartSet();//Calling Function to set chart format
  }
//+------------------------------------------------------------------+

En la estructura ChartFormat que declaramos, almacenamos diferentes variables de gráfico que cambiaremos en el gráfico actual en el que se encuentre el experto.

   struct ChartFormat
     {
      ulong             CHART_MODE;//Chart Candle Mode
      ulong             CHART_COLOR_BACKGROUND;//Chart Background Color
      ulong             CHART_COLOR_FOREGROUND;//Chart Foreground Color
      ulong             CHART_COLOR_CHART_LINE;//Chart Line Color
      ulong             CHART_COLOR_CANDLE_BEAR;//Chart Bear Candle Color
      ulong             CHART_COLOR_CHART_DOWN;//Chart Down Candle Color
      ulong             CHART_COLOR_CANDLE_BULL;//Chart Bull Candle Color
      ulong             CHART_COLOR_CHART_UP;//Chart Up Candle Color
      ulong             CHART_COLOR_ASK;//Chart Ask Color
      ulong             CHART_COLOR_BID;//Chart Bid Color
      ulong             CHART_COLOR_STOP_LEVEL;//Chart Stoplevel Color
      ulong             CHART_SHOW_PERIOD_SEP;//Chart Show Period Separator
      ulong             CHART_SCALE;//Chart Scale
      ulong             CHART_FOREGROUND;//Chart Show Foreground
      ulong             CHART_SHOW_ASK_LINE;//Chart Show Ask Line
      ulong             CHART_SHOW_BID_LINE;//Chart Show Bid Line
      ulong             CHART_SHOW_TRADE_LEVELS;//Chart Show Trade Levels
      ulong             CHART_SHOW_OHLC;//Chart Show Open-High-Low-Close
      ulong             CHART_SHOW_GRID;//Chart Show Grid
      ulong             CHART_SHOW_VOLUMES;//Chart Show Volumes
      ulong             CHART_AUTOSCROLL;//Chart Auto Scroll
      double            CHART_SHIFT_SIZE;//Chart Shift Size
      ulong             CHART_SHIFT;//Chart Shift
      ulong             CHART_SHOW_ONE_CLICK;//Chart One Click Trading
     };

El array ChartConfig almacenará todas las propiedades del gráfico antes de que hagamos cambios en él.

ulong             ChartConfig[65];//Array To Store Chart Properties

En la función SetBackground obtenemos el color de fondo del símbolo actual de "Observación del Mercado":

Observación del Mercado

Y establezca el color de fondo del gráfico actual:

Color de fondo del gráfico

En el constructor de la clase obtenemos todas las propiedades del gráfico de tipo entero y las almacenamos en el array ChartConfig. 

CChartProperties::CChartProperties(void)//Class Constructor
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartGetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Storing Chart values into ChartConfig array
     }
   ChartConfigure();
  }

También inicializamos la variable Chart que es de tipo estructura ChartFormat anteriormente mencionada y le damos los valores adecuados para personalizar el gráfico a nuestro gusto en la función ChartConfigure.

void CChartProperties::ChartConfigure(void)
  {
   Chart.CHART_MODE=(ulong)CHART_CANDLES;//Assigning Chart Mode of CHART_CANDLES
   Chart.CHART_COLOR_BACKGROUND=ulong(SymbolBackground());//Assigning Chart Background Color of Symbol's Background color
   Chart.CHART_COLOR_FOREGROUND=(ulong)clrBlack;//Assigning Chart Foreground Color of clrBalck(Black color)
   Chart.CHART_COLOR_CHART_LINE=(ulong)clrBlack;//Assigning Chart Line Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BEAR=(ulong)clrBlack;//Assigning Chart Bear Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CHART_DOWN=(ulong)clrBlack;//Assigning Chart Down Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BULL=(ulong)clrWhite;//Assigning Chart Bull Candle Color of clrWhite(White color)
   Chart.CHART_COLOR_CHART_UP=(ulong)clrBlack;//Assigning Chart Up Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_ASK=(ulong)clrBlack;//Assigning Chart Ask Color of clrBlack(Black color)
   Chart.CHART_COLOR_BID=(ulong)clrBlack;//Assigning Chart Bid Color of clrBlack(Black color)
   Chart.CHART_COLOR_STOP_LEVEL=(ulong)clrBlack;//Assigning Chart Stop Level Color of clrBlack(Black color)
   Chart.CHART_FOREGROUND=(ulong)false;//Assigning Chart Foreground Boolean Value of 'false'
   Chart.CHART_SHOW_ASK_LINE=(ulong)true;//Assigning Chart Ask Line Boolean Value of 'true'
   Chart.CHART_SHOW_BID_LINE=(ulong)true;//Assigning Chart Bid Line Boolean Value of 'true'
   Chart.CHART_SHOW_PERIOD_SEP=(ulong)true;//Assigning Chart Period Separator Boolean Value of 'true'
   Chart.CHART_SHOW_TRADE_LEVELS=(ulong)true;//Assigning Chart Trade Levels Boolean Value of 'true'
   Chart.CHART_SHOW_OHLC=(ulong)false;//Assigning Chart Open-High-Low-Close Boolean Value of 'false'
   Chart.CHART_SHOW_GRID=(ulong)false;//Assigning Chart Grid Boolean Value of 'false'
   Chart.CHART_SHOW_VOLUMES=(ulong)false;//Assigning Chart Volumes Boolean Value of 'false'
   Chart.CHART_SCALE=(ulong)3;//Assigning Chart Scale Boolean Value of '3'
   Chart.CHART_AUTOSCROLL=(ulong)true;//Assigning Chart Auto Scroll Boolean Value of 'true'
   Chart.CHART_SHIFT_SIZE=30;//Assigning Chart Shift Size Value of '30'
   Chart.CHART_SHIFT=(ulong)true;//Assigning Chart Shift Boolean Value of 'true'
   Chart.CHART_SHOW_ONE_CLICK=ulong(false);//Assigning Chart One Click Trading a value of 'false'
   ChartSet();//Calling Function to set chart format
  }

En la función ChartSet se establecerán los valores de las propiedades del gráfico seleccionadas de la variable Chart de tipo de estructura ChartFormat.

void CChartProperties::ChartSet()
  {
   ChartSetInteger(0,CHART_MODE,Chart.CHART_MODE);//Set Chart Candle Mode
   ChartSetInteger(0,CHART_COLOR_BACKGROUND,Chart.CHART_COLOR_BACKGROUND);//Set Chart Background Color
   ChartSetInteger(0,CHART_COLOR_FOREGROUND,Chart.CHART_COLOR_FOREGROUND);//Set Chart Foreground Color
   ChartSetInteger(0,CHART_COLOR_CHART_LINE,Chart.CHART_COLOR_CHART_LINE);//Set Chart Line Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,Chart.CHART_COLOR_CANDLE_BEAR);//Set Chart Bear Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_DOWN,Chart.CHART_COLOR_CHART_DOWN);//Set Chart Down Candle Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,Chart.CHART_COLOR_CANDLE_BULL);//Set Chart Bull Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_UP,Chart.CHART_COLOR_CHART_UP);//Set Chart Up Candle Color
   ChartSetInteger(0,CHART_COLOR_ASK,Chart.CHART_COLOR_ASK);//Set Chart Ask Color
   ChartSetInteger(0,CHART_COLOR_BID,Chart.CHART_COLOR_BID);//Set Chart Bid Color
   ChartSetInteger(0,CHART_COLOR_STOP_LEVEL,Chart.CHART_COLOR_STOP_LEVEL);//Set Chart Stop Level Color
   ChartSetInteger(0,CHART_FOREGROUND,Chart.CHART_FOREGROUND);//Set if Chart is in Foreground Visibility
   ChartSetInteger(0,CHART_SHOW_ASK_LINE,Chart.CHART_SHOW_ASK_LINE);//Set Chart Ask Line Visibility
   ChartSetInteger(0,CHART_SHOW_BID_LINE,Chart.CHART_SHOW_BID_LINE);//Set Chart Bid Line Visibility
   ChartSetInteger(0,CHART_SHOW_PERIOD_SEP,Chart.CHART_SHOW_PERIOD_SEP);//Set Chart Period Separator Visibility
   ChartSetInteger(0,CHART_SHOW_TRADE_LEVELS,Chart.CHART_SHOW_TRADE_LEVELS);//Set Chart Trade Levels Visibility
   ChartSetInteger(0,CHART_SHOW_OHLC,Chart.CHART_SHOW_OHLC);//Set Chart Open-High-Low-Close Visibility
   ChartSetInteger(0,CHART_SHOW_GRID,Chart.CHART_SHOW_GRID);//Set Chart Grid Visibility
   ChartSetInteger(0,CHART_SHOW_VOLUMES,Chart.CHART_SHOW_VOLUMES);//Set Chart Volumes Visibility
   ChartSetInteger(0,CHART_SCALE,Chart.CHART_SCALE);//Set Chart Scale Value
   ChartSetInteger(0,CHART_AUTOSCROLL,Chart.CHART_AUTOSCROLL);//Set Chart Auto Scroll Option
   ChartSetDouble(0,CHART_SHIFT_SIZE,Chart.CHART_SHIFT_SIZE);//Set Chart Shift Size Value
   ChartSetInteger(0,CHART_SHIFT,Chart.CHART_SHIFT);//Set Chart Shift Option
   ChartSetInteger(0,CHART_SHOW_ONE_CLICK,Chart.CHART_SHOW_ONE_CLICK);//Set Chart One Click Trading
  }

En el destructor restauraremos los valores enteros del gráfico anterior.

CChartProperties::~CChartProperties(void)
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartSetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Restoring Chart values from ChartConfig array
     }
  }


Clase de propiedades de las velas

CCandleProperties tiene herencia multinivel desde las clases:

  • CChartProperties
  • CSymbolProperties

CCandleProperties tiene inclusión de la clase CTimeManagement.

CCandleProperties tiene herencia jerárquica de clases:

  • CSymbolProperties
  • CSymbolInfo

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "TimeManagement.mqh"
#include "ChartProperties.mqh"
//+------------------------------------------------------------------+
//|CandleProperties class                                            |
//+------------------------------------------------------------------+
class CCandleProperties : public CChartProperties
  {
private:
   CTimeManagement   Time;

public:
   double            Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Open-Price
   double            Close(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Close-Price
   double            High(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle High-Price
   double            Low(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Low-Price
   bool              IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL);//Determine if one candle is larger than two others
  };

//+------------------------------------------------------------------+
//|Retrieve Candle Open-Price                                        |
//+------------------------------------------------------------------+
double CCandleProperties::Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iOpen(GetSymbolName(),Period,CandleIndex):0;//return candle open price
  }

//+------------------------------------------------------------------+
//|Retrieve Candle Close-Price                                       |
//+------------------------------------------------------------------+
double CCandleProperties::Close(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iClose(GetSymbolName(),Period,CandleIndex):0;//return candle close price
  }

//+------------------------------------------------------------------+
//|Retrieve Candle High-Price                                        |
//+------------------------------------------------------------------+
double CCandleProperties::High(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iHigh(GetSymbolName(),Period,CandleIndex):0;//return candle high price
  }

//+------------------------------------------------------------------+
//|Retrieve Candle Low-Price                                         |
//+------------------------------------------------------------------+
double CCandleProperties::Low(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iLow(GetSymbolName(),Period,CandleIndex):0;//return candle low price
  }

//+------------------------------------------------------------------+
//|Determine if one candle is larger than two others                |
//+------------------------------------------------------------------+
bool CCandleProperties::IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL)
  {
   int CandleIndex = iBarShift(SYMBOL,PERIOD_M15,CandleTime);//Assign candle index of candletime
//--Assign candle index of candletime minus time offset
   int CandleIndexMinusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimeMinusOffset(CandleTime,Offset));
//--Assign candle index of candletime plus time offset
   int CandleIndexPlusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimePlusOffset(CandleTime,Offset));
//--Assign height of M15 candletime in pips
   double CandleHeight = High(CandleIndex,PERIOD_M15,SYMBOL)-Low(CandleIndex,PERIOD_M15,SYMBOL);
//--Assign height of M15 candletime  minus offset in Pips
   double CandleHeightMinusOffset = High(CandleIndexMinusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexMinusOffset,PERIOD_M15,SYMBOL);
//--Assign height of M15 candletime plus offset in Pips
   double CandleHeightPlusOffset = High(CandleIndexPlusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexPlusOffset,PERIOD_M15,SYMBOL);
//--Determine if candletime height is greater than candletime height minus offset and candletime height plus offset
   if(CandleHeight>CandleHeightMinusOffset&&CandleHeight>CandleHeightPlusOffset)
     {
      return true;//Candletime is likely when the news event occured
     }
   return false;//Candletime is unlikely when the real news data was released
  }
//+------------------------------------------------------------------+


Clase de propiedades de objeto

Esta clase se encargará de crear y eliminar objetos gráficos.

CObjectProperties tiene herencia multinivel de clases:

  • CChartProperties
  • CSymbolProperties
CObjectProperties tiene herencia jerárquica de las clases:

  • CSymbolProperties
  • CSymbolInfo
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "ChartProperties.mqh"
//+------------------------------------------------------------------+
//|ObjectProperties class                                            |
//+------------------------------------------------------------------+
class CObjectProperties:public CChartProperties
  {
private:
   //Simple  chart objects structure
   struct ObjStruct
     {
      long           ChartId;
      string         Name;
     } Objects[];//ObjStruct variable array

   //-- Add chart object to Objects array
   void              AddObj(long chart_id,string name)
     {
      ArrayResize(Objects,Objects.Size()+1,Objects.Size()+2);
      Objects[Objects.Size()-1].ChartId=chart_id;
      Objects[Objects.Size()-1].Name=name;
     }

public:
                     CObjectProperties(void) {}//Class constructor

   //-- Create Rectangle chart object
   void              Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor);

   //-- Create text chart object
   void              TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                             ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10);

   //-- Create Event object
   void               EventObj(long chartID,string name,string description,datetime eventdate);

   //-- Class destructor removes all chart objects created previously
                    ~CObjectProperties(void)
     {
      for(uint i=0;i<Objects.Size();i++)
        {
         ObjectDelete(Objects[i].ChartId,Objects[i].Name);
        }
     }
  };

//+------------------------------------------------------------------+
//|Create Rectangle chart object                                     |
//+------------------------------------------------------------------+
void CObjectProperties::Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor)
  {
   const int              sub_window=0;             // subwindow index
   const int              x=x_coord;                // X coordinate
   const int              y=y_coord;                // Y coordinate
   const color            back_clr=clrBlack;        // background color
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // border type
   const color            clr=clrRed;               // flat border color (Flat)
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // flat border style
   const int              line_width=0;             // flat border width
   const bool             back=false;               // in the background
   const bool             selection=false;          // highlight to move
   const bool             hidden=true;              // hidden in the object list

   ObjectDelete(chart_ID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0))//create rectangle object label
     {
      AddObj(chart_ID,name);//Add object to array
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);//Set x Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);//Set y Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);//Set object's width/x-size
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);//Set object's height/y-size
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);//Set object's background color
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);//Set object's border type
      ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,Anchor);//Set objects anchor point
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);//Set object's color
      ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);//Set object's style
      ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);//Set object's flat border width
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);//Set if object is in foreground or not
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);//Set if object is selectable/dragable
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);//Set if object is Selected
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);//Set if object is hidden in object list
      ChartRedraw(chart_ID);
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

//+------------------------------------------------------------------+
//|Create text chart object                                          |
//+------------------------------------------------------------------+
void CObjectProperties::TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                                ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_LABEL,0,0,0))//Create object label
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetInteger(chartID,name,OBJPROP_XDISTANCE,x_coord);//Set x Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_YDISTANCE,y_coord);//Set y Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_CORNER,Corner);//Set object's corner anchor
      ObjectSetString(chartID,name,OBJPROP_TEXT,text);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,SymbolBackground());//Set object's color
      ObjectSetInteger(chartID,name,OBJPROP_FONTSIZE,fontsize);//Set object's font-size
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

//+------------------------------------------------------------------+
//|Create Event object                                               |
//+------------------------------------------------------------------+
void CObjectProperties::EventObj(long chartID,string name,string description,datetime eventdate)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_EVENT,0,eventdate,0))//Create object event
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetString(chartID,name,OBJPROP_TEXT,description);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,clrBlack);//Set object's color
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }
//+------------------------------------------------------------------

La variable array Objects almacenará todos los objetos de gráfico creados en esta clase CObjectProperties.

struct ObjStruct
     {
      long           ChartId;
      string         Name;
     } Objects[];//ObjStruct variable array

La función AddObj añadirá la ID del gráfico y el nombre del objeto al array Objects.

   //-- Add chart object to Objects array
   void              AddObj(long chart_id,string name)
     {
      ArrayResize(Objects,Objects.Size()+1,Objects.Size()+2);
      Objects[Objects.Size()-1].ChartId=chart_id;
      Objects[Objects.Size()-1].Name=name;
     }

El propósito de la función Square es crear un objeto rectángulo con propiedades específicas para permitir su personalización. 

void CObjectProperties::Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor)
  {
   const int              sub_window=0;             // subwindow index
   const int              x=x_coord;                // X coordinate
   const int              y=y_coord;                // Y coordinate
   const color            back_clr=clrBlack;        // background color
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // border type
   const color            clr=clrRed;               // flat border color (Flat)
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // flat border style
   const int              line_width=0;             // flat border width
   const bool             back=false;               // in the background
   const bool             selection=false;          // highlight to move
   const bool             hidden=true;              // hidden in the object list

   ObjectDelete(chart_ID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0))//create rectangle object label
     {
      AddObj(chart_ID,name);//Add object to array
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);//Set x Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);//Set y Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);//Set object's width/x-size
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);//Set object's height/y-size
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);//Set object's background color
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);//Set object's border type
      ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,Anchor);//Set objects anchor point
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);//Set object's color
      ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);//Set object's style
      ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);//Set object's flat border width
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);//Set if object is in foreground or not
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);//Set if object is selectable/dragable
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);//Set if object is Selected
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);//Set if object is hidden in object list
      ChartRedraw(chart_ID);
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

La función TextObj creará objetos de texto en el gráfico. 

void CObjectProperties::TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                                ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_LABEL,0,0,0))//Create object label
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetInteger(chartID,name,OBJPROP_XDISTANCE,x_coord);//Set x Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_YDISTANCE,y_coord);//Set y Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_CORNER,Corner);//Set object's corner anchor
      ObjectSetString(chartID,name,OBJPROP_TEXT,text);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,SymbolBackground());//Set object's color
      ObjectSetInteger(chartID,name,OBJPROP_FONTSIZE,fontsize);//Set object's font-size
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

La función EventObj creará objetos de evento en el gráfico para mostrar los eventos económicos que se han producido o se producirán.

void CObjectProperties::EventObj(long chartID,string name,string description,datetime eventdate)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_EVENT,0,eventdate,0))//Create object event
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetString(chartID,name,OBJPROP_TEXT,description);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,clrBlack);//Set object's color
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }


Clase de noticias

Tablas del calendario en la parte 1:

Tablas de bases de datos en la parte 1

Base de datos del calendario, parte 1

En nuestra base de datos anterior en la parte 1, el tamaño del archivo era enorme y bastante lento para almacenar todos los datos de noticias en la base de datos. Esto se debe a que los datos de noticias se almacenan de manera ineficiente. El mayor factor que contribuye al tamaño del archivo y al rendimiento lento es el almacenamiento repetido de datos similares.

Las tablas:

  • Data_AU
  • Data_None
  • Data_UK
  • Data_US

Almacenar los mismos datos de noticias con una variación en los datos de tiempo.

Nuevo diseño:

Contenido del calendario en la parte 2:

Contenido de la base de datos

Base de datos del calendario, parte 2

En lugar de almacenar repetidamente los mismos datos de eventos de noticias con diferentes horas, crearemos una tabla única para almacenar todos los datos de eventos de noticias llamada MQL5Calendar. Y para almacenar los diferentes datos horarios entre cada horario de verano, tendremos otra tabla llamada TimeSchedule. Por lo tanto, este diseño reducirá el tamaño del archivo a más de la mitad de la base de datos del calendario anterior y aumentará el rendimiento.


En el nuevo diseño de la base de datos tendremos estos contenidos:

  • Tabla de AutoDST
  • Calendar_AU (vista)
  • Calendar_NONE (vista)
  • Calendar_UK (vista)
  • Calendar_US (vista)
  • Tabla de MQL5Calendar
  • Tabla de registros
  • Tabla TimeSchedule
  • Activador OnlyOne_AutoDST
  • Disparador OnlyOne_Record

Vamos a normalizar las tablas de la base de datos anterior, estas tablas son Data_AU, Data_None, Data_UK y Data_US.

¿Qué es la normalización de bases de datos?

La normalización de bases de datos es un proceso de diseño de bases de datos utilizado para organizar una base de datos en tablas y columnas con el fin de minimizar la redundancia y la dependencia. Los principales objetivos son eliminar los datos redundantes (por ejemplo, almacenar los mismos datos en más de una tabla) y garantizar que las dependencias de los datos tengan sentido (almacenar sólo datos relacionados en una tabla). Este proceso da como resultado un conjunto de tablas cuyo mantenimiento es más sencillo y reduce las posibilidades de que se produzcan anomalías en los datos.

UML

También crearemos triggers (disparadores/activadores) para asegurar que sólo se almacena un registro en las tablas AutoDST y Record. Además, crearemos vistas para cada horario DST para mostrar los eventos de noticias del último día actualizado, idealmente para navegar fácilmente por los eventos de noticias actualizados sin tener que ejecutar repetidamente consultas en las tablas con miles de entradas. 


Bien, entonces ¿qué es un disparador?

Un disparador en SQLite es un tipo especial de procedimiento almacenado que ejecuta automáticamente un conjunto especificado de acciones en respuesta a determinados eventos en una tabla concreta. Estos eventos pueden ser inserciones, actualizaciones o eliminaciones de filas en la tabla. 

¿Qué es una vista?

Una vista en SQLite es una tabla virtual que se basa en el conjunto de resultados de una consulta SELECT. A diferencia de una tabla, una vista no almacena datos físicamente. En cambio, proporciona una forma de presentar datos de una o más tablas en una estructura o formato específico, simplificando a menudo consultas complejas y mejorando la seguridad de los datos.

Antes de comenzar a crear nuevas tablas y agregarlas al ya gran tamaño de la base de datos. Necesitamos una forma de saber qué tablas eliminar y cuáles conservar. Una solución sencilla sería verificar cada tabla que sabemos que existe en nuestra base de datos anterior, que incluiría Data_AU y otras. Pero no podemos simplemente codificar qué tablas eliminar de nuestra memoria, nuestro programa necesita encontrar por sí solo las tablas que ya no necesitamos. Para hacer esto necesitamos verificar qué tablas existen en nuestra base de datos, iterar a través de las tablas que queremos eliminar y omitir aquellas que queremos conservar.

En SQLite hay una tabla llamada SQLITE_MASTER/SQLITE_SCHEMA que almacena los metadatos de la base de datos, incluida información sobre todos los objetos de la base de datos y SQL utilizado para definirlos. Es el catálogo de sistema más importante en SQLite. La siguiente consulta se utiliza para obtener toda la información de la base de datos.

SELECT * FROM SQLITE_MASTER;

 Salida de la base de datos:

type    name		    		tbl_name        rootpage        sql
table   Data_None       		Data_None       2       	CREATE TABLE Data_None(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_None_1    Data_None       3       
table   Data_US 			Data_US 	4       	CREATE TABLE Data_US(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_US_1      Data_US 	5       
table   Data_UK 			Data_UK 	6       	CREATE TABLE Data_UK(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_UK_1      Data_UK 	7       
table   Data_AU 			Data_AU 	8       	CREATE TABLE Data_AU(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_AU_1      Data_AU 	9       
table   Records 			Records 	38774   	CREATE TABLE Records(RECORDEDTIME INT NOT NULL)
table   AutoDST 			AutoDST 	38775   	CREATE TABLE AutoDST(DST STRING NOT NULL)

Como se ve en la salida de la base de datos, hay algo llamado index (índice), que no hemos creado antes.

¿Qué es un índice?

Un índice en SQLite es un objeto de base de datos que proporciona una forma de mejorar el rendimiento de las operaciones de consulta facilitando una recuperación más rápida de los registros de una tabla. Los índices son particularmente útiles para acelerar las búsquedas, la clasificación y las operaciones de unión al crear una estructura de datos ordenada (normalmente un árbol B) que permite al motor de base de datos localizar filas más rápidamente.

¿Por qué se creó el índice si no lo creamos previamente?

Cuando una tabla tiene una clave principal en SQLite, se crea automáticamente un índice para esa tabla en particular.

En nuestra salida de base de datos anterior, finalmente obtuvimos todos los objetos y metadatos en la base de datos, ahora podemos encontrar las tablas que ya no son necesarias y eliminarlas. Para ello crearemos un array de las tablas que necesitamos y las sentencias SQL que crean estas tablas, para poder compararlas con la de la salida de la base de datos y así eliminar las que no coincidan.

CNews tiene herencia multinivel de clases:

  • CCandleProperties
  • CChartProperties
  • CSymbolProperties

CNews tiene inclusiones de clases:

  • CDaylightSavings_UK
  • CDaylightSavings_US
  • CDaylightSavings_AU

CNews tiene una inclusión del fichero de cabecera CommonVariables.mqh.

CNews tiene herencia jerárquica de las clases:

  • CSymbolProperties
  • CSymbolInfo
  • CCandleProperties
  • CTimeManagement
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "CommonVariables.mqh"
#include "DayLightSavings/DaylightSavings_UK.mqh"
#include "DayLightSavings/DaylightSavings_US.mqh"
#include "DayLightSavings/DaylightSavings_AU.mqh"
#include "CandleProperties.mqh"

//+------------------------------------------------------------------+
//|News class                                                        |
//+------------------------------------------------------------------+
class CNews : private CCandleProperties
  {
   //Private Declarations Only accessable by this class/header file
private:

   //-- To keep track of what is in our database
   enum CalendarComponents
     {
      AutoDST_Table,//AutoDST Table
      CalendarAU_View,//View for DST_AU
      CalendarNONE_View,//View for DST_NONE
      CalendarUK_View,//View for DST_UK
      CalendarUS_View,//View for DST_US
      Record_Table,// Record Table
      TimeSchedule_Table,//TimeSchedule Table
      MQL5Calendar_Table,//MQL5Calendar Table
      AutoDST_Trigger,//Table Trigger for AutoDST
      Record_Trigger//Table Trigger for Record
     };

   //-- structure to retrieve all the objects in the database
   struct SQLiteMaster
     {
      string         type;//will store object's type
      string         name;//will store object's name
      string         tbl_name;//will store table name
      int            rootpage;//will store rootpage
      string         sql;//Will store the sql create statement
     } DBContents[];//Array of type SQLiteMaster

   //--  MQL5CalendarContents inherits from SQLiteMaster structure
   struct MQL5CalendarContents:SQLiteMaster
     {
      CalendarComponents  Content;
      string         insert;//Will store the sql insert statement
     } CalendarContents[10];//Array to Store objects in our database

   CTimeManagement   Time;//TimeManagement Object declaration
   CDaylightSavings_UK  Savings_UK;//DaylightSavings Object for the UK and EU
   CDaylightSavings_US  Savings_US;//DaylightSavings Object for the US
   CDaylightSavings_AU  Savings_AU;//DaylightSavings Object for the AU

   bool              AutoDetectDST(DST_type &dstType);//Function will determine Broker DST
   DST_type          DSTType;//variable of DST_type enumeration declared in the CommonVariables class/header file
   bool              InsertIntoTables(int db,Calendar &Evalues[]);//Function for inserting Economic Data in to a database's table
   void              CreateAutoDST(int db);//Function for creating and inserting Recommend DST for the Broker into a table
   bool              CreateCalendarTable(int db,bool &tableExists);//Function for creating a table in a database
   bool              CreateTimeTable(int db,bool &tableExists);//Function for creating a table in a database
   void              CreateCalendarViews(int db);//Function for creating a view in a database
   void              CreateRecordTable(int db);//Creates a table to store the record of when last the Calendar database was updated/created
   bool              UpdateRecords();//Checks if the main Calendar database needs an update or not
   void              EconomicDetails(Calendar &NewsTime[]);//Gets values from the MQL5 economic Calendar
   string            DropRequest;//Variable for dropping tables in the database

   //-- Function for retrieving the MQL5CalendarContents structure for the enumartion type CalendarComponents
   MQL5CalendarContents CalendarStruct(CalendarComponents Content)
     {
      MQL5CalendarContents Calendar;
      for(uint i=0;i<CalendarContents.Size();i++)
        {
         if(CalendarContents[i].Content==Content)
           {
            return CalendarContents[i];
           }
        }
      return Calendar;
     }

   //Public declarations accessable via a class's Object
public:
                     CNews(void);
                    ~CNews(void);//Deletes a text file created when the Calendar database is being worked on
   void              CreateEconomicDatabase();//Creates the Calendar database for a specific Broker
   datetime          GetLatestNewsDate();//Gets the lastest/newest date in the Calendar database
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CNews::CNews(void):DropRequest("PRAGMA foreign_keys = OFF; "
                                  "PRAGMA secure_delete = ON; "
                                  "Drop %s IF EXISTS %s; "
                                  "Vacuum; "
                                  "PRAGMA foreign_keys = ON;")//Sql drop statement
  {
//-- initializing properties for the AutoDST table
   CalendarContents[0].Content = AutoDST_Table;
   CalendarContents[0].name = "AutoDST";
   CalendarContents[0].sql = "CREATE TABLE AutoDST(DST TEXT NOT NULL DEFAULT 'DST_NONE')STRICT;";
   CalendarContents[0].tbl_name = "AutoDST";
   CalendarContents[0].type = "table";
   CalendarContents[0].insert = "INSERT INTO 'AutoDST'(DST) VALUES ('%s');";

   string views[] = {"UK","US","AU","NONE"};
   string view_sql = "CREATE VIEW IF NOT EXISTS Calendar_%s "
                     "AS "
                     "SELECT C.Eventid,C.Eventname,C.Country,T.DST_%s as Time,C.EventCurrency,C.Eventcode from MQL5Calendar C,Record R "
                     "Inner join TimeSchedule T on C.ID=T.ID "
                     "Where DATE(REPLACE(T.DST_%s,'.','-'))=R.Date "
                     "Order by T.DST_%s Asc;";

//-- Sql statements for creating the table views
   for(uint i=1;i<=views.Size();i++)
     {
      CalendarContents[i].Content = (CalendarComponents)i;
      CalendarContents[i].name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].sql = StringFormat(view_sql,views[i-1],views[i-1],views[i-1],views[i-1]);
      CalendarContents[i].tbl_name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].type = "view";
     }

//-- initializing properties for the Record table
   CalendarContents[5].Content = Record_Table;
   CalendarContents[5].name = "Record";
   CalendarContents[5].sql = "CREATE TABLE Record(Date TEXT NOT NULL)STRICT;";
   CalendarContents[5].tbl_name="Record";
   CalendarContents[5].type = "table";
   CalendarContents[5].insert = "INSERT INTO 'Record'(Date) VALUES (Date(REPLACE('%s','.','-')));";

//-- initializing properties for the TimeSchedule table
   CalendarContents[6].Content = TimeSchedule_Table;
   CalendarContents[6].name = "TimeSchedule";
   CalendarContents[6].sql = "CREATE TABLE TimeSchedule(ID INT NOT NULL,DST_UK   TEXT   NOT NULL,DST_US   TEXT   NOT NULL,"
                             "DST_AU   TEXT   NOT NULL,DST_NONE   TEXT   NOT NULL,FOREIGN KEY (ID) REFERENCES MQL5Calendar (ID))STRICT;";
   CalendarContents[6].tbl_name="TimeSchedule";
   CalendarContents[6].type = "table";
   CalendarContents[6].insert = "INSERT INTO 'TimeSchedule'(ID,DST_UK,DST_US,DST_AU,DST_NONE) "
                                "VALUES (%d,'%s','%s', '%s', '%s');";

//-- initializing properties for the MQL5Calendar table
   CalendarContents[7].Content = MQL5Calendar_Table;
   CalendarContents[7].name = "MQL5Calendar";
   CalendarContents[7].sql = "CREATE TABLE MQL5Calendar(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  TEXT   NOT NULL,"
                             "EVENTNAME   TEXT   NOT NULL,EVENTTYPE   TEXT   NOT NULL,EVENTIMPORTANCE   TEXT   NOT NULL,"
                             "EVENTCURRENCY  TEXT   NOT NULL,EVENTCODE   TEXT   NOT NULL,EVENTSECTOR TEXT   NOT NULL,"
                             "EVENTFORECAST  TEXT   NOT NULL,EVENTPREVALUE  TEXT   NOT NULL,EVENTIMPACT TEXT   NOT NULL,"
                             "EVENTFREQUENCY TEXT   NOT NULL,PRIMARY KEY(ID))STRICT;";
   CalendarContents[7].tbl_name="MQL5Calendar";
   CalendarContents[7].type = "table";
   CalendarContents[7].insert = "INSERT INTO 'MQL5Calendar'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTCURRENCY,EVENTCODE,"
                                "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY) "
                                "VALUES (%d,%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');";

//-- Sql statement for creating the AutoDST table's trigger
   CalendarContents[8].Content = AutoDST_Trigger;
   CalendarContents[8].name = "OnlyOne_AutoDST";
   CalendarContents[8].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_AutoDST "
                             "BEFORE INSERT ON AutoDST "
                             "BEGIN "
                             "Delete from AutoDST; "
                             "END;";
   CalendarContents[8].tbl_name="AutoDST";
   CalendarContents[8].type = "trigger";

//-- Sql statement for creating the Record table's trigger
   CalendarContents[9].Content = Record_Trigger;
   CalendarContents[9].name = "OnlyOne_Record";
   CalendarContents[9].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_Record "
                             "BEFORE INSERT ON Record "
                             "BEGIN "
                             "Delete from Record; "
                             "END;";
   CalendarContents[9].tbl_name="Record";
   CalendarContents[9].type = "trigger";
  }

//+------------------------------------------------------------------+
//|Destructor                                                        |
//+------------------------------------------------------------------+
CNews::~CNews(void)
  {
   if(FileIsExist(NEWS_TEXT_FILE,FILE_COMMON))//Check if the news database open text file exists
     {
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);
     }
  }

//+------------------------------------------------------------------+
//|Gets values from the MQL5 economic Calendar                       |
//+------------------------------------------------------------------+
void CNews::EconomicDetails(Calendar &NewsTime[])
  {
   int Size=0;//to keep track of the size of the events in the NewsTime array
   MqlCalendarCountry countries[];
   string Country_code="";

   for(int i=0,count=CalendarCountries(countries); i<count; i++)
     {
      MqlCalendarValue values[];
      datetime date_from=0;//Get date from the beginning
      datetime date_to=(datetime)(Time.MonthsS()+iTime(Symbol(),PERIOD_D1,0));//Date of the next month from the current day
      if(CalendarValueHistory(values,date_from,date_to,countries[i].code))
        {
         for(int x=0; x<(int)ArraySize(values); x++)
           {
            MqlCalendarEvent event;
            ulong event_id=values[x].event_id;//Get the event id
            if(CalendarEventById(event_id,event))
              {
               ArrayResize(NewsTime,Size+1,Size+2);//Readjust the size of the array to +1 of the array size
               StringReplace(event.name,"'","");//Removing or replacing single quotes(') from event name with an empty string
               NewsTime[Size].CountryName = countries[i].name;//storing the country's name from the specific event
               NewsTime[Size].EventName = event.name;//storing the event's name
               NewsTime[Size].EventType = EnumToString(event.type);//storing the event type from (ENUM_CALENDAR_EVENT_TYPE) to a string
               //-- storing the event importance from (ENUM_CALENDAR_EVENT_IMPORTANCE) to a string
               NewsTime[Size].EventImportance = EnumToString(event.importance);
               NewsTime[Size].EventId = event.id;//storing the event id
               NewsTime[Size].EventDate = TimeToString(values[x].time);//storing normal event time
               NewsTime[Size].EventCurrency = countries[i].currency;//storing event currency
               NewsTime[Size].EventCode = countries[i].code;//storing event code
               NewsTime[Size].EventSector = EnumToString(event.sector);//storing event sector from (ENUM_CALENDAR_EVENT_SECTOR) to a string
               if(values[x].HasForecastValue())//Checks if the event has a forecast value
                 {
                  NewsTime[Size].EventForecast = (string)values[x].forecast_value;//storing the forecast value into a string
                 }
               else
                 {
                  NewsTime[Size].EventForecast = "None";//storing 'None' as the forecast value
                 }

               if(values[x].HasPreviousValue())//Checks if the event has a previous value
                 {
                  NewsTime[Size].EventPreval = (string)values[x].prev_value;//storing the previous value into a string
                 }
               else
                 {
                  NewsTime[Size].EventPreval = "None";//storing 'None' as the previous value
                 }
               //-- storing the event impact from (ENUM_CALENDAR_EVENT_IMPACT) to a string
               NewsTime[Size].EventImpact =  EnumToString(values[x].impact_type);
               //-- storing the event frequency from (ENUM_CALENDAR_EVENT_FREQUENCY) to a string
               NewsTime[Size].EventFrequency =  EnumToString(event.frequency);
               Size++;//incrementing the Calendar array NewsTime
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//|Checks if the main Calendar database needs an update or not       |
//+------------------------------------------------------------------+
bool CNews::UpdateRecords()
  {
//initialize variable to true
   bool perform_update=true;
//--- open/create
//-- try to open database Calendar
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)//Checks if the database was able to be opened
     {
      //if opening the database failed
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if the database Calendar exists in the common folder
        {
         return perform_update;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder
        }
     }

   int MasterRequest = DatabasePrepare(db,"select * from sqlite_master where type<>'index';");
   if(MasterRequest==INVALID_HANDLE)
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
     }
   else
     {
      SQLiteMaster ReadContents;
      //Assigning values from the sql query into DBContents array
      for(int i=0; DatabaseReadBind(MasterRequest,ReadContents); i++)
        {
         ArrayResize(DBContents,i+1,i+2);
         DBContents[i].type = ReadContents.type;
         DBContents[i].name = ReadContents.name;
         DBContents[i].tbl_name = ReadContents.tbl_name;
         DBContents[i].rootpage = ReadContents.rootpage;
         /*Check if the end of the sql string has a character ';' if not add this character to the string*/
         DBContents[i].sql = (StringFind(ReadContents.sql,";",StringLen(ReadContents.sql)-1)==
                              (StringLen(ReadContents.sql)-1))?ReadContents.sql:ReadContents.sql+";";;
        }

      uint contents_exists = 0;
      for(uint i=0;i<DBContents.Size();i++)
        {
         bool isCalendarContents = false;
         for(uint x=0;x<CalendarContents.Size();x++)
           {
            /*Store Sql query from CalendarContents without string ' IF NOT EXISTS'*/
            string CalendarSql=CalendarContents[x].sql;
            StringReplace(CalendarSql," IF NOT EXISTS","");
            //-- Check if the Db object is in our list
            if(DBContents[i].name==CalendarContents[x].name&&
               (DBContents[i].sql==CalendarSql||
                DBContents[i].sql==CalendarContents[x].sql)&&
               CalendarContents[x].type==DBContents[i].type&&
               CalendarContents[x].tbl_name==DBContents[i].tbl_name)
              {
               contents_exists++;
               isCalendarContents = true;
              }
           }
         if(!isCalendarContents)
           {
            //-- Print DBcontent's name if it does not match with CalendarContents
            PrintFormat("DBContent: %s is not needed!",DBContents[i].name);
            //-- We will drop the table if it is not neccessary
            DatabaseExecute(db,StringFormat(DropRequest,DBContents[i].type,DBContents[i].name));
            Print("Attempting To Clean Database...");
           }
        }
      /*If not all the CalendarContents exist in the Calendar Database before an update */
      if(contents_exists!=CalendarContents.Size())
        {
         return perform_update;
        }
     }
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//If the database table 'Record' doesn't exist
     {
      DatabaseClose(db);
      return perform_update;
     }

//-- Sql query to determine the lastest or maximum date recorded
   /* If the last recorded date data in the 'Record' table is not equal to the current day, perform an update! */
   string request_text=StringFormat("SELECT Date FROM %s where Date=Date(REPLACE('%s','.','-'))",
                                    CalendarStruct(Record_Table).name,TimeToString(TimeTradeServer()));
   int request=DatabasePrepare(db,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
   if(request==INVALID_HANDLE)//Checks if the request failed to be completed
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return perform_update;
     }

   if(DatabaseRead(request))//Will be true if there are results from the sql query/request
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      perform_update=false;
      return perform_update;
     }
   else
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      return perform_update;
     }
  }

//+------------------------------------------------------------------+
//|Creates the Calendar database for a specific Broker               |
//+------------------------------------------------------------------+
void CNews::CreateEconomicDatabase()
  {
   if(FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Check if the database exists
     {
      if(!UpdateRecords())//Check if the database is up to date
        {
         return;//will terminate execution of the rest of the code below
        }
     }
   if(FileIsExist(NEWS_TEXT_FILE,FILE_COMMON))//Check if the database is open
     {
      return;//will terminate execution of the rest of the code below
     }

   Calendar Evalues[];//Creating a Calendar array variable
   bool failed=false,tableExists=false;
   int file=INVALID_HANDLE;
//--- open/create the database 'Calendar'
//-- will try to open/create in the common folder
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)//Checks if the database 'Calendar' failed to open/create
     {
      Print("DB: ",NEWS_DATABASE_FILE, " open failed with code ", GetLastError());
      return;//will terminate execution of the rest of the code below
     }
   else
     {
      //-- try to create a text file 'NewsDatabaseOpen' in common folder
      file=FileOpen(NEWS_TEXT_FILE,FILE_WRITE|FILE_ANSI|FILE_TXT|FILE_COMMON);
      if(file==INVALID_HANDLE)
        {
         DatabaseClose(db);//Closes the database 'Calendar' if the News text file failed to be created
         return;//will terminate execution of the rest of the code below
        }
     }

   DatabaseTransactionBegin(db);//Starts transaction execution
   Print("Please wait...");

//-- attempt to create the MQL5Calendar and TimeSchedule tables
   if(!CreateCalendarTable(db,tableExists)||!CreateTimeTable(db,tableExists))
     {
      FileClose(file);//Closing the file 'NewsDatabaseOpen.txt'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Deleting the file 'NewsDatabaseOpen.txt'
      return;//will terminate execution of the rest of the code below
     }

   EconomicDetails(Evalues);//Retrieving the data from the Economic Calendar
   if(tableExists)//Checks if there is an existing table within the Calendar Database
     {
      //if there is an existing table we will notify the user that we are updating the table.
      PrintFormat("Updating %s",NEWS_DATABASE_FILE);
     }
   else
     {
      //if there isn't an existing table we will notify the user that we about to create one
      PrintFormat("Creating %s",NEWS_DATABASE_FILE);
     }

//-- attempt to insert economic event data into the calendar tables
   if(!InsertIntoTables(db,Evalues))
     {
      //-- Will assign true if inserting economic vaules failed in the MQL5Calendar and TimeSchedule tables
      failed=true;
     }

   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
      FileClose(file);//Close the text file 'NEWS_TEXT_FILE'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Delete the text file, as we are reverted/rolled-back the database
      ArrayRemove(Evalues,0,WHOLE_ARRAY);//Removes the values in the array
     }
   else
     {
      CreateCalendarViews(db);
      CreateRecordTable(db);//Will create the 'Record' table and insert the  current time
      CreateAutoDST(db);//Will create the 'AutoDST' table and insert the broker's DST schedule
      FileClose(file);//Close the text file 'NEWS_TEXT_FILE'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Delete the text file, as we are about to close the database
      ArrayRemove(Evalues,0,WHOLE_ARRAY);//Removes the values in the array
      if(tableExists)
        {
         //Let the user/trader know that the database was updated
         PrintFormat("%s Updated",NEWS_DATABASE_FILE);
        }
      else
        {
         //Let the user/trader know that the database was created
         PrintFormat("%s Created",NEWS_DATABASE_FILE);
        }
     }
//--- all transactions have been performed successfully - record changes and unlock the database
   DatabaseTransactionCommit(db);
   DatabaseClose(db);//Close the database
  }


//+------------------------------------------------------------------+
//|Function for creating a table in a database                       |
//+------------------------------------------------------------------+
bool CNews::CreateCalendarTable(int db,bool &tableExists)
  {
//-- Checks if a table 'MQL5Calendar' exists
   if(DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      tableExists=true;//Assigns true to tableExists variable
      //-- Checks if a table 'TimeSchedule' exists in the database 'Calendar'
      if(DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
        {
         //-- We will drop the table if the table already exists
         if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(TimeSchedule_Table).name)))
           {
            //If the table failed to be dropped/deleted
            PrintFormat("Failed to drop table %s with code %d",CalendarStruct(TimeSchedule_Table).name,GetLastError());
            DatabaseClose(db);//Close the database
            return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
           }
        }
      //--We will drop the table if the table already exists
      if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(MQL5Calendar_Table).name)))
        {
         //If the table failed to be dropped/deleted
         PrintFormat("Failed to drop table %s with code %d",CalendarStruct(MQL5Calendar_Table).name,GetLastError());
         DatabaseClose(db);//Close the database
         return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
        }
     }
//-- If the database table 'MQL5Calendar' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      //--- create the table 'MQL5Calendar'
      if(!DatabaseExecute(db,CalendarStruct(MQL5Calendar_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

//+------------------------------------------------------------------+
//|Function for creating a table in a database                       |
//+------------------------------------------------------------------+
bool CNews::CreateTimeTable(int db,bool &tableExists)
  {
//-- If the database table 'TimeSchedule' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
     {
      //--- create the table 'TimeSchedule'
      if(!DatabaseExecute(db,CalendarStruct(TimeSchedule_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

//+------------------------------------------------------------------+
//|Function for creating views in a database                         |
//+------------------------------------------------------------------+
void CNews::CreateCalendarViews(int db)
  {
   for(uint i=1;i<=4;i++)
     {
      if(!DatabaseExecute(db,CalendarStruct((CalendarComponents)i).sql))//Checks if the view was successfully created
        {
         Print("DB: create the Calendar view failed with code ", GetLastError());
        }
     }
  }

//+------------------------------------------------------------------+
//|Function for inserting Economic Data in to a database's table     |
//+------------------------------------------------------------------+
bool CNews::InsertIntoTables(int db,Calendar &Evalues[])
  {
   for(uint i=0; i<Evalues.Size(); i++)//Looping through all the Economic Events
     {
      string request_insert_into_calendar =
         StringFormat(CalendarStruct(MQL5Calendar_Table).insert,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Evalues[i].EventCurrency,
                      Evalues[i].EventCode,
                      Evalues[i].EventSector,
                      Evalues[i].EventForecast,
                      Evalues[i].EventPreval,
                      Evalues[i].EventImpact,
                      Evalues[i].EventFrequency);//Inserting all the columns for each event record
      if(DatabaseExecute(db,request_insert_into_calendar))//Check if insert query into calendar was successful
        {
         string request_insert_into_time =
            StringFormat(CalendarStruct(TimeSchedule_Table).insert,
                         i,
                         //-- Economic EventDate adjusted for UK DST(Daylight Savings Time)
                         Savings_UK.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for US DST(Daylight Savings Time)
                         Savings_US.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for AU DST(Daylight Savings Time)
                         Savings_AU.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         Evalues[i].EventDate//normal Economic EventDate
                        );//Inserting all the columns for each event record
         if(!DatabaseExecute(db,request_insert_into_time))
           {
            Print(GetLastError());
            //-- Will print the sql query to check for any errors or possible defaults in the query/request
            Print(request_insert_into_time);
            return false;//Will end the loop and return false, as values failed to be inserted into the table
           }
        }
      else
        {
         Print(GetLastError());
         //-- Will print the sql query to check for any errors or possible defaults in the query/request
         Print(request_insert_into_calendar);
         return false;//Will end the loop and return false, as values failed to be inserted into the table
        }
     }
   return true;//Will return true, all values were inserted into the table successfully
  }

//+------------------------------------------------------------------+
//|Creates a table to store the record of when last the Calendar     |
//|database was updated/created                                      |
//+------------------------------------------------------------------+
void CNews::CreateRecordTable(int db)
  {
   bool failed=false;
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//Checks if the table 'Record' exists in the databse 'Calendar'
     {
      //--- create the table
      if(!DatabaseExecute(db,CalendarStruct(Record_Table).sql))//Will attempt to create the table 'Record'
        {
         Print("DB: create the Records table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
        }
     }
   else
     {
      DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
     }
//Sql query/request to insert the current time into the 'Date' column in the table 'Record'
   string request_text=StringFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }

//+------------------------------------------------------------------+
//|Function for creating and inserting Recommend DST for the Broker  |
//|into a table                                                      |
//+------------------------------------------------------------------+
void CNews::CreateAutoDST(int db)
  {
   bool failed=false;//boolean variable
   if(!AutoDetectDST(DSTType))//Check if AutoDetectDST went through all the right procedures
     {
      return;//will terminate execution of the rest of the code below
     }

   if(!DatabaseTableExists(db,CalendarStruct(AutoDST_Table).name))//Checks if the table 'AutoDST' exists in the databse 'Calendar'
     {
      //--- create the table AutoDST
      if(!DatabaseExecute(db,CalendarStruct(AutoDST_Table).sql))//Will attempt to create the table 'AutoDST'
        {
         Print("DB: create the AutoDST table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
        }
     }
   else
     {
      //Create trigger if AutoDST table exists
      DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
     }
//Sql query/request to insert the recommend DST for the Broker using the DSTType variable to determine which string data to insert
   string request_text=StringFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));//Will print the sql query if failed
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }

//+------------------------------------------------------------------+
//|Gets the latest/newest date in the Calendar database              |
//+------------------------------------------------------------------+
datetime CNews::GetLatestNewsDate()
  {
//--- open the database 'Calendar' in the common folder
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READONLY|DATABASE_OPEN_COMMON);

   if(db==INVALID_HANDLE)//Checks if 'Calendar' failed to be opened
     {
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if 'Calendar' database exists
        {
         Print("Could not find Database!");
         return 0;//Will return the earliest date which is 1970.01.01 00:00:00
        }
     }
   string latest_record="1970.01.01";//string variable with the first/earliest possible date in MQL5
//Sql query to determine the lastest or maximum recorded time from which the database was updated.
   string request_text="SELECT REPLACE(Date,'-','.') FROM 'Record'";
   int request=DatabasePrepare(db,request_text);
   if(request==INVALID_HANDLE)
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);//Close Database
      return 0;
     }
   if(DatabaseRead(request))//Will read the one record in the 'Record' table
     {
      //-- Will assign the first column(column 0) value to the variable 'latest_record'
      if(!DatabaseColumnText(request,0,latest_record))
        {
         Print("DatabaseRead() failed with code ", GetLastError());
         DatabaseFinalize(request);//Finalize request
         DatabaseClose(db);//Closes the database 'Calendar'
         return D'1970.01.01';//Will end the for loop and will return the earliest date which is 1970.01.01 00:00:00
        }
     }
   DatabaseFinalize(request);
   DatabaseClose(db);//Closes the database 'Calendar'
   return (datetime)latest_record;//Returns the string latest_record converted to datetime
  }

//+------------------------------------------------------------------+
//|Function will determine Broker DST                                |
//+------------------------------------------------------------------+
bool CNews::AutoDetectDST(DST_type &dstType)
  {
   MqlCalendarValue values[];//Single array of MqlCalendarValue type
   string eventtime[];//Single string array variable to store NFP(Nonfarm Payrolls) dates for the 'United States' from the previous year
//-- Will store the previous year into an integer
   int lastyear = Time.ReturnYear(Time.TimeMinusOffset(iTime(Symbol(),PERIOD_CURRENT,0),Time.YearsS()));
//-- Will store the start date for the previous year
   datetime lastyearstart = StringToTime(StringFormat("%s.01.01 00:00:00",(string)lastyear));
//-- Will store the end date for the previous year
   datetime lastyearend = StringToTime(StringFormat("%s.12.31 23:59:59",(string)lastyear));
//-- Getting last year's calendar values for CountryCode = 'US'
   if(CalendarValueHistory(values,lastyearstart,lastyearend,"US"))
     {
      for(int x=0; x<(int)ArraySize(values); x++)
        {
         if(values[x].event_id==840030016)//Get only NFP Event Dates
           {
            ArrayResize(eventtime,eventtime.Size()+1,eventtime.Size()+2);//Increasing the size of eventtime array by 1
            eventtime[eventtime.Size()-1] = TimeToString(values[x].time);//Storing the dates in an array of type string
           }
        }
     }
//-- datetime variables to store the broker's timezone shift(change)
   datetime ShiftStart=D'1970.01.01 00:00:00',ShiftEnd=D'1970.01.01 00:00:00';
   string   EURUSD="";//String variables declarations for working with EURUSD
   bool     EurusdIsFound=false;//Boolean variables declarations for working with EURUSD
   for(int i=0;i<SymbolsTotal(true);i++)//Will loop through all the Symbols inside the Market Watch
     {
      string SymName = SymbolName(i,true);//Assign the Symbol Name of index 'i' from the list of Symbols inside the Market Watch
      //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
      //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
      if(((CurrencyBase(SymName)=="EUR"&&CurrencyProfit(SymName)=="USD")||
          (StringFind(SymName,"EUR")>-1&&CurrencyProfit(SymName)=="USD"))&&!Custom(SymName))
        {
         EURUSD = SymName;//Assigning the name of the EURUSD Symbol found inside the Market Watch
         EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
         break;//Will end the for loop
        }
     }
   if(!EurusdIsFound)//Check if EURUSD Symbol was already Found in the Market Watch
     {
      for(int i=0; i<SymbolsTotal(false); i++)//Will loop through all the available Symbols outside the Market Watch
        {
         string SymName = SymbolName(i,false);//Assign the Symbol Name of index 'i' from the list of Symbols outside the Market Watch
         //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
         //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
         if(((CurrencyBase(SymName)=="EUR"&&CurrencyProfit(SymName)=="USD")||
             (StringFind(SymName,"EUR")>-1&&CurrencyProfit(SymName)=="USD"))&&!Custom(SymName))
           {
            EURUSD = SymName;//Assigning the name of the EURUSD Symbol found outside the Market Watch
            EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
            break;//Will end the for loop
           }
        }
     }
   if(!EurusdIsFound)//Check if EURUSD Symbol was Found in the Trading Terminal for your Broker
     {
      Print("Cannot Find EURUSD!");
      Print("Cannot Create Database!");
      Print("Server DST Cannot be Detected!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   struct DST
     {
      bool           result;
      datetime       date;
     } previousresult,currentresult;

   bool timeIsShifted;//Boolean variable declaration will be used to determine if the broker changes it's timezone
   for(uint i=0;i<eventtime.Size();i++)
     {
      //-- Store the result of if the eventdate is the larger candlestick
      currentresult.result = IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD);
      currentresult.date = (datetime)eventtime[i];//Store the eventdate from eventtime[i]
      //-- Check if there is a difference between the previous result and the current result
      timeIsShifted = ((currentresult.result!=previousresult.result&&i>0)?true:false);

      //-- Check if the Larger candle has shifted from the previous event date to the current event date in eventtime[i] array
      if(timeIsShifted)
        {
         if(ShiftStart==D'1970.01.01 00:00:00')//Check if the ShiftStart variable has not been assigned a relevant value yet
           {
            ShiftStart=currentresult.date;//Store the eventdate for when the timeshift began
           }
         ShiftEnd=previousresult.date;//Store the eventdate timeshift
        }
      previousresult.result = currentresult.result;//Store the previous result of if the eventdate is the larger candlestick
      previousresult.date = currentresult.date;//Store the eventdate from eventtime[i]
     }
//-- Check if the ShiftStart variable has not been assigned a relevant value and the eventdates are more than zero
   if(ShiftStart==D'1970.01.01 00:00:00'&&eventtime.Size()>0)
     {
      Print("Broker ServerTime unchanged!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return true;//Returning True, Broker's DST schedule was found successfully
     }

   datetime DaylightStart,DaylightEnd;//Datetime variables declarations for start and end dates for DaylightSavings
   if(Savings_AU.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For AU DST");
         dstType = DST_AU;//Assigning enumeration value AU_DST, Broker has AU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For AU");
      Print("Year: %d Cannot Be Found!",lastyear);
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_UK.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For UK DST");
         dstType = DST_UK;//Assigning enumeration value UK_DST, Broker has UK/EU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For UK");
      Print("Year: %d Cannot Be Found!",lastyear);
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_US.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For US DST");
         dstType = DST_US;//Assigning enumeration value US_DST, Broker has US DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For US");
      Print("Year: %d Cannot Be Found!",lastyear);
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }
   Print("Cannot Detect Broker ServerTime Configuration!");
   dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
   return false;//Returning False, Broker's DST schedule was not found
  }
//+------------------------------------------------------------------+

Daremos a cada componente de nuestro diseño de base de datos deseado un valor de enumeración, como una forma de identidad.

 enum CalendarComponents
     {
      AutoDST_Table,//AutoDST Table
      CalendarAU_View,//View for DST_AU
      CalendarNONE_View,//View for DST_NONE
      CalendarUK_View,//View for DST_UK
      CalendarUS_View,//View for DST_US
      Record_Table,// Record Table
      TimeSchedule_Table,//TimeSchedule Table
      MQL5Calendar_Table,//MQL5Calendar Table
      AutoDST_Trigger,//Table Trigger for AutoDST
      Record_Trigger//Table Trigger for Record
     };

El propósito de la estructura SQLiteMaster es almacenar las propiedades del objeto de base de datos actual, como tipo, nombre, etc. Así podemos hacer un seguimiento de todos los objetos en la array DBContents.

//-- structure to retrieve all the objects in the database
   struct SQLiteMaster
     {
      string         type;//will store object type
      string         name;//will store object's name
      string         tbl_name;//will store table name
      int            rootpage;//will store rootpage
      string         sql;//Will store the sql create statement
     } DBContents[];//Array of type SQLiteMaster

En la estructura MQL5CalendarContents, almacenaremos unas propiedades adicionales que son las variables Content e insert.

Nuestra variable string insert, almacenará las sentencias SQL de inserción de nuestros objetos SQL. Mientras que la variable de CalendarComponents Content almacenará el valor de enumeración de nuestro objeto SQL como de identidad, de forma que podamos saber qué objeto SQL es cuál, una vez que todas las propiedades del objeto estén almacenadas en la array de la estructura CalendarContents.

//--  MQL5CalendarContents inherits from SQLiteMaster structure
   struct MQL5CalendarContents:SQLiteMaster
     {
      CalendarComponents  Content;
      string         insert;//Will store the sql insert statement
     } CalendarContents[10];//Array to Store objects in our database

La función CalendarStruct devolverá el valor de la estructura MQL5CalendarContents, cuando el parámetro Content sea igual al valor de enumeración de la variable Content de la estructura de array CalendarContents.

//-- Function for retrieving the MQL5CalendarContents structure for the enumartion type CalendarComponents
   MQL5CalendarContents CalendarStruct(CalendarComponents Content)
     {
      MQL5CalendarContents Calendar;
      for(uint i=0;i<CalendarContents.Size();i++)
        {
         if(CalendarContents[i].Content==Content)
           {
            return CalendarContents[i];
           }
        }
      return Calendar;
     }

La variable string DropRequest será responsable de eliminar los objetos de la base de datos que ya no necesitamos o queremos. En la consulta SQL utilizamos declaraciones PRAGMA.

¿Qué es una declaración PRAGMA?

La declaración PRAGMA en SQLite es un comando especial utilizado para modificar el funcionamiento de la biblioteca SQLite o para consultar el estado interno del motor de la base de datos. Los PRAGMA no son parte del SQL estándar sino que son específicos de SQLite. Proporcionan una forma de controlar diversas configuraciones ambientales y comportamientos de bases de datos.


¿Cuáles son los propósitos de las declaraciones PRAGMA?

  • Configuración: Las declaraciones PRAGMA le permiten configurar el entorno de la base de datos, como habilitar o deshabilitar restricciones de clave externa, configurar el modo de diario o ajustar los parámetros de uso de memoria.
  • Diagnósticos: Se pueden utilizar para recuperar información sobre la base de datos, como comprobar la integridad de la base de datos, obtener la configuración actual o ver el estado del motor SQLite.
  • Optimización: Los PRAGMA ayudan a optimizar el rendimiento de la base de datos ajustando parámetros como el tamaño de la caché, el modo de bloqueo y las configuraciones sincrónicas.
  • Mantenimiento: Son útiles para tareas de mantenimiento como reconstruir índices, analizar tablas y administrar la configuración de vaciado automático.

En nuestra primera declaración PRAGMA deshabilitamos cualquier restricción de clave externa que nos impida eliminar cualquier tabla con restricciones de clave externa.

En nuestra segunda declaración PRAGMA habilitamos secure_delete, que controla si el contenido eliminado se pone en cero o no antes de eliminarse del archivo de base de datos. En este caso, la base de datos sobrescribirá el contenido borrado con ceros sólo si al hacerlo no aumenta la cantidad de E/S (entradas/salidas).

En nuestra tercera declaración eliminaremos el objeto SQL si existe. A continuación, utilizaremos el comando Vacuum, que reconstruirá el archivo de base de datos, reempaquetándolo en una cantidad mínima de espacio en disco. Este proceso puede ayudar a optimizar el rendimiento de la base de datos y recuperar espacio no utilizado.

Finalmente volveremos a habilitar las restricciones de clave externa.

CNews::CNews(void):DropRequest("PRAGMA foreign_keys = OFF; "
                                  "PRAGMA secure_delete = ON; "
                                  "Drop %s IF EXISTS %s; "
                                  "Vacuum; "
                                  "PRAGMA foreign_keys = ON;")//Sql drop statement

Almacenaremos las propiedades de nuestra tabla AutoDST en el primer índice de la matriz CalendarContents. Aquí en la variable Content asignamos un valor de enumeración de AutoDST_Table.

Luego asignamos las declaraciónes nombre, nombre de la tabla, tipo e insertar. En la sentencia SQL para crear la tabla, le damos a la columna 'DST' un valor por defecto de 'DST_NONE' y terminamos la sentencia con la palabra clave 'STRICT'.

La palabra clave STRICT, impone que los datos insertados en una columna deben coincidir con el tipo declarado de esa columna. Esto proporciona más previsibilidad y coherencia en el tipo de datos almacenados.

Sin la palabra clave STRICT, se puede insertar cualquier tipo de datos en la tabla y el tipo de datos de la columna declarada se considera una recomendación en lugar de un requisito. 

//-- initializing properties for the AutoDST table
   CalendarContents[0].Content = AutoDST_Table;
   CalendarContents[0].name = "AutoDST";
   CalendarContents[0].sql = "CREATE TABLE AutoDST(DST TEXT NOT NULL DEFAULT 'DST_NONE')STRICT;";
   CalendarContents[0].tbl_name = "AutoDST";
   CalendarContents[0].type = "table";
   CalendarContents[0].insert = "INSERT INTO 'AutoDST'(DST) VALUES ('%s');";

Ahora inicializaremos las propiedades de las vistas de calendario para UK, US, AU y NONE. 

En la variable view_sql almacenamos nuestra sentencia SQL para crear las vistas individuales. En nuestra sentencia SQL seleccionamos Eventid, Eventname, Country, EventCurrency y Eventcode de la tabla MQL5Calendar.

ID      EVENTID 	COUNTRY 	EVENTNAME       				EVENTTYPE       	EVENTIMPORTANCE 		EVENTCURRENCY   EVENTCODE       EVENTSECTOR     		EVENTFORECAST   EVENTPREVALUE   EVENTIMPACT     		EVENTFREQUENCY
18742   999020002       European Union  Eurogroup Meeting       			CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    EUR     	EU      	CALENDAR_SECTOR_GOVERNMENT      None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
18746   999010020       European Union  ECB Executive Board Member Lane Speech  	CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    EUR     	EU      	CALENDAR_SECTOR_MONEY   	None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
34896   392010004       Japan   	Coincident Index        			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        113900000       113900000       CALENDAR_IMPACT_NEGATIVE        CALENDAR_FREQUENCY_MONTH
34897   392010005       Japan   	Leading Index   				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        111400000       111400000       CALENDAR_IMPACT_POSITIVE        CALENDAR_FREQUENCY_MONTH
34898   392010011       Japan   	Coincident Index m/m    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        None    	2400000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
34899   392010012       Japan   	Leading Index m/m       			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        None    	-700000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
55462   156010014       China   	Industrial Profit YTD y/y       		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	CNY     	CN      	CALENDAR_SECTOR_BUSINESS        None    	4300000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72568   276030001       Germany 	Ifo Business Expectations       		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    EUR     	DE      	CALENDAR_SECTOR_BUSINESS        92000000        89900000        CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72569   276030002       Germany 	Ifo Current Business Situation  		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    EUR     	DE      	CALENDAR_SECTOR_BUSINESS        88800000        88900000        CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72570   276030003       Germany 	Ifo Business Climate    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH        EUR     	DE      	CALENDAR_SECTOR_BUSINESS        89900000        89400000        CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72571   276050007       Germany 	Bbk Executive Board Member Mauderer Speech      CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    EUR     	DE      	CALENDAR_SECTOR_MONEY   	None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
78850   250020001       France  	3-Month BTF Auction     			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	EUR     	FR      	CALENDAR_SECTOR_MARKET  	None    	3746000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
78851   250020002       France  	6-Month BTF Auction     			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	EUR     	FR      	CALENDAR_SECTOR_MARKET  	None    	3657000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
78852   250020003       France  	12-Month BTF Auction    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	EUR     	FR      	CALENDAR_SECTOR_MARKET  	None    	3467000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
84771   76020007        Brazil  	BCB Bank Lending m/m    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	BRL     	BR      	CALENDAR_SECTOR_MONEY   	400000  	1200000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
84772   76020001        Brazil  	BCB Focus Market Report 			CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    BRL     	BR      	CALENDAR_SECTOR_MONEY   	None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
94938   344020004       Hong Kong       Exports y/y     				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	HKD     	HK      	CALENDAR_SECTOR_TRADE   	18100000        4700000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
94939   344020005       Hong Kong       Imports y/y     				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	HKD     	HK      	CALENDAR_SECTOR_TRADE   	15000000        5300000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
94940   344020006       Hong Kong       Trade Balance   				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    HKD     	HK      	CALENDAR_SECTOR_TRADE   	-29054000       -45000000       CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
102731  578020001       Norway  	Unemployment Rate       			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    NOK     	NO      	CALENDAR_SECTOR_JOBS    	3700000 	4000000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
102732  578020020       Norway  	General Public Domestic Loan Debt y/y   	CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	NOK     	NO      	CALENDAR_SECTOR_MONEY   	3300000 	3500000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
147163  840031004       United States   Memorial Day    				CALENDAR_TYPE_HOLIDAY   CALENDAR_IMPORTANCE_NONE        USD     	US      	CALENDAR_SECTOR_HOLIDAYS        None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
162245  826090005       United Kingdom  Spring Bank Holiday     			CALENDAR_TYPE_HOLIDAY   CALENDAR_IMPORTANCE_NONE        GBP     	GB      	CALENDAR_SECTOR_HOLIDAYS        None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE

A continuación, seleccionamos la columna DST para el horario correspondiente de TimeSchedule.

ID      DST_UK  		DST_US  		DST_AU  		DST_NONE
18742   2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00
18746   2024.05.27 14:00        2024.05.27 14:00        2024.05.27 14:00        2024.05.27 14:00
34896   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
34897   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
34898   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
34899   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
55462   2024.05.27 03:30        2024.05.27 03:30        2024.05.27 03:30        2024.05.27 03:30
72568   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
72569   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
72570   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
72571   2024.05.27 15:30        2024.05.27 15:30        2024.05.27 15:30        2024.05.27 15:30
78850   2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50
78851   2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50
78852   2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50
84771   2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30
84772   2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30
94938   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
94939   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
94940   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
102731  2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00
102732  2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00
147163  2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00
162245  2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00

Y unir las dos tablas MQL5Calendar y TimeSchedule con la misma ID.

Filtramos esta lista por la Fecha (Date) de la tabla Registro (Record).

Date
27-05-2024

Una vez que obtenemos los resultados de la consulta, los resultados se ordenan en orden ascendente basado en el respectivo DST desde TimeSchedule.

   string views[] = {"UK","US","AU","NONE"};
   string view_sql = "CREATE VIEW IF NOT EXISTS Calendar_%s "
                     "AS "
                     "SELECT C.Eventid,C.Eventname,C.Country,T.DST_%s as Time,C.EventCurrency,C.Eventcode from MQL5Calendar C,Record R "
                     "Inner join TimeSchedule T on C.ID=T.ID "
                     "Where DATE(REPLACE(T.DST_%s,'.','-'))=R.Date "
                     "Order by T.DST_%s Asc;";

//-- Sql statements for creating the table views
   for(uint i=1;i<=views.Size();i++)
     {
      CalendarContents[i].Content = (CalendarComponents)i;
      CalendarContents[i].name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].sql = StringFormat(view_sql,views[i-1],views[i-1],views[i-1],views[i-1]);
      CalendarContents[i].tbl_name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].type = "view";
     }

Echaremos un vistazo a una de las vistas y veremos qué producirá el resultado de la consulta:

SELECT * FROM 'Calendar_UK';

Salida:

EVENTID 	EVENTNAME       				COUNTRY 	Time    		EVENTCURRENCY   EVENTCODE
999020002       Eurogroup Meeting       			European Union  2024.05.27 02:00        EUR     	EU
840031004       Memorial Day    				United States   2024.05.27 02:00        USD     	US
826090005       Spring Bank Holiday     			United Kingdom  2024.05.27 02:00        GBP     	GB
156010014       Industrial Profit YTD y/y       		China   	2024.05.27 03:30        CNY     	CN
392010004       Coincident Index        			Japan   	2024.05.27 07:00        JPY     	JP
392010005       Leading Index   				Japan   	2024.05.27 07:00        JPY     	JP
392010011       Coincident Index m/m    			Japan   	2024.05.27 07:00        JPY     	JP
392010012       Leading Index m/m       			Japan   	2024.05.27 07:00        JPY     	JP
578020001       Unemployment Rate       			Norway  	2024.05.27 08:00        NOK     	NO
578020020       General Public Domestic Loan Debt y/y   	Norway  	2024.05.27 08:00        NOK     	NO
276030001       Ifo Business Expectations       		Germany 	2024.05.27 10:30        EUR     	DE
276030002       Ifo Current Business Situation  		Germany 	2024.05.27 10:30        EUR     	DE
276030003       Ifo Business Climate    			Germany 	2024.05.27 10:30        EUR     	DE
344020004       Exports y/y     				Hong Kong       2024.05.27 10:30        HKD     	HK
344020005       Imports y/y     				Hong Kong       2024.05.27 10:30        HKD     	HK
344020006       Trade Balance   				Hong Kong       2024.05.27 10:30        HKD     	HK
76020007        BCB Bank Lending m/m    			Brazil  	2024.05.27 13:30        BRL     	BR
76020001        BCB Focus Market Report 			Brazil  	2024.05.27 13:30        BRL     	BR
999010020       ECB Executive Board Member Lane Speech  	European Union  2024.05.27 14:00        EUR     	EU
250020001       3-Month BTF Auction     			France  	2024.05.27 14:50        EUR     	FR
250020002       6-Month BTF Auction     			France  	2024.05.27 14:50        EUR     	FR
250020003       12-Month BTF Auction    			France  	2024.05.27 14:50        EUR     	FR
276050007       Bbk Executive Board Member Mauderer Speech      Germany 	2024.05.27 15:30        EUR     	DE

Técnicamente tenemos una nueva tabla llamada Record, esta tabla sustituirá a nuestra anterior tabla Records, ya que ahora sólo almacenaremos un registro por registro. Nuestra tabla tendrá el tipo de dato TEXT y el nombre de columna 'Date', que no debe confundirse con la función Date de SQLite.

//-- initializing properties for the Record table
   CalendarContents[5].Content = Record_Table;
   CalendarContents[5].name = "Record";
   CalendarContents[5].sql = "CREATE TABLE Record(Date TEXT NOT NULL)STRICT;";
   CalendarContents[5].tbl_name="Record";
   CalendarContents[5].type = "table";
   CalendarContents[5].insert = "INSERT INTO 'Record'(Date) VALUES (Date(REPLACE('%s','.','-')));";

Nuestra tabla TimeSchedule almacenara todos los datos de tiempo de los eventos individuales y usara la clave foranea de referencia 'ID' para enlazar la tabla (crear una relacion) con la tabla MQL5Calendar.

//-- initializing properties for the TimeSchedule table
   CalendarContents[6].Content = TimeSchedule_Table;
   CalendarContents[6].name = "TimeSchedule";
   CalendarContents[6].sql = "CREATE TABLE TimeSchedule(ID INT NOT NULL,DST_UK   TEXT   NOT NULL,DST_US   TEXT   NOT NULL,"
                             "DST_AU   TEXT   NOT NULL,DST_NONE   TEXT   NOT NULL,FOREIGN KEY (ID) REFERENCES MQL5Calendar (ID))STRICT;";
   CalendarContents[6].tbl_name="TimeSchedule";
   CalendarContents[6].type = "table";
   CalendarContents[6].insert = "INSERT INTO 'TimeSchedule'(ID,DST_UK,DST_US,DST_AU,DST_NONE) "
                                "VALUES (%d,'%s','%s', '%s', '%s');";

La tabla MQL5Calendar tendrá una clave primaria llamada 'ID' que será única para cada registro de evento de noticias en la tabla.

//-- initializing properties for the MQL5Calendar table
   CalendarContents[7].Content = MQL5Calendar_Table;
   CalendarContents[7].name = "MQL5Calendar";
   CalendarContents[7].sql = "CREATE TABLE MQL5Calendar(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  TEXT   NOT NULL,"
                             "EVENTNAME   TEXT   NOT NULL,EVENTTYPE   TEXT   NOT NULL,EVENTIMPORTANCE   TEXT   NOT NULL,"
                             "EVENTCURRENCY  TEXT   NOT NULL,EVENTCODE   TEXT   NOT NULL,EVENTSECTOR TEXT   NOT NULL,"
                             "EVENTFORECAST  TEXT   NOT NULL,EVENTPREVALUE  TEXT   NOT NULL,EVENTIMPACT TEXT   NOT NULL,"
                             "EVENTFREQUENCY TEXT   NOT NULL,PRIMARY KEY(ID))STRICT;";
   CalendarContents[7].tbl_name="MQL5Calendar";
   CalendarContents[7].type = "table";
   CalendarContents[7].insert = "INSERT INTO 'MQL5Calendar'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTCURRENCY,EVENTCODE,"
                                "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY) "
                                "VALUES (%d,%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');";

Crearemos un disparador llamado OnlyOne_AutoDST. El disparador se activará cuando intentamos insertar un valor en el AutoDST y eliminar todos los registros de AutoDST antes de insertar un nuevo registro.

//-- Sql statement for creating the AutoDST table's trigger
   CalendarContents[8].Content = AutoDST_Trigger;
   CalendarContents[8].name = "OnlyOne_AutoDST";
   CalendarContents[8].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_AutoDST "
                             "BEFORE INSERT ON AutoDST "
                             "BEGIN "
                             "Delete from AutoDST; "
                             "END;";
   CalendarContents[8].tbl_name="AutoDST";
   CalendarContents[8].type = "trigger";

Lo mismo puede decirse de OnlyOne_Record, pero este desencadenante está relacionado con la tabla Record.

//-- Sql statement for creating the Record table's trigger
   CalendarContents[9].Content = Record_Trigger;
   CalendarContents[9].name = "OnlyOne_Record";
   CalendarContents[9].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_Record "
                             "BEFORE INSERT ON Record "
                             "BEGIN "
                             "Delete from Record; "
                             "END;";
   CalendarContents[9].tbl_name="Record";
   CalendarContents[9].type = "trigger";

Ahora en nuestra función UpdateRecords determinaremos si nuestra base de datos Calendar requiere una actualización.

Los cambios de esta función respecto a la anterior de la parte 1 son los siguientes:

1. Leeremos todos los objetos que no sean índices presentes en la base de datos con la consulta SQL «select * from sqlite_master where type<>“index” ; ».

2. Almacenaremos todos los atributos del objeto en el array DBContents y si no hay punto y coma presente al final de la sentencia SQL añadiremos uno.

3. Compararemos los objetos hallados en nuestra base de datos y los objetos que inicializamos en nuestra array CalendarContents. Eliminaremos 'IF NOT EXISTS' del SQL 'CalendarContents'.

4. Cuando no hallamos una coincidencia entre DBContents y CalendarContents procederemos a dar de baja el objeto en el índice DBcontents.

5. Si las coincidencias del objeto SQL no son iguales al tamaño de CalendarContents realizaremos una actualización. 

bool CNews::UpdateRecords()
  {
//initialize variable to true
   bool perform_update=true;
//--- open/create
//-- try to open database Calendar
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)//Checks if the database was able to be opened
     {
      //if opening the database failed
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if the database Calendar exists in the common folder
        {
         return perform_update;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder
        }
     }

   int MasterRequest = DatabasePrepare(db,"select * from sqlite_master where type<>'index';");
   if(MasterRequest==INVALID_HANDLE)
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
     }
   else
     {
      SQLiteMaster ReadContents;
      //Assigning values from the sql query into DBContents array
      for(int i=0; DatabaseReadBind(MasterRequest,ReadContents); i++)
        {
         ArrayResize(DBContents,i+1,i+2);
         DBContents[i].type = ReadContents.type;
         DBContents[i].name = ReadContents.name;
         DBContents[i].tbl_name = ReadContents.tbl_name;
         DBContents[i].rootpage = ReadContents.rootpage;
         /*Check if the end of the sql string has a character ';' if not add this character to the string*/
         DBContents[i].sql = (StringFind(ReadContents.sql,";",StringLen(ReadContents.sql)-1)==
                              (StringLen(ReadContents.sql)-1))?ReadContents.sql:ReadContents.sql+";";;
        }

      uint contents_exists = 0;
      for(uint i=0;i<DBContents.Size();i++)
        {
         bool isCalendarContents = false;
         for(uint x=0;x<CalendarContents.Size();x++)
           {
            /*Store Sql query from CalendarContents without string ' IF NOT EXISTS'*/
            string CalendarSql=CalendarContents[x].sql;
            StringReplace(CalendarSql," IF NOT EXISTS","");
            //-- Check if the Db object is in our list
            if(DBContents[i].name==CalendarContents[x].name&&
               (DBContents[i].sql==CalendarSql||
                DBContents[i].sql==CalendarContents[x].sql)&&
               CalendarContents[x].type==DBContents[i].type&&
               CalendarContents[x].tbl_name==DBContents[i].tbl_name)
              {
               contents_exists++;
               isCalendarContents = true;
              }
           }
         if(!isCalendarContents)
           {
            //-- Print DBcontent's name if it does not match with CalendarContents
            PrintFormat("DBContent: %s is not needed!",DBContents[i].name);
            //-- We will drop the table if it is not neccessary
            DatabaseExecute(db,StringFormat(DropRequest,DBContents[i].type,DBContents[i].name));
            Print("Attempting To Clean Database...");
           }
        }
      /*If not all the CalendarContents exist in the Calendar Database before an update */
      if(contents_exists!=CalendarContents.Size())
        {
         return perform_update;
        }
     }
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//If the database table 'Record' doesn't exist
     {
      DatabaseClose(db);
      return perform_update;
     }

//-- Sql query to determine the lastest or maximum date recorded
   /* If the last recorded date data in the 'Record' table is not equal to the current day, perform an update! */
   string request_text=StringFormat("SELECT Date FROM %s where Date=Date(REPLACE('%s','.','-'))",
                                    CalendarStruct(Record_Table).name,TimeToString(TimeTradeServer()));
   int request=DatabasePrepare(db,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
   if(request==INVALID_HANDLE)//Checks if the request failed to be completed
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return perform_update;
     }

   if(DatabaseRead(request))//Will be true if there are results from the sql query/request
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      perform_update=false;
      return perform_update;
     }
   else
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      return perform_update;
     }
  }

En la funcion CreateCalendarTable, comprobaremos si la tabla MQL5Calendar ya existe en la base de datos del calendario, tambien comprobaremos si la tabla TimeSchedule ya existe e intentaremos eliminar cada tabla si existen. Como TimeSchedule requiere MQL5Calendar no podemos eliminar MQL5Calendar sin eliminar TimeSchedule primero. 

Una vez que MQL5Calendar no existe crearemos su tabla.

bool CNews::CreateCalendarTable(int db,bool &tableExists)
  {
//-- Checks if a table 'MQL5Calendar' exists
   if(DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      tableExists=true;//Assigns true to tableExists variable
      //-- Checks if a table 'TimeSchedule' exists in the database 'Calendar'
      if(DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
        {
         //-- We will drop the table if the table already exists
         if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(TimeSchedule_Table).name)))
           {
            //If the table failed to be dropped/deleted
            PrintFormat("Failed to drop table %s with code %d",CalendarStruct(TimeSchedule_Table).name,GetLastError());
            DatabaseClose(db);//Close the database
            return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
           }
        }
      //--We will drop the table if the table already exists
      if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(MQL5Calendar_Table).name)))
        {
         //If the table failed to be dropped/deleted
         PrintFormat("Failed to drop table %s with code %d",CalendarStruct(MQL5Calendar_Table).name,GetLastError());
         DatabaseClose(db);//Close the database
         return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
        }
     }
//-- If the database table 'MQL5Calendar' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      //--- create the table 'MQL5Calendar'
      if(!DatabaseExecute(db,CalendarStruct(MQL5Calendar_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

En la función CreateTimeTable, verificamos si la tabla existe en la base de datos Calendar, y si no la creamos. 

bool CNews::CreateTimeTable(int db,bool &tableExists)
  {
//-- If the database table 'TimeSchedule' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
     {
      //--- create the table 'TimeSchedule'
      if(!DatabaseExecute(db,CalendarStruct(TimeSchedule_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

En la función CreateCalendarViews, creamos todas las vistas usando CalendarComponents para encontrar la identidad (valor de enumeración) para cada vista y crearla.

void CNews::CreateCalendarViews(int db)
  {
   for(uint i=1;i<=4;i++)
     {
      if(!DatabaseExecute(db,CalendarStruct((CalendarComponents)i).sql))//Checks if the view was successfully created
        {
         Print("DB: create the Calendar view failed with code ", GetLastError());
        }
     }
  }

En la función InsertIntoTables, insertaremos cada registro de la matriz Evalues en la tabla MQL5Calendar y en la tabla TimeSchedule respectivamente. Las fechas de los eventos se ajustarán a los distintos horarios de verano en TimeSchedule.

bool CNews::InsertIntoTables(int db,Calendar &Evalues[])
  {
   for(uint i=0; i<Evalues.Size(); i++)//Looping through all the Economic Events
     {
      string request_insert_into_calendar =
         StringFormat(CalendarStruct(MQL5Calendar_Table).insert,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Evalues[i].EventCurrency,
                      Evalues[i].EventCode,
                      Evalues[i].EventSector,
                      Evalues[i].EventForecast,
                      Evalues[i].EventPreval,
                      Evalues[i].EventImpact,
                      Evalues[i].EventFrequency);//Inserting all the columns for each event record
      if(DatabaseExecute(db,request_insert_into_calendar))//Check if insert query into calendar was successful
        {
         string request_insert_into_time =
            StringFormat(CalendarStruct(TimeSchedule_Table).insert,
                         i,
                         //-- Economic EventDate adjusted for UK DST(Daylight Savings Time)
                         Savings_UK.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for US DST(Daylight Savings Time)
                         Savings_US.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for AU DST(Daylight Savings Time)
                         Savings_AU.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         Evalues[i].EventDate//normal Economic EventDate
                        );//Inserting all the columns for each event record
         if(!DatabaseExecute(db,request_insert_into_time))
           {
            Print(GetLastError());
            //-- Will print the sql query to check for any errors or possible defaults in the query/request
            Print(request_insert_into_time);
            return false;//Will end the loop and return false, as values failed to be inserted into the table
           }
        }
      else
        {
         Print(GetLastError());
         //-- Will print the sql query to check for any errors or possible defaults in the query/request
         Print(request_insert_into_calendar);
         return false;//Will end the loop and return false, as values failed to be inserted into the table
        }
     }
   return true;//Will return true, all values were inserted into the table successfully
  }

En la función CreateRecordTable verificamos si la tabla Record ya existe, si no existe crearemos la tabla. Una vez que exista la tabla Record crearemos el disparador de la misma. Se proceda a insertar la fecha actual del servidor.


¿Por qué utilizar TimeTradeServer en lugar de TimeCurrent?

Si utilizamos TimeCurrent obtendremos los datos de tiempo para el símbolo del gráfico actual y los tiempos de los símbolos pueden variar entre sí ya que los datos de tiempo se actualizan cada nuevo tick. Esto es potencialmente problemático cuando los símbolos no tienen el mismo horario de negociación y cuando el símbolo del gráfico actual podría estar cerrado, lo que significa que no se están recibiendo nuevos ticks, por lo que el TimeCurrent podría devolver una fecha que está un día o más por detrás de la fecha real. Mientras que TimeTradeServer se actualiza constantemente independientemente del tipo de símbolo.

void CNews::CreateRecordTable(int db)
  {
   bool failed=false;
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//Checks if the table 'Record' exists in the databse 'Calendar'
     {
      //--- create the table
      if(!DatabaseExecute(db,CalendarStruct(Record_Table).sql))//Will attempt to create the table 'Record'
        {
         Print("DB: create the Records table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
        }
     }
   else
     {
      DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
     }
//Sql query/request to insert the current time into the 'Date' column in the table 'Record'
   string request_text=StringFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }
En la función CreateAutoDST, comprobamos el horario DST del broker, si obtenemos con éxito el horario DST comprobamos si la tabla AutoDST existe en la base de datos Calendar. Si la tabla AutoDST no existe, se creará. Una vez que la tabla AutoDST existe, creamos su disparador e intentamos insertar el horario DST convertido de una enumeración a una cadena (string). 
void CNews::CreateAutoDST(int db)
  {
   bool failed=false;//boolean variable
   if(!AutoDetectDST(DSTType))//Check if AutoDetectDST went through all the right procedures
     {
      return;//will terminate execution of the rest of the code below
     }

   if(!DatabaseTableExists(db,CalendarStruct(AutoDST_Table).name))//Checks if the table 'AutoDST' exists in the databse 'Calendar'
     {
      //--- create the table AutoDST
      if(!DatabaseExecute(db,CalendarStruct(AutoDST_Table).sql))//Will attempt to create the table 'AutoDST'
        {
         Print("DB: create the AutoDST table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
        }
     }
   else
     {
      //Create trigger if AutoDST table exists
      DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
     }
//Sql query/request to insert the recommend DST for the Broker using the DSTType variable to determine which string data to insert
   string request_text=StringFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));//Will print the sql query if failed
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }


Clase de gestión de riesgos

La gestión del riesgo es un componente esencial del éxito en la negociación. El objetivo principal de la gestión del riesgo es proteger el capital de negociación. Sin capital, un operador no puede seguir operando. Aplicar estrategias para limitar las pérdidas garantiza que los operadores puedan permanecer más tiempo en el mercado, lo que les brinda más oportunidades de recuperarse de los contratiempos y lograr una rentabilidad general. En este caso, ofreceremos al usuario distintos perfiles de riesgo para que elija y hallar la opción más adecuada.

Aclaración: Me referiré indistintamente a lote, tamaño de lote y volumen. Considérelos iguales en el contexto de la gestión de riesgos. 


Lista de perfiles de riesgo:

  • Tamaño mínimo del lote
  • Tamaño máximo del lote
  • Porcentaje del balance
  • Porcentaje del margen libre
  • Riesgo en importe por balance
  • Riesgo en importe por margen libre
  • Tamaño del lote por balance
  • Tamaño del lote por margen libre
  • Tamaño de lote personalizado
  • Porcentaje de riesgo

Tamaño mínimo del lote:

En esta opción de riesgo utilizaremos el tamaño de lote mínimo permitido para el símbolo actual.

Tamaño máximo del lote:

En esta opción de riesgo utilizaremos el tamaño de lote máximo permitido para el símbolo actual.

Porcentaje del balance:

En esta opción de riesgo primero obtendremos la cantidad de riesgo.

amount_of_risk = Balance*Percent;

Supongamos que el porcentaje es del 5% y el saldo de la cuenta es de 10.000.

amount_of_risk = 10000*(5/100);
amount_of_risk = 500;

Entonces necesitaremos un precio de apertura y un precio de cierre para calcular el riesgo mínimo para la operación específica cuando se utiliza el tamaño de lote mínimo.

OrderCalcProfit(ORDER_TYPE,Symbol(),Minimum_lotsize,OpenPrice,ClosePrice,Minimum_risk);

Una vez devuelto el riesgo mínimo, utilizaremos la siguiente ecuación para obtener el tamaño de lote necesario para la cantidad de riesgo.

required_lotsize = (amount_of_risk/Minimum_risk)*Minimum_lotsize;

Supongamos que Minimum_risk = 100 y Minimum_lotsize = 0,1;

required_lotsize = (500/100)*0.1;
required_lotsize = 5*0.1;
required_lotsize = 0.5;


Porcentaje de margen libre:

Esta opción de riesgo es similar al Porcentaje del balance. Pero las ventajas de esta opción de riesgo entran en juego cuando hay operaciones abiertas en la cuenta del operador.

Mientras que el riesgo sigue siendo el mismo en el Porcentaje del balance, independientemente de las operaciones abiertas, siempre que el saldo sea el mismo. Con el Porcentaje de margen libre el riesgo cambia a medida que fluctúa el beneficio de las operaciones abiertas. De este modo, se obtiene un cálculo más exacto del riesgo para el estado de la cuenta corriente.


Riesgo en importe por balance:

En esta opción de riesgo primero tenemos que obtener el cociente entre riesgo del importe (el divisor) y el balance (dividendo).

risk = Balance/Risk_in_Amount;

Entonces dejaremos el balance como 10.000 y la cantidad en riesgo como 800;

En este caso, básicamente queremos arriesgar 800 $ por operación por cada 10.000 $ en el saldo de la cuenta del operador.

risk = 10000/800;
risk = 12.5;

A continuación, dividiremos el riesgo por el saldo real de la cuenta para obtener nuestro importe de riesgo.

amount_of_risk = AccountBalance/risk;

Deje AccountBalance como 5000;

amount_of_risk = 5000/12.5;
amount_of_risk = 400;

Ahora sabemos que el operador quiere arriesgar 400 dólares en esta operación concreta.


Riesgo en cantidad por margen libre:

Esta opción de riesgo es similar al riesgo en importe por balance, solo repasaremos otro ejemplo.

risk = FreeMargin/Risk_in_Amount;

Dejaremos FreeMargin = 150 y Risk_in_Amount = 1;

En este caso arriesgaremos $1 por cada $150 en FreeMargin.

risk = 150/1;
risk = 150;

amount_of_risk = AccountFreeMargin/risk;

//-- Let AccountFreeMargin = 750

amount_of_risk = 750/150;
amount_of_risk = 5;

Después de obtener el monto de riesgo, calcularemos el tamaño de lote requerido para esa operación específica para cubrir los 5 dólares de riesgo.


Tamaño de lote por saldo:

En esta opción de riesgo, el operador proporcionará el tamaño de lote que desea arriesgar para un saldo de cuenta determinado.

required_lotsize = (AccountBalance/Balance)*lotsize;

Donde AccountBalance es el saldo real de la cuenta del comerciante, Balance y tamaño del lote son valores de entrada proporcionados por el comerciante.

Supongamos que AccountBalance = 10 000 y Balance = 350 y tamaño del lote = 0.01.

En este caso, el operador quiere arriesgar 0.01 lotes por cada 350 dólares de saldo en su cuenta. 

required_lotsize = (10000/350)*0.01;
required_lotsize = 0.285;

El tamaño de lote requerido es 0.285, el valor real es mucho más largo. Supongamos que el paso de volumen para el símbolo específico en el que queremos abrir una operación es 0.01. Intentar abrir una operación con un tamaño de lote de 0.285 cuando el paso de volumen es 0.01 provocará un error.

Para evitar esto, normalizaremos el tamaño del lote, básicamente formatearemos el tamaño del lote de acuerdo con el paso del volumen.

required_lotsize = Volume_Step*MathFloor(0.285/Volume_Step);
requred_lotsize = 0.01*MathFloor(0.285/0.01);
required_lotsize = 0.01*MathFloor(28.5);
required_lotsize = 0.01*28;
required_lotsize = 0.28;


Tamaño de lote por margen libre:

Esta opción de riesgo es similar a tamaño de lote por balance, proporcionaremos otro ejemplo.

required_lotsize = (AccountFreeMargin/FreeMargin)*lotsize;

Supongamos AccountFreeMargin = 134.560 y FreeMargin = 1622 y tamaño de lote = 0.0056

En este caso:

required_lotsize = (134560/1622)*0.0056;
required_lotsize = 0.464;

Supongamos que el paso de volumen es 0.02.

Normalizaremos required_lotsize de acuerdo con el paso de volumen.

//-- normalize for Volume Step
required_lotsize = Volume_Step*MathFloor(0.464/Volume_Step);
requred_lotsize = 0.02*MathFloor(0.464/0.02);
required_lotsize = 0.02*MathFloor(23.2);
required_lotsize = 0.02*23;
required_lotsize = 0.46;


Tamaño de lote personalizado:

En esta opción de riesgo utilizaremos el tamaño de lote de entrada proporcionado por el comerciante.


Porcentaje de riesgo:

En esta opción de riesgo calcularemos el porcentaje de riesgo máximo permitido para el símbolo actual utilizando los requisitos de margen libre y margen para el símbolo específico.

La clase CRiskManagement tiene herencia multinivel de las clases:

  • CSymbolProperties
  • CChartProperties

La clase CRiskManagement tiene la inclusión de la clase CAccountInfo.

La clase CRiskManagement tiene herencia jerárquica de clases:

  • CSymbolProperties
  • CSymbolInfo
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "ChartProperties.mqh"
#include <Trade/AccountInfo.mqh>
CAccountInfo      Account;

//-- Enumeration declaration for Risk options
enum RiskOptions
  {
   MINIMUM_LOT,//MINIMUM LOTSIZE
   MAXIMUM_LOT,//MAXIMUM LOTSIZE
   PERCENTAGE_OF_BALANCE,//PERCENTAGE OF BALANCE
   PERCENTAGE_OF_FREEMARGIN,//PERCENTAGE OF FREE-MARGIN
   AMOUNT_PER_BALANCE,//AMOUNT PER BALANCE
   AMOUNT_PER_FREEMARGIN,//AMOUNT PER FREE-MARGIN
   LOTSIZE_PER_BALANCE,//LOTSIZE PER BALANCE
   LOTSIZE_PER_FREEMARGIN,//LOTSIZE PER FREE-MARGIN
   CUSTOM_LOT,//CUSTOM LOTSIZE
   PERCENTAGE_OF_MAXRISK//PERCENTAGE OF MAX-RISK
  } RiskProfileOption;//variable for Risk options

//-- Enumeration declaration for Risk floor
enum RiskFloor
  {
   RiskFloorMin,//MINIMUM LOTSIZE
   RiskFloorMax,//MAX-RISK
   RiskFloorNone//NONE
  } RiskFloorOption;//variable for Risk floor

//-- Enumeration declaration for Risk ceiling(Maximum allowable risk in terms of lot-size)
enum RiskCeil
  {
   RiskCeilMax,//MAX LOTSIZE
   RiskCeilMax2,//MAX LOTSIZE(x2)
   RiskCeilMax3,//MAX LOTSIZE(x3)
   RiskCeilMax4,//MAX LOTSIZE(x4)
   RiskCeilMax5,//MAX LOTSIZE(x5)
  } RiskCeilOption;//variable for Risk ceiling

//-- Structure declaration for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
struct RISK_AMOUNT
  {
   double            RiskAmountBoF;//store Balance or Free-Margin
   double            RiskAmount;//store risk amount
  } Risk_Profile_2;//variable for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)

//-- Structure declaration for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
struct RISK_LOT
  {
   double            RiskLotBoF;//store Balance or Free-Margin
   double            RiskLot;//store lot-size
  } Risk_Profile_3;//variable for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)


double            RiskFloorPercentage;//variable for RiskFloorMax
double            Risk_Profile_1;//variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
double            Risk_Profile_4;//variable for Risk option (CUSTOM LOTSIZE)
double            Risk_Profile_5;//variable for Risk option (PERCENTAGE OF MAX-RISK)

//+------------------------------------------------------------------+
//|RiskManagement class                                              |
//+------------------------------------------------------------------+
class CRiskManagement : public CChartProperties
  {

private:
   double            Medium;//variable to store actual Account (Balance or Free-Margin)
   double            RiskAmount,MinimumAmount;
   double            Lots;//variable to store Lot-size to open trade
   const double      max_percent;//variable to store percentage for Maximum risk

   //-- enumeration for dealing with account balance/free-margin
   enum RiskMedium
     {
      BALANCE,
      MARGIN
     };

   //-- calculations for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
   double              RiskProfile1(const RiskMedium R_Medium);
   //-- calculations for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
   double              RiskProfile2(const RiskMedium R_Medium);
   //-- calculations for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
   double              RiskProfile3(const RiskMedium R_Medium);
   //-- calculations for Maximum allowable Risk
   double              MaxRisk(const double percent);
   //-- Store Trade's Open-price
   double              OpenPrice;
   //-- Store Trade's Close-price
   double              ClosePrice;
   //-- Store Ordertype between (ORDER_TYPE_BUY or ORDER_TYPE_SELL) for risk calaculations
   ENUM_ORDER_TYPE     ORDERTYPE;
   //-- Set Medium variable value
   void                SetMedium(const RiskMedium R_Medium) {Medium = (R_Medium==BALANCE)?Account.Balance():Account.FreeMargin();}
   //-- Get Minimum Risk for a Trade using Minimum Lot-size
   bool                GetMinimumRisk()
     {
      return OrderCalcProfit(ORDERTYPE,Symbol(),LotsMin(),OpenPrice,ClosePrice,MinimumAmount);
     }
   //-- Retrieve Risk amount based on Risk inputs
   double            GetRisk(double Amount)
     {
      if(!GetMinimumRisk()||Amount==0)
         return 0.0;
      return ((Amount/MinimumAmount)*LotsMin());
     }

protected:
   //-- Application of Lot-size limits
   void              ValidateLotsize(double &Lotsize);
   //-- Set ORDERTYPE variable to (ORDER_TYPE_BUY or ORDER_TYPE_SELL) respectively
   void              SetOrderType(ENUM_ORDER_TYPE Type)
     {
      if(Type==ORDER_TYPE_BUY||Type==ORDER_TYPE_BUY_LIMIT||Type==ORDER_TYPE_BUY_STOP)
        {
         ORDERTYPE = ORDER_TYPE_BUY;
        }
      else
         if(Type==ORDER_TYPE_SELL||Type==ORDER_TYPE_SELL_LIMIT||Type==ORDER_TYPE_SELL_STOP)
           {
            ORDERTYPE = ORDER_TYPE_SELL;
           }
     }

public:

                     CRiskManagement();//Class's constructor
   //-- Retrieve user's Risk option
   string            GetRiskOption()
     {
      switch(RiskProfileOption)
        {
         case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
            return "MINIMUM LOTSIZE";
            break;
         case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
            return "MAXIMUM LOTSIZE";
            break;
         case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
            return "PERCENTAGE OF BALANCE";
            break;
         case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
            return "PERCENTAGE OF FREE-MARGIN";
            break;
         case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
            return "AMOUNT PER BALANCE";
            break;
         case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
            return "AMOUNT PER FREE-MARGIN";
            break;
         case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
            return "LOTSIZE PER BALANCE";
            break;
         case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
            return "LOTSIZE PER FREE-MARGIN";
            break;
         case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
            return "CUSTOM LOTSIZE";
            break;
         case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
            return "PERCENTAGE OF MAX-RISK";
            break;
         default:
            return "";
            break;
        }
     }
   //-- Retrieve user's Risk Floor Option
   string            GetRiskFloor()
     {
      switch(RiskFloorOption)
        {
         case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
            return "MINIMUM LOTSIZE";
            break;
         case RiskFloorMax://MAX-RISK for Risk floor options
            return "MAX-RISK";
            break;
         case RiskFloorNone://NONE for Risk floor options
            return "NONE";
            break;
         default:
            return "";
            break;
        }
     }
   //-- Retrieve user's Risk Ceiling option
   string            GetRiskCeil()
     {
      switch(RiskCeilOption)
        {
         case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
            return "MAX LOTSIZE";
            break;
         case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
            return "MAX LOTSIZE(x2)";
            break;
         case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
            return "MAX LOTSIZE(x3)";
            break;
         case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
            return "MAX LOTSIZE(x4)";
            break;
         case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
            return "MAX LOTSIZE(x5)";
            break;
         default:
            return "";
            break;
        }
     }

   double            Volume();//Get risk in Volume
   //Apply fixes to lot-size where applicable
   void              NormalizeLotsize(double &Lotsize);
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
//Initialize values
CRiskManagement::CRiskManagement(void):Lots(0.0),max_percent(100),
   ORDERTYPE(ORDER_TYPE_BUY),OpenPrice(Ask()),
   ClosePrice(NormalizePrice(Ask()+Ask()*0.01))

  {
  }

//+------------------------------------------------------------------+
//|Get risk in Volume                                                |
//+------------------------------------------------------------------+
double CRiskManagement::Volume()
  {
   switch(RiskProfileOption)
     {
      case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
         return LotsMin();
         break;
      case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
         Lots = LotsMax();
         break;
      case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
         Lots = RiskProfile1(BALANCE);
         break;
      case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
         Lots = RiskProfile1(MARGIN);
         break;
      case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
         Lots = RiskProfile2(BALANCE);
         break;
      case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
         Lots = RiskProfile2(MARGIN);
         break;
      case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
         Lots =  RiskProfile3(BALANCE);
         break;
      case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
         Lots = RiskProfile3(MARGIN);
         break;
      case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
         Lots = Risk_Profile_4;
         break;
      case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
         Lots = MaxRisk(Risk_Profile_5);
         break;
      default:
         Lots = 0.0;
         break;
     }
   ValidateLotsize(Lots);//Check/Adjust Lotsize Limits
   NormalizeLotsize(Lots);//Normalize Lotsize
   return Lots;
  }

//+------------------------------------------------------------------+
//|calculations for Risk options                                     |
//|(PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)             |
//+------------------------------------------------------------------+
//-- calculations for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
double CRiskManagement::RiskProfile1(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   RiskAmount = Medium*(Risk_Profile_1/100);
   return GetRisk(RiskAmount);
  }

//+------------------------------------------------------------------+
//|calculations for Risk options                                     |
//|(AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)                   |
//+------------------------------------------------------------------+
//-- calculations for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
double CRiskManagement::RiskProfile2(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   double risk = (Risk_Profile_2.RiskAmountBoF/Risk_Profile_2.RiskAmount);
   risk = (risk<1)?1:risk;

   if(Medium<=0)
      return 0.0;

   RiskAmount = Medium/risk;
   return GetRisk(RiskAmount);
  }

//+------------------------------------------------------------------+
//|calculations for Risk options                                     |
//|(LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)                 |
//+------------------------------------------------------------------+
//-- calculations for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
double CRiskManagement::RiskProfile3(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   return (Medium>0)?((Medium/Risk_Profile_3.RiskLotBoF)*Risk_Profile_3.RiskLot):0.0;
  }

//+------------------------------------------------------------------+
//|calculations for Maximum allowable Risk                           |
//+------------------------------------------------------------------+
//-- calculations for Maximum allowable Risk
double CRiskManagement::MaxRisk(const double percent)
  {
   double margin=0.0,max_risk=0.0;
//--- checks
   if(percent<0.01 || percent>100)
     {
      Print(__FUNCTION__," invalid parameters");
      return(0.0);
     }
//--- calculate margin requirements for 1 lot
   if(!OrderCalcMargin(ORDERTYPE,Symbol(),1.0,OpenPrice,margin) || margin<0.0)
     {
      Print(__FUNCTION__," margin calculation failed");
      return(0.0);
     }
//--- calculate maximum volume
   max_risk=Account.FreeMargin()*(percent/100.0)/margin;
//--- return volume
   return(max_risk);
  }

//+------------------------------------------------------------------+
//|Apply fixes to lot-size where applicable                          |
//+------------------------------------------------------------------+
void CRiskManagement::NormalizeLotsize(double &Lotsize)
  {
   if(Lotsize<=0.0)
      return;

//-- Check if the is a Volume limit for the current Symbol
   if(LotsLimit()>0.0)
     {
      if((Lots+PositionsVolume()+OrdersVolume())>LotsLimit())
        {
         //-- calculation of available lotsize remaining
         double remaining_avail_lots = (LotsLimit()-(PositionsVolume()+OrdersVolume()));
         if(remaining_avail_lots>=LotsMin())
           {
            if(RiskFloorOption==RiskFloorMin)//Check if Risk floor option is MINIMUM LOTSIZE
              {
               Print("Warning: Volume Limit Reached, minimum Lotsize selected.");
               Lotsize = LotsMin();
              }
            else
               if(RiskFloorOption==RiskFloorMax)//Check if Risk floor option is MAX-RISK
                 {
                  Print("Warning: Volume Limit Reached, Lotsize Reduced.");
                  Lotsize = ((remaining_avail_lots*(RiskFloorPercentage/100))>LotsMin())?
                            (remaining_avail_lots*(RiskFloorPercentage/100)):LotsMin();
                 }
           }
         else
           {
            Print("Volume Limit Reached!");
            Lotsize=0.0;
            return;
           }
        }
     }

//Check if there is a valid Volume Step for the current Symbol
   if(LotsStep()>0.0)
      Lotsize=LotsStep()*MathFloor(Lotsize/LotsStep());
  }

//+------------------------------------------------------------------+
//|Application of Lot-size limits                                    |
//+------------------------------------------------------------------+
void CRiskManagement::ValidateLotsize(double &Lotsize)
  {
   switch(RiskFloorOption)
     {
      case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
         //-- Check if lot-size is not less than Minimum lot or more than maximum allowable risk
         if(Lotsize<LotsMin()||Lotsize>MaxRisk(max_percent))
           {
            Lotsize=LotsMin();
           }
         break;
      case RiskFloorMax://MAX-RISK for Risk floor options
         //-- Check if lot-size is more the maximum allowable risk
         if(Lotsize>MaxRisk(max_percent))
           {
            Lotsize=(MaxRisk(RiskFloorPercentage)>LotsMin())?MaxRisk(RiskFloorPercentage):LotsMin();
           }
         else
            if(Lotsize<LotsMin())//Check if lot-size is less than Minimum lot
              {
               Lotsize=LotsMin();
              }
         break;
      case RiskFloorNone://NONE for Risk floor options
         //Check if lot-size is less than Minimum lot
         if(Lotsize<LotsMin())
           {
            Lotsize=0.0;
           }
         break;
      default:
         Lotsize=0.0;
         break;
     }

   switch(RiskCeilOption)
     {
      case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
         //Check if lot-size is more than Maximum lot
         if(Lotsize>LotsMax())
            Lotsize=LotsMax();
         break;
      case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times two
         if(Lotsize>(LotsMax()*2))
            Lotsize=(LotsMax()*2);
         break;
      case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times three
         if(Lotsize>(LotsMax()*3))
            Lotsize=(LotsMax()*3);
         break;
      case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times four
         if(Lotsize>(LotsMax()*4))
            Lotsize=(LotsMax()*4);
         break;
      case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times five
         if(Lotsize>(LotsMax()*5))
            Lotsize=(LotsMax()*5);
         break;
      default:
         break;
     }
  }
//+------------------------------------------------------------------+

La variable RiskProfileOption de tipo enumeración RiskOptions, almacenará la opción de perfil de riesgo del usuario/operador que será una entrada para el experto.  

//-- Enumeration declaration for Risk options
enum RiskOptions
  {
   MINIMUM_LOT,//MINIMUM LOTSIZE
   MAXIMUM_LOT,//MAXIMUM LOTSIZE
   PERCENTAGE_OF_BALANCE,//PERCENTAGE OF BALANCE
   PERCENTAGE_OF_FREEMARGIN,//PERCENTAGE OF FREE-MARGIN
   AMOUNT_PER_BALANCE,//AMOUNT PER BALANCE
   AMOUNT_PER_FREEMARGIN,//AMOUNT PER FREE-MARGIN
   LOTSIZE_PER_BALANCE,//LOTSIZE PER BALANCE
   LOTSIZE_PER_FREEMARGIN,//LOTSIZE PER FREE-MARGIN
   CUSTOM_LOT,//CUSTOM LOTSIZE
   PERCENTAGE_OF_MAXRISK//PERCENTAGE OF MAX-RISK
  } RiskProfileOption;//variable for Risk options

La variable RiskFloorOption de tipo enumeración RiskFloor, almacenará la preferencia de riesgo mínimo del usuario/operador que será una entrada para el experto.

//-- Enumeration declaration for Risk floor
enum RiskFloor
  {
   RiskFloorMin,//MINIMUM LOTSIZE
   RiskFloorMax,//MAX-RISK
   RiskFloorNone//NONE
  } RiskFloorOption;//variable for Risk floor

La variable RiskCeilOption de tipo enumeración RiskCeil, almacenará la preferencia de riesgo máximo del usuario/operador que será una entrada para el experto.

//-- Enumeration declaration for Risk ceiling(Maximum allowable risk in terms of lot-size)
enum RiskCeil
  {
   RiskCeilMax,//MAX LOTSIZE
   RiskCeilMax2,//MAX LOTSIZE(x2)
   RiskCeilMax3,//MAX LOTSIZE(x3)
   RiskCeilMax4,//MAX LOTSIZE(x4)
   RiskCeilMax5,//MAX LOTSIZE(x5)
  } RiskCeilOption;//variable for Risk ceiling

El balance de la cuenta o margen libre del usuario/comerciante se almacenará en la variable doble RiskAmountBoF y RiskAmount almacenará el valor del monto del riesgo. Risk_Profile_2 se utilizará para almacenar las propiedades IMPORTE POR BALANCE e IMPORTE POR MARGEN LIBRE de los perfiles de riesgo.

//-- Structure declaration for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
struct RISK_AMOUNT
  {
   double            RiskAmountBoF;//store Balance or Free-Margin
   double            RiskAmount;//store risk amount
  } Risk_Profile_2;//variable for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)

La variable Risk_Profile_3 de tipo estructura RISK_LOT, almacenará las propiedades TAMAÑO DE LOTE POR BALANCE y TAMAÑO DE LOTE POR MARGEN LIBRE de los perfiles de riesgo.

//-- Structure declaration for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
struct RISK_LOT
  {
   double            RiskLotBoF;//store Balance or Free-Margin
   double            RiskLot;//store lot-size
  } Risk_Profile_3;//variable for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)

La variable RiskFloorPercentage almacenará el porcentaje de máximo riesgo para la opción RiskFloorMax de Riskfloor.

double            RiskFloorPercentage;//variable for RiskFloorMax

La variable Risk_Profile_1 almacenará el porcentaje de riesgo para las opciones de riesgo PORCENTAJE DEL BALANCE o PORCENTAJE DE MARGEN LIBRE.

double            Risk_Profile_1;//variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)

La variable Risk_Profile_4 almacenará el tamaño de lote personalizado para la opción de riesgo TAMAÑO DE LOTE PERSONALIZADO.

double            Risk_Profile_4;//variable for Risk option (CUSTOM LOTSIZE)

La variable Risk_Profile_5 almacenará el porcentaje de riesgo máximo para la opción de riesgo PERCENTAJE DE RIESGO MÁXIMO.

double            Risk_Profile_5;//variable for Risk option (PERCENTAGE OF MAX-RISK)

En la función GetRiskOption, recuperaremos la opción de riesgo del usuario/operador en un string (cadena) de datos.

   //-- Retrieve user's Risk option
   string            GetRiskOption()
     {
      switch(RiskProfileOption)
        {
         case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
            return "MINIMUM LOTSIZE";
            break;
         case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
            return "MAXIMUM LOTSIZE";
            break;
         case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
            return "PERCENTAGE OF BALANCE";
            break;
         case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
            return "PERCENTAGE OF FREE-MARGIN";
            break;
         case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
            return "AMOUNT PER BALANCE";
            break;
         case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
            return "AMOUNT PER FREE-MARGIN";
            break;
         case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
            return "LOTSIZE PER BALANCE";
            break;
         case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
            return "LOTSIZE PER FREE-MARGIN";
            break;
         case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
            return "CUSTOM LOTSIZE";
            break;
         case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
            return "PERCENTAGE OF MAX-RISK";
            break;
         default:
            return "";
            break;
        }
     }

En la función GetRiskFloor, recuperaremos la opción Risk floor del usuario/operador en un string (cadena) de datos.

   //-- Retrieve user's Risk Floor Option
   string            GetRiskFloor()
     {
      switch(RiskFloorOption)
        {
         case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
            return "MINIMUM LOTSIZE";
            break;
         case RiskFloorMax://MAX-RISK for Risk floor options
            return "MAX-RISK";
            break;
         case RiskFloorNone://NONE for Risk floor options
            return "NONE";
            break;
         default:
            return "";
            break;
        }
     }

En la función GetRiskCeil, recuperaremos la opción Risk ceiling del usuario/operador en un tipo de datos string.

   //-- Retrieve user's Risk Ceiling option
   string            GetRiskCeil()
     {
      switch(RiskCeilOption)
        {
         case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
            return "MAX LOTSIZE";
            break;
         case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
            return "MAX LOTSIZE(x2)";
            break;
         case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
            return "MAX LOTSIZE(x3)";
            break;
         case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
            return "MAX LOTSIZE(x4)";
            break;
         case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
            return "MAX LOTSIZE(x5)";
            break;
         default:
            return "";
            break;
        }
     }

En el constructor de la clase Gestión de Riesgos inicializaremos las variables previamente declaradas con un valor por defecto. El valor por defecto de la variable ORDERTYPE es ORDER_TYPE_BUY, por lo que para las opciones de riesgo que requieran un tipo de orden para calcular el riesgo el tipo de orden será fijado por esta variable y se utilizará para simular los cálculos de riesgo en las operaciones de apertura. El precio abierto por defecto se almacenará en la variable OpenPrice y será el precio Ask del símbolo para nuestro ORDERTYPE. El precio de cierre por defecto será una desviación del 1% del precio Ask almacenado en la variable ClosePrice.

//Initialize values
CRiskManagement::CRiskManagement(void):Lots(0.0),max_percent(100),
   ORDERTYPE(ORDER_TYPE_BUY),OpenPrice(Ask()),
   ClosePrice(NormalizePrice(Ask()+Ask()*0.01))

  {
  }

La función volumen recuperará el tamaño de lote para la opción perfil de riesgo del usuario/operador y ajustará el tamaño de lote de la opción de riesgo seleccionada de acuerdo con la opción Risk Ceiling y la opción Risk Floor seleccionadas por el usuario/operador.

Después, el tamaño del lote se normalizará para que pueda abrirse una operación real con el tamaño de lote específico.

double CRiskManagement::Volume()
  {
   switch(RiskProfileOption)
     {
      case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
         return LotsMin();
         break;
      case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
         Lots = LotsMax();
         break;
      case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
         Lots = RiskProfile1(BALANCE);
         break;
      case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
         Lots = RiskProfile1(MARGIN);
         break;
      case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
         Lots = RiskProfile2(BALANCE);
         break;
      case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
         Lots = RiskProfile2(MARGIN);
         break;
      case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
         Lots =  RiskProfile3(BALANCE);
         break;
      case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
         Lots = RiskProfile3(MARGIN);
         break;
      case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
         Lots = Risk_Profile_4;
         break;
      case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
         Lots = MaxRisk(Risk_Profile_5);
         break;
      default:
         Lots = 0.0;
         break;
     }
   ValidateLotsize(Lots);//Check/Adjust Lotsize Limits
   NormalizeLotsize(Lots);//Normalize Lotsize
   return Lots;
  }

La función SetMedium asignará a la variable doble Medium el valor del balance de cuenta o del margen libre de cuenta del usuario/operador en base a la variable de enumeración R_Medium.

//-- Set Medium variable value
   void                SetMedium(const RiskMedium R_Medium) {Medium = (R_Medium==BALANCE)?Account.Balance():Account.FreeMargin();}

La función GetMinimumRisk asignará la variable MinimumAmount con el riesgo mínimo requerido para una operación específica con el tamaño de lote mínimo.

//-- Get Minimum Risk for a Trade using Minimum Lot-size
   bool                GetMinimumRisk()
     {
      return OrderCalcProfit(ORDERTYPE,Symbol(),LotsMin(),OpenPrice,ClosePrice,MinimumAmount);
     }

La función GetRisk obtendrá el tamaño de lote requerido para una cantidad de riesgo especificada en la variable Amount. Cuando se establece el MinimumAmount (importe mínimo de riesgo para una operación concreta). Amount se divide por MinimumAmount para obtener un cociente a multiplicar por el tamaño mínimo de lote con el fin de obtener el tamaño de lote requerido para Amount

//-- Retrieve Risk amount based on Risk inputs
   double            GetRisk(double Amount)
     {
      if(!GetMinimumRisk()||Amount==0)
         return 0.0;
      return ((Amount/MinimumAmount)*LotsMin());
     }

En la función RiskProfile1 calculamos y devolvemos el tamaño del lote para las opciones de riesgo PORCENTAJE DE BALANCE o PORCENTAJE DE MARGEN LIBRE.

//-- calculations for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
double CRiskManagement::RiskProfile1(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   RiskAmount = Medium*(Risk_Profile_1/100);
   return GetRisk(RiskAmount);
  }

En la función RiskProfile2 calculamos y devolvemos el tamaño del lote para las opciones de riesgo IMPORTE POR BALANCE o IMPORTE POR MARGEN LIBRE.

//-- calculations for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
double CRiskManagement::RiskProfile2(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   double risk = (Risk_Profile_2.RiskAmountBoF/Risk_Profile_2.RiskAmount);
   risk = (risk<1)?1:risk;

   if(Medium<=0)
      return 0.0;

   RiskAmount = Medium/risk;
   return GetRisk(RiskAmount);
  }

En la función RiskProfile3 calculamos y devolvemos el tamaño del lote para las opciones de riesgo TAMAÑO DE LOTE POR BALANCE o TAMAÑO DE LOTE POR MARGEN LIBRE.

//-- calculations for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
double CRiskManagement::RiskProfile3(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   return (Medium>0)?((Medium/Risk_Profile_3.RiskLotBoF)*Risk_Profile_3.RiskLot):0.0;
  }

En la función ValidateLotsize, se realizan ajustes en la variable Lotsize pasada por referencia. 

En la primera expresión de conmutación RiskFloorOption:

  • En el caso de RiskFloorMin: Comprobamos si la variable Lotsize está fuera de sus límites y le asignamos el tamaño de lote mínimo del símbolo actual. Comprobamos el límite inferior, es decir, si el valor de la variable es menor que el tamaño de lote mínimo. El límite superior se produce cuando la variable Lotsize está por encima del máximo riesgo posible.
  • En caso RiskFloorMax: Primero comprobamos si la variable Lotsize es mayor que el riesgo máximo posible, si lo es comprobamos si el riesgo mínimo máximo deseado es mayor que el tamaño de lote mínimo, si lo es asignamos Lotsize con el riesgo mínimo máximo deseado para esa operación, sino asignamos el tamaño de lote mínimo . Si Lotsize era inicialmente menor que el riesgo máximo posible y menor que el tamaño de lote mínimo, asignamos entonces el tamaño de lote mínimo. 
En la segunda expresión de conmutación RiskCeilOption, para cada caso comprobamos si Lotsize está por encima de cualquiera de los riesgos máximos basados en el valor del tamaño del lote y establecemos el valor de Lotsize en el límite del tamaño del lote si se alcanza.

void CRiskManagement::ValidateLotsize(double &Lotsize)
  {
   switch(RiskFloorOption)
     {
      case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
         //-- Check if lot-size is not less than Minimum lot or more than maximum allowable risk
         if(Lotsize<LotsMin()||Lotsize>MaxRisk(max_percent))
           {
            Lotsize=LotsMin();
           }
         break;
      case RiskFloorMax://MAX-RISK for Risk floor options
         //-- Check if lot-size is more the maximum allowable risk
         if(Lotsize>MaxRisk(max_percent))
           {
            Lotsize=(MaxRisk(RiskFloorPercentage)>LotsMin())?MaxRisk(RiskFloorPercentage):LotsMin();
           }
         else
            if(Lotsize<LotsMin())//Check if lot-size is less than Minimum lot
              {
               Lotsize=LotsMin();
              }
         break;
      case RiskFloorNone://NONE for Risk floor options
         //Check if lot-size is less than Minimum lot
         if(Lotsize<LotsMin())
           {
            Lotsize=0.0;
           }
         break;
      default:
         Lotsize=0.0;
         break;
     }

   switch(RiskCeilOption)
     {
      case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
         //Check if lot-size is more than Maximum lot
         if(Lotsize>LotsMax())
            Lotsize=LotsMax();
         break;
      case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times two
         if(Lotsize>(LotsMax()*2))
            Lotsize=(LotsMax()*2);
         break;
      case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times three
         if(Lotsize>(LotsMax()*3))
            Lotsize=(LotsMax()*3);
         break;
      case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times four
         if(Lotsize>(LotsMax()*4))
            Lotsize=(LotsMax()*4);
         break;
      case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times five
         if(Lotsize>(LotsMax()*5))
            Lotsize=(LotsMax()*5);
         break;
      default:
         break;
     }
  }

En la función NormalizeLotsize su propósito es comprobar si el tamaño del lote está dentro del límite de volumen del símbolo y que el tamaño del lote está de acuerdo con el paso de volumen.

Si el tamaño de lote viola el límite de volumen del símbolo, calculamos entonces los tamaños de lote restantes disponibles antes del límite de volumen. A continuación comprobamos si el tamaño de lote restante es mayor o igual que el tamaño de lote mínimo para el símbolo actual.

  • RiskFloorMin: Fijamos la variable Lotsize en el tamaño mínimo de lote.
  • RiskFloorMax: Si el RiskFloorPercentage del tamaño de lote restante es superior al tamaño de lote mínimo, fijamos la variable Lotsize en este valor. Si RiskFloorPercentage del tamaño de lote restante es menor o igual que el tamaño de lote mínimo, fijamos la variable Lotsize en el tamaño de lote mínimo. 

void CRiskManagement::NormalizeLotsize(double &Lotsize)
  {
   if(Lotsize<=0.0)
      return;

//-- Check if the is a Volume limit for the current Symbol
   if(LotsLimit()>0.0)
     {
      if((Lots+PositionsVolume()+OrdersVolume())>LotsLimit())
        {
         //-- calculation of available lotsize remaining
         double remaining_avail_lots = (LotsLimit()-(PositionsVolume()+OrdersVolume()));
         if(remaining_avail_lots>=LotsMin())
           {
            if(RiskFloorOption==RiskFloorMin)//Check if Risk floor option is MINIMUM LOTSIZE
              {
               Print("Warning: Volume Limit Reached, minimum Lotsize selected.");
               Lotsize = LotsMin();
              }
            else
               if(RiskFloorOption==RiskFloorMax)//Check if Risk floor option is MAX-RISK
                 {
                  Print("Warning: Volume Limit Reached, Lotsize Reduced.");
                  Lotsize = ((remaining_avail_lots*(RiskFloorPercentage/100))>LotsMin())?
                            (remaining_avail_lots*(RiskFloorPercentage/100)):LotsMin();
                 }
           }
         else
           {
            Print("Volume Limit Reached!");
            Lotsize=0.0;
            return;
           }
        }
     }

//Check if there is a valid Volume Step for the current Symbol
   if(LotsStep()>0.0)
      Lotsize=LotsStep()*MathFloor(Lotsize/LotsStep());
  }


Clase de gráficos comunes

La clase de gráficos comunes mostrará las propiedades generales del símbolo actual y algunas de las opciones de riesgo establecidas por el operador.

CommonGraphics tiene herencia multinivel de clases:

  • CObjectProperties
  • CChartProperties
  • CSymbolProperties

CommonGraphics tiene inclusión de la clase CRiskManagement.

CommonGraphics tiene herencia jerárquica de clases:

  • CSymbolProperties
  • CSymbolInfo

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "ObjectProperties.mqh"
#include "RiskManagement.mqh"
//+------------------------------------------------------------------+
//|CommonGraphics class                                              |
//+------------------------------------------------------------------+
class CCommonGraphics:CObjectProperties
  {
private:
   CRiskManagement   CRisk;//Risk management class object

public:
                     CCommonGraphics(void);//class constructor
                    ~CCommonGraphics(void) {}//class destructor
   void              GraphicsRefresh();//will create the chart objects
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CCommonGraphics::CCommonGraphics(void)
  {
   GraphicsRefresh();//calling GraphicsRefresh function
  }

//+------------------------------------------------------------------+
//|Specify Chart Objects                                             |
//+------------------------------------------------------------------+
void CCommonGraphics::GraphicsRefresh()
  {
//-- Will create the rectangle object
   Square(0,"Symbol Properties",2,20,330,183,ANCHOR_LEFT_UPPER);
//-- Will create the text object for the Symbol's name
   TextObj(0,"Symbol Name",Symbol(),5,23);
//-- Will create the text object for the contract size
   TextObj(0,"Symbol Contract Size","Contract Size: "+string(ContractSize()),5,40,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Minimum lotsize
   TextObj(0,"Symbol MinLot","Minimum Lot: "+string(LotsMin()),5,60,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Maximum lotsize
   TextObj(0,"Symbol MaxLot","Max Lot: "+string(LotsMax()),5,80,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Step
   TextObj(0,"Symbol Volume Step","Volume Step: "+string(LotsStep()),5,100,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Limit
   TextObj(0,"Symbol Volume Limit","Volume Limit: "+string(LotsLimit()),5,120,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Option
   TextObj(0,"Risk Option","Risk Option: "+CRisk.GetRiskOption(),5,140,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Floor
   TextObj(0,"Risk Floor","Risk Floor: "+CRisk.GetRiskFloor(),5,160,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Ceiling
   TextObj(0,"Risk Ceil","Risk Ceiling: "+CRisk.GetRiskCeil(),5,180,CORNER_LEFT_UPPER,9);
  }
//+------------------------------------------------------------------+

En la función GraphicsRefresh establecemos las propiedades para nuestros objetos de gráfico.

Objetos visibles del gráfico

Objetos del gráfico

void CCommonGraphics::GraphicsRefresh()
  {
//-- Will create the rectangle object
   Square(0,"Symbol Properties",2,20,330,183,ANCHOR_LEFT_UPPER);
//-- Will create the text object for the Symbol's name
   TextObj(0,"Symbol Name",Symbol(),5,23);
//-- Will create the text object for the contract size
   TextObj(0,"Symbol Contract Size","Contract Size: "+string(ContractSize()),5,40,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Minimum lotsize
   TextObj(0,"Symbol MinLot","Minimum Lot: "+string(LotsMin()),5,60,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Maximum lotsize
   TextObj(0,"Symbol MaxLot","Max Lot: "+string(LotsMax()),5,80,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Step
   TextObj(0,"Symbol Volume Step","Volume Step: "+string(LotsStep()),5,100,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Limit
   TextObj(0,"Symbol Volume Limit","Volume Limit: "+string(LotsLimit()),5,120,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Option
   TextObj(0,"Risk Option","Risk Option: "+CRisk.GetRiskOption(),5,140,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Floor
   TextObj(0,"Risk Floor","Risk Floor: "+CRisk.GetRiskFloor(),5,160,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Ceiling
   TextObj(0,"Risk Ceil","Risk Ceiling: "+CRisk.GetRiskCeil(),5,180,CORNER_LEFT_UPPER,9);
  }


Experto

Una vez más no abriremos ninguna operación en este artículo.
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
//--- width and height of the canvas (used for drawing)
#define IMG_WIDTH  200
#define IMG_HEIGHT 100
//--- enable to set color format
ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_XRGB_NOALPHA;
//--- drawing array (buffer)
uint ExtImg[IMG_WIDTH*IMG_HEIGHT];

#include "News.mqh"
CNews NewsObject;//Class CNews Object 'NewsObject'
#include "TimeManagement.mqh"
CTimeManagement CTM;//Class CTimeManagement Object 'CTM'
#include "WorkingWithFolders.mqh"
CFolders Folder();//Calling Class's Constructor
#include "ChartProperties.mqh"
CChartProperties CChart;//Class CChartProperties Object 'CChart'
#include "RiskManagement.mqh"
CRiskManagement CRisk;//Class CRiskManagement Object 'CRisk'
#include "CommonGraphics.mqh"
CCommonGraphics CGraphics();//Calling Class's Constructor

enum iSeparator
  {
   Delimiter//__________________________
  };

sinput group "+--------| RISK MANAGEMENT |--------+";
input RiskOptions RISK_Type=MINIMUM_LOT;//SELECT RISK OPTION
input RiskFloor RISK_Mini=RiskFloorMin;//RISK FLOOR
input double RISK_Mini_Percent=75;//MAX-RISK [100<-->0.01]%
input RiskCeil  RISK_Maxi=RiskCeilMax;//RISK CEILING
sinput iSeparator iRisk_1=Delimiter;//__________________________
sinput iSeparator iRisk_1L=Delimiter;//PERCENTAGE OF [BALANCE | FREE-MARGIN]
input double Risk_1_PERCENTAGE=3;//[100<-->0.01]%
sinput iSeparator iRisk_2=Delimiter;//__________________________
sinput iSeparator iRisk_2L=Delimiter;//AMOUNT PER [BALANCE | FREE-MARGIN]
input double Risk_2_VALUE=1000;//[BALANCE | FREE-MARGIN]
input double Risk_2_AMOUNT=10;//EACH AMOUNT
sinput iSeparator iRisk_3=Delimiter;//__________________________
sinput iSeparator iRisk_3L=Delimiter;//LOTSIZE PER [BALANCE | FREE-MARGIN]
input double Risk_3_VALUE=1000;//[BALANCE | FREE-MARGIN]
input double Risk_3_LOTSIZE=0.1;//EACH LOTS(VOLUME)
sinput iSeparator iRisk_4=Delimiter;//__________________________
sinput iSeparator iRisk_4L=Delimiter;//CUSTOM LOTSIZE
input double Risk_4_LOTSIZE=0.01;//LOTS(VOLUME)
sinput iSeparator iRisk_5=Delimiter;//__________________________
sinput iSeparator iRisk_5L=Delimiter;//PERCENTAGE OF MAX-RISK
input double Risk_5_PERCENTAGE=1;//[100<-->0.01]%

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//Initializing CRiskManagement variable for Risk options
   RiskProfileOption = RISK_Type;
//Initializing CRiskManagement variable for Risk floor
   RiskFloorOption = RISK_Mini;
//Initializing CRiskManagement variable for RiskFloorMax
   RiskFloorPercentage = (RISK_Mini_Percent>100)?100:
                         (RISK_Mini_Percent<0.01)?0.01:RISK_Mini_Percent;//Percentage cannot be more than 100% or less than 0.01%
//Initializing CRiskManagement variable for Risk ceiling
   RiskCeilOption = RISK_Maxi;
//Initializing CRiskManagement variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
   Risk_Profile_1 = (Risk_1_PERCENTAGE>100)?100:
                    (Risk_1_PERCENTAGE<0.01)?0.01:Risk_1_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01%
//Initializing CRiskManagement variables for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
   Risk_Profile_2.RiskAmountBoF = Risk_2_VALUE;
   Risk_Profile_2.RiskAmount = Risk_2_AMOUNT;
//Initializing CRiskManagement variables for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
   Risk_Profile_3.RiskLotBoF = Risk_3_VALUE;
   Risk_Profile_3.RiskLot = Risk_3_LOTSIZE;
//Initializing CRiskManagement variable for Risk option (CUSTOM LOTSIZE)
   Risk_Profile_4 = Risk_4_LOTSIZE;
//Initializing CRiskManagement variable for Risk option (PERCENTAGE OF MAX-RISK)
   Risk_Profile_5 = (Risk_5_PERCENTAGE>100)?100:
                    (Risk_5_PERCENTAGE<0.01)?0.01:Risk_5_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01%

   CChart.ChartRefresh();//Load chart configurations
   CGraphics.GraphicsRefresh();//-- Create/Re-create chart objects

   if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester
     {
      //--- create OBJ_BITMAP_LABEL object for drawing
      ObjectCreate(0,"STATUS",OBJ_BITMAP_LABEL,0,0,0);
      ObjectSetInteger(0,"STATUS",OBJPROP_XDISTANCE,5);
      ObjectSetInteger(0,"STATUS",OBJPROP_YDISTANCE,22);
      //--- specify the name of the graphical resource
      ObjectSetString(0,"STATUS",OBJPROP_BMPFILE,"::PROGRESS");
      uint   w,h;          // variables for receiving text string sizes
      uint    x,y;          // variables for calculation of the current coordinates of text string anchor points

      /*
      In the Do while loop below, the code will check if the terminal is connected to the internet.
      If the the program is stopped the loop will break, if the program is not stopped and the terminal
      is connected to the internet the function CreateEconomicDatabase will be called from the News.mqh header file's
      object called NewsObject and the loop will break once called.
      */
      bool done=false;
      do
        {
         //--- clear the drawing buffer array
         ArrayFill(ExtImg,0,IMG_WIDTH*IMG_HEIGHT,0);

         if(!TerminalInfoInteger(TERMINAL_CONNECTED))
           {
            //-- integer dots used as a loading animation
            static int dots=0;
            //--- set the font
            TextSetFont("Arial",-150,FW_EXTRABOLD,0);
            TextGetSize("Waiting",w,h);//get text width and height values
            //--- calculate the coordinates of the 'Waiting' text
            x=10;//horizontal alignment
            y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
            //--- output the 'Waiting' text to ExtImg[] buffer
            TextOut("Waiting",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CChart.SymbolBackground()),clr_format);
            //--- calculate the coordinates for the dots after the 'Waiting' text
            x=w+13;//horizontal alignment
            y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
            TextSetFont("Arial",-160,FW_EXTRABOLD,0);
            //--- output of dots to ExtImg[] buffer
            TextOut(StringSubstr("...",0,dots),x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CChart.SymbolBackground()),clr_format);
            //--- update the graphical resource
            ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format);
            //--- force chart update
            ChartRedraw();
            dots=(dots==3)?0:dots+1;
            //-- Notify user that program is waiting for connection
            Print("Waiting for connection...");
            Sleep(500);
            continue;
           }
         else
           {
            //--- set the font
            TextSetFont("Arial",-120,FW_EXTRABOLD,0);
            TextGetSize("Getting Ready",w,h);//get text width and height values
            x=20;//horizontal alignment
            y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
            //--- output the text 'Getting Ready...' to ExtImg[] buffer
            TextOut("Getting Ready...",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CChart.SymbolBackground()),clr_format);
            //--- update the graphical resource
            ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format);
            //--- force chart update
            ChartRedraw();
            //-- Notify user that connection is successful
            Print("Connection Successful!");
            NewsObject.CreateEconomicDatabase();//calling the database create function
            done=true;
           }
        }
      while(!done&&!IsStopped());
      //-- Delete chart object
      ObjectDelete(0,"STATUS");
      //-- force chart to update
      ChartRedraw();
     }
   else
     {
      //Checks whether the database file exists
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))
        {
         Print("Necessary Files Do not Exist!");
         Print("Run Program outside of the Strategy Tester");
         Print("Necessary Files Should be Created First");
         return(INIT_FAILED);
        }
      else
        {
         //Checks whether the lastest database date includes the time and date being tested
         datetime latestdate = CTM.TimePlusOffset(NewsObject.GetLatestNewsDate(),CTM.DaysS());//Day after the lastest recorded time in the database
         if(latestdate<TimeCurrent())
           {
            Print("Necessary Files outdated!");
            Print("To Update Files: Run Program outside of the Strategy Tester");
           }
         Print("Database Dates End at: ",latestdate);
         PrintFormat("Dates after %s will not be available for backtest",TimeToString(latestdate));
        }
     }
//-- the volume calculations and the risk type set by the trader
   Print("Lots: ",CRisk.Volume()," || Risk type: ",CRisk.GetRiskOption());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

Archivos del proyecto


Experto


Una vez que todo esté compilado, repasaremos algunos pasos que se producirán una vez que el Experto se coloque en el gráfico.

Gráfico US30

Una vez que decida qué ventana del gráfico de símbolos abrir, su gráfico podría verse similar al gráfico anterior antes de adjuntar el Experto.

Ahora adjuntaremos el Experto.

Adjuntar al Experto

Primero podemos configurar las variables de entrada de Gestión de Riesgos, dejaré los valores por defecto para empezar.

Entradas del experto

Si es la primera vez que ejecuta NewsTrading 2.00 y no ejecutó previamente NewsTrading 1.00 en su corredor y la base de datos del Calendario no existe en la carpeta común.

Su gráfico aparecerá de esta manera, los colores del gráfico pueden variar de un corredor a otro.

Gráfico del experto nº 1

Como se ve en el mensaje siguiente, los lotes se mostrarán según las opciones de riesgo seleccionadas en la configuración.

Gráfico del experto nº 1 (Mensaje)


Si el terminal no puede hallar una conexión.


Cuando se ejecuta NewsTrading 2.00 por primera vez y la base de datos del calendario se creó a partir de NewsTrading 1.00 Expert.

NewsTrading 2.00 borrará todas las tablas de NewsTrading 1.00

Experto adjunto y calendario DD existe desde la versión anterior


Conclusión

En este artículo exploramos cómo funciona la herencia, con un ejemplo a modo ilustrativo. Creamos una nueva clase de horario de verano para que actúe como padre de las diferentes clases de programación de horario de verano. Creamos una clase de propiedades de símbolo para recuperar propiedades de símbolo de todas las clases que heredan de ella. También creamos una clase de propiedades de gráfico para personalizar el gráfico. Revisamos diferentes metodologías SQLite y una forma sencilla de mejorar la eficiencia general de la base de datos. Creamos una clase de propiedades de objeto para crear objetos gráficos y eliminarlos, luego creamos una clase de gestión de riesgos para atender diferentes perfiles de riesgo y comerciantes. Finalmente, creamos la última clase llamada 'Common Graphics' para mostrar las propiedades del símbolo en el gráfico junto con las opciones de riesgo del operador. En el próximo artículo, 'Operar con noticias de manera sencilla (Parte 3): Ejecutando operaciones' finalmente comenzaremos a abrir operaciones. ¡Estoy ansioso por terminarlo dentro de un plazo razonable!


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14912

Archivos adjuntos |
Desarrollo de un sistema de repetición (Parte 59): Un nuevo futuro Desarrollo de un sistema de repetición (Parte 59): Un nuevo futuro
La correcta comprensión de las cosas nos permite hacer más con menos esfuerzo. En este artículo, explicaré por qué es necesario ajustar la aplicación de la plantilla antes de que el servicio comience a interactuar realmente con el gráfico. Además, ¿qué tal si mejoramos el indicador del mouse para que podamos hacer más cosas con él?
Visualización de transacciones en un gráfico (Parte 1): Seleccionar un periodo para el análisis Visualización de transacciones en un gráfico (Parte 1): Seleccionar un periodo para el análisis
Aquí vamos a desarrollar un script desde cero que simplifica la descarga de pantallas de impresión de transacciones para analizar entradas comerciales. Toda la información necesaria sobre una única operación se puede mostrar cómodamente en un gráfico con la posibilidad de dibujar diferentes marcos temporales.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Aprendiendo MQL5 de principiante a profesional (Parte III): Tipos de datos complejos y archivos de inclusión Aprendiendo MQL5 de principiante a profesional (Parte III): Tipos de datos complejos y archivos de inclusión
Este artículo es el tercero de una serie de materiales sobre los principales aspectos de la programación en MQL5. Aquí nos encargaremos de tipos de datos complejos que no describimos en el artículo anterior, como estructuras, uniones, clases y el tipo de datos "función". También veremos cómo añadir modularidad a nuestro programa utilizando la directiva #include del preprocesador.