English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Indicadores personalizados para principiantes en MQL5

Indicadores personalizados para principiantes en MQL5

MetaTrader 5Ejemplos | 16 diciembre 2013, 11:38
3 127 0
Nikolay Kositsin
Nikolay Kositsin

Introducción

Lo fundamental para entender en profundidad cualquier materia (ya sea matemáticas, música o programación) es el estudio de sus principios fundamentales. Está muy bien cuando un estudio de este tipo se inicia a una edad muy temprana, ya que es más fácil comprender estos principios fundamentales y su conocimiento es más específico y amplio.

Por desgracia, la mayoría de la gente comienza a estudiar los mercados financieros y las bolsas de valores a una edad media, con lo que su estudio ya no es tan fácil. En este artículo intentaré ayudar a superar esta barrera inicial a la hora de comprender MQL5 y escribir indicadores personalizados para el terminal de cliente de Meta Trader 5.

El indicador SMA con un simple ejemplo

La forma más efectiva y racional de estudiar algo es la resolución de problemas prácticos. Como estamos considerando indicadores personalizados, vamos a empezar con el estudio de un indicador simple que contiene código que ilustra los principios del funcionamiento de un indicador en MQL5.

Como ejemplo, vamos a considerar el indicador más famoso del análisis técnico: la media móvil simple (SMA). Su cálculo es simple:

SMA = SUM (CLOSE (i), MAPeriod) / MAPeriod

donde:

  • SUM — suma de valores;
  • CLOSE (i) — precio de cierre de la i-ésima barra;
  • MAPeriod — número de barras promedio (período promedio).

Este es el código del indicador libre de cualquier exceso:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red  

input int MAPeriod = 13;
input int MAShift = 0;   

double ExtLineBuffer[];
//+------------------------------------------------------------------+
//| función de inicialización del indicador personalizado            |
//+------------------------------------------------------------------+ 
void OnInit()
  {
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_SHIFT, MAShift);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MAPeriod - 1);
  }
//+------------------------------------------------------------------+
//| Función de iteración del indicador personalizado                 |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
  {
   if (rates_total < MAPeriod - 1)
    return(0);

   int first, bar, iii;
   double Sum, SMA;

   if (prev_calculated == 0)
    first = MAPeriod - 1 + begin;
   else first = prev_calculated - 1;

   for(bar = first; bar < rates_total; bar++)
    {
     Sum = 0.0;
     for(iii = 0; iii < MAPeriod; iii++)
      Sum += price[bar - iii];     
     SMA = Sum / MAPeriod;      

     ExtLineBuffer[bar] = SMA;
    }   

   return(rates_total);
  }
//+------------------------------------------------------------------+

Y este es el resultado de su funcionamiento en el terminal de cliente de Meta Trader 5:

Primero debemos considerar dos cosas. La finalidad de cada string del código por un lado, y la interacción entre el código de este programa y el terminal de cliente por otro.


Usar comentarios

Al mirar por primera vez el código del indicador vemos objetos como estos:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Función de inicialización del indicador personalizado            |
//+------------------------------------------------------------------+  
//+------------------------------------------------------------------+
//| Función de iteración del indicador personalizado                 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

Es necesario tener en cuenta que no se relacionan directamente con el código, sino que son solo comentarios pensados para mejorar la lectura y mostrar un cierto contenido semántico de algunas partes del código. Por supuesto, pueden eliminarse del código sin afectar en absoluto a la simplificación, pero si se hace, el código perderá su capacidad para ser comprendido rápidamente. En nuestro caso, trabajamos con comentarios en una sola línea que siempre se inician con un par de caracteres "//" y finalizan con un carácter de nueva línea.

Está claro que en los comentarios el autor puede escribir todo lo que sea necesario para ayudar a la comprensión del código después de un cierto tiempo. En nuestro caso, en la primera parte de nuestros strings comentados encontramos el nombre del indicador y la información sobre su autor. La segunda y tercera parte de los comentarios dividen las funciones OnInit() y OnCalculate(). La última línea al final simplemente cierra el código del programa.

Estructura del código de SMA

Por tanto, como vemos, todo el código de nuestro indicador puede dividirse en 3 partes:

1. El código escrito sin paréntesis a nivel global que se encuentra entre los primeros dos comentarios.

2. La descripción de la función OnInit().

3. La descripción de la función OnCalculate().

Es necesario tener en cuenta que en programación el significado de función es mucho más amplio que en matemáticas. Por ejemplo, en los lenguajes de programación las funciones matemáticas siempre reciben algunos parámetros de entrada y devuelven los valores calculados y, además, las funciones en MQL5 también pueden realizar algunas operaciones con gráficos, transacciones, operaciones con archivos, etc.

De hecho, cualquier indicador escrito en MQL5 siempre tiene una mínima parte escrita por el usuario, cuyos contenidos son individuales y dependen de las características de un indicador.

Además de estos componentes, el conjunto mínimo de funciones puede contener la descripción de otra función MQL5, OnDeInit():

//+------------------------------------------------------------------+
//| Función de deinicialización del indicador personalizado          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

  }

En nuestro caso no es necesario, por lo que no lo he incluido aquí.

Interacción entre SMA y el terminal de cliente de Meta Trader

Vamos a considerar ahora el funcionamiento del archivo SMA.ex5 que obtuvimos al presionar la tecla "Compilar" en MetaEditor con el archivo SMA.mq5 abierto. Es preciso tener en cuenta que los archivos de texto con extensión .mq5 son código fuente en formato texto y deben compilarse primero antes de ser usados en el terminal de cliente.

Después de adjuntar este indicador a un gráfico desde la ventana del navegador Meta Trader ejecutará el código de la primera parte del indicador. Después llamará a la función OnInit() para una ejecución de la misma y después, con cada nuevo tick (tras la llegada de la nueva cotización) llamará a la función OnCalculate() y por tanto ejecutará el código de esta función. Si OnDeInit() estuviera presente en el indicador, Meta Trader llamaría a esta función una vez después de retirar el indicador del gráfico o después del cambio de período de tiempo.

El significado y finalidad de todas las partes del indicador queda claro después de esta explicación. En la primera parte del código, a nivel global hay algunos operadores simples que son ejecutados una vez tras el inicio del indicador. Además, hay una declaración de variables que son "visibles" en todos los bloques del indicador y que recuerdan sus valores mientras el indicador está sobre el gráfico.

Las constantes y funciones que se ejecutan una vez deben situarse dentro de la función OnInit(), ya que sería desaconsejable situarlas en un bloque de la función OnCalculate(). El código para el cálculo del indicador que permite calcular sus valores para cada barra, debe situarse en la función OnCalculate().

Los procedimientos que borran la basura inútil (si la hay) del gráfico después de que el indicador ha sido eliminado de él, deben situarse dentro de la función OnDeInit(). Por ejemplo, esto es necesario para el borrado de los objetos gráficos creados por el indicador.

Después de estas explicaciones, estamos preparados para examinar en detalle el código del indicador que hemos considerado antes.

Código del programa del indicador SMA

El primer grupo de líneas de código se inicia con el operador #property, que permite especificar parámetros adicionales en los ajustes del indicador. La lista completa de todas las propiedades del programa posibles se encuentra en la documentación de MQL5. Si es necesario, es posible escribir propiedades adicionales del indicador. En nuestro caso, tenemos 5 líneas de código cuya finalidad se describe en los comentarios:

//---- el indicador será trazado en la ventana principal
#property indicator_chart_window
//---- se usará un buffer para los cálculos y el trazado del indicador
#property indicator_buffers 1
//---- solo se utiliza un trazado gráfico
#property indicator_plots   1
//---- el indicador debe trazarse como una línea
#property indicator_type1   DRAW_LINE
//---- el color de la línea del indicador es el rojo
#property indicator_color1  Red 

Observe que no hay punto y coma al final de las líneas. La razón es, en nuestro caso, la definición de las constantes, pero presentada de otra forma.

Nuestra media móvil simple solo tiene dos parámetros que pueden ser cambiados por el usuario. Es un período de promediación y un cambio horizontal (en barras) del indicador a lo largo de los ejes de tiempo. Estos dos parámetros deben declararse como variables de entrada del indicador, ya que han sido declarados en dos líneas de código posteriores:

//---- parámetros de entrada del indicador
input int MAPeriod = 13; //período de promediación
nput int MAShift = 0; //cambio horizontal (en barras)

Observe que después de la declaración de estos parámetros de entrada hay comentarios que serán visibles como nombres de parámetros de entrada en la ventana de "propiedades" del indicador.


En nuestro caso, estos nombres son mucho más claros que los nombres de la variable del indicador. Por tanto, estos comentarios deben ser simples.

Y la última línea de código que no tiene paréntesis es la declaración de la matriz dinámica ExtLineBuffer[].

//---- la declaración de la matriz dinámica
//que se usará posteriormente como buffer de indicador
double ExtLineBuffer[];  

Ha sido declarada como variable global debido a varias razones.

En primer lugar, esta matriz debe convertirse en el buffer del indicador y se implementa en el bloque de la función OnInit(). En segundo lugar, el buffer del indicador mismo será usado dentro de la función OnCalculate(). Además, esta matriz almacenará los valores del indicador que se trazarán como curva en el gráfico. Debido al hecho de que se ha declarado como variable global, se encuentra disponible para todos los bloques del indicador y almacena sus valores todo el tiempo hasta que el indicador es separado del gráfico.

El contenido de la función OnInit() presenta 3 operadores que son funciones que ya vienen incorporadas en el terminal de cliente de Meta Trader.

La llamada de la primera función asigna el buffer de indicador cero con una matriz dinámica dimensional ExtLineBuffer[]. Dos llamadas de otra función con diferentes valores de los parámetros de entrada permiten cambiar el indicador a lo largo del eje del precio e igualmente permiten especificar su trazado desde la barra con el número MAPeriod.

void OnInit()
  {
//----+
//---- asigna la matriz dinámica ExtLineBuffer con el buffer de indicador 0
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- establece el cambio de trazado a lo largo del eje horizontal por MAShift barras
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- establece el inicio del trazado desde la barra con el número MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);
//----+
  }

La última llamada de la función PlotIndexSetInteger() pasa el valor igual a MAPeriod (a través del parámetro begin de la función OnCalculate()) a otro indicador, si es aplicado a los valores de nuestro indicador. La lógica es simple, no hay nada que promediar en las primeras MAPeriod-1 barras, y por eso el trazado de este indicador no tiene utilidad alguna. Sin embargo, debe pasarse este valor para cambiar el origen de los cálculos de otro indicador.

No es una lista completa de todas las funciones incorporadas que se usan en indicadores personalizados y puede ubicarse en este bloque del indicador. Para más información vea la documentación de MQL5.

Finalmente, vamos a considerar el código de la función OnCalculate(). No hay ninguna llamada personalizada en esta función, como ocurre en la función OnInit(), ya que estas funciones son llamadas por el terminal de cliente de Meta Trader. Por esta razón, los parámetros de entrada de la función se declaran como constantes.

int OnCalculate(
                const int rates_total,    // número de barras disponibles en el historial para el tick actual
                const int prev_calculated,//número de barras calculadas en el tick anterior
                const int begin,          // índice de la primera barra
                const double &price[]     // matriz de precios para el cálculo
                )

Estos parámetros de entrada no pueden cambiarse y sus valores son pasados por el terminal de cliente para su uso posterior en el código de esta función. Las variables de entrada de OnCalculate se describen en la documentación de MQL5. La función OnCalculate devuelve sus valores al terminal de cliente usando la función de retorno rates_total. El terminal de cliente recibe este valor del tick actual después de la ejecución de OnCalculate () y pasa el valor devuelto a otro parámetro prev_calculated. Por tanto, siempre es posible determinar el rango de índices de barras y realizar los cálculos de una vez para los nuevos valores del indicador que han aparecido después del tick previo.

Es preciso tener en cuenta que el orden de las barras en el terminal de cliente de Meta Trader se realiza de izquierda a derecha, por lo que la barra más antigua (la de la izquierda), en el gráfico tiene índice 0, la siguiente tiene índice 1, etc. Los elementos del buffer ExtLineBuffer[] tienen el mismo orden.

La estructura simple del código dentro de la función OnCalculate de nuestro indicador es universal y típica para muchos indicadores de análisis técnicos. Por tanto, vamos a tratarlo en profundidad. La lógica de la función OnCalculate() es la siguiente:

1. Comprobar la presencia de las barras necesarias para los cálculos.
2. Declarar las variables locales.
3. Obtener el índice de la barra inicial para el cálculo.
4. El lazo principal del cálculo del indicador.
5. Devolver el valor de rates_total al terminal de cliente usando el operador return().

Creo que el primer elemento está claro. Por ejemplo, si el período de promediación de la media móvil es igual a 200, pero el terminal de cliente solo tiene 100 barras, no es necesario realizar los cálculos ya que no hay barras suficientes para ello. Tenemos que devolver 0 al terminal de cliente usando el operador return().

//---- comprobar la presencia de barras suficientes para el cálculo
   if(rates_total<MAPeriod-1+begin)
      return(0);

Nuestro indicador puede aplicarse a los datos de algún otro indicador que también tenga un número mínimo de barras para el cálculo.  El uso de la constante begin es necesario para tener en cuenta este hecho. Para más detalles vea el artículo aplicando un indicador a otro.

Las variables locales declaradas en este bloque son necesarias solo para los cálculos intermedios dentro de la función OnCalculate(). Estas variables son liberadas de la RAM del ordenador después de la llamada de la función.

//---- declaración de variables locales
   int first,bar,iii;
   double Sum,SMA;

Es necesario tener cuidado con el índice de inicio del bucle principal (variable first). En la primera llamada de la función (podemos determinarla por el valor del parámetro prev_calculated) tenemos que realizar los cálculos de los valores del indicador para todas las barras. Para todos los ticks posteriores del terminal de cliente, tenemos que realizar los cálculos solo para las nuevas barras que han aparecido. Esto se hace gracias a 3 líneas de código:

//---- cálculo del índice de inicio first  del lazo principal
   if(prev_calculated==0) // comprobar el primer inicio del indicador
      first=MAPeriod-1+begin; // ínide de inicio para todas las barras
   else first=prev_calculated-1; // índice de inicio para las nuevas barras

El rango de los cambios de las variables en el operador del bucle principal del recálculo del indicador ya lo hemos visto.

//---- bucle principal del cálculo
   for(bar=first;bar<rates_total;bar++)

El procesado de la barra en el lazo principal se realiza en orden creciente (barra++), en otras palabras, de izquierda a derecha, como forma correcta y natural. En nuestro indicador, esto puede implementarse de otra forma (en orden inverso). Es mejor usar el orden creciente en los indicadores. La variable del bucle principal recibe el nombre de "barra" pero muchos programadores prefieren usar el nombre "i". Yo prefiero usar "barra" porque hace que el código sea más claro y fácil de leer.

El algoritmo de promediación que se ha implementado en el buble principal es simple:

     {
      Sum=0.0;
       //---- lazo de suma para la promediación de la barra actual
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // Sum = Sum + price[bar - iii]; // eqaual to       

      //---- calcula el valor promediado
      SMA=Sum/MAPeriod;

      //---- establece el elemento del buffer del indicador con el valor de SMA que hemos calculado
      ExtLineBuffer[bar]=SMA;
     }

En el segundo bucle estamos realizando la suma acumulativa de los precios de las barras anteriores del período y dividiéndolos por su período de promediación. Como resultado tenemos el valor final de SMA.

Después de finalizar el bucle principal, la función OnCaculate devuelve el número de barras disponibles de la variable rates_total. En la siguiente llamada de la función OnCalculate(), este valor será pasado por el terminal de cliente a la variable prev_calculated. Este valor, disminuido en 1, se usará como índice de inicio para el bucle principal.

Este es el código fuente completo del indicador con comentarios detallados para cada línea de código.

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//---- el indicador se trazará en la ventana principal
#property indicator_chart_window
//---- se usará un buffer para los cálculo y el trazado del indicador
#property indicator_buffers 1
//---- solo se usa un trazado gráfico 
#property indicator_plots   1
//---- el indicador debe trazarse como una línea
#property indicator_type1   DRAW_LINE
//---- el color de la línea del indicador es el rojo 
#property indicator_color1  Red 

//---- parámetros de entrada del indicador
input int MAPeriod = 13; //período de promediación
input int MAShift = 0; //cambio horizontal (en barras)

//---- la declaración de la matriz dinámica
//que se usará posteriormente como buffer del indicador
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| función de inicialización del indicador personalizado            |
//+------------------------------------------------------------------+ 
void OnInit()
  {
//----+
//----asigna la matriz dinámica ExtLineBuffer con el buffer del indicador 0
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- establece el cambio de trazado a lo largo del eje horizontal por MAShift barras
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- Establece el inicio del trazado en la barra con número MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);  
//----+
  }
