Transferir el código de un indicador al código de un asesor experto. Diseños estructurales generales de un asesor experto y de funciones de indicador

Nikolay Kositsin | 11 mayo, 2016


Introducción

En el artículo anterior (Transferir el código de un indicador al código de un asesor experto. Estructura del indicador) hemos analizado la estructura general de un indicador cuyo código se pretende transferir al código de un asesor experto y hemos descrito las ideas principales de un ajuste previo del código de un indicador. Ahora vamos a intentar transformar el código obtenido en una función personalizada, ya que es quizás la forma más conveniente de presentar el código de un indicador en un asesor experto. Una función personalizada puede representarse como un archivo mqh y su declaración en un asesor experto usando la directiva #include ocupará muy poco espacio, y llamar a esta función no será mucho más difícil que llamar a un indicador personalizado. Y lo que es más importante, dichas funciones personalizadas pueden ser universales para un uso adicional en cualquier asesor experto.

Antes de empezar a escribir dicha función, vamos a analizar cómo interactuará esta función con la otra parte del asesor experto, con independencia de la estructura interna de la función.

Estructura de un asesor experto con llamada a un indicador personalizado

Vamos a estudiar primero la estructura esquemática de un asesor experto que recibe datos de indicadores personalizados. En este asesor experto estamos en primer lugar interesados solo en la parte que recibe los datos de los indicadores personalizados. Hasta ahora no discutiremos la forma en que el asesor experto procesa estos datos, transfiriéndolos a señales de trading ni la estructura de la parte ejecutiva de un asesor experto. Vamos a analizar. como indicador personalizado en este asesor experto, el que hemos discutido en el artículo anterior. Este es un ejemplo de la estructura de un asesor experto:


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


En este esquema, en cada tick tomamos de un búfer cero del indicador personalizado IndicatorPlan.mq4 los valores contados en dos llamadas y las colocamos en las matrices comunes Ind_Buffer1[] y Ind_Buffer2[]. El esquema de llamada a un indicador se realiza teniendo en cuenta que para los cálculos adicionales necesitaremos solo los cinco últimos valores del indicador, excepto el valor cero.

Estructura de un asesor experto con llamada a la función personalizada
Aunque estamos creando una función de un indicador universal, adecuado para cualquier asesor experto, esta debe enviar los valores recibidos los análogos del búfer de indicador, que almacenará los valores del indicador para todas las barras del gráfico. Por supuesto, podríamos desarrollar una función de indicador y la llamada a la misma sería completamente análoga a la llamada a un indicador personalizado, pero escribir dicha función necesitaría que dedicáramos demasiado tiempo y su código sería bastante largo.

Podemos hacerlo más sencillo. Esta función debe recibir como parámetros de entrada los parámetros de un indicador personalizado y búfer, y debe devolver el mismo búfer con una emulación del modo del indicador, donde las celdas se rellenan con los valores del indicador calculados. Puede hacerse fácilmente declarando en nuestra función una variable externa enlazada por referencia para la función correspondiente a un búfer de indicador. En el lenguaje MQL4, tendrá el aspecto siguiente: double& InputBuffer. La función del indicador debe declararse como lógica, devolviendo "true" si el cálculo tuvo éxito o "false" si el cálculo no tuvo éxito debido a la ausencia de un número adecuado de barras en el gráfico. Después de estas explicaciones, la función del indicador, creada a partir del esquema del indicador y discutida en el artículo anterior, se supone que tiene la forma siguiente:


bool Get_IndSeries(int Number,string symbol, int timeframe, 
                   bool NullBarRecount, int period0, int period1, 
                   int period2, double& InputBuffer0[],
                   double& InputBuffer1[], double& InputBuffer2[])


La función del indicador tiene una variable externa adicional más, Number, que acepta el valor de este número de llamada a la función del indicador.

Es natural que excepto el búfer del indicador InputBuffer0, las variables externas contengan también búferes para los cálculos intermedios InputBuffer1 y InputBuffer2, ya que es bastante problemático incluir estos búferes dentro de la función. Es mejor emular el modo del indicador del funcionamiento de los búferes dentro de la función. No dará ningún problema. Ahora vamos a hacer hincapié en el significado de la variable externa NullBarRecount. En realidad, la mayoría de asesores expertos no necesitan cálculos en la barra cero, y aunque estamos escribiendo un código de la función del indicador universal, este naturalmente recalculará los valores del indicador en la barra cero, lo que puede aumentar sustancialmente el tiempo de ejecución. Al indicar el parámetro externo de la función NullBarRecount como "falso" prohibimos los cálculos en la barra cero, si no es necesario.

Ahora podemos presentar el esquema de la estructura de un asesor experto para llamar a los indicadores en una variante con la llamada a las funciones:

//+------------------------------------------------------------------+
//|                                               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 general de transformación del código de un indicador en una función personalizada

Después de este trabajo preliminar podemos pasar a crear un esquema general de la estructura interna de una función del indicador. Tomemos como base el esquema del último indicador del artículo anterior. No debería haber ninguna dificultad:

1. Tomamos solo el contenido de la función int start();
2. Añadimos la declaración de la función Get_IndSeries():

bool Get_IndSeries(string symbol, int timeframe, bool NullBarRecount,
                   int period0, int period1, int period2, 
                   double& InputBuffer0, double& InputBuffer1, 
                   double& InputBuffer2)



3. Cambiamos los nombres de los búferes del indicador dentro del código (Ind_Buffer) en consecuencia por los nombres del búfer (InputBuffer) de las variables externas de la función Get_IndSeries();
4. Añadimos la declaración de la variable LastCountBar;
5. Comprobamos la veracidad de la variable NullBarRecount:

if(!NullBarRecount)
    LastCountBar = 1;


6. En todos los ciclos del cálculo del indicador cambiamos cero por LastCountBar;
7. Realizamos los cambios al principio del código, cuando comprobamos si el número de barras es suficiente para un cálculo adicional; return (0) en return (false);
8. Al final del código cambiamos return(0) por return(true);

La función del indicador está lista:

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


Creo que si un lector utiliza bastante bien MQL4, después de leer las acciones descritas anteriormente, no tendrá ningún problema a la hora de escribir las funciones del indicador de acuerdo con el esquema indicado.

Ejemplo de escritura de una función de indicador personalizada
Ahora vamos a escribir una función del indicador. Tomemos un indicador lo más simple posible:

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




Algoritmo solución

1. Nos deshacemos de todos los elementos innecesarios en el código del indicador;
2. Escribimos el código de la emulación del búfer del indicador para un único búfer ExtBuffer[];
3. Sustituimos la función IndicatorCounted() por la variable IndCounted;
4. Inicializamos la variable IndCounted por la cantidad de barras del gráfico menos una;
5. Cambiamos la variable predeterminada Bars por la llamada a las series de tiempo iBars(symbol, timeframe);
6. Borramos las comprobaciones innecesarias 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. Dejamos solo el contenido de la función int start(); 2.


8. Añadimos la declaración de la función 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. Sustituimos los nombres del búfer del indicador dentro del código (ExtBuffer) en consecuencia para los nombres del búfer (InputBuffer) de las variables externas de la función Get_RAVISeries();


10. Añadimos la declaración de la variable LastCountBar;
11. Convertimos la variable estática IndCounted en una matriz IndCounted[Number] añadimos un código para cambiar el tamaño de las variables en función del número de llamadas a la función Get_RAVISeries.mqh:

//----+ changing the size of static variables
   if(ArraySize(IndCounted) < Number + 1)
     {
       ArrayResize(IndCounted, Number + 1); 
     }

12. Comprobamos la veracidad de la variable NullBarRecount:

if(!NullBarRecount)
    LastCountBar = 1;


13. En todos los ciclos de cálculo del indicador cambiamos cero en LastCountBar:


for(bar = limit; bar >= LastCountBar; bar--)


14. Realizamos el cambio al principio del código, cuando comprobamos si el número de barras es suficiente para un cálculo adicional; return (0) en return (false);


15. Al final sustituimos return(0) por return(true).


Después de todos los cambios en el código obtenemos la función del 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);
  }
//+------------------------------------------------------------------+



Ciertamente, todo esto está genial. Hábilmente, no demasiado simple. Pero surge una pregunta: ¿calculará esta función de indicador lo mismo que un indicador personalizado?

Prueba de la función del indicador personalizado respecto a la precisión del cálculo
Necesitamos comprobar si los resultados del cálculo de la función son iguales a los resultados del cálculo del indicador personalizado. Para este propósito, el mejor asesor experto es aquel que no opera y que solo recibe valores del indicador personalizado RAVI.mq4 y la función personalizada Get_RAVISeries() encuentra la diferencia después de enviar en un archivo de registro el valor del indicador, el valor de la función personalizada y la diferencia entre ellos. Todo lo que tenemos que hacer es analizar el contenido del archivo de registro para sacar la conclusión final sobre la correspondencia de nuestro algoritmo de la función personalizada Get_RAVISeries() y el 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);
  }
//+------------------------------------------------------------------+


En un probador de estrategias, iniciamos el asesor experto Get_RAVISeriesTest. Naturalmente, el archivo compilado RAVI.ex4 debe ya estar en la carpeta \expert\indicators, y el archivo Get_RAVISeries.mqh en la carpeta \expert \include del terminal de cliente de MetaTrader. En el diario del probador de estrategias y en el archivo de registro vemos dos columnas con los valores del indicador y su análogo en forma de función. La tercera columna muestra la diferencia de estos valores. Todos los valores de la última columna son iguales a cero. Esto significa que los valores son idénticos en ambos casos. Podemos concluir que la tarea de escribir una función personalizada del indicador ha sido resuelta con éxito.



Conclusión

Hemos conseguido resolver la tarea de transferir el código de un indicador desde un indicador personalizado al código de un asesor experto creando uno análogo del indicador como función personalizada universal, que puede ser colocada en un archivo mqh y usarse en el código de cualquier asesor experto de forma análoga a un indicador personalizado.

En el siguiente artículo, dedicado a este tema, analizaremos un ejemplo más complejo de escritura de funciones de este tipo y la implementación de un sencillo asesor experto basado en dichas funciones.