Transferir el código de un indicador al código de un asesor experto. Estructura del indicador

Nikolay Kositsin | 11 mayo, 2016

Introducción


Para comprenderlo mejor, el autor recomienda la lectura del siguiente material:


1. MetaQuotes Software Corp. Features of Custom Indicators Creation.
2. Nikolay Kositsin. Multiple Null Bar Re-Count in Some Indicators.

Antes de comenzar con el tema del artículo indicado en el título, sería conveniente realizar primero la siguiente pregunta: "¿Qué debemos hacer para transferir el código de un indicador al código de un asesor experto, si en la mayoría de casos un asesor experto funciona con indicadores personalizados, parece mucho más fácil que su análogo, que contiene todo lo necesario para el funcionamiento de los indicadores personalizados dentro de su propio código? Especialmente si tenemos en cuenta el hecho de que si un código está escrito correctamente, los resultados en ambos casos serán completamente idénticos.


En mi opinión, es necesario en dos casos:
1. En el cálculo de un asesor experto, los valores calculados en una barra cero no se usan en absoluto, naturalmente nos gustaría omitir un recálculo innecesario de la barra cero y de la primera barra. Esto permite acortar el tiempo de optimización de dicho asesor experto, que es más importante en un código muy complejo e intensivo en recursos.
2. El uso comercial de un asesor experto con la máxima protección de su código frente a la decompilación.

En el segundo caso, la situación es bastante clara y la transferencia del código es muy razonable. Y en el primer caso, en la mayoría de situaciones es mucho más fácil reescribir los códigos de los indicadores personalizados, excluyendo los cálculos innecesarios. Naturalmente, dichos indicadores serán adecuados solo para los asesores expertos y no para el trading. Por tanto, comencemos nuestra discusión de esta variante de la solución del problema.

Ejemplo de optimización del indicador

En primer lugar, me gustaría que prestara atención al siguiente fragmento de código de un 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);
  }

En este caso, para nosotros es importante la siguiente línea:

if(counted_bars > 0) 
    counted_bars--;


El significado de esta comprobación con disminución del valor de la variable 'counted_bars' en uno es el siguiente: si un indicador personalizado no incluye esta línea, puede enviar valores incorrectos desde sus búferes a un asesor experto cuando se cambia la barra cero. La curva del indicador en el asesor experto tendrá una forma bastante "arrugada".

En los indicadores personalizados, las variables 'limit' y 'counted_bars' pueden tener distintos nombres, pero el código del programa deberá tener estas comprobaciones. Supongo que esta explicación es suficiente para aclarar las quejas de algunos escritores de asesores expertos, que en MetaTrader los datos de un búfer de indicador y los mismos datos recibidos de un indicador personalizado no son los mismos. Si los códigos de un indicador y el de un asesor experto se escriben correctamente, no importa cuál sea la dificultad del código del indicador, los datos siempre serán los mismos.

Pero debe recordarse aquí que algunos algoritmos de ajuste son sensibles al punto de referencia a partir del cual comienza el ajuste. Es decir, para tener idénticos valores, los números de las barras más antiguas, a partir de las cuales comienza el recálculo de las barras, en ciclos en el indicador y en el código del indicador dentro del asesor experto deben coincidir.


Este es un ejemplo que explica este método de optimización del código de un indicador para un funcionamiento más rápido en un asesor experto. En el indicador principal el ciclo cambia de cero a uno, después de que el indicador deje de recalcular su valor en la barra cero.

// instead of
for(int i = limit; i >= 0; i--)      
// write
for(int i = limit; i >= 1; i--)

Como resultado, el código fuente tendrá el siguiente aspecto:

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


Lo repetiré, el método anterior es para asesores expertos que solo trabajan en barras cerradas, es decir, en todas las barras menos en la barra cero.

Si va a usar en serio su asesor experto en el trading real y confiar a este su dinero, debe comprobar cuidadosamente todos los detalles de su asesor experto, así como los indicadores con los que trabaja el asesor experto. Además, debe hacerlo usted mismo. Creo que es mucho más fácil y acertado dedicar un par de días a conocer en profundidad la estructura del indicador y los métodos de optimización de su código que usar con paciencia un asesor experto durante tres meses que reciba valores de un indicador escrito toscamente.