//+------------------------------------------------------------------+
//| Función de iteración del indicador personalizado                 |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // número de barras disponibles en el historial en el tick actual
                const int prev_calculated,// número de barras calculadas en el tick anterior
                const int begin,          // índice de la primera barra
                const double &price[]     // matriz de precio para al cálculo
                )
  {
//----+   
   //---- comprueba la presencia de barras suficientes para el cálculo
   if (rates_total < MAPeriod - 1 + begin)
    return(0);   

   //---- declaración de variables locales 
   int first, bar, iii;
   double Sum, SMA;   

   //---- cálculo del índice de inicio first del lazo principal
   if(prev_calculated==0) // comprueba el primer inicio del indicador
      first=MAPeriod-1+begin; // índice de inicio para todas las barras
   else first=prev_calculated-1; // índice de inicio para las nuevas barras

   //---- lazo principal del cálculo
   for(bar = first; bar < rates_total; bar++)
    {    
      Sum=0.0;
      //---- lazo de suma para la promediación de la barra actual
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // It's equal to: Sum = Sum + price[bar - iii];         

      //---- calcula el valor promediado
      SMA=Sum/MAPeriod;

      //---- establece el elemento del buffer del indicador con el valor de SMA que hemos calculado
      ExtLineBuffer[bar]=SMA;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Este formato de código es mucho más fácil de comprender y leer.

Me gustaría destacar otra característica que puede usarse para simplificar la comprensión del código. Puede usar espacios y líneas vacías para hacerlo más claro.

Conclusión

Esto es todo con relación a la interacción entre el código del indicador personalizado y el terminal de cliente de Meta Trader. Por supuesto, la materia es mucho más extensa que lo que hemos visto. La finalidad de este artículo es ayudar a los más inexpertos a comprender los principios básicos. Puede consultar la documentación para más información.

Traducido desde el ruso por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/es/articles/37

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/37

Archivos adjuntos |
sma.mq5 (3.56 KB)
sma_.mq5 (6.84 KB)
Procesando los eventos de transacciones en el Expert Advisor por medio de la función OnTrade() Procesando los eventos de transacciones en el Expert Advisor por medio de la función OnTrade()
MQL5 introdujo infinidad de soluciones innovadoras, incluyendo el trabajo con distintos tipos de eventos (eventos de reloj, eventos sobre transacciones, eventos personalizados, etc.). La capacidad para gestionar eventos permite crear un tipo completamente nuevo de programas para el trading automático o semi-automático. En este artículo vamos a ver los eventos de transacciones y a escribir código para la función OnTrade(), encargada de procesar el evento Trade.
Usando los punteros de objeto en MQL5 Usando los punteros de objeto en MQL5
Por defecto, todos los objetos en MQL5 se pasan por referencia, pero hay una posibilidad de usar los punteros de objeto. Sin embargo, es necesario realizar una comprobación del puntero ya que el objeto puede no ser inicializado. En este caso, el programa MQL5 terminará con un error crítico y descargado. Los objetos, creados automáticamente, no causan este error, por lo que, en esencia, son muy seguros. En este artículo intentaremos comprender la diferencia entre la referencia del objeto y el puntero del mismo y veremos cómo escribir código seguro con el uso de punteros.
Cómo llamar a los indicadores en MQL5 Cómo llamar a los indicadores en MQL5
Con la nueva versión del lenguaje de programación MQL no solo ha cambiado la forma de trabajar con los indicadores, sino que también hay nuevas formas de crearlos. Además, dispone de mayor flexibilidad al trabajar con buffers de indicadores, ya que ahora es posible indicar la dirección de indexado y obtener exactamente el número de indicadores que desee. Este artículo explica los métodos básicos para llamar a los indicadores y obtener los datos a partir del buffer de cada indicador.
Introducción a MQL5: Cómo escribir un Expert Advisor y un Indicador Personalizado Introducción a MQL5: Cómo escribir un Expert Advisor y un Indicador Personalizado
MetaQuotes Programming Language 5 (MQL5), incluido en el terminal del cliente de MetaTrader 5, tiene muchas nuevas posibilidades y un mayor rendimiento, en comparación con MQL4. Este artículo le ayudará a familiarizarse con este nuevo lenguaje de programación. En este artículo se encuentran los sencillos ejemplos de cómo escribir un Expert Advisor y un Indicador Personalizado. También tendremos en cuenta algunos detalles del lenguaje MQL5, que son necesarios para entender estos ejemplos.