Transferência de um Código Indicador para um Código Expert Advisor. Esquemas da estrutura geral de um Expert Advisor e funções indicadoras
Introdução
No artigo anterior (Transferência de um Código Indicador para um Código Expert Advisor. Estrutura do indicador) analisamos a estrutura geral de um indicador, o código que se destina para a transferência de um código Expert Advisor e descrevemos as principais ideias de um ajuste preliminar de um código indicador. Agora vamos tentar transformar o código obtido em uma função personalizada, porque esta é, talvez, a forma mais conveniente de apresentar um código indicador em um Expert Advisor. A função personalizada pode ser apresentada como um arquivo mqh e sua declaração em um Expert Advisor usando a diretiva #include tomará muito pouco espaço e chamar essa função não é muito mais difícil que chamar um indicador personalizado. O mais importante é que essas funções personalizadas podem ser universais para utilização posterior em quaisquer Expert Advisors.
Antes de começar a escrever tal função, vamos analisar como essa função irá interagir com a outra parte do Expert Advisor, independentemente da estrutura interna da função.
Estrutura de um EA com indicador de chamada personalizado
Vamos primeiro estudar a estrutura esquemática de um EA que recebe dados de indicadores personalizados. Neste EA, estamos primeiramente interessados apenas na parte que recebe dados de indicadores personalizados. Até agora, não discutiremos a forma como o EA processa esses dados, transferindo-os em sinais de negociações e a estrutura de uma parte executiva do EA. Vamos analisar como um indicador personalizado neste EA, discutido no artigo anterior. Aqui está um exemplo de uma estrutura de um Expert Advisor:
//+------------------------------------------------------------------+ //| ExpertIndPlan0.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" //---- EA input parameters extern int period10 = 15; extern int period11 = 15; extern int period12 = 15; //---- EA input parameters extern int period20 = 15; extern int period21 = 15; extern int period22 = 15; //---- Declaring buffers for indicator values double Ind_Buffer1[6]; double Ind_Buffer2[6]; //+------------------------------------------------------------------+ //| Custom Expert initialization function | //+------------------------------------------------------------------+ int init() { // Here is the code of EA initialization //---- initialization end return(0); } //+------------------------------------------------------------------+ //| Custom Expert iteration function | //+------------------------------------------------------------------+ int start() { //---- Checking whether the bars number is enough for further calculation if(iBars(Symbol(), 0) < period10 + period11 + period12 + 10) return(0); if(iBars(Symbol(), 0) < period20 + period21 + period22 + 10) return(0); //---- getting indicator values for further calculation for(int bar = 5; bar >= 1; bar--) { Ind_Buffer1[bar] = iCustom("IndicatorPlan", Symbol(), 0, period10, period11, period12, 0, bar); Ind_Buffer2[bar] = iCustom("IndicatorPlan", Symbol(), 0, period20, period21, period22, 0, bar); } // Here is the EA code, forming trade signals // based on the values of indicator buffer cells //---- // here is the EA executive part code, // requesting for placing an order //---- return(0); } //+------------------------------------------------------------------+
Neste esquema a cada crédito que tomamos de um buffer zero do indicador personalizado, o IndicatorPlan.mq4 conta os valores em duas chamadas e os coloca em matrizes comuns Ind_Buffer1 [] e Ind_Buffer2 []. O esquema de chamar um indicador é feito considerando que para outros cálculos vamos precisar apenas dos cinco últimos valores dos indicadores, exceto o zero.
Estrutura de um EA com função personalizada de chamada
Enquanto estamos desenvolvendo uma função de indicador universal adequada para um Expert Advisor, ela deve enviar os valores recebidos para o buffer de indicador análogo, que irá armazenar valores do indicador para todas as barras do gráfico. Claro que poderíamos desenvolver uma função indicadora, chamando o que seria completamente análogo ao chamar um indicador personalizado, mas escrever tal função levaria muito tempo e seu código seria bastante longo.
Podemos fazer isto de uma maneira mais fácil. Esta função deve receber como parâmetros de entrada os parâmetros de um indicador personalizado e buffer, e deve retornar o mesmo buffer com uma emulação de modo indicador, onde as células são preenchidas com valores de indicadores calculados. Isto pode ser feito facilmente declarando em nossa função de uma variável externa ligada por referência para a função correspondente de um buffer de indicador. Na linguagem MQL4 ele parecerá com este: double & inputbuffer. A função do indicador deve ser declarada de maneira lógica, retornando 'verdadeiro', se um cálculo foi bem-sucedido, ou "falso", se um cálculo foi mal sucedido devido à ausência de um número adequado de barras no gráfico. Após estas explicações a função do indicador, construída a partir do esquema do indicador, discutido no artigo anterior, deve ter o seguinte formato:
bool Get_IndSeries(int Number,string symbol, int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0[], double& InputBuffer1[], double& InputBuffer2[])
É natural que, com exceção do buffer de indicador InputBuffer0 as variáveis externas também irão conter buffers para cálculos intermediários InputBuffer1 e InputBuffer2, porque é muito problemático fazer esses buffers dentro da função. É melhor emular o modo do indicador desses buffers de operação dentro da função. Não irá causar problemas. Agora vamos nos debruçar sobre o significado da variável externa NullBarRecount. Na verdade, a maioria dos EAs não precisa de cálculos na barra zero, e enquanto estamos escrevendo um código de função universal do indicador, ele irá recalcular valores do indicador na barra zero, o que pode aumentar substancialmente o tempo de execução. Ao indicar o parâmetro externo da função NullBarRecount como "falso", nós proibimos cálculos desnecessários da função na barra zero.
Agora podemos apresentar o esquema de uma estrutura EA para chamar indicadores em uma variante com funções de chamada:
//+------------------------------------------------------------------+ //| ExpertIndPlan1.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" //---- EA input parameters extern int period10 = 15; extern int period11 = 15; extern int period12 = 15; //---- EA input parameters extern int period20 = 15; extern int period21 = 15; extern int period22 = 15; //---- Indicator buffers declaration double Ind_Buffer10[], Ind_Buffer11[], Ind_Buffer12[]; double Ind_Buffer20[], Ind_Buffer21[], Ind_Buffer22[]; //+------------------------------------------------------------------+ //| Get_IndSeries() function | //+------------------------------------------------------------------+ //---- Declaration of the function Get_IndSeries() bool Get_IndSeries(int Number,string symbol, int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0[], double& InputBuffer1[], double& InputBuffer2[]) { //---- // Here is the code of the function GetIdicator() //---- return(true); } //+------------------------------------------------------------------+ //| Custom Expert initialization function | //+------------------------------------------------------------------+ int init() { //---- // Here is the code of the EA initialization //---- initialization end return(0); } //+------------------------------------------------------------------+ //| Custom Expert iteration function | //+------------------------------------------------------------------+ int start() { //---- Checking whether the bars number is enough for further calculation if(iBars(Symbol(), 0) < period10 + period11 + period12 + 10) return(0); if(iBars(Symbol(), 0) < period20 + period21 + period22 + 10) return(0); //---- getting indicator values for further calculation if(!Get_IndSeries(0,Symbol(), 0, false, period10, period11, period12, Ind_Buffer10, Ind_Buffer11, Ind_Buffer12)) return(0); if(!Get_IndSeries(1, Symbol(), 0, false, period20, period21, period22, Ind_Buffer20, Ind_Buffer21,Ind_Buffer22)) return(0); //---- // Here is the EA code, forming trade signals // based on the values of indicator buffer cells //---- // here is the EA executive part code, // requesting for placing an order //---- return(0); } //+------------------------------------------------------------------+
Esquema geral de transformação de um código indicador em função personalizada
Após este trabalho preliminar, podemos passar para a construção de um esquema geral de uma estrutura interna de uma função do indicador. Vamos tomar como base o último esquema do indicador no artigo anterior. Não há dificuldades.
1. Leve em consideração apenas o conteúdo da função start int();
2. Adicione declaração da função Get_IndSeries():
bool Get_IndSeries(string symbol, int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0, double& InputBuffer1, double& InputBuffer2)
3. Altere os nomes dos buffers de indicador dentro do código (Ind_Buffer) de acordo com os nomes do buffer (InputBuffer) das variáveis externas da função Get_IndSeries();
4. Adicione declaração da variável LastCountBar;
5. Verifique a veracidade da variável NullBarRecount:
if(!NullBarRecount) LastCountBar = 1;
7. Faça mudanças no início do código, ao verificar se o número de barras é suficiente para cálculo posterior: retorno (0) em retorno (falso);
8. No final da alteração do código retorno (0) em retorno (verdadeiro);
A função do indicador está pronta:
//+------------------------------------------------------------------+ //| Get_IndSeries.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ bool Get_IndSeries(int Number, string symbol,int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0[], double& InputBuffer1[], double& InputBuffer2[]) { //---- getting the number of all bars of a chart int IBARS = iBars(symbol, timeframe); //---- Checking whether the bars number is enough for further calculation if(IBARS < period0 + period1 + period2) return(false); //---- EMULATION OF INDICATOR BUFFERS if(ArraySize(InputBuffer0) < IBARS) { ArraySetAsSeries(InputBuffer0, false); ArraySetAsSeries(InputBuffer1, false); ArraySetAsSeries(InputBuffer2, false); //---- ArrayResize(InputBuffer0, IBARS); ArrayResize(InputBuffer1, IBARS); ArrayResize(InputBuffer2, IBARS); //---- ArraySetAsSeries(InputBuffer0, true); ArraySetAsSeries(InputBuffer1, true); ArraySetAsSeries(InputBuffer2, true); } //----+ introducing static memory variables static int IndCounted[]; //----+ changing the size of static variables if(ArraySize(IndCounted) < Number + 1) ArrayResize(IndCounted, Number + 1); //----+ introducing an integer variable int LastCountBar; //----+ Checking if the recalculation of the zero bar is allowed if(!NullBarRecount) LastCountBar = 1; else LastCountBar = 0; //----+ Inserting a variable with a floating point double Resalt0, Resalt1, Resalt2; //----+ Inserting integer variables and getting already calculated bars int limit, MaxBar, bar, counted_bars = IndCounted[Number]; //----+ Remembering the number of all bars of a chart (we do not count the zero bar!) IndCounted[Number] = IBARS - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated limit = IBARS - counted_bars - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated MaxBar = IBARS - 1 - (period0 + period1 + period2); //---- initialization of zero if(limit > MaxBar) { limit = MaxBar; for(bar = IBARS - 1; bar >= 0; bar--) { InputBuffer0[bar] = 0.0; InputBuffer1[bar] = 0.0; InputBuffer2[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { // Here code of the variable Resalt1 calculation // based on the external variable period1 InputBuffer1[bar] = Resalt1; } //----+ THE SECOND CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { // Here code of the variable Resalt2 calculation // based on the values of the buffer Ind_Buffer1[] // and external variable period2 InputBuffer2[bar] = Resalt2; } //----+ THE MAIN CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { // Here code of the variable Resalt0 calculation // based on the values of the buffer Ind_Buffer2[] // and external variable period0 InputBuffer0[bar] = Resalt0; } return(true); } //+------------------------------------------------------------------+
Acho que caso o leitor use o MQL4 muito bem, depois de ler as ações descritas acima, ele não terá nenhum problema em escrever funções dos indicadores de acordo com o esquema dado.
Exemplo de como escrever uma função de indicador personalizada
Agora vamos escrever uma função do indicador. Vamos pegar de exemplo um indicador extremamente simples:
//+------------------------------------------------------------------+ //| RAVI.mq4 | //| Copyright © 2005, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2005, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" //---- drawing the indicator in a separate window #property indicator_separate_window //---- number of indicator buffers #property indicator_buffers 1 //---- indicator color #property indicator_color1 Red //---- INPUT PARAMETERS OF THE INDICATOR extern int Period1 = 7; extern int Period2 = 65; extern int MA_Metod = 0; extern int PRICE = 0; //---- indicator buffers double ExtBuffer[]; //+------------------------------------------------------------------+ //| RAVI initialization function | //+------------------------------------------------------------------+ int init() { //---- indicator drawing style SetIndexStyle(0, DRAW_LINE); //---- indicator buffers SetIndexBuffer(0,ExtBuffer); //---- indicator name and labels for subwindows IndicatorShortName("RAVI (" + Period1+ ", " + Period2 + ")"); SetIndexLabel(0, "RAVI"); //---- initialization end return(0); } //+------------------------------------------------------------------+ //| RAVI iteration function | //+------------------------------------------------------------------+ int start() { int MinBars = MathMax(Period1, Period2); //---- checking whether the bars number is enough for further calculation if(Bars < MinBars) return(0); //----+ Introducing variables with a floating point double MA1, MA2, result; //----+ Introducing integer variables and getting already calculated bars int MaxBar, bar, limit, counted_bars = IndicatorCounted(); //---- checking for possible errors if(counted_bars < 0) return(-1); //---- the last calculated bar should be recalculated if(counted_bars > 0) counted_bars--; //---- defining the number of the oldest bar, // starting from which all bars will be recalculated MaxBar = Bars - 1 - MinBars; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated limit = Bars - counted_bars - 1; //---- zero initialization if(limit > MaxBar) { for(int ii = Bars - 1; ii >= MaxBar; ii--) ExtBuffer[ii] = 0.0; limit = MaxBar; } //---- main cycle for(bar = 0; bar <= limit; bar++) { MA1 = iMA(NULL, 0, Period1, 0, MA_Metod, PRICE,bar); MA2 = iMA(NULL, 0, Period2, 0, MA_Metod, PRICE,bar); //---- result = ((MA1 - MA2) / MA2)*100; ExtBuffer[bar] = result; } //---- return(0); } //+------------------------------------------------------------------+
Fixação de algoritmo
1. Livre-se de todos os elementos desnecessários no código indicador;
2. Escreva o código de emulação do buffer de indicador para um único buffer ExtBuffer[];
3. Substitua a função IndicatorCounted() pela variável IndCounted;
4. Inicialize a variável IndCounted pelo montante das barras do gráfico menos um;
5. Altere as barras de variáveis predeterminadas na chamada de séries cronológicas iBars(símbolo, calendário);
6. Delete verificação desnecessária para counted_bars:
//---- checking possible errors if(counted_bars < 0) return(-1); //---- the last calculated bar must be recalculated if(counted_bars > 0) counted_bars--;
7. Leve em consideração apenas o conteúdo da função start int ();
8. Adicione declaração da função Get_RAVISeries():
bool Get_RAVISeries(int Number, string symbol,int timeframe, bool NullBarRecount, int Period1, int Period2, int MA_Metod, int PRICE, double& InputBuffer[])
9. Substitua os nomes do buffer de indicador dentro do código (ExtBuffer) de acordo com os nomes do buffer (InputBuffer) de variáveis externas da função Get_RAVISeries();
10. Adicione declaração da variável LastCountBar;
11. Transforme a variável estática IndCounted em uma matriz IndCounted[Number] e adicione um código para alterar o tamanho das variáveis de acordo com os número de chamadas da função Get_RAVISeries. mqh:
//----+ changing the size of static variables if(ArraySize(IndCounted) < Number + 1) { ArrayResize(IndCounted, Number + 1); }
12. Verifique a veracidade da variável NullBarRecount:
if(!NullBarRecount) LastCountBar = 1;
13. Em todos os ciclos de cálculo do indicador altere para zero em LastCountBar:
for(bar = limit; bar >= LastCountBar; bar--)
14. Ao verificar se o número de barras é suficiente para cálculo posterior: retorno (0) em retorno (falso), faça uma mudança no início do código;
15. No final, substitua retorno(0) para retorno(verdadeiro).
Após o código inteiro mudar, temos a função do indicador Get_RAVISeries():
//+------------------------------------------------------------------+ //| Get_RAVISeries.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ bool Get_RAVISeries(int Number, string symbol,int timeframe, bool NullBarRecount, int Period1, int Period2, int MA_Metod, int PRICE, double& InputBuffer[]) { //---- getting the number of all bars of a chart int IBARS = iBars(symbol, timeframe); //---- Checking whether the bars number is enough for further calculation if(IBARS < MathMax(Period1, Period2)) return(false); //---- EMULATION OF INDICATOR BUFFERS if(ArraySize(InputBuffer) < IBARS) { ArraySetAsSeries(InputBuffer, false); //---- ArrayResize(InputBuffer, IBARS); //---- ArraySetAsSeries(InputBuffer, true); } //----+ inserting static variables of memory static int IndCounted[]; //----+ changing the size of static variables if(ArraySize(IndCounted) < Number + 1) { ArrayResize(IndCounted, Number + 1); } //----+ Introducing an integer variable int LastCountBar; //----+ Checking whether the recalculation of the zero bar is allowed if(!NullBarRecount) LastCountBar = 1; //----+ Introducing floating point variables double MA1,MA2,result; //----+ Introducing integer variables and getting alreadu calculated bars int MaxBar, bar, limit, counted_bars = IndCounted[Number]; //----+ Remembering the amount of all chart bars IndCounted[Number] = IBARS - 1; //---- determining the number of the oldest bar, // starting from which new bars will be recalculated limit = IBARS - counted_bars - 1; // Print(IBARS - counted_bars); //---- determining the number of the oldest bar, // starting from which all bars will be recalculated MaxBar = IBARS - 1 - MathMax(Period1, Period2); //---- zero initialization if(limit > MaxBar) { limit = MaxBar; for(bar = IBARS - 1; bar >= 0; bar--) { InputBuffer[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { MA1 = iMA(symbol, timeframe, Period1, 0, MA_Metod, PRICE, bar); MA2 = iMA(symbol, timeframe, Period2, 0, MA_Metod, PRICE, bar); //---- result = ((MA1 - MA2) / MA2)*100; InputBuffer[bar] = result; } //----+ return(true); } //+------------------------------------------------------------------+
Certamente, tudo isso é ótimo! Mas não é muito simples. Mas surge uma pergunta: será que esta função do indicador vai calcular da mesma forma que um indicador personalizado?
Teste da função do indicador para precisão de cálculo
Precisamos verificar se os resultados dos cálculos da função são iguais aos resultados dos cálculos dos indicadores personalizados. Para este propósito o Expert Advisor mais adequado é o que não negocia e só recebe valores do indicador personalizado RAVI.mq4 e da função personalizada Get_RAVISeries(), encontra a diferença e depois disso envia para um arquivo de log o valor do indicador, o valor da função personalizada e a diferença entre eles. Tudo que precisamos fazer é analisar o conteúdo do arquivo de log para fazer a conclusão final sobre a correspondência do nosso algoritmo da função personalizada Get_RAVISeries() e o indicador RAVI.mq4:
//+------------------------------------------------------------------+ //| Get_RAVISeriesTest.mq4 | //| Copyright © 2007, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" //---- INPUT EA PARAMETERS extern bool NullBarRecount = true; //---- indicator buffers double RAVI_Buffer0[]; double RAVI_Buffer1[]; double RAVI_Buffer2[]; //+------------------------------------------------------------------+ //| Get_RAVISeries() function | //+------------------------------------------------------------------+ #include <Get_RAVISeries.mqh> //+------------------------------------------------------------------+ //| Custom Expert initialization function | //+------------------------------------------------------------------+ int init() { //---- initialization end return(0); } //+------------------------------------------------------------------+ //| Custom Expert iteration function | //+------------------------------------------------------------------+ int start() { //---- double Ind_Velue, Resalt; //---- if(!Get_RAVISeries(0, Symbol(), 0, NullBarRecount, 10, 20, 1, 0, RAVI_Buffer0)) return(0); if(!Get_RAVISeries(1, Symbol(), 240, NullBarRecount, 25, 66, 2, 1, RAVI_Buffer1)) return(0); if(!Get_RAVISeries(2, Symbol(), 1440, NullBarRecount, 30, 70, 3, 3, RAVI_Buffer2)) return(0); //---- getting indicator values for the test 0 Ind_Velue = iCustom(NULL, 0, "RAVI", 10, 20, 1, 0, 0, 2); Resalt = RAVI_Buffer0[2] - Ind_Velue; Print(" " + Ind_Velue + " " + RAVI_Buffer0[2] + " " + Resalt+""); //---- getting indicator values for the test 1 Ind_Velue = iCustom(NULL, 240, "RAVI", 25, 66, 2, 1, 0, 2); Resalt = RAVI_Buffer1[2] - Ind_Velue; Print(" " + Ind_Velue + " " + RAVI_Buffer1[2] + " " + Resalt+"" ); //---- getting indicator values for the test 2 Ind_Velue = iCustom(NULL, 1440, "RAVI", 30, 70, 3, 3, 0, 2); Resalt = RAVI_Buffer2[2] - Ind_Velue; Print(" " + Ind_Velue + " " + RAVI_Buffer2[2] + " " + Resalt + ""); //---- return(0); } //+------------------------------------------------------------------+
No strategy tester inicie o Expert Advisor Get_RAVISeriesTest. Naturalmente, o arquivo compilado RAVI.ex4 já deve estar na pasta \expert\indicators, e o arquivo Get_RAVISeries.mqh na pasta \ \expert \include do Terminal do cliente MetaTrader. No strategy tester journal e no arquivo de log podemos visualizar duas colunas com valores do indicador e o seu análogo na forma de uma função; A terceira coluna mostra a diferença desses valores. Todos os valores da última coluna são iguais a zero. Isso significa que os valores são idênticos em ambos os casos. Podemos concluir que a tarefa de escrever uma função personalizada de indicador foi resolvida com sucesso!
Conclusão
No próximo artigo, dedicado a este tema, vamos analisar um exemplo mais difícil de como escrever funções deste tipo e a implementação de um Expert Advisor simples baseado em tais funções.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1457
- 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