20 señales de trading en MQL5

Sergey Gritsay | 18 diciembre, 2013

Introducción

Los traders tratan de encontrar patrones en el comportamiento de los precios, se esfuerzan en elaborar reglas que les dan buenas oportunidades para determinar los momentos favorables para comprar o vender. Para crear un sistema completamente automatizado, necesitas aprender a informar sobre la ocurrencia de tales momentos -las señales de trading.

Las señales informan a los traders sobre posibles puntos de entrada de una posición, aunque no todas las señales son obligatorias. Criterios adicionales pueden incluso filtrar la mayoría de las señales, pero eso no es relevante para nosotros. El tema del artículo es la manera de programar las señales de trading más populares en MQL5.


1. ¿Qué señales conocemos?

Se pueden dividir todos los métodos para determinar los momentos de entrada al mercado en distintos tipos:


2. ¿Cuál es la mejor manera de escribir el código?

Hay muchas maneras de escribir un código; se puede escribir en OnStart(), OnTick() y otras funciones. Puedes encontrar información más detallada en la documentación de este sitio Web o en el manual de usuario incluido en MetaEditor; sin embargo este método no es muy eficaz y a veces tienes que escribir las mismas partes del código varias veces. Por lo tanto, vamos a elegir otro método - usaremos las funciones personalizadas a las que se pude llamar desde cualquier parte de nuestro código.

Por comodidad de uso de las funciones creadas, las vamos a combinar en grupos separados en un archivo de inclusión externo y le llamaremos SignalTrade; se almacenará en la carpeta ...\MQL5\Include. Esto permitirá conectar fácilmente este módulo a cualquier programa. 

A pesar de que las señales parecen diferentes, tienen muchas cosas en común. Se pueden recibir tres variantes desde la función que genera las señales:

Así que vamos a crear una enumeración relativa a estas señales.

Vamos a escribir un modelo de función que devuelve una señal. Dividimos nuestra función en distintas partes, en las que se puede llevar a cabo una operación u otra. Se declaran y se ponen los valores iniciales a las variables necesarias para almacenar los datos al principio de la función. Más adelante, se llevará a cabo la carga y comprobación de los datos necesarios a partir el indicador creado. La comprobación de los datos es fundamental para evitar consecuencias impredecibles de trabajar con los datos y el programa en su conjunto. 

Una vez cargados y comprobados los datos, procedemos a la formación de la señal. En cuanto se forma la señal, se sale de la función y se le devuelve la señal obtenido como uno de los valores enumerados anteriormente.

int TradeSignal()
  {
   //--- cero significa que no hay señal
   int sig=0;
   
   //--- comprobar el identificador de los indicadores

   //--- si todos los identificadores son inválidos, crearlos

   //--- si los identificadores son válidos, copiar los valores desde el indicador

   //--- comprobar los datos copiados

   //--- en el caso de un error de copia, salir de la función

   //--- indexar el array como una serie de tiempos

   //--- en el caso de un error de indexación, salir de la función 

   //--- comprobar las condiciones y establecer el valor de sig
 
   //--- devolver la señal de trading
   return(sig);
  }


3. Ejemplos de 20 señales de trading

Usaremos distintos indicadores para obtener las señales de trading. Se llama a los indicadores en MQL5 mediante las funciones especiales, por ejemplo iMA, iAC, iMACD, iIchimoku, etc.; crean una copia del indicador técnico correspondiente en la caché global del terminal de cliente. Si ya existe una copia del indicador con los mismos parámetros, no se crea una nueva copia pero se incrementa el contador de referencias hacia la copia existente.

Estas funciones devuelven el identificador de la correspondiente copia del indicador. Además, mediante este identificador, se pueden obtener los datos calculados por el correspondiente indicador. Los datos del correspondiente buffer (los datos calculados están en los indicadores técnicos; su número puede variar de 1 a 5, según el tipo de indicador) se pueden copiar en un programa MQL5 mediante la función CopyBuffer().

Es imposible utilizar los datos de un indicador justo después de su creación, ya que a veces hace falta para calcular los valores del indicador; así que la mejor manera es crear los identificadores en OnInit(). La función iCustom() crea el indicador personalizado correspondiente; y se crea con éxito, devuelve el identificador del indicador. Los indicadores personalizados pueden tener hasta 512 buffers de indicadores, y su contenido se puede obtener también mediante la función CopyBuffer() y el identificador obtenido.

Para cada señal vamos a crear una función según nuestro prototipo y las TradeSignal() y las enumeramos en el siguiente orden: TradeSignal_01() - TradeSignal_20(). Veamos más de cerca la estructura de la función para formar una señal mediante el ejemplo de una señal basada en la intersección de promedios móviles; y luego escribimos las funciones para las demás señales de la misma manera.

3.1. Intersección de los promedios móviles

Figura 1. La intersección de los promedios móviles

Figura 1. La intersección de los promedios móviles

Mediante la función TradeSignal_01() obtenemos la señal correspondiente a la intersección de dos Promedios móviles "Moving Averages" (МА): el rápido con un período de 8 y el lento con un período de 16.

En nuestra función grabaremos sólo la intersección y devolvemos el valor de la señal correspondiente a la función. De acuerdo con nuestras reglas y el modelo, esta sería la función:

int TradeSignal_01()
  {
//--- cero significa que no hay ninguna señal
   int sig=0;

//--- comprobar los identificadores de los indicadores
   if(h_ma1==INVALID_HANDLE)//--- si el identificador no es válido
     {
      //--- crear de nuevo                                             
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- salir de la función
      return(0);
     }
   else //--- si el identificador es válido
     {
      //--- copiar el valor del indicador al array
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- si el array de datos es inferior al requerido
         //--- salir de la función
         return(0);
      //--- definir los índices del array como series de tiempo                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- en el caso de un error de indexación, salir de la función
         return(0);
     }

   if(h_ma2==INVALID_HANDLE)//--- si el identificador no es válido
     {
      //--- crear de nuevo                                                      
      h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
      //--- salir de la función
      return(0);
     }
   else //--- si el identificador es válido
     {
      //--- copiar los valores del indicador al array
      if(CopyBuffer(h_ma2,0,0,2,ma2_buffer)<2) //--- si hay menos datos de lo necesario
         //--- salir de la función
         return(0);
      //--- definir los índices del array como series de tiempo                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- en el caso de un error de indexación, salir de la función
         return(0);
     }

//--- comprobar la condición y establecer el valor de sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

Analicemos de más de cerca cada parte del código. Al principio de la función declaramos una variable local que almacenará el tipo de señal y le pone un valor inicial cero, lo que significa que no hay señal.

int sig=0;

Además, comprobamos la validez del identificador; si el identificador es válido lo creamos y salimos de la función, puesto que el cálculo del indicador lleva algún tiempo. Se lleva a cabo para evitar un error al copiar los datos del buffer de indicador.

   if(h_ma1==INVALID_HANDLE)//--- si el identificador no es válido
     {
      //--- crear de nuevo                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- salir de la función
      return(0);
     }

Si el identificador es válido, copiar los datos al array. Para analizar la situación, basta con copiar los datos de las tres últimas barras de MA rápido y dos últimas barras del lento. Usa la siguiente función para este propósito:

int  CopyBuffer(
   int       indicator_handle,     // identificador del indicador
   int       buffer_num,           // número de buffers del indicador
   int       start_pos,            // de donde empezar 
   int       count,                // cantidad a copiar
   double    buffer[]              // un array para copiar los datos
   );

Compruébalos: si hay menos datos de los necesarios, entonces ha ocurrido un error durante la copia; y esto dará lugar a un error en el array donde se deben almacenar los datos. Para evitarlo, salimos de la función. Necesitamos también establecer la indexación del array en series de tiempo; es lo que hace la siguiente función:

bool  ArraySetAsSeries(
   void  array[],     // array por referencia
   bool  set          // true significa que el orden de indexación es inverso
   );

Si ocurre un error durante la indexación del array, hay que salir de la función, de lo contrario podemos obtener resultados erróneos.

else //--- si el identificador es válido
     {
      //--- copiar los valores del indicador al array
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- si hay menos datos de lo necesario
         //--- salir de la función
         return(0);
      //--- definir los índices del array como series de tiempo                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- en el caso de un error de indexación, salir de la función
         return(0);
     }

Ahora que ya se han creado los indicadores y hemos obtenido todos los datos necesarios, vamos a pasar a la parte principal - crear una señal.

Para eliminar las fluctuaciones de la señal en la barra actual, analiza sólo las 1ª y 2ª barras cerradas.

Formar la señal para comprar. Para ello, toma el valor del MA rápido en la 2ª barra y compáralo con el valor de MA lento en la 1ª barra; y compara el valor de MA rápido en la 1ª barra con el valor de MA lento en la 1ª barra. Si el valor de MA rápido en la 2ª barra es inferior al valor de MA lento en la 1ª barra, y el valor de MA rápido en la 1ª barra es superior del valor de MA lento en la 1ª barra, entonces MA rápido ha cruzado el MA lento al alza; es nuestra señal para comprar. Si se cumple nuestra condición, escribimos 1 en la variable sig.

La señal para vender se forma de la misma manera. Si MA rápido en la 2ª barra es superior a MA lento en la 1ª barra, y si MA rápido en la 1ª barra es inferior a MA lento en la 1ª barra, entonces MA rápido se cruza con MA lento a la baja. Si se cumple nuestra condición, escribimos -1 en la variable sig. Si no se cumple ninguna de las dos condiciones, entonces no hay ninguna señal, y escribimos 0 en la variable sig. Ya están formadas la señales, ahora se devuelve la señal obtenida a nuestra función TradeSignal_01()

//--- comprobar la condición y asignar el valor a sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);

3.2. Intersección de la línea principal y la línea de señal de MACD

Figura 2. La intersección de la línea principal y de línea de señal de MACD

En la función TradeSignal_02() obtenemos la señal de la intersección de la líneas de señal y la línea principal de MACD. Si la línea de la señal cruza la principal al alza, es una señal para comprar. Si la línea de la señal cruza la principal a la baja, es una señal para vender. Se tendrán en cuenta otros casos si no hay ninguna señal.

int TradeSignal_02()
  {
   int sig=0;

   if(h_macd==INVALID_HANDLE)
     {
      h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_macd,0,0,2,macd1_buffer)<2)
         return(0);
      if(CopyBuffer(h_macd,1,0,3,macd2_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(macd1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(macd2_buffer,true))
         return(0);
     }

//--- comprobar la condición y asignar el valor a sig
   if(macd2_buffer[2]>macd1_buffer[1] && macd2_buffer[1]<macd1_buffer[1])
      sig=1;
   else if(macd2_buffer[2]<macd1_buffer[1] && macd2_buffer[1]>macd1_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.3. Sobrepasar el rango del Canal de precios

Figura 3. Sobrepasar la banda inferior y superior del Canal de precios

En la función TradeSignal_03() obtenemos la señal al sobrepasar la banda superior o inferior del Canal de precios.

Cuando el precio traspasa el límite superior del Canal de precios y el precio se queda por encima de este límite, se trata de una señal de compra. Cuando el precio sobrepasa el límite inferior del Canal de precios y el precio se queda por debajo de este límite, entonces hablamos de una señal de venta. Se tendrán en cuenta otros casos si no hay ninguna señal.

A diferencia de las dos funciones anteriores, tenemos aquí un array para almacenar los precios de cierre. Usa la siguiente función para obtenerlos:

int  CopyClose(
   string           symbol_name,       // nombre del símbolo
   ENUM_TIMEFRAMES  timeframe,          // período
   int              start_pos,         // donde empezar 
   int              count,             // cantidad a copiar
   double           close_array[]      // array para copiar los precios de cierre
   );

int TradeSignal_03()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,3,pc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_pc,1,0,3,pc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[1]>pc1_buffer[2])
      sig=1;
   else if(Close[1]<pc2_buffer[2])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.4. Sobrepasar el rango del canal adaptativo ADX

Figura 4. Sobrepasar la banda superior e inferior del canal adaptativo ADX.

En la función TradeSignal_04() obtenemos la señal al sobrepasar por encima o por debajo las bandas del Canal adaptativo ADX.

Cuando el precio sobrepasa el límite superior del Canal adaptativo ADX y el precio de cierre se queda por encima de este límite, se trata de una señal de compra. Cuando el precio sobrepasa el límite inferior del Canal de precios y el precio de cierre se queda por debajo de este límite, entonces hablamos de una señal de venta. Se tendrán en cuenta otros casos si no hay ninguna señal.

int TradeSignal_04()
  {
   int sig=0;

   if(h_acadx==INVALID_HANDLE)
     {
      h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_acadx,0,0,2,acadx1_buffer)<2)
         return(0);
      if(CopyBuffer(h_acadx,1,0,2,acadx2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(acadx1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(acadx2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[1]>acadx1_buffer[1])
      sig=1;
   else if(Close[1]<acadx2_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.5. Salida de las zonas de sobrecompra y sobreventa del estocástico

Figura 5. El cruce de los niveles de sobrecompra y sobreventa mediante el estocástico.

Mediante la función TradeSignal_05() obtenemos la señal correspondiente a la salida del estocástico de las zonas de sobrecompra y sobreventa; los niveles de estas zonas tendrán un valor de 80 y 20.

Compramos, cuando el oscilador (%K o %D) cae por debajo de cierto nivel (suele ser 20) y luego sube por encima del mismo. Vendemos, cuando el oscilador sube por encima de cierto nivel (suele ser 80) y luego cae por debajo del mismo.

int TradeSignal_05()
  {
   int sig=0;

   if(h_stoh==INVALID_HANDLE)
     {
      h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_stoh,0,0,3,stoh_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(stoh_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(stoh_buffer[2]<20 && stoh_buffer[1]>20)
      sig=1;
   else if(stoh_buffer[2]>80 && stoh_buffer[1]<80)
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.6. Salida de las zonas de sobrecompra y sobreventa del RSI

Figura 6. El cruce de los niveles de sobrecompra y sobreventa mediante el indicador RSI

Mediante la función TradeSignal_06() obtenemos la señal correspondiente a la salida del indicador RSI de las zonas de sobrecompra y sobreventa; los niveles de estas zonas tendrán un valor de 70 y 30.

Compramos, cuando el indicador RSI cae por debajo de cierto nivel (suele ser 30) y luego sube por encima del mismo. Vendemos, cuando el indicador RSI sube por encima de cierto nivel (suele ser 70) y luego cae por debajo del mismo.

int TradeSignal_06()
  {
   int sig=0;

   if(h_rsi==INVALID_HANDLE)
     {
      h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_rsi,0,0,3,rsi_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(rsi_buffer,true))
         return(0);
     }
//--- sube por encima de cierto nivel (suele ser 80) y luego cae por debajo del mismo.
   if(rsi_buffer[2]<30 && rsi_buffer[1]>30)
      sig=1;
   else if(rsi_buffer[2]>70 && rsi_buffer[1]<70)
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);

 
3.7. Salida de las zonas de sobrecompra y sobreventa del CCI

Figura 7. El cruce de los niveles de sobrecompra y sobreventa mediante el indicador CCI

Mediante la función TradeSignal_07() obtenemos la señal correspondiente a la salida del indicador CCI de las zonas de sobrecompra y sobreventa; los niveles de estas zonas tendrán un valor de 100 y -100.

Compramos, cuando el indicador CCI cae por debajo del nivel -100 y luego sube por encima del mismo. Compramos, cuando el indicador CCI sube por encima del nivel 100 y luego cae por debajo del mismo.

int TradeSignal_07()
  {
   int sig=0;

   if(h_cci==INVALID_HANDLE)
     {
      h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_cci,0,0,3,cci_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(cci_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(cci_buffer[2]<-100 && cci_buffer[1]>-100)
      sig=1;
   else if(cci_buffer[2]>100 && cci_buffer[1]<100)
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.8. Salida de las zonas de sobrecompra y sobreventa de Williams %

Figura 8. El cruce de los niveles de sobrecompra y sobreventa mediante el indicador Williams %

Mediante la función TradeSignal_08() obtenemos la señal correspondiente a la salida del indicador Williams % de las zonas de sobrecompra y sobreventa; los niveles de estas zonas tendrán un valor de -20 y -80.

Compramos, cuando el indicador Williams % cae por debajo del nivel -80 y luego sube por encima del mismo. Compramos, cuando el indicador Williams % sube por encima del nivel -20 y luego cae por debajo del mismo.

int TradeSignal_08()
  {
   int sig=0;

   if(h_wpr==INVALID_HANDLE)
     {
      h_wpr=iWPR(Symbol(),Period(),14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_wpr,0,0,3,wpr_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(wpr_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(wpr_buffer[2]<-80 && wpr_buffer[1]>-80)
      sig=1;
   else if(wpr_buffer[2]>-20 && wpr_buffer[1]<-20)
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }
3.9. Rebote entre las bandas del Canal de Bollinger

Figura 9. Rebote entre las bandas del Canal de Bollinger

Mediante la función TradeSignal_09() obtenemos la señal correspondiente al rebote del precio entre las bandas del canal de Bollinger.

Cuando el precio traspasa o toca la banda superior de Bollinger y luego vuelve, se trata de una señal para vender. Cuando el precio traspasa o toca la banda inferior de Bollinger, se trata de una señal para comprar.

int TradeSignal_09()
  {
   int sig=0;

   if(h_bb==INVALID_HANDLE)
     {
      h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_bb,1,0,2,bb1_buffer)<2)
         return(0);
      if(CopyBuffer(h_bb,2,0,2,bb2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(bb1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(bb2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[2]<=bb2_buffer[1] && Close[1]>bb2_buffer[1])
      sig=1;
   else if(Close[2]>=bb1_buffer[1] && Close[1]<bb1_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.10. Rebote entre las bandas del Canal de de desviación típica

Figura 10. Rebote entre las bandas del Canal de desviación típica

Mediante la función TradeSignal_10() obtenemos la señal correspondiente al rebote del precio entre las bandas del canal de desviación típica.

Cuando el precio traspasa o toca la banda superior del canal de desviación típica y luego vuelve, se trata de una señal para vender. Cuando el precio traspasa o toca la banda inferior del canal de desviación típica, se trata de una señal para comprar.

int TradeSignal_10()
  {
   int sig=0;

   if(h_sdc==INVALID_HANDLE)
     {
      h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sdc,0,0,2,sdc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sdc,1,0,2,sdc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sdc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sdc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[2]<=sdc2_buffer[1] && Close[1]>sdc2_buffer[1])
      sig=1;
   else if(Close[2]>=sdc1_buffer[1] && Close[1]<sdc1_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.11. Rebote entre las bandas del Canal de precios

Figura 11. Rebote entre las bandas del Canal de precios

Mediante la función TradeSignal_11() obtenemos la señal correspondiente al rebote del precio entre las bandas del canal de precios.

Cuando el precio traspasa o toca la banda superior del canal de precios y luego vuelve, se trata de una señal para vender. Cuando el precio traspasa o toca la banda inferior del canal de precios, se trata de una señal para comprar.

int TradeSignal_11()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,4,pc1_buffer)<4)
         return(0);
      if(CopyBuffer(h_pc,1,0,4,pc2_buffer)<4)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[1]>pc2_buffer[2] && Close[2]<=pc2_buffer[3])
      sig=1;
   else if(Close[1]<pc1_buffer[2] && Close[2]>=pc1_buffer[3])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.12. Rebote entre las bandas del Canal Envelopes

Figura 12. El rebote entre las bandas del Canal Envelopes

Mediante la función TradeSignal_12() obtenemos la señal correspondiente al rebote del precio entre las bandas del canal Envelopes.

Cuando el precio traspasa o toca la banda superior del canal Envelopes y luego vuelve, se trata de una señal para vender. Cuando el precio traspasa o toca la banda inferior del canal Envelopes, se trata de una señal para comprar.

int TradeSignal_12()
  {
   int sig=0;

   if(h_env==INVALID_HANDLE)
     {
      h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_env,0,0,2,env1_buffer)<2)
         return(0);
      if(CopyBuffer(h_env,1,0,2,env2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(env1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(env2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[2]<=env2_buffer[1] && Close[1]>env2_buffer[1])
      sig=1;
   else if(Close[2]>=env1_buffer[1] && Close[1]<env1_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.13. Sobrepasar las bandas del Canal de Donchian

Figura 13. Sobrepasar las bandas del canal de Donchian

Mediante la función TradeSignal_13() obtenemos la señal cuando los precios traspasan las bandas del canal de Donchian.

Cuando el precio sobrepasa el límite superior del Canal de Donchian y el precio de cierre se queda por encima de este límite, se trata de una señal de compra. Cuando el precio sobrepasa el límite inferior del Canal de Donchian y el precio de cierre se queda por debajo de este límite, entonces hablamos de una señal de venta.

int TradeSignal_13()
  {
   int sig=0;

   if(h_dc==INVALID_HANDLE)
     {
      h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_dc,0,0,3,dc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_dc,1,0,3,dc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(dc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(dc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[1]>dc1_buffer[2])
      sig=1;
   else if(Close[1]<dc2_buffer[2])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.14. Sobrepasar las bandas de Silver-Channel

Figura 14. Sobrepasar las bandas de Silver-Channel

Mediante la función TradeSignal_14() obtenemos la señal cuando los precios sobrepasan las bandas de Silver-Channel.  El indicador Silver-Channel dibuja 8 bandas que pueden servir también como niveles de soporte y resistencia. Para obtener la señal usaremos 2 bandas en el medio. 

Cuando el precio sobrepasa el límite superior de Silver-Channel y el precio de cierre se queda por encima de este límite, se trata de una señal de compra. Cuando el precio sobrepasa el límite inferior de Silver-Channel y el precio de cierre se queda por debajo de este límite, entonces hablamos de una señal de venta.

int TradeSignal_14()
  {
   int sig=0;

   if(h_sc==INVALID_HANDLE)
     {
      h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sc,0,0,2,sc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sc,1,0,2,sc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[2]<sc1_buffer[1] && Close[1]>sc1_buffer[1])
      sig=1;
   else if(Close[2]>sc2_buffer[1] && Close[1]<sc2_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.15. Sobrepasar las bandas del Canal de Gallagher

Figura 15. Sobrepasar las bandas del canal de Gallagher

Mediante la función TradeSignal_15() obtenemos la señal cuando los precios traspasan las bandas del canal de Gallagher. El indicador del canal de Gallagher se dibuja basándose en los máximos y mínimos de los últimos 10 días. 

Cuando el precio traspasa el límite superior del Canal de Gallagher y el precio de cierre se queda por encima de este límite, se trata de una señal de compra. Cuando el precio sobrepasa el límite inferior del Canal de Gallagher y el precio de cierre se queda por debajo de este límite, entonces hablamos de una señal de venta.

int TradeSignal_15()
  {
   int sig=0;

   if(h_gc==INVALID_HANDLE)
     {
      h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
      return(0);
     }
   else
     {
      if(CopyBuffer(h_gc,0,0,3,gc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_gc,1,0,3,gc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(gc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(gc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(Close[1]>gc1_buffer[2])
      sig=1;
   else if(Close[1]<gc2_buffer[2])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.16. Cambio de tendencia mediante NRTR

Figura 16. Detección de un cambio de tendencia mediante el indicador NRTR

En la función TradeSignal_16() obtenemos una señal cuando hay cambios en la tendencia NRTR.

Si el indicador NRTR muestra una tendencia alcista, se trata de una señal para comprar. Si el indicador NRTR muestra una tendencia bajista, se trata de una señal para vender.

int TradeSignal_16()
  {
   int sig=0;

   if(h_nrtr==INVALID_HANDLE)
     {
      h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_nrtr,0,0,2,nrtr1_buffer)<2)
         return(0);
      if(CopyBuffer(h_nrtr,1,0,2,nrtr2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(nrtr1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(nrtr2_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar el valor a sig
   if(nrtr1_buffer[1]>0)
      sig=1;
   else if(nrtr2_buffer[1]>0)
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.17. Cambio de tendencia mediante Alligator

Figura 17. Cambio de tendencia mediante Alligator

En la función TradeSignal_17() obtenemos una señal cuando hay cambios en la tendencia Alligator (caimán en español).

Cuando la mandíbula, los dientes y los labios están cerrados o entrelazados, el Alligator se va dormir o ya está durmiendo Mientras está dormido, se pone cada vez más hambriento; y más duerme, más hambriento estará al despertar. Lo primero que hace al despertarse es abrir la boca y bostezar. Entonces empieza a oler la comida - carne de toro u oso (mercado alcista o bajista), y se pone a cazar. En cuanto haya comido lo suficiente, el Alligator pierde el interés en la comida-precio (las líneas de saldo convergen); es el momento de recoger las ganancias.

int TradeSignal_17()
  {
   int sig=0;

   if(h_al==INVALID_HANDLE)
     {
      h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_al,0,0,2,al1_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,1,0,2,al2_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,2,0,2,al3_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(al1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al3_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(al3_buffer[1]>al2_buffer[1] && al2_buffer[1]>al1_buffer[1])
      sig=1;
   else if(al3_buffer[1]<al2_buffer[1] && al2_buffer[1]<al1_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.18. Cambio de tendencia mediante AMA

Figura 18. Cambio de tendencia mediante AMA

En la función TradeSignal_18() obtenemos una señal cuando hay cambios en la tendencia AMA.

Si el indicador AMA tiene una tendencia alcista, se trata de una señal para comprar. Si el indicador AMA tiene una tendencia bajista, se trata de una señal para vender.

int TradeSignal_18()
  {
   int sig=0;

   if(h_ama==INVALID_HANDLE)
     {
      h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ama,0,0,3,ama_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(ama_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(ama_buffer[2]<ama_buffer[1])
      sig=1;
   else if(ama_buffer[2]>ama_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.19. Cambio de color en el Oscilador impresionante (Awesome Oscillator)

Figura 19. Detección de un cambio de tendencia mediante el indicador Awesome Oscillator

Mediante la función TradeSignal_19() obtenemos la señal cuando cambian el color del histograma de Awesome Oscillator.

Una de las características de MQL5 es la posibilidad de crear buffers para los indicadores, donde se pueden guardar los índices del color de las líneas establecido en las propiedades de #property indicator_colorN. Cuando el color del histograma de Awesome Oscillator está verde, se trata de una señal para comprar. Si el color cambia a rojo, entonces es una señal para comprar.

int TradeSignal_19()
  {
   int sig=0;

   if(h_ao==INVALID_HANDLE)
     {
      h_ao=iAO(Symbol(),Period());
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ao,1,0,20,ao_buffer)<20)
         return(0);
      if(!ArraySetAsSeries(ao_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar un valor a sig
   if(ao_buffer[1]==0)
      sig=1;
   else if(ao_buffer[1]==1)
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }

3.20. Cambio de tendencia mediante Ichimoku

Figura 20. Detección de un cambio de tendencia mediante el indicador Ichimoku

En la función TradeSignal_20() obtenemos una señal cuando hay cambios en la tendencia Ichimoku. Por ello, analizaremos la intersección de las líneas Tenkan-sen y Kijun-sen.

La señal para comprar se genera cuando la línea Tenkam-sen cruza la de Kijun-sen desde abajo hacia arriba. El cruce desde arriba hacia abajo indica una señal para vender.

int TradeSignal_20()
  {
   int sig=0;

   if(h_ich==INVALID_HANDLE)
     {
      h_ich=iIchimoku(Symbol(),Period(),9,26,52);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ich,0,0,2,ich1_buffer)<2)
         return(0);
      if(CopyBuffer(h_ich,1,0,2,ich2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(ich1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(ich2_buffer,true))
         return(0);
     }
//--- comprobar la condición y asignar el valor a sig
   if(ich1_buffer[1]>ich2_buffer[1])
      sig=1;
   else if(ich1_buffer[1]<ich2_buffer[1])
      sig=-1;
   else sig=0;

//--- devolver la señal de trading
   return(sig);
  }


4. Hacerlo como un indicador

Ahora, que tenemos los bloques de los indicadores ya hechos, podemos empezar a escribir un indicador que muestra las señales en función de todos los métodos seleccionados. Mediante la plantilla creada podemos implementar la recepción de señales desde cualquier indicador; es suficiente para formular correctamente la condición de la señal y añadirla al código.

Vamos a escribir un indicador que incluye unos parámetros inflexibles. Se van a dibujar les señales de los indicadores en forma de flechas (flecha hacia arriba - comprar, hacia abajo - vender, cruz - ninguna señal) en la parte derecha del indicador. Vamos a utilizar la fuente estándar Wingdings para dibujar las flechas. Además, tenemos que crear algunas funciones más para mostrar la información relativa a las señales en el gráfico del símbolo. La combinaremos en bloques separados en forma de librería que se puede utilizar para escribir nuestros propios programas, añadiendo nuevas funciones a la misma. Vamos a llamar esta librería LibFunctions.

En la cabecera de nuestro futuro indicador, escribe la conexión del archivo con las funciones para generar las señales, y importa las funciones necesarias para la representación gráfica de la señal, y declara variables globales que almacenarán el tipo de señal recibida desde el indicador.

//--- Conectar las librerías de funciones necesarias
#include <SignalTrade.mqh>
//--- Importar la funciones desde la librería LibFunctions
#import "LibFunctions.ex5"
void SetLabel(string nm,string tx,ENUM_BASE_CORNER cn,ENUM_ANCHOR_POINT cr,int xd,int yd,string fn,int fs,double yg,color ct);
string arrow(int sig);
color Colorarrow(int sig);
#import
//+------------------------------------------------------------------+
//| Declarar variables para almacenar las señales de los indicadores |
//+------------------------------------------------------------------+
int SignalMA;
int SignalMACD;
int SignalPC;
int SignalACADX;
int SignalST;
int SignalRSI;
int SignalCCI;
int SignalWPR;
int SignalBB;
int SignalSDC;
int SignalPC2;
int SignalENV;
int SignalDC;
int SignalSC;
int SignalGC;
int SignalNRTR;
int SignalAL;
int SignalAMA;
int SignalAO;
int SignalICH;

Tal y como mencioné antes, los indicadores se han cargado en el terminal sólo una vez y los punteros (identificadores) a los mismos se han creado; por lo que vamos a escribir la parte relativa a su creación en la función OnInit() ya que esta función se ejecuta sólo una vez al inicio del programa.

int OnInit()
  {
//--- crear los identificadores del indicador
   h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
   h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
   h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
   h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
   h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
   h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
   h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
   h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
   h_wpr=iWPR(Symbol(),Period(),14);
   h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
   h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
   h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
   h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
   h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
   h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
   h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
   h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
   h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
   h_ao=iAO(Symbol(),Period());
   h_ich=iIchimoku(Symbol(),Period(),9,26,52);
   return(0);
  }

Se llevan a cabo los principales cálculos en la función OnCalculate(); y donde vamos a colocar el resto del código del indicador.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---asignar el valor de la señal a la variable
   SignalMA    = TradeSignal_01();
   SignalMACD  = TradeSignal_02();
   SignalPC    = TradeSignal_03();
   SignalACADX = TradeSignal_04();
   SignalST    = TradeSignal_05();
   SignalRSI   = TradeSignal_06();
   SignalCCI   = TradeSignal_07();
   SignalWPR   = TradeSignal_08();
   SignalBB    = TradeSignal_09();
   SignalSDC   = TradeSignal_10();
   SignalPC2   = TradeSignal_11();
   SignalENV   = TradeSignal_12();
   SignalDC    = TradeSignal_13();
   SignalSC    = TradeSignal_14();
   SignalGC    = TradeSignal_15();
   SignalNRTR  = TradeSignal_16();
   SignalAL    = TradeSignal_17();
   SignalAMA   = TradeSignal_18();
   SignalAO    = TradeSignal_19();
   SignalICH   = TradeSignal_20();

//--- dibujar objetos gráficos en el gráfico en la esquina superior izquierda
   int size=((int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS)/22);
   int i=0;
   int x=10;
   int y=0;
   int fz=size-4;

   y+=size;
   SetLabel("arrow"+(string)i,arrow(SignalMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMA));
   x+=size;
   SetLabel("label"+(string)i,"Moving Average",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalMACD),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMACD));
   x+=size;
   SetLabel("label"+(string)i,"MACD",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalACADX),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalACADX));
   x+=size;
   SetLabel("label"+(string)i,"Adaptive Channel ADX",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalST),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalST));
   x+=size;
   SetLabel("label"+(string)i,"Stochastic Oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalRSI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalRSI));
   x+=size;
   SetLabel("label"+(string)i,"RSI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalCCI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalCCI));
   x+=size;
   SetLabel("label"+(string)i,"CCI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalWPR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalWPR));
   x+=size;
   SetLabel("label"+(string)i,"WPR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalBB),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalBB));
   x+=size;
   SetLabel("label"+(string)i,"Bollinger Bands",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSDC));
   x+=size;
   SetLabel("label"+(string)i,"StDevChannel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC2),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC2));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell 2",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalENV),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalENV));
   x+=size;
   SetLabel("label"+(string)i,"Envelopes",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalDC));
   x+=size;
   SetLabel("label"+(string)i,"Donchian Channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSC));
   x+=size;
   SetLabel("label"+(string)i,"Silver-channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalGC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalGC));
   x+=size;
   SetLabel("label"+(string)i,"Galaher Channel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalNRTR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalNRTR));
   x+=size;
   SetLabel("label"+(string)i,"NRTR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAL),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAL));
   x+=size;
   SetLabel("label"+(string)i,"Alligator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAMA));
   x+=size;
   SetLabel("label"+(string)i,"AMA",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAO),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAO));
   x+=size;
   SetLabel("label"+(string)i,"Awesome oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalICH),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalICH));
   x+=size;
   SetLabel("label"+(string)i,"Ichimoku Kinko Hyo",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);

   return(rates_total);
  }

Bueno, nuestro indicador está listo. Al final tenemos la siguiente figura en el gráfico.



5. Hacerlo como un Expert Advisor

De la misma manera, podemos escribir en Expert Advisor que muestre la señal del indicador en el gráfico. Vamos a implementar un sistema de información, con elementos de control gráfico. Se puede elegir el indicador necesario y ajustar sus parámetros a través de la interfaz gráfica.


No vamos a describir la creación de la interfaz gráfica; puedes encontrar información relativa a la misma en el artículo Creación de paneles de control activo en MQL5 para el trading.

Para cambiar los ajustes de los indicadores a través de la interfaz gráfica, vamos a modificar nuestra librería SignalTrade.mqh y llamarla SignalTradeExp.mqh.

Primero, necesitamos variables adicionales para almacenar los ajustes de los indicadores.

//--- input parameters Moving Average
int                periodma1=8;
int                periodma2=16;
ENUM_MA_METHOD     MAmethod=MODE_SMA;
ENUM_APPLIED_PRICE MAprice=PRICE_CLOSE;
//--- input parameters MACD
int                FastMACD=12;
int                SlowMACD=26;
int                MACDSMA=9;
ENUM_APPLIED_PRICE MACDprice=PRICE_CLOSE;
//--- input parameters Price Channel
int                PCPeriod=22;
//--- input parameters Adaptive Channel ADX
int                ADXPeriod=14;
//--- input parameters Stochastic Oscillator
int                SOPeriodK=5;
int                SOPeriodD=3;
int                SOslowing=3;
ENUM_MA_METHOD     SOmethod=MODE_SMA;
ENUM_STO_PRICE     SOpricefield=STO_LOWHIGH;
//--- input parameters RSI
int                RSIPeriod=14;
ENUM_APPLIED_PRICE RSIprice=PRICE_CLOSE;
//--- input parameters CCI
int                CCIPeriod=14;
ENUM_APPLIED_PRICE CCIprice=PRICE_TYPICAL;
//--- input parameters WPR
int                WPRPeriod=14;
//--- input parameters Bollinger Bands
int                BBPeriod=20;
double             BBdeviation=2.0;
ENUM_APPLIED_PRICE BBprice=PRICE_CLOSE;
//--- input parameters Standard Deviation Channel
int                SDCPeriod=14;
double             SDCdeviation=2.0;
ENUM_APPLIED_PRICE SDCprice=PRICE_CLOSE;
ENUM_MA_METHOD     SDCmethod=MODE_SMA;
//--- input parameters Price Channel 2
int                PC2Period=22;
//--- input parameters Envelopes
int                ENVPeriod=14;
double             ENVdeviation=0.1;
ENUM_APPLIED_PRICE ENVprice=PRICE_CLOSE;
ENUM_MA_METHOD     ENVmethod=MODE_SMA;
//--- input parameters Donchian Channels
int                DCPeriod=24;
int                DCExtremes=3;
int                DCMargins=-2;
//--- input parameters Silver-channels
int                SCPeriod=26;
double             SCSilvCh=38.2;
double             SCSkyCh=23.6;
double             SCFutCh=61.8;
//--- input parameters NRTR
int                NRTRPeriod   =  40;
double             NRTRK        =  2.0;
//--- input parameters Alligator
int                ALjawperiod=13;
int                ALteethperiod=8;
int                ALlipsperiod=5;
ENUM_MA_METHOD     ALmethod=MODE_SMMA;
ENUM_APPLIED_PRICE ALprice=PRICE_MEDIAN;
//--- input parameters AMA
int                AMAperiod=9;
int                AMAfastperiod=2;
int                AMAslowperiod=30;
ENUM_APPLIED_PRICE AMAprice=PRICE_CLOSE;
//--- input parameters Ichimoku Kinko Hyo
int                IKHtenkansen=9;
int                IKHkijunsen=26;
int                IKHsenkouspanb=52;

Sustituir los valores constantes de los indicadores por las variables. No hay que cambiar nada más.

h_ma1=iMA(Symbol(),Period(),periodma1,0,MAmethod,MAprice);

Un punto importante es el ahorro en el uso de la memoria del ordenador; la cambiar los ajustes es necesario descargar una copia del indicador con los ajustes antiguos y cargar una nueva. Esto se puede hacer mediante la función:

bool  IndicatorRelease(
   int       indicator_handle,     // indicator handle
   );

   if(id==CHARTEVENT_OBJECT_ENDEDIT && sparam=="PIPSetEditMA2")
     {
      periodma2=(int)ObjectGetString(0,"PIPSetEditMA2",OBJPROP_TEXT);
      ObjectSetString(0,"PIPSetEditMA2",OBJPROP_TEXT,(string)periodma2);
      //--- unload old copy of the indicator
      IndicatorRelease(h_ma2);
      //--- create new copy of the indicator
      h_ma2=iMA(Symbol(),Period(),periodma2,0,MAmethod,MAprice);
      ChartRedraw();
     }

Conclusión

Hemos aprendido a leer la información de los indicadores y enviarla a un Expert Advisor. De esta manera, puedes obtener señales desde cualquier indicador.

Nota