English Русский Español Deutsch 日本語
preview
Negociação de Notícias Simplificada (Parte 1): Criando um Banco de Dados

Negociação de Notícias Simplificada (Parte 1): Criando um Banco de Dados

MetaTrader 5Exemplos | 28 agosto 2024, 16:42
130 0
Kabelo Frans Mampa
Kabelo Frans Mampa

Introdução

Neste artigo, aprenderemos a criar um banco de dados no qual armazenaremos dados do Calendário Econômico do MQL5. Esses dados podem ser usados posteriormente, em artigos futuros, para negociar com base nas notícias. Também exploraremos como executar consultas SQL básicas para recuperar certas informações organizadas desse banco de dados. Todo o processo será realizado no IDE do MQL5.

Os traders mantêm um olhar atento nas fontes de notícias em busca de informações que possam impactar os mercados. Isso inclui eventos geopolíticos, anúncios de lucros corporativos, eventos políticos e relatórios econômicos como crescimento do PIB ou dados de emprego. Os traders agem rapidamente para responder a lançamentos de notícias significativas, buscando lucrar com as mudanças resultantes no mercado. Dependendo de como a notícia é interpretada, pode ser necessário comprar ou vender ativos. Neste artigo, focaremos em eventos econômicos, pois eles estão prontamente disponíveis para nós através do Calendário Econômico do MQL5.


Por Que Criar um Banco de Dados

Exemplo de Tabela de Banco de Dados


Um banco de dados é uma coleção estruturada de dados que é armazenada e acessada eletronicamente. Os bancos de dados podem gerenciar e armazenar eficientemente grandes quantidades de dados, permitindo várias atividades, como análise de dados, armazenamento e gestão. No MQL5, trabalhamos com bancos de dados SQLite que são criados e gerenciados pelo motor de banco de dados SQLite. Os bancos de dados SQLite podem ter qualquer extensão de arquivo, mas geralmente são arquivos de disco único com extensões .sqlite, .sqlite3 ou .db. Esses arquivos contêm todos os dados e estruturas presentes nos bancos de dados, incluindo tabelas, gatilhos, índices e outros metadados.

Os bancos de dados são perfeitos para lidar com grandes conjuntos de dados e simplificam o processo de recuperação de dados de uma data ou evento específico, sem a necessidade de loops complexos. Além disso, o calendário econômico do MQL5 é inacessível no testador de estratégias. Então, se você quiser testar sua estratégia com base em notícias, como faria isso?

A resposta é usar um banco de dados. Para mais informações sobre bancos de dados, confira esses slides da palestra e, de preferência, este artigo MQL5. Além disso, adicionarei um excelente tutorial de SQLite.


Moedas no Calendário Econômico do MQL5

No Símbolo Nome
 1. NZD Dólar Neozelandês
 2. EUR Euro
 3. JPY Iene Japonês
 4. CAD Dólar Canadense
 5. AUD Dólar Australiano
 6. CNY Yuan Chinês
 7. SGD Dólar de Singapura
 8. BRL Real Brasileiro
 9. MXN Peso Mexicano
 10. ZAR Rand Sul-Africano
 11. HKD Dólar de Hong Kong
 12. INR Rúpia Indiana
 13. NOK Coroa Norueguesa
 14. USD Dólar dos Estados Unidos
 15. GBP Libra Esterlina
 16. CHF Franco Suíço
 17. KRW Won Sul-Coreano
 18. SEK Coroa Sueca 

A tabela acima não está em nenhuma ordem específica.

Existem várias moedas disponíveis no calendário do MQL5, mas algumas delas não são comumente usadas por corretores ou facilmente acessíveis para traders. Por exemplo, o Real Brasileiro e o Won Sul-Coreano não estão amplamente disponíveis. Mesmo que você consiga encontrar um corretor que trabalhe com essas moedas exóticas, os spreads geralmente são desfavoráveis para negociação. Eu falo por experiência própria.


Criando o Banco de Dados

Conceito de Horário de Verão

Antes de criarmos o banco de dados, precisamos primeiro considerar o horário de verão e por que isso afetaria nossos testes retrospectivos.Horário de Verão (DST) é a prática de adiantar o relógio em uma hora durante os meses mais quentes do ano, tipicamente da primavera ao outono, para melhor aproveitamento da luz do dia e economia de energia.

Em áreas onde o horário de verão é observado, a hora local pode mudar em uma hora durante os períodos de transição da primavera e do outono. Isso pode ter um impacto nos horários de abertura e fechamento dos mercados financeiros. Por exemplo, se o horário de verão começar e os relógios forem adiantados em uma hora, o mercado pode abrir mais cedo no horário local. Em contrapartida, quando o horário de verão termina e os relógios são atrasados em uma hora (horário normal), o horário de abertura do mercado pode parecer mais tarde no horário local. Também devemos reconhecer que diferentes regiões do mundo podem ter seu próprio horário de verão para se adaptar às suas estações.

Por exemplo, os EUA têm seu próprio horário de verão que normalmente começa no segundo domingo de março de cada ano e termina no primeiro domingo de novembro do mesmo ano. Os países europeus também são conhecidos por terem seu próprio horário de verão que normalmente começa no último domingo de março de cada ano e termina no último domingo de outubro do mesmo ano. A Austrália é outro país conhecido por praticar o horário de verão, cujo cronograma geralmente começa no primeiro domingo de outubro e termina no primeiro domingo de abril do ano seguinte. 

Para levar em conta os ajustes do horário de verão, algumas bolsas e mercados financeiros podem modificar seus horários de negociação ou fuso horário. As bolsas podem divulgar horários de negociação revisados para contabilizar as mudanças de horário locais ou ajustar seu fuso horário de acordo com o cronograma do horário de verão que praticam, garantindo uniformidade e clareza para os participantes do mercado.

Pelo que observei, se um corretor, por exemplo, adotar o horário de verão dos EUA, o fuso horário antes do horário de verão seria GMT+2, e quando o horário de verão dos EUA começar, o fuso horário mudará para GMT+3. Você não perceberá a diferença de horário entre os lançamentos de notícias dos EUA no horário do servidor do corretor antes ou durante o horário de verão dos EUA, pois estarão sincronizados. Então, um exemplo que usaremos é o evento NFP dos EUA (Non-Farm Payrolls) para ilustrar; vamos supor que o NFP seja divulgado às 14h no horário do servidor do corretor antes do horário de verão dos EUA, o horário permanecerá o mesmo às 14h durante o horário de verão dos EUA, embora o fuso horário tenha mudado para GMT+3.


Mudança no Horário das Sessões de Negociação no Horário de Verão do Reino Unido


Por outro lado, se o comunicado de emprego do Reino Unido for às 8h antes do horário de verão do Reino Unido e dos EUA, quando o corretor mudar o fuso horário ao iniciar o horário de verão dos EUA, e o comunicado de emprego do Reino Unido ocorrer antes de o horário de verão do Reino Unido (UE) começar, o comunicado de emprego do Reino Unido seria às 7h no horário do corretor. Quando o horário de verão do Reino Unido começar e coincidir com o horário de verão dos EUA, o comunicado de emprego do Reino Unido voltará para as 8h. 

Eu sei que tudo isso é muito confuso, mas, felizmente, implementaremos isso em nosso banco de dados para que tenhamos datas precisas de quando os eventos ocorrem para testes retrospectivos mais tarde. Precisaremos determinar se o corretor usa o horário de verão dos EUA, o horário de verão do Reino Unido (UE), o horário de verão da Austrália ou se não usa horário de verão algum. Também criaremos tabelas de calendário econômico para cada tipo de horário de verão, para que possamos alterar qual cronograma utilizar nos testes retrospectivos para fins educativos. 


Implementação

Teremos três Classes de Horário de Verão:

  • DaylightSavings_AU
  • DaylightSavings_UK
  • DaylightSavings_US
Trabalharei apenas com uma neste artigo, pois são muito semelhantes entre si; todas estarão incluídas nos arquivos do projeto.

class CDaylightSavings_AU:CObject
  {

private:

   CTimeManagement   Time;
                     CDaylightSavings_AU(datetime startdate,datetime enddate);
   CObject           *List() { return savings;}//Gets the list of Daylight savings time
   datetime          StartDate;
   datetime          EndDate;
   CArrayObj         *savings;
   CArrayObj         *getSavings;
   CDaylightSavings_AU      *dayLight;

public:

                     CDaylightSavings_AU(void);
                    ~CDaylightSavings_AU(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 Daylight Savings Dates are available for a certain Year
   void              adjustDaylightSavings(datetime EventDate,string &AdjustedDate);//Will adjust the date's time zone depending on Daylight Savings
  };


Vamos criar uma classe chamada:

CDaylightSavings_AU

que herda de

CObject

Esta classe consistirá em criar uma lista que armazenará todas as datas de horário de verão a partir de 2007, pois esta é a data mais antiga armazenada no calendário econômico do MQL5.

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

Em seguida, usaremos a função booleana

bool              isDaylightSavings(datetime Date);//Esta função verifica se uma data específica cai dentro do horário de verão.

Para determinar se um parâmetro de data está dentro das datas de horário de verão.

bool CDaylightSavings_AU::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;
  }

Também utilizaremos outra função booleana.

bool              DaylightSavings(int Year,datetime &startDate,datetime &endDate);//Adjusts time when it is daylight savings an hour behind if outside

Para determinar se o ano está dentro da lista de datas de horário de verão que inicializamos anteriormente, então adicionaremos a data de início e a data de término às variáveis startDate e endDate.

bool CDaylightSavings_US::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 Daylight Savings 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 Daylight Savings end dates in the List
        {
         endDate = dayLight.EndDate;
         endDateDetected = true;
        }
      if(startDateDetected&&endDateDetected)//Check if both Daylight Savings 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 Daylight Saving date is found
   endDate = D'1970.01.01 00:00:00';//Set a default end date if no Daylight Saving date is found
   return false;
  }

Nossa função pública final nesta classe

void              adjustDaylightSavings(datetime EventDate,string &AdjustedDate);//Will adjust the date's time -zone depending on Daylight Savings

Modificará uma variável string AdjustedDate por referência com a EventDate.

void CDaylightSavings_AU::adjustDaylightSavings(datetime EventDate,string &AdjustedDate)
  {
   if(isDaylightSavings(TimeTradeServer()))//Check if the current trade server time is already within the Daylight Savings Period
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         AdjustedDate = TimeToString(EventDate);//storing normal event time
        }
      else
        {
         AdjustedDate = TimeToString((datetime)(EventDate-Time.HoursS()));//storing event time and removing an hour for DST
        }
     }
   else
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         AdjustedDate = TimeToString((datetime)(Time.HoursS()+EventDate));//storing event time and adding an hour for DST
        }
      else
        {
         AdjustedDate = TimeToString(EventDate);//storing normal event time
        }
     }
  }


Agora passaremos para nosso próximo arquivo de cabeçalho neste projeto, dedicado a manipulações de tempo.

CTimeManagement

que é uma classe no mesmo projeto.

class CTimeManagement
  {

private:

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

public:

   bool              DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime);//Checks if a date is within two other dates
   bool              DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd);//Check if two dates(Start&End) are within CompareStart & 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
   datetime          TimeMinusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an subtraction offset in seconds
   datetime          TimePlusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an addition offset in seconds
  };


A função chamada anteriormente 

Time.DateIsInRange(dayLight.StartDate,dayLight.EndDate,Date)

é uma função simples que verifica se uma única data está entre duas outras datas.

bool CTimeManagement::DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime)
  {
   if(FirstTime<=compareTime&&SecondTime>compareTime)
     {
      return true;
     }
   return false;
  }

O mesmo nome de função com parâmetros diferentes

bool		DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd);

também é simples; essa função verificará se duas datas estão entre outras duas datas.

bool CTimeManagement::DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd)
  {
   if(Start<=CompareStart&&CompareEnd<End)
     {
      return true;
     }
   return false;
  }


Precisaremos de uma função para saber se uma determinada data é hoje, por isso declarei

bool              DateisToday(datetime TimeRepresented);//Checks if a date is within the current day

que comparará o ano, mês e dia da hora atual com o 'TimeRepresented' datetime.

bool CTimeManagement::DateisToday(datetime TimeRepresented)
  {
   MqlDateTime TiM;
   TimeToStruct(TimeRepresented,TiM);
   TimeCurrent(today);
   if(TiM.year==today.year&&TiM.mon==today.mon&&TiM.day==today.day)
     {
      return true;
     }
   return false;
  }


Para obter um datetime com um deslocamento, temos duas funções.

datetime          TimeMinusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an subtraction offset in seconds

e

datetime          TimePlusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an addition offset in seconds


Quando queremos obter uma data com um deslocamento negativo de uma hora (ou qualquer unidade de tempo), usaremos

datetime CTimeManagement::TimeMinusOffset(datetime standardtime,int timeoffset)
  {
   standardtime-=timeoffset;
   return standardtime;
  }

Por outro lado, se quisermos uma data com um deslocamento positivo, usaremos

datetime CTimeManagement::TimePlusOffset(datetime standardtime,int timeoffset)
  {
   standardtime+=timeoffset;
   return standardtime;
  }



Decidi criar um arquivo de inclusão que armazenará variáveis globais e estruturas, bem como tipos enum para serem usados em diferentes classes e no expert no futuro.

Esse arquivo de inclusão é chamado CommonVariables e armazenará variáveis comuns.

string broker=AccountInfoString(ACCOUNT_COMPANY);//Getting brokers name via AccountInfoString
int Str = StringReplace(broker," ","");//Removing or replacing any spaces in the broker's name with an empty string
int Str1 = StringReplace(broker,".","");//Removing or replacing any dots in the broker's name with an empty string
int Str2 = StringReplace(broker,",","");//Removing or replacing any commas in the broker's name with an empty string
#define BROKER_NAME                    broker//Broker's Name
#define NEWS_TRADING_FOLDER            "NewsTrading"//Name of main folder in common/files
#define NEWS_CALENDAR_FOLDER           StringFormat("%s\\NewsCalendar",NEWS_TRADING_FOLDER)//name of subfolder in NewsTrading
#define NEWS_CALENDAR_BROKER_FOLDER    StringFormat("%s\\%s",NEWS_CALENDAR_FOLDER,BROKER_NAME)//Name of subfolder in NewsCalendar
#define NEWS_DATABASE_FILE             StringFormat("%s\\Calendar.sqlite",NEWS_CALENDAR_BROKER_FOLDER)//Name of sqlite file in subfolder in "Broker's Name"
#define NEWS_TEXT_FILE                 StringFormat("%s\\CalendarOpen.txt",NEWS_CALENDAR_BROKER_FOLDER)//Name of text file to indicate Calendar is open.

struct Calendar
  {
   ulong             EventId;//Event Id
   string            CountryName;//Event Country
   string            EventName;//Event Name
   string            EventType;//Event Type
   string            EventImportance;//Event Importance
   string            EventDate;//Event Date
   string            EventCurrency;//Event Currency
   string            EventCode;//Event Code
   string            EventSector;//Event Sector
   string            EventForecast;//Event Forecast Value
   string            EventPreval;//Event Previous Value
   string            EventImpact;//Event Impact
   string            EventFrequency;//Event Frequency
  };

enum DST_type
  {
   US_DST,//US Daylight Savings
   UK_DST,//UK(EU) Daylight Savings
   AU_DST,//AU Daylight Savings
   DST_NONE//No Daylight Savings Available
  };



Quando criarmos arquivos na pasta Common Files, gostaria de organizar os arquivos por pastas, para que seja fácil coordenar através da pasta Common Files.

Então, criei uma classe

class CFolders

que criará as pastas na pasta Common Files em sequência: NewsTrading/NewsCalendar/Broker. O construtor da classe realizará essa tarefa.

class CFolders
  {
private:
   bool              CreateFolder(string FolderPath);//Will create a folder with the FolderPath string parameter

public:
                     CFolders(void);//Class's constructor
  };

dentro do construtor

CFolders::CFolders(void)
  {
   if(CreateFolder(NEWS_TRADING_FOLDER))//Will create the NewsTrading Folder
     {
      if(CreateFolder(NEWS_CALENDAR_FOLDER))//Will create the NewsCalendar Folder
        {
         if(!CreateFolder(NEWS_CALENDAR_BROKER_FOLDER))//Will create the Broker Folder
           {
            Print("Something went wrong with creating folder: ",NEWS_CALENDAR_BROKER_FOLDER);
           }
        }
     }
  }

Função sendo chamada no construtor para criar as pastas individuais

bool CFolders::CreateFolder(string FolderPath)
  {
//--- attempt to create a folder relative to the MQL5\Files path
   if(FolderCreate(FolderPath,FILE_COMMON))
     {
      //--- successful execution
      return true;
     }
   else
     {
      PrintFormat("Failed to create the folder %s. Error code %d",FolderPath,GetLastError());
     }
//--- execution failed
   return false;
  }



Os candlesticks são muito importantes; precisaremos reunir informações sobre os preços de High, Low, Open e Close de um candle específico. Para fazer isso, criaremos outra classe para reunir essas informações para nós quando necessário em outros programas do projeto. O nome da classe será

class CCandleProperties

Agora declararemos as funções nesta classe.

class CCandleProperties
  {
private:
   CTimeManagement   Time;

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

Vamos dar uma olhada rápida em

double            Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Open Price

Nesta função, retornaremos o preço de abertura de um candle com base no parâmetro inteiro CandleIndex e no timeframe do candle, bem como no símbolo do candle.

double CCandleProperties::Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return iOpen(((SYMBOL==NULL)?Symbol():SYMBOL),Period,CandleIndex);//return candle open price
  }

Para verificar se um evento econômico aconteceu em uma data específica, visto que os corretores podem alterar seus fusos horários. Obteremos a data de um evento econômico e, com essa data, analisaremos o candle M15 para esse horário. Em seguida, calcularemos a altura do candle (iHigh-iLow) e compararemos essa altura com o candle M15 uma hora antes do evento e uma hora depois. Como os eventos econômicos normalmente são muito voláteis nos preços do mercado, devemos notar um candle muito longo quando o evento ocorrer. Usaremos o candle M15 para dar ao mercado tempo suficiente para formar esse candle longo. Se o corretor tiver alterado seu fuso horário, a data do evento armazenada no Calendário Econômico MQL5 pode não corresponder à volatilidade do candle na hora específica no candle do corretor, mas essa volatilidade pode estar presente uma hora depois ou uma hora antes da data especificada. A função que usaremos para fazer essa verificação de validação será

bool              IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL);//Determine if one candle is larger than two others

Esta função verificará se a altura do candle em uma data específica é maior do que o candle com um deslocamento de tempo antes e depois dessa data especificada.

bool CCandleProperties::IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL)
  {
   int CandleIndex = iBarShift(SYMBOL,PERIOD_M15,CandleTime);//Assign candle index of candletime
   int CandleIndexMinusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimeMinusOffset(CandleTime,Offset));//Assign candle index of candletime minus time offset 
   int CandleIndexPlusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimePlusOffset(CandleTime,Offset));//Assign candle index of candletime plus time offset
   double CandleHeight = High(CandleIndex,PERIOD_M15,SYMBOL)-Low(CandleIndex,PERIOD_M15,SYMBOL);//Assign height of M15 candletime in pips
   double CandleHeightMinusOffset = High(CandleIndexMinusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexMinusOffset,PERIOD_M15,SYMBOL);//Assign height of M15 candletime  minus offset in Pips
   double CandleHeightPlusOffset = High(CandleIndexPlusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexPlusOffset,PERIOD_M15,SYMBOL);//Assign height of M15 candletime plus offset in Pips
   //--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 occurred
     }
   return false;//Candletime is unlikely when the real news data was released
  }



Vamos passar para a classe News; essa classe trabalhará diretamente com a obtenção de valores do MQL5 calendário econômico e armazenando

os valores em um banco de dados para teste e, eventualmente, negociação 

class CNews
  {
   //Private Declarations Only accessible by this class/header file
private:
   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
   CCandleProperties Candle;//CandleProperties Object
   string            CurrencyBase,CurrencyProfit,EURUSD;//String variables declarations for working with EURUSD
   bool              EurusdIsSelected,EurusdIsFound,is_Custom;//Boolean variables declarations for working with EURUSD
   bool              timeIsShifted;//Boolean variable declaration will be used to determine if the broker changes it's time zone
   datetime          DaylightStart,DaylightEnd;//Datetime variables declarations for start and end dates for Daylight Savings
   //Structure Declaration for DST
   struct DST
     {
      bool           result;
      datetime       date;
     };
   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              InsertIntoTable(int db,DST_type Type,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              CreateTable(int db,string tableName,bool &tableExists);//Function for creating a table in a database
   void              CreateRecords(int db);//Creates a table to store records 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

   //Public declarations accessable via a class's Object
public:
                    ~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          GetLastestNewsDate();//Gets the latest/newest date in the Calendar database
  };


Há muito a se entender aqui; primeiro, daremos uma olhada em 

bool              AutoDetectDST(DST_type &dstType);//Function will determine Broker DST

Pelo nome desta função, seu principal objetivo é obter o cronograma de DST que o corretor utiliza. Vamos recuperar essa informação por meio da enumeração

enum DST_type
  {
   US_DST,//US Daylight Savings
   UK_DST,//UK(EU) Daylight Savings
   AU_DST,//AU Daylight Savings
   DST_NONE//No Daylight Savings Available
  };

passada por referência na função AutoDectectDST; essa função retornará falso se o cronograma de DST do corretor for conhecido ou não puder ser reconhecido.

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
   int lastyear = Time.ReturnYear(Time.TimeMinusOffset(iTime(Symbol(),PERIOD_CURRENT,0),Time.YearsS()));//Will store the previous year into an integer
   datetime lastyearstart = StringToTime(StringFormat("%s.01.01 00:00:00",(string)lastyear));//Will store the start date for the previous year
   datetime lastyearend = StringToTime(StringFormat("%s.12.31 23:59:59",(string)lastyear));//Will store the end date for the previous year

   if(CalendarValueHistory(values,lastyearstart,lastyearend,"US"))//Getting last year's calendar values for CountryCode = '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 ShiftStart=D'1970.01.01 00:00:00',ShiftEnd=D'1970.01.01 00:00:00';//datetime variables to store the broker's time zone shift(change)
   DST previousresult,currentresult;//Variables of structure type DST declared at the beginning of the class

   EURUSD="";//String variable assigned empty string
   EurusdIsSelected = false;//Boolean variable assigned value false
   EurusdIsFound = false;//Boolean variable assigned value false

   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
      CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);//Assign the Symbol's Currency Base
      CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);//Assign the Symbol's Currency Profit
      SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

      //-- 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=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
        {
         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
         CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);
         CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);
         SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

         //-- 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=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
           {
            EurusdIsSelected = SymbolSelect(SymName,true);//Adding the EURUSD Symbol to the Market Watch
            if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
              {
               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
     }

   for(uint i=0;i<eventtime.Size();i++)
     {
      currentresult.result = Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD);//Store the result of if the event date is the larger candlestick
      currentresult.date = (datetime)eventtime[i];//Store the eventdate from eventtime[i]
      timeIsShifted = ((currentresult.result!=previousresult.result&&i>0)?true:false);//Check if there is a difference between the previous result and the current result

      //--- Print Event Dates and if the event date's candle is larger than the previous candle an hour ago and the next candle an hour ahead
      Print("Date: ",eventtime[i]," is Larger: ",Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD)," Shifted: ",timeIsShifted);

      if(timeIsShifted)//Check if the Larger candle has shifted from the previous event date to the current event date in eventtime[i] array
        {
         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 event date for when the time shift began
           }
         ShiftEnd=previousresult.date;//Store the event date timeshift
        }
      previousresult.result = currentresult.result;//Store the previous result of if the event date is the larger candlestick
      previousresult.date = currentresult.date;//Store the event date from eventtime[i]
     }

   if(ShiftStart==D'1970.01.01 00:00:00'&&eventtime.Size()>0)//Check if the ShiftStart variable has not been assigned a relevant value and the event dates are more than zero
     {
      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
     }

   if(Savings_AU.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For AU DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = AU_DST;//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);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      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");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = UK_DST;//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);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      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");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = US_DST;//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);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      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(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
     {
      SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
     }
   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
  }


Ok, a função acima é longa; vamos dividi-la. Na primeira parte, reuniremos as datas dos eventos econômicos para NFP do ano anterior, então, se o ano atual for 2024, reuniremos todas as datas de 2023, pois precisamos analisar o ano inteiro para obter com sucesso o provável cronograma de DST do corretor. Essas datas serão armazenadas em nossa única matriz de strings eventtime. 

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
   int lastyear = Time.ReturnYear(Time.TimeMinusOffset(iTime(Symbol(),PERIOD_CURRENT,0),Time.YearsS()));//Will store the previous year into an integer
   datetime lastyearstart = StringToTime(StringFormat("%s.01.01 00:00:00",(string)lastyear));//Will store the start date for the previous year
   datetime lastyearend = StringToTime(StringFormat("%s.12.31 23:59:59",(string)lastyear));//Will store the end date for the previous year

   if(CalendarValueHistory(values,lastyearstart,lastyearend,"US"))//Getting last year's calendar values for CountryCode = '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 event time array by 1
            eventtime[eventtime.Size()-1] = TimeToString(values[x].time);//Storing the dates in an array of type string
           }
        }
     }


Depois, precisaremos usar essas datas no símbolo EURUSD, porque XAUUSD (OURO) e outros índices como US30 (Dow Jones) são muito voláteis, mesmo sem eventos de notícias; EURUSD é mais estável e se torna realmente volátil durante eventos econômicos, o que facilita a detecção de quando um evento econômico ocorreu, pois o mercado provavelmente disparará devido ao evento. Essa também é a razão pela qual nos concentraremos nos eventos de NFP, pois eles regularmente criam esses picos de preços no mercado. Com essa informação, precisaremos do símbolo EURUSD; primeiro, verificaremos todos os símbolos disponíveis no corretor para encontrar o EURUSD e selecioná-lo. Se não encontrarmos o EURUSD, retornaremos falso. 

   EURUSD="";//String variable assigned empty string
   EurusdIsSelected = false;//Boolean variable assigned value false
   EurusdIsFound = false;//Boolean variable assigned value false

   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
      CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);//Assign the Symbol's Currency Base
      CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);//Assign the Symbol's Currency Profit
      SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

      //-- 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=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
        {
         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
         CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);
         CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);
         SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

         //-- 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=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
           {
            EurusdIsSelected = SymbolSelect(SymName,true);//Adding the EURUSD Symbol to the Market Watch
            if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
              {
               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
     }


Depois de encontrar o EURUSD, iteraremos por todas as datas de NFP e encontraremos os candlesticks M15 para cada data individual e compararemos a altura do candle com as datas de deslocamento de uma hora antes e depois do evento para detectar se o evento provavelmente ocorreu. Se as datas do evento não corresponderem à volatilidade nos candlesticks, armazenaremos a primeira data do evento em que essa discrepância ocorrer e quando ela terminar nas variáveis ShiftStart e ShiftEnd.

for(uint i=0;i<eventtime.Size();i++)
     {
      currentresult.result = Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD);//Store the result of if the event date is the larger candlestick
      currentresult.date = (datetime)eventtime[i];//Store the event date from eventtime[i]
      timeIsShifted = ((currentresult.result!=previousresult.result&&i>0)?true:false);//Check if there is a difference between the previous result and the current result

      //--- Print Event Dates and if the event date's candle is larger than the previous candle an hour ago and the next candle an hour ahead
      Print("Date: ",eventtime[i]," is Larger: ",Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD)," Shifted: ",timeIsShifted);

      if(timeIsShifted)//Check if the Larger candle has shifted from the previous event date to the current event date in eventtime[i] array
        {
         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 event date for when the time shift began
           }
         ShiftEnd=previousresult.date;//Store the event date timeshift
        }
      previousresult.result = currentresult.result;//Store the previous result of if the event date is the larger candlestick
      previousresult.date = currentresult.date;//Store the event date from eventtime[i]
     }


Uma vez que tenhamos as datas ShiftStart e ShiftEnd, verificaremos se essas datas correspondem a alguma das datas de início e término do DST. Se houver uma correspondência, atribuiremos o cronograma de DST na variável dstType e retornaremos true. Se não tivermos uma data de ShiftStart (ShiftStart=D'1970.01.01 00:00:00') e o tamanho da matriz eventtime for maior que zero, saberemos que o corretor não segue nenhum cronograma de DST.

if(ShiftStart==D'1970.01.01 00:00:00'&&eventtime.Size()>0)//Check if the ShiftStart variable has not been assigned a relevant value and the event dates are more than zero
     {
      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
     }

   if(Savings_AU.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For AU DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = AU_DST;//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);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      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");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = UK_DST;//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);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      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");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = US_DST;//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);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      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(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
     {
      SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
     }
   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
  }


Depois de detectar o cronograma de DST, precisamos armazenar essas informações para implementação posterior ao testar os eventos econômicos, assim saberemos qual calendário usar.

void              CreateAutoDST(int db);//Function for creating and inserting Recommend DST for the Broker into a table

Um único registro será armazenado na tabela AutoDST.

void CNews::CreateAutoDST(int db)
  {
   bool failed=false;//boolean variable

   if(!DatabaseTableExists(db,"AutoDST"))//Checks if the table 'AutoDST' exists in the databse 'Calendar'
     {
      //--- create the table AutoDST
      if(!DatabaseExecute(db,"CREATE TABLE AutoDST(DST STRING NOT NULL);"))//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
     {
      return;//Exits the function if the table AutoDST table already exists
     }

//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("INSERT INTO 'AutoDST'(DST) VALUES ('%s')",((DSTType==US_DST)?"Data_US":
                                    (DSTType==UK_DST)?"Data_UK":(DSTType==AU_DST)?"Data_AU":"Data_None"));

   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat("INSERT INTO 'AutoDST'(DST) VALUES ('%s')",((DSTType==US_DST)?"Data_US":
                  (DSTType==UK_DST)?"Data_UK":(DSTType==AU_DST)?"Data_AU":"Data_None"));//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());
     }
  }


Precisamos de uma função que crie nossas tabelas de calendário, para que possamos reutilizar essa função para criar nossos calendários individuais ajustados para diferentes cronogramas de DST.

bool              CreateTable(int db,string tableName,bool &tableExists);//Function for creating a table in a database

Nesta função CreateTable, criaremos apenas as tabelas 'Data_UK' para o DST do Reino Unido, 'Data_US' para o DST dos EUA, 'Data_AU' para o DST da Austrália e 'Data_None' para nenhum DST. O string tableName será o parâmetro para o sufixo do nome da tabela, exemplo 'US'.

bool CNews::CreateTable(int db,string tableName,bool &tableExists)
  {
   if(DatabaseTableExists(db,StringFormat("Data_%s",tableName)))//Checks if a table 'Data_%s' exists in the database 'Calendar'
     {
      tableExists=true;//Assigns true to tableExists variable

      if(!DatabaseExecute(db,StringFormat("DROP TABLE Data_%s",tableName)))//We will drop the table if the table already exists
        {
         //If the table failed to be dropped/deleted
         PrintFormat("Failed to drop table Data_%s with code %d",tableName,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(!DatabaseTableExists(db,StringFormat("Data_%s",tableName)))//If the database table 'Data_%s' doesn't exist
     {
      //--- create the table 'Data' with the following columns
      if(!DatabaseExecute(db,StringFormat("CREATE TABLE Data_%s("
                                          "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));",tableName)))//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
  }


Agora precisamos inserir dados nessas tabelas que criamos, mas primeiro precisamos obter esses dados. Nossa próxima função

void              EconomicDetails(Calendar &NewsTime[]);//Gets values from the MQL5 economic Calendar

Recuperará todos os eventos econômicos disponíveis por referência na matriz NewsTime Calendar.

void CNews::EconomicDetails(Calendar &NewsTime[])
  {
   int Size=0;//to keep track of the size of the events in the NewsTime array
   MqlCalendarCountry countries[];
   int count=CalendarCountries(countries);//Get the array of country names available in the Calendar
   string Country_code="";

   for(int i=0; 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
               NewsTime[Size].EventImportance = EnumToString(event.importance);//storing the event importance from (ENUM_CALENDAR_EVENT_IMPORTANCE) to a string
               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
                 }

               NewsTime[Size].EventImpact =  EnumToString(values[x].impact_type);//storing the event impact from (ENUM_CALENDAR_EVENT_IMPACT) to a string
               NewsTime[Size].EventFrequency =  EnumToString(event.frequency);//storing the event frequency from (ENUM_CALENDAR_EVENT_FREQUENCY) to a string
               Size++;//incrementing the Calendar array NewsTime
              }
           }
        }
     }
  }


Uma vez que tenhamos nossos dados, precisamos inseri-los nas tabelas de calendário.

bool              InsertIntoTable(int db,DST_type Type,Calendar &Evalues[]);//Function for inserting Economic Data in to a database's table

A função InsertIntoTable ajudará a realizar essa tarefa. Ela tem três parâmetros.

1. Este parâmetro de entrada é o valor inteiro do banco de dados.

int db

2. Este parâmetro de entrada é o cronograma de DST.

DST_type Type

3. Esta referência de matriz é uma entrada dos eventos do calendário que teríamos recuperado da nossa função anterior, EconomicDetails.

Calendar &Evalues[]

 Nesta função, mudaremos as datas individuais dos eventos se estiverem dentro do cronograma de DST. Adicionaremos uma hora à data do evento; se a hora atual do servidor estiver no cronograma de DST, removeremos uma hora da data do evento. Em seguida, armazenaremos todos os dados dos eventos econômicos na tabela de calendário Data_%s.

bool CNews::InsertIntoTable(int db,DST_type Type,Calendar &Evalues[])
  {
   string tableName;//will store the table name suffix
   for(uint i=0; i<Evalues.Size(); i++)//Looping through all the Economic Events
     {
      string Date;//Will store the date for the economic event
      switch(Type)//Switch statement to check all possible 'case' scenarios for the variable Type
        {
         case DST_NONE://if(Type==DST_NONE) then run code below
            Date = Evalues[i].EventDate;//Assign the normal Economic EventDate
            tableName = "None";//Full table name will be 'Data_None'
            break;//End switch statement
         case US_DST://if(Type==US_DST) then run code below
            Savings_US.adjustDaylightSavings(StringToTime(Evalues[i].EventDate),Date);//Assign by Reference the Economic EventDate adjusted for US DST(Daylight Savings Time)
            tableName = "US";//Full table name will be 'Data_US'
            break;//End switch statement
         case UK_DST://if(Type==UK_DST) then run code below
            Savings_UK.adjustDaylightSavings(StringToTime(Evalues[i].EventDate),Date);//Assign by Reference the Economic EventDate adjusted for UK DST(Daylight Savings Time)
            tableName = "UK";//Full table name will be 'Data_UK'
            break;//End switch statement
         case AU_DST://if(Type==AU_DST) then run code below
            Savings_AU.adjustDaylightSavings(StringToTime(Evalues[i].EventDate),Date);//Assign by Reference the Economic EventDate adjusted for AU DST(Daylight Savings Time)
            tableName = "AU";//Full table name will be 'Data_AU'
            break;//End switch statement
         default://if(Type==(Unknown value)) then run code below
            Date = Evalues[i].EventDate;//Assign the normal Economic EventDate
            tableName = "None";//Full table name will be 'Data_None'
            break;//End switch statement
        }

      string request_text =
         StringFormat("INSERT INTO 'Data_%s'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTDATE,EVENTCURRENCY,EVENTCODE,"
                      "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY)"
                      "VALUES (%d,%d,'%s','%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s')",
                      tableName,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Date,
                      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_text))//Checks whether the event was inserted into the table 'Data_%s'
        {
         Print(GetLastError());
         PrintFormat("INSERT INTO 'Data_%s'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTDATE,EVENTCURRENCY,EVENTCODE,"
                      "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY)"
                      "VALUES (%d,%d,'%s','%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s')",
                      tableName,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Date,
                      Evalues[i].EventCurrency,
                      Evalues[i].EventCode,
                      Evalues[i].EventSector,
                      Evalues[i].EventForecast,
                      Evalues[i].EventPreval,
                      Evalues[i].EventImpact,
                      Evalues[i].EventFrequency);//Will print the sql query to check for any errors or possible defaults in the query/request

         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
  }


Depois de criarmos nossas tabelas e inserirmos os dados econômicos nelas, precisaremos de um carimbo de data/hora de quando essa ação foi realizada. Isso serve para saber quando atualizar a tabela.

void              CreateRecords(int db);//Creates a table to store records of when last the Calendar database was updated/created

Vamos criar uma tabela chamada Records e armazenar a hora atual do servidor nesta tabela toda vez que criarmos ou atualizarmos as tabelas de calendário.

void CNews::CreateRecords(int db)
  {
   bool failed=false;

   if(!DatabaseTableExists(db,"Records"))//Checks if the table 'Records' exists in the databse 'Calendar'
     {
      //--- create the table
      if(!DatabaseExecute(db,"CREATE TABLE Records(RECORDEDTIME INT NOT NULL);"))//Will attempt to create the table 'Records'
        {
         Print("DB: create the Records table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
     }

//Sql query/request to insert the current time into the 'RECORDEDTIME' column in the table 'Records'
   string request_text=StringFormat("INSERT INTO 'Records'(RECORDEDTIME) VALUES (%d)",(int)TimeCurrent());

   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat("INSERT INTO 'Records'(RECORDEDTIME) VALUES (%d)",(int)TimeCurrent());
      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());
     }
  }

 

Agora basicamente temos todas as nossas funções prontas; há mais três em que quero me concentrar. A primeira

bool              UpdateRecords();//Checks if the main Calendar database needs an update or not

Esta função retornará falso quando o registro máximo na tabela chamada Records estiver dentro da data atual (o que significa que as tabelas de calendário já foram criadas ou atualizadas no dia atual e nenhuma atualização adicional precisa ser realizada hoje). A função retornará verdadeiro se as tabelas de calendário não existirem ou se o banco de dados de calendário não existir ou se o registro máximo na tabela chamada Records não estiver dentro da data atual (dia).

bool CNews::UpdateRecords()
  {
//--- open/create
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READONLY|DATABASE_OPEN_COMMON);//try to open database Calendar

   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 true;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder
        }
     }

   if(!DatabaseTableExists(db,"Records"))//If the database table 'Records' doesn't exist
     {
      DatabaseClose(db);
      return true;
     }

   int recordtime=0;//will store the maximum date recorded in the database table 'Records'
   string request_text="SELECT MAX(RECORDEDTIME) FROM Records";//Sql query to determine the lastest or maximum date recorded
   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 true;
     }

   for(int i=0; DatabaseRead(request); i++)//Will read all the results from the sql query/request
     {
      if(!DatabaseColumnInteger(request, 0, recordtime))//Will assign the first column value to the variable 'recordtime'
        {
         Print(i, ": DatabaseRead() failed with code ", GetLastError());
         DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
         DatabaseClose(db);//Closes the database
         return true;
        }
     }

   DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
   DatabaseClose(db);//Closes the database

   if(!Time.DateisToday((datetime)recordtime))//Checks if the recorded time/date is today(current day)
     {
      return true;
     }

   return false;
  }


A segunda função em que quero me concentrar é

datetime          GetLastestNewsDate();//Gets the lastest/newest date in the Calendar database

Esta função é semelhante à última chamada UpdateRecords, mas a diferença é que GetLastestNewsDate apenas retornará o tempo máximo registrado na tabela Records. Isso será usado posteriormente para notificar o usuário se ele tentar testar uma data no strategy tester que seja posterior a essa data. Diremos ao usuário/trader que não há eventos econômicos para testar após essa data.

datetime CNews::GetLastestNewsDate()
  {
//--- 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
        {
         return 0;//Will return the earliest date which is 1970.01.01 00:00:00
        }
     }

   string eventtime="1970.01.01 00:00:00";//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 MAX(RECORDEDTIME) FROM Records";
   int request=DatabasePrepare(db,request_text);

   if(request==INVALID_HANDLE)
     {

      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return true;

     }

   for(int i=0; DatabaseRead(request); i++)//Will read all the results from the sql query/request
     {
      if(!DatabaseColumnText(request, 0,eventtime))//Will assign the first column(column 0) value to the variable 'eventtime'
        {

         Print(i, ": DatabaseRead() failed with code ", GetLastError());
         DatabaseFinalize(request);//Finalize request
         DatabaseClose(db);//Closes the database 'Calendar'
         return 0;//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 StringToTime(eventtime);//Returns the string eventtime converted to datetime
  }


Agora passaremos para a função que criará o banco de dados do Calendário, bem como chamará todas as outras funções que criamos anteriormente para criar as tabelas neste banco de dados e inserir valores nessas tabelas.

void              CreateEconomicDatabase();//Creates the Calendar database for a specific Broker

Esta é a função que chamaremos quando o expert advisor for anexado ao gráfico, para criar nosso banco de dados.

void CNews::CreateEconomicDatabase()
  {
   Print("Please wait...");

   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
        }
     }
   else
     {
      if(!AutoDetectDST(DSTType))//Check if AutoDetectDST went through all the right procedures
        {
         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;
   datetime lastestdate=D'1970.01.01 00:00:00';
//--- open/create the database 'Calendar'
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);//will try to open/create in the common folder

   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
     {
      file=FileOpen(NEWS_TEXT_FILE,FILE_WRITE|FILE_ANSI|FILE_TXT|FILE_COMMON);//try to create a text file 'NewsDatabaseOpen' in common folder
      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 calendar tables
   if(!CreateTable(db,"None",tableExists)||!CreateTable(db,"US",tableExists)
      ||!CreateTable(db,"UK",tableExists)||!CreateTable(db,"AU",tableExists))
     {
      FileClose(file);//Closing the file 'NewsDatabaseOpen.txt'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Deleting the file 'NewsDatabaseOpen.txt'
      return;//
     }

   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(!InsertIntoTable(db,DST_NONE,Evalues)||!InsertIntoTable(db,US_DST,Evalues)
      ||!InsertIntoTable(db,UK_DST,Evalues)||!InsertIntoTable(db,AU_DST,Evalues))
     {
      failed=true;//Will assign true if inserting economic vaules failed in any of the Data tables
     }

   if(failed)//Checks if the event/s failed to be recorded/inserted into the database table 'Data_%s'
     {
      //--- 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//if all the events were recorded or inserted into the tables 'Data_%s'
     {
      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);
        }

      CreateRecords(db);//Will create the 'Records' 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
     }
//--- all transactions have been performed successfully - record changes and unlock the database
   DatabaseTransactionCommit(db);
   DatabaseClose(db);//Close the database
  }



Agora passamos para o expert advisor, que executará todo o código que criamos em todas as diferentes classes e arquivos que criamos neste Projeto NewsTrading.

