English Русский 中文 Español Deutsch 日本語
preview
Criando um painel dinâmico multissímbolo e multiperíodo do Índice de Força Relativa (RSI) em MQL5

Criando um painel dinâmico multissímbolo e multiperíodo do Índice de Força Relativa (RSI) em MQL5

MetaTrader 5Sistemas de negociação | 11 março 2025, 15:30
164 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Introdução

Neste artigo, exploraremos em detalhes a criação de um painel dinâmico multissímbolo e multiperíodo do RSI (Relative Strength Index – Índice de Força Relativa) na MetaQuotes Language 5 (MQL5) para MetaTrader 5. Vamos examinar a funcionalidade e a aplicação prática desse painel personalizado de monitoramento do RSI, bem como os passos necessários para desenvolvê-lo em MetaQuotes Language 5 (MQL5).

O painel dinâmico do RSI é uma ferramenta poderosa para traders, pois fornece uma visão consolidada dos valores do RSI em múltiplos símbolos e períodos gráficos. Isso permite tomar decisões mais fundamentadas ao identificar condições de sobrecompra ou sobrevenda no mercado. Ao visualizar os dados do RSI em uma única interface, os traders podem avaliar rapidamente as condições do mercado e ajustar suas estratégias conforme necessário.

Vamos abordar as seguintes áreas principais:

  • Inicialização do painel: Configuração do ambiente, criação dos botões principais e exibição dos períodos gráficos e símbolos.
  • Atualizações em tempo real: Implementação da funcionalidade para calcular e exibir dinamicamente os valores do RSI com base nos dados reais do mercado.
  • Criação e atualização de botões: Explicação detalhada das funções utilizadas para criar e atualizar os botões, garantindo que o painel seja intuitivo e informativo.
  • Configuração e uso prático: Como personalizar o painel de acordo com as necessidades individuais de negociação e integrá-lo à sua estratégia de trading.

Ao final deste artigo, você terá um entendimento completo sobre como criar e utilizar um painel RSI multissímbolo e multiperíodo na linguagem MQL5. Isso ampliará seu conjunto de ferramentas de negociação, melhorará sua capacidade de análise das tendências do mercado e, consequentemente, ajudará a tomar decisões de trading mais bem fundamentadas. Os tópicos que abordaremos neste artigo incluem:

  1. Visão geral dos elementos
  2. Implementação em MQL5
  3. Considerações finais

Durante o desenvolvimento, utilizaremos ativamente a linguagem MetaQuotes Language 5 (MQL5) como nosso ambiente de desenvolvimento principal (IDE) no MetaEditor e executaremos os arquivos no terminal de negociação MetaTrader 5.  


Visão geral dos elementos

Vamos desenvolver um painel abrangente que consolidará os valores do RSI para vários símbolos de negociação e períodos gráficos, proporcionando uma análise eficiente do mercado. Nosso desenvolvimento se concentrará nos seguintes elementos principais:

  • Inicialização do painel:

Criação do botão principal: O primeiro passo no processo de inicialização será a criação do botão central, que servirá como a base e o ponto de referência do painel de controle. Este botão será o principal elemento de interação e funcionará como o centro da interface do usuário. Ele será posicionado na parte superior do painel para garantir fácil acesso e boa visibilidade.

Botões de períodos gráficos: Em seguida, criaremos botões para alternar entre os diferentes períodos gráficos. Eles serão dispostos horizontalmente ao lado do botão principal e serão responsáveis por exibir os valores do RSI de cada período correspondente. Cada botão de período gráfico será identificado com sua respectiva abreviação, permitindo que os traders identifiquem rapidamente e comparem os dados de diferentes períodos.

  • Botões de símbolos:

Lista dinâmica de símbolos: Para oferecer uma visão abrangente do mercado, criaremos botões para cada símbolo de negociação disponível na janela "Observação do Mercado" do MetaTrader 5 do usuário. Esses botões serão gerados dinamicamente e organizados verticalmente abaixo do botão principal. O símbolo de negociação atualmente ativo será destacado com uma cor diferenciada (como verde-claro ou verde), facilitando sua identificação. Essa funcionalidade permitirá que os traders identifiquem rapidamente o ativo em foco e acompanhem seus valores do RSI em tempo real.

Botão de base: Na parte inferior da lista de símbolos, adicionaremos um botão de base que terá a largura equivalente à soma de todos os botões de períodos gráficos. Esse botão terá um título que representará o painel de controle. Ele poderá ser utilizado para exibir o estado do sinal do símbolo atual ou funcionar como um resumo ou rodapé da lista de símbolos. Esse botão ajudará a organizar visualmente o painel, separando claramente os botões de símbolos da exibição dos valores do RSI, garantindo uma interface bem estruturada e de fácil navegação.

  • Atualizações em tempo real:

Cálculo do RSI: Para garantir que o painel forneça informações precisas e atualizadas, calcularemos os valores do RSI para cada símbolo e período gráfico em tempo real, utilizando as funções nativas do MQL5. A função calculará o RSI com base nos preços de fechamento do período selecionado, fornecendo um indicador essencial da dinâmica do mercado. Os valores do RSI serão armazenados em um array e atualizados a cada novo tick, refletindo as mudanças mais recentes do mercado.

Exibição dinâmica: Os valores calculados do RSI serão exibidos dinamicamente nos botões correspondentes. Para melhorar a clareza visual, implementaremos um esquema de cores baseado em valores predefinidos do RSI. Se o valor do RSI for inferior a 30, indicando uma condição de sobrevenda, o fundo do botão será alterado para verde. Se o valor do RSI for superior a 70, indicando uma condição de sobrecompra, o fundo do botão será alterado para vermelho. Para valores entre 30 e 70, o fundo permanecerá branco, indicando uma condição neutra. Essa exibição dinâmica permitirá que os traders avaliem rapidamente o estado do mercado e tomem decisões de negociação informadas.

Este é o resultado que queremos alcançar.

VISÃO GERAL

Para ilustrar todo o processo de desenvolvimento, dividiremos cada elemento em etapas detalhadas, fornecendo trechos de código e explicações. Ao final do artigo, você terá um painel RSI totalmente funcional, que poderá personalizar e integrar à sua estratégia de negociação.


Implementação em MQL5

O painel de indicadores será baseado em um EA (Expert Advisor). No terminal MetaTrader 5, selecione "Serviço" → "Editor MetaQuotes Language" ou simplesmente pressione F4. Alternativamente, você pode clicar no ícone do IDE (ambiente de desenvolvimento integrado) na barra de ferramentas. Isso abrirá o ambiente de desenvolvimento MQL5, que permite criar robôs de negociação, indicadores técnicos, scripts e bibliotecas de funções.

IDE

Na barra de ferramentas, selecione "Arquivo" → "Novo Arquivo" ou pressione CTRL + N para criar um novo documento. Você também pode clicar no ícone "Criar" na barra de ferramentas. Isso abrirá a janela do Assistente MQL.

Novo Expert Advisor

Na janela aberta do Assistente, selecione "Expert Advisor (modelo)" e clique em "Avançar".

