English Русский 中文 Español Deutsch 日本語
preview
Negociando com o Calendário Econômico MQL5 (Parte 4): Implementando Atualizações de Notícias em Tempo Real no Painel

Negociando com o Calendário Econômico MQL5 (Parte 4): Implementando Atualizações de Notícias em Tempo Real no Painel

MetaTrader 5Negociação |
18 2
Allan Munene Mutiiria
Allan Munene Mutiiria

Introdução

Neste artigo, avançamos em nosso trabalho no painel do MetaQuotes Language 5 (MQL5) Calendário Econômico adicionando a funcionalidade de atualizações ao vivo, permitindo manter uma exibição continuamente atualizada dos principais eventos de notícias econômicas. Na parte anterior, projetamos e implementamos um painel para filtrar notícias com base em moeda, importância e tempo, fornecendo uma visão personalizada dos eventos relevantes. Agora, levamos isso adiante habilitando atualizações em tempo real, garantindo que nosso calendário exiba os dados mais recentes para uma tomada de decisão oportuna. Os tópicos que iremos abordar incluem:

  1. Implementando Atualizações de Notícias em Tempo Real no MQL5
  2. Conclusão

Essa melhoria transformará nosso painel em uma ferramenta dinâmica e em tempo real, continuamente atualizada com os últimos eventos econômicos. Ao implementar a funcionalidade de atualização ao vivo, garantiremos que nosso calendário permaneça preciso e relevante, apoiando decisões de negociação em tempo hábil no MetaTrader 5 ou no terminal de negociação.


Implementando Atualizações de Notícias em Tempo Real no MQL5

Para implementar atualizações ao vivo em nosso painel, precisamos garantir que os eventos de notícias sejam armazenados e comparados periodicamente para detectar quaisquer alterações. Isso requer a manutenção de arrays para armazenar os dados atuais e anteriores dos eventos, permitindo identificar atualizações e refletí-las com precisão no painel. Dessa forma, podemos garantir que o painel se ajuste dinamicamente para exibir os eventos econômicos mais recentes em tempo real. A seguir, definimos os arrays que utilizaremos para esse propósito:

string current_eventNames_data[];
string previous_eventNames_data[];

Aqui, definimos dois arrays de string, "current_eventNames_data" e "previous_eventNames_data", que usaremos para gerenciar e comparar os dados dos eventos econômicos para atualizações ao vivo no painel. O array "current_eventNames_data" armazenará os eventos mais recentes recuperados do calendário econômico, enquanto "previous_eventNames_data" manterá os dados do último ciclo de atualização. Comparando esses dois arrays, podemos identificar quaisquer alterações ou novas adições aos eventos, permitindo atualizar dinamicamente o painel.

Usando esses arrays, precisaremos obter os valores atuais em cada evento selecionado na seção de inicialização e armazená-los no array de dados atuais, e depois copiá-los para o array anterior, que utilizaremos para fazer a comparação no próximo tick de preço.

ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1);
current_eventNames_data[ArraySize(current_eventNames_data)-1] = event.name;

Aqui, expandimos dinamicamente o array "current_eventNames_data" e adicionamos um novo nome de evento a ele. Usamos a função ArrayResize para aumentar o tamanho do array em uma posição, criando espaço para uma nova entrada. Após redimensionar, atribuimos o nome do evento ao último índice do array usando a expressão "current_eventNames_data[ArraySize(current_eventNames_data)-1]". Esse processo garante que cada novo nome de evento recuperado do calendário econômico seja armazenado no array, permitindo manter uma lista atualizada de eventos para processamento e comparação.

No entanto, antes de adicionar os eventos ao array, precisamos garantir que comecemos do zero, ou seja, precisamos de um array vazio.

ArrayFree(current_eventNames_data);

Aqui, usamos a função ArrayFree para limpar todos os elementos do array "current_eventNames_data", redefinindo-o para um estado vazio. Isso é essencial para garantir que o array não mantenha dados desatualizados de iterações anteriores, preparando-o assim para armazenar um novo conjunto de nomes de eventos durante o ciclo de processamento. Depois de preencher o array, precisamos copiá-lo para o array anterior e usá-lo posteriormente para fazer a comparação.

Print("CURRENT EVENT NAMES DATA SIZE = ",ArraySize(current_eventNames_data));
ArrayPrint(current_eventNames_data);
Print("PREVIOUS EVENT NAMES DATA SIZE (Before) = ",ArraySize(previous_eventNames_data));
ArrayCopy(previous_eventNames_data,current_eventNames_data);
Print("PREVIOUS EVENT NAMES DATA SIZE (After) = ",ArraySize(previous_eventNames_data));
ArrayPrint(previous_eventNames_data);

Aqui, registramos e gerenciamos a transição de dados entre os arrays "current_eventNames_data" e "previous_eventNames_data". Primeiro, usamos a função Print para exibir o tamanho do array "current_eventNames_data", fornecendo visibilidade sobre a quantidade de nomes de eventos armazenados naquele momento. Em seguida, chamamos a função ArrayPrint para exibir o conteúdo do array para maior verificação. Depois, registramos o tamanho do array "previous_eventNames_data" antes da cópia, fornecendo uma linha de base para comparação.

Usando a função ArrayCopy, copiamos o conteúdo de "current_eventNames_data" para "previous_eventNames_data", transferindo efetivamente os nomes mais recentes dos eventos para comparações futuras. Em seguida, imprimimos o tamanho do array "previous_eventNames_data" após a operação de cópia para confirmar a atualização bem-sucedida. Por fim, chamamos a função ArrayPrint para exibir o conteúdo atualizado de "previous_eventNames_data", garantindo que a transferência de dados esteja precisa e completa. Essas são as mudanças de que precisamos no manipulador de eventos OnInit para armazenar os eventos iniciais. Vamos destacá-las para maior clareza.

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

   //---

   ArrayFree(current_eventNames_data);
      
   //--- Loop through each calendar value up to the maximum defined total
   for (int i = 0; i < valuesTotal; i++){
   
      //---

      //--- Loop through calendar data columns
      for (int k=0; k<ArraySize(array_calendar); k++){
         //---

         //--- Prepare news data array with time, country, and other event details
         string news_data[ArraySize(array_calendar)];
         news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date
         news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time
         news_data[2] = country.currency; //--- Event country currency
      
         //--- Determine importance color based on event impact
         color importance_color = clrBlack;
         if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;}
         else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;}
         else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;}
      
         //--- Set importance symbol for the event
         news_data[3] = ShortToString(0x25CF);
      
         //--- Set event name in the data array
         news_data[4] = event.name;
            
         //--- Populate actual, forecast, and previous values in the news data array
         news_data[5] = DoubleToString(value.GetActualValue(),3);
         news_data[6] = DoubleToString(value.GetForecastValue(),3);
         news_data[7] = DoubleToString(value.GetPreviousValue(),3);         
         //---

      }
      
      ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1);
      current_eventNames_data[ArraySize(current_eventNames_data)-1] = event.name;
      
   }
   Print("CURRENT EVENT NAMES DATA SIZE = ",ArraySize(current_eventNames_data));
   ArrayPrint(current_eventNames_data);
   Print("PREVIOUS EVENT NAMES DATA SIZE (Before) = ",ArraySize(previous_eventNames_data));
   ArrayCopy(previous_eventNames_data,current_eventNames_data);
   Print("PREVIOUS EVENT NAMES DATA SIZE (After) = ",ArraySize(previous_eventNames_data));
   ArrayPrint(previous_eventNames_data); 
   
   //Print("Final News = ",news_filter_count);
   updateLabel(TIME_LABEL,"Server Time: "+TimeToString(TimeCurrent(),
              TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
              IntegerToString(news_filter_count)+"/"+IntegerToString(allValues));
//---
   return(INIT_SUCCEEDED);
}

Aqui, acabamos de fazer as alterações que nos ajudarão a obter os dados iniciais dos eventos. Nós as destacamos em amarelo para maior clareza. Em seguida, precisamos apenas atualizar os valores do painel quando forem detectadas mudanças durante a comparação. Para isso, criaremos uma função personalizada que conterá toda a lógica para as atualizações dos eventos e o recálculo do painel, respectivamente.

//+------------------------------------------------------------------+
//| Function to update dashboard values                              |
//+------------------------------------------------------------------+
void update_dashboard_values(){

//---

}

Aqui, definimos a função "update_dashboard_values", que usaremos para lidar com a atualização dinâmica do painel do calendário econômico. Essa função conterá a lógica principal para comparar os dados armazenados de notícias, identificar quaisquer alterações e aplicar as atualizações necessárias na interface do painel. Ao organizar essa funcionalidade nessa função dedicada, garantiremos uma estrutura de código limpa e modular, facilitando modificações ou melhorias futuras. Em seguida, chamaremos a função no manipulador de eventos OnTick da seguinte forma.

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

Aqui, apenas chamamos a função personalizada a cada tick para realizar as atualizações. Após a compilação e execução do programa, temos os seguintes resultados:

DADOS DOS ARRAYS NA INICIALIZAÇÃO

A partir da imagem, podemos ver que reunimos todos os eventos de notícias no array "current", que então copiamos para o novo array "previous" para armazenamento, o que se alinha perfeitamente com o que queremos. Agora podemos prosseguir para usar esses dados copiados em análises posteriores. Na função, simplesmente obtemos os eventos atuais da seguinte forma:

//--- Declare variables for tracking news events and status
int totalNews = 0;
bool isNews = false;
MqlCalendarValue values[]; //--- Array to store calendar values

//--- Define start and end time for calendar event retrieval
datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H12);
datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H12);

//--- Set a specific country code filter (e.g., "US" for USD)
string country_code = "US";
string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);

//--- Retrieve historical calendar values within the specified time range
int allValues = CalendarValueHistory(values,startTime,endTime,NULL,NULL);

//--- Print the total number of values retrieved and the array size
//Print("TOTAL VALUES = ",allValues," || Array size = ",ArraySize(values));

//--- Define time range for filtering news events based on daily period
datetime timeRange = PeriodSeconds(PERIOD_D1);
datetime timeBefore = TimeTradeServer() - timeRange;
datetime timeAfter = TimeTradeServer() + timeRange;

//--- Print the furthest time look-back and current server time
//Print("FURTHEST TIME LOOK BACK = ",timeBefore," >>> CURRENT = ",TimeTradeServer());

//--- Limit the total number of values to display
int valuesTotal = (allValues <= 11) ? allValues : 11;
   
string curr_filter[] = {"AUD","CAD","CHF","EUR","GBP","JPY","NZD","USD"};
int news_filter_count = 0;

ArrayFree(current_eventNames_data);

// Define the levels of importance to filter (low, moderate, high)
ENUM_CALENDAR_EVENT_IMPORTANCE allowed_importance_levels[] = {CALENDAR_IMPORTANCE_LOW, CALENDAR_IMPORTANCE_MODERATE, CALENDAR_IMPORTANCE_HIGH};

//--- Loop through each calendar value up to the maximum defined total
for (int i = 0; i < valuesTotal; i++){

   MqlCalendarEvent event; //--- Declare event structure
   CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID

   MqlCalendarCountry country; //--- Declare country structure
   CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID

   MqlCalendarValue value; //--- Declare calendar value structure
   CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values
   
   //--- Check if the event’s currency matches any in the filter array (if the filter is enabled)
   bool currencyMatch = false;
   if (enableCurrencyFilter) {
      for (int j = 0; j < ArraySize(curr_filter); j++) {
         if (country.currency == curr_filter[j]) {
            currencyMatch = true;
            break;
         }
      }
      
      //--- If no match found, skip to the next event
      if (!currencyMatch) {
         continue;
      }
   }
   
   //--- Check importance level if importance filter is enabled
   bool importanceMatch = false;
   if (enableImportanceFilter) {
      for (int k = 0; k < ArraySize(allowed_importance_levels); k++) {
         if (event.importance == allowed_importance_levels[k]) {
            importanceMatch = true;
            break;
         }
      }
      
      //--- If importance does not match the filter criteria, skip the event
      if (!importanceMatch) {
         continue;
      }
   }
   
   //--- Apply time filter and set timeMatch flag (if the filter is enabled)
   bool timeMatch = false;
   if (enableTimeFilter) {
      datetime eventTime = values[i].time;
      
      if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) {
         timeMatch = true;  //--- Event is already released
      }
      else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) {
         timeMatch = true;  //--- Event is yet to be released
      }
      
      //--- Skip if the event doesn't match the time filter
      if (!timeMatch) {
         continue;
      }
   }
   
   //--- If we reach here, the currency matches the filter
   news_filter_count++; //--- Increment the count of filtered events
   
   //--- Set alternating colors for each data row holder
   color holder_color = (news_filter_count % 2 == 0) ? C'213,227,207' : clrWhite;
      
   //--- Loop through calendar data columns
   for (int k=0; k<ArraySize(array_calendar); k++){
         
      //--- Print event details for debugging
      //Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time);
   
      //--- Skip event if currency does not match the selected country code
      // if (StringFind(_Symbol,country.currency) < 0) continue;
   
      //--- Prepare news data array with time, country, and other event details
      string news_data[ArraySize(array_calendar)];
      news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date
      news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time
      news_data[2] = country.currency; //--- Event country currency
   
      //--- Determine importance color based on event impact
      color importance_color = clrBlack;
      if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;}
      else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;}
      else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;}
   
      //--- Set importance symbol for the event
      news_data[3] = ShortToString(0x25CF);
   
      //--- Set event name in the data array
      news_data[4] = event.name;
         
      //--- Populate actual, forecast, and previous values in the news data array
      news_data[5] = DoubleToString(value.GetActualValue(),3);
      news_data[6] = DoubleToString(value.GetForecastValue(),3);
      news_data[7] = DoubleToString(value.GetPreviousValue(),3);
   }
   
   ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1);
   current_eventNames_data[ArraySize(current_eventNames_data)-1] = event.name;
  
}   
//Print("Final News = ",news_filter_count);
updateLabel(TIME_LABEL,"Server Time: "+TimeToString(TimeCurrent(),
           TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
           IntegerToString(news_filter_count)+"/"+IntegerToString(allValues));

Este é apenas o trecho de código anterior que usamos durante a seção de inicialização, mas precisamos dele a cada tick para obter as atualizações. No entanto, passaremos por ele brevemente. Focamos em atualizar dinamicamente o painel com os últimos eventos de notícias econômicas a cada tick e, para isso, simplificamos a lógica removendo a criação de objetos gráficos e concentrando-nos em gerenciar os dados dos eventos de forma eficiente. Começamos definindo variáveis para rastrear a contagem total de notícias e critérios de filtro, como moeda, importância e intervalos de tempo. Esses filtros garantem que apenas eventos relevantes sejam considerados para processamento posterior.

Nós iteramos (loop) pelos valores do calendário recuperados, aplicando os filtros para identificar eventos que correspondam às condições especificadas. Para cada evento correspondente, extraímos detalhes importantes como o nome do evento, horário, moeda e nível de importância. Esses detalhes são armazenados no array "current_eventNames_data", que é redimensionado dinamicamente para acomodar novas entradas. Esse array é crucial para rastrear os eventos e nos permite identificar mudanças entre ticks ao compará-los com os dados anteriores. Por fim, atualizamos o rótulo do painel para refletir o total de eventos filtrados e o horário atual do servidor, garantindo que o painel sempre reflita os dados mais recentes dos eventos sem criar objetos desnecessários. Essa abordagem captura e atualiza de forma eficiente as informações de notícias econômicas em tempo real.

Em seguida, precisamos rastrear se há mudanças nos arrays de armazenamento usando os dados recém-adquiridos. Para isso, utilizamos uma função personalizada.

//+------------------------------------------------------------------+
//|   Function to compare two string arrays and detect changes       |
//+------------------------------------------------------------------+
bool isChangeInStringArrays(string &arr1[], string &arr2[]) {
   bool isChange = false;
   
   int size1 = ArraySize(arr1);  // Get the size of the first array
   int size2 = ArraySize(arr2);  // Get the size of the second array
   
   // Check if sizes are different
   if (size1 != size2) {
      Print("Arrays have different sizes. Size of Array 1: ", size1, ", Size of Array 2: ", size2);
      isChange = true;
      return (isChange);
   }

   // Loop through the arrays and compare corresponding elements
   for (int i = 0; i < size1; i++) {
      // Compare the strings at the same index in both arrays
      if (StringCompare(arr1[i], arr2[i]) != 0) {  // If strings are different
         // Action when strings differ at the same index
         Print("Change detected at index ", i, ": '", arr1[i], "' vs '", arr2[i], "'");
         isChange = true;
         return (isChange);
      }
   }

   // If no differences are found, you can also log this as no changes detected
   //Print("No changes detected between arrays.");
   return (isChange);
}

Aqui, definimos a função boolean "isChangeInStringArrays", que compara dois arrays de string, "arr1" e "arr2", para detectar quaisquer alterações entre eles. Começamos determinando os tamanhos de ambos os arrays usando a função ArraySize e armazenamos esses tamanhos em "size1" e "size2". Se os tamanhos dos arrays forem diferentes, imprimimos os respectivos tamanhos, definimos a flag "isChange" como verdadeira e retornamos true, indicando uma alteração. Se os tamanhos forem iguais, prosseguimos para comparar os elementos dos arrays usando um for loop. Para cada índice, usamos a função StringCompare para verificar se as strings em ambos os arrays são idênticas. Se alguma string for diferente, imprimimos os detalhes da alteração e definimos "isChange" como verdadeira, retornando true para sinalizar a atualização. Se nenhuma diferença for encontrada após o loop, a função retorna false, indicando que não houve alterações. Essa abordagem é essencial para detectar atualizações, como novos eventos de notícias ou eventos modificados, que precisamos refletir no painel.

Com a função em mãos, podemos então usá-la para atualizar os eventos.

if (isChangeInStringArrays(previous_eventNames_data,current_eventNames_data)){
   Print("CHANGES IN EVENT NAMES DETECTED. UPDATE THE DASHBOARD VALUES");
   
   ObjectsDeleteAll(0,DATA_HOLDERS);
   ObjectsDeleteAll(0,ARRAY_NEWS);
   
   ArrayFree(current_eventNames_data);

   //---

}

Aqui, verificamos se houve alguma mudança entre os arrays "previous_eventNames_data" e "current_eventNames_data" chamando a função "isChangeInStringArrays". Se a função retornar true, indicando que mudanças foram detectadas, imprimimos a mensagem "MUDANÇAS NOS NOMES DOS EVENTOS DETECTADAS. ATUALIZE OS VALORES DO PAINEL". Em seguida, excluímos todos os objetos relacionados a armazenadores de dados e arrays de notícias no gráfico usando a função ObjectsDeleteAll, especificando os identificadores "DATA_HOLDERS" e "ARRAY_NEWS" como prefixos dos objetos. Fazemos isso para limpar quaisquer informações desatualizadas antes de atualizar o painel com os dados mais recentes dos eventos. Por fim, liberamos a memória usada pelo array "current_eventNames_data" usando a função ArrayFree, garantindo que o array seja limpo em preparação para a próxima atualização. Após isso, atualizamos os dados dos eventos como de costume, mas desta vez criamos os armazenadores de dados e atualizamos os eventos de notícias no painel renovado. Segue a lógica:

if (isChangeInStringArrays(previous_eventNames_data,current_eventNames_data)){
   Print("CHANGES IN EVENT NAMES DETECTED. UPDATE THE DASHBOARD VALUES");
   
   ObjectsDeleteAll(0,DATA_HOLDERS);
   ObjectsDeleteAll(0,ARRAY_NEWS);
   
   ArrayFree(current_eventNames_data);
   
   //--- Initialize starting y-coordinate for displaying news data
   int startY = 162;
   //--- Loop through each calendar value up to the maximum defined total
   for (int i = 0; i < valuesTotal; i++){
   
      MqlCalendarEvent event; //--- Declare event structure
      CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID
   
      MqlCalendarCountry country; //--- Declare country structure
      CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID

      MqlCalendarValue value; //--- Declare calendar value structure
      CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values
      
      //--- Check if the event’s currency matches any in the filter array (if the filter is enabled)
      bool currencyMatch = false;
      if (enableCurrencyFilter) {
         for (int j = 0; j < ArraySize(curr_filter); j++) {
            if (country.currency == curr_filter[j]) {
               currencyMatch = true;
               break;
            }
         }
         
         //--- If no match found, skip to the next event
         if (!currencyMatch) {
            continue;
         }
      }
      
      //--- Check importance level if importance filter is enabled
      bool importanceMatch = false;
      if (enableImportanceFilter) {
         for (int k = 0; k < ArraySize(allowed_importance_levels); k++) {
            if (event.importance == allowed_importance_levels[k]) {
               importanceMatch = true;
               break;
            }
         }
         
         //--- If importance does not match the filter criteria, skip the event
         if (!importanceMatch) {
            continue;
         }
      }
      
      //--- Apply time filter and set timeMatch flag (if the filter is enabled)
      bool timeMatch = false;
      if (enableTimeFilter) {
         datetime eventTime = values[i].time;
         
         if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) {
            timeMatch = true;  //--- Event is already released
         }
         else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) {
            timeMatch = true;  //--- Event is yet to be released
         }
         
         //--- Skip if the event doesn't match the time filter
         if (!timeMatch) {
            continue;
         }
      }
         
      //--- If we reach here, the currency matches the filter
      news_filter_count++; //--- Increment the count of filtered events
      
      //--- Set alternating colors for each data row holder
      color holder_color = (news_filter_count % 2 == 0) ? C'213,227,207' : clrWhite;
   
      //--- Create rectangle label for each data row holder
      createRecLabel(DATA_HOLDERS+string(news_filter_count),62,startY-1,716,26+1,holder_color,1,clrNONE);
   
      //--- Initialize starting x-coordinate for each data entry
      int startX = 65;
      
      //--- Loop through calendar data columns
      for (int k=0; k<ArraySize(array_calendar); k++){
            
         //--- Print event details for debugging
         //Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time);
      
         //--- Skip event if currency does not match the selected country code
         // if (StringFind(_Symbol,country.currency) < 0) continue;
      
         //--- Prepare news data array with time, country, and other event details
         string news_data[ArraySize(array_calendar)];
         news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date
         news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time
         news_data[2] = country.currency; //--- Event country currency
      
         //--- Determine importance color based on event impact
         color importance_color = clrBlack;
         if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;}
         else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;}
         else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;}
      
         //--- Set importance symbol for the event
         news_data[3] = ShortToString(0x25CF);
      
         //--- Set event name in the data array
         news_data[4] = event.name;
            
         //--- Populate actual, forecast, and previous values in the news data array
         news_data[5] = DoubleToString(value.GetActualValue(),3);
         news_data[6] = DoubleToString(value.GetForecastValue(),3);
         news_data[7] = DoubleToString(value.GetPreviousValue(),3);         
         
         //--- Create label for each news data item
         if (k == 3){
            createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(22-12),news_data[k],importance_color,22,"Calibri");
         }
         else {
            createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,12,"Calibri");
         }
      
         //--- Increment x-coordinate for the next column
         startX += buttons[k]+3;
      }
      
      ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1);
      current_eventNames_data[ArraySize(current_eventNames_data)-1] = event.name;
      
      //--- Increment y-coordinate for the next row of data
      startY += 25;
      //Print(startY); //--- Print current y-coordinate for debugging
   }
   
   Print("CURRENT EVENT NAMES DATA SIZE = ",ArraySize(current_eventNames_data));
   ArrayPrint(current_eventNames_data);
   Print("PREVIOUS EVENT NAMES DATA SIZE (Before) = ",ArraySize(previous_eventNames_data));
   ArrayPrint(previous_eventNames_data);
   ArrayFree(previous_eventNames_data);
   ArrayCopy(previous_eventNames_data,current_eventNames_data);
   Print("PREVIOUS EVENT NAMES DATA SIZE (After) = ",ArraySize(previous_eventNames_data));
   ArrayPrint(previous_eventNames_data);
   
}

Aqui, atualizamos o painel com base nos dados recém-adquiridos para garantir que as atualizações em tempo real estejam sendo aplicadas. Usando a lógica de cópia, registramos os dados no armazenador de dados "previous" para que possamos usar os dados atuais na próxima verificação. Destacamos a lógica que cuida disso, mas vamos analisá-la mais a fundo.

Print("CURRENT EVENT NAMES DATA SIZE = ",ArraySize(current_eventNames_data));
ArrayPrint(current_eventNames_data);
Print("PREVIOUS EVENT NAMES DATA SIZE (Before) = ",ArraySize(previous_eventNames_data));
ArrayPrint(previous_eventNames_data);
ArrayFree(previous_eventNames_data);
ArrayCopy(previous_eventNames_data,current_eventNames_data);
Print("PREVIOUS EVENT NAMES DATA SIZE (After) = ",ArraySize(previous_eventNames_data));
ArrayPrint(previous_eventNames_data);

Aqui, começamos imprimindo o tamanho atual do array "current_eventNames_data" usando a função ArraySize e exibindo seu conteúdo com a função ArrayPrint. Isso nos ajudará a inspecionar o conjunto atual de nomes de eventos que estamos rastreando. Em seguida, imprimimos o tamanho do array "previous_eventNames_data" antes de ser atualizado, seguido da impressão de seu conteúdo.

Depois, liberamos a memória usada por "previous_eventNames_data" com a função ArrayFree, garantindo que quaisquer dados anteriores armazenados no array sejam apagados para evitar problemas de memória. Após liberar a memória, usamos a função ArrayCopy para copiar o conteúdo do array "current_eventNames_data" para "previous_eventNames_data", atualizando-o efetivamente com os nomes de eventos mais recentes.

Por fim, imprimimos o tamanho atualizado do array "previous_eventNames_data" e seu conteúdo para confirmar que o array agora contém os nomes de eventos mais recentes. Isso garante que os nomes de eventos anteriores sejam corretamente atualizados para comparações futuras. Ao executar o programa, temos o seguinte resultado.

Atualizações de tempo.

GIF DE ATUALIZAÇÕES DE TEMPO

Atualizações de eventos.

ATUALIZAÇÃO DE EVENTOS

Atualização do painel.

ATUALIZAÇÃO DE PAINEL

A partir da imagem, podemos ver que os dados recém-registrados são atualizados com precisão no painel. Para confirmar novamente, podemos aguardar mais algum tempo e tentar verificar se conseguimos acompanhar esses dados, juntamente com os que registram os dados atualizados. Aqui está o resultado.

Atualização dos dados dos eventos.

ATUALIZAÇÃO DE DADOS DE NOVOS EVENTOS

Atualização do painel.

NOVA ATUALIZAÇÃO DO PAINEL

A partir da imagem, podemos ver que, uma vez que haja mudanças em relação aos dados armazenados anteriormente, elas são detectadas e atualizadas de acordo com os dados recém-registrados e armazenadas para referência futura. Os dados armazenados são então usados para atualizar a interface do painel em tempo real, exibindo os eventos de notícias atuais e, assim, confirmando o sucesso do nosso objetivo.


Conclusão

Em conclusão, implementamos um sistema robusto para monitorar e detectar mudanças nos eventos de Notícias Econômicas do MQL5, comparando os dados de eventos armazenados anteriormente com as novas atualizações recuperadas. Esse mecanismo de comparação garante que quaisquer diferenças nos nomes ou detalhes dos eventos sejam prontamente identificadas, acionando a atualização do painel para manter a precisão e a relevância. Ao filtrar os dados com base em moeda, importância e tempo, refinamos ainda mais o processo para focar em eventos de maior impacto, enquanto atualizamos dinamicamente a interface.

Nas próximas partes desta série, construiremos sobre essa base integrando eventos de notícias econômicas em estratégias de negociação, permitindo aplicações práticas dos dados. Além disso, nosso objetivo é aprimorar a funcionalidade do painel, introduzindo mobilidade e responsividade, garantindo uma experiência mais fluida e interativa para os traders. Fique atento.

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

Arquivos anexados |
Últimos Comentários | Ir para discussão (2)
Stanislav Korotky
Stanislav Korotky | 3 dez. 2024 em 20:57
Os usuários provavelmente ficarão interessados em saber que os mesmos tópicos foram abordados minuciosamente no livro de algotrading (e o site não mostra isso de alguma forma nas sugestões automáticas), incluindo rastreamento e salvamento de alterações de eventos, filtragem de eventos por várias condições (com diferentes operadores lógicos) para um conjunto estendido de campos, exibição de subconjunto de calendário personalizável em tempo real em um painel no gráfico e incorporação para negociação em EAs com suporte para transferência de arquivo completo do calendário para o testador.
Allan Munene Mutiiria
Allan Munene Mutiiria | 5 dez. 2024 em 00:08
Stanislav Korotky #:
Os usuários provavelmente ficarão interessados em saber que os mesmos tópicos foram abordados minuciosamente no livro de algotrading (e o site não mostra isso de alguma forma nas sugestões automáticas), incluindo rastreamento e salvamento de alterações de eventos, filtragem de eventos por várias condições (com diferentes operadores lógicos) para um conjunto estendido de campos, exibição de subconjunto de calendário personalizável em tempo real em um painel no gráfico e incorporação para negociação em EAs com suporte para transferência de arquivo completo do calendário para o testador.
Tudo bem. Obrigado.
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Utilizando o modelo de Machine Learning CatBoost como Filtro para Estratégias de Seguimento de Tendência Utilizando o modelo de Machine Learning CatBoost como Filtro para Estratégias de Seguimento de Tendência
CatBoost é um poderoso modelo de machine learning baseado em árvores que se especializa em tomada de decisão com base em features estacionárias. Outros modelos baseados em árvores como XGBoost e Random Forest compartilham características semelhantes em termos de robustez, capacidade de lidar com padrões complexos e interpretabilidade. Esses modelos têm uma ampla gama de usos, desde análise de features até gestão de risco. Neste artigo, vamos percorrer o procedimento de utilização de um modelo CatBoost treinado como filtro para uma estratégia clássica de seguimento de tendência com cruzamento de médias móveis.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Simulação de mercado: Position View (XII) Simulação de mercado: Position View (XII)
No artigo, você aprenderá como criar uma indicação visual na sua plataforma de trading para saber se você está em uma posição comprada ou vendida no gráfico, sem precisar acessar o terminal. Além disso, o texto aborda a implementação de uma funcionalidade que melhora a visualização ao mover linhas de take profit e stop loss, ocultando a linha de preço do mouse durante a movimentação para evitar confusões. A leitura oferece insights práticos para customizar sistemas de simulação de mercado.