Por tanto, debe quedar claro que son necesarias serias razones para transferir el código de un indicador al código de un asesor experto. Si se ha escrito correctamente un indicador, el funcionamiento de un asesor experto no será mucho más lento sin este. Es más fácil escribir primero el código de un asesor experto usando indicadores personalizados y realizar la comprobación de esta forma. Y si el asesor experto muestra realmente resultados perfectos, puede optimizarse más el código uno a uno cambiando las llamadas a los indicadores personalizados a fragmentos de código del indicador.

Y debe recordarse que los valores probados de pérdidas y beneficios no deben modificarse después de cambiar el código del asesor experto.

El número de indicadores existentes es muy grande y cada uno de ellos tiene su código único. Por esa razón es difícil crear un método universal para transferir el código a todos los indicadores. El problema se agrava por el hecho de que los mismos indicadores personalizados pueden estar representados en el código de un asesor experto varias veces. Si el código de un indicador es más o menos simple, puede ser escrito en una función personalizada y cambiar los indicadores personalizados a funciones en tal caso es bastante fácil. Pero con mucha frecuencia el código de un asesor experto alcanza tales dimensiones que es casi imposible detectar un error en él. Y todos nuestros esfuerzos son en vano.


Esquema general de la estructura de un indicador


Antes de comenzar con el tema principal del artículo, analicemos primero la estructura de un indicador desde la perspectiva de un programador que está interesado en el indicador como parte futura del código de un asesor experto:


//+------------------------------------------------------------------+
//|                                                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 Resalt0calculation
       // based on the values of the buffer Ind_Buffer2[]  
       // and external variable0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+

Naturalmente un indicador real puede tener un número distinto de valores del indicador reflejados, distinto número de búferes de indicador, ser usado en cálculos y distinto número de ciclos del cálculo de los valores del búfer del indicador, pero esto no cambia el significado del esquema dado. En otros casos será absolutamente análogo.

Ahora vamos a excluir del esquema elementos que no nos interesan en este contexto y son innecesarios en un asesor experto.


//+------------------------------------------------------------------+
//|                                               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 puede colocarse fácilmente en el código de un asesor experto si no para un parte de pequeños errores lamentables:

1. En primer lugar, no debemos olvidar que la función IndicatorCounted() no funciona en los asesores expertos.
2. Tampoco podemos convertir matrices personalizadas en matrices de indicadores en el bloque de inicialización.

Por tanto, para preservar completamente el código del indicador primero tenemos que desarrollar una función análoga a IndicatorCounted() y de alguna forma emular a búferes de indicadores análogos en un asesor experto. Por desgracia es imposible emular directamente búferes de indicador en un asesor experto usando funciones estándar. Por ahora no hay análogas a SetIndexBuffer() y IndicatorBuffers() para los asesores expertos. Por tanto, este problema debe resolverse usando otros métodos. Además, hay opciones suficientes para ello en MQL4.



Emulación de búferes de indicadores en un asesor experto


En primer lugar vamos a ver en detalle los procesos que tienen lugar en los búferes de indicadores.

1. Los valores asignados a las variables del búfer del indicador no se pierden entre ciclos, cuando el indicador está unido a un gráfico y trabaja un terminal.
2. Si se cambia una barra cero (la última) en un gráfico, se cambian todos los elementos del búfer del indicador.
3. Si llega una nueva barra, el valor del límite de la variable cambia desde uno a dos en el indicador y todos los elementos del búfer se cambian por uno, es decir, la barra cero se convierte en la primera, la primera en la segunda, y así sucesivamente.
4. Naturalmente, las dimensiones del indicador del búfer cambian. Si con el nuevo tick no cambia la barra, todos los elementos del búfer permanecen en sus lugares.
//---- 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); 
    } 
//----

Ahora viene la emulación de los búferes de los indicadores. Para este propósito usaremos las siguientes funciones estándar de MQL4: ArraySize(), ArrayResize() y ArraySetAsSeries(). El código de la emulación de los búferes de los indicadores es bastante simple y su principio de funcionamiento puede describirse: cuando cambia la barra cero, el orden directo de la definición de los elementos en los búferes se restaura, se añaden nuevas celdas a los búferes a partir de nuevas barras usando la función ArrayResize() y posteriormente se establece el orden inverso de la definición de los elementos en los búferes y las celdas vacías parecen estar entre las primeras en el búfer del indicador emulado.




A propósito, este método de emulación de los búferes de indicador puede usarse también en los indicadores cuando no son suficientes ocho búferes de indicador para los cálculos intermedios. Los ejemplos están en el archivo adjunto SMI.mq4 y SMI_New.mq4.




Sustitución de la función IndicatorCounted()


A hora vamos a analizar la emulación de la función IndicatorCounted(). Esta función devuelve la cantidad de barras del gráfico actual que no han cambiado después de la última llamada del indicador. O dicho con otras palabras. Esta función devuelve la cantidad de barras del gráfico actual que estaban disponibles en el terminal del cliente en el tick previo. Para que los valores sea completamente idénticos necesitamos restar uno de la cantidad resultante de barras. Por tanto, esta función puede sustituirse fácilmente por una variable de entero estática que será inicializada por el valor de una variable predeterminada Bars-1 después de recibir de esta un valor. Después de esto, el esquema del indicador tendrá la forma siguiente:

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

Transformación adicional del código del indicador y esquema final de su estructura

Por supuesto, podríamos simplemente transferir el código del indicador por partes en el código de un asesor experto, suponiendo que necesitemos un indicador en un asesor experto para el funcionamiento en el gráfico actual y solo una vez. Si se usa el indicador dos veces, podríamos simplemente cambiar en el segundo caso los nombres de todas las variables del indicador y añadir el código una vez más. Pero en este caso el asesor experto será más complicado.

La tarea de procesar los datos y demás períodos de tiempo se resuelve también fácilmente. Sustituir las variables predeterminadas del tipo Bars por series de tiempo del tipo

iBars(string symbol,  int timeframe);


NULL - para el símbolo string;, 0 (en series de tiempo) - para el período de tiempo int;, Close[bar] - para


iClose(string symbol, int timeframe, bar);


y así sucesivamente.

Ahora vamos a analizar las líneas del indicador:


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


Al igual que en la sustitución ofrecida para la función IndicatorCounted() en nuestra variante de la estructura del indicador, el indicador counted_bars nunca será inferior a cero. Por tanto, las líneas:


//---- checking for possible errors
if(counted_bars < 0)
    return(-1);


en el código del asesor experto pueden omitirse. Con las siguientes dos líneas:


//---- the last calculated bar must be recalculated 
if(counted_bars > 0) 
    counted_bars--;


es lo mismo. Deben borrarse, aunque solo ralentizan el trabajo del asesor experto mediante recálculos innecesarios de la primera barra y en la operación del asesor experto esta comprobación es absolutamente inútil. Después de eso, el código final para transferir a un asesor experto tiene la forma siguiente:


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

Aquí debemos tener en cuenta un momento. Hay indicadores que en el recálculo múltiple de la barra cero, en la primera barra al comienzo de los ciclos de cálculo recuerdan los valores de algunas variables para devolver el código a su estado inicial (Artículo). En un asesor experto, después de borrar las últimas dos líneas este recordatorio debe producirse en la barra cero y no en la primera. Normalmente dichos indicadores contienen al comienzo de los ciclos de cálculo los siguientes fragmentos de código:

// 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 debe modificarse para guardar los valores de la variable no en la primera, sino en la barra cero. Todos los '1' deben sustituirse por '0' '2' - por '1'- Y si el código está diseñado para un funcionamiento no en el gráfico actual, la referencia a una matriz de series de tiempo debe cambiarse también

time = Time[1];


en

time = iTime(symbol, timeframe, 1);


Como resultado, obtendremos el siguiente fragmento cambiado:

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


El código de un indicador también contiene las funciones de ajuste como XXXSeries(), desarrolladas por mí. Para usarlos en el código de un asesor experto los fragmentos con dichas funciones, estas deben sustituirse por sus asesores expertos análogos.



Conclusión


Sin duda, en este momento el algoritmo ofrecido para transferir el código de un indicador en el de un asesor experto de esta forma es bastante torpe y poco óptimo, aunque el propósito de este artículo no es una descripción minuciosa de todos los detalles de este proceso. La idea principal de este artículo es dar una visión general de los indicadores y analizar la idea general de la transferencia del código en una forma lo más simple posible, sin sobrecargarlo con detalles secundarios. Creo que hemos logrado este propósito. En el próximo artículo analizaremos la estructura general de un asesor experto y los esquemas de estructura de las funciones del indicador.