English Русский 中文 Español Deutsch 日本語
preview
Negociando com o Calendário Econômico MQL5 (Parte 2): Criando um Painel de Notícias

Negociando com o Calendário Econômico MQL5 (Parte 2): Criando um Painel de Notícias

MetaTrader 5Negociação |
175 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Introdução

Neste artigo, damos continuidade à nossa exploração anterior na Parte 1 do MetaQuotes Language 5 (MQL5) Economic Calendar, onde focamos em dominar as funções necessárias para recuperar e analisar eventos de notícias econômicas. Agora, daremos o próximo passo criando um painel de Notícias que fornece aos traders uma interface conveniente para acessar dados econômicos críticos em tempo real. Este painel ajudará a simplificar os processos de tomada de decisão, destacando eventos de notícias relevantes que podem influenciar os movimentos do mercado. Os tópicos que cobriremos incluem:

  1. Projetando o Painel de Notícias
  2. Configurando o Painel no MQL5
  3. Conclusão

Com esses componentes, nosso objetivo é aprimorar a experiência de negociação, fornecendo uma ferramenta eficaz para monitorar eventos econômicos em tempo real no MQL5.


Projetando o Painel de Notícias

Projetar o painel de notícias é uma etapa crucial na criação de uma ferramenta eficaz para monitorar eventos de notícias econômicas usando o Calendário Econômico MQL5. Nosso objetivo é criar uma interface amigável e visualmente atraente que apresente informações importantes de forma clara e concisa. Um painel bem estruturado nos permitirá avaliar rapidamente a relevância e o impacto dos eventos econômicos em nossas estratégias de negociação.

Ao projetar o painel do dashboard, precisaremos começar identificando os componentes-chave que precisam ser exibidos. Esses componentes normalmente incluem o nome do evento, horário agendado, moeda afetada, nível de importância e uma breve descrição do evento. Para melhorar a usabilidade, organizaremos essas informações em um formato de tabela, com cada linha representando um evento econômico diferente. Pretendemos tornar a tabela de fácil leitura, utilizando cores contrastantes para diferentes níveis de importância para permitir a identificação rápida de eventos de alto impacto.

Para tornar o painel mais atraente, escolheremos elementos visuais, como bordas, fundos e fontes, para criar uma aparência limpa e profissional. O layout permitirá fácil navegação, garantindo que possamos localizar rapidamente as informações de que precisamos sem nos sobrecarregar com detalhes excessivos. Mantendo o design intuitivo e direto, permitiremos que os traders se concentrem em tomar decisões informadas com base nos eventos econômicos exibidos no painel do dashboard. O painel será organizado conforme mencionado e apresentará os componentes ilustrados abaixo:

COMPONENTES DO PAINEL

Com uma compreensão clara de nossos objetivos, vamos mergulhar no processo de automação. No artigo anterior, focamos em dominar as funções do Calendário Econômico do MQL5 para recuperar e analisar efetivamente eventos de notícias econômicas. Se você ainda não o fez, consulte esse conteúdo para garantir que esteja bem preparado para acompanhar enquanto prosseguimos com a criação do nosso painel de notícias. Vamos começar então!


Configurando o Painel no MQL5

Nesta seção, vamos nos concentrar em configurar o painel de notícias criando os elementos necessários usando MQL5. Primeiro, precisaremos criar funções para os três elementos que serão necessários: o rótulo retangular, o botão e os rótulos de texto. Essa abordagem será extremamente benéfica, pois nos permite reutilizar as mesmas funções ao criar recursos semelhantes, eliminando a necessidade de repetir todo o processo para cada novo objeto. Dessa forma, economizamos tempo e espaço, tornando o processo rápido, simples e mantendo nossos trechos de código concisos.

Para criar o rótulo retangular, criaremos uma função que recebe dez argumentos ou parâmetros. Essa função definirá as propriedades do retângulo, como posição, tamanho, cor e estilo, permitindo que personalizemos a aparência visual do rótulo de acordo com os requisitos de design do nosso painel.

//+------------------------------------------------------------------+
//|     Function to create rectangle label                           |
//+------------------------------------------------------------------+

bool createRecLabel(string objName, int xD, int yD, int xS, int yS,
                    color clrBg, int widthBorder, color clrBorder = clrNONE,
                    ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {

...
}

A assinatura da função ilustra tudo. É uma função booleana com o nome "createRecLabel", o que significa que retornará dois sinais booleanos, verdadeiro ou falso, em caso de sucesso ou falha, respectivamente. Para compreender facilmente seus parâmetros, vamos detalhá-los e explicá-los individualmente abaixo.

  • "objName:" Este parâmetro representa o nome exclusivo do objeto rótulo retangular. Ele serve como identificador para o elemento gráfico que está sendo criado.
  • "xD e yD:" Esses parâmetros determinam as distâncias X e Y a partir do canto onde o rótulo retangular será posicionado. Pense neles como as coordenadas que definem o canto superior esquerdo do retângulo em relação ao gráfico.
  • "xS e yS:" Esses parâmetros especificam a largura e a altura do retângulo. O valor "xS" determina o quão largo o retângulo será na horizontal, enquanto "yS" controla sua altura vertical.

DISTÂNCIA & TAMANHO

  • "clrBg:" O parâmetro "clrBg" representa a cor de fundo do rótulo do retângulo. Escolha uma cor que contraste bem com o fundo do gráfico ou complemente outros elementos.
  • "widthBorder:" Este parâmetro define a largura da borda ao redor do retângulo. Se você quiser uma borda, defina um valor positivo; caso contrário, use zero para nenhuma borda.
  • "clrBorder:" Parâmetro opcional para a cor da borda. Se você quiser uma borda, especifique uma cor (por exemplo, "clrNONE" para nenhuma cor de borda).
  • "borderType:" Especifique o tipo de borda para o retângulo. As opções incluem plana, elevada ou outros estilos. Para uma borda plana simples, use BORDER_FLAT.
  • "borderStyle:" Se você escolher uma borda plana, este parâmetro determina o estilo da linha (por exemplo, sólida, tracejada). Use STYLE_SOLID para uma linha contínua.

Na assinatura da função, você deve ter notado que alguns dos argumentos já estão inicializados com algum valor. O valor de inicialização representa o valor padrão que será atribuído a esse parâmetro caso ele seja ignorado durante a chamada da função. Por exemplo, nossa cor de borda padrão é nenhuma, o que significa que, se o valor da cor não for especificado durante a chamada da função, nenhuma cor será aplicada à borda do nosso rótulo de retângulo. 

Dentro do corpo da função, contido pelas chaves ({}), definimos nossos procedimentos de criação de objeto.

// Create a rectangle label object
if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) {
  Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
  return (false); // Return false if object creation fails
}

Começamos usando uma instrução if para verificar se o objeto não está criado. A função ObjectCreate, um booleano que recebe 6 argumentos, é usada. Essa função cria um objeto com o nome, tipo e coordenadas iniciais especificados na subjanela do gráfico especificado. Primeiro, especificamos a janela do gráfico, 0 significa que o objeto deve ser criado na janela principal. Em seguida, fornecemos o nome do objeto. Esse é o nome que será atribuído exclusivamente a um objeto específico. O tipo de objeto que queremos criar é do tipo OBJ_RECTANGLE_LABEL, significando um objeto para criar e projetar a interface gráfica personalizada. Em seguida, fornecemos a subjanela, 0 para a subjanela atual. Por fim, fornecemos os valores de tempo e preço como zero (0), já que não os vincularemos ao gráfico, mas sim às coordenadas da janela do gráfico. Os pixels são usados para definir o mapeamento.

Se a criação do objeto falhar, a função ObjectCreate retorna false, claramente não há motivo para prosseguir, retornamos com um erro. Nesse caso, informamos o erro imprimindo-o no diário ao lado do código de erro e retornando false. Pode ter ocorrido um erro anterior e, portanto, para obter o erro mais recente, precisamos limpar o erro anterior. Isso é conseguido chamando a função "ResetLastError", que é uma função interna do MQL5, logo antes da nossa lógica de criação do objeto.

ResetLastError(); // Reset any previous error codes

A finalidade da função é definir o valor da variável predefinida _LastError, que armazena o código de erro da última operação que encontrou um erro, para zero. Ao chamá-la, garantimos que quaisquer códigos de erro anteriores sejam limpos antes de prosseguir com as próximas operações. Essa etapa é essencial porque nos permite lidar com erros novos de forma independente, sem interferência de estados de erro anteriores.

Se não retornarmos até este ponto, significa que criamos o objeto e, portanto, podemos continuar com a atualização de propriedade do objeto. Uma função interna "ObjectSet..." define o valor da propriedade correspondente do objeto. A propriedade do objeto deve ser do tipo datetime, integer, color, boolean ou character.

// Set properties for the rectangle label
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle
ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color
ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type
ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object
ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

Vamos nos concentrar na primeira lógica de propriedade.

ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner

Aqui, usamos a função interna ObjectSetInteger e passamos os parâmetros respectivamente. Os parâmetros são descritos abaixo.

  • Chart id: Este é o identificador do gráfico. "0" refere-se ao gráfico atual (ID do gráfico). Estamos ajustando as propriedades de um objeto dentro deste gráfico.
  • Name: Este é o nome do objeto. "objName" representa o nome exclusivo atribuído ao objeto de rótulo do retângulo.
  • Property id: Este é o ID da propriedade do objeto e seu valor pode ser um dos valores da enumeração ENUM_OBJECT_PROPERTY_INTEGER. OBJPROP_XDISTANCE especifica que estamos modificando a propriedade de distância X.
  • Property value: Este é o valor da propriedade. O valor atribuído a "xD" determina o quão longe à direita (ou à esquerda, se negativo) o canto superior esquerdo do nosso rótulo de retângulo será posicionado horizontalmente a partir da borda esquerda do gráfico.

Da mesma forma, configuramos as outras propriedades usando o mesmo formato. OBJPROP_YDISTANCE configura a propriedade de distância Y do rótulo do retângulo. O valor "yD" determina o quão longe o canto superior esquerdo do rótulo do retângulo será posicionado verticalmente a partir da borda superior do gráfico. Em outras palavras, controla o posicionamento vertical do rótulo dentro da área do gráfico. Isso define a distância Y a partir do canto. O "OBJPROP_XSIZE" e o "OBJPROP_YSIZE" definem respectivamente a largura e a altura do retângulo. 

Para posicionar nosso objeto, usamos a propriedade OBJPROP_CORNER para determinar o canto em que queremos que nosso objeto esteja na janela do gráfico.

ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner

A propriedade só pode ser de 4 tipos:

  • CORNER_LEFT_UPPER: Centro das coordenadas está no canto superior esquerdo do gráfico.
  • CORNER_LEFT_LOWER: Centro das coordenadas está no canto inferior esquerdo do gráfico.
  • CORNER_RIGHT_LOWER: Centro das coordenadas está no canto inferior direito do gráfico.
  • CORNER_RIGHT_UPPER: Centro das coordenadas está no canto superior direito do gráfico.

Em uma representação fotográfica, é isso que temos.

CANTOS

O restante das propriedades é direto. Adicionamos comentários a elas para melhor compreensão. Em seguida, apenas redesenhamos o gráfico usando a função ChartRedraw para que as alterações entrem em vigor automaticamente sem precisar esperar por uma mudança nas cotações de preços ou eventos do gráfico.

ChartRedraw(0); // Redraw the chart

Finalmente, nós return true, significando que a criação e atualização das propriedades do objeto foram um sucesso.

return (true); // Retorna true se a criação do objeto e a configuração das propriedades forem bem-sucedidas

O código completo da função responsável pela criação de um objeto retângulo na janela do gráfico está abaixo.

bool createRecLabel(string objName, int xD, int yD, int xS, int yS,
                    color clrBg, int widthBorder, color clrBorder = clrNONE,
                    ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {
    ResetLastError(); // Reset any previous error codes
    
    // Create a rectangle label object
    if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) {
        Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
        return (false); // Return false if object creation fails
    }
    
    // Set properties for the rectangle label
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle
    ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color
    ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type
    ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected
    
    ChartRedraw(0); // Redraw the chart
    
    return (true); // Return true if object creation and property settings are successful
}

Para criar um objeto botão, a mesma abordagem de função é usada. O código para criar uma função de botão personalizado está abaixo.

//+------------------------------------------------------------------+
//|     Function to create button                                    |
//+------------------------------------------------------------------+

bool createButton(string objName, int xD, int yD, int xS, int yS,
                  string txt = "", color clrTxt = clrBlack, int fontSize = 12,
                  color clrBg = clrNONE, color clrBorder = clrNONE,
                  string font = "Arial Rounded MT Bold") {
    // Reset any previous errors
    ResetLastError();

    // Attempt to create the button object
    if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
        // Print an error message if creation fails
        Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError);
        return (false);
    }

    // Set properties for the button
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the button
    ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the button
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the button
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color
    ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size
    ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name
    ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color
    ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Button state (not pressed)
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

    // Redraw the chart to display the button
    ChartRedraw(0);

    return (true); // Button creation successful
}

As diferenças no código são que um objeto retângulo não pode conter um texto dentro dele, mas um botão inclui um texto descritivo da funcionalidade do botão, caso seja necessário. Portanto, para os parâmetros de entrada, consideramos as propriedades de texto, no nosso caso o valor do texto, cor, tamanho da fonte e nome da fonte. O tipo de borda para o nosso botão é estático e, assim, eliminamos suas propriedades e mantemos apenas a cor da borda. 

O tipo de objeto que criamos é OBJ_BUTTON, significando que criamos um objeto gráfico de botão. Seus pontos de ancoragem são definidos em pixels. A propriedade de borda que mantemos é a cor da borda e substituímos o restante pelas propriedades de entrada de texto.

Finalmente, precisamos da função do último elemento, que é o rótulo de texto. O rótulo de texto elimina a necessidade de um objeto de fundo e, assim, sua implementação é bem mais fácil do que o restante das funções. Só precisamos do texto e, portanto, nos concentramos nas propriedades de texto. Seu código está abaixo.

//+------------------------------------------------------------------+
//|     Function to create text label                                |
//+------------------------------------------------------------------+

bool createLabel(string objName, int xD, int yD,
                 string txt, color clrTxt = clrBlack, int fontSize = 12,
                 string font = "Arial Rounded MT Bold") {
    // Reset any previous errors
    ResetLastError();

    // Attempt to create the label object
    if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) {
        // Print an error message if creation fails
        Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError);
        return (false);
    }

    // Set properties for the label
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the label
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color
    ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size
    ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Label state (not active)
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

    // Redraw the chart to display the label
    ChartRedraw(0);

    return (true); // Label creation successful
}

As principais diferenças nessa estrutura de código em relação à função do botão são o tamanho do objeto e as propriedades da borda. Na assinatura da função, eliminamos os tamanhos do objeto, bem como as propriedades da borda. Definimos nosso tipo de objeto como OBJ_LABEL para significar que desenhamos rótulos conforme as coordenadas definidas do rótulo na janela do gráfico. Finalmente, eliminamos os parâmetros de tamanho e de borda e é isso. Tão simples quanto isso.

Agora que temos as funções necessárias para criar uma Interface Gráfica de Usuário (GUI), vamos usá-las para criar o painel. Precisaremos de nomes para o objeto e, para gerenciar facilmente a interação dos nomes dos objetos, é muito mais fácil definir macros. 

#define MAIN_REC "MAIN_REC"

Usamos a palavra-chave #define para definir uma macro chamada "MAIN_REC" com o valor "MAIN_REC" para armazenar facilmente o nome base do nosso retângulo principal, em vez de ter que reescrever repetidamente o nome em cada instância em que criamos o nível, economizando significativamente tempo e reduzindo as chances de fornecer o nome incorretamente. Basicamente, macros são usadas para substituição de texto durante a compilação.

Nosso código será majoritariamente baseado na seção de inicialização do expert, já que queremos criar o painel no momento da inicialização. Assim, o manipulador de eventos OnInit conterá a maior parte da estrutura do código. 

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

int OnInit(){

   ...
   
   return(INIT_SUCCEEDED);
}

A função OnInit é um manipulador de eventos chamado na instância de inicialização do expert para fazer as inicializações necessárias, se houver. 

Em seguida, chamamos a função para criar um rótulo de retângulo digitando seu nome e fornecendo seus parâmetros.

//--- Create main rectangle label for the dashboard panel
createRecLabel(MAIN_REC,50,50,740,410,clrSeaGreen,1);

Aqui, nosso nome de retângulo é "MAIN_REC", conforme a definição da macro. Nossa distância ao longo do eixo x, a escala de tempo e data, a partir do canto superior esquerdo da janela do gráfico é de 50 pixels, e a distância ao longo do eixo y, a escala de preço, é de 50 pixels. A largura é de 740 pixels e a altura é de 410 pixels, respectivamente. Escolhemos nossa cor de fundo verde-mar, com largura de borda de 1, e o restante dos parâmetros padrão. Para obter a faixa de pixels aproximadamente, você pode reduzir o gráfico para 0 e o número de barras entre duas coordenadas de cruzamento é igual ao número de pixels na escala horizontal. Em um exemplo, aqui está o que queremos dizer.

CROSS-HAIR

Os outros parâmetros foram deixados de fora, o que significa que os valores padrão serão aplicados automaticamente. Ou seja, o tipo de borda será plano e o estilo de linha será uma linha contínua sólida. Após a compilação, é isso que temos atualmente.

PAINEL PRINCIPAL

Para criar os subquadros, novamente declaramos explicitamente as respectivas macros para eles.

#define SUB_REC1 "SUB_REC1"
#define SUB_REC2 "SUB_REC2"

Em seguida, chamamos a mesma função para criar os subquadros. Queremos que nossos quadros fiquem dentro do quadro do painel base e, portanto, exigirão que usemos uma cor ligeiramente diferente. Para conseguir isso, usamos uma cor branca e verde e uma margem de 3 e 5 pixels.

//--- Create sub-rectangle labels within the main panel for different sections
createRecLabel(SUB_REC1,50+3,50+30,740-3-3,410-30-3,clrWhite,1);
createRecLabel(SUB_REC2,50+3+5,50+30+50+27,740-3-3-5-5,410-30-3-50-27-10,clrGreen,1);

Aqui, estamos configurando duas subseções adicionais, "SUB_REC1" e "SUB_REC2", dentro do painel principal do dashboard para ajudar a organizar e separar visualmente o conteúdo. Usando a função "createRecLabel", posicionamos "SUB_REC1" adicionando um deslocamento de 3 pixels das bordas esquerda e direita e 30 pixels do topo do retângulo principal "MAIN_REC" — criando efetivamente uma seção emoldurada dentro do painel principal. Definimos sua largura como "740-3-3" para se ajustar às margens reduzidas e sua altura como "410-30-3" para deixar espaço acima e abaixo, permitindo que se encaixe ordenadamente dentro do retângulo principal. Essa subseção é configurada com uma cor branca, estabelecendo um fundo neutro que contrasta com a cor verde-mar do painel principal para melhorar a clareza visual.

Em seguida, usamos "createRecLabel" para adicionar "SUB_REC2", uma seção adicional dentro de "SUB_REC1", e posicioná-la com deslocamentos mais precisos para um layout organizado e em camadas. Para conseguir isso, definimos a coordenada X inicial como "50+3+5", posicionando-a mais para dentro de "SUB_REC1" para defini-la visualmente como uma área distinta dentro dessa subseção. Definimos a coordenada Y como "50+30+50+27" para contabilizar os deslocamentos verticais tanto do retângulo principal quanto do primeiro sub-retângulo. A largura, "740-3-3-5-5", ajusta "SUB_REC2" precisamente dentro do espaço horizontal restante, enquanto a altura, "410-30-3-50-27-10", permite uma região equilibrada e separada. Definir "SUB_REC2" como verde adiciona um contraste forte, indicando que é uma área que exibirá dados críticos. Esse cuidadoso empilhamento de retângulos é essencial para estabelecer um painel estruturado e visualmente navegável para o dashboard. Ao compilar, obtemos os seguintes resultados:

QUADROS DO PAINEL

Até este ponto, a configuração de nossos quadros, margens e limites do nosso painel está completa. Em seguida, prosseguimos para adicionar as outras utilidades do painel, suas propriedades e efeitos. Para começar, vamos dar um título ao painel.

#define HEADER_LABEL "HEADER_LABEL"

//---

//--- Create the header label with text "MQL5 Economic Calendar"
createLabel(HEADER_LABEL,50+3+5,50+5,"MQL5 Economic Calendar",clrWhite,15);

Aqui, definimos o identificador do rótulo "HEADER_LABEL" com o valor "HEADER_LABEL" para consistência e facilidade de referência a este rótulo específico em todo o nosso código. Este rótulo servirá como o cabeçalho do nosso painel de dashboard, exibindo de forma proeminente o título "MQL5 Economic Calendar".

Em seguida, usando a função "createLabel", criamos o rótulo de cabeçalho na posição especificada. Definimos sua coordenada X como "50+3+5", posicionando-a ligeiramente à direita da borda do painel principal para garantir que ela se alinhe dentro do retângulo "SUB_REC1" e não sobreponha nenhuma margem. A coordenada Y, "50+5", a posiciona alguns pixels abaixo da borda superior do retângulo principal, garantindo legibilidade. Para visibilidade, definimos a cor do texto como branca com tamanho de fonte "15", criando um cabeçalho ousado e perceptível que marca o propósito do dashboard. Este cabeçalho ancorará o design visual do dashboard, comunicando imediatamente seu propósito aos usuários. Aqui está o que obtemos.

O TÍTULO

Isso foi um sucesso. Agora podemos prosseguir para criar os cabeçalhos do painel. Para isso, usaremos o método mais simples, que é apenas definir os títulos dos cabeçalhos, colocá-los em um array e depois usar um loop para posicioná-los dinamicamente, já que estão em apenas uma linha. No entanto, também teremos que definir os tamanhos dos botões de forma diferente, já que terão larguras variadas devido ao comprimento individual de cada cabeçalho. Abaixo está a lógica que usaremos para alcançar isso.

string array_calendar[] = {"Date","Time","Cur.","Imp.","Event","Actual","Forecast","Previous"};
int buttons[] = {80,50,50,40,281,60,70,70};

Definimos dois arrays para organizar os rótulos e dimensões dos botões no dashboard. O primeiro array, "array_calendar", contém strings para cada cabeçalho de coluna que exibiremos, especificando o tipo de informação: "Date", "Time", "Cur." (Currency), "Imp." (Impacto/Importância), "Event", "Actual", "Forecast" e "Previous". Cada string representa um rótulo para uma categoria de dados, ajudando-nos a entender o que cada seção do dashboard exibirá.

O segundo array, "buttons", contém inteiros representando as larguras (em pixels) de cada botão associado às respectivas colunas em "array_calendar". Essas larguras são adaptadas a cada tipo de dado para garantir que o layout permaneça alinhado e visualmente organizado. Por exemplo, "80" pixels para a coluna "Date" acomodam formatos de data mais longos, enquanto larguras menores, como "50" pixels, são definidas para as colunas "Time" e "Cur.", que exigem menos espaço. Juntos, esses arrays ajudam a simplificar a criação dos cabeçalhos das colunas do dashboard, estabelecendo uma base estruturada para outros elementos da Interface de Usuário (UI). A partir daqui, podemos então usar um loop para criar os cabeçalhos dinamicamente.

#define ARRAY_CALENDAR "ARRAY_CALENDAR"

//---

//--- Initialize starting x-coordinate for button positioning
int startX = 59;
   
//--- Loop through the array_calendar elements to create buttons
for (int i=0; i<ArraySize(array_calendar); i++){
   //--- Create each button for calendar categories
   createButton(ARRAY_CALENDAR+IntegerToString(i),startX,132,buttons[i],25,array_calendar[i],clrWhite,13,clrGreen,clrNONE,"Calibri Bold");
   startX += buttons[i]+3; //--- Update x-coordinate for the next button
}

Aqui, inicializamos e posicionamos os botões com base nos elementos de "array_calendar" para rotular cada categoria em nosso painel do dashboard. Primeiro, definimos o identificador "ARRAY_CALENDAR" para a série de botões do calendário. Em seguida, definimos "startX" como "59" como a coordenada x inicial, que posicionará horizontalmente o primeiro botão no painel.

Em seguida, usamos um for loop para iterar por cada item em "array_calendar" para criar um botão. Para cada iteração, chamamos a função "createButton" e passamos um ID exclusivo para cada botão, anexando o índice do loop a "ARRAY_CALENDAR". Isso garante que cada ID de botão seja único, referenciando categorias como "Date", "Time", "Cur.", etc. Especificamos a posição "startX" e usamos valores de "buttons" para definir a largura de cada botão, garantindo alinhamento com a respectiva categoria de dados. Cada botão também recebe propriedades de estilo, incluindo uma cor de fonte (branca), tamanho de fonte ("13") e cores de fundo (verde para o botão e nenhuma para a borda), definidos na fonte "Calibri Bold". Após criar cada botão, ajustamos "startX" adicionando a largura do botão atual mais uma margem de "3" pixels, espaçando uniformemente os botões para a próxima iteração. Após a compilação, temos a seguinte saída.

CABEÇALHOS

Após criar os cabeçalhos, agora precisamos criar a outra subseção para exibir o horário, o número de eventos de notícias identificados e os níveis de impacto. Primeiro, começaremos obtendo os eventos de notícias da parte anterior da série.

//--- 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));

Isso nos dá os valores de eventos históricos conforme recuperados do Calendário Econômico do MQL5 e, portanto, em vez de apenas imprimi-los, podemos exibi-los no dashboard. Aqui está a lógica que aplicamos.

#define TIME_LABEL "TIME_LABEL"

//---

//--- Create label displaying server time and total number of news events found
createLabel(TIME_LABEL,70,85,"Server Time: "+TimeToString(TimeCurrent(),
           TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
           IntegerToString(allValues),clrBlack,14,"Times new roman bold");

Aqui, criamos um rótulo para exibir a hora atual do servidor junto com o número total de eventos de notícias recuperados. Primeiro, definimos o identificador "TIME_LABEL" para referenciar exclusivamente este rótulo dentro do nosso painel do dashboard.

Em seguida, chamamos a função "createLabel" para gerar o próprio rótulo. Especificamos a posição do rótulo fornecendo as coordenadas "70" e "85", que determinam onde o rótulo aparecerá no painel. O texto do rótulo é construído dinamicamente usando a função TimeToString, que formata a hora atual do servidor recuperada pela função TimeCurrent no formato de data e segundos. Concatenamos essa hora formatada com a string "||| Total News: " e convertemos a variável "allValues", que contém a contagem de eventos de notícias, em uma string usando a função IntegerToString. Isso cria um rótulo abrangente que mostra tanto a hora do servidor quanto o número total de eventos de notícias encontrados. Estilizamos o rótulo com a cor preta, tamanho de fonte 14 e usamos a fonte "Times New Roman bold" para clara visibilidade. Pela mesma lógica, criamos também o rótulo de impacto.

#define IMPACT_LABEL "IMPACT_LABEL"

//---

//--- Create label for displaying "Impact" category header
createLabel(IMPACT_LABEL,70,105,"Impact: ",clrBlack,14,"Times new roman bold");

Após a compilação, obtemos a seguinte saída.

VALORES E ETIQUETAS DE IMPACTO

Isso foi um sucesso. Agora precisamos avançar para exibir os respectivos botões de impacto com seus respectivos rótulos e cores, para que os usuários possam saber o que cada nível de impacto identifica. 

//--- Define labels for impact levels and size of impact display areas
string impact_labels[] = {"None", "Low", "Medium", "High"};
int impact_size = 100;

//--- Loop through impact levels to create buttons for each level
for (int i=0; i<ArraySize(impact_labels); i++){
   color impact_color = clrBlack, label_color = clrBlack; //--- Default colors for label and button

   //--- Assign color based on impact level
   if (impact_labels[i] == "None"){label_color = clrWhite;}
   else if (impact_labels[i] == "Low"){impact_color = clrYellow;}
   else if (impact_labels[i] == "Medium"){impact_color = clrOrange;}
   else if (impact_labels[i] == "High"){impact_color = clrRed;}

   //--- Create button for each impact level
   createButton(IMPACT_LABEL+string(i),140+impact_size*i,105,impact_size,25,impact_labels[i],label_color,12,impact_color,clrBlack);
}

Aqui, definimos os rótulos para os diferentes níveis de impacto associados a eventos econômicos e o tamanho das áreas de exibição para esses indicadores de impacto. Primeiro, declaramos um array chamado "impact_labels" contendo strings que representam os vários níveis de impacto: "None", "Low", "Medium" e "High". Além disso, inicializamos uma variável inteira "impact_size" com o valor 100, que determina a largura dos botões que serão criados para cada nível de impacto.

Em seguida, entramos em um loop que itera sobre o array "impact_labels", utilizando a função ArraySize para determinar o número total de níveis de impacto. Dentro desse loop, primeiro definimos as cores padrão para o botão e o rótulo usando a cor preta. Depois, usamos instruções condicionais para atribuir cores específicas com base no nível de impacto atual. Se o nível de impacto for "None", alteramos "label_color" para branco. Se o nível for "Low", definimos "impact_color" como amarelo. Para "Medium", atribuímos "impact_color" como laranja, e para "High", designamos "impact_color" como vermelho". Por fim, chamamos a função "createButton" para gerar um botão para cada nível de impacto, posicionando-o usando uma coordenada x dinâmica calculada com "140 + impact_size * i", mantendo uma coordenada y fixa de 105, e fornecendo as dimensões e cores de acordo. Aqui está o marco atual.

BOTÕES DE IMPACTO

Isso foi um sucesso. Agora podemos prosseguir para adicionar os dados reais do calendário ao dashboard. No entanto, antes disso, precisaremos particionar o segundo subquadro para que possamos dar ao painel um visual mais profissional, em vez de apenas colocar dados nele. Conseguimos isso por meio da seguinte lógica.

//--- Limit the total number of values to display
int valuesTotal = (allValues <= 11) ? allValues : 11;

//--- 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++){

   //--- Set alternating colors for each data row holder
   color holder_color = (i % 2 == 0) ? C'213,227,207' : clrWhite;

   //--- Create rectangle label for each data row holder
   createRecLabel(DATA_HOLDERS+string(i),62,startY-1,716,26,holder_color,1,clrBlack);

   //--- Increment y-coordinate for the next row of data
   startY += 25;
   Print(startY); //--- Print current y-coordinate for debugging
}

Limitamos o número total de valores a serem exibidos em nosso dashboard definindo uma variável inteira chamada "valuesTotal". Usamos um operador condicional (ternary) para verificar se "allValues" é menor ou igual a "11". Se for, definimos "valuesTotal" como "allValues"; caso contrário, definimos como "11". Essa abordagem garantirá que não tentemos exibir mais de "11" eventos de notícias, mantendo nosso dashboard organizado e gerenciável.

Em seguida, inicializamos uma variável inteira "startY" com o valor "162", que serve como a coordenada y inicial para posicionar os dados de notícias no painel. Depois, entramos em um loop que itera de "0" até "valuesTotal", processando efetivamente cada valor do calendário que pretendemos exibir. Dentro desse loop, definimos a cor para cada suporte de linha usando um padrão alternado com base no índice atual "i". Se "i" for par, definimos "holder_color" como um cinza claro representado por "C'213,227,207'"; se "i" for ímpar, definimos como branco. Após determinar a cor, chamamos a função "createRecLabel" para gerar um rótulo retangular para cada suporte de linha de dados, posicionado em "62" no eixo x, "startY - 1" no eixo y, com largura de "716", altura de "26" e cor de borda preta. Por fim, incrementamos "startY" em "25" para ajustar a coordenada y para a próxima linha de dados, garantindo que cada entrada seja exibida sequencialmente. Para fins de depuração, imprimimos o valor atual de "startY", permitindo rastrear o posicionamento vertical de cada linha de dados à medida que é criada. Aqui está o marco atual.

DATA HOLDERS

Você pode ter notado que usamos uma variável macro "DATA_HOLDERS" durante a criação dos subquadros de suporte. Aqui está como a definimos.

#define DATA_HOLDERS "DATA_HOLDERS"
#define ARRAY_NEWS "ARRAY_NEWS"

Também definimos a macro "ARRAY_NEWS" para facilitar a criação dos respectivos dados que devem ser mapeados dentro dos suportes de dados em relação aos cabeçalhos das colunas. Para preencher os dados, exigiremos que, para cada suporte selecionado, iteremos por todos os dados de um valor de evento específico e obtenhamos seus dados, que exibiremos. Assim, isso será feito dentro do primeiro loop e terá a seguinte lógica.

//--- 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++){

   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

   //--- 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;

   MqlCalendarValue value; //--- Declare calendar value structure
   CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values

   //--- 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;
}

Aqui, inicializamos uma variável inteira chamada "startX" com o valor 65, que servirá como a coordenada x inicial para posicionar cada entrada de dados relacionada aos eventos do calendário. Depois, entramos em um loop que itera por cada coluna em "array_calendar" usando o índice "k". Dentro desse loop, declaramos uma variável de estrutura "event" do tipo MqlCalendarEvent, que será usada para armazenar os detalhes de um evento de calendário específico. Recuperamos os detalhes do evento chamando a função CalendarEventById, passando o ID do evento a partir do array "values", e armazenando os resultados em "event".

Em seguida, declaramos outra variável de estrutura "country" do tipo MqlCalendarCountry para armazenar informações sobre o país associado ao evento do calendário. Usamos a função CalendarCountryById para preencher "country" com detalhes com base no ID do país do evento. Para fins de depuração, imprimimos detalhes-chave do evento, como o nome do evento, seu nível de importância (convertido em string usando a função EnumToString), o nome do país e a hora do evento armazenada em "values[i].time".

Em seguida, preparamos um array de strings chamado "news_data" com um tamanho igual ao de "array_calendar" para armazenar as informações relacionadas ao evento. O primeiro elemento de "news_data" é definido como a data do evento, formatada como string usando a função TimeToString com a flag "TIME_DATE". O segundo elemento captura a hora do evento, formatada usando a flag "TIME_MINUTES". O terceiro elemento armazena a moeda do país do evento.

Em seguida, determinamos a cor de importância para o evento com base em seu nível de impacto, inicializando uma variável "importance_color" como preta. Verificamos o valor de "event.importance" e, com base no seu valor (baixo, moderado ou alto), atribuímos a cor apropriada: amarelo para baixo, laranja para moderado e vermelho para alto.

Também definimos o quarto elemento de "news_data" como um símbolo que representa o nível de importância do evento, usando "ShortToString(0x25CF)" para criar um círculo preenchido. O quinto elemento é atribuído ao nome do evento recuperado de "event.name".

Para buscar os valores real, previsão e anterior do evento, declaramos outra variável de estrutura chamada "value" do tipo MqlCalendarValue e usamos a função CalendarValueById para preencher essa estrutura com base no ID do evento armazenado em "values[i].id". O sexto, sétimo e oitavo elementos de "news_data" são preenchidos com os valores real, previsão e anterior, respectivamente, formatados com três casas decimais usando a função DoubleToString.

Por fim, criamos um rótulo para cada item de dados de notícias usando a função "createLabel". Se "k" for igual a "3", aplicamos a cor de importância; caso contrário, padronizamos para a cor preta. A coordenada x para cada rótulo é determinada por "startX", que então incrementamos adicionando a largura do botão do array "buttons", garantindo que cada coluna de dados seja posicionada corretamente para exibição clara. Após a compilação, obtemos a seguinte saída.

PAINEL PREENCHIDO

Isso foi um sucesso. Agora criamos um dashboard do Calendário Econômico do MQL5 que exibe os dados de notícias no gráfico para referência mais fácil. Quanto ao marco atual, apenas obtemos todos os dados. Nas próximas partes da série, melhoraremos o painel integrando filtros, integrando atualizações de dados em tempo real e usando os dados de notícias para fins de negociação.


Conclusão

Em conclusão, estabelecemos com sucesso a base para o nosso MQL5 Economic Calendar construindo um painel de dashboard interativo que exibe eventos econômicos cruciais em um formato fácil de usar. Ao implementar recursos como recuperação de dados do calendário, categorização visual de eventos com base em sua importância e rotulagem intuitiva, podemos manter-nos informados sobre movimentos significativos do mercado. Essa configuração inicial não apenas aprimora a experiência do usuário, mas também fornece uma base sólida para melhorias adicionais que levarão nosso dashboard para o próximo nível.

Nas próximas partes desta série, integraremos funcionalidades adicionais, como filtros de notícias para nos ajudar a focar nas informações mais relevantes para nossas estratégias em MQL5. Também implementaremos atualizações em tempo real para garantir que nosso painel reflita os dados econômicos mais recentes à medida que estiverem disponíveis. Além disso, focaremos em tornar o painel responsivo, permitindo que ele se adapte perfeitamente a diferentes tamanhos de tela e interações do usuário. Em última análise, nosso objetivo é aproveitar esses dados para facilitar decisões de negociação informadas, transformando nosso calendário econômico em uma ferramenta poderosa para traders que buscam capitalizar a volatilidade do mercado. Fique atento.

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

Arquivos anexados |
Informação mútua como critério para seleção progressiva de características Informação mútua como critério para seleção progressiva de características
Neste artigo apresentamos a implementação da seleção progressiva de características em MQL5, baseada na informação mútua entre o conjunto ótimo de preditores e a variável alvo.
Desenvolvimento de ferramentas para análise do movimento de preços (Parte 2): Script de comentários analíticos Desenvolvimento de ferramentas para análise do movimento de preços (Parte 2): Script de comentários analíticos
Dando continuidade ao nosso trabalho para simplificar a interação com o comportamento do preço, temos o prazer de apresentar mais uma ferramenta que pode melhorar significativamente sua análise de mercado e ajudar na tomada de decisões bem fundamentadas. Esta ferramenta exibe indicadores técnicos importantes, como os preços do dia anterior, níveis significativos de suporte e resistência, além do volume de negociação, gerando automaticamente dicas visuais no gráfico.
Criando um Painel de Administração de Trading em MQL5 (Parte VI): Interface de Funções Múltiplas (I) Criando um Painel de Administração de Trading em MQL5 (Parte VI): Interface de Funções Múltiplas (I)
O papel do Administrador de Trading vai além das comunicações via Telegram; ele também pode realizar várias atividades de controle, incluindo gerenciamento de ordens, acompanhamento de posições e personalização da interface. Neste artigo, compartilharemos insights práticos sobre como expandir nosso programa para suportar múltiplas funcionalidades em MQL5. Esta atualização tem como objetivo superar a limitação atual do Painel de Administração de se concentrar principalmente na comunicação, permitindo que ele lide com uma gama mais ampla de tarefas.
Ciência de dados e aprendizado de máquina (Parte 32): Como manter a relevância de modelos de IA com treinamento on-line Ciência de dados e aprendizado de máquina (Parte 32): Como manter a relevância de modelos de IA com treinamento on-line
No mundo em constante transformação do trading, adaptar-se às mudanças do mercado é simplesmente uma necessidade. Todos os dias surgem novos padrões e tendências, o que torna difícil até mesmo para os modelos mais avançados de aprendizado de máquina manterem sua eficácia diante de condições em mutação. Neste artigo, vamos falar sobre como manter os modelos relevantes e capazes de reagir a novos dados de mercado por meio de reeducação automática.