Assistente MQL

Nas propriedades gerais, defina o nome do arquivo do seu EA. Para especificar ou criar uma pasta, caso ela ainda não exista, use uma barra invertida antes do nome do EA. Por padrão, a pasta "Experts" será utilizada. Isso significa que nosso EA será criado dentro dessa pasta. As demais seções são bastante simples, mas você pode seguir o link na parte inferior do Assistente para obter mais detalhes.

Novo Expert Advisor

Após definir o nome do arquivo do EA, clique em "Avançar" → "Avançar" → "Concluir". Agora estamos prontos para criar o painel.

Para começar, precisamos criar uma função para os botões que serão gerados. Isso será muito útil, pois nos permitirá reutilizar a mesma função para criar objetos semelhantes, em vez de repetir todo o processo. Isso também economizará muito tempo e espaço, tornando o processo rápido e simples, e garantindo que os trechos de código sejam curtos e eficientes.

Para criar os botões, desenvolveremos uma função que aceita 11 argumentos ou parâmetros.

//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {

...

}

A assinatura da função fala por si mesma. Trata-se da função lógica createButton. Ela retorna dois sinalizadores lógicos: true em caso de sucesso e false em caso de falha. Para facilitar a compreensão dos parâmetros, vamos descrevê-los e explicá-los individualmente abaixo.

  • objName (string) – nome do objeto botão. Cada botão deve ter um nome único para diferenciá-lo de outros objetos no gráfico. Esse nome será utilizado para referenciar o botão ao atualizar ou modificar seus atributos.
  • text (string) – texto exibido no botão. Pode ser "RSI", "BUY" ou qualquer outro texto que corresponda à finalidade do botão.
  • xD (int) – distância horizontal do botão em relação ao canto especificado do gráfico. A unidade de medida é pixels.
  • yD (int) – distância vertical do botão em relação ao canto especificado do gráfico.
  • xS (int) – largura do botão em pixels. Define o quão largo o botão será exibido no gráfico.
  • yS (int) – altura do botão em pixels. Define a altura do botão no gráfico.

DISTÂNCIA E TAMANHO

  • clrTxt (color) – cor do texto exibido no botão. A cor pode ser especificada utilizando constantes de cores predefinidas no MQL5, como clrBlack, clrWhite, clrRed, entre outras.
  • clrBg (color) – cor de fundo do botão. Também é definida por constantes de cores predefinidas e determina a cor de preenchimento do botão.
  • fontSize (int) – tamanho da fonte do texto do botão. O valor padrão é 12. Define o tamanho do texto exibido no botão.
  • clrBorder (color) – cor da borda do botão (opcional). O valor padrão é clrNONE (não aplicado). Se uma cor de borda for especificada, ela pode aprimorar a aparência do botão.
  • font (string) – fonte do texto do botão (opcional). O valor padrão é "Arial Rounded MT Bold". A fonte define o estilo do texto exibido no botão.

Talvez você tenha notado que alguns argumentos já estão inicializados com um valor específico na assinatura da função. O valor de inicialização representa um valor padrão, que será atribuído a esse parâmetro caso ele seja omitido durante a chamada da função. Por exemplo, se a cor da borda não for especificada ao chamar a função, então a cor não será aplicada à borda do botão. 

Os procedimentos de criação dos objetos são definidos dentro do corpo da função, delimitado por chaves ({}). 

   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }

Começamos utilizando a estrutura condicional if para verificar se o objeto já foi criado. Para isso, usamos a função ObjectCreate, que recebe seis argumentos e retorna um valor booleano. Essa função cria um objeto com um nome específico, define seu tipo e suas coordenadas iniciais dentro da subjanela especificada do gráfico. Primeiro, indicamos a janela do gráfico; o valor 0 significa que o objeto será criado na janela principal. Em seguida, especificamos o nome do objeto, que deve ser único para identificá-lo corretamente. O tipo do objeto que queremos criar é OBJ_BUTTON, o que indica que estamos gerando um botão para a interface do usuário no painel de indicadores personalizados. Depois, passamos a subjanela em que o objeto será criado; o valor 0 refere-se à subjanela atual. Por fim, definimos os valores de tempo e preço como 0, pois o botão não será vinculado ao gráfico de preços, mas sim a coordenadas da interface do gráfico. O posicionamento será feito em pixels.

Se a criação do objeto falhar, a função ObjectCreate retornará false. Nesse caso, não faz sentido prosseguir, então retornamos uma falha. Para registrar o erro, utilizamos o log do sistema, onde incluímos o código do erro e retornamos false. Se houver um erro anterior, precisamos garantir que ele não interfira no diagnóstico do problema atual. Para isso, usamos a função ResetLastError, que é uma função nativa do MQL5. Ela deve ser chamada antes da lógica de criação do objeto para redefinir o código de erro.

   ResetLastError(); // Reset the last error code

O objetivo é garantir que a função GetLastError, que retorna o código de erro da última operação, seja resetada para 0. Ao chamá-la antes da criação do objeto, garantimos que nenhum erro prévio influencie a análise de falhas. Esse passo é crucial para lidarmos com novos erros sem o risco de interpretarmos códigos antigos.

Se chegarmos a esse ponto, significa que o objeto foi criado com sucesso. A partir daqui, podemos prosseguir para a configuração de suas propriedades. Para isso, utilizamos a função ObjectSet..., que atribui um valor a um determinado atributo do objeto. O tipo do atributo pode ser datetime, integer, color, boolean ou symbol.

   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected

Agora, focamos na lógica da primeira propriedade.

   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance

Usamos a função ObjectSetInteger, passando os seguintes parâmetros. Os parâmetros são descritos a seguir.

  • Chart id – identificador do gráfico. O valor 0 indica que estamos editando um objeto no gráfico atual.
  • Name – nome do objeto. objName representa o identificador exclusivo atribuído ao botão ou rótulo retangular.
  • Property id – identificador da propriedade do objeto. Este valor corresponde a uma das constantes do enumerador ENUM_OBJECT_PROPERTY_INTEGER. No caso, utilizamos OBJPROP_XDISTANCE, que define a distância horizontal (X) do objeto em relação ao canto esquerdo do gráfico.
  • Property value – valor da propriedade. O valor atribuído a xD define a distância do canto superior esquerdo do objeto em relação à borda esquerda do gráfico. Se o valor for positivo, o objeto será posicionado mais à direita; se for negativo, será deslocado para a esquerda.

Os demais atributos seguem o mesmo formato. OBJPROP_YDISTANCE define a distância vertical (Y) do canto superior esquerdo do objeto em relação à borda superior do gráfico. Em outras palavras, esse parâmetro controla o posicionamento vertical do objeto dentro da área do gráfico. O deslocamento vertical é especificado a partir do canto selecionado. Já OBJPROP_XSIZE e OBJPROP_YSIZE determinam, respectivamente, a largura e a altura do objeto retangular. 

Para posicionar corretamente o objeto, utilizamos OBJPROP_CORNER, que define em qual canto do gráfico o objeto será ancorado.

   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position

