Simplificando a negociação com base em notícias (Parte 6): Executando trades (III)
Introdução
Neste artigo, faremos melhorias na base de dados de armazenamento, adicionando novas visualizações de dados, como a exibição das datas das últimas notícias ou da próxima notícia para cada evento único no calendário econômico do MQL5 calendário econômico MQL5. Isso aprimorará a experiência do usuário ao utilizar o programa, pois permitirá que ele acompanhe eventos futuros ou passados. Além disso, o menu de entrada do EA será expandido para incluir a ordenação de notícias e métodos de entrada com ordens stop.
O código do EA também será atualizado para utilizar trechos anteriores criados com o objetivo de reduzir o tempo de execução no testador de estratégias, conforme descrito no artigo "Simplificando o trading em notícias (Parte 4): "Aumentando a performance", bem como o código do artigo "Simplificando a negociação com base em notícias (Parte 5): Executando operações (II)", onde tratamos do gerenciamento de slippage e da abertura de ordens stop.
Configurações de notícias
- SELECT NEWS OPTION - selecionar o perfil de notícias. Perfis disponíveis:
- NEWS SETTINGS - ordenar as notícias por:
- CALENDAR IMPORTANCE - importância no calendário
- EVENT FREQUENCY - frequência do evento
- EVENT SECTOR - setor ao qual o evento pertence
- EVENT TYPE - tipo de evento
- EVENT CURRENCY - moeda do evento
- CUSTOM NEWS EVENTS - ordenar as notícias de acordo com os identificadores dos eventos informados como parâmetros de entrada (até 14 identificadores de eventos por parâmetro).

Configurações de trade
- SELECT TRADE ENTRY OPTION - permitir diferentes métodos de entrada no trade. Métodos disponíveis:
- MARKET POSITION - somente execução a mercado (compra e venda). Devemos conhecer antecipadamente a direção do trade baseada na notícia selecionada (compra ou venda).
- STOP ORDERS - utilizar ordens stop de compra e venda antes da divulgação da notícia selecionada. É necessário definir um desvio de preço, de forma que o EA tenha uma margem de segurança para posicionar as ordens stop. Após a ativação de uma ordem stop, a ordem oposta será removida. Por exemplo, quando ordens stop de compra e venda são colocadas antes da divulgação do NFP (dados de emprego no setor não agrícola) e a ordem stop de compra é acionada (compra ao atingir determinado preço), o EA removerá a ordem stop de venda restante. Esse método de entrada não requer a ocorrência do evento para o pré-posicionamento das ordens.
- SINGLE STOP ORDER - utilizar uma única ordem stop — de compra ou de venda. Dois requisitos principais:
- Devemos conhecer antecipadamente a direção do trade baseada na notícia selecionada.
- É necessário definir um desvio de preço, de forma que o EA (Expert Advisor) tenha um buffer de preço para posicionar a ordem stop.

Classe News
No arquivo de cabeçalho News.mqh, declararemos o enum NewsSelection fora da classe CNews. O objetivo desse enum é permitir que os usuários escolham diferentes perfis de notícias dentro das entradas do EA. Também teremos a variável myNewsSelection, que armazenará a preferência selecionada pelo usuário. Além disso, será declarada uma estrutura chamada CustomEvent. Essa estrutura armazenará um valor lógico que define se os identificadores de eventos devem ser classificados em um array de strings chamado EventIds dentro da estrutura. A estrutura também declara variáveis como CEvent1. Essa variável atuará como uma das cinco opções pelas quais o usuário/trader poderá ordenar os identificadores de eventos personalizados.
Enumerações:
- NewsSelection define dois perfis:
- News_Select_Custom_Events - para notícias personalizadas.
- News_Select_Settings - para configurações de notícias.
- myNewsSelection - variável do tipo enum que armazena a seleção atual do perfil de notícias.
Estruturas:
- CustomEvent - estrutura usada para armazenar os identificadores de eventos personalizados e o flag (useEvents), que indica se esses eventos devem ser incluídos na consulta.
- Existem cinco variáveis: CEvent1, CEvent2, CEvent3, CEvent4 e CEvent5, todas do tipo CustomEvent, cada uma representando um grupo separado de eventos.
//--- Enumeration for News Profiles enum NewsSelection { News_Select_Custom_Events,//CUSTOM NEWS EVENTS News_Select_Settings//NEWS SETTINGS } myNewsSelection; //--- Structure to store event ids and whether to use these ids struct CustomEvent { bool useEvents; string EventIds[]; } CEvent1,CEvent2,CEvent3,CEvent4,CEvent5;
Enumeração CalendarComponents:
- CalendarComponents - enumera os vários componentes do calendário econômico, como tabelas e visualizações, usadas para estruturar dados relacionados à transição de horário de verão (DST), informações sobre eventos e dados de moedas.
Foram adicionados dois novos valores ao enum CalendarComponents:
- RecentEventInfo_View
- UpcomingEventInfo_View
//-- To keep track of what is in our database enum CalendarComponents { // ... RecentEventInfo_View,//View for Recent Dates For Events UpcomingEventInfo_View,//View for Upcoming Dates For Events // ... };
Função GetCalendar(CalendarData &Data[]):
- A função extrai todos os dados relevantes do calendário a partir do banco de dados de calendário no repositório e os armazena no array de dados.
- Ela abre o banco de dados (NEWS_DATABASE_FILE) e executa uma consulta SQL com base na seleção atual de notícias (myNewsSelection).
- Dependendo de o valor selecionado ser News_Select_Custom_Events ou News_Select_Settings, uma consulta SQL diferente é gerada para extrair as informações dos eventos.
- Custom Events - combina as tabelas MQL5Calendar e TimeSchedule para extrair notícias personalizadas utilizando um filtro baseado nos identificadores dos eventos personalizados.
- News Settings - extrai os dados dos eventos classificados de acordo com as configurações definidas pelo usuário, como importância, frequência, setor, tipo e moeda.
- A função processa os resultados da consulta SQL e armazena os dados obtidos no array Data.
- Se a consulta ao banco de dados não for executada corretamente, é exibida uma mensagem de erro juntamente com a consulta SQL que falhou.
//--- Will Retrieve all relevant Calendar data for DB in Memory from DB in Storage void GetCalendar(CalendarData &Data[]) { // ... string SqlRequest; //--- switch statement for different News Profiles switch(myNewsSelection) { case News_Select_Custom_Events://CUSTOM NEWS EVENTS //--- Get filtered calendar DB data SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID Where %s OR %s OR %s OR %s OR %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Events(CEvent1),Request_Events(CEvent2),Request_Events(CEvent3), Request_Events(CEvent4),Request_Events(CEvent5)); break; case News_Select_Settings://NEWS SETTINGS //--- Get filtered calendar DB data SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID " "Where %s and %s and %s and %s and %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Importance(myImportance),Request_Frequency(myFrequency), Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency)); break; default://Unknown break; } // ...
Função Request_Events(CustomEvent &CEvent):
- A função gera a cláusula SQL WHERE da consulta ao banco de dados com base nos identificadores dos eventos personalizados armazenados na estrutura CustomEvent.
- Ela verifica se useEvents é verdadeiro. Caso seja, adiciona cada identificador de evento do array CEvent.EventIds[] à consulta SQL.
//--- Retrieve Sql request string for custom event ids string Request_Events(CustomEvent &CEvent) { //--- Default request string string EventReq="MQ.EventId='0'"; //--- Check if this Custom event should be included in the SQL request if(CEvent.useEvents) { //--- Get request for first event id EventReq=StringFormat("(MQ.EventId='%s'", (CEvent.EventIds.Size()>0)? CEvent.EventIds[0]:"0"); //--- Iterate through remaining event ids and add to the SQL request for(uint i=1;i<CEvent.EventIds.Size();i++) { EventReq+=StringFormat(" OR MQ.EventId='%s'",CEvent.EventIds[i]); } EventReq+=")"; } //--- Return SQL request for custom event ids return EventReq; }
Membros públicos da classe:
As seguintes funções foram atualizadas: EconomicDetailsMemory, EconomicNextEvent e isEvent.
- EconomicDetailsMemory - extrai valores do banco de dados de calendário em memória.
- EconomicNextEvent - atualiza a variável estrutural com os dados do próximo evento.
- isEvent - garante que um evento de notícia está prestes a ocorrer e ajusta os parâmetros passados de forma correspondente.
//Public declarations accessable via a class's Object public: // ... void EconomicDetailsMemory(Calendar &NewsTime[],datetime date,bool ImpactRequired);//Gets values from the MQL5 DB Calendar in Memory void EconomicNextEvent();//Will update UpcomingNews structure variable with the next event data // ... //--- Checks if a news event is occurring and modifies the parameters passed by reference bool isEvent(uint SecondsPreEvent,string &Name,string &Importance,string &Code);
O objetivo da função EconomicDetailsMemory é extrair os dados dos eventos do calendário econômico a partir do banco de dados em memória (DBMemory) para uma data específica, considerando, quando necessário, o impacto do evento, e armazenar as informações no array NewsTime[].
- A função extrai do banco de dados os detalhes dos eventos econômicos para a data especificada e os armazena no array NewsTime[].
- Se ImpactRequired for true, a função extrai os valores anteriores e previstos do evento e define o nível de impacto (impact) com base nos dados históricos.
- Se ImpactRequired for false, ela simplesmente extrai os dados do dia atual e do próximo evento.
- Os resultados são obtidos através de uma consulta SQL preparada e armazenados no array NewsTime[], cujo tamanho é ajustado dinamicamente conforme o número de eventos recuperados.
//+------------------------------------------------------------------+ //|Gets values from the MQL5 DB Calendar in Memory | //+------------------------------------------------------------------+ void CNews::EconomicDetailsMemory(Calendar &NewsTime[],datetime date,bool ImpactRequired) { //--- SQL query to retrieve news data for a certain date string request_text; //--- Check if Event impact is required for retrieving news events if(ImpactRequired) { request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as" " 'Type',M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE" " as 'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'," "M.EVENTFREQUENCY as 'Freq' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))=DATE(REPLACE('%s','.','-'))" " AND (Forecast<>'None' AND Prevalue<>'None')),DAILY_IMPACT AS(SELECT DE.E_ID,DE.COUNTRY,DE.Name," "DE.Type,DE.Importance,DE.Time,DE.Currency,DE.Code,DE.Sector,DE.Forecast,DE.Prevalue,DE.Freq," "MC.EVENTIMPACT as 'IMPACT', RANK() OVER(PARTITION BY DE.E_ID,DE.Time ORDER BY MC.%s DESC)DateOrder" " FROM %s MC INNER JOIN DAILY_EVENTS DE on DE.E_ID=MC.EVENTID WHERE DATE(REPLACE(MC.%s,'.','-'))<" "DATE(REPLACE(DE.Time,'.','-')) AND DATE(REPLACE(MC.%s,'.','-'))>=DATE(REPLACE(DE.Time,'.','-')," "'-24 months') AND (MC.EVENTFORECAST<>'None' AND MC.EVENTPREVALUE<>'None' AND (CASE WHEN Forecast>" "Prevalue THEN 'more' WHEN Forecast<Prevalue THEN 'less' ELSE 'equal' END)=(CASE WHEN MC.EVENTFORECAST" ">MC.EVENTPREVALUE THEN 'more' WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN 'less' ELSE 'equal' END)) " "ORDER BY MC.%s),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_IMPACT WHERE DateOrder=1 ORDER BY Time" " ASC),NEXT_EVENT AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as 'Type'," "M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE as 'Code'," "M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE',M.EVENTFREQUENCY" " as 'Freq' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))>DATE(REPLACE('%s','.','-')) AND (Forecast<>" "'None' AND Prevalue<>'None' AND DATE(REPLACE(Time,'.','-'))<=DATE(REPLACE('%s','.','-'),'+60 days')))," "NEXT_IMPACT AS(SELECT NE.E_ID,NE.COUNTRY,NE.Name,NE.Type,NE.Importance,NE.Time,NE.Currency,NE.Code" ",NE.Sector,NE.Forecast,NE.Prevalue,NE.Freq,MC.EVENTIMPACT as 'IMPACT',RANK() OVER(PARTITION BY " "NE.E_ID,NE.Time ORDER BY MC.%s DESC)DateOrder FROM %s MC INNER JOIN NEXT_EVENT NE on NE.E_ID=MC.EVENTID " "WHERE DATE(REPLACE(MC.%s,'.','-'))<DATE(REPLACE(NE.Time,'.','-')) AND DATE(REPLACE(MC.%s,'.','-'))>=" "DATE(REPLACE(NE.Time,'.','-'),'-24 months') AND (MC.EVENTFORECAST<>'None' AND MC.EVENTPREVALUE<>'None'" " AND (CASE WHEN Forecast>Prevalue THEN 'more' WHEN Forecast<Prevalue THEN 'less' ELSE 'equal' END)=" "(CASE WHEN MC.EVENTFORECAST>MC.EVENTPREVALUE THEN 'more' WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN " "'less' ELSE 'equal' END)) ORDER BY MC.%s),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_IMPACT WHERE " "DateOrder=1 ORDER BY Time ASC LIMIT 1),ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL " "SELECT * FROM DAILY_EVENTS_RECORDS)SELECT E_ID,Country,Name,Type,Importance,Time,Currency,Code," "Sector,Forecast,Prevalue,Impact,Freq FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;", EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name, EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule) ,DBMemory.name,TimeToString(date),TimeToString(date),EnumToString(MySchedule),DBMemory.name, EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule)); } else { /* Within this request we select all the news events that will occur or have occurred in the current day and the next news event after the current day */ request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as" " 'Type',M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE" " as 'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'" ",M.EVENTFREQUENCY as 'Freq',M.EVENTIMPACT as 'Impact' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))" "=DATE(REPLACE('%s','.','-'))),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_EVENTS ORDER BY Time ASC)" ",NEXT_EVENT AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as 'Type'," "M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE as " "'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'," "M.EVENTFREQUENCY as 'Freq',M.EVENTIMPACT as 'Impact' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))" ">DATE(REPLACE('%s','.','-')) AND (DATE(REPLACE(Time,'.','-'))<=DATE(REPLACE('%s','.','-')," "'+60 days'))),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_EVENT ORDER BY Time ASC LIMIT 1)," "ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL SELECT * FROM " "DAILY_EVENTS_RECORDS)SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;", EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name, TimeToString(date),TimeToString(date)); } // ...
Assinatura da função
- Argumentos:
- Calendar &NewsTime[] - array de estruturas de calendário (cada uma contendo informações dos eventos) que será preenchido com os dados extraídos.
- datetime date - data específica para a qual os eventos econômicos serão extraídos.
- bool ImpactRequired - flag que indica se o impacto do evento deve ser considerado na consulta.
Construção da consulta SQL com base nos requisitos de impacto (impact)
A consulta SQL varia dependendo de o parâmetro ImpactRequired estar definido como true ou false. Quando true, a direção do trade requer o impacto do evento; quando false, o impacto não é necessário, pois a direção do evento não é um fator essencial para a abertura da posição.
A. Quando ImpactRequired é true
Essa consulta é mais complexa e composta por várias partes:
- A consulta extrai os eventos econômicos da data especificada, levando também em consideração o próximo evento após o dia atual.
- Ela busca eventos anteriores ocorridos nos últimos 24 meses, para os quais existem tanto valores previstos quanto valores anteriores, e os compara com o evento atual.
- Se o valor previsto for maior, menor ou até mesmo igual ao valor anterior, ele é associado a um evento histórico que apresentou a mesma tendência (previsão > valor anterior, previsão < valor anterior ou previsão = valor anterior).
- O impacto do evento passado é então atribuído ao evento atual.
A estrutura da consulta é a seguinte:
"WITH DAILY_EVENTS AS(...) , DAILY_IMPACT AS(...) , NEXT_EVENT AS(...), NEXT_IMPACT AS(...),
ALL_EVENTS AS(...) SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;"
- DAILY_EVENTS seleciona todos os eventos econômicos da data especificada no calendário. São selecionados apenas os eventos que possuem valores previstos e anteriores válidos.
- DAILY_IMPACT busca eventos históricos (nos últimos 24 meses) cuja tendência entre o valor previsto e o valor anterior (maior/menor ou igual) seja semelhante ao evento atual. Os eventos são classificados por data para identificar o mais recente com tendência correspondente, e seu impacto é atribuído ao evento atual.
- NEXT_EVENT seleciona o próximo evento futuro após o dia atual.
- NEXT_IMPACT, de forma semelhante a DAILY_IMPACT, extrai o evento passado mais recente que apresenta a mesma tendência entre previsão e valor anterior, atribuindo o impacto desse evento ao próximo evento.
- ALL_EVENTS combina DAILY_EVENTS_RECORDS (para o dia atual) e NEXT_EVENT_RECORD (para o próximo evento), organizando ambos cronologicamente.
B. Quando ImpactRequired é false
Quando o impacto não é necessário, a consulta é simplificada:
- Ela extrai todos os eventos do dia atual e o próximo evento após esse dia (dentro de um período de 60 dias).
Estrutura da consulta SQL:
"WITH DAILY_EVENTS AS(...) , NEXT_EVENT AS(...), ALL_EVENTS AS(...) SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;"
- DAILY_EVENTS extrai os eventos econômicos do dia atual.
- NEXT_EVENT extrai o próximo evento após o dia atual dentro dos 60 dias seguintes.
- ALL_EVENTS combina os eventos diários e o próximo evento, organizando-os por horário.
Execução da consulta SQL
int request = DatabasePrepare(DBMemoryConnection, request_text);
- DatabasePrepare prepara a consulta SQL para execução. O resultado armazenado é um handle da consulta. que será utilizado para a extração dos dados.
- Error Handling – se a consulta não for executada corretamente (request == INVALID_HANDLE), é exibida uma mensagem de erro junto com a consulta SQL, para fins de depuração.
Leitura dos resultados
Calendar ReadDB_Data; ArrayRemove(NewsTime, 0, WHOLE_ARRAY); for (int i = 0; DatabaseReadBind(request, ReadDB_Data); i++) { ArrayResize(NewsTime, i + 1, i + 2); NewsTime[i] = ReadDB_Data; }
- ArrayRemove limpa o array NewsTime[], preparando-o para receber novos dados.
- DatabaseReadBind extrai os resultados da consulta preparada, vinculando cada linha de resultado à variável ReadDB_Data, que representa uma estrutura de calendário.
- ArrayResize ajusta o tamanho do array NewsTime[] para acomodar os novos dados. Para cada linha retornada por DatabaseReadBind, o array é expandido em 1 elemento.
- NewsTime[i] = ReadDB_Data copia os dados extraídos para o array NewsTime[].
Finalização da consulta
DatabaseFinalize(request);
- DatabaseFinalize libera os recursos alocados por DatabasePrepare, liberando o handle da consulta.
A função EconomicNextEvent é responsável por identificar a próxima notícia econômica futura e atualizar a variável UpcomingNews com seus detalhes.
- A função percorre todos os eventos contidos em CalendarArray e encontra aquele que será o próximo evento futuro, com base no horário do servidor (TimeTradeServer()).
- Ela atualiza a estrutura UpcomingNews adicionando as informações sobre o próximo evento.
- A lógica garante que apenas eventos futuros (em relação ao horário do servidor) sejam considerados e seleciona o evento mais próximo no tempo.
//+------------------------------------------------------------------+ //|Will update UpcomingNews structure variable with the next news | //|event data | //+------------------------------------------------------------------+ void CNews::EconomicNextEvent() { //--- Declare unassigned Calendar structure variable Next Calendar Next; //--- assign empty values to Calendar structure variable UpcomingNews UpcomingNews = Next; //--- assign default date datetime NextEvent=0; //--- Iterate through CalendarArray to retrieve news events for(uint i=0;i<CalendarArray.Size();i++) { //--- Check for next earliest news event from CalendarArray if((NextEvent==0)||(TimeTradeServer()<datetime(CalendarArray[i].EventDate) &&NextEvent>datetime(CalendarArray[i].EventDate))||(NextEvent<TimeTradeServer())) { //--- assign values from the CalendarArray NextEvent = datetime(CalendarArray[i].EventDate); Next = CalendarArray[i]; } } //--- assign the next news event data into UpcomingNews variable UpcomingNews = Next; }
Declaração da estrutura de calendário não atribuída
Calendar Next;
- A variável Next é declarada como uma estrutura Calendar (estrutura personalizada que contém informações do evento, como data, nome, país etc.).
- Inicialmente, ela não está atribuída e mais tarde armazenará os dados referentes ao próximo evento econômico.
Atribuição de valores vazios a UpcomingNews
UpcomingNews = Next;
- UpcomingNews é uma variável global ou de nível de classe (outra estrutura Calendar) que armazena as informações do próximo evento futuro.
- No início, ela é redefinida para os valores padrão (vazios) da variável Next.
Atribuição de data padrão
datetime NextEvent = 0;
- A variável NextEvent é inicializada com o valor 0, indicando que ainda não há evento atribuído.
- NextEvent armazenará o carimbo de data/hora (em formato datetime) do próximo evento econômico durante o loop.
Iteração sobre CalendarArray
for (uint i = 0; i < CalendarArray.Size(); i++)
- O array CalendarArray contém informações detalhadas sobre os eventos econômicos.
- O loop for percorre cada elemento desse array (cada um representando um evento), verificando se o evento atual é o próximo evento futuro.
Verificação das condições do próximo evento
if ((NextEvent == 0) || (TimeTradeServer() < datetime(CalendarArray[i].EventDate) && NextEvent > datetime(CalendarArray[i].EventDate)) || (NextEvent < TimeTradeServer()))
A instrução if verifica várias condições para determinar se o evento atual no array (CalendarArray[i]) é o próximo evento de notícia:
- Primeira condição: (NextEvent == 0)
- Se NextEvent ainda for igual a 0 (nenhum evento atribuído), o evento atual será selecionado como o próximo evento.
- Segunda condição: (TimeTradeServer() < datetime(CalendarArray[i].EventDate) && NextEvent > datetime(CalendarArray[i].EventDate))
- Dessa forma, é verificado se o evento atual no array (CalendarArray[i]) ocorre após o horário atual do servidor (TimeTradeServer()) e antes do evento atualmente armazenado em NextEvent. Se essa condição for verdadeira, o evento atual passa a ser considerado o próximo evento.
- Terceira condição: (NextEvent < TimeTradeServer())
- Se o evento armazenado em NextEvent já tiver ocorrido (ou seja, estiver no passado), a função continua buscando um evento futuro válido.
Atribuição de valores a partir de CalendarArray
NextEvent = datetime(CalendarArray[i].EventDate);
Next = CalendarArray[i];
- Se pelo menos uma das condições for satisfeita, o evento atual em CalendarArray[i] é definido como o próximo evento:
- NextEvent é atualizado com a data do evento atual.
- Next é atualizado para incluir todos os detalhes (data, nome, tipo, etc.) do evento atual.
Definição do próximo evento em UpcomingNews
UpcomingNews = Next;
- Após o término da iteração pelo CalendarArray, a variável UpcomingNews é atualizada com as informações do próximo evento futuro (armazenadas em Next).
- A função garante que o primeiro evento futuro encontrado em relação ao horário atual do servidor seja salvo em UpcomingNews.
A função isEvent verifica se um evento de notícia ocorrerá em breve ou está ocorrendo no momento dentro de um intervalo de tempo especificado. O objetivo dessa função é determinar se algum evento de notícia do array CalendarArray está em andamento ou prestes a acontecer, com base no deslocamento de tempo definido. Se tal evento for encontrado, são retornados os detalhes do evento, como nome, importância e código.
- A função percorre o array CalendarArray (que contém os dados dos eventos econômicos) e verifica se há algum evento ocorrendo ou prestes a ocorrer dentro do intervalo de tempo definido por SecondsPreEvent.
- Se um evento for encontrado, ela atualiza as variáveis de nome, importância e código de acordo com as informações do evento e retorna o valor true.
- Se nenhum evento for encontrado dentro do intervalo especificado, a função retorna false, mantendo as variáveis nome, importância e código inalteradas (ou definidas para o valor padrão/NULL).
//+------------------------------------------------------------------+ //|Checks if News is event is about to occur or is occurring | //+------------------------------------------------------------------+ bool CNews::isEvent(uint SecondsPreEvent,string &Name,string &Importance,string &Code) { //--- assign default value Name=NULL; //--- Iterate through CalendarArray for(uint i=0;i<CalendarArray.Size();i++) { //--- Check if news event is within a timespan if(CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate),SecondsPreEvent), CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate),59))) { //--- assign appropriate CalendarArray values Name=CalendarArray[i].EventName; Importance=CalendarArray[i].EventImportance; Code=CalendarArray[i].EventCode; //--- news event is currently within the timespan return true; } } //--- no news event is within the current timespan. return false; }
Parâmetros:
- uint SecondsPreEvent - número de segundos antes do evento dentro do qual ele é considerado "iminente".
- string &Name - referência para a string onde será armazenado o nome do evento, caso esteja dentro do intervalo de tempo definido.
- string &Importance - referência para a string onde será armazenado o nível de importância do evento, caso ele seja encontrado.
- string &Code - referência para a string onde será armazenado o código do evento.
Atribuição de valor padrão ao nome
Name = NULL;
- A variável Name é inicializada com o valor NULL. Caso nenhum evento seja encontrado dentro do intervalo de tempo especificado, Name permanecerá igual a NULL.
- Isso garante que Name só será atualizada se um evento for detectado dentro do período de tempo definido.
Iteração sobre CalendarArray
for (uint i = 0; i < CalendarArray.Size(); i++)
- O loop percorre cada elemento do array CalendarArray, em que cada elemento representa um evento de notícia.
- O ciclo verifica cada evento para determinar se ele se encontra dentro do intervalo de tempo especificado em relação ao horário atual.
Verificação se o evento está dentro do intervalo de tempo definido
if (CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate), SecondsPreEvent), CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate), 59)))
- CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate), SecondsPreEvent):
- Essa chamada de função verifica se a data do evento (CalendarArray[i].EventDate), subtraída do valor SecondsPreEvent, está no passado.
- Em essência, ela define o limite inferior da janela de tempo (quantos segundos antes do evento).
- CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate), 59):
- A função verifica se a data do evento está até 59 segundos no futuro, definindo o limite superior da janela de tempo (quanto tempo o evento é considerado ativo).
- A função CTime.TimeIsInRange verifica se o horário atual está dentro desse intervalo de tempo. Se o horário atual estiver dentro do intervalo, isso significa que o evento está prestes a ocorrer ou já está em andamento.
Atribuição de valores a partir de CalendarArray
Name = CalendarArray[i].EventName; Importance = CalendarArray[i].EventImportance; Code = CalendarArray[i].EventCode;
- Se o evento estiver dentro do intervalo de tempo especificado, os dados correspondentes (nome do evento, importância e código) são extraídos de CalendarArray e atribuídos aos parâmetros passados por referência:
- Name - nome do evento de notícia.
- Importance - nível de importância do evento.
- Code - código do evento.
Retorno true se o evento for encontrado
return true;
- Se um evento de notícia for identificado dentro do intervalo de tempo especificado, a função retorna true, indicando que o evento correspondente está ocorrendo ou prestes a ocorrer.
Retorno false se o evento não for encontrado
return false;
- Se o loop percorrer todos os eventos em CalendarArray e nenhum evento for encontrado dentro do intervalo de tempo definido, a função retorna false, indicando que não há eventos correspondentes no momento.
Construtor CNews::CNews(void):
- Inicializa a classe configurando as instruções SQL DROP, CREATE e INSERT para os diversos componentes do calendário econômico.
- Tables — define tabelas como AutoDST, Record, TimeSchedule e MQL5Calendar.
- Views — define visualizações para diferentes programações de horário de verão (Calendar_AU, Calendar_UK, etc.), informações sobre eventos, moedas e datas de eventos recentes ou futuros.
- Triggers — define gatilhos como OnlyOne_AutoDST e OnlyOne_Record, que garantem que exista apenas um registro em determinadas tabelas a qualquer momento.
Cada componente (tabela, visualização ou gatilho) é inicializado com os comandos SQL correspondentes, incluindo criação, inserção e exclusão de registros.
Criação de visualizações SQL:
- Para cada componente de programação e calendário de horário de verão, são definidas visualizações SQL específicas que estruturam os dados conforme diferentes critérios (por exemplo, pela importância do evento, moeda ou data do evento). Por exemplo:
- View for Upcoming Events — exibe as datas dos próximos eventos, juntamente com os dias da semana e detalhes sobre cada evento.
- View for Recent Events — de forma semelhante à visualização anterior, mas recupera as datas dos eventos recentes.
Gatilhos SQL:
- Os gatilhos são usados para garantir que as tabelas AutoDST e Record contenham apenas um registro em qualquer momento, excluindo registros existentes antes de inserir novos.
//+------------------------------------------------------------------+ //|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 { // ... string views[] = {"AU","NONE","UK","US"}; //-- Sql statement for creating the table views for each DST schedule string view_sql = "CREATE VIEW IF NOT EXISTS Calendar_%s " "AS " "SELECT C.Eventid as 'ID',C.Eventname as 'Name',C.Country as 'Country', " "(CASE WHEN Date(REPLACE(T.DST_%s,'.','-'))<R.Date THEN CONCAT(T.DST_%s,' | Yesterday') " "WHEN Date(REPLACE(T.DST_%s,'.','-'))=R.Date THEN CONCAT(T.DST_%s,' | Today') " "WHEN Date(REPLACE(T.DST_%s,'.','-'))>R.Date THEN CONCAT(T.DST_%s,' | Tomorrow') END) as " "'Date',C.EventCurrency as 'Currency',Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','')" " as 'Importance' from MQL5Calendar C,Record R Inner join TimeSchedule T on C.ID=T.ID Where" " DATE(REPLACE(T.DST_%s,'.','-'))>=DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_%s,'.','-'))" "<=DATE(R.Date,'+1 day') Order by T.DST_%s Asc;"; // ... //--- initializing properties for the EventInfo view CalendarContents[5].Content = EventInfo_View; CalendarContents[5].name = "Event Info"; CalendarContents[5].sql = "CREATE VIEW IF NOT EXISTS 'Event Info' " "AS SELECT DISTINCT MC.EVENTID as 'ID',MC.COUNTRY as 'Country',MC.EVENTNAME as 'Name'," "REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector'," "REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',MC.EVENTCURRENCY as 'Currency'," "REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency',MC.EVENTCODE as 'Code' " "FROM MQL5Calendar MC ORDER BY \"Country\" Asc," "CASE \"Importance\" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,\"Sector\" Desc;"; CalendarContents[5].tbl_name = "Event Info"; CalendarContents[5].type = "view"; // ... //--- initializing properties for the UpcomingEventInfo view CalendarContents[7].Content = UpcomingEventInfo_View; CalendarContents[7].name = "Upcoming Event Dates"; CalendarContents[7].sql = "CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID'," "M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M)," "INFO_DATE AS(SELECT E_ID,Country,Name,Currency,(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M," "Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))>R.Date AND " "E_ID=M.EVENTID ORDER BY Time ASC LIMIT 1) as 'Next Event Date' FROM UNIQUE_EVENTS) SELECT E_ID " "as 'ID',Country,Name,Currency,(CASE WHEN \"Next Event Date\" IS NULL THEN 'Unknown' ELSE " "\"Next Event Date\" END) as 'Upcoming Date',(CASE WHEN \"Next Event Date\"<>'Unknown' THEN " "(case cast (strftime('%w', DATE(REPLACE(\"Next Event Date\",'.','-'))) as integer) WHEN 0 THEN" " 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday'" " WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE Order BY " "\"Upcoming Date\" ASC;"; CalendarContents[7].tbl_name = "Upcoming Event Dates"; CalendarContents[7].type = "view"; //--- initializing properties for the RecentEventInfo view CalendarContents[8].Content = RecentEventInfo_View; CalendarContents[8].name = "Recent Event Dates"; CalendarContents[8].sql = "CREATE VIEW IF NOT EXISTS 'Recent Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID" " as 'E_ID',M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency'" "FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency," "(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON" " T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))<=R.Date AND E_ID=M.EVENTID ORDER BY Time DESC" " LIMIT 1) as 'Last Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name,Currency" ",\"Last Event Date\" as 'Recent Date',(case cast (strftime('%w', DATE(REPLACE(\"Last Event Date\"" ",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN" " 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) as 'Day' FROM INFO_DATE" " Order BY \"Recent Date\" DESC;"; CalendarContents[8].tbl_name = "Recent Event Dates"; CalendarContents[8].type = "view"; // ...
A consulta abaixo é responsável pela criação de quatro visualizações semelhantes, porém distintas: Calendar_AU, Calendar_NONE, Calendar_UK e Calendar_US. Cada uma dessas visualizações extrai dados de três tabelas: MQL5Calendar, Record e TimeSchedule. Essas consultas criam visualizações que exibem informações de eventos (identificador, nome, país, data, moeda e importância) para diferentes fusos horários — Austrália, Padrão (None), Reino Unido e Estados Unidos. A data de cada evento é marcada de acordo com sua relação com a data atual — se ocorreu ontem, hoje ou ocorrerá amanhã — e os resultados são organizados para mostrar apenas eventos dentro de um intervalo de um dia em relação à data atual. As visualizações são ordenadas pela data do evento no respectivo fuso horário.
Para exemplificar, utilizamos a visualização Calendar_AU.
Consulta completa visível:
CREATE VIEW IF NOT EXISTS Calendar_AU AS SELECT C.Eventid as 'ID',C.Eventname as 'Name',C.Country as 'Country', (CASE WHEN Date(REPLACE(T.DST_AU,'.','-'))<R.Date THEN CONCAT(T.DST_AU,' | Yesterday') WHEN Date(REPLACE(T.DST_AU,'.','-'))=R.Date THEN CONCAT(T.DST_AU,' | Today') WHEN Date(REPLACE(T.DST_AU,'.','-'))>R.Date THEN CONCAT(T.DST_AU,' | Tomorrow') END) as 'Date', C.EventCurrency as 'Currency',Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','') as 'Importance' from MQL5Calendar C, Record R Inner join TimeSchedule T on C.ID=T.ID Where DATE(REPLACE(T.DST_AU,'.','-'))>=DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_AU,'.','-'))<=DATE(R.Date,'+1 day') Order by T.DST_AU Asc;
CREATE VIEW IF NOT EXISTS Calendar_AU — cria a visualização com o nome Calendar_AU, caso ainda não exista. Uma visualização é, essencialmente, uma tabela virtual criada a partir de uma consulta, permitindo acessar dados sem a necessidade de armazená-los novamente.
Expressão SELECT:
SELECT C.Eventid as 'ID', C.Eventname as 'Name', C.Country as 'Country', (CASE WHEN Date(REPLACE(T.DST_AU,'.','-')) < R.Date THEN CONCAT(T.DST_AU, ' | Yesterday') WHEN Date(REPLACE(T.DST_AU,'.','-')) = R.Date THEN CONCAT(T.DST_AU, ' | Today') WHEN Date(REPLACE(T.DST_AU,'.','-')) > R.Date THEN CONCAT(T.DST_AU, ' | Tomorrow') END) as 'Date', C.EventCurrency as 'Currency', Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','') as 'Importance'
Essa parte da consulta seleciona campos específicos das tabelas MQL5Calendar, Record e TimeSchedule e formata os dados de maneira apropriada:
- C.Eventid — identificador do evento.
- C.Eventname — nome do evento.
- C.Country — país associado ao evento.
- O operador CASE é utilizado para comparar a data DST_AU (fuso horário australiano, armazenado em TimeSchedule) com R.Date (data atual da tabela Record), a fim de marcar o evento como Yesterday (ontem), Today (hoje) ou Tomorrow (amanhã).
- C.EventCurrency — moeda associada ao evento.
- Replace(C.EventImportance, 'CALENDAR_IMPORTANCE_', '') remove o prefixo CALENDAR_IMPORTANCE_ do campo EventImportance, extraindo apenas o nível de importância correspondente (por exemplo, HIGH — alto, LOW — baixo).
Expressão FROM:
FROM MQL5Calendar C, Record R Inner join TimeSchedule T on C.ID=T.ID
- A consulta extrai dados de três tabelas: MQL5Calendar (C), Record (R) e TimeSchedule (T).
- As tabelas MQL5Calendar e Record fazem parte da cláusula FROM, enquanto a tabela TimeSchedule é associada por meio de um INNER JOIN sob a condição C.ID = T.ID, o que significa que o identificador da tabela MQL5Calendar deve coincidir com o identificador da tabela TimeSchedule.
Expressão WHERE:
WHERE DATE(REPLACE(T.DST_AU,'.','-')) >= DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_AU,'.','-')) <= DATE(R.Date,'+1 day')
- Permite filtrar os resultados para incluir apenas os eventos cuja data DST_AU (data do evento no fuso horário australiano) esteja dentro de um intervalo de um dia antes ou depois da data atual (R.Date).
Expressão ORDER BY:
ORDER BY T.DST_AU Asc;
- Ordena os eventos em ordem crescente com base na data DST_AU — a data no horário da Austrália.
Conceitos-chave:
- Formatação de data e hora: a função REPLACE() é usada para substituir pontos “.” por hífens “-” nas colunas DST_, que armazenam as datas como strings (por exemplo, 2024.09.23 é convertido em 2024-09-23).
- Lógica condicional: o operador CASE verifica se a data do evento (DST_AU, DST_NONE, DST_UK ou DST_US) é anterior, igual ou posterior à data atual (R.Date) e adiciona o rótulo correspondente (Yesterday, Today ou Tomorrow).
- Ordenação: a expressão WHERE garante que a visualização inclua apenas os eventos dentro de um intervalo de um dia antes ou depois da data atual.
- Extração da importância: a função REPLACE() remove o prefixo da coluna EventImportance, exibindo apenas o nível real de importância (por exemplo, HIGH — alto, MEDIUM — médio ou LOW — baixo).
Saída da visualização Calendar_AU:
ID Name Country Date Currency Importance 392080012 Autumnal Equinox Day Japan 2024.09.22 02:00 | Yesterday JPY NONE 554010007 Exports New Zealand 2024.09.23 00:45 | Today NZD LOW 554010008 Imports New Zealand 2024.09.23 00:45 | Today NZD LOW // ... 710010010 Heritage Day South Africa 2024.09.24 02:00 | Tomorrow ZAR NONE 36030005 RBA Rate Statement Australia 2024.09.24 07:30 | Tomorrow AUD MODERATE // ...
A consulta abaixo cria uma visualização chamada Event Info (informações do evento), que seleciona e organiza os dados sobre os eventos da tabela MQL5Calendar. Essa visualização extrai informações específicas dos eventos contidas na tabela MQL5Calendar e formata os dados para torná-los mais legíveis e adequados para análise. Ela também limpa os nomes dos campos, removendo prefixos desnecessários (CALENDAR_TYPE_, CALENDAR_SECTOR_, etc.).
CREATE VIEW IF NOT EXISTS 'Event Info' AS SELECT DISTINCT MC.EVENTID as 'ID',MC.COUNTRY as 'Country',MC.EVENTNAME as 'Name', REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector', REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',MC.EVENTCURRENCY as 'Currency', REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency',MC.EVENTCODE as 'Code' FROM MQL5Calendar MC ORDER BY "Country" Asc,CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,"Sector" Desc;
CREATE VIEW IF NOT EXISTS 'Event Info'
Esta parte cria a visualização chamada Event Info, caso ela ainda não exista. Uma visualização em SQL é uma tabela virtual baseada no resultado de uma consulta SELECT, que permite encapsular uma consulta complexa e referenciá-la como se fosse uma tabela.
Expressão SELECT DISTINCT:
SELECT DISTINCT MC.EVENTID as 'ID', MC.COUNTRY as 'Country', MC.EVENTNAME as 'Name', REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type', REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector', REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance', MC.EVENTCURRENCY as 'Currency', REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency', MC.EVENTCODE as 'Code'
Nesta seção, são extraídas linhas únicas (sem duplicatas) da tabela MQL5Calendar (MC — alias desta tabela) e selecionadas colunas específicas para formar a visualização.
Campos selecionados:
- MC.EVENTID as 'ID' — extrai o identificador único de cada evento e o renomeia como 'ID'.
- MC.COUNTRY as 'Country' — extrai o país associado ao evento.
- MC.EVENTNAME as 'Name' — extrai o nome do evento.
Transformação de dados com uso de REPLACE():
Em alguns campos, utiliza-se a função REPLACE() para remover prefixos como CALENDAR_TYPE_, CALENDAR_SECTOR_, CALENDAR_IMPORTANCE_ e CALENDAR_FREQUENCY_, mantendo apenas a parte significativa do campo:
- REPLACE(MC.EVENTTYPE, 'CALENDAR_TYPE_', '') as 'Type' — remove o prefixo CALENDAR_TYPE_ do campo EVENTTYPE, resultando em um valor mais limpo (por exemplo, em vez de CALENDAR_TYPE_CONSUMER, obtém-se CONSUMER).
- REPLACE(MC.EVENTSECTOR, 'CALENDAR_SECTOR_', '') as 'Sector' — remove o prefixo CALENDAR_SECTOR_ do campo EVENTSECTOR, exibindo apenas o nome do setor.
- REPLACE(MC.EVENTIMPORTANCE, 'CALENDAR_IMPORTANCE_', '') as 'Importance' — remove o prefixo CALENDAR_IMPORTANCE_ do campo EVENTIMPORTANCE, exibindo o nível de importância (por exemplo, HIGH, MODERATE ou LOW).
- MC.EVENTCURRENCY as 'Currency' — extrai a moeda associada ao evento.
- REPLACE(MC.EVENTFREQUENCY, 'CALENDAR_FREQUENCY_', '') as 'Frequency' — remove o prefixo CALENDAR_FREQUENCY_ do campo EVENTFREQUENCY, indicando a frequência do evento (por exemplo, MONTHLY ou QUARTERLY).
- MC.EVENTCODE as 'Code' — extrai o código do evento sem realizar nenhuma transformação adicional.
Expressão FROM:
FROM MQL5Calendar MC
A consulta extrai dados da tabela MQL5Calendar, que recebe o alias MC.
Expressão ORDER BY:
ORDER BY "Country" Asc, CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END, "Sector" Desc
Nesta seção, os dados são organizados com base em múltiplos campos para estruturar a saída da visualização.
Ordenação por país (em ordem crescente):
- O primeiro critério de ordenação é o campo Country em ordem crescente (Asc), agrupando e classificando os eventos alfabeticamente de acordo com o país.
Ordenação por importância (ordem personalizada):
- O operador CASE é usado para ordenar os níveis de importância conforme uma hierarquia de prioridade definida:
- O nível de importância 'HIGH' (alto) recebe o valor 1 — maior prioridade.
- O nível 'MODERATE' (médio) recebe o valor 2.
- O nível 'LOW' (baixo) recebe o valor 3.
- Qualquer outro valor (por exemplo, NONE) recebe o valor 4 — menor prioridade.
Isso significa que os eventos com importância HIGH serão exibidos primeiro, seguidos pelos eventos MODERATE, depois LOW e, por fim, NONE.
Ordenação por setor (em ordem decrescente):
- Por último, os eventos são ordenados pelo campo Sector em ordem decrescente (Desc). Essa ordenação organiza setores como MONEY (dinheiro), CONSUMER (consumo) e outros em ordem alfabética inversa.
Exemplo de saída da visualização Event Info:
ID Country Name Type Sector Importance Currency Frequency Code 36030008 Australia RBA Interest Rate Decision INDICATOR MONEY HIGH AUD NONE AU 36030006 Australia RBA Governor Lowe Speech EVENT MONEY HIGH AUD NONE AU 36010003 Australia Employment Change INDICATOR JOBS HIGH AUD MONTH AU // ... 36010036 Australia Current Account INDICATOR TRADE MODERATE AUD QUARTER AU 36010011 Australia Trade Balance INDICATOR TRADE MODERATE AUD MONTH AU 36010029 Australia PPI q/q INDICATOR PRICES MODERATE AUD QUARTER AU // ... 36010009 Australia Exports m/m INDICATOR TRADE LOW AUD MONTH AU 36010010 Australia Imports m/m INDICATOR TRADE LOW AUD MONTH AU 36010037 Australia Net Exports Contribution INDICATOR TRADE LOW AUD QUARTER AU // ... 76020002 Brazil BCB Interest Rate Decision INDICATOR MONEY HIGH BRL NONE BR // ...
A consulta abaixo cria a visualização Recent Event Dates (datas dos eventos recentes), que apresenta um resumo dos últimos eventos extraídos da tabela MQL5Calendar, incluindo o dia da semana em que o evento ocorreu. Essa visualização lista os eventos mais recentes da tabela MQL5Calendar, destacando o dia da semana correspondente a cada um. O foco está em exibir o evento mais recente para cada item individual do calendário.
CREATE VIEW IF NOT EXISTS 'Recent Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID',M.COUNTRY as 'Country', M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency'FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency, (SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-')) <=R.Date AND E_ID=M.EVENTID ORDER BY Time DESC LIMIT 1) as 'Last Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name, Currency,"Last Event Date" as 'Recent Date',(case cast (strftime('%w', DATE(REPLACE("Last Event Date",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) as 'Day' FROM INFO_DATE Order BY "Recent Date" DESC;
CREATE VIEW IF NOT EXISTS 'Recent Event Dates'
Esta parte cria a visualização chamada Recent Event Dates, caso ela ainda não exista.
Expressão WITH: Expressões de Tabela Comuns (Common Table Expressions — CTEs)
Nesta parte, são definidas duas expressões de tabela comuns (CTE), que simplificam a lógica da consulta ao dividi-la em etapas intermediárias.
CTE 1: UNIQUE_EVENTS
WITH UNIQUE_EVENTS AS ( SELECT DISTINCT M.EVENTID as 'E_ID', M.COUNTRY as 'Country', M.EVENTNAME as 'Name', M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M )
- A CTE (UNIQUE_EVENTS) extrai eventos distintos da tabela MQL5Calendar.
- Ela seleciona o identificador do evento (EVENTID), o país, o nome e a moeda, garantindo que cada evento seja listado apenas uma vez (DISTINCT remove registros duplicados).
Colunas selecionadas em UNIQUE_EVENTS:
- M.EVENTID as 'E_ID' — identificador único do evento.
- M.COUNTRY as 'Country' — país associado ao evento.
- M.EVENTNAME as 'Name' — nome do evento.
- M.EVENTCURRENCY as 'Currency' — moeda associada ao evento.
CTE 2: INFO_DATE
INFO_DATE AS ( SELECT E_ID, Country, Name, Currency, ( SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M, Record R INNER JOIN TimeSchedule T ON T.ID = M.ID WHERE DATE(REPLACE(Time, '.', '-')) <= R.Date AND E_ID = M.EVENTID ORDER BY Time DESC LIMIT 1 ) as 'Last Event Date' FROM UNIQUE_EVENTS )
A CTE (INFO_DATE) adiciona um campo de data (Last Event Date — data do último evento) aos eventos únicos da CTE anterior. Isso é feito da seguinte forma:
- Para cada evento único (E_ID, Country, Name, Currency), é selecionada a data mais recente do evento a partir das tabelas TimeSchedule e MQL5Calendar.
Campos em INFO_DATE:Explicação do subselect:
- O subselect extrai o campo DST_NONE da tabela TimeSchedule (associada às tabelas MQL5Calendar e Record) — que representa o timestamp ou horário do evento.
- A condição DATE(REPLACE(Time, '.', '-')) <= R.Date garante que a data do evento (Time, após a substituição dos pontos “.” por hífens “-” para formar o formato de data correto) seja menor ou igual à data atual registrada na tabela Record (R.Date).
- Os eventos são classificados em ordem decrescente de tempo (ORDER BY Time DESC), e o uso de LIMIT 1 assegura que apenas o horário mais recente do evento seja recuperado.
- E_ID — identificador do evento a partir da CTE UNIQUE_EVENTS.
- Country — país proveniente da CTE UNIQUE_EVENTS.
- Name — nome do evento proveniente da CTE UNIQUE_EVENTS.
- Currency — moeda proveniente da CTE UNIQUE_EVENTS.
- Last Event Date — data mais recente do evento para cada ocorrência, extraída das tabelas MQL5Calendar e TimeSchedule.
Consulta principal: seleção final e transformação
SELECT E_ID as 'ID', Country, Name, Currency, "Last Event Date" as 'Recent Date', ( CASE CAST (strftime('%w', DATE(REPLACE("Last Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END ) as 'Day' FROM INFO_DATE ORDER BY "Recent Date" DESC;
A parte principal da consulta seleciona o resultado final a partir da CTE INFO_DATE e inclui os seguintes campos:
- E_ID as 'ID' — identificador do evento (ID).
- Country — país associado ao evento.
- Name — nome do evento.
- Currency — moeda relacionada ao evento.
- "Last Event Date" as 'Recent Date' — data do evento mais recente, renomeada como 'Recent Date'.
Cálculo do dia da semana:
- A consulta utiliza strftime('%w', DATE(REPLACE("Last Event Date", '.', '-'))), que converte o valor de 'Last Event Date' (data do último evento) em um formato de data válido e extrai o dia da semana correspondente.
- %w retorna um número inteiro que representa o dia da semana, onde 0 = Sunday (domingo), 1 = Monday (segunda-feira), ..., 6 = Saturday (sábado).
- O operador CASE associa esse número ao nome correspondente do dia (por exemplo, 0 → Sunday, 1 → Monday, e assim por diante).
Ordenação dos resultados:
ORDER BY "Recent Date" DESC
Os resultados são classificados pelo campo Recent Date em ordem decrescente, ou seja, os eventos mais recentes aparecem no topo da lista.
Exemplo de saída da visualização Recent Event Dates:
ID Country Name Currency Recent Date Day 554520001 New Zealand CFTC NZD Non-Commercial Net Positions NZD 2024.09.27 21:30 Friday 999520001 European Union CFTC EUR Non-Commercial Net Positions EUR 2024.09.27 21:30 Friday 392520001 Japan CFTC JPY Non-Commercial Net Positions JPY 2024.09.27 21:30 Friday // ...
A consulta a seguir cria a visualização chamada Upcoming Event Dates (datas dos próximos eventos), que exibe uma lista dos próximos eventos extraídos da tabela MQL5Calendar. Ela inclui a data do próximo evento e o dia da semana correspondente. A consulta é composta por duas partes principais: primeiro, identifica os eventos únicos e, em seguida, determina a próxima data agendada para cada um desses eventos.
CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID',M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency,(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))>R.Date AND E_ID=M.EVENTID ORDER BY Time ASC LIMIT 1) as 'Next Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name,Currency,(CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END) as 'Upcoming Date',(CASE WHEN "Next Event Date"<>'Unknown' THEN (case cast (strftime('%w', DATE(REPLACE("Next Event Date",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE Order BY "Upcoming Date" ASC;
CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates'
Essa instrução cria a visualização chamada Upcoming Event Dates (datas dos próximos eventos), mas apenas se ela ainda não existir.
Expressão WITH: Expressões de Tabela Comuns (Common Table Expressions — CTEs)
A consulta utiliza expressões de tabela comuns (CTE), que ajudam a dividir consultas complexas em partes menores e reutilizáveis. Aqui, existem duas CTEs principais: UNIQUE_EVENTS e INFO_DATE.
CTE 1: UNIQUE_EVENTS
WITH UNIQUE_EVENTS AS ( SELECT DISTINCT M.EVENTID as 'E_ID', M.COUNTRY as 'Country', M.EVENTNAME as 'Name', M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M )
- Esta parte seleciona os eventos únicos da tabela MQL5Calendar.
- Ela extrai o identificador do evento (EVENTID), o país, o nome e a moeda, garantindo que cada evento apareça apenas uma vez por meio do uso de DISTINCT.
O resultado desta CTE é um conjunto de eventos distintos, com seus respectivos detalhes.
Colunas em UNIQUE_EVENTS:
- E_ID — identificador único do evento.
- Country — país em que o evento ocorre.
- Name — nome do evento.
- Currency — moeda relacionada ao evento.
CTE 2: INFO_DATE
INFO_DATE AS ( SELECT E_ID, Country, Name, Currency, ( SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M, Record R INNER JOIN TimeSchedule T ON T.ID=M.ID WHERE DATE(REPLACE(Time, '.', '-')) > R.Date AND E_ID = M.EVENTID ORDER BY Time ASC LIMIT 1 ) as 'Next Event Date' FROM UNIQUE_EVENTS )
A CTE (INFO_DATE) extrai a data do próximo evento para cada evento único.
- Para cada evento (E_ID, Country, Name, Currency), é pesquisada a data do evento futuro na tabela TimeSchedule (campo DST_NONE).
Explicação do subselect:
- O subselect extrai o campo DST_NONE da tabela TimeSchedule — que representa a data ou o horário do evento.
- A condição DATE(REPLACE(Time, '.', '-')) > R.Date garante que apenas os eventos futuros sejam selecionados (ou seja, aqueles com datas posteriores à data atual, R.Date).
- Os eventos são ordenados em ordem crescente de tempo (ORDER BY Time ASC), de modo que a primeira data futura seja selecionada. O uso de LIMIT 1 assegura que apenas uma data — a mais próxima — seja retornada.
Colunas em INFO_DATE:
- E_ID — identificador do evento a partir de UNIQUE_EVENTS.
- Country — país associado ao evento.
- Name — nome do evento.
- Country — país associado ao evento.
- Next Event Date — data do próximo evento futuro, determinada pelo subselect.
Consulta principal: Seleção final e transformação
SELECT E_ID as 'ID', Country, Name, Currency, (CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END) as 'Upcoming Date', (CASE WHEN "Next Event Date" <> 'Unknown' THEN (CASE CAST (strftime('%w', DATE(REPLACE("Next Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE ORDER BY "Upcoming Date" ASC;
A consulta principal extrai o resultado final da CTE INFO_DATE, transformando os dados e adicionando lógica adicional para tratar casos em que as datas dos eventos possam estar ausentes (valores NULL).
Colunas selecionadas:
- E_ID as 'ID' — identificador do evento (ID).
- Country — país associado ao evento.
- Name — nome do evento.
- Currency — moeda relacionada ao evento.
- Upcoming Date — data do próximo evento, com base em Next Event Date. Se Next Event Date for NULL, o resultado exibirá 'Unknown'; caso contrário, será mostrada a data do evento.
CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END
O operador CASE verifica se existe uma data válida para o próximo evento. Se a data for NULL, o resultado exibirá 'Unknown'; caso contrário, mostrará Next Event Date (a data do próximo evento).
Cálculo do dia da semana:
- Se Next Event Date não for 'Unknown', a consulta converte a data futura em um dia da semana por meio do operador CASE:
CASE CAST (strftime('%w', DATE(REPLACE("Next Event Date", '.', '-'))) AS INTEGER)
WHEN 0 THEN 'Sunday'
WHEN 1 THEN 'Monday'
WHEN 2 THEN 'Tuesday'
WHEN 3 THEN 'Wednesday'
WHEN 4 THEN 'Thursday'
WHEN 5 THEN 'Friday'
ELSE 'Saturday'
END
A função strftime('%w', ...) extrai o dia da semana a partir de Next Event Date:
- %w retorna um número inteiro representando o dia da semana (0 = Sunday, 1 = Monday, etc.).
- O operador CASE associa esse número ao nome correspondente do dia (por exemplo, 0 → Sunday).
- Se Next Event Date for 'Unknown', a coluna Day também exibirá 'Unknown'.
Ordenação dos resultados:
ORDER BY "Upcoming Date" ASC;
Os resultados são classificados pelo campo Upcoming Date (data futura) em ordem crescente, de modo que os eventos mais próximos no tempo sejam exibidos primeiro.
Exemplo de saída da visualização Upcoming Date:
ID Country Name Currency Upcoming Date Day 410020004 South Korea Industrial Production y/y KRW 2024.09.30 01:00 Monday 410020005 South Korea Retail Sales m/m KRW 2024.09.30 01:00 Monday 410020006 South Korea Index of Services m/m KRW 2024.09.30 01:00 Monday // ... 36500001 Australia S&P Global Manufacturing PMI AUD 2024.10.01 01:00 Tuesday 392030007 Japan Unemployment Rate JPY 2024.10.01 01:30 Tuesday 392050002 Japan Jobs to Applicants Ratio JPY 2024.10.01 01:30 Tuesday // ...
Código do EA
Este é o arquivo principal do programa, onde implementamos a estratégia de trading baseada em notícias. O código apresentado abaixo permite a entrada dos parâmetros necessários para a configuração das operações.
input Choice iCustom_Event_1=No;//USE EVENT IDs BELOW? input string iCustom_Event_1_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_2=No;//USE EVENT IDs BELOW? input string iCustom_Event_2_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_3=No;//USE EVENT IDs BELOW? input string iCustom_Event_3_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_4=No;//USE EVENT IDs BELOW? input string iCustom_Event_4_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_5=No;//USE EVENT IDs BELOW? input string iCustom_Event_5_IDs="";//EVENT IDs[Separate with a comma][MAX 14]
Explicação de cada parâmetro:
Choice iCustom_Event_1 = No;
- Tipo: Choice
- Nome da variável: iCustom_Event_1
- Valor padrão: No
- Descrição: Este parâmetro de entrada permite ao usuário ativar ou desativar o uso de identificadores de eventos personalizados para o evento 1. O tipo Choice aqui é um tipo de enumeração com dois valores possíveis: Yes ou No. Quando definido como Yes, o programa utilizará os identificadores de eventos especificados no campo de entrada correspondente (iCustom_Event_1_IDs).
string iCustom_Event_1_IDs = "";
- Tipo: string
- Nome da variável: iCustom_Event_1_IDs
- Valor padrão: string vazia ""
- Descrição: Este parâmetro de entrada permite ao usuário inserir uma lista de identificadores de eventos para o evento personalizado 1. Esses identificadores devem ser separados por vírgulas (por exemplo, "36010006,840030005,840030016") e podem ser fornecidos até 14 identificadores no máximo.
Inicialização de eventos de notícias personalizados
CEvent1.useEvents = Answer(iCustom_Event_1); StringSplit(iCustom_Event_1_IDs, ',', CEvent1.EventIds); CEvent2.useEvents = Answer(iCustom_Event_2); StringSplit(iCustom_Event_2_IDs, ',', CEvent2.EventIds); CEvent3.useEvents = Answer(iCustom_Event_3); StringSplit(iCustom_Event_3_IDs, ',', CEvent3.EventIds); CEvent4.useEvents = Answer(iCustom_Event_4); StringSplit(iCustom_Event_4_IDs, ',', CEvent4.EventIds); CEvent5.useEvents = Answer(iCustom_Event_5); StringSplit(iCustom_Event_5_IDs, ',', CEvent5.EventIds);
O bloco de código inicializa vários objetos de eventos de notícias personalizados (CEvent1, CEvent2, etc.) e processa seus respectivos identificadores.
- CEvent1.useEvents = Answer(iCustom_Event_1);
- CEvent1 é uma estrutura que contém informações sobre um conjunto específico de eventos de notícias personalizados. O flag useEvents é definido pela função Answer(), que retorna um valor lógico (true ou false) com base na variável de entrada iCustom_Event_1.
- Se iCustom_Event_1 for true, o EA utilizará esse evento personalizado na lógica de negociação; caso contrário, o evento será ignorado.
- StringSplit(iCustom_Event_1_IDs, ',', CEvent1.EventIds);
- StringSplit() é uma função que divide a string de identificadores de eventos (iCustom_Event_1_IDs) usando a vírgula (,) como separador, e o resultado é armazenado em CEvent1.EventIds.
- iCustom_Event_1_IDs é uma string que contém um ou mais identificadores de eventos, e a função split converte essa string em um array de identificadores para uso posterior na lógica de trading.
Um código semelhante é utilizado para CEvent2 até CEvent5.
Função OnInit()
Esta é a função de inicialização, chamada quando o EA é executado ou adicionado ao gráfico.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Assign if in LightMode or not isLightMode=(iDisplayMode==Display_LightMode)?true:false; //--- call function for common initialization procedure InitCommon(); //--- store Init result int InitResult; if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester { //--- initialization procedure outside strategy tester InitResult=InitNonTester(); } else { //--- initialization procedure inside strategy tester InitResult=InitTester(); } //--- Create DB in memory NewsObject.CreateEconomicDatabaseMemory(); //--- Initialize Candle properties pointer object CP = new CCandleProperties(); //--- Retrieve news events for the current Daily period into array CalendarArray NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Initialize Common graphics class pointer object CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread), Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj)); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects //--- Set Time CDay.SetmyTime(CalendarArray); /* create timer, if in the strategy tester set the timer to 30s else 100ms */ EventSetMillisecondTimer((!MQLInfoInteger(MQL_TESTER))?100:30000); //-- Initialize Trade Management class pointer object Trade = new CTradeManagement(iDeviation); //--- return Init result return InitResult; }
Etapas principais:
Modo de exibição:
- O EA verifica se está operando no modo claro ou escuro (isLightMode).
Inicialização geral:
- Chama a função InitCommon() para executar tarefas de inicialização comuns.
Verificação do testador de estratégias:
- Verifica se o EA está sendo executado no modo de teste de estratégia, utilizando MQLInfoInteger(MQL_TESTER), e em seguida chama InitNonTester() ou InitTester(), dependendo do resultado.
Banco de dados de eventos de notícias:
- A função NewsObject.CreateEconomicDatabaseMemory() inicializa o banco de dados de eventos econômicos na memória. É aqui que o EA armazena os dados relacionados às notícias.
Inicialização das propriedades das velas:
- É criado um ponteiro de classe CP para CCandleProperties. Essa classe é responsável por gerenciar as propriedades das velas — como abertura, fechamento, máxima, mínima e outros atributos relacionados.
Obtenção dos eventos de notícias:
- A função NewsObject.EconomicDetailsMemory() extrai os eventos de notícias relevantes com base nos critérios selecionados. Ela organiza e classifica os eventos de notícias para o dia de negociação atual.
Inicialização dos objetos gráficos:
- O EA inicializa a classe CGraphics, responsável por criar elementos gráficos no gráfico, como a visualização dos dados dos eventos de notícias. O método GraphicsRefresh() assegura a atualização dos objetos gráficos conforme o tempo configurado (iSecondsPreEvent).
Configuração de tempo e do timer:
- O método CDay.SetmyTime() processa o array de eventos de notícias e realiza o controle temporal das operações de trading.
- O temporizador (EventSetMillisecondTimer()) é configurado com diferentes intervalos, dependendo se o EA está sendo executado no testador de estratégias ou em tempo real. Essa diferença de configuração é essencial para garantir o desempenho durante os testes no Strategy Tester.
Inicialização do gerenciamento de trading:
- O ponteiro da classe Trade é inicializado com o parâmetro iDeviation, permitindo a abertura de ordens com stop orders quando necessário.
Retorno do resultado da inicialização:
- A função retorna o resultado da inicialização.
Função OnTimer
A função OnTimer() é executada periodicamente, sendo acionada a cada vez que o evento de temporizador é disparado.
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { if(((!MQLInfoInteger(MQL_TESTER))?int(TimeTradeServer())%30==0:true)) { //--- Store start-up time. static datetime Startup_date = TimeTradeServer(); if(CTM.DateisToday(Startup_date)&&CP.NewCandle(0,PERIOD_D1) &&MQLInfoInteger(MQL_TESTER)) { //--- Retrieve news events for the current Daily period into array CalendarArray NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Initialize Common graphics class pointer object CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread), Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj)); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects //--- Set Time CDay.SetmyTime(CalendarArray); } //--- Run procedures ExecutionOnTimer(Startup_date); if(CTS.isSessionStart()&&!CTS.isSessionEnd()) { //--- function to open trades TradeTime(); } //--- close trades within 45 min before end of session if(CTS.isSessionStart()&&CTS.isSessionEnd()&&!CTS.isSessionEnd(0,0)) { Trade.CloseTrades("NewsTrading"); } } }
Principais características:
- Condições baseadas em tempo: a instrução if verifica se o código está sendo executado dentro do testador de estratégias ou em tempo real. Quando o EA está operando em tempo real (fora do testador), verifica se o tempo do servidor é divisível por 30 — correspondendo a um intervalo de 30 segundos.
- Atualização de notícias e velas:
- Se a data de hoje coincidir com Startup_date e for um novo dia em que uma nova vela diária foi formada (CP.NewCandle(0, PERIOD_D1)), o EA extrai as notícias econômicas referentes ao dia atual utilizando NewsObject.EconomicDetailsMemory.
- O gráfico também é atualizado nesse momento.
- Gerenciamento da sessão: se a sessão de trading tiver começado (CTS.isSessionStart()), as operações serão permitidas durante o período ativo da sessão. O EA também encerrará as posições abertas quando a sessão estiver se aproximando do fim.
Função ExecutionOnTimer
//+------------------------------------------------------------------+ //|Execute program procedures in time intervals | //+------------------------------------------------------------------+ void ExecutionOnTimer(datetime Startup_date) { //--- Check if not start-up date if(!CTM.DateisToday(Startup_date)) { //--- Run every New Daily Candle if(CP.NewCandle(1,PERIOD_D1)) { //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Update/Create DB in Memory NewsObject.CreateEconomicDatabaseMemory(); } //--- retrieve news events for the current day NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Set time from news events CDay.SetmyTime(CalendarArray); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create/Re-create chart objects } //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Run every New Hourly Candle if(CP.NewCandle(2,PERIOD_H1)) { //--- Check if DB in Storage needs an update if(NewsObject.UpdateRecords()) { //--- initialization procedure outside strategy tester InitNonTester(); } } } } else { //--- Run every New Daily Candle if(CP.NewCandle(3,PERIOD_D1)) { //--- Update Event objects on chart CGraphics.NewsEvent(); } } //--- Update realtime Graphic every 1 min if(CP.NewCandle(4,PERIOD_M1)) { //--- get the news events for the next min ahead of time. datetime Time_ahead = TimeTradeServer()+CTM.MinutesS(); CDay.GetmyTime(CTV.Hourly(CTM.ReturnHour(Time_ahead)), CTV.Minutely(CTV.Minutely(CTM.ReturnMinute(Time_ahead))), myTimeData,myEvents); CGraphics.Block_2_Realtime(iSecondsPreEvent); } }
Principais características:
- Atualização diária das velas: verifica a existência de novas velas diárias. Se uma nova vela for formada, o banco de dados de notícias é atualizado (NewsObject.CreateEconomicDatabaseMemory()), e os eventos econômicos do dia são novamente extraídos.
- Atualização de velas horárias: quando uma nova vela de uma hora é formada, o EA verifica se o banco de dados de eventos econômicos precisa ser atualizado e, caso positivo, reinicializa o sistema fora do modo testador de estratégias (InitNonTester()).
- Atualizações em tempo real: A cada minuto, o EA atualiza as informações gráficas em tempo real e extrai os eventos de notícias para o próximo minuto, preparando-se para possíveis operações.
Função TradeTime
A função gerencia a execução de operações durante eventos de notícias.
//+------------------------------------------------------------------+ //|function to check trading time | //+------------------------------------------------------------------+ void TradeTime() { //--- Iterate through the event times for(uint i=0;i<myTimeData.Size();i++) { //--- Check if it is time to trade each news event if(CTM.TimePreEvent(CTM.TimeMinusOffset(datetime(myEvents[i].EventDate),iSecondsPreEvent) ,datetime(myEvents[i].EventDate)) &&(CTM.isDayOfTheWeek(TradingDay)||iNewSelection==News_Select_Custom_Events)) { //--- switch for order type selection switch(iOrderType) { case StopOrdersType:// triggers for STOP ORDERS StopOrders(myEvents[i]); break; default:// triggers for both MARKET POSITION & SINGLE STOP ORDER SingleOrder(myEvents[i]); break; } } } }
Principais características:
- Verificação do horário do evento: para cada evento presente no array myTimeData, o EA verifica se o horário atual está dentro da janela de tempo pré-definida antes do evento (iSecondsPreEvent).
- Classificação por dia da semana: as operações só são abertas se o dia atual coincidir com o dia configurado para negociação (TradingDay) ou se estiverem selecionados eventos personalizados (iNewSelection == News_Select_Custom_Events).
- Seleção do tipo de ordem: com base na entrada iOrderType (posição a mercado ou ordens stop), a função abre ordens de mercado ou ordens stop durante o período do evento de notícia.
Função SingleOrder
A função abre ordens de mercado ou de stop, dependendo do impacto do evento noticioso.
//+------------------------------------------------------------------+ //|function to open single order types | //+------------------------------------------------------------------+ void SingleOrder(Calendar &NewsEvent) { //--- Check each Impact value type switch(NewsObject.IMPACT(NewsEvent.EventImpact)) { //--- When Impact news is negative case CALENDAR_IMPACT_NEGATIVE: //--- Check if profit currency is news event currency if(NewsEvent.EventCurrency==CSymbol.CurrencyProfit()) { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open buy-stop with Event id as Magic number Trade.OpenBuyStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } else { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open buy-stop with Event id as Magic number Trade.OpenSellStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } break; //--- When Impact news is positive case CALENDAR_IMPACT_POSITIVE: //--- Check if profit currency is news event currency if(NewsEvent.EventCurrency==CSymbol.CurrencyProfit()) { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open sell-stop with Event id as Magic number Trade.OpenSellStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } else { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open sell-stop with Event id as Magic number Trade.OpenBuyStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } break; //--- Unknown default: break; } }
Principais características:
- Avaliação do impacto: o EA avalia o impacto do evento de notícia (NewsObject.IMPACT(NewsEvent.EventImpact)), que pode ser negativo ou positivo.
- Impacto negativo: se o impacto da notícia for negativo e a moeda do evento coincidir com a moeda de lucro da conta, o EA abrirá uma posição de compra caso iOrderType seja uma posição de mercado, ou colocará uma ordem stop de compra caso se trate de ordens pendentes. Se a moeda do evento não coincidir com a moeda de lucro, o EA abrirá uma posição de venda ou uma ordem stop de venda.
- Impacto positivo: se o impacto da notícia for positivo e a moeda do evento coincidir com a moeda de lucro da conta, o EA abrirá uma posição de venda ou uma ordem stop de venda. Se a moeda do evento for diferente da moeda de lucro, o EA abrirá uma posição de compra ou uma ordem stop de compra.
Função StopOrders
A função gerencia a abertura simultânea de ordens stop de compra e de venda durante um evento de notícia. Independentemente das consequências do evento, ambos os tipos de ordens stop são colocados para capturar o movimento do preço em qualquer direção.
//+------------------------------------------------------------------+ //|function to open orders | //+------------------------------------------------------------------+ void StopOrders(Calendar &NewsEvent) { //--- Opens both buy-stop & sell-stop regardless of event impact Trade.OpenStops(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-Stops-"+NewsEvent.EventCode); }
Principais características:
- Ordens stop de compra e venda: o EA abre ambos os tipos de ordens stop, cada uma associada a um evento específico, utilizando o EventId do evento como número mágico da operação (magic number).
Função OnTrade
A função é acionada sempre que ocorre um novo evento de trade. Ela é utilizada para gerenciar as operações abertas.
void OnTrade() { //--- Check if time is within the trading session if(CTS.isSessionStart() && !CTS.isSessionEnd(0,0)) { //--- Run procedures ExecutionOnTrade(); } }
- CTS.isSessionStart() and !CTS.isSessionEnd(0,0):
- CTS (objeto da classe de sessões) é usado para verificar se o horário atual está dentro de uma sessão ativa de trading.
- isSessionStart() verifica se a sessão foi iniciada.
- !CTS.isSessionEnd(0,0) verifica se a sessão foi encerrada. Os argumentos 0,0 representam o deslocamento ou o período de buffer antes do encerramento da sessão.
- Isso garante que as operações sejam ajustadas apenas quando o horário atual estiver dentro de uma sessão ativa.
- ExecutionOnTrade();
- Se a sessão estiver ativa, a função ExecutionOnTrade() é chamada para executar os procedimentos necessários relacionados à nova operação.
Função ExecutionOnTrade
Esta função contém a lógica que é executada sempre que uma nova operação é aberta.
void ExecutionOnTrade() { //--- if stop orders, enable fundamental mode if(iOrderType == StopOrdersType) { Trade.FundamentalMode("NewsTrading"); } //--- when stop order(s), enable slippage reduction if(iOrderType != MarketPositionType) { Trade.SlippageReduction(iStoploss, iTakeprofit, "NewsTrading"); } }
- if (iOrderType == StopOrdersType):
- A função verifica se a operação atual é uma ordem stop (StopOrdersType). Se a condição for verdadeira, o código chama a função Trade.FundamentalMode().
- Trade.FundamentalMode("NewsTrading");
- Essa função ativa o modo fundamental (Fundamental Mode) e é responsável por excluir ordens pendentes que estejam no sentido oposto ao da operação executada.
- "NewsTrading" é o rótulo ou comentário comercial atribuído ao EA.
- if(iOrderType != MarketPositionType):
- A condição verifica se a variável de entrada não está definida como MarketPositionType. Se for verdadeira, o código aplica um mecanismo para reduzir o slippage (derrapagem de preço).
- Trade.SlippageReduction(iStoploss, iTakeprofit, "NewsTrading");
- A função reduz o slippage em ordens não executadas a mercado.
- O slippage ocorre quando o preço de execução difere do preço esperado, algo comum durante períodos de alta volatilidade, especialmente durante eventos de notícias.
- Ao chamar SlippageReduction(), o EA busca minimizar esse desvio de preço.
- Parâmetros:
- iStoploss — valor do stop loss, que representa o preço no qual a operação deve ser encerrada automaticamente para limitar possíveis perdas.
- iTakeprofit — valor do take profit, que representa o preço no qual a operação deve ser encerrada automaticamente para garantir o lucro.
- "NewsTrading" — rótulo ou comentário comercial atribuído ao EA.
Considerações finais
Neste artigo, o EA permite que os usuários definam eventos de notícias personalizados sobre os quais o sistema realizará operações. Esses eventos são inicializados a partir dos dados inseridos pelo usuário, que são posteriormente analisados e processados pelo EA de forma adequada. O banco de dados do calendário no repositório foi aprimorado para exibir informações adicionais sobre eventos recentes e futuros na forma de visualizações, e cada consulta dessas visualizações foi explicada em detalhes. As funções OnTimer e OnTrade foram adicionadas para gerenciar a execução do código com base em condições de tempo específicas. Obrigado pela atenção! Nos vemos no próximo artigo!
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16170
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Construa EAs auto-otimizáveis em MQL5 (Parte 3): Acompanhamento dinâmico de tendência e retorno à média
Simulação de mercado: Position View (XIX)
Aplicação de métodos de ensemble para tarefas de classificação em MQL5
Do básico ao intermediário: Filas, Listas e Árvores (VIII)
- 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