
Simplificando a negociação com base em notícias (Parte 2): Gerenciando riscos
Introdução
Em primeiro lugar, veremos um resumo do artigo anterior da série 'Simplificando a negociação com base em notícias'. Na primeira parte, examinamos o conceito de horário de verão (DST) e as diferentes versões para diferentes países que mudam seus fusos horários uma hora para frente e para trás durante o ano fiscal. Isso, por sua vez, altera os gráficos de negociação das respectivas corretoras que usam o horário de verão. Examinamos os motivos para criar o banco de dados e suas vantagens. Criamos um banco de dados para armazenar notícias do Calendário Econômico MQL5 com alterações posteriores nos dados de horário do evento para que se refletissem no gráfico de verão da corretora, de modo a permitir testes precisos no histórico no futuro. Fornecemos os resultados do script SQL em formato Excel - nos arquivos do projeto - para todos os eventos exclusivos disponíveis no calendário MQL5 para os diferentes países.
Neste artigo, faremos algumas alterações em nosso código. Primeiramente, incluiremos a herança no código existente e no novo. Como resultado, o banco de dados de notícias/calendário se tornará mais prático e cômodo de usar. Além disso, abordaremos o gerenciamento de riscos e criaremos diferentes perfis de risco para usuários com diferentes apetites de risco.
Herança
O que é herança?
A Herança é um conceito fundamental da programação orientada a objetos (OOP) que permite que uma nova classe (chamada de subclasse ou classe derivada) herde as propriedades e o comportamento (campos e métodos) de uma classe existente (chamada de superclasse ou classe base). Esse mecanismo facilita a criação de uma nova classe mediante a ampliação ou modificação do comportamento de uma classe existente, o que ajuda na reutilização do código e na elaboração de uma estrutura de classes mais lógica e hierárquica.
Qual é o propósito da herança?
A herança é uma forma de reutilizar o código existente. Ao herdar da classe base, uma subclasse pode usar métodos e campos existentes sem precisar reescrevê-los. Isso reduz a redundância e simplifica o manuseio do código. A herança ajuda a organizar o código em uma estrutura hierárquica que é mais fácil de gerenciar. As classes podem ser agrupadas com base em atributos e comportamentos comuns, que resultam em uma organização clara e lógica da base de código. A herança está intimamente relacionada ao polimorfismo, que permite que objetos de classes diferentes sejam tratados como objetos de uma superclasse comum. Isso é particularmente útil para implementar códigos flexíveis e extensíveis. Por exemplo, uma função pode trabalhar com objetos de classes diferentes se eles herdarem a mesma classe base, o que permite a vinculação dinâmica de métodos e um código mais generalizado.
A herança permite ampliar a funcionalidade de classes existentes. As subclasses podem adicionar novos métodos e campos ou sobrescrever os já existentes para impor um comportamento específico, sem modificar a classe original. Isso favorece o princípio de abertura/fechamento, em que as classes são abertas para serem ampliadas, mas fechadas para serem modificadas. A herança também mantém a encapsulação, permitindo que a subclasse acesse membros protegidos e públicos da superclasse, enquanto preserva os detalhes de implementação privados. Isso proporciona uma separação clara entre a interface e a implementação, aumentando a modularidade e reduzindo o risco de interações não intencionais.
O que são modificadores de acesso?
Os modificadores de acesso são palavras-chave em linguagens de programação orientadas a objetos que definem a acessibilidade de classes, métodos/funções e outros membros. Eles controlam a visibilidade e acessibilidade desses elementos em diferentes partes do código, mantendo a encapsulação e protegendo a integridade dos dados.
Tipos de modificadores de acesso
- Públicos (Public)
- Privados (Private)
- Protegidos (Protected)
1. Públicos
Propósito: permitir que uma classe, função ou variável possa ser usada em outras classes ou programas.
2. Privados
Propósito: restringir o acesso aos membros da classe, para proteger a integridade dos dados.
3. Protegidos
Propósito: permitir que as subclasses herdem e acessem os membros, enquanto limitam o acesso a partir de objetos da classe.
Exemplo de herança relacionado ao MQL5
Primeiro, criaremos um diagrama de classe UML para visualizar as classes, seus relacionamentos e atributos.
As classes UnitedStates e Switzerland têm uma única herança da classe 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 };
Neste exemplo:
NewsData atua como a classe pai/base/superclasse, e quaisquer declarações privadas estarão acessíveis apenas a essa classe. As declarações privadas não estarão acessíveis para objetos e descendentes da classe. Ao mesmo tempo, as declarações protegidas estarão acessíveis tanto à classe quanto a seus descendentes. Todas as declarações públicas estarão acessíveis à classe, seus descendentes e objetos.
Tabela de acessibilidade para NewsData:
Propriedades da classe | Classe | Descendentes | Objetos |
---|---|---|---|
Variável: Country(Private) | ✔ | ✘ | ✘ |
Estrutura: EventDetails(Private) | ✔ | ✘ | ✘ |
Variável: News(Protected) | ✔ | ✔ | ✘ |
Função: SetNews(Protected) | ✔ | ✔ | ✘ |
Função: SetCountry(Protected) | ✔ | ✔ | ✘ |
Função: GetNews(Public) | ✔ | ✔ | ✔ |
Construtor: NewsData(Public) | ✔ | ✘ | ✘ |
Destrutor: ~NewsData(Public) | ✔ | ✘ | ✘ |
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 };
Propriedades visíveis a partir do objeto NewsData:
Resultado da função GetNews em NewsData:
A herança foi implementada com ambas as classes restantes:
Na subclasse derivada UnitedStates, ela herda a classe pai (NewsData) de forma privada.
Isso significa que a subclasse (UnitedStates) pode acessar as propriedades protegidas e públicas da classe pai (NewsData), mas os descendentes da classe UnitedStates e seus objetos não terão acesso a nenhuma propriedade da classe pai (NewsData). Se o modificador de acesso da herança fosse protegido, os descendentes da classe UnitedStates teriam acesso às propriedades protegidas e públicas da classe pai (NewsData), mas os objetos da subclasse (UnitedStates) não teriam acesso às propriedades da classe pai.
Tabela de acessibilidade para UnitedStates:
Propriedades da classe | Classe | Descendentes | Objetos |
---|---|---|---|
Variável herdada (privada): Country(Private) | ✘ | ✘ | ✘ |
Estrutura herdada (privada): EventDetails(Private) | ✘ | ✘ | ✘ |
Variável herdada (privada): News(Protected) | ✔ | ✘ | ✘ |
Função herdada (privada): SetNews(Protected) | ✔ | ✘ | ✘ |
Função herdada (privada): SetCountry(Protected) | ✔ | ✘ | ✘ |
Função herdada (privada): GetNews(Public) | ✔ | ✘ | ✘ |
Construtor herdado (privado): NewsData(Public) | ✘ | ✘ | ✘ |
Destrutor herdado (privado): ~NewsData(Public) | ✘ | ✘ | ✘ |
Função: SetNews(Private) | ✔ | ✘ | ✘ |
Função: myNews(Public) | ✔ | ✔ | ✔ |
Construtor: UnitedStates(Public) | ✔ | ✘ | ✘ |
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 };
Propriedades visíveis a partir do objeto UnitedStates:
Erro de compilação ocorre ao tentar acessar a função GetNews, que é herdada de forma privada de NewsData. Isso impede o acesso à função do objeto UnitedStates.
Na subclasse derivada Switzerland.
O modificador de acesso à herança é público. Isso permite que os descendentes da subclasse (Switzerland) tenham acesso às propriedades públicas e protegidas da classe pai (NewsData), enquanto os objetos da classe Switzerland têm acesso apenas às propriedades públicas de todas as classes associadas.
Tabela de acessibilidade para Switzerland:
Propriedades da classe | Classe | Descendentes | Objetos |
---|---|---|---|
Variável herdada (pública): Country(Private) | ✘ | ✘ | ✘ |
Estrutura herdada (pública): EventDetails(Private) | ✘ | ✘ | ✘ |
Variável herdada (pública): News(Protected) | ✔ | ✔ | ✘ |
Função herdada (pública): SetNews(Protected) | ✔ | ✔ | ✘ |
Função herdada (pública): SetCountry(Protected) | ✔ | ✔ | ✘ |
Função herdada (pública): GetNews(Public) | ✔ | ✔ | ✔ |
Construtor herdado (público): NewsData(Public) | ✘ | ✘ | ✘ |
Destrutor herdado (público): ~NewsData(Public) | ✘ | ✘ | ✘ |
Função: SetNews(Public) | ✔ | ✔ | ✔ |
Construtor: Switzerland(Public) | ✔ | ✘ | ✘ |
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 };
Propriedades visíveis a partir do objeto Switzerland:
Resultados:
Classes de horário de verão
Na primeira parte da série “Simplificando a negociação com base em notícias”:
Diagrama de classes UML
Arquivos do projeto:
No código anterior, tínhamos três classes de horário de verão, que são:
- CDaylightSavings_AU
- CDaylightSavings_UK
- CDaylightSavings_US
Na Parte 2:
Diagrama de classes UML
Arquivos do projeto:
Teremos as seguintes classes para horário de verão:
- CDaylightSavings
- CDaylightSavings_AU
- CDaylightSavings_UK
- CDaylightSavings_US
Por que criar mais uma classe?
Havia muita repetição no código das classes anteriores, e era essencialmente o mesmo código reescrito para valores diferentes na lista. Em vez de repetir classes semelhantes, agruparemos todas as características comuns em uma classe separada e herdaremos essas características comuns nas diferentes classes de horário de verão.
O que são funções virtuais?
Na programação orientada a objetos (OOP), uma função virtual é uma função-membro de uma classe base que pode ser sobrescrita na classe derivada. Quando uma função é declarada como virtual, ela permite o polimorfismo, possibilitando que a classe derivada forneça uma implementação específica da função que pode ser chamada mediante um ponteiro ou referência da classe base.
Por que isso é feito?
- Polimorfismo: as funções virtuais permitem o polimorfismo dinâmico. Isso significa que o método a ser executado é determinado em tempo de execução com base no tipo real do objeto referenciado, e não no tipo da referência ou ponteiro.
- Flexibilidade: fornece um código mais flexível e reutilizável, permitindo que classes derivadas modifiquem ou ampliem o comportamento da classe base.
- Separação: as funções virtuais ajudam a separar o código, dividindo a interface da implementação, o que facilita a alteração da implementação sem impactar o código que usa a interface da classe base.
Classe CDaylightSavings
Nesta classe, todas as características comuns do código anterior foram reunidas em uma única classe, e declaramos várias funções virtuais para inicializar diferentes listas para o respectivo cronograma de horário de verão.
A classe CDaylightSavings possui herança única da classe CObject.
A classe CDaylightSavings inclui as seguintes classes:
- 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 } //+------------------------------------------------------------------+
Classe CDaylightSavings_AU
Aqui, expandimos o método virtual void SetDaylightSavings_AU e começamos a adicionar o cronograma de horário de verão para a Austrália.
As datas de horário de verão para a Austrália foram encontradas aqui.
A classe CDaylightSavings_UK possui herança em múltiplos níveis das classes:
- CDaylightSavings
- CObject
A classe CDaylightSavings_UK apresenta herança hierárquica das classes:
- 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(); } //+------------------------------------------------------------------+
Classe CDaylightSavings_US
Aqui, expandimos o método virtual void SetDaylightSavings_US e começamos a adicionar o cronograma de horário de verão para os Estados Unidos.
As datas de horário de verão para os EUA foram encontradas aqui.
A classe CDaylightSavings_US possui herança em múltiplos níveis das classes:
- CDaylightSavings
- CObject
A classe CDaylightSavings_US apresenta herança hierárquica das classes:
- 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(); } //+------------------------------------------------------------------+
Classe CDaylightSavings_US
Aqui, estendemos o void virtual SetDaylightSavings_US e continuamos a adicionar o horário de verão para os EUA.
As datas do horário de verão nos EUA foram encontradas aqui.
A classe CDaylightSavings_US tem vários níveis de herança de classes:
- CDaylightSavings
- CObject
A classe CDaylightSavings_US tem herança hierárquica de classes:
- 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(); } //+------------------------------------------------------------------+
Classe de propriedades de símbolo
Nesta classe, configuraremos o símbolo do qual gostaríamos de extrair dados. A classe nos fornecerá um método simples e eficiente para obter as propriedades do símbolo em outros classes, eliminando assim a redundância em nosso código.
Escolheremos uma combinação de propriedades para gerar uma lista que pode ser expandida. Caso contrário, a lista será assim:
- Ask Price (preço Ask)
- Bid Price (preço Bid)
- Contract Size (tamanho do contrato)
- Minimum Volume (volume mínimo)
- Maximum Volume (volume máximo)
- Volume Step (passo de volume)
- Volume Limit (limite de volume)
- Spread (spread)
- Stops Level (nível de stops)
- Freeze Level (nível de congelamento)
- Symbol's Time (tempo do símbolo)
- Symbol's Normalized Price (preço normalizado do símbolo)
- Symbol's Digits (número de dígitos do símbolo)
- Symbol's Point (ponto do símbolo)
- Symbol's Trade Mode (modo de negociação do símbolo)
- Sum of Symbol's Orders' Volume (volume total das ordens do símbolo)
- Sum of Symbol's Positions' Volume (volume total das posições do símbolo)
- Symbol's Currency Base (moeda base do símbolo)
- Symbol's Currency Profit (moeda de lucro do símbolo)
- Symbol's Currency Margin (moeda de margem do símbolo)
- Symbol's Custom Status (status personalizado do símbolo)
- Symbol's Background Color (cor de fundo do símbolo)
A classe CSymbolProperties inclui a classe 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 } //+------------------------------------------------------------------+
No construtor da classe abaixo, inicializamos as variáveis que declaramos anteriormente, como a variável double ASK. Definimos o valor de ASK como 0.0, já que ainda não temos o preço Ask para o 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) { }
No código abaixo, realizaremos uma série de etapas para finalmente obter o preço Ask do símbolo.
1. Primeiro, temos um parâmetro opcional que permite decidir se introduziremos ou editaremos o nome do símbolo na variável SYMBOL.
2. Em seguida, definimos o nome do símbolo conforme o valor do parâmetro. Se o valor do parâmetro ainda for o valor padrão NULL, assumimos que precisamos das propriedades do símbolo atual do gráfico - Symbol().
3. Se não conseguirmos encontrar o nome do símbolo, exibiremos uma mensagem de erro para notificar o usuário de que o preço Ask do símbolo não pode ser obtido e retornaremos 0.0.
4. Assim que pudermos definir o nome do símbolo, obteremos o preço Ask para esse símbolo específico e retornaremos o 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;
}
A função abaixo extrai o preço Bid do símbolo com base na variável SYMBOL.
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; }
A função abaixo extrai o tamanho do contrato do símbolo. O tamanho do contrato do símbolo impacta o trader, pois um tamanho de contrato maior aumenta o risco de negociações individuais. Um tamanho de contrato menor reduzirá o risco de negociações individuais.
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; }
A função abaixo extrai o tamanho mínimo de lote/volume permitido para o símbolo. Isso significa que o trader não pode abrir uma posição com um tamanho de lote inferior ao 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; }
A função abaixo extrai o tamanho máximo de lote/volume permitido para o símbolo. Isso significa que o trader não poderá abrir uma posição com um tamanho de lote/volume que exceda o máximo, embora possa abrir várias posições que, em conjunto, possam ultrapassar o valor máximo, dependendo do limite de volume da corretora e do limite de ordens da conta.

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; }
A função abaixo extrai o passo de volume/tamanho de lote do símbolo. Isso significa que o tamanho do lote deve ter um intervalo especificado. Por exemplo, se o passo de volume for 1, o trader não poderá escolher um tamanho de lote/volume de 1,5.

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; }
A função abaixo extrai o limite de volume/tamanho de lote do símbolo. Este é o total de volumes/tamanhos de lotes permitidos antes que restrições sejam impostas à conta do trader para esse símbolo específico.

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; }
A função abaixo extrai o spread do símbolo. O spread afeta os traders, pois quanto maior o spread do símbolo, menos lucrativa será a negociação. Dependendo do spread, uma estratégia pode ser lucrativa ou não. É evidente que há várias circunstâncias que podem tornar uma estratégia não lucrativa, mas o spread do símbolo pode desempenhar um papel significativo. Os spreads são uma fonte de receita para a corretora. E podem ser vistos como uma taxa cobrada pela corretora sobre esse símbolo.
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; }
A função abaixo extrai o nível de stops do símbolo. Este é um limite na distância mínima entre o preço de abertura e o stop loss ou take profit, bem como na distância mínima entre o preço atual de Ask ou Bid e o preço de abertura da ordem.
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; }
A função abaixo extrai o nível de congelamento do símbolo. Esta é a distância mínima que o preço deve mover a partir do preço de abertura para que a negociação possa ser fechada (quando é permitido fechar uma negociação específica com lucro ou prejuízo).
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; }
A função abaixo extrai o tempo do símbolo.
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); }
A função abaixo tentará normalizar o preço para um símbolo específico.
Por exemplo, o preço Ask para EURUSD é 1,07735, e você tenta abrir uma negociação de compra a 1,077351. Você pode receber o erro "Preço inválido" porque o número de dígitos decimais excede o limite permitido, como 5 dígitos. Esta função pegará o preço com 6 dígitos e o converterá em 5 dígitos, normalizando assim o preço.
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; }
A função abaixo extrai os dígitos do símbolo. Os dígitos são representados como casas decimais do preço do símbolo.
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; }
A função abaixo extrai o ponto do 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; }
A função abaixo extrai o modo de negociação do símbolo.
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; }
A função abaixo extrai o volume total/número de lotes das ordens do 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; }
A função abaixo extrai o volume total/número de lotes das posições do 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; }
A função abaixo extrai a moeda base do 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 ""; }
A função abaixo extrai a moeda de lucro do símbolo.
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 ""; }
A função abaixo extrai a moeda de margem do símbolo.
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 ""; }
A função abaixo extrai um valor booleano para determinar se o símbolo é personalizado ou não.
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; }
A função abaixo extrai a cor de fundo do símbolo. A função possui um parâmetro opcional allow_black, que por padrão é false, pois usaremos a cor de fundo do símbolo para definir a cor de fundo do gráfico mais adiante, e não queremos uma cor preta, uma vez que outros elementos do nosso gráfico também serão pretos. Se permitíssemos o uso da cor preta, de acordo com o formato presumido do gráfico, isso o tornaria ilegível.
Exemplo de gráfico com fundo preto usando nosso novo formato, que será configurado posteriormente.
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 }
Classe de gerenciamento de tempo
Esta classe demonstra novas funcionalidades. O objetivo da classe é gerenciar dados relacionados ao tempo.
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Classe de propriedades do gráfico
O objetivo das propriedades do gráfico é salvar suas propriedades antes de fazer alterações. Após a remoção do EA do gráfico, o destrutor da classe restaurará o estado original antes das modificações.
Alterar o gráfico não é obrigatório para o funcionamento do EA, mas é visualmente mais agradável trabalhar com um gráfico onde os preços e/ou os níveis de negociação são claramente visíveis após a execução das negociações.
A classe CChartProperties possui herança única da classe CSymbolProperties.
A classe CChartProperties apresenta herança hierárquica das classes:
- 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 } //+------------------------------------------------------------------+
Na estrutura ChartFormat que declaramos, armazenamos várias variáveis do gráfico que modificaremos no gráfico atual onde o EA está rodando.
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 };
O array ChartConfig armazenará todas as propriedades do gráfico antes de fazermos qualquer modificação.
ulong ChartConfig[65];//Array To Store Chart Properties
Na função SetBackground, obtemos a cor de fundo do MarketWatch do símbolo atual:
e definimos a cor de fundo do gráfico atual para a cor obtida:
No construtor da classe, obtemos todas as propriedades do gráfico do tipo Integer e as armazenamos no 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(); }
Também inicializamos a variável Chart, que possui o tipo de estrutura ChartFormat mencionado anteriormente, e atribuimos os valores correspondentes para configurar o gráfico ao nosso gosto na função 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 }
Na função ChartSet, definimos os valores das propriedades selecionadas do gráfico a partir da variável Chart, do tipo estrutura 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 }
No destrutor, restauramos os valores inteiros do 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 } }
Classe de propriedades de velas
A classe CCandleProperties possui herança em múltiplos níveis das classes:
- CChartProperties
- CSymbolProperties
A classe CCandleProperties inclui a classe CTimeManagement.
A classe CCandleProperties apresenta herança hierárquica das classes:
- 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 } //+------------------------------------------------------------------+
Classe de propriedades de objeto
Esta classe será responsável pela criação e remoção de objetos no gráfico.
A classe CObjectProperties possui herança em múltiplos níveis das classes:
- CChartProperties
- CSymbolProperties
- 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); } } //+------------------------------------------------------------------
A variável de array Objects armazenará todos os objetos do gráfico criados na classe CObjectProperties.
struct ObjStruct { long ChartId; string Name; } Objects[];//ObjStruct variable array
A função AddObj adiciona o identificador do objeto do gráfico e o nome do objeto no 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; }
O objetivo da função Square é criar um objeto Rectangle com propriedades específicas, permitindo personalização.
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); } }
A função TextObj cria objetos de texto no 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); } }
A função EventObj cria objetos de eventos no gráfico para exibir eventos econômicos esperados ou que já ocorreram.
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); } }
Classe de notícias
Tabelas de calendário na Parte 1:
Em nosso banco de dados anterior da Parte 1, utilizávamos um arquivo grande e salvar todas as notícias no banco de dados consumia bastante tempo. Isso se devia ao armazenamento ineficiente dos dados de notícias. O maior impacto no tamanho do arquivo e na queda de desempenho era causado pela repetição de dados semelhantes.
Tabelas:
- Data_AU
- Data_None
- Data_UK
- Data_US
armazenavam dados de notícias semelhantes, diferenciando-se apenas pelo tempo.
Nova estrutura:
Conteúdo do calendário na Parte 2:
Com a nova estrutura, o conteúdo do banco de dados será o seguinte:
- AutoDST Table
- Calendar_AU View
- Calendar_NONE View
- Calendar_UK View
- Calendar_US View
- MQL5Calendar Table
- Record Table
- TimeSchedule Table
- OnlyOne_AutoDST Trigger
- OnlyOne_Record Trigger
Vamos normalizar as tabelas do banco de dados anterior. Estas são as tabelas Data_AU, Data_None, Data_UK e Data_US.
O que é a normalização de banco de dados?
A normalização de banco de dados é a organização de um banco de dados em tabelas e colunas para minimizar a redundância e dependência. Os principais objetivos são eliminar dados redundantes (como armazenar as mesmas informações em várias tabelas) e garantir a lógica das dependências dos dados (armazenando apenas dados relacionados em uma tabela). Como resultado, obtemos um conjunto de tabelas mais fácil de gerenciar, com menos probabilidade de sofrer de anomalias de dados.
Também criaremos triggers (gatilhos) para garantir que apenas um registro seja armazenado nas tabelas AutoDST e Record. Além disso, criaremos views (visões) para cada horário de verão (DST) a fim de mostrar as notícias do último dia atualizado. Idealmente, isso facilitará a visualização dos eventos de notícias que foram relevantes, sem a necessidade de fazer consultas repetidas em tabelas com milhares de registros.
O que é um trigger?
Um trigger no SQLite é um tipo especial de procedimento armazenado que executa automaticamente um conjunto específico de ações em resposta a eventos em uma tabela específica. Esses eventos podem ser a inserção, atualização ou exclusão de linhas na tabela.
O que é um view?
Um view no SQLite é uma tabela virtual baseada no conjunto de resultados de uma consulta SELECT. Diferente de uma tabela, um view não armazena dados fisicamente. Em vez disso, ele fornece dados de uma ou mais tabelas em uma estrutura, ou formato específico, o que frequentemente simplifica consultas complexas e melhora a segurança dos dados.
Antes de começarmos a criar novas tabelas e expandir ainda mais o banco de dados, precisamos identificar quais tabelas devem ser removidas e quais devem ser mantidas. Uma solução simples seria verificar cada tabela conhecida no banco de dados anterior, incluindo Data_AU e outras. No entanto, não podemos simplesmente codificar rigidamente quais tabelas devem ser descartadas. Nossa aplicação deve ser capaz de localizar essas tabelas automaticamente. Para isso, precisamos verificar quais tabelas existem no banco de dados, iterar sobre as tabelas que queremos excluir, e ignorar aquelas que queremos manter.
No SQLite, há uma tabela chamada SQLITE_MASTER/SQLITE_SCHEMA que armazena os metadados do banco de dados, incluindo informações sobre todos os objetos do banco de dados e o SQL usado para defini-los. Este é o catálogo de sistema mais importante no SQLite. A consulta abaixo é usada para obter todas as informações do banco de dados.
SELECT * FROM SQLITE_MASTER;
Resultado:
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)
Vemos que surgiu uma nova entidade chamada "índice" (index), que não criamos anteriormente.
O que é um índice?
Um índice no SQLite é um objeto de banco de dados que permite melhorar o desempenho das consultas, acelerando a recuperação de registros de uma tabela. Índices são especialmente úteis para agilizar operações de busca, ordenação e junção, criando uma estrutura de dados ordenada (geralmente uma árvore B) que permite ao núcleo do banco de dados localizar linhas mais rapidamente.
Por que o índice foi criado?
Se uma tabela no SQLite possui uma chave primária, um índice é criado automaticamente para ela.
No resultado do nosso banco de dados anteriormente, finalmente obtivemos todos os objetos e metadados. Agora, podemos identificar as tabelas que não são mais necessárias e removê-las. Para isso, criaremos um array com as tabelas que precisamos e os comandos SQL que as criam, para podermos compará-las com as tabelas presentes nos dados do banco e, assim, excluir as que não correspondem.
A classe CNews possui herança em múltiplos níveis das classes:
- CCandleProperties
- CChartProperties
- CSymbolProperties
A classe CNews inclui as classes:
- CDaylightSavings_UK
- CDaylightSavings_US
- CDaylightSavings_AU
A classe CNews também inclui o arquivo de cabeçalho CommonVariables.mqh.
A classe CNews apresenta herança hierárquica das classes:
- 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 } //+------------------------------------------------------------------+
Atribuiremos a cada componente da nossa estrutura de banco de dados um valor de enumeração como uma forma de identificação.
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 };
O objetivo da estrutura SQLiteMaster é armazenar as propriedades atuais de um objeto de banco de dados, como tipo, nome, etc. Dessa forma, podemos rastrear todos os objetos no 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
Na estrutura MQL5CalendarContents, armazenaremos propriedades adicionais — as variáveis Content e insert.
Nossa variável de string insert armazenará os comandos SQL de inserção para nossos objetos SQL. Como a variável CalendarComponents Content armazenará o valor de enumeração para nosso objeto SQL como uma forma de identificação, poderemos reconhecer os objetos SQL assim que todas as propriedades forem salvas no array de estrutura 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
A função CalendarStruct retornará o valor da estrutura MQL5CalendarContents, em que o parâmetro Content é igual ao valor de enumeração na variável Content da estrutura do 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; }
A variável de string DropRequest será responsável por remover objetos de banco de dados que não precisamos mais. No comando SQL, utilizamos os operadores PRAGMA.
O que é um operador PRAGMA?
O operador PRAGMA no SQLite é um comando especial usado para modificar o funcionamento da biblioteca SQLite ou consultar o estado interno do núcleo do banco de dados. Os operadores PRAGMA não fazem parte do SQL padrão; são específicos do SQLite. Eles oferecem a capacidade de controlar vários parâmetros ambientais e o comportamento do banco de dados.
Propósito dos operadores PRAGMA
- Configuração: Os operadores PRAGMA permitem ajustar o ambiente do banco de dados, como ativar ou desativar restrições de chave estrangeira, definir o modo de log ou ajustar configurações de uso de memória.
- Diagnóstico: Os operadores podem ser usados para obter informações sobre o banco de dados, como verificar a integridade da base de dados, obter configurações atuais ou visualizar o estado do mecanismo SQLite.
- Otimização: Os operadores PRAGMA ajudam a otimizar o desempenho do banco de dados ajustando parâmetros como o tamanho do cache, o modo de bloqueio e as configurações de sincronização.
- Manutenção: Os operadores são úteis na manutenção, incluindo a reconstrução de índices, análise de tabelas e gerenciamento de configurações de limpeza automática.
No nosso primeiro operador PRAGMA, desativamos todas as restrições de chave estrangeira que impediriam a exclusão de qualquer tabela com essas restrições.
No segundo operador PRAGMA, ativamos o secure_delete, que controla se o conteúdo excluído deve ser zerado antes de ser removido do arquivo do banco de dados. Nesse caso, o banco de dados sobrescreverá o conteúdo excluído com zeros somente se isso não aumentar a quantidade de operações de entrada/saída.
No terceiro operador, excluímos o objeto SQL, caso ele exista. Em seguida, usamos o comando Vacuum, que reconstrói o arquivo do banco de dados, reembalando-o para ocupar o menor espaço possível no disco. Isso pode ajudar a otimizar o desempenho do banco de dados e liberar espaço não utilizado.
Por fim, reativamos as restrições de chave estrangeira.
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
Armazenamos as propriedades da nossa tabela AutoDST no primeiro índice do array CalendarContents. Aqui, na variável Content, atribuímos o valor de enumeração AutoDST_Table.
Em seguida, atribuiremos o nome, nome da tabela, tipo e o comando insert. No comando SQL para criar a tabela, atribuímos ao campo DST o valor padrão DST_NONE e encerramos o comando com a palavra-chave STRICT.
A palavra-chave STRICT garante que os dados inseridos na coluna devem corresponder ao tipo declarado para essa coluna. Isso proporciona maior previsibilidade e uniformidade nos tipos de dados armazenados.
Sem a palavra-chave STRICT, qualquer tipo de dado poderia ser inserido na tabela, e o tipo de dado declarado na coluna seria tratado como uma sugestão, não como uma exigência.
//-- 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');";
Agora, inicializamos as propriedades dos views de calendário para UK, US, AU e NONE.
A variável view_sql armazenará nosso comando SQL para criar os views individuais. No nosso comando SQL, selecionamos Eventid, Eventname, Country, EventCurrency e Eventcode da tabela 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
Depois, selecionamos a coluna DST para o cronograma correspondente a partir 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
Unimos as tabelas MQL5Calendar e TimeSchedule com base no mesmo identificador.
Classificamos a lista por Date da tabela Record.
Date 27-05-2024
Após obtermos os resultados da consulta, eles serão ordenados em ordem crescente com base no tempo DST correspondente da 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"; }
Vamos examinar um dos views e ver o que o resultado da consulta retornará:
SELECT * FROM 'Calendar_UK';
Resultado:
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
Tecnicamente, agora temos uma nova tabela chamada Record. Ela substituirá nossa tabela Records anterior, pois agora armazenaremos apenas um registro. Nossa tabela terá o tipo de dado TEXT e um nome de coluna Date, que não deve ser confundido com a função Date do 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','.','-')));";
Nossa tabela TimeSchedule armazenará todos os dados temporais dos eventos individuais e utilizará uma referência de chave estrangeira chamada ID para vincular a tabela (criando uma relação) com a tabela 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');";
A tabela MQL5Calendar terá uma chave primária chamada "ID", que será única para cada registro de evento de notícia na tabela.
//-- 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');";
Vamos criar um trigger chamado OnlyOne_AutoDST. O trigger será ativado quando tentarmos inserir um valor na AutoDST, removendo todas as entradas de AutoDST antes de inserir um novo 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";
O mesmo se aplica ao OnlyOne_Record, mas esse trigger está associado à tabela 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";
Agora, na nossa função UpdateRecords, determinaremos se o calendário do banco de dados precisa ser atualizado.
As alterações desta função em relação à Parte 1 são as seguintes:
1. Leremos todos os objetos que não são índices presentes no banco de dados usando a consulta SQL "select * from sqlite_master where type<>'index';".
2. Armazenaremos todos os atributos do objeto no array DBContents, e se no final do comando SQL não houver um ponto e vírgula, nós o adicionaremos.
3. Compararemos os objetos encontrados no nosso banco de dados com os objetos que inicializamos no array CalendarContents. Removeremos 'IF NOT EXISTS' de CalendarContents.
4. Se não houver correspondência entre DBContents e CalendarContents, excluiremos o objeto do índice DBContents.
5. Se a quantidade de objetos SQL não for igual ao tamanho de CalendarContents, realizaremos uma atualização.
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; } }
Na função CreateCalendarTable, verificaremos se a tabela MQL5Calendar já existe no banco de dados de calendário, assim como a tabela TimeSchedule, e tentaremos excluir cada tabela, se elas existirem. Como TimeSchedule depende de MQL5Calendar, não podemos excluir MQL5Calendar sem primeiro excluir TimeSchedule.
Se MQL5Calendar não existir, criaremos a tabela.
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 }
Na função CreateTimeTable, verificamos se a tabela existe no banco de dados de calendário. Se não existir, criaremos a tabela.
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 }
Na função CreateCalendarViews, criaremos todas as views usando CalendarComponents para encontrar o identificador (valor de enumeração) de cada view e criá-las.
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()); } } }
Na função InsertIntoTables, inseriremos cada registro do array Evalues nas tabelas MQL5Calendar e TimeSchedule, respectivamente. As datas dos eventos serão ajustadas para os diferentes horários de verão em 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 }
Na função CreateRecordTable, verificaremos se a tabela Record já existe. Se não existir, criaremos a tabela. Assim que a tabela Record for criada, criaremos o trigger para ela e continuaremos adicionando a data atual do servidor.
Por que usar TimeTradeServer em vez de TimeCurrent?
Se usarmos TimeCurrent, obteremos os dados de tempo do símbolo atual do gráfico. O horário dos símbolos pode variar, pois as informações de tempo são atualizadas a cada novo tick. Isso é um problema potencial se os horários de negociação dos símbolos forem diferentes e se o símbolo atual do gráfico estiver fechado, o que significa que não haverá novos ticks, então o TimeCurrent pode retornar uma data atrasada em um dia ou mais. Por outro lado, TimeTradeServer é constantemente atualizado, independentemente do 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()); } }Na função CreateAutoDST, verificamos o cronograma de horário de verão da corretora. Se conseguirmos obter o cronograma de horário de verão, verificamos se a tabela AutoDST existe no banco de dados Calendar. Se a tabela AutoDST não existir, ela será criada. Após a criação da tabela AutoDST, criamos o trigger correspondente e tentamos inserir o cronograma DST, convertido da enumeração para uma 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()); } }
Classe de gerenciamento de riscos
O gerenciamento de riscos é um componente essencial para o sucesso na negociação. O principal objetivo do gerenciamento de riscos é proteger o capital de negociação. Sem capital, um trader não pode continuar negociando. Implementar estratégias para limitar perdas permite que os traders permaneçam no mercado por mais tempo, proporcionando mais oportunidades de se recuperar de falhas e alcançar a lucratividade geral. Neste caso, ofereceremos ao usuário a opção de escolher entre diferentes perfis de risco e encontrar a melhor opção.
Nesta seção, os termos "lote", "tamanho de lote" e "volume" são usados como sinônimos. Considere-os equivalentes no contexto do gerenciamento de riscos.
Lista de perfis de risco:
- Tamanho mínimo de lote
- Tamanho máximo de lote
- Percentual do saldo
- Percentual de margem livre
- Risco em valor sobre o saldo
- Risco em valor sobre a margem livre
- Tamanho de lote com base no saldo
- Tamanho de lote com base na margem livre
- Tamanho de lote personalizado
- Percentual de risco
Tamanho mínimo de lote:
Neste caso, usaremos o tamanho mínimo de lote permitido para o símbolo atual.
Tamanho máximo de lote:
Aqui, utilizaremos o tamanho máximo de lote permitido para o símbolo atual.
Percentual do saldo:
Primeiro, calcularemos o tamanho do risco.
amount_of_risk = Balance*Percent;
Suponha que Percent = 5% e o saldo da conta seja 10.000.
amount_of_risk = 10000*(5/100); amount_of_risk = 500;
Em seguida, precisaremos dos preços de abertura e fechamento para calcular o risco mínimo de uma negociação específica ao usar o tamanho mínimo de lote.
OrderCalcProfit(ORDER_TYPE,Symbol(),Minimum_lotsize,OpenPrice,ClosePrice,Minimum_risk);
Ao retornar o Minimum_risk, utilizamos a seguinte equação para obter o tamanho de lote necessário para amount_of_risk.
required_lotsize = (amount_of_risk/Minimum_risk)*Minimum_lotsize;
Suponha que Minimum_risk = 100 e Minimum_lotsize = 0,1.
required_lotsize = (500/100)*0.1; required_lotsize = 5*0.1; required_lotsize = 0.5;
Percentual de margem livre:
Este método é semelhante ao percentual do saldo. No entanto, as vantagens dessa abordagem de gerenciamento de riscos se manifestam quando há negociações abertas na conta do trader.
Nesse caso, o risco permanece constante como uma porcentagem do saldo, independentemente das negociações abertas, desde que o saldo não mude. Ao usar o percentual de margem livre, o risco varia conforme os lucros das negociações abertas flutuam. Isso permite um cálculo mais preciso do risco conforme o estado atual da conta.
Risco em valor sobre o saldo:
Aqui, precisamos primeiro obter o quociente do risco total (dividendo) e do saldo (divisor).
risk = Balance/Risk_in_Amount;
Suponha que o saldo seja 10.000 e o Risk_in_Amount seja 800;
Nesse caso, queremos essencialmente arriscar USD 800 por negociação para cada USD 10.000 no saldo da conta do trader.
risk = 10000/800; risk = 12.5;
Em seguida, dividimos o risco pelo saldo real da conta para obter o valor do risco.
amount_of_risk = AccountBalance/risk;
Suponha que AccountBalance = 5000;
amount_of_risk = 5000/12.5; amount_of_risk = 400;
Agora sabemos que o trader deseja arriscar USD 400 nesta negociação específica.
Risco em valor sobre a margem livre:
Este método é semelhante ao risco em valor sobre o saldo, mas vamos considerar outro exemplo.
risk = FreeMargin/Risk_in_Amount;
Suponha que FreeMargin = 150 e Risk_in_Amount = 1;
Neste caso, arriscaremos USD 1 para cada USD 150 de margem livre.
risk = 150/1; risk = 150; amount_of_risk = AccountFreeMargin/risk; //-- Let AccountFreeMargin = 750 amount_of_risk = 750/150; amount_of_risk = 5;
Após obter o valor do risco, calcularemos o tamanho de lote necessário para cobrir o risco de USD 5 em uma negociação específica.
Tamanho de lote com base no saldo:
Neste perfil de risco, o trader especifica o tamanho de lote que gostaria de arriscar para um saldo específico na conta.
required_lotsize = (AccountBalance/Balance)*lotsize;
Aqui, AccountBalance é o saldo real na conta do trader, enquanto Balance e lotsize são os valores fornecidos pelo trader.
Suponha que AccountBalance = 10.000, Balance = 350 e lotsize = 0,01.
Neste caso, o trader deseja arriscar 0,01 lotes para cada USD 350 no saldo da conta.
required_lotsize = (10000/350)*0.01; required_lotsize = 0.285;
required_lotsize será 0,285, o que é um valor muito maior. Suponha que o passo de volume para o símbolo específico em que desejamos abrir uma negociação seja 0,01. Tentar abrir uma negociação com um tamanho de lote de 0,285 quando o passo de volume é 0,01 resultará em um erro.
Para evitar isso, normalizamos o tamanho de lote. Essencialmente, formatamos o tamanho de lote segundo o passo de volume.
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;
Tamanho de lote com base na margem livre:
Este método é semelhante ao tamanho de lote com base no saldo. Vamos considerar mais um exemplo.
required_lotsize = (AccountFreeMargin/FreeMargin)*lotsize;
Suponha que AccountFreeMargin = 134.560, FreeMargin = 1622, e o lot-size = 0,0056.
Neste caso:
required_lotsize = (134560/1622)*0.0056; required_lotsize = 0.464;
Suponha que o passo de volume seja 0,02.
Normalizamos o required_lotsize segundo o passo de volume (Volume step).
//-- 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;
Tamanho de lote personalizado:
Neste perfil, utilizaremos o tamanho de lote fornecido pelo trader.
Percentual de risco:
Neste perfil, calcularemos a porcentagem de risco máximo permitido para o símbolo atual, utilizando os requisitos de margem livre e margem para o símbolo específico.
A classe CRiskManagement possui herança em múltiplos níveis das classes:
- CSymbolProperties
- CChartProperties
A classe CRiskManagement inclui a classe CAccountInfo.
A classe CRiskManagement apresenta herança hierárquica das classes:
- 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; } } //+------------------------------------------------------------------+
A variável RiskProfileOption, do tipo enumeração RiskOptions, armazenará o parâmetro do perfil de risco do usuário/trader, que servirá como um parâmetro de entrada para o EA.
//-- 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
A variável RiskFloorOption, do tipo enumeração RiskFloor, armazenará o valor mínimo de risco permitido pelo usuário/trader, que servirá como um parâmetro de entrada para o EA.
//-- Enumeration declaration for Risk floor enum RiskFloor { RiskFloorMin,//MINIMUM LOTSIZE RiskFloorMax,//MAX-RISK RiskFloorNone//NONE } RiskFloorOption;//variable for Risk floor
A variável RiskCeilOption, do tipo enumeração RiskCeil, armazenará o valor máximo de risco permitido pelo usuário/trader, que servirá como um parâmetro de entrada para o EA.
//-- 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
O saldo da conta ou a margem livre do usuário/trader será armazenado na variável double RiskAmountBoF, enquanto a variável double RiskAmount armazenará o valor do risco. Risk_Profile_2 será usada para armazenar as propriedades dos perfis de risco AMOUNT PER BALANCE (valor por saldo) e AMOUNT PER FREE-MARGIN (valor por margem livre).
//-- 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)
A variável Risk_Profile_3 da estrutura do tipo RISK_LOT armazenará as propriedades dos perfis de risco LOTSIZE PER BALANCE (tamanho de lote por saldo) e LOTSIZE PER FREE-MARGIN (tamanho de lote por margem livre).
//-- 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)
A variável RiskFloorPercentage armazenará o percentual de risco máximo para a opção Riskfloor chamada RiskFloorMax.
double RiskFloorPercentage;//variable for RiskFloorMax
A variável Risk_Profile_1 armazenará o percentual de risco para as opções PERCENTAGE OF BALANCE (percentual do saldo) ou PERCENTAGE OF FREE-MARGIN (percentual da margem livre).
double Risk_Profile_1;//variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
A variável Risk_Profile_4 armazenará o tamanho de lote personalizado para a opção CUSTOM LOTSIZE (tamanho de lote personalizado).
double Risk_Profile_4;//variable for Risk option (CUSTOM LOTSIZE)
A variável Risk_Profile_5 armazenará o percentual de risco máximo para a opção PERCENTAGE OF MAX-RISK (percentual do risco máximo).
double Risk_Profile_5;//variable for Risk option (PERCENTAGE OF MAX-RISK)
Na função GetRiskOption, obteremos o parâmetro de risco do usuário/trader como um tipo de dado string.
//-- 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; } }
Na função GetRiskFloor, obteremos o valor do nível mínimo de risco do usuário/trader como um tipo de dado string.
//-- 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; } }
Na função GetRiskCeil, obteremos o parâmetro Risk Ceiling (teto de risco) do usuário/trader como um tipo de dado 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; } }
No construtor da classe de gerenciamento de riscos, inicializamos as variáveis declaradas anteriormente com valores padrão. O valor padrão da variável ORDERTYPE é ORDER_TYPE_BUY, portanto, para as opções de risco que exigem o tipo de ordem para o cálculo de risco, o tipo de ordem será definido por esta variável e será utilizado para modelar os cálculos de risco ao abrir negociações. O preço de abertura padrão será armazenado na variável OpenPrice e corresponderá ao preço Ask do nosso ORDERTYPE. O preço de fechamento padrão terá uma variação de 1% em relação ao preço Ask e será armazenado na variável ClosePrice.
//Initialize values CRiskManagement::CRiskManagement(void):Lots(0.0),max_percent(100), ORDERTYPE(ORDER_TYPE_BUY),OpenPrice(Ask()), ClosePrice(NormalizePrice(Ask()+Ask()*0.01)) { }
A função Volume extrai o tamanho de lote para o perfil de risco do usuário/trader e ajusta o tamanho de lote com base no parâmetro de risco selecionado, de acordo com os parâmetros Risk Ceiling e Risk Floor escolhidos pelo usuário/trader.
Depois disso, o tamanho de lote será normalizado para que seja possível abrir uma negociação real com o tamanho de lote especificado.
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; }
A função SetMedium atribuirá à variável Medium o valor do saldo da conta do usuário/trader ou da margem livre da conta, com base na variável de enumeração R_Medium.
//-- Set Medium variable value void SetMedium(const RiskMedium R_Medium) {Medium = (R_Medium==BALANCE)?Account.Balance():Account.FreeMargin();}
A função GetMinimumRisk atribuirá à variável MinimumAmount o risco mínimo permitido para uma negociação específica com o tamanho mínimo de lote.
//-- Get Minimum Risk for a Trade using Minimum Lot-size bool GetMinimumRisk() { return OrderCalcProfit(ORDERTYPE,Symbol(),LotsMin(),OpenPrice,ClosePrice,MinimumAmount); }
A função GetRisk calculará o tamanho de lote necessário para o valor de risco especificado no argumento variável Amount. Ao definir MinimumAmount (o valor mínimo de risco para a negociação específica), Amount é dividido por MinimumAmount para obter um quociente que seja múltiplo do tamanho mínimo de lote, a fim de obter o tamanho de lote necessário para Amount.
//-- Retrieve Risk amount based on Risk inputs double GetRisk(double Amount) { if(!GetMinimumRisk()||Amount==0) return 0.0; return ((Amount/MinimumAmount)*LotsMin()); }
Na função RiskProfile1, calculamos e retornamos o tamanho de lote para as opções PERCENTAGE OF BALANCE ou 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); }
Na função RiskProfile2, calculamos e retornamos o tamanho de lote para as opções AMOUNT PER BALANCE ou 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); }
Na função RiskProfile3, calculamos e retornamos o tamanho de lote para as opções LOTSIZE PER BALANCE ou 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; }
Na função ValidateLotsize, editamos a variável Lotsize passada por referência.
No primeiro operador Switch RiskFloorOption:
- Caso RiskFloorMin: Verificamos se a variável Lotsize ultrapassa os limites estabelecidos e atribuimos a ela o valor do tamanho mínimo de lote do símbolo atual. Verificamos o limite inferior, ou seja, se o valor da variável é menor que o tamanho mínimo de lote. O limite superior é quando a variável Lotsize excede o risco máximo permitido.
- Caso RiskFloorMax: Primeiro, verificamos se a variável Lotsize ultrapassa o risco máximo permitido. Se sim, verificamos se o risco mínimo desejado (maximum desired minimum risk) excede o tamanho mínimo de lote. Se sim, atribuimos a Lotsize o risco mínimo desejado para a negociação. Caso contrário, atribuimos o tamanho mínimo de lote. Se Lotsize for originalmente menor que o risco máximo permitido e menor que o tamanho mínimo de lote, atribuimos o tamanho mínimo de lote.
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; } }
Na função NormalizeLotsize, verificamos se o tamanho de lote está dentro dos limites de volume do símbolo e se respeita o passo de volume.
Se o tamanho de lote ultrapassar o limite de volume do símbolo, calculamos os tamanhos de lotes restantes disponíveis até atingir o limite de volume. Em seguida, verificamos se os tamanhos de lotes restantes são maiores ou iguais ao tamanho mínimo de lote do símbolo atual.
- RiskFloorMin: Atribuímos à variável Lotsize o tamanho mínimo de lote.
- RiskFloorMax: Se o RiskFloorPercentage do tamanho de lote restante for maior que o tamanho mínimo de lote, atribuimos esse valor à variável Lotsize. Se o RiskFloorPercentage do tamanho de lote restante for menor ou igual ao tamanho mínimo de lote, atribuimos à variável Lotsize o tamanho mínimo de lote.
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()); }
Classe Gráfica Comum (Common Graphics Class)
A Classe Gráfica Comum exibirá as propriedades gerais do símbolo atual e alguns parâmetros de risco definidos pelo trader.
CommonGraphics possui herança em múltiplos níveis das classes:
- CObjectProperties
- CChartProperties
- CSymbolProperties
CommonGraphics inclui a classe CRiskManagement.
CommonGraphics apresenta herança hierárquica das classes:
- 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); } //+------------------------------------------------------------------+
Na função GraphicsRefresh, definimos as propriedades dos objetos gráficos.
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); }
EA (Expert Advisor)
Mais uma vez, neste artigo, não abriremos nenhuma negociação.//+------------------------------------------------------------------+ //| 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() { //--- } //+------------------------------------------------------------------+
Depois que tudo for compilado, é hora de considerar algumas etapas a serem realizadas após colocar o EA no gráfico.
Depois de decidir qual janela de gráfico abrir, seu gráfico pode se parecer com o exemplo mostrado acima, antes de adicionar o EA.
Agora, vamos adicionar o EA.
Primeiro, podemos configurar as variáveis de entrada para o gerenciamento de riscos. No início, deixarei os valores padrão.
Se for a primeira vez que você executa o NewsTrading 2.00 e não tiver rodado o NewsTrading 1.00 anteriormente com sua corretora, e o banco de dados do calendário estiver ausente na pasta comum, seu gráfico ficará assim. As cores do gráfico podem variar de acordo com o broker.
Como indicado na mensagem abaixo, os lotes serão exibidos de acordo com os parâmetros de risco selecionados nas configurações.
Se o terminal não conseguir estabelecer uma conexão.
Se, durante a primeira execução do EA NewsTrading 2.00, o banco de dados do calendário foi criado a partir do NewsTrading 1.00, o NewsTrading 2.00 excluirá todas as tabelas do NewsTrading 1.00.
Conclusão
Nesta artigo, discutimos como funciona a herança e fornecemos um exemplo para ilustrar o conceito. Criamos uma nova classe de horário de verão que servirá como classe pai para vários gráficos DST. Criamos uma classe de propriedades Symbol para extrair as propriedades do símbolo de todos os classes que a herdam. Também desenvolvemos uma classe de propriedades Chart para configurar o gráfico. Abordamos várias metodologias do SQLite e um método simples de aumentar a eficiência geral do banco de dados. Criamos uma classe de propriedades Object para criar e remover objetos gráficos, e, em seguida, desenvolvemos uma classe de gerenciamento de riscos para acomodar diferentes opções de gerenciamento de riscos. Finalmente, criamos a classe Common Graphics para exibir as propriedades Symbol no gráfico, junto com as opções de risco para o trader. No próximo artigo, "Simplificando a negociação com base em notícias (Parte 3): Realizando negociações", finalmente começaremos a abrir negociações.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/14912





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso