Descargar MetaTrader 5

Lenguaje MQL4 para principiantes. Indicadores personalizados (Primera parte)

30 marzo 2016, 15:47
Antoniuk Oleg
0
918

Introducción

Este es el cuarto artículo de la serie "El lenguaje MQL4 para Principiantes". Hoy vamos a aprender a escribir indicadores personalizados. Vamos a familiarizarnos con la clasificación de las funciones del indicador, veremos cómo estas características influyen en el indicador, aprenderemos nuevas funciones y la optimización y finalmente, vamos a escribir nuestros propios indicadores. Además, al final del artículo podrá encontrar consejos sobre el estilo de programación. Si este es el primer artículo "para principiantes" que está leyendo, quizá sería mejor que leyera los anteriores. Además, asegúrese de que ha entendido correctamente el material anterior, porque en este artículo no se explican los conceptos básicos.


Tipos de indicadores

Ahora le voy a mostrar, qué tipos de indicadores existen. Seguro que ha visto muchos más, pero ahora me gustaría llamar su atención sobre las características y parámetros de los indicadores, para lo cual haremos una pequeña clasificación de características y parámetros. Esto le ayudará luego a escribir indicadores personalizados. Así que, el primer indicador simple:

Este es el Moving Average, MA (Promedio móvil), un indicador técnico ampliamente utilizado. Preste atención a los siguientes datos importantes:

  • el indicador está dibujado en la ventana del gráfico
  • el indicador muestra solamente un valor
  • el rango de los valores del indicador es ilimitado y depende de los precios actuales.
  • la línea se dibuja con un determinado color, grosor y estilo (línea continua)

Ahora vamos a ver otro indicador:

Es el Williams’ Percent Range, %R. Preste atención a los siguientes datos importantes:

  • el indicador está dibujado en una subventana separada
  • como en el caso anterior, el indicador muestra solamente un valor
  • el rango de los valores del indicador está estrictamente limitado
  • la línea dibujada tiene otro estilo, color y grosor

Por lo tanto, tenemos las siguientes propiedades del indicador:

  • el indicador está dibujado: en la ventana del gráfico o en una subventana separada. Ahora trataremos de comprender, por qué el Promedio móvil está dibujado en el gráfico, y Williams’ Percent Range, %R está dibujado en una ventana separada. La diferencia está en el rango de los valores mostrados. Tenga en cuenta que el segundo indicador muestra valores en el rango de 0 a -100. Ahora imagine que mostramos estos valores en una ventana gráfica. ¿Qué pasaría? No se vería esta línea, porque el precio tiene un rango mucho más reducido. En nuestro caso es de 0.6805 a 0.7495. Pero no es todo. En realidad, los precios son números positivos, y nuestro valor es negativo. Los indicadores se dibujan en una subventana separada si sus valores están fuera del rango de precios del gráfico activo. Y si el rango es casi el mismo (por ejemplo, diferentes tipos de Promedios móviles), se dibuja un indicador en la ventana gráfica. En el futuro, establezca el parámetro de este indicador según esta sencilla lógica. Fíjese en esta figura:
  • un indicador que se dibuja en una subventana separada puede estar limitado a un rango estricto. Esto significa que el terminal establece una escala fija para mostrar los valores del indicador; e incluso si los valores superan el rango, no los verá. Si se desactiva este parámetro, el terminal cambiará automáticamente la escala de forma que contenga todos los valores de un indicador. Vea la figura:

  • un indicador puede mostrar sus valores usando diferentes colores, estilos y grosores. Lo ha visto muy a menudo a la hora de configurar el dibujo de los indicadores en el terminal. Si utiliza una línea con un grosor superior a 1, sólo podrá utilizar un estilo; la línea continua.

Este es otro indicador:



Como puede ver, el indicador de Volumes (volúmenes) se dibuja en forma de histograma. Así que se pueden mostrar los valores del indicador de distintas maneras. Este es un ejemplo de otro tipo:



Se dibuja el indicador Fractals (Fractales) en forma de símbolos especiales. Ahora observe el siguiente indicador:



Este es el Alligator (cocodrilo). Tenga en cuenta que el indicador dibuja simultáneamente tres valores (líneas de balance). ¿Cómo funciona? En realidad, cualquier indicador (hay algunas excepciones, pero hablaremos de ellas más adelante) utiliza buffers de datos cuando se muestran los valores.

El buffer de datos es casi una simple matriz. Su peculiaridad consiste en el hecho de que esta matriz está parcialmente controlada por el terminal. El terminal modifica la matriz, de modo que en el momento de la recepción de cada nueva barra, se produce un cambio. Se hace con el fin de que cada elemento de la matriz corresponda a una barra determinada. El número máximo de buffers de datos que se muestran en un indicador es 8. Ahora puede parecer extraño, pero pronto comprenderá que no podía ser de otra manera. Recuerde que sólo hay un buffer de datos independiente para cada línea del Alligator. Cada buffer de datos tiene sus propios parámetros, a partir de los cuales el terminal los dibuja. En nuestro caso hay 3 buffers que se pueden describir de la siguiente forma:

  1. El primer buffer se dibuja con una línea continua de color verde y 3 de grosor.
  2. El segundo buffer se dibuja con una línea discontinua de color rojo y 1 de grosor.
  3. El tercer buffer se dibuja con una línea continua de color azul y 2 de grosor.

El indicador no necesita dibujar el buffer. Se puede utilizar para los cálculos intermedios. Es por esto que el número de buffers puede ser mayor de lo que usted ve. Pero la propiedad más importante del buffer de datos es que cada elemento del buffer debería corresponder a una determinada barra en un gráfico. Sólo recuerde esto. Pronto verá cómo funciona esto en un código.

Ahora vamos a sacar una conclusión de lo que hemos visto. Cualquier indicador tiene los siguientes parámetros:

  • uno o más buffers de datos (aunque no necesariamente) para mostrar sus valores o para cálculos intermedios. Cada buffer, tiene a su vez sus propios parámetros que definen como se dibujará y si se dibujará. Por ejemplo: dibujar el valor en forma de histograma, símbolo o línea; qué color y estilo;
  • dónde debería dibujarse el indicador (en una ventana gráfica o en una subventana);
  • si el indicador se dibuja en una subventana, deberíamos limitar el rango o la escala debería ser automática.

Asegúrese de comprender adecuadamente estos parámetros. Ahora usaremos un Asistente para crear un indicador personalizado.


Crear un indicador personalizado

Inicie MetaEditor, seleccione Archivo->Nuevo:



A continuación, se abre la ventana del Asistente (Expert Advisor Wizard), seleccione Indicador personalizado, haga clic en Siguiente:



Rellene los campos Nombre, Autor y Enlace. Todo es como de costumbre aquí, pero ahora se pueden añadir parámetros. ¿Esto qué es?

Los parámetros son variables comunes que pueden ser configuradas por el usuario. Y lo que es importante, estas variables pueden usarse en el código de un indicador. La aplicación de los parámetros es evidente; permite a los usuarios configurar algunos aspectos del funcionamiento del indicador. Esto puede ser cualquier cosa que queramos. Por ejemplo, el período de tiempo que vamos a utilizar, el modo de funcionamiento, el número de barras para calcular el promedio, etc.

Como ejemplo vamos a intentar añadir un parámetro que mostrará el número de barras procesadas para el cálculo de los valores del indicador. ¿Dónde se puede utilizar? Imagine que su indicador carga seriamente su procesador debido a demasiados cálculos. Y que a menudo cambia el período de tiempo del gráfico y ve sólo las últimas 100-200 barras. Entonces, no necesita otros cálculos que pierdan el tiempo. Este parámetro le ayudará en este caso. Por supuesto, no habrá nada complicado en nuestro indicador que pueda desperdiciar los recursos del ordenador. Esta es sólo una variante del uso de los parámetros del indicador.

Por lo tanto, para añadir un parámetro haga clic en Añadir (1). Después de esto, puede cambiar el nombre de una variable (2). En nuestro caso tenemos un sustituto para barsToProcess. También puede cambiar el valor inicial (3), es decir, el valor predeterminado. Cámbielo a 100. Además, puede cambiar el tipo de variable, pero en nuestro caso no necesitamos cambiar nada, porque el tipo int se adapta perfectamente a nuestros propósitos. Después de realizar todos los cambios necesarios, haga clic en Siguiente:



Ya está casi listo. Ahora indique cómo debería dibujarse el indicador. en una ventana separada o en la ventana del gráfico. También puede limitar el rango. Compruebe el indicador de la ventana separada.. A continuación el campo Índices (buffers de datos) está vacio. Aquí puede añadir el número de buffers de datos necesarios (máximo 8). Además, siempre puede añadir o eliminar un buffer más adelante, cambiando el código. Haga clic en Añadir para añadir un buffer. Ahora puede cambiar la manera en la que se dibuja el buffer: línea, histograma, sección, flecha. No cambiaremos nada, así que nuestro tipo es la línea. Ajuste el color y haga clic en Aceptar.

Finalmente, ¡su primer indicador está listo! Bueno, ¡no se dibuja nada, pero es un código! El archivo con el código fuente está en la carpeta con los indicadores: MetaTrader4\experts\indicators.


Vamos a analizar cada línea

Veamos ahora lo que ha creado MetaEditor:

//+------------------------------------------------------------------+
//|                                             myFirstIndicator.mq4 |
//|                                                     Antonuk Oleg |
//|                                                   banderass@i.ua |
//+------------------------------------------------------------------+

Como de costumbre la cabecera consta de una línea de comentarios que incluye la información que ha escrito anteriormente. Siguiente:

#property copyright "Antonuk Oleg"

¿Todavía se acuerda de la directiva del preprocesador #define del segundo artículo? La hemos usado para declarar las constantes. Está es una directiva más que se usa para denotar las propiedades específicas de un indicador. En nuestro caso se usa para indicar la autoría. Tenga en cuenta que empieza con el signo especial #, seguida de la palabra clave property (sin espacios). Luego viene una propiedad concreta que queremos establecer, en nuestro caso es copyright y después el valor de esta propiedad. En nuestro caso es una línea con su nombre. Usando la directiva #property puede configurar muchos aspectos específicos del indicador. Lo verá ahora. Todas estas propiedades se establecerán de forma predeterminada. Vayamos más lejos:

#property link      "banderass@i.ua"

Esta directiva muestra, cómo ponerse en contacto con el autor. Se puede preguntar dónde se encuentra esta información (el nombre del autor y la información de contacto), ya que no aparece en ninguna parte. Pero se incluye en el archivo ejecutable. Y si ve el archivo ejecutable como un texto común, verá esta información:

Siguiente:

#property indicator_separate_window

Esta directiva indica que el indicador debe dibujarse en una subventana separada. Como puede ver, no hay parámetros adicionales, a diferencia de la anterior directiva.

#property indicator_buffers 1

Esta directiva indica cuántos buffers de datos usará el indicador. Debe haber notado que las directivas son de alguna manera similares a las funciones comunes: ellas también aceptan algunos parámetros y hacen algo en respuesta a ellos. Pero hay una diferencia importante: se ejecutan en primera instancia (antes de la compilación).

#property indicator_color1 DarkOrchid

Indique color predeterminado para el primer buffer. Tenga en cuenta que la numeración del buffer empieza desde uno, no desde cero. Intente recordarlo, para que no tenga ninguna confusión en el futuro. El color se indica usando uno de los muchos nombres predeterminados. Puede ver las palabras clave para todos los colores disponibles en la ayuda: MQL4 Reference -> Standard Constants -> Web-Colors. Asimismo, puede indicar el color para otros buffers simplemente cambiando el número del buffer.

extern int       barsToProcess=100;

Este es nuestro parámetro del indicador. Lo hemos establecido en el Asistente. Tenga en cuenta que la única diferencia entre una variable común es la palabra clave extern antes del tipo de variable. Así es cómo se mostrará el parámetro al usuario en el inicio del indicador:



Siguiente:

double ExtMapBuffer1[];

Esta es una matriz habitual. Pero las dimensiones no están indicadas y no se realiza la inicialización. Se configurará esta matriz más tarde como un buffer de datos.

Después, declaramos y describimos las funciones. A diferencia de un script habitual, cada indicador tiene 3 funciones, no 1:

  • init() - se llama a esta función por el terminal sólo una vez, cuando iniciamos el indicador. Su finalidad es preparar el indicador para el funcionamiento, configurar los buffers de datos, comprobar los parámetros (lo que ha escrito el usuario) y otras acciones preparatorias. Esta función no es obligatoria. Si no realiza escribe código en ella, puede eliminarla.
  • deinit() - se llama también a esta función sólo una vez, cuando se elimina un indicador de un gráfico. Debería preparar el indicador para la finalizar de su funcionamiento. Por ejemplo, cerrar los archivos abiertos, eliminar objetos gráficos desde el archivo (no se preocupe, aprenderá cómo hacerlo). Esta función tampoco es obligatoria.
  • start() - a diferencia de los scripts, en los indicadores se llama a esta función en cada punto. Es decir, cuando aparecen nuevas cotizaciones del par de divisas, con el gráfico que se haya adjuntado el indicador, se llama a esta función. Además, se llama a esta función en el indicador de inicio, es decir, después de la función init().

Veamos qué sucede en cada función:

int init()
{
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
 
   return(0);
}

Aquí vemos la llamada de dos funciones importantes para establecer un buffer de datos:

SetIndexStyle(0,DRAW_LINE);

Esta función define cómo dibujar el buffer de datos. El primer parámetro indica, a qué buffer debería aplicarse el cambio. Por favor, tenga en cuenta que en esta función (y otras funciones similares) la numeración del buffer empieza desde cero, no desde uno como en las directivas. Es un momento importante, así que tenga cuidado. El segundo parámetro indica, cómo dibujar el buffer escogido. En nuestro caso, utilizamos la constante DRAW_LINE, lo que demuestra que el buffer se dibuja como una línea. Por supuesto, hay otras constantes, pero hablaremos de ellas más adelante.

SetIndexBuffer(0,ExtMapBuffer1);

Esta función "vincula" una matriz a un número de buffer. Es decir, demuestra que el buffer con el número indicado usará la matriz indicada para almacenar datos. Así, cambiando los elementos de esta matriz se cambiará el valor del buffer. En realidad una matriz es un buffer de datos. El primer argumento es el nombre de la matriz que se debería vincular.

return(0);

Final de la función, retorno a cero - la inicialización se ha realizado con éxito.

int deinit()
{
//----
   
//----
   return(0);
}

La función de deinicialización está vacía por defecto.

int start()
{
   int counted_bars=IndicatorCounted();
//----
   
//----
   return(0);
}

Ahora viene la función más importante; el código principal se encuentra aquí. Preste atención: se declara la variable counted_bars con antelación, se inicializa mediante la función IndicatorCounted(). Esta variable se usa generalmente para la optimización y aceleración de funcionamiento del indicador, la analizaremos más adelante. Y ahora vamos a dibujar algo en la ventana del indicador.



Final de la tarea del indicador

Decidamos lo que se debe mostrar. ¿Qué nos mostrará el indicador? Algo simple. Primero vamos a dibujar números aleatorios. ¿Por qué no? Esto garantiza un 50% de las señales con beneficios.

Escribamos en nuestra función init() un código para la inicialización del generador de números aleatorios:

int init()
{
 
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
 
   // initialization of the generator of random numbers
   MathSrand(TimeLocal());
 
   return(0);
}

La inicialización está lista, ahora viene la función start():

int start()
{
   int counted_bars=IndicatorCounted();
 
   for(int i=0;i<Bars;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

Compilar - F7. Inicie el terminal, encuentre el panel Navigator, seleccione la sección Indicadores Personalizados y haga doble clic en el nombre de nuestro indicador:

El indicador estará adjunto al gráfico activo:

Usted ve, todos los trabajos. Veamos ahora qué hace el código:

for(int i=0;i<Bars;i++)

Utilizamos el bucle para recorrer todos los elementos del bufer de datos. Como una determinada barra corresponde a cada elemento del buffer, usamos el bucle, empezando desde la barra cero (la última disponible) y acabar con la primera disponible, que se encuentra en sucesión una menos que la variable Barras (porque contamos barras desde cero).

{
   ExtMapBuffer1[i]=MathRand()%1001;
}

En cada iteración de un contador se incrementa en uno, y nos movemos desde la última barra disponible a la primera asignando al mismo tiempo a cada elemento del buffer (que corresponde a una determinada barra) un número aleatorio de 0 a 1000. Si que es difícil de entender cómo un determinado elemento del buffer corresponde a una barra determinada, pruebe a cambiar el bucle de la siguiente manera y vea el resultado en el terminal:

for(int i=0;i<Bars;i++)
{
   ExtMapBuffer1[i]=i;
}

Ahora el indicador mostrará el número de cada barra, mire:



Como ve, el número de barras aumenta desde la última barra a la primera (de 0 a Bars). Espero que ahora entienda la correspondencia de los elementos del buffer con las barras en el gráfico.

Ahora volvamos al código del indicador "aleatorio". Si lo usa al menos varios minutos, podría ver que cada tick del indicador dibuja un gráfico completamente distinto. Es decir, cada tick hace nuevos cálculos de lo que ya estaba calculado la vez anterior. Esto es un inconveniente para nosotros porque no podemos ver incluso lo que ocurrió en un tick anterior. Pero no importa, porque nadie usará este indicador; estamos simplemente aprendiendo a escribirlo. Hay una cosa más. Imagine que su indicador realiza un montón de cálculos complejos y el cálculo de una barra necesita grandes recursos del procesador. En tal caso, si aparece un precio nuevo, su indicador calculará el valor de cada barra disponible, incluso si se hizo antes. ¿Está claro? En lugar de calcular sólo una vez, lo calculará otra y otra vez. Eliminar estos problemas relacionados con el desperdicio injustificable de los recursos se llama optimización.

¿Cómo podemos solucionar este problema? Generalmente se hace de la siguiente forma. Primero se calcula un indicador sobre todas las velas disponibles, y entonces solamente cuando se reciben las cotizaciones, hará nuevos cálculos del valor solamente para la última vela. Esto es razonable; no hay acciones innecesarias. Ahora vamos a optimizar la función start(), para que funcione de la siguiente forma:

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
  
   for(int i=0;i<limit;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

Vamos a analizar cada línea:

int counted_bars=IndicatorCounted(),

Declaramos la variable counted_bars que almacenará el número de barras calculadas por el indicador. La función IndicatorCounted() devuelve el número de barras sin cambios después de la llamada anterior de la función start(). Por lo tanto, si es la primera llamada a start(), IndicatorBars() devolverá 0, ya que todas las barras son nuevas para nosotros. Si no es la primera llamada, el cambio se produce sólo en la última barra, por lo tanto, IndicatorBars() devolverá un número igual a Bars-1.

limit;

Esta es otra una variable que se usará como un limitador, es decir, ayudará al bucle para que se complete antes, omitiendo las velas ya calculadas.

   if(counted_bars>0)
      counted_bars--;

Como ya se dijo, si IndicatorCounted() devuelve 0, se llama a la función start() por primera vez y todas las barras son "nuevas" para nosotros (no se ha calculado el indicador para ellas). Pero si no es la primera llamada a start(), se devuelve el valor igual a Bars-1. Por lo tanto, esta condición hace el seguimiento de la situación. Después de ésto, restamos 1 a la variable counted_bars. Solamente se puede cambiar la última barra, entonces, ¿porqué hacemos esto? El hecho es que hay algunas situaciones, cuando el último tick de la barra anterior se queda sin procesar, ya que al llegar el último tick se había procesado el penúltimo tick. No se hizo una llamada al indicador personalizado ni se ha calculado. Es por eso que restamos 1 a la variable counted_bars, para eliminar esta situación.

limit=Bars-counted_bars;

Aquí asignamos a la variable limit (el limitador) el número de las últimas barras que hay que volver a calcular. Mientras que la variable counted_bars almacena el número de velas calculadas, simplemente averiguamos la diferencia entre la Bars (el número total de barras disponibles) y counted_bars para definir cuántos gráficos de velas hay que calcular.

for(int i=0;i<limit;i++)
{
   ExtMapBuffer1[i]=MathRand()%1001;
}

El bucle en sí no cambió prácticamente. Cambiamos simplemente la condición de implementación. Ahora se llevará a cabo el bucle mientras que el contador i sea inferior al limit.

Ahora la optimización ha terminado. Si observa la versión actualizada del indicador, verá que cuando se recibe un nuevo tick, cambia solamente el valor de la última barra. Intente siempre usar una optimización, incluso si los cálculos de su indicador no son para nada complicados. Esto es alta tonelada.

¿Recuerda el parámetro del indicador barsToProcess que añadimos en el Asistente? Ya es el momento de usarlo. Necesitamos simplemente añadir un par de líneas antes del bucle:

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
   
   if(limit>barsToProcess)
      limit=barsToProcess;
  
   for(int i=0;i<limit;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

Como ve, todo es bastante simple. Comprobamos si limit es superior a barsToProcess. Si lo es, disminuimos el limitador a través de la asignación. Como resultado, si establecemos barsToProcess=100, verá un dibujo como este:

Como ve, sólo se calcula el número de barras que hemos establecido.

Nuestro indicador está casi listo. Pero no tenemos señales claras para entrar al mercado. Así que necesitamos añadir más certeza. Para este propósito usaremos niveles.

Los niveles son líneas horizontales dibujadas por el indicador usando estilo, color y grosor determinados. Cabe señalar aquí que el número máximo de niveles de una barra es 8. Además puede ajustar los niveles usando directivas o funciones. Si quiere ajustar los niveles por defecto es preferible usar la primera opción. Para el cambio dinámico de los niveles durante el funcionamiento del indicador use las funciones. Así que vamos a ajustar dos niveles: el primero en el punto 800 y el segundo en 200. Para este fin, vamos a añadir varias directivas al principio del código del indicador:

//+------------------------------------------------------------------+
//|                                             myFirstIndicator.mq4 |
//|                                                     Antonuk Oleg |
//|                                                   banderass@i.ua |
//+------------------------------------------------------------------+
#property copyright "Antonuk Oleg"
#property link      "banderass@i.ua"
 
#property indicator_level1 800.0
#property indicator_level2 200.0
#property indicator_levelcolor LimeGreen
#property indicator_levelwidth 2
#property indicator_levelstyle 0
 
#property indicator_separate_window

Vamos a analizar las nuevas directivas:

#property indicator_level1 800.0

Esta directiva muestra que el nivel 1 debería colocarse en el punto 800.0. Preste atención a que la numeración del buffer empieza con 1, como en las directivas de configuración del buffer. Para ajustar otro nivel, cambie simplemente el número del nivel al final de una directiva:

#property indicator_level2 200.0

Hay una limitación importante para ajustar la forma externa de los niveles. No puede ajustar cada nivel individualmente Todos los ajustes se aplican absolutamente a todos los niveles. Si necesita ajustar individualmente cada nivel, debería usar objetos (y no usar niveles para nada) que se describen en el siguiente artículo.

#property indicator_levelcolor LimeGreen

Esta directiva define el color que se usará para dibujar todos los niveles.

#property indicator_levelwidth 2

Esta directiva define la grosor que se usará para dibujar las líneas de todos los niveles. Puede establecer el grosor entre 1 y 5. No olvide que si el grosor es superior a 1, los niveles se dibujarán en una línea continua. Si necesita otro estilo para dibujar niveles, use sólo el grosor 1.

#property indicator_levelstyle STYLE_SOLID

Esta directiva ajusta el estilo para dibujar las líneas. Existen las siguientes constantes predefinidas:

  • STYLE_SOLID - línea continua
  • STYLE_DASH - línea discontinua
  • STYLE_DOT - línea de puntos
  • STYLE_DASHDOT - línea de puntos y discontinua
  • STYLE_DASHDOTDOT - línea de puntos dobles y discontinua


Hemos terminado con el desarrollo de nuestro indicador "aleatorio". Ahora vamos a guardar el archivo del código fuente con un nombre más apropiado - randomIndicator.mq4. Vuelva a compilar el archivo del código fuente una vez más. Este indicador se usará también en la siguiente parte. La versión final debería tener este aspecto:



La función iCustom

Ahora detengámonos en una función muy útil - iCustom. Se usa para obtener valores de cualquier indicador personalizado. Recuerde, para incorporar los indicadores podemos utilizar funciones para trabajar con los indicadores técnicos del artículo anterior (por ejemplo: iADX(), iMACD etc.). Para todos los otros indicadores (indicadores personalizados) use la función iCustom. Esta función es universal y puede aplicarse a cualquier indicador personalizado que cumpla los siguientes requisitos:

  • el indicador se compila y se presenta en forma de un archivo ejecutable (*.ex4);
  • el indicador se encuentra en la carpeta MetaTrader 4\experts\indicators.

El prototipo de la función tiene el siguiente formato:

double iCustom( string symbol, int timeframe, string name, ..., int mode, int shift);

Parámetros:

  • symbol – define cuál va a ser el instrumento financiero (par de divisas) que se va a utilizar para el cálculo de los valores del indicador personalizado. Use NULL (o 0), si necesita el instrumento financiero (gráfico) actual (activa).
  • timeframe – define en qué período de tiempo (período) debería usarse el indicador. Use 0 para el período actual o una de las constantes (PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1).
  • name – el nombre del archivo ejecutable del indicador personalizado. Debería indicarse solamente el nombre: no escriba la extensión (.ex4) o la ruta de acceso al archivo (experts/indicators/). Por ejemplo, si el nombre del archivo ejecutable del indicador personalizado es "RandomIndicator.ex4", debe escribir "RandomIndicator". Las mayúsculas y minúsculas no son relevantes. Eso significa que puede escribir "RANDOMindicator" y funcionará.
  • ... – aquí hay que indicar todos los valores de los parámetros del indicador personalizado. Por ejemplo, en nuestro indicador RandomIndicator sólo hay un parámetro; barsToProcess. En nuestro caso escribimos aquí 100 (o cualquier otro valor que le conviene). Si hay más de un parámetros, se indican en la misma sucesión que se declaran en el indicador personalizado, separados por comas. Ahora intentaremos escribir un indicador basado en esta función y lo entenderá mejor.
  • mode – el modo de funcionamiento del indicador personalizado. En realidad es el número del buffer de datos, el valor que queremos obtener. La numeración comienza desde cero (no como en las directivas). Si el indicador personalizado sólo tiene un buffer de datos, este parámetro debería ser igual a 0.
  • shift – define con que barra hay que usar el indicador personalizado.

Ejemplos de Uso:

ExtMapBuffer[0]=iCustom(NULL,PERIOD_H1,"Momentum",14,0,0);
 
// assign to the first element of the array ExtMapBuffer the value of the custom 
// indicator Momentum on the last available bar. We use here the active 
// security on hour chart. The name of the executable file: Momentum. 
// This indicator has only one parameter - period. In our case the period 
// is equal to 14. This indicator has only one data buffer, so we use zero, 
// in order to get access to its values.
double signalLast=iCustom("EURUSD",PERIOD_D1,"MACD",12,26,9,1,0);
 
// declare a new variable signalLast and assign to it the value of the custom 
// indicator индикатора MACD on the last available bar. We use the pair EURUSD on 
// a daily chart. The name of the executable file: MACD. This indicator has 3 parameters: 
// period for quick average, period for slow average and period for a signal line. 
// This indicator also has 2 data buffers. The first one is with values of the main line. The second one 
// with values of a signal line. In our case we take the value of the signal line.

Indicador de señal

Ahora escribiremos un indicador más sencillo. Imagínese la siguiente situación. Ha escrito un indicador bastante complejo con muchos buffers de datos. Muchos de ellos se muestran en una ventana separada, otros se usan para cálculos intermedios. Usted conoce exactamente las señales para comprar y vender. Pero es muy difícil rastrear las señales. Necesita mirar constantemente a su pantalla, intentando encontrar líneas que se cruzan, que están por encima de los niveles o por debajo de ellos. Por eso ha decidido escribir otro indicador que podría hacerlo por usted y que sólo le muestre las señales de entrada. Por ejemplo, estas señales podrían ser flechas que muestren en qué dirección debería abrir posiciones. Esto es sólo una idea que muestra dónde sería apropiado un indicador de señal. Nuestro caso es mucho más fácil, pero aún así es parecido al primero.

Escribiremos un indicador de señal basado en el indicador anterior RandomIndicator. En primer lugar necesitamos definir las condiciones de entrada; aquí necesitaremos nuestros niveles. Por lo tanto, las condiciones serán las siguientes:

  • si una línea se mueve por encima del nivel superior (800.0), comprar
  • si una línea se mueve por debajo del nivel inferior (200.0), vender

Ahora ya es el momento de escribir un nuevo indicador. Use el Asistente para crear un nuevo indicador personalizado. Añada un parámetro adicional, como en el caso anterior:

Y el último paso (las propiedades de dibujo del programa del indicador personalizado) debe ser el siguiente:



Primero añada dos buffers de datos que se usarán para dibujar las señales para comprar y vender en forma de flechas. Cambie el tipo de buffers de datos a Arrow. Cambie los colores y los códigos de los símbolos. Abajo se encuentran todos los códigos de los símbolos disponibles:

No necesitamos dibujar el indicador en una ventana separada, ya que vamos a dibujar las señales en la ventana de gráfico.

Usamos dos buffers de datos, porque no podemos dibujar flechas distintas (símbolos) usando sólo un buffer. Cada buffer de datos que se muestra en forma de símbolos se puede dibujar sólo mediante un símbolo. Ahora vamos a analizar muy atentamente el código de inicialización del indicador:

int init()
{
//---- indicators
   SetIndexStyle(0,DRAW_ARROW);
   SetIndexArrow(0,236);
   SetIndexBuffer(0,ExtMapBuffer1);
   SetIndexEmptyValue(0,0.0);
   SetIndexStyle(1,DRAW_ARROW);
   SetIndexArrow(1,238);
   SetIndexBuffer(1,ExtMapBuffer2);
   SetIndexEmptyValue(1,0.0);
//----
   return(0);
}

Fíjese ahora que se usa otra constante para el tipo de dibujo del buffer de datos, es DRAW_ARROW:

SetIndexStyle(0,DRAW_ARROW);

También vemos dos nuevas funciones que se usan para definir el dibujo del símbolo. Se usa SetIndexArrow para definir qué símbolo representará el buffer. El primer argumento es el número del buffer, el segundo es el código del símbolo que representará el indicador:

SetIndexArrow(0,236);

Se usa SetIndexEmptyValue para indicar un valor "vacío". Eso significa que indicamos el valor en el cual no hay que dibujar nada. En nuestro caso, es muy conveniente, ya que las señales no se generan en cada barra. Funciona de la siguiente manera: cuando no necesitamos dibujar una flecha en la barra actual, asignamos al correspondiente elemento del buffer de datos un valor "vacío", en nuestro caso es 0. El primer argumento de la función es el número del buffer de datos. El segundo es el valor "vacío":

SetIndexEmptyValue(0,0.0);

El resto del código de inicialización establece los buffers análogos al indicador "aleatorio", que hemos analizado anteriormente. Ahora vamos a finalizar el código en la función start():

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
   
   if(limit>barsToProcess)
      limit=barsToProcess;
  
   for(int i=0;i<limit;i++)
   {
      double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
      
      if(randomValue>800.0)
         ExtMapBuffer1[i]=High[i]+5*Point;
      else
         ExtMapBuffer1[i]=0.0;
         
      if(randomValue<200.0)
         ExtMapBuffer2[i]=Low[i]-5*Point;         
      else
         ExtMapBuffer2[i]=0.0;         
   }
   
   return(0);
}

Todo el código hasta que se repita el bucle desde el indicador "aleatorio". En realidad este código es estándar en cualquier indicador y se repite con pequeños cambios. Ahora analicemos el bucle en detalle:

   for(int i=0;i<limit;i++)
   {
      double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
      
      if(randomValue>800.0)
         ExtMapBuffer1[i]=High[i]+5*Point;
      else
         ExtMapBuffer1[i]=0.0;
         
      if(randomValue<200.0)
         ExtMapBuffer2[i]=Low[i]-5*Point;         
      else
         ExtMapBuffer2[i]=0.0;         
   }

Primero debemos establecer la variable randomValue (valor aleatorio) y asignarle el valor de nuestro indicador "aleatorio" en la barra actual. Para este propósito se usa la función iCustom:

double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
 
// get the value of the "random" indicator on the i-th bar. Use the active chart on the current period. 
// The name of the executable file of indicator: RandomIndicator. Single parameter of "random" indicator
// is number of bars for calculation. In our indicator there is also analogous variable, that is why
// we use it. In "random" indicator only 1 data buffer, so we use 0, for getting
// access to its values.

Si el valor del indicador "aleatorio" es mayor que el nivel superior (800), esta es una señal para comprar (Buy):

if(randomValue>800.0)
   ExtMapBuffer1[i]=High[i]+5*Point;

// if there is signal to buy, assign to current element of data buffer the highest
// value of the current bar. Besides add 5 points, so that the arrow were a little higher 
// than the current price. The predetermined variable Point is used to get automatically
// a multiplier for presenting points. Otherwise we would have to write something like
// this: ExtMapBuffer1[i]=High[i]+0.0005; 

De lo contrario, no hay señal de compra:

else
   ExtMapBuffer1[i]=0.0;
 
// if no Buy signal, assign to the current element of data
// buffer "empty" value, which is equal to 0.0.
// Now no symbol will be shown on this bar.

Si el valor del indicador "aleatorio" está por debajo del nivel inferior (200), esta es una señal para vender (Sell):

if(randomValue<200.0)
   ExtMapBuffer2[i]=Low[i]-5*Point;
 
// if it is signal to sell, assign to the current element of data buffer the lowest
// value of the current bar. Besides diminish the value by 5 points, so that the arrow were 
// a little lower than the current price.

De lo contrario, no hay señal de venta:

else
   ExtMapBuffer2[i]=0.0;
 
// if no Sell signal, assign to the current element of data
// buffer "empty" value. Now no symbol will be shown on this bar.

Este era el bucle. Compile el indicador y inícielo en el terminal:





Sobre el Estilo

No, no se trata de las reglas de la elección de una corbata para adaptarse a una chaqueta y una camisa, aunque siempre es oportuno. El estilo de programación es muy importante, si no escribe el código sólo para sí mismo. En realidad, cada programador tiene su propio estilo de programación. Cada uno diseña los bucles a su manera, hace diferentes sangrados (o ningún sangrado en absoluto), establece variables, etc. Debería encontrar su propio estilo de programación, que siempre usará en el futuro. Me gustaría darle una serie de recomendaciones que le ayudarán a hacer que su código sea fácil de leer y de entender:

  • no escriba muchas operaciones separada por punto y coma (;) en una línea
  • escriba los nombres de las variables y las funciones en inglés
  • en los nombres de las variables use mayúsculas como delimitadores
  • evite el uso excesivo de abreviaturas y reducciones en los nombres de las variables y de las funciones
  • haga sangrados de cierta longitud para tener incluso bloques de códigos.
  • en cada nuevo cuerpo (de un bucle o condición) haga sangrados adicionales
  • agrupe las variables del mismo tipo
  • haga comentarios adecuadas para los bloques de códigos grandes y complejos.
  • haga comentarios adecuados para las funciones ha escrito por sí mismo (su asignación, sus parámetros)


Conclusión

Ha aprendido algo nuevo hoy. Ha escrito dos indicadores sencillos. Bueno, son inútiles, ¡pero no le estoy enseñando a operar con éxito! Ha visto cómo funcionan los indicadores, qué parámetros y propiedades tienen. Ha aprendido a establecer los buffers y trabajar con ellos. Se ha familiarizado con varias funciones nuevas. La función iCustom es muy importante y se usará incluso en los Asesores Expertos. Si encuentra alguna dificultad, vuelva a leer el artículo una vez más, tratando de entenderla. Si todavía tiene alguna pregunta, no dude en usar los foros o escribir comentarios en el artículo.

Traducción del inglés realizada por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/en/articles/1500

Archivos adjuntos |
El enfoque orientado a objetos en MQL El enfoque orientado a objetos en MQL

Este artículo puede resultar muy interesante para los programadores que sean principiantes o expertos y que trabajan en el entorno MQL. Me gustaría también que lo leyeran los desarrolladores del entorno MQL, ya que las preguntas que se plantean aquí pueden convertirse en proyectos para las futuras implementaciones de MetaTrader y MQL.

Cómo implementar sus propios criterios de optimización Cómo implementar sus propios criterios de optimización

En este artículo veremos la implementación de los criterios del beneficio/disminución de fondos, con los resultados resumidos en un archivo desarrollado para el Asesor Experto Moving Average (Promedio móvil).

Indicador Taichi - un sencillo método para interpretar los valores de Ichimoku Kinko Hyo. Indicador Taichi - un sencillo método para interpretar los valores de Ichimoku Kinko Hyo.

¿Es difícil interpretar las señales Ichimoku? Este artículo presenta algunos principios para interpretar los valores y señales de Ichimoku Kinko Hyo. Para la visualización de su funcionamiento el autor escogió el par de divisas EURUSD en base a sus propias preferencias. Sin embargo, se puede usar el indicador con cualquier par de divisas.

Visualización de un calendario de noticias Visualización de un calendario de noticias

En este artículo se describe la implementación de un indicador sencillo y cómodo que muestra en el área de trabajo los principales eventos económicos a partir de fuentes externas en Internet.