//+------------------------------------------------------------------+
//|                                                  NewsTrading.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "News.mqh"
CNews NewsObject;
#include "TimeManagement.mqh"
CTimeManagement CTM;
#include "WorkingWithFolders.mqh"
CFolders Folder();//Calling the class's constructor
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester
     {
      //Checks whether the database file exists and whether it has been modified in the current date
      if((!CTM.DateisToday((datetime)FileGetInteger(NEWS_DATABASE_FILE,FILE_MODIFY_DATE,true)))||(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON)))
        {
         /*
         In the Do while loop below, the code will check if the terminal is connected to the internet.
         Se o programa for interrompido, o loop será interrompido. Se o programa não for interrompido e o terminal estiver  conectado à Internet, a função CreateEconomicDatabase será chamada a partir do objeto do arquivo de cabeçalho News.mqh chamado NewsObject e o loop será interrompido quando chamado.
         */
         bool done=false;
         do
           {
            if(IsStopped())
              {
               done=true;
              }

            if(!TerminalInfoInteger(TERMINAL_CONNECTED))
              {
               Print("Waiting for connection...");
               Sleep(500);
               continue;
              }
            else
              {
               Print("Connection Successful!");
               NewsObject.CreateEconomicDatabase();//calling the database create function
               done=true;
              }
           }
         while(!done);
        }
     }
   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);
        }
      //Checks whether the lastest database date includes the time and date being tested
      datetime lastestdate = CTM.TimePlusOffset(NewsObject.GetLastestNewsDate(),CTM.DaysS());//Day after the lastest recorded time in the database
      if(lastestdate<TimeCurrent())
        {
         Print("Necessary Files OutDated!");
         Print("Database Dates End at: ",lastestdate);
         Print("Dates after %s will not be available for backtest",lastestdate);
         Print("To Update Files:");
         Print("Run Program outside of the Strategy Tester");
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

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

 Agora, tudo o que resta é compilar os arquivos, bem como o expert advisor, adicionar o expert advisor a um gráfico e começar a analisar o que codificamos.

E, mais importante, ver o que o Calendário Econômico tem a oferecer.

Estes são nossos arquivos do projeto:

Arquivos do Programa do Projeto




MQL5 Descrição do Calendário Econômico

Janela de Navegação


Depois de tudo compilado, abriremos o terminal de negociação e localizaremos a janela Navigator e abriremos nossa pasta NewsTrading.


Anexar Expert ao gráfico

Agora anexaremos o Expert Advisor a um gráfico de sua escolha, não habilitaremos "Allow Algo Trading" neste artigo porque nenhuma negociação será realizada. Basta pressionar OK e anexar o Expert.



Mensagens de Impressão do Expert

Depois que o Expert for anexado ao gráfico, você deverá ver um texto impresso na guia Experts na parte inferior do terminal, informando ao usuário que o banco de dados foi criado com sucesso.


CommonFolderIDE


Agora você terá que localizar o botão IDE na seção superior do terminal de negociação, depois de localizar o botão, pressione-o e você verá outra janela aberta separada

do terminal de negociação. Localize o botão Arquivo na seção superior esquerda do IDE e selecione Abrir Pasta de Dados Comum.


Diretório de Arquivos 

Seu diretório de arquivos deve ser semelhante a esta imagem acima, que mostrará os arquivos nesta pasta chamada Arquivos.


Pasta NewsTrading

Pasta NewsCalendar

Pasta Broker

Você verá que a pasta NewsTrading foi criada e muitas outras.


Diretório

Arquivo de Banco de Dados do Calendário

Depois de abrir todas as pastas, você encontrará o banco de dados separado do Calendário SQLite do seu corretor.

Agora voltaremos ao MQL5 IDE.


Ícone da Pasta

Selecione o ícone da Pasta na parte superior esquerda do IDE e você deve ser direcionado para a Pasta Comum da MetaQuotes.


Arquivo de Banco de Dados

Em seguida, localizaremos o banco de dados do Calendário SQLite do corretor e o abriremos.



Abrir Banco de Dados

Você deve ver o conteúdo do banco de dados na janela Navigator, clicaremos com o botão direito do mouse na primeira tabela e selecionaremos "Open Table".



Tabela AutoDST

A tabela será aberta e todos os registros (*) de 'AutoDST' serão exibidos. Haverá apenas um registro nesta tabela, pois esta será a tabela de calendário recomendada de DST do corretor.


Tabela Data_AU

Uma vez aberta a tabela 'Data_AU', a tabela exibirá todos os registros nela, bem como todas as colunas.



Junção de Tabelas do Calendário

SQL query:

SELECT  None.EVENTNAME, DATE(REPLACE(None.EVENTDATE,'.','-')) as Date, TIME(REPLACE(None.EVENTDATE,'.','-')) as Time_None,

TIME(REPLACE(US.EVENTDATE,'.','-')) as Time_US, TIME(REPLACE(UK.EVENTDATE,'.','-')) as Time_UK, TIME(REPLACE(AU.EVENTDATE,'.','-')) as Time_AU

FROM 'Data_None' None

INNER JOIN 'Data_US' US on US.ID=None.ID

INNER JOIN 'Data_UK' UK on UK.ID=None.ID

INNER JOIN 'Data_AU' AU on AU.ID=None.ID

WHERE Date BETWEEN '2023-01-01' AND '2024-01-01' AND None.EVENTID='840100001';

Propósito: Esta consulta é para exibir o nome do evento com o id de evento = '840100001' e a data do evento como 'Date' e a hora como 'Time_None' nas datas entre '2023-01-01' e '2024-01-01' da tabela 'Data_None', assim como selecionar o mesmo id de evento das tabelas 'Data_UK', 'Data_US', 'Data_AU', mas exibindo apenas a hora dos eventos dessas tabelas como 'Time_UK', 'Time_US', 'Time_AU'. Nesta consulta, queremos mostrar a diferença de tempo entre as diferentes tabelas de calendário para o mesmo evento durante todo o ano de 2023.

Na consulta SQL acima, damos à tabela 'Data_None' um alias chamado None, à tabela 'Data_US' um alias chamado US, também damos à tabela 'Data_UK' um alias chamado UK, e à tabela 'Data_AU' é dado o alias AU.

Nós SELECT

None.EVENTNAME

da tabela 'Data_None'.


Na Função SQL

REPLACE(None.EVENTDATE,'.','-')

Os caracteres de ponto ('.') no valor de None.EVENTDATE são substituídos por caracteres de hífen ('-'). Isso porque a SQL

DATE()

A função só aceita datas no formato datetime com um hífen como separador de caracteres e não datas no formato datetime com pontos como separador de caracteres. Essa função converte datetime em data.


TIME()

Esta função SQL acima converte datetime para hora.



Distinct Country

SQL query: 

SELECT Distinct(COUNTRY) FROM 'Data_None';

Propósito: Esta consulta nos fornece todos os países únicos que têm registros na tabela de calendário 'Data_None'.



Exportar Resultados

Se você quiser manter os resultados de suas consultas SQL para análise posterior, pode simplesmente clicar com o botão direito em um dos resultados e exportar o arquivo CSV, que pode ser convertido em um arquivo Excel

para uma análise mais detalhada. 


Nova Zelândia

SQL query: 

SELECT * FROM 'Data_None' where COUNTRY='New Zealand';

Propósito: Esta consulta nos fornece todos os registros para um país específico. Neste caso, esse país é a Nova Zelândia.



IDs de Eventos Únicos

SQL query: 

SELECT Distinct(EVENTID), COUNTRY, EVENTNAME,EVENTTYPE, EVENTSECTOR, EVENTIMPORTANCE, EVENTFREQUENCY,
EVENTCURRENCY, EVENTCODE FROM 'Data_None' 
where COUNTRY='New Zealand';

Propósito: Esta consulta recupera todos os IDs de eventos únicos para um país específico e seleciona colunas específicas, como "EVENTNAME" e "EVENTSECTOR", entre outras.

Mas especificamente não "EVENTDATE", pois cada "EVENTID" tem uma "EVENTDATE" diferente. Tentar incluir "EVENTDATE" nesta consulta retornará erros.



Setor de Eventos Específico

SQL query: 

SELECT Distinct(EVENTID), COUNTRY, EVENTNAME, EVENTTYPE, EVENTIMPORTANCE FROM 'Data_None' 
where COUNTRY='New Zealand' and EVENTSECTOR='CALENDAR_SECTOR_PRICES';

Propósito: Esta consulta recupera todos os IDs de eventos únicos para um "PAÍS" e "SETOR DE EVENTOS" específicos.



Eventos com Alta Importância

SQL query:

SELECT COUNTRY,EVENTNAME, EVENTIMPORTANCE FROM 'Data_None' 
where EVENTIMPORTANCE='CALENDAR_IMPORTANCE_HIGH'
UNION
SELECT COUNTRY,EVENTNAME, EVENTIMPORTANCE FROM 'Data_None' 
where EVENTIMPORTANCE='CALENDAR_IMPORTANCE_HIGH';

Propósito: Esta consulta recupera todos os registros únicos para Importância do Evento que é "CALENDAR_IMPORTANCE_HIGH".



Conclusão

Há muito o que absorver neste artigo; espero que você tenha aprendido algo novo e obtido novas ideias após ler este artigo. Neste artigo, abordamos por que usar um banco de dados foi a escolha certa e alguns benefícios de usar um banco de dados. Também abordamos todas as moedas fornecidas pelo Calendário Econômico MQL5 e o conceito de DST (horário de verão). Além disso, codificamos vários arquivos essenciais para criar o banco de dados do Calendário e verificar se devemos atualizar ou criar o banco de dados.

Finalmente, passamos pela localização dos arquivos do banco de dados depois que eles foram criados e extraímos dados específicos usando consultas SQL básicas para manipular a tabela. No próximo artigo, abordaremos a gestão de risco. Até a próxima.



Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/14324

Desenvolvimento de robô em Python e MQL5 (Parte 1): Pré-processamento de dados Desenvolvimento de robô em Python e MQL5 (Parte 1): Pré-processamento de dados
Esse será um guia detalhado sobre como desenvolver um robô de trading baseado em aprendizado de máquina. Realizaremos a coleta e preparação de dados e características. Para a execução do projeto, utilizaremos a linguagem de programação Python e bibliotecas, bem como a plataforma MetaTrader 5.
Agrupamento de séries temporais na inferência causal Agrupamento de séries temporais na inferência causal
Os algoritmos de agrupamento em aprendizado de máquina são ferramentas importantes de aprendizado não supervisionado que permitem dividir os dados brutos em grupos com características semelhantes. Com esses grupos, é possível, por exemplo, realizar análise de mercado para um cluster específico, identificar os clusters mais resilientes em novos conjuntos de dados e também realizar inferências causais. Este artigo apresenta um método original para o agrupamento de séries temporais, utilizando a linguagem Python.
Construa Expert Advisors Auto-Otimizáveis em MQL5 Construa Expert Advisors Auto-Otimizáveis em MQL5
Construa expert advisors que olhem para frente e se ajustem a qualquer mercado.
Do básico ao intermediário: Comandos BREAK e CONTINUE Do básico ao intermediário: Comandos BREAK e CONTINUE
Neste artigo veremos como usar os comando RETURN, BREAK e CONTINUE dentro de um laço. Entender o que cada um destes comandos faz no fluxo de execução de um laço é algo muito importante, para que você consiga trabalhar com aplicações mais elaboradas. O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como sendo, uma aplicação cuja finalidade não venha a ser o aprendizado e estudo dos conceitos mostrados.