Existem quatro opções possíveis:

  • CORNER_LEFT_UPPER – canto superior esquerdo do gráfico.
  • CORNER_LEFT_LOWER – canto inferior esquerdo do gráfico.
  • CORNER_RIGHT_LOWER – canto inferior direito do gráfico.
  • CORNER_RIGHT_UPPER – canto superior direito do gráfico.

Visualmente, isso pode ser representado como:

CANTOS DO OBJETO

Os demais atributos são diretos e incluem comentários explicativos para facilitar o entendimento. Após configurar os atributos, atualizamos o gráfico para que as mudanças entrem em vigor automaticamente, sem precisar aguardar variações de preços ou eventos gráficos. 

   ChartRedraw(0); // Redraw the chart to reflect the new button

Por fim, a função retorna true, indicando que o objeto foi criado e suas propriedades foram configuradas com sucesso.

   return (true); // Return true if creation is successful

Abaixo está o código completo da função responsável por criar o botão na interface do gráfico.

//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {
   ResetLastError(); // Reset the last error code
   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }
   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
   
   ChartRedraw(0); // Redraw the chart to reflect the new button
   return (true); // Return true if creation is successful
}

Agora que temos uma função para criar botões, utilizaremos essa função para construir o painel de indicadores. Precisamos definir nomes de objetos, então utilizaremos macros para facilitar a manipulação dos nomes.

// Define button identifiers and properties
#define BTN1 "BTN1"

Usamos a diretiva #define para definir o macro BTN1, atribuindo-lhe o valor "BTN1", o que nos permite reutilizar o nome do botão principal sem precisar digitá-lo repetidamente em cada criação de botão. Isso economiza tempo e reduz a chance de erros ao referenciar os nomes. Essencialmente, macros substituem textos durante a compilação.

Da mesma forma, definimos outros macros que utilizaremos. 

#define Desc "Desc "
#define Symb "Symb "
#define Base "Base "
#define RSI "RSI "
#define XS1 90
#define XS2 100
#define YS1 25
#define clrW clrWhite
#define clrB clrBlack
#define clrW_Gray C'230,230,230'

Aqui, utilizamos o macro Desc como prefixo para os nomes dos botões que representam os diferentes períodos gráficos, permitindo-nos gerar nomes exclusivos para esses botões descritivos, adicionando índices e sufixos, como Desc0, Desc1, etc. Da mesma forma, o macro Symb é usado como prefixo para os botões que representam os diferentes símbolos de negociação, ajudando a criar identificadores únicos como Symb0, Symb1 e assim por diante. O macro Base serve como prefixo para o nome do botão base, garantindo uma convenção de nomenclatura clara e padronizada para os componentes do painel. Para os botões que exibem valores do RSI, usamos o macro RSI, permitindo a criação de identificadores únicos para cada botão associado a diferentes símbolos e períodos gráficos.

No que diz respeito aos tamanhos, XS1 define a largura de determinados botões, enquanto XS2 e YS1 especificam a largura e a altura de outros botões, respectivamente, garantindo um tamanho padronizado para os elementos da interface gráfica. Definimos os macros de cores clrW e clrB para referenciar facilmente as cores branca e preta dentro do código. O MQL5 possui um formato predefinido para variáveis do tipo color. Isso nos permite utilizar clrWhite para branco e outras cores padronizadas em web design; Web-cores.

Formato color, seção 1:

COLOR, SEÇÃO 1

Formato color, seção 2:

COLOR, SEÇÃO 2

Além disso, definimos um tom de cinza personalizado como clrW_Gray C'230,230,230', que será usado como cor de fundo ou borda para manter uma identidade visual consistente em toda a interface do painel. Para um controle mais preciso das cores, apresentamos o último macro em formato literal. Ele segue a sintaxe "C'000,000,000'", onde os três números variam de 0 a 255. Esse formato corresponde ao modelo RGB (Red, Green, Blue). Cada número representa a intensidade das cores vermelha, verde e azul, respectivamente. Assim, o valor 230,230,230 representa um tom de cinza próximo ao branco.

Precisamos definir os períodos gráficos (timeframes) específicos que utilizaremos no painel. Para isso, devemos armazená-los de maneira acessível. A forma mais simples de fazer isso é utilizando arrays, permitindo fácil recuperação dos valores. 

// Define the timeframes to be used
ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};

Definimos um array estático do tipo ENUM_TIMEFRAMES para especificar os períodos gráficos que serão usados na interface do painel. Nomeamos esse array como periods e incluímos os seguintes períodos específicos: PERIOD_M1 (1 minuto), PERIOD_M5 (5 minutos), PERIOD_H1 (1 hora), PERIOD_H4 (4 horas) e PERIOD_D1 (1 dia). Ao listar esses períodos dentro do array periods, garantimos que a interface do painel exibirá os valores do RSI para cada um desses intervalos de tempo, proporcionando uma visão detalhada das tendências do mercado em múltiplos períodos. Essa configuração nos permitirá percorrer o array de timeframes e aplicar cálculos e criação de botões de forma uniforme para cada período especificado. A variável usada para definir esse array é um tipo enumerado que contém todos os períodos gráficos disponíveis no MetaTrader 5. Qualquer um desses períodos pode ser utilizado, desde que esteja explicitamente declarado. Aqui está uma lista de todos os períodos disponíveis que podem ser utilizados.

PERÍODOS GRÁFICOS

Por fim, precisaremos criar uma variável global para armazenar o identificador do indicador, bem como um array onde armazenaremos os valores do RSI para os diferentes períodos gráficos e símbolos utilizados. Isso é feito com o seguinte trecho de código.

// Global variables
int handleName_Id; // Variable to store the handle ID for the RSI indicator
double rsi_Data_Val[]; // Array to store the RSI values

Declaramos uma variável inteira chamada handleName_Id, que armazenará o identificador do indicador RSI. Quando criamos um indicador RSI para um determinado símbolo e período gráfico, ele retorna um identificador único. Esse identificador será armazenado no handle do indicador, permitindo que façamos referência a esse indicador específico nas operações subsequentes, como a extração de seus valores para análise posterior. Aqui entra o segundo array de variáveis. Definimos um array dinâmico do tipo double, chamado rsi_Data_Val, que armazenará os valores do RSI obtidos pelo indicador. Quando extraímos os dados do RSI, os valores serão copiados para esse array. Ao tornar esse array uma variável global, garantimos que os dados do RSI estarão acessíveis em toda a execução do programa, permitindo que sejam utilizados para atualizações em tempo real e exibidos nos botões do painel.

Inicialmente, o painel será criado na seção de inicialização do EA. Vamos analisar como funciona o evento de inicialização.

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

...
   
   return(INIT_SUCCEEDED); // Return initialization success
}

A função OnInit é um manipulador de eventos chamado quando o EA é inicializado, sendo responsável por executar todas as configurações necessárias para seu funcionamento correto. Isso inclui a criação de elementos da interface do usuário, inicialização de variáveis e definição das condições essenciais para a operação do programa. No nosso caso, utilizaremos essa função para inicializar os elementos do painel.

Chamamos a função de criação do botão passando o nome e os parâmetros necessários.

   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);

