![Indicadores personalizados no MQL5 para novatos](https://c.mql5.com/i/articles/overlay.png)
Indicadores personalizados no MQL5 para novatos
Introdução
O fundamental para entender profundamente um assunto (não importa se matemática, música ou programação) é o estudo de seus fundamentos. É ótimo quando um estudo similar é iniciado em uma idade bem jovem, então, o entendimento dos fundamentos é muito mais fácil e o conhecimento é específico e compreensivo.
Infelizmente, a maioria das pessoas começam a estudar os mercados financeiro e de ações na meia idade, então, o estudo não é fácil. Neste artigo, eu tentarei ajudar a superar esta barreira inicial no entendimento do MQL5 e na escrita de indicadores personalizados para o terminal de cliente MetaTrader 5.
Indicador SMA como um exemplo simples
A maneira mais eficaz e racional de estudar algo é a solução de problemas práticos. Já que estamos considerando indicadores personalizados, começaremos com o estudo do indicador simples que contenha um código que ilustre os fundamentos da operação do indicador no MQL5.
Como exemplo, vamos considerar o indicador mais famoso da análise técnica - Média de Movimento Simples (SMA). O seu cálculo é simples:
SMA = SUM (CLOSE (i), MAPeriod) / MAPeriod
onde:
- SUM - soma dos valores;
- CLOSE (i) — preço de fechamento da barra i-th;
- MAPeriod — número de barras para a média (período da média).
Aqui está um código deste indicador livre de qualquer acesso:
//+------------------------------------------------------------------+ //| SMA.mq5 | //| Copyright 2009, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_type1 DRAW_LINE #property indicator_color1 Red input int MAPeriod = 13; input int MAShift = 0; double ExtLineBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ void OnInit() { SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA); PlotIndexSetInteger(0, PLOT_SHIFT, MAShift); PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MAPeriod - 1); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { if (rates_total < MAPeriod - 1) return(0); int first, bar, iii; double Sum, SMA; if (prev_calculated == 0) first = MAPeriod - 1 + begin; else first = prev_calculated - 1; for(bar = first; bar < rates_total; bar++) { Sum = 0.0; for(iii = 0; iii < MAPeriod; iii++) Sum += price[bar - iii]; SMA = Sum / MAPeriod; ExtLineBuffer[bar] = SMA; } return(rates_total); } //+------------------------------------------------------------------+
E aqui está o resultado do seu trabalho no terminal de cliente MetaTrader 5:
Primeiro precisamos considerar duas coisas: o propósito de cada série do código em uma mão e a interação entre este código de programa e o terminal do cliente em outra mão.
Usando os comentários
Em um primeiro olhar no código do indicador, os olhos capturam objetos como esses:
//+------------------------------------------------------------------+ //| SMA.mq5 | //| Copyright 2009, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+
//+------------------------------------------------------------------+
É necessário notar que eles não estão relacionados diretamente com o código, eles são apenas comentários, eles são projetados para a leitura de códigos e para exibir um certo conteúdo de semântica de algumas partes deste código. É claro, eles poderiam ser removidos do código sem nenhum dano para sua simplificação futura, mas, o código então perderia o sua brevidade lacônica no entendimento. Em nosso caso, trabalhamos com comentários de linha simples, que sempre começam com um par de caracteres "//" e terminam com um caractere de linha nova.
Está claro que nos comentários o autor escreve tudo que é necessário que ajudará a entender este código depois de algum tempo. Em nosso caso, na primeira parte das séries comentadas, há um nome do indicador e informações sobre o seu autor, a segunda e terceira partes dos comentários dividem as funções OnInit() e OnCalculate(). A última linha no final simplesmente fecha o código do programa.
Estrutura do código SMA
Então, como vemos, o código completo do nosso indicador pode ser dividido em 3 partes:
1. O código, que está escrito sem colchetes no nível global, ele está localizado entre os dois primeiros comentários.
2. Descrição da função OnInit().
3. Descrição da função OnCalculate().
É necessário notar que em programação o significado de função é muito mais amplo que em matemática. Por exemplo, em linguagens de programação funções matemáticas sempre recebem alguns parâmetros de entrada e retornam valores calculados, além disso, as funções no MQL5 também podem executar algumas operações de gráfico, operações de comércio, operações de arquivo, etc.
De fato, qualquer indicador escrito no MQL5 sempre tem alguns conjuntos de partes escritas pelo usuário mínimas, os conteúdos dos quais são individuais e dependem dos recursos de um indicador criado.
Além destes componentes, o conjunto mínimo de funções pode conter a descrição de outra função do MQL5 - OnDeInit():
//+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { }
Em nosso caso, não é necessário então está ausente aqui.
Interação entre SMA e o terminal do cliente MetaTrader
Agora vamos considerar o trabalho do arquivo compilado SMA.ex5 que obtemos após pressionar a tecla "Compilar" no MetaEditor com o SMA.mq5 aberto. É necessário notar que os arquivos de texto com a extensão .mq5 são somente um código-fonte em formato de texto, ele deve ser compilado primeiramente para ser usado no terminal de cliente.
Após anexar este indicador a um gráfico através da janela do navegador o MetaTrader executará o código da primeira parte do indicador. Depois disso, ele chamará a função OnInit() para uma execução única desta função e, futuramente, a cada novo tick (depois da chegada de uma nova cota) ele chamará a função OnCalculate() e consequentemente executará o código dessa função. Se a OnDeInit() estivesse presente no indicador, o MetaTrader chamaria essa esta função uma vez depois de deslocar o indicador do gráfico ou depois da modificação do timeframe.
O significado e propósito de todas as partes do indicador ficam claros depois desta explicação. Na primeira parte do código no nível global há alguns operadores simples que são executados uma vez depois do início do indicador. Além disso, há uma declaração de variáveis que são "visíveis" em todos os blocos do indicador e que lembram seus valores enquanto o indicador está no gráfico.
As constantes e funções que são executadas uma vez devem estar localizadas dentro da função OnInit(), porquê pode não valer à pena colocá-las no bloco da função OnCalculate(). O código para o cálculo do indicador, que permite calcular os seus valores para cada barra, deve ser colocado na função OnCalculate().
Os procedimentos que apagam o lixo inútil (se houver algum) do gráfico depois que o indicador seja removido dele, deve ser colocado dentro da OnDeInit(). Por exemplo, é necessário para a exclusão dos objetos gráficos, criados pelo indicador.
Depois destas explicações, estamos prontos para examinar com detalhes o código do indicador, que foi considerado acima.
Código do programa do indicador SMA
O primeiro grupo das linhas de códigos começa com a #property do operador, que permite a especificação de parâmetros adicionais de ajustes do indicador. A lista completa de propriedades de programação possíveis pode ser encontrada em documentação do MQL5. Se necessário, é possível escrever propriedades adicionais do indicador. Em nosso caso temos 5 linhas do código, o propósito de cada linha está descrito nos comentários:
//---- the indicator will be plotted in the main window #property indicator_chart_window //---- one buffer will be used for the calculations and plot of the indicator #property indicator_buffers 1 //---- only one graphic plot is used #property indicator_plots 1 //---- the indicator should be plotted as a line #property indicator_type1 DRAW_LINE //---- the color of the indicator's line is red #property indicator_color1 Red
Note que não há pontos e vírgulas (";") no final das linhas. A razão é a seguinte: de fato, em nosso caso é a definição de constantes, mas apresentada de outra maneira.
A nossa média de movimento simples tem apenas 2 parâmetros que podem ser modificados por um usuário - é um período médio ao longo dos eixos de tempo. Estes dois parâmetros devem ser declarados como variáveis de entrada do indicador, conforme foi declarado nas duas linhas de códigos seguintes:
//---- indicator input parameters input int MAPeriod = 13; //averaging period nput int MAShift = 0; //horizontal shift (in bars)
Note que depois da declaração destes parâmetros de entrada há comentários e esses comentários estarão visíveis como nomes de parâmetros de entrada na janela "Propriedades" do indicador:
Em nosso caso, estes nomes são muito mais claros que os nomes das variáveis do indicador. Então, estes comentários devem ser simples.
E a última linha de código que não tem nenhum colchetes é a declaração do banco de dados dinâmico ExtLineBuffer[].
//---- the declaration of the dynamic array //that will be used further as an indicator's buffer double ExtLineBuffer[];
Foi declarado como uma variável global por várias razões.
Primeiramente, este banco de dados deve ser convertido no buffer do indicador, ele é implementado no bloco da função OnInit(). Segundo, o próprio buffer do indicador será usado dentro da função OnCalculate(). Terceiro, este banco de dados gravará os valores do indicador que será diagramado como uma curva no gráfico. Devido ao fato de que ele foi declarado como uma variável global, ele está disponível para todos os blocos do indicador e ele grava os seus valores o tempo todo até que o indicador seja destacado do gráfico.
O conteúdo da função OnInit() é apresentado por apenas 3 operadores, eles são funções integradas no terminal de cliente do MetaTrader.
O chamado da primeira função designa o buffer do indicador zeroth com um banco de dados dinâmico dimensional ExtLineBuffer[]. Duas chamadas de outra função com valores diferentes de parâmetros de entrada permitem movimentar o indicador através do eixo de preço e permite especificar a sua diagramação a partir da barra com o número MAPeriod.
void OnInit() { //----+ //---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA); //---- set plot shift along the horizontal axis by MAShift bars PlotIndexSetInteger(0,PLOT_SHIFT,MAShift); //---- set plot begin from the bar with number MAPeriod PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod); //----+ }
A última chamada da função PlotIndexSetInteger() passa o valor igual ao do MAPeriod (através do parâmetro iniciar da função OnCalculate()) para outro indicador, se ele for aplicado aos valores do nosso indicador. A lógica é simples, não há nenhuma média nas primeiras barras do MaPeriod-1, por isso, a diagramação desse indicador é inútil. Entretanto, este valor deve ser passado para mover a origem dos cálculos de outro indicador.
Não é uma lista completa das funções integradas que são usadas nos indicadores personalizados e pode ser localizada neste bloco do indicador. Consulte documentação do MQL5 para mais detalhes.
Finalmente, vamos considerar o código da função OnCalculate(). Não existem chamadas personalizadas nesta função como na função OnInit() pois essas funções são chamadas pelo terminal de cliente MetaTrader. Por essa razão, os parâmetros de entrada da função são declarados como constantes.
int OnCalculate( const int rates_total, // number of available bars in history at the current tick const int prev_calculated,// number of bars, calculated at previous tick const int begin, // index of the first bar const double &price[] // price array for the calculation )
Estes parâmetros de entrada não podem ser modificados, os seus valores são passados pelo terminal de cliente para uso futuro no código desta função. As variáveis de entrada da OnCalculate estão descritas na documentação do MQL5. A função OnCalculate() retorna os seus valores para o terminal de cliente usando a função de retorno (rates_total). O terminal de cliente recebe este valor do tick atual depois da execução da OnCalculate() e passa o valor retornado para outro parâmetro prev_calculated. Então, é sempre possível determinar o alcance dos índices de barras e executar os cálculos de uma vez para os novos valores do indicador que apareceram depois do tique anterior.
É necessário notar que a ordem das barras no terminal de cliente do MetaTrader é executada da esquerda para a direita, então, a barra mais antiga (da esquerda) apresentada no gráfico tem índice 0, a próxima tem índice 1, etc. Os elementos do buffer ExtLineBuffer[] têm a mesma ordem.
A estrutura de código simples dentro da função OnCalculate do nosso indicador é universal e típica para muitos indicadores de análises técnicas. Então, vamos considerá-la em detalhes. A lógica da função OnCalculate() é:
1. Verifique a presença de barras, necessárias para os cálculos.
2. Declaração de variáveis locais.
3. Consiga o índice da barra inicial para o cálculo.
4. O loop principal do cálculo do indicador.
5. Retorna o valor do rates_total para o terminal de cliente usando o operador return().
Eu acho que o primeiro termo está claro. Por exemplo, se o período de média de média de movimento for igual a 200, mas o terminal de cliente te somente 100 barras, não é necessário executar o cálculo, pois, não existem barras suficientes para o cálculo. Então temos que retornar 0 para o terminal de cliente usando o operador return.
//---- check for the presence of bars, sufficient for the calculation if(rates_total<MAPeriod-1+begin) return(0);
O nosso indicador pode ser aplicado aos dados de algum outro indicador que também pode ter um número mínimo de barras para o cálculo. O uso da constante iniciar é necessário para levar em conta este fato. Consulte o artigo Aplicando um Indicador à Outro para detalhes.
As variáveis locais declaradas neste bloco são necessárias somente para os cálculos intermediários dentro da função OnCalculate(). Estas variáveis são lançadas a partir da RAM do computador depois da chamada da função.
//---- declaration of local variables int first,bar,iii; double Sum,SMA;
É necessário ter muito cuidado com o índice de início do loop principal (primeira variável). Na primeira chamada da função (podemos determinar pelo valor do parâmetro prev_calculated) temos que executar o cálculo dos valores do indicador para todas as barras. Para todos os ticks futuros do terminal de cliente temos que executar o cálculo somente para as novas barras que apareceram. É feito com 3 linhas de código:
//---- calculation of starting index first of the main loop if(prev_calculated==0) // check for the first start of the indicator first=MAPeriod-1+begin; // start index for all the bars else first=prev_calculated-1; // start index for the new bars
O alcance da variável muda no operador do loop principal do recálculo do indicador que já foi discutido.
//---- main loop of the calculation for(bar=first;bar<rates_total;bar++)
O processamento da barra no loop principal é executado na ordem crescente (bar++), em outras palavras, da esquerda para a direita como uma forma natural e certa. Em nosso indicador ele pode ser implementado de outra forma (na ordem inversa). É melhor usar a ordem crescente nos indicadores. A variável do loop principal é chamada de "barra" mas muitos programadores preferem usar o nome "i". Eu prefiro usar "barra" pois deixa o código mais claro e legível.
O algoritmo de média, que foi implementado no loop principal é simples.
{ Sum=0.0; //---- summation loop for the current bar averaging for(iii=0;iii<MAPeriod;iii++) Sum+=price[bar-iii]; // Sum = Sum + price[bar - iii]; // eqaual to //---- calculate averaged value SMA=Sum/MAPeriod; //---- set the element of the indicator buffer with the value of SMA we have calculated ExtLineBuffer[bar]=SMA; }
No segundo loop, estamos executando a soma cumulativa dos preços das barras anteriores do período e as dividindo por este período médio. Como resultado temos o valor final do SMA.
Depois do término do loop principal, a função OnCalculate retorna o número das barras disponíveis a partir da variável rates_total. Na próxima chamada da função OnCalculate(), este valor será passado pelo terminal de cliente para a variável prev_calculated. Este valor, diminuído por 1 será usado como índice inicial para o loop principal.
Aqui está o código-fonte completo do indicador com comentários detalhados para cada linha de código:
//+------------------------------------------------------------------+ //| SMA.mq5 | //| Copyright 2009, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ //---- the indicator will be plotted in the main window #property indicator_chart_window //---- one buffer will be used for the calculations and plot of the indicator #property indicator_buffers 1 //---- only one graphic plot is used #property indicator_plots 1 //---- the indicator should be plotted as a line #property indicator_type1 DRAW_LINE //---- the color of the indicator's line is red #property indicator_color1 Red //---- indicator input parameters input int MAPeriod = 13; //Averaging period input int MAShift = 0; //Horizontal shift (in bars) //---- the declaration of the dynamic array //that will be used further as an indicator's buffer double ExtLineBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ void OnInit() { //----+ //---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA); //---- set plot shift along the horizontal axis by MAShift bars PlotIndexSetInteger(0,PLOT_SHIFT,MAShift); //---- set plot begin from the bar with number MAPeriod PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod); //----+ } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate( const int rates_total, // number of available bars in history at the current tick const int prev_calculated,// number of bars, calculated at previous tick const int begin, // index of the first bar const double &price[] // price array for the calculation ) { //----+ //---- check for the presence of bars, sufficient for the calculation if (rates_total < MAPeriod - 1 + begin) return(0); //---- declaration of local variables int first, bar, iii; double Sum, SMA; //---- calculation of starting index first of the main loop if(prev_calculated==0) // check for the first start of the indicator first=MAPeriod-1+begin; // start index for all the bars else first=prev_calculated-1; // start index for the new bars //---- main loop of the calculation for(bar = first; bar < rates_total; bar++) { Sum=0.0; //---- summation loop for the current bar averaging for(iii=0;iii<MAPeriod;iii++) Sum+=price[bar-iii]; // It's equal to: Sum = Sum + price[bar - iii]; //---- calculate averaged value SMA=Sum/MAPeriod; //---- set the element of the indicator buffer with the value of SMA we have calculated ExtLineBuffer[bar]=SMA; } //----+ return(rates_total); } //+------------------------------------------------------------------+
Esta forma de código é muito mais fácil de entender e ler.
Eu gostaria de esboçar outro recurso que pode ser usado para simplificar o entendimento do código. Você pode usar espaços e linhas vazias para deixá-lo mais claro.
Conclusão
Isto é tudo sobre a interação entre o código do indicador personalizado e o terminal de cliente MetaTrader. É claro, o assunto é muito mais amplo do que consideramos, o alvo deste artigo é ajudar novatos a entender os fundamentos, então consulte a documentação para detalhes.
Traduzido do Russo por MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/pt/articles/37
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/37
![Processamento de eventos trade no Expert Advisor usando a função OnTrade()](https://c.mql5.com/2/0/function_ontrade_MQL5.png)
![Usando os Ponteiros de Objeto no MQL5](https://c.mql5.com/2/0/pointer_small.png)
![Como chamar indicadores no MQL5](https://c.mql5.com/2/0/Calling_indicators_MQL5.png)
![Introdução ao MQL5: Como escrever Expert Advisor e Custom Indicator simples](https://c.mql5.com/2/0/a03__1.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso