Transferência de um Código Indicador para um Código Expert Advisor. Estrutura do Indicator

Nikolay Kositsin | 19 fevereiro, 2016

Introdução

Para melhor compreensão, o autor recomenda a leitura do seguinte material:

  1. MetaQuotes Software Corp. Características de Criação de Indicadores Personalizados.
  2. Nikolay Kositsin. Recontagem de barras nulas em em alguns indicadores.

Antes de começar a me debruçar sobre o tema do artigo, indicado no título, a seguinte pergunta seria bastante apropriada: "Para quê precisamos transferir um código indicador para um código EA, se na maioria dos casos, um EA, operando com indicadores personalizados, parece muito mais fácil do que o seu análogo, que contém todas as informações necessárias sobre os indicadores personalizados de operação dentro do próprio código? Especialmente se levarmos em consideração o fato de que, se um código é escrito corretamente, os resultados em ambos os casos serão absolutamente idênticos! "

Na minha opinião, nos dois casos é necessário:
  1. Se os valores de cálculo do EA calculados em uma barra zero não forem usados, naturalmente gostaríamos de omitir um recálculo desnecessário na barra zero e na primeira barra. Isto permite encurtar em três vezes o tempo de tal otimização do EA, o que é bastante relevante em um código muito complicado e com intensivo uso de recursos!
  2. O uso comercial de um Expert Advisor com a proteção máxima de seu código contra descompilação.

No segundo caso, a situação é bem clara e a transferência de código é razoável. E, no primeiro caso, na maioria das situações, é muito mais fácil reescrever códigos de indicadores personalizados, exceto os cálculos desnecessários! Naturalmente, tais indicadores são adequados apenas para o Expert Advisors, e não para uma negociação! Então, vamos começar a nossa discussão desta variante da solução do problema.


Exemplo de Otimização do Indicador

Primeiramente, gostaria de chamar atenção para o seguinte fragmento de código de um indicador personalizado:

int start()
  {
    int limit;
    int counted_bars = IndicatorCounted();
//---- the last calculated bar will be recalculated
    if(counted_bars > 0) 
        counted_bars--;
    limit = Bars - counted_bars - 1;
//---- the main cycle
    for(int i = limit; i >= 0; i--)
      {
        //---- 
        ExtBlueBuffer[i] = iMA(NULL, 0, JawsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
        ExtRedBuffer[i] = iMA(NULL, 0, TeethPeriod, 0, MODE_SMMA, 
                              PRICE_MEDIAN, i);
        ExtLimeBuffer[i] = iMA(NULL, 0, LipsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
      }
//----
     return(0);
  }
Neste caso, a seguinte linha é relevante:
if(counted_bars > 0) 
    counted_bars--;

O significado desta verificação com diminuição do valor variável ''counted_bars" por um é o seguinte: se um indicador personalizado não incluir esta linha, ele pode enviar valores errados de seus buffers para um EA, quando a barra zero for alterada. A curva de indicador no EA terá uma forma "amassada".

Na indicadores personalizados, as variáveis 'limit' e "counted_bars" podem ter nomes diferentes, mas o código do programa deve ter essas verificações! Suponho que esta explicação é suficiente para esclarecer alegações de que alguns escritos em EA e em dados MetaTrader de buffer de indicador os mesmos dados recebidos de um indicador personalizado não são idênticos. Se um código de indicador e um código de EA estiverem escritos corretamente, não importa o quão difícil seja o código indicador, os dados serão sempre iguais!

Mas deve-se notar aqui, que alguns algoritmos suaves são sensíveis ao ponto de referência, a partir do qual começa a suavização. Isto é, a fim de ter valores idênticos, os números das barras mais antigas, a partir dos quais o novo cálculo de todas as barras inicia, em ciclos tanto no indicador e no código do indicador no interior do EA devem coincidir.

Aqui está um exemplo explicando este método de otimização de um código indicador para a sua operação mais rápida em um EA. No ciclo principal do indicador mudar de zero para um, depois que o indicador descontinuar para recalcular o seu valor na barra zero.

// instead of
for(int i = limit; i >= 0; i--)      
// write
for(int i = limit; i >= 1; i--)
Como resultado, o código fonte terá a seguinte aparência:
int start()
  {
    int limit;
    int counted_bars = IndicatorCounted();
//---- the last bar will be recalculated
    if(counted_bars > 0) 
        counted_bars--;
    limit = Bars - counted_bars - 1;
      
  //---- the main cycle //now the cycle ends in 1
    for(int i = limit; i >= 1; i--)
      {
        //---- 
        ExtBlueBuffer[i] = iMA(NULL, 0, JawsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
        ExtRedBuffer[i] = iMA(NULL, 0, TeethPeriod, 0, MODE_SMMA, 
                              PRICE_MEDIAN, i);
        ExtLimeBuffer[i] = iMA(NULL, 0, LipsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
      }
  //----
    return(0);
  }

Vou repetir, o método acima é para os EA em funcionamento apenas nas barras fechadas, isto é, em todas as barras exceto a barra zero!

Se você estiver indo utilizar seriamente seu EA em negociações reais e confiar seu dinheiro, você deve verificar cuidadosamente todos os detalhes em seu Expert Advisor, bem como os indicadores com os quais o EA trabalha. Além disso, você deve fazê-lo sozinho! Penso que é muito mais fácil e mais inteligente dedicar alguns dias para uma compreensão completa da estrutura e dos métodos de otimização de códigos do que ter paciência durante durante três meses com um EA que recebe valores de um indicador mal escrito!

Então, deve ficar claro que é preciso sérias razões para a transferência de um código indicador para um código EA. Se um indicador estiver escrito corretamente, a operação do EA não vai ser muito mais lenta. É mais fácil escrever primeiro um código EA utilizando indicadores personalizados e conferir desta forma. Se o EA realmente mostrar resultados perfeitos, o código pode ser ainda mais otimizado, um por um mudando chamadas para indicadores personalizados em fragmentos de códigos indicadores.

E deve-se notar que os valores testados de perda e de lucro não devem ser modificados após a alteração do código EA!

O número de indicadores existentes é muito grande, cada um deles possui seu próprio código. É por isso que dificilmente se pode criar um método universal de transferência de código para todos os indicadores. O problema é agravado pelo fato de que um único e mesmo indicador personalizado pode ser representado em um código EA diversas vezes. Se um código indicador é mais ou menos simples, ele pode ser escrito em uma função personalizada e mudar a função de indicadores personalizados é muito fácil neste caso. Mas muitas vezes em que um código EA alcança tais dimensões, é quase impossível detectar um erro. E todos os nossos esforços são em vão.


Esquema geral de uma estrutura de indicador

Antes de começar a me debruçar sobre o tema principal do artigo, em primeiro lugar, vamos analisar uma estrutura de indicador do ponto de vista de um programador que está interessado no indicador como parte de um futuro código EA:

//+------------------------------------------------------------------+
//|                                                IndicatorPlan.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- drawing the indicator in the main window
#property indicator_chart_window 
//---- number of indicator buffers
#property indicator_buffers 1 
//---- indicator color
#property indicator_color1 Gold
//---- INPUT PARAMETERS OF THE INDICATOR
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- indicator buffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- defining the graph execution style
   SetIndexStyle(0, DRAW_LINE); 
//---- 3 indicator buffers are used for calculation
   IndicatorBuffers(3);
   SetIndexBuffer(0, Ind_Buffer0); 
   SetIndexBuffer(1, Ind_Buffer1);
   SetIndexBuffer(2, Ind_Buffer2);
//---- end of initialization
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation
   if(Bars < period0 + period1 + period2)
       return(0);
//----+ Insertion of variables with a floating point
   double Resalt0, Resalt1, Resalt2;
//----+ Insertion of integer variables and getting calculated bars
   int limit, MaxBar,bar, counted_bars = IndicatorCounted();
//---- checking for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last calculated bar must be recalculated 
   if(counted_bars > 0) 
       counted_bars--;
//---- defining the number of the oldest bar, 
// starting from which new bars will be recalculated
   limit = Bars - counted_bars - 1; 
//---- defining the number of the oldest bar, 
// starting from which new bars will be recalculated
   MaxBar = Bars - 1 - (period0 + period1 + period2); 
//---- initialization of zero 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = Bars - 1; bar >= MaxBar; bar--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ THE FIRST CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt1 calculation based on the external 
       // variable period1
       Ind_Buffer1[bar] = Resalt1;
     }
//----+ THE SECOND CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt2 calculation 
       // based on the values of the buffer Ind_Buffer1[]  
       // and external variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ THE MAIN CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt0 calculation 
       // based on the values of the buffer Ind_Buffer2[]  
       // and external variable0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+

Naturalmente, um indicador real pode ter um número diferente dos valores refletidos pelo indicador, um número diferente de buffers de indicadores utilizados nos cálculo e números diferente de ciclos de valores de cálculo do buffer de indicador, mas isso não modifica o sentido do esquema apresentado. Em outros casos, será absolutamente análogo.

Agora vamos excluir os elementos do esquema que não são interessantes para nós neste contexto e que não são necessários em um Expert Advisor:

//+------------------------------------------------------------------+
//|                                               IndicatorPlan1.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
//---- INPUT PARAMETERS OF THE INDICATOR
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- indicator buffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- end of initialization
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation
   if(Bars < period0 + period1 + period2)
       return(0);
//----+ Insertion of variables with a floating point
   double Resalt0, Resalt1, Resalt2;
//----+ Insertion of integer variables and getting calculated bars
   int limit, MaxBar, bar, counted_bars = IndicatorCounted();
//---- checking for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last calculated bar must be recalculated 
   if(counted_bars > 0) 
       counted_bars--;
//---- defining the number of the oldest bar, 
// starting from which new bars will be recalculated
   limit = Bars - counted_bars - 1; 
//---- defining the number of the oldest bar, 
// starting from which new bars will be recalculated
   MaxBar = Bars - 1 - (period0 + period1 + period2); 
//---- initialization of zero 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = Bars - 1; bar >= MaxBar; bar--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ THE FIRST CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt1 calculation  
       // based on the external variable period1
       Ind_Buffer1[bar]= Resalt1;
     }
//----+ THE SECOND CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt2 calculation 
       // based on the values of the buffer Ind_Buffer1[]  
       // and external variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ THE MAIN CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt0 
       // based on the values of the buffer Ind_Buffer2[]  
       // and external variable period0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+

Este código pode ser facilmente colocado em um Código do Expert Advisor, se não fosse, infelizmente, por um par de pequenos erros:

  1. Primeiro de tudo, não devemos esquecer, que a função IndicatorCounted () não funciona no Expert Advisors!
  2. Também não podemos virar as matrizes personalizadas em direção aos indicadores no bloco de inicialização!

Assim, para uma preservação integral do código indicador, primeiramente, precisamos desenvolver um análogo da função IndicatorCounted () e de alguma forma emular análogos de buffers de indicadores em um Expert Advisor. Infelizmente, é impossível emular diretamente buffers de indicadores em um EA utilizando as funções padrão. Até agora não há análogos de SetIndexBuffer () e IndicatorBuffers() para Expert Advisors! Portanto, este problema deve ser resolvido de outras maneiras. Além disso, para isto, existem opções suficientes no MQL4.

Emulação de Buffers de indicadores no Expert Advisor

Primeiro vamos ver em detalhes os processos que ocorrem em buffers de indicadores.
  1. Valores que são atribuídos às variáveis dos buffers indicadores não são perdidos entre os ciclos, quando o indicador está ligado a um gráfico e um terminal.
  2. Se uma barra zero (a mais recente) for alterada em um gráfico, todos os elementos do buffer do indicador serão deslocados.
  3. Se vier uma barra nova, o valor do limite variável mudará de um para dois, o indicador, e todos os elementos do buffer serão deslocados por um, por exemplo, uma barra zero torna-se a primeira, a primeira torna-se segunda e assim por diante.
  4. Naturalmente as dimensões do buffer de indicador mudam. Se no próximo crédito a barra não mudar, todos os elementos do buffer permanecem no lugar.
Agora vem a emulação dos buffers de indicadores. Para isso, usaremos as seguintes funções padrão do MQL4: ArraySize(), ArrayResize() e ArraySetAsSeries(). O código de emulação dos buffers de indicadores é bastante simples e o princípio de funcionamento pode ser descrito assim: quando a barra zero muda, a ordem direta dos elementos de definição nos buffers é restaurada, novas células são adicionadas nos buffers de novas barras usando a função ArrayResize(), depois que a ordem inversa dos elementos de definição estiver configurada e as células vazias parecerem estar entre as primeiras no buffer de indicador emulado.
//---- INDICATOR BUFFERS EMULATION
  int NewSize = iBars(symbol, timeframe);
  //----  Checking the change of the zero bar
  if(ArraySize(Ind_Buffer0) < NewSize)
    {
      //---- Set the direct indexing direction in the array 
      ArraySetAsSeries(Ind_Buffer0, false);
      ArraySetAsSeries(Ind_Buffer1, false);
      ArraySetAsSeries(Ind_Buffer2, false);
      //---- Change the size of the emulated indicator buffers 
      ArrayResize(Ind_Buffer0, NewSize); 
      ArrayResize(Ind_Buffer1, NewSize); 
      ArrayResize(Ind_Buffer2, NewSize); 
      //---- Set the reverse indexing direction in the array 
      ArraySetAsSeries(Ind_Buffer0, true);
      ArraySetAsSeries(Ind_Buffer1, true);
      ArraySetAsSeries(Ind_Buffer2, true); 
    } 
//----

A propósito, este método de emulação de buffers de indicadores pode também ser usado em indicadores quando oito buffers de indicadores não forem suficientes para cálculos intermediários. Os exemplos estão nos arquivos anexados SMI.mq4 e SMI_New.mq4.


Substituição da função IndicatorCounted()

Agora vamos analisar a emulação da função IndicatorCounted(). Esta função retorna o montante das barras do gráfico atual que não foram alteradas após a última chamada do indicador. Ou podemos dizer isto em outras palavras. Esta função retorna o montante das barras do gráfico atual que estavam disponíveis no terminal do cliente no crédito anterior. Para os valores serem absolutamente idênticos, precisamos subtrair um do montante resultante das barras. Assim, esta função pode ser facilmente substituída por uma variável estática, que será inicializada pelo valor de uma variável pré-determinados Barras-1 após um valor for recebido por ela. Depois disto, o esquema indicador terá a seguinte forma:
//+------------------------------------------------------------------+
//|                                            NewIndicatorPlan1.mqh |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
 
//---- INPUT INDICATOR PARAMETERS
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- indicator buffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation
   if(Bars < period0 + period1 + period2)
       return(0);
//---- INDICATOR BUFFERS EMULATION
   if(ArraySize(Ind_Buffer0) < Bars)
     {
       ArraySetAsSeries(Ind_Buffer0, false);
       ArraySetAsSeries(Ind_Buffer1, false);
       ArraySetAsSeries(Ind_Buffer2, false);
       //----  
       ArrayResize(Ind_Buffer0, Bars); 
       ArrayResize(Ind_Buffer1, Bars); 
       ArrayResize(Ind_Buffer2, Bars); 
       //----
       ArraySetAsSeries(Ind_Buffer0, true);
       ArraySetAsSeries(Ind_Buffer1, true);
       ArraySetAsSeries(Ind_Buffer2, true); 
     } 
//----+ INSERTION OF A STATIC INTEGER MEMORY VARIABLE
   static int IndCounted;
//----+ Insertion of variables with a floating point
   double Resalt0, Resalt1, Resalt2;
//----+ Insertion of integer variables and getting calculated bars
   int limit, MaxBar, bar, counted_bars = IndCounted;
//---- checking for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last calculated bar must be recalculated 
   if(counted_bars > 0) 
       counted_bars--;
//----+ REMEMBERING THE AMOUNT OF ALL BARS OF THE CHART
   IndCounted = Bars - 1;
//---- defining the number of the oldest bar, 
//     starting from which new bars will be recalculated
   limit = Bars - counted_bars - 1; 
//---- defining the number of the oldest bar, 
//     starting from which new bars will be recalculated
   MaxBar = Bars - 1 - (period0 + period1 + period2); 
//---- initialization of zero 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = Bars - 1; bar >= 0; bar--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ THE FIRST CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt1 calculation  
       // based on the external variable period1
       Ind_Buffer1[bar] = Resalt1;
     }
//----+ THE SECOND CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt2 calculation 
       // based on the values of the buffer Ind_Buffer1[] and external variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ THE MAIN CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt0 calculation 
       // based on the values of the buffer Ind_Buffer2[] and external variable period0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+

Transformação adicional do Código Indicador e esquema final de sua estrutura

Claro, nós poderíamos simplesmente transferir o código indicador em partes para um código EA, supondo que precisamos deste indicador em um Expert Advisor para a operação no gráfico atual e apenas uma vez! Se o indicador for usado duas vezes, poderíamos simplesmente mudar, no segundo caso, nomes de todas as variáveis dos indicadores e adicionar o código mais uma vez. Mas neste caso o Expert Advisor ficará mais complicado.

A tarefa de processamento de dados em outros calendários também é facilmente resolvida. Substituir variáveis predeterminadas de modelos de barra por séries cronológicas do modelo
iBars(string symbol,  int timeframe);

NULO - para o símbolo de coda; 0 (em séries cronológicas) - para o calendário; Fechar(barra) - para

iClose(string symbol, int timeframe, bar);

e assim por diante.

Agora vamos analisar as linhas dos indicadores:

//---- checking for possible errors
if(counted_bars < 0)
    return(-1);
//---- the last calculated bar must be recalculated 
if(counted_bars > 0) 
    counted_bars--;

Como para a substituição oferecida da função IndicatorCounted () na nossa variante da estrutura do indicador, o indicador counted_bars nunca será inferior a zero. Assim, as linhas:

//---- checking for possible errors
if(counted_bars < 0)
    return(-1);
no código do Expert Advisor podem ser omitidas. Com as duas linhas seguintes:
//---- the last calculated bar must be recalculated 
if(counted_bars > 0) 
    counted_bars--;

é o mesmo. Eles devem ser deletadas, enquanto elas apenas retardam o trabalho do EA com recálculos desnecessários da primeira barra e na operação do EA essa verificação é absolutamente inútil. Depois disso o código final para a transferência em um EA tem a seguinte forma:

//+------------------------------------------------------------------+
//|                                            NewIndicatorPlan2.mqh |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
 
//---- INPUT INDICATOR PARAMETERS
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- indicator buffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//---- DECLARING VARIABLES FOR CHOOSING A CHART
string symbol; int timeframe;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- CHOOSING A CHART FRO INDICATOR CALCULATION
   symbol = Symbol();//INITIALIZATION OF THE VARIABLE symbol;
   timeframe =240;//INITIALIZATION OF THE VARIABLE timeframe;
//---- end of initialization
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
// GETTING THE AMOUNT OF ALL BARS OF THE CHART
   int IBARS = iBars(symbol, timeframe);
//---- Checking whether the bars number is enough for further calculation
   if(IBARS < period0 + period1 + period2)
       return(0);
// INDICATOR BUFFERS EMULATION
   if(ArraySize(Ind_Buffer0) < IBARS)
     {
       ArraySetAsSeries(Ind_Buffer0, false);
       ArraySetAsSeries(Ind_Buffer1, false);
       ArraySetAsSeries(Ind_Buffer2, false);
       //----  
       ArrayResize(Ind_Buffer0, IBARS); 
       ArrayResize(Ind_Buffer1, IBARS); 
       ArrayResize(Ind_Buffer2, IBARS); 
       //----
       ArraySetAsSeries(Ind_Buffer0, true);
       ArraySetAsSeries(Ind_Buffer1, true);
       ArraySetAsSeries(Ind_Buffer2, true); 
     } 
// INSERTION OF A STATIC INTEGER MEMORY VARIABLE
   static int IndCounted;
//----+ Insertion of variables with a floating point
   double Resalt0, Resalt1, Resalt2;
//----+ Insertion of integer variables and GETTING ALREADY CALCULATED BARS
   int limit, MaxBar, bar, counted_bars = IndCounted;
//----+ REMEMBERING THE AMOUNT OF ALL BARS OF THE CHART
   IndCounted = 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--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ THE FIRST CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt1 calculation based on the external 
       // variable period1
       Ind_Buffer1[bar] = Resalt1;
     }
//----+ THE SECOND CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt2 calculation  
       // based on the values of the buffer Ind_Buffer1[] and the external variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ THE MAIN CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= 0; bar--)
     {
       // Here code of the variable Resalt0 calculation 
       // based on the values of the buffer Ind_Buffer2[] and the external variable period0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+
Aqui devemos levar em consideração um momento. Existem indicadores, que no novo cálculo múltiplo da barra zero, na primeira barra no início dos ciclos de cálculo lembram os valores de algumas variáveis para retornar o código ao estado inicial (Artigo). Em um Expert Advisor, após deletar as duas últimas linhas, essa recordação deve ocorrer na barra zero, e não na primeira barra. Normalmente, tais indicadores contêm no início dos ciclos de cálculo os fragmentos do código seguinte:
// Saving the variables values
if(bar == 1)
    if(((limit == 1) && (time == Time[2])) || (limit > 1))
      {
        time = Time[2];
        // These variables are not interesting for us
        PRICE = price;
        TREND = trend;
        RESALT = Resalt;
      }
//+------------------------------------------------------------------+
Este fragmento deve ser alterado para salvar os valores das variáveis não na primeira barra, mas na barra zero. Todo '1' deve ser substituído por '0', '2' - por '1'. E se o código for para uma operação que não estiver no gráfico corrente, a referência a uma matriz de séries cronológicas deve ser alterada
time = Time[1];

para

time = iTime(symbol, timeframe, 1);

Como resultado, temos o seguinte fragmento alterado:

// Saving variables values
if(bar == 0)
    if(((limit == 0) && (time == iTime(symbol, timeframe, 1))) || (limit > 0))
      {
        time = iTime(symbol, timeframe, 1);
        PRICE = price;
        TREND = trend;
        RESALT = Resalt;
      }
//+------------------------------------------------------------------+

Um código indicador também pode conter funções de suavização como XXXSeries (), desenvolvida por mim. Para utilizar fragmentos com tais funções em um código EA, essas funções devem ser substituídas por seus análogos EA.


Conclusão

Sem dúvida, neste momento o algoritmo oferecido de transferência de um código indicador em um código EA neste formulário é bastante estranho e não é o ideal, mas o objetivo deste artigo não é uma descrição minuciosa de todos os detalhes deste processo. A ideia principal deste artigo é dar uma visão geral dos indicadores e analisar a ideia geral de transferência de código de forma mais simples possível, não sobrecarregando-a com detalhes secundários. Acho que alcançamos esse objetivo! No próximo artigo vamos analisar a estrutura geral de um Expert Advisor e esquemas de estrutura de funções indicadoras.