Aqui, o nome do botão é BTN1, conforme definido pelo macro. O segundo parâmetro especifica o texto que será exibido ao lado dos botões. A distância horizontal (eixo X) a partir do canto superior direito da janela do gráfico é de 600 pixels, enquanto a distância vertical (eixo Y) em relação à escala de preços é de 50 pixels. A largura do botão é definida pelo macro XS1, correspondente a 90 pixels, enquanto a altura é definida pelo macro YSI, equivalente a 25 pixels. Escolhemos a cor do texto clrW, ou seja, branco, com fundo cinza, tamanho de fonte 15 e uma borda cinza para o botão. Para obter uma estimativa do intervalo de pixels, podemos reduzir o zoom do gráfico ao mínimo (0), e a quantidade de barras entre duas coordenadas cruzadas será equivalente ao número de pixels na escala horizontal. Abaixo, mostramos um exemplo dessa referência visual.

PIXELS DE REFERÊNCIA

Outro parâmetro foi omitido, o que significa que o valor padrão será aplicado automaticamente. Nesse caso, o nome da fonte será Arial Rounded MT Bold. Este é o resultado que obtemos após a compilação.

BOTÃO PAIR

Mesmo que todos os parâmetros tivessem valores padrão, como mostrado abaixo, os resultados permaneceriam os mesmos.

   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray, "Arial Rounded MT Bold");

Agora queremos criar botões para cada período gráfico predefinido, a fim de exibir as informações correspondentes do RSI. Poderíamos fazer isso de forma estática, chamando a função de criação de botões para cada elemento manualmente, mas isso tornaria nosso código muito extenso. Para evitar isso, usaremos um formato dinâmico que nos permitirá criar os botões dentro de iterações controladas.

   // Loop to create buttons for each timeframe with the corresponding RSI label
   for(int i = 0; i < ArraySize(periods); i++) {
      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray);
   }

Iniciamos um laço for, que percorre o array periods, contendo os períodos gráficos que definimos e preenchemos anteriormente. Utilizamos a função ArraySize para garantir que o laço cubra todos os elementos do array. Essa função é simples, recebe apenas um argumento e retorna a quantidade de elementos no array especificado, que, no nosso caso, é periods. Dentro do laço, chamamos a função createButton para criar um botão correspondente a cada período gráfico. O nome do botão é formado pela concatenação do macro Desc com o índice i, convertido para string usando a função IntegerToString, garantindo que cada botão tenha um nome único, como Desc0, Desc1 e assim por diante. Essa função converte um valor inteiro em uma string de comprimento especificado e retorna a string resultante. Ela aceita três parâmetros de entrada, mas os dois últimos são opcionais. O primeiro parâmetro é o número a ser convertido, que, no nosso caso, é o índice i. Aplicamos então a função personalizada truncPrds para gerar o rótulo do botão. Essa função reduz a representação em string do período gráfico para um formato mais compacto e intuitivo (por exemplo, M1, M5), adicionando a indicação RSI 14 para especificar que esse botão exibirá o valor do RSI com período 14. O trecho de código correspondente à função tem a seguinte estrutura:

// Function to truncate the ENUM_TIMEFRAMES string for display purposes
string truncPrds(ENUM_TIMEFRAMES period) {
   // Extract the timeframe abbreviation from the full ENUM string
   string prd = StringSubstr(EnumToString(period), 7);
   return prd; // Return the truncated string
}

A função recebe um período do tipo ENUM_TIMEFRAMES como parâmetro e converte seu valor ENUM em uma string utilizando a função EnumToString. Normalmente, essa conversão gera uma string com um prefixo desnecessário para exibição. Para removê-lo, usamos a função StringSubstr, extraindo a substring a partir do sétimo caractere. Isso reduz a string para um formato mais curto e legível, adequado para a interface do usuário. Por fim, a função retorna essa versão truncada da string, proporcionando um rótulo claro e conciso para os botões da nossa interface. Para compreender a necessidade dessa função, vejamos a ilustração abaixo.

Lógica aplicada:

      Print("BEFORE: ",EnumToString(periods[i]));
      Print("AFTER: ",truncPrds(periods[i]));

Expressões print.

PERÍODOS TRUNCADOS

Agora fica evidente que os períodos originais são mais longos do que os truncados e contêm sete caracteres desnecessários (PERIOD_), incluindo o caractere de sublinhado, que removemos no processo.

A coordenada X de cada botão é calculada dinamicamente, começando com um valor inicial de 600 - XS1 e ajustando-o ao subtrair XS2 (a largura do botão) multiplicado pelo índice i. Esse posicionamento garante que cada botão fique à esquerda do anterior, criando um alinhamento horizontal adequado. A coordenada Y é fixada em 50 pixels a partir da parte superior do gráfico, assegurando uma posição vertical uniforme para todos os botões de período gráfico. O tamanho do botão é definido utilizando os valores dos macros XS2 para a largura (100 pixels) e YS1 para a altura (25 pixels). Além disso, configuramos a cor do texto dos botões para clrWhite (branco), o fundo para um tom de cinza, o tamanho da fonte para 13 e a borda para cinza. Após a compilação, obtemos o seguinte resultado:

PERÍODOS CINZAS

Naturalmente, você pode modificar as cores. Por exemplo, para um fundo azul brilhante e uma borda preta, basta alterar os valores correspondentes no código.

      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrDodgerBlue, 13, clrBlack);

Aqui, alteramos a cor de fundo para azul vibrante e a borda para preta. O resultado da compilação é o seguinte:

PERÍODOS COM CORES MODIFICADAS

Os botões ficaram mais visualmente atrativos. No entanto, para manter a uniformidade do artigo, utilizaremos cores mais neutras por padrão. Posteriormente, aplicaremos cores mais vibrantes ao definir e exibir sinais apropriados.

Agora, precisamos criar outra série dinâmica de botões verticais para os símbolos de negociação. Não há necessidade de definir símbolos manualmente em um array para visualizá-los. Podemos acessá-los automaticamente a partir dos instrumentos oferecidos pela corretora. Para isso, utilizamos um laço for para percorrer todos os símbolos disponíveis, filtrando aqueles que desejamos exibir conforme necessário.

   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {

   ...

   }

Para obter os símbolos disponibilizados pela corretora, utilizamos a função nativa do MQL5 SymbolsTotal. Essa função retorna a quantidade de símbolos disponíveis, que podem ser aqueles selecionados na "Observação do Mercado" ou todos os símbolos disponíveis no terminal. Ela requer apenas um parâmetro booleano de entrada: se for true, a função retorna o número de símbolos selecionados na "Observação do Mercado"; se for false, retorna o número total de símbolos disponíveis. Para ilustrar, imprimimos os valores retornados quando o parâmetro de entrada da função é false.

   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(false); i++) {
      Print("Index ",i,": Symbol = ",SymbolName(i,false));

      ...

   }

No laço for, definimos a flag do valor selecionado como false, permitindo o acesso a toda a lista de símbolos. Na instrução print, utilizamos a função SymbolName para obter o nome do símbolo de acordo com sua posição na lista. O segundo parâmetro dessa função determina o modo de consulta com base nos critérios de seleção da "Observação do Mercado". Se for true, o símbolo será obtido da lista de símbolos selecionados na "Observação do Mercado". Se for false, o símbolo será extraído da lista completa de ativos. Este é o resultado que obtemos após a compilação. 

TODOS OS SÍMBOLOS 1

Continuação.

TODOS OS SÍMBOLOS 2

Como podemos ver, todos os símbolos foram listados. Neste caso, há um total de 396 símbolos disponíveis. Agora imagine tentar exibir todos esses símbolos em um único gráfico. Seria um número excessivo, certo? Eles não caberiam na tela, e o tamanho da fonte ficaria tão pequeno que os nomes dos símbolos seriam praticamente ilegíveis. O gráfico ficaria sobrecarregado de informações. Além disso, provavelmente você não precisa de todos esses símbolos. Nesta etapa, faz sentido selecionar apenas os ativos mais relevantes e excluir os demais. O processo de seleção das moedas ou ativos prioritários será feito diretamente na "Observação do Mercado". Aqui, você pode adicionar apenas os símbolos que deseja monitorar, facilitando a visualização das cotações e acompanhando suas variações. É a partir dessa lista reduzida que extrairemos os símbolos que serão exibidos no painel. Para isso, configuramos o valor das funções para true.

   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {
      Print("Index ",i,": Symbol = ",SymbolName(i,true));

      ...

   }

O resultado da compilação é o seguinte:

SÍMBOLOS DA OBSERVAÇÃO DO MERCADO

Note que os símbolos presentes na "Observação do Mercado" (12 no total) são os mesmos exibidos no painel "Ferramentas", listados na mesma ordem cronológica. Para facilitar a diferenciação e referência, destacamos esses símbolos nas cores vermelha e preta. Agora, para criar dinamicamente os botões verticais que representarão os símbolos, utilizamos a seguinte lógica.

         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);

Aqui, o nome do botão é formado pela concatenação do macro Symb com o índice i, convertido em string por meio da função IntegerToString, garantindo que cada botão tenha um identificador único, como Symb0, Symb1 e assim por diante. Definimos o rótulo do botão com o nome do símbolo de negociação correspondente ao índice i, obtido pela função SymbolName, que recupera o nome do ativo. O parâmetro true garante que o nome seja retirado da lista da "Observação do Mercado". Definimos a coordenada X do botão como 600 pixels, alinhando todos os botões de símbolos verticalmente. A coordenada Y é calculada dinamicamente como (50 + YS1) + i * YS1, deslocando cada botão para baixo ao adicionar a altura do botão (YS1, que equivale a 25 pixels) multiplicada pelo índice i ao deslocamento inicial de 50 pixels mais YS1. O tamanho do botão é determinado pelos valores XS1 (90 pixels de largura) e YS1 (25 pixels de altura). Ajustamos a cor do texto para clrW (branco), o fundo para cinza, o tamanho da fonte para 11 e a cor da borda para cinza. Após a compilação, o resultado é o seguinte:

COLUNA DE SÍMBOLOS

Esta é a lista de todos os símbolos presentes na "Observação do Mercado". Se você adicionar ou remover símbolos, os botões serão ajustados automaticamente, conforme a lógica dinâmica da criação. Vamos remover as três últimas moedas e verificar se a atualização ocorre como esperado.

MENOS SÍMBOLOS

Como podemos ver, a atualização ocorre automaticamente. Agora, adicionamos novamente os símbolos removidos e seguimos para desenvolver a lógica que permitirá identificar e destacar o símbolo atualmente selecionado, diferenciando-o dos outros botões de símbolos.

      if (SymbolName(i, true) == _Symbol) {
         createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW);
      } else {
         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
      }

Em vez de criar botões visualmente idênticos, aplicamos um estilo diferenciado para destacar o botão do símbolo ativo. Começamos verificando se o nome do símbolo no índice i corresponde ao símbolo atualmente ativo, extraído da variável predefinida _Symbol. Se houver correspondência, criamos um botão com um estilo especial para destacar o ativo selecionado. Para o símbolo ativo, configuramos a cor do texto como clrB (preto), o fundo como verde-claro, o tamanho da fonte como 11 e a borda como clrW (branca). Essa diferenciação visual permite uma identificação rápida do ativo principal. Caso contrário, a função padrão assume a criação do botão com o esquema visual padrão, garantindo que os símbolos inativos sejam exibidos de forma consistente e visualmente distinta do ativo. Este é o resultado que obtemos após a compilação.

SÍMBOLO SELECIONADO

Depois de criarmos todos os botões de símbolos necessários, adicionamos um rodapé na parte inferior da matriz visual, marcando o fim da seção de símbolos e incorporando algumas informações resumidas. Utilizamos o seguinte trecho de código.

      // Create the base button for the RSI Dashboard at the end
      if (i == SymbolsTotal(true) - 1) {
         createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray);
      }

Para criar o botão base do painel RSI e garantir que ele esteja posicionado no final da lista de símbolos, primeiro verificamos se o índice atual i é igual ao número total de símbolos menos um, indicando que estamos no último símbolo da lista. Se essa condição for atendida, passamos para a criação do botão base. Utilizamos a função createButton para definir a aparência e a posição do botão base. Para gerar o nome do botão, concatenamos a string Base com o índice i, criando um identificador único, e definimos seu rótulo como "RSI Dashboard". Esse é um valor arbitrário. Você pode alterá-lo conforme necessário. Posicionamos o botão na coordenada X 600 pixels e na coordenada Y, calculada como (50 + YS1) + (i * YS1) + YS1, garantindo que ele apareça logo abaixo do último botão de símbolo. Determinamos a largura do botão como (XS1 + XS2) * ArraySize (periods), cobrindo a largura de todas as colunas de botões de períodos gráficos, e definimos a altura como YS1 (25 pixels). O esquema de cores do botão segue o padrão visual do painel. Normalmente, esse botão base atua como uma referência clara para toda a interface do RSI, criando um ponto visual de ancoragem na parte inferior da lista de símbolos. Aqui estão os principais resultados.

BOTÃO BASE

Agora, precisamos criar os botões que exibirão os valores do RSI para cada combinação de símbolo e período gráfico. Utilizamos o seguinte trecho de código.

      // Loop to create buttons for RSI values for each symbol and timeframe
      for (int j = 0; j < ArraySize(periods); j++) {
         createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW);
      }
   }

Iniciamos um laço com um contador inteiro j, que representa o índice do período gráfico. Para cada período, chamamos a função createButton para configurar um botão que exibirá o valor do RSI correspondente ao símbolo e ao período gráfico atual. Geramos um nome único para cada botão concatenando a string RSI com o índice j e o nome do símbolo, garantindo um identificador exclusivo. O rótulo do botão é inicialmente definido como "-/-", que posteriormente será atualizado com o valor real do RSI. Neste momento, apenas precisamos garantir que conseguimos criar uma grade organizada de símbolos e períodos gráficos. O botão é posicionado na coordenada X (600 - XS1) + (j * -XS2), garantindo que as colunas de botões fiquem alinhadas horizontalmente, considerando a largura XS2 (100 pixels). Da mesma forma, a coordenada Y é definida como (50 + YS1) + (i * YS1), permitindo que os botões sejam organizados verticalmente conforme o índice do símbolo. Os tamanhos dos botões são definidos como XS2 - 1 para a largura e YS1 - 1 para a altura. A subtração de 1 assegura que haja um espaçamento de 1 pixel entre os botões, criando uma aparência visual semelhante a uma grade organizada. Em seguida, aplicamos o esquema de cores para os botões, definindo a cor do texto como clrB (preto), o fundo como clrW (branco), o tamanho da fonte como 12 e a borda como clrW (branca). Essa configuração organiza os botões do RSI em uma estrutura de grade, onde cada botão está associado a um símbolo e um período gráfico específico, garantindo uma apresentação clara e bem estruturada dos valores do RSI para diferentes períodos. O resultado da compilação é o seguinte:

INICIALIZAÇÃO DA GRADE DE BOTÕES

Agora que conseguimos criar a estrutura geral da interface do painel, falta apenas obter os valores do indicador e atualizar os botões. Antes de chegarmos a essa etapa, podemos visualizar os objetos criados clicando com o botão direito em qualquer lugar do gráfico e selecionando a opção "Lista de Objetos" no menu suspenso. Alternativamente, podemos pressionar Ctrl + B. Na janela que aparece, clicamos em "Todos", e será exibida a lista completa dos elementos que criamos.

LISTA DE OBJETOS

Agora temos certeza de que os objetos foram criados na interface em ordem cronológica e possuem nomes únicos. Isso garante que nenhum elemento seja omitido. Por exemplo, podemos ver que o primeiro símbolo foi nomeado como Symb0, distinguindo-o dos demais. Se não tivéssemos concatenado os nomes dos símbolos com os respectivos objetos, todas as teclas teriam o mesmo nome de símbolo, o que resultaria em um erro, pois após a criação do nome ele fica fixo no objeto, e nenhuma outra tecla pode receber esse mesmo nome. Assim, na prática, apenas um botão seria criado, enquanto os demais seriam ignorados. Estratégia inteligente! Agora, seguimos para a criação dos valores de inicialização.

   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      for (int j = 0; j < ArraySize(periods); j++) {
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array

         ...

   }

Utilizamos dois laços para percorrer cada símbolo de negociação e cada período gráfico, inicializando os valores do RSI e atualizando os botões correspondentes. O primeiro laço externo, indexado por i, seleciona um símbolo da "Observação do Mercado", e dentro dele há um segundo for que percorre todos os períodos gráficos definidos para cada símbolo selecionado. Isso significa que, por exemplo, ao selecionar AUDUSD, iteramos pelos períodos M1, M5, H1, H4 e D1. O seguinte trecho de código ilustra essa lógica.

   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      Print("SELECTED SYMBOL = ",SymbolName(i, true));
      for (int j = 0; j < ArraySize(periods); j++) {
         Print("PERIOD = ",EnumToString(periods[j]));

         ...

   }

Aqui está o que temos:

LISTA DE SÍMBOLOS

Para cada combinação de símbolo e período gráfico, obtemos o identificador do indicador RSI utilizando a função iRSI, que recebe os parâmetros: símbolo, período gráfico, período do RSI (14) e tipo de preço de fechamento. O handle handleName_Id é simplesmente um número inteiro armazenado na memória do computador, permitindo que interajamos com os dados do indicador RSI. Por padrão, esse número inteiro começa a partir do índice 10. Cada novo indicador criado incrementa esse valor em uma unidade, gerando identificadores como 10, 11, 12, 13 e assim por diante. Para melhor compreensão, imprimimos esses valores no console.

         Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);

O resultado da compilação é o seguinte:

HANDLES 1

Como podemos ver, o identificador do handle começa a partir de 10, e como temos 12 símbolos e 5 períodos para cada símbolo, esperamos um total de (12 * 5 = 60) handles de indicador. No entanto, como a indexação começa a partir de 10, precisamos incluir os primeiros 9 valores no cálculo para obter a contagem final dos handles. Matematicamente, isso resulta em 60 + 9, totalizando (60 + 9 = 69) handles. Vamos verificar se essa lógica está correta por meio de uma representação visual.

HANDLES 2

Tudo está correto. Agora configuramos o array rsi_Data_Val para ser utilizado como uma série temporal, chamando a função ArraySetAsSeries e definindo o flag como true para confirmar a ação. Essa configuração garante que os valores mais recentes do RSI estejam no início do array. Em seguida, copiamos os valores do RSI para o array utilizando a função CopyBuffer, onde 0 indica o índice do buffer, 0 define a posição inicial, 1 determina a quantidade de pontos de dados a serem extraídos, e rsi_Data_Val é o array de destino. Agora, imprimimos os dados no log e verificamos o resultado. Para isso, utilizamos a função ArrayPrint, que nos possibilita visualizar um array dinâmico.

         ArrayPrint(rsi_Data_Val);

Aqui está o que obtemos:

ARRAY DE DADOS

Perfeito! Os dados foram extraídos corretamente. Podemos compará-los com o indicador no gráfico e na janela de dados para garantir a correspondência. Agora podemos prosseguir. Para cada nova lógica que adicionamos ao painel, convém compilar e executar testes para verificar se tudo funciona como esperado. Neste ponto, precisamos apenas utilizar os valores do indicador para atualizar o painel, e tudo estará pronto.

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), clrBlack, clrW_Gray, clrWhite);

Aqui, empregamos a função personalizada update_Button para atualizar os botões com os dados do indicador. A lógica da função é a seguinte.

//+------------------------------------------------------------------+
//| Function to update a button                                      |
//+------------------------------------------------------------------+
bool update_Button(string objName, string text, color clrTxt = clrBlack,
                  color clrBG = clrWhite, color clrBorder = clrWhite
) {
   int found = ObjectFind(0, objName); // Find the button by name
   // Check if the button exists
   if (found < 0) {
      ResetLastError(); // Reset the last error code
      Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found
      return (false); // Return false if button is not found
   } else {
      // Update button properties
      ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
      ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color
      ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
      
      ChartRedraw(0); // Redraw the chart to reflect the updated button
      return (true); // Return true if update is successful
   }
}

Essa função é praticamente idêntica à que utilizamos para criar os botões. A diferença é que, em vez de criar um novo botão, localizamos um botão existente com a função ObjectFind. Se a função for bem-sucedida, ela retorna o número da subjanela (0 significa a janela principal do gráfico) em que o objeto foi encontrado. Se o objeto não for encontrado, ela retorna um valor negativo. Portanto, se o valor retornado for menor que 0, significa que não encontramos o objeto; nesse caso, registramos o erro e imprimimos o código no log, chamamos ResetLastError e retornamos false. Caso o teste seja bem-sucedido, localizamos o objeto e atualizamos suas propriedades, como o texto exibido, as cores e a borda. O resultado da compilação é o seguinte:

DADOS PREENCHIDOS

Ótimo! Agora temos os dados do indicador corretamente distribuídos na grade. Observe como eles são exibidos adequadamente com apenas uma linha de código. Além disso, podemos confirmar que o valor 55,27, por exemplo, para o período H1 do símbolo atual, foi preenchido corretamente. Para melhorar a funcionalidade, vamos definir as condições de entrada no mercado com base nos níveis de sobrecompra e sobrevenda do RSI e modificar as cores dos botões para facilitar a interpretação visual. Em vez de utilizar cores fixas, aplicaremos um esquema de cores dinâmico.

         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);

Primeiro, declaramos três variáveis para armazenar as cores dos botões: text_clr para a cor do texto; bg_clr para a cor de fundo; border_clr para a cor da borda. A seguir, definimos as cores apropriadas com base nos valores do RSI armazenados no array de dados. Se o valor do RSI estiver abaixo de 30, indicando uma condição de sobrevenda, definimos o texto como branco, o fundo como verde e a borda também como verde. Isso destaca a célula do botão em verde, sinalizando uma oportunidade de compra.

Da mesma forma, se o valor do RSI estiver acima de 70, indicando uma condição de sobrecompra, configuramos o texto como branco, o fundo como vermelho e a borda como vermelha, destacando a célula do botão em vermelho para indicar uma oportunidade de venda. Se o RSI estiver entre 30 e 70, aplicamos a paleta de cores padrão, indicando um estado neutro do mercado. Finalmente, chamamos a função update_Button, passando os parâmetros necessários para atualizar a aparência de cada botão. Dessa forma, cada botão do painel reflete com precisão a condição atual do RSI e transmite visualmente o estado do mercado. Os resultados são apresentados a seguir:

ONINIT FINALIZADO

Excelente! Criamos um painel dinâmico e adaptável que exibe as condições atuais do mercado diretamente no gráfico, proporcionando uma visualização clara das oportunidades de negociação. 

O código completo responsável pela inicialização do painel do indicador está estruturado da seguinte forma:

//+------------------------------------------------------------------+
//|                                       ADVANCED IND DASHBOARD.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

// Define button identifiers and properties
#define BTN1 "BTN1"
#define Desc "Desc "
#define Symb "Symb "
#define Base "Base "
#define RSI "RSI "
#define XS1 90
#define XS2 100
#define YS1 25
#define clrW clrWhite
#define clrB clrBlack
#define clrW_Gray C'230,230,230'

// Define the timeframes to be used
ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};

// Function to truncate the ENUM_TIMEFRAMES string for display purposes
string truncPrds(ENUM_TIMEFRAMES period) {
   // Extract the timeframe abbreviation from the full ENUM string
   string prd = StringSubstr(EnumToString(period), 7);
   return prd; // Return the truncated string
}

// Global variables
int handleName_Id; // Variable to store the handle ID for the RSI indicator
double rsi_Data_Val[]; // Array to store the RSI values

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);

   // Loop to create buttons for each timeframe with the corresponding RSI label
   for(int i = 0; i < ArraySize(periods); i++) {
      //Print("BEFORE: ",EnumToString(periods[i]));
      //Print("AFTER: ",truncPrds(periods[i]));
      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray);
   }
   
   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("Index ",i,": Symbol = ",SymbolName(i,true));
      // Check if the symbol is the current symbol being traded
      if (SymbolName(i, true) == _Symbol) {
         createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW);
      } else {
         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
      }

      // Create the base button for the RSI Dashboard at the end
      if (i == SymbolsTotal(true) - 1) {
         createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray);
      }
      
      // Loop to create buttons for RSI values for each symbol and timeframe
      for (int j = 0; j < ArraySize(periods); j++) {
         createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW);
      }
   }
   
   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("SELECTED SYMBOL = ",SymbolName(i, true));
      for (int j = 0; j < ArraySize(periods); j++) {
         //Print("PERIOD = ",EnumToString(periods[j]));
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         //ArrayPrint(rsi_Data_Val);
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
   
   return(INIT_SUCCEEDED); // Return initialization success
}

//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {
   ResetLastError(); // Reset the last error code
   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }
   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
   
   ChartRedraw(0); // Redraw the chart to reflect the new button
   return (true); // Return true if creation is successful
}

//+------------------------------------------------------------------+
//| Function to update a button                                      |
//+------------------------------------------------------------------+
bool update_Button(string objName, string text, color clrTxt = clrBlack,
                  color clrBG = clrWhite, color clrBorder = clrWhite
) {
   int found = ObjectFind(0, objName); // Find the button by name
   // Check if the button exists
   if (found < 0) {
      ResetLastError(); // Reset the last error code
      Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found
      return (false); // Return false if button is not found
   } else {
      // Update button properties
      ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
      ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color
      ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
      
      ChartRedraw(0); // Redraw the chart to reflect the updated button
      return (true); // Return true if update is successful
   }
}

Mesmo que tenhamos construído um painel com todos os elementos, precisamos garantir que ele se atualize continuamente para refletir os dados mais recentes. Isso é realizado por meio do manipulador de eventos OnTick.

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

...

}

A função OnTick é um manipulador de eventos void que é chamado sempre que ocorrem mudanças nas cotações de preço. Para garantir que os valores do indicador sejam sempre atualizados corretamente, precisamos executar um trecho do código de inicialização dentro dessa função para obter os dados mais recentes. 

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   // Loop to update RSI values and buttons on each tick
   for(int i = 0; i < SymbolsTotal(true); i++) {
      for (int j = 0; j < ArraySize(periods); j++) {
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
}

Aqui, simplesmente copiamos e colamos o trecho de código da seção de inicialização, que contém dois laços for — um externo e um interno — e atualizamos os valores do indicador. Isso garante que, a cada novo tick, os valores sejam atualizados com os dados mais recentes. Dessa forma, o painel de controle se torna altamente dinâmico, interativo e visualmente intuitivo. Os resultados podem ser observados no GIF abaixo:

ATUALIZAÇÕES INSTANTÂNEAS GIF

Por fim, precisamos garantir que todos os objetos criados pelo painel sejam removidos assim que o EA for retirado do gráfico. Isso garante que a interface seja completamente limpa e que o gráfico permaneça organizado. Esse procedimento é essencial para manter um ambiente de negociação eficiente e sem interferências visuais. A remoção dos objetos é feita por meio da função OnDeinit, um manipulador de eventos chamado sempre que o EA é removido do gráfico.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // remove all dashboard objects
   ObjectsDeleteAll(0,BTN1);
   ObjectsDeleteAll(0,Desc);
   ObjectsDeleteAll(0,Symb);
   ObjectsDeleteAll(0,Base);
   ObjectsDeleteAll(0,RSI);
   
   ChartRedraw(0);
}

Aqui, chamamos a função ObjectDeleteAll, que exclui todos os objetos cujo nome contenha um prefixo específico. A função remove todos os objetos de um determinado tipo utilizando prefixos de identificação nos nomes dos objetos. A lógica é simples: chamamos a função para cada um dos prefixos definidos anteriormente — BTN1, Desc, Symb, Base e RSI, isso garante que todos os elementos gráficos e botões criados pela nossa interface sejam excluídos. Finalmente, chamamos a função ChartRedraw para atualizar o gráfico e confirmar a remoção dos objetos, garantindo que a tela fique limpa e livre de elementos desnecessários. O resultado pode ser visto no GIF abaixo:

REMOÇÃO DO EA GIF

Agora temos uma interface de painel de indicadores totalmente funcional em MQL5. O código-fonte completo para a criação deste painel está estruturado da seguinte forma:

//+------------------------------------------------------------------+
//|                                       ADVANCED IND DASHBOARD.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

// Define button identifiers and properties
#define BTN1 "BTN1"
#define Desc "Desc "
#define Symb "Symb "
#define Base "Base "
#define RSI "RSI "
#define XS1 90
#define XS2 100
#define YS1 25
#define clrW clrWhite
#define clrB clrBlack
#define clrW_Gray C'230,230,230'

// Define the timeframes to be used
ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};

// Function to truncate the ENUM_TIMEFRAMES string for display purposes
string truncPrds(ENUM_TIMEFRAMES period) {
   // Extract the timeframe abbreviation from the full ENUM string
   string prd = StringSubstr(EnumToString(period), 7);
   return prd; // Return the truncated string
}

// Global variables
int handleName_Id; // Variable to store the handle ID for the RSI indicator
double rsi_Data_Val[]; // Array to store the RSI values

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);

   // Loop to create buttons for each timeframe with the corresponding RSI label
   for(int i = 0; i < ArraySize(periods); i++) {
      //Print("BEFORE: ",EnumToString(periods[i]));
      //Print("AFTER: ",truncPrds(periods[i]));
      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray);
   }
   
   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("Index ",i,": Symbol = ",SymbolName(i,true));
      // Check if the symbol is the current symbol being traded
      if (SymbolName(i, true) == _Symbol) {
         createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW);
      } else {
         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
      }

      // Create the base button for the RSI Dashboard at the end
      if (i == SymbolsTotal(true) - 1) {
         createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray);
      }
      
      // Loop to create buttons for RSI values for each symbol and timeframe
      for (int j = 0; j < ArraySize(periods); j++) {
         createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW);
      }
   }
   
   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("SELECTED SYMBOL = ",SymbolName(i, true));
      for (int j = 0; j < ArraySize(periods); j++) {
         //Print("PERIOD = ",EnumToString(periods[j]));
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         //ArrayPrint(rsi_Data_Val);
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
   
   return(INIT_SUCCEEDED); // Return initialization success
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // remove all dashboard objects
   ObjectsDeleteAll(0,BTN1);
   ObjectsDeleteAll(0,Desc);
   ObjectsDeleteAll(0,Symb);
   ObjectsDeleteAll(0,Base);
   ObjectsDeleteAll(0,RSI);
   
   ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   // Loop to update RSI values and buttons on each tick
   for(int i = 0; i < SymbolsTotal(true); i++) {
      for (int j = 0; j < ArraySize(periods); j++) {
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
}
//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {
   ResetLastError(); // Reset the last error code
   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }
   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
   
   ChartRedraw(0); // Redraw the chart to reflect the new button
   return (true); // Return true if creation is successful
}

//+------------------------------------------------------------------+
//| Function to update a button                                      |
//+------------------------------------------------------------------+
bool update_Button(string objName, string text, color clrTxt = clrBlack,
                  color clrBG = clrWhite, color clrBorder = clrWhite
) {
   int found = ObjectFind(0, objName); // Find the button by name
   // Check if the button exists
   if (found < 0) {
      ResetLastError(); // Reset the last error code
      Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found
      return (false); // Return false if button is not found
   } else {
      // Update button properties
      ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
      ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color
      ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
      
      ChartRedraw(0); // Redraw the chart to reflect the updated button
      return (true); // Return true if update is successful
   }
}


Considerações finais

O painel multissímbolo e multiperíodo do indicador RSI, desenvolvido em MetaQuotes Language 5 (MQL5), é uma ferramenta valiosa para análise de mercado. Ele exibe os valores do RSI em tempo real para diversos símbolos e períodos gráficos, auxiliando os traders na tomada de decisões fundamentadas de forma mais rápida e eficiente.

O processo de desenvolvimento envolveu: Configuração dos componentes da interface; Criação dos botões interativos; Atualização dinâmica dos valores do RSI com funções nativas do MQL5. O resultado é um painel intuitivo e funcional, que pode ser utilizado em diferentes estratégias de negociação.

Este artigo demonstrou como o MQL5 pode ser utilizado para desenvolver ferramentas práticas para o trading. Traders podem copiar ou modificar este painel conforme suas necessidades, integrando-o a estratégias semi-automatizadas ou manuais, adaptáveis às constantes mudanças do mercado.

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

Ciência de dados e aprendizado de máquina (Parte 28): Previsão de múltiplos valores futuros para EURUSD Ciência de dados e aprendizado de máquina (Parte 28): Previsão de múltiplos valores futuros para EURUSD
Muitos modelos de inteligência artificial são projetados para prever um único valor futuro. Neste artigo, veremos como utilizar modelos de aprendizado de máquina para prever múltiplos valores futuros. Essa abordagem, chamada de previsão multietapa, permite não apenas prever o preço de fechamento de amanhã, mas também o de depois de amanhã e assim por diante. A previsão multietapa oferece uma vantagem inegável para traders e analistas de dados, pois amplia o espectro de informações para oportunidades de planejamento estratégico.
Funcionalidades do Assistente MQL5 que você precisa conhecer (Parte 29): Taxas de aprendizado e perceptrons multicamadas Funcionalidades do Assistente MQL5 que você precisa conhecer (Parte 29): Taxas de aprendizado e perceptrons multicamadas
Estamos concluindo a análise da sensibilidade da taxa de aprendizado ao desempenho do EA, estudando taxas de aprendizado adaptáveis Essas taxas devem ser ajustadas para cada parâmetro da camada durante o treinamento, por isso precisamos avaliar os potenciais benefícios em relação às perdas esperadas no desempenho.
Reimaginando estratégias clássicas (Parte III): Prevendo máximas mais altas e mínimas mais baixas Reimaginando estratégias clássicas (Parte III): Prevendo máximas mais altas e mínimas mais baixas
Neste artigo, analisamos empiricamente estratégias de trading clássicas para verificar se é possível aprimorá-las com inteligência artificial (IA). Utilizaremos o modelo de Análise Discriminante Linear (Linear Discriminant Analysis) para tentar prever máximas mais altas e mínimas mais baixas.
Reimaginando Estratégias Clássicas (Parte IV): SP500 e Notas do Tesouro dos EUA Reimaginando Estratégias Clássicas (Parte IV): SP500 e Notas do Tesouro dos EUA
Nesta série de artigos, analisamos estratégias clássicas de negociação usando algoritmos modernos para determinar se podemos melhorar a estratégia utilizando IA. No artigo de hoje, revisamos uma abordagem clássica para negociar o SP500 usando a relação que ele tem com as Notas do Tesouro dos EUA.