¡Cree su propio robot de trading en 6 pasos!

MetaQuotes | 25 diciembre, 2013


Una vez más sobre el MQL5 Wizard

Nuestro mundo está cambiando a nuestro alrededor rápidamente e intentamos mantenernos actualizados. No tenemos tiempo para aprender cosas nuevas y esta actitud es normal en el ser humano. Los operadores son personas como las demás que quieren obtener los mejores resultados con el menor esfuerzo. MetaEditor 5 ofrece un maravilloso MQL5 Wizard pensado especialmente para operadores. Hay varios artículos que describen cómo crear un sistema de trading automatizado usando el wizard, incluyendo una versión "light" MQL5 Wizard para tontos y una "versión de desarrolladores", el MQL5 Wizard: Nueva versión.

Todo pinta muy bien, podemos crear un robot de trading en 5 clics de ratón y podemos probarlo en el probador de estrategias y optimizar los parámetros del sistema de trading, y podemos dejar que el robot opere por cuenta nuestra sin necesidad de hacer nada más manualmente. Pero el problema surge cuando un operador/programador de MQL5 quiere crear algo por sí mismo, algo único que nunca ha sido descrito en ninguna parte, y va a escribir su propio módulo de señales de trading. El operador abre la documentación de MQL5, obtiene la librería estándar y le horroriza ver...


Cinco clases horribles

Es cierto, el MQL5 Wizard simplifica enormemente la creación de asesores expertos, pero primero necesitamos aprender qué usar como entrada para ello. Para crear automáticamente un asesor experto usando el MQL5 Wizard, debe asegurarse de que sus componentes se adhieren a cinco clases básicas de la sección Clases básicas de los asesores expertos:

Esta es toda la fuerza del "gran y terrible" enfoque llamado programación orientada a objetos (POO). Pero no se asuste, ahora casi todo el mundo tiene un móvil con muchas funciones y casi nadie sabe como funcionan. No necesitamos estudiar todo esto, solo veremos algunas funciones de la clase CExpertSignal.


En este artículo veremos las etapas para la creación de un módulo para señales de trading, y verá cómo hacer esto sin tener que aprender POO o las clases. Pero si quiere, puede ir un poco más lejos.


1. Crear una clase desde cero

No vamos a alterar ningún módulo de señales de trading existente según necesitemos, ya que esta sería una forma de confundirnos. Tan solo escribiremos nuestra propia clase, pero primero usaremos el navegador para crear una nueva carpeta para almacenar nuestras señales en MQL5/Include/Expert/.



Haga clic con el botón derecho del ratón sobre la carpeta que hemos creado, seleccione "Nuevo archivo" y cree una nueva clase para nuestro módulo de señales de trading.


Rellene los campos:

Haga clic en "Finalizar" y ya tendremos un borrador para nuestro módulo. Es fácil hasta ahora. Solo necesitamos añadir al archivo resultante la declaración #include para que el compilador sepa donde encontrar la clase básica CExpertSignal.

#include "..\ExpertSignal.mqh"   // CExpertSignal está en el archivo ExpertSignal

El resultado:

//+------------------------------------------------------------------+
//|                                                     MA_Cross.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "..\ExpertSignal.mqh"   // CExpertSignal está en el archivo ExpertSignal
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class MA_Cross : public CExpertSignal
  {
private:

public:
                     MA_Cross();
                    ~MA_Cross();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MA_Cross::MA_Cross()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MA_Cross::~MA_Cross()
  {
  }
//+------------------------------------------------------------------+

Verifique la clase obtenida (debe estar libre de errores de compilación) y haga clic en F7. No hay errores y podemos continuar.


2. Un controlador para el módulo

Nuestra clase está completamente vacía, no tiene errores y podemos probarla. Vamos a crear un nuevo asesor experto en el MQL5 Wizard basándonos en ella. Llegamos al momento en el que debemos seleccionar un módulo de señales de trading y vemos... que nuestro módulo no está ahí.


Y ¿cómo podemos conseguir que se encuentre ahí? No añadimos ninguna instrucción para que el MQL5 Wizard comprendiera que nuestra clase podría ser de alguna utilidad. Vamos a solucionarlo. Si miramos los módulos del paquete estándar, veremos que cada uno de ellos contiene un encabezado al principio del archivo. Este es el controlador del módulo compilado según ciertas reglas. Y las reglas son muy simples.

Abra, por ejemplo, el código fuente del módulo de las señales de trading basadas en AMA (vea la descripción en Señales de la media móvil adaptable). Y ejecute el MQL5 Wizard eligiendo este módulo. Compare:

El último bloque en el controlador se refiere a los parámetros del módulo, la primera línea contiene el nombre del módulo que se mostrará en pantalla en el MQL5 Wizard. Como puede ver, no hay nada complicado. De esta forma, el controlador de cada módulo contiene las siguientes entradas:

A continuación viene la descripción de los parámetros en forma de Parameter=list_of_values, en los que se especifican los siguientes (separados por comas):

  1. El nombre de la función para establecer el valor del parámetro al iniciar el asesor experto.
  2. El tipo de parámetro puede ser de enumeración.
  3. El valor por defecto para el parámetro, es decir, el valor al que se establecerá el parámetro si no lo cambiamos en el MQL5 Wizard.
  4. Descripción del parámetro, que podemos ver cuando iniciamos el asesor experto generado en el MQL5 Wizard.

Ahora, sabiendo todo esto vamos a crear el controlador de nuestro módulo de señales de trading. Por tanto, estamos escribiendo un módulo para obtener señales de trading en la intersección de dos medias móviles. Necesitamos establecer, al menos, cuatro parámetros externos:

Puede también añadir un cambio y el tipo de precios para calcular cada una de las medias móviles, pero esto no cambiaría nada en lo fundamental. Por tanto, la versión actual es como sigue:

// inicio de la descripción del wizard 
//+------------------------------------------------------------------+
//| Descripción de la clase                                          |
//| Title=Señales en la intersección de dos medias móviles           |
//| Type=SignalAdvanced                                              |
//| Name=My_MA_Cross                                                 |
//| ShortName=MaCross                                                |
//| Class=MA_Cross                                                   |
//| Page=Not needed                                                  |
//| Parameter=FastPeriod,int,13,Period of fast MA                    |
//| Parameter=FastMethod,ENUM_MA_METHOD,MODE_SMA,Method of fast MA   |
//| Parameter=SlowPeriod,int,21,Period of slow MA                    |
//| Parameter=SlowMethod,ENUM_MA_METHOD,MODE_SMA,Method of slow MA   |
//+------------------------------------------------------------------+
// final de la descripción del wizard

El controlador del módulo está listo y hemos descrito en él lo siguiente:

  1. El nombre mostrado en el MQL5 Wizard -"Señales en la intersección de dos medias móviles".
  2. Cuatro parámetros externos para configurar las señales de trading.
    • FastPeriod - el período de la media móvil rápida con el valor por defecto de 13.
    • FastMethod - el tipo de ajuste de la media móvil rápida, ajuste simple por defecto.
    • SlowPeriod - el período de la media móvil lenta con el valor por defecto de 21.
    • SlowMethod - el tipo de ajuste de la media móvil lenta, ajuste simple por defecto.

Guarde los cambios y compile. No debe haber ningún error. Ejecute el MQL5 Wizard para hacer una comprobación. Verá que nuestro módulo está ahora disponible para su selección ¡y muestra todos nuestros parámetros!


¡Enhorabuena, nuestro módulo de señales de trading tiene un aspecto impresionante!


3. Métodos para establecer parámetros

Ahora es el momento de trabajar con los parámetros externos. Como nuestro módulo de trading está representado por la clase MA_Cross, sus parámetros deben almacenarse en la misma clase que los miembros privados. Vamos a añadir líneas (iguales al número de parámetros) a la declaración de la clase. Ya hemos descrito el parámetro en el controlador y sabemos lo siguiente:

class MA_Cross : public CExpertSignal
  {
private:
   //--- Parámetros del módulo configurables
   int               m_period_fast;    // Período de la media móvil rápida
   int               m_period_slow;    // Período de la media móvil lenta
   ENUM_MA_METHOD    m_method_fast;    // Tipo de ajuste de la media móvil
   ENUM_MA_METHOD    m_method_slow;    // Tipo de ajuste de la media móvil lenta

Pero ¿cómo aparecen los valores de los parámetros externos en los miembros apropiados de nuestra clase MA_Cross? Todo es muy simple, solo necesita declarar métodos públicos con el mismo nombre en la clase, osea, añadir cuatro líneas a la sección pública:

class MA_Cross : public CExpertSignal
  {
private:
   //--- Parámetros del módulo configurables
   int               m_period_fast;    // Período de la media móvil rápida
   int               m_period_slow;    // Período de la media móvil lenta
   ENUM_MA_METHOD    m_method_fast;    // Tipo de ajuste de la media móvil
   ENUM_MA_METHOD    m_method_slow;    // Tipo de ajuste de la media móvil lenta
   //--- Constructor de la clase
                     MA_Cross();
   //--- Destructor de la clase
                    ~MA_Cross();
   //--- Métodos para establecer
   void              FastPeriod(int value)               { m_period_fast=value;        }
   void              FastMethod(ENUM_MA_METHOD value)    { m_method_fast=value;        }
   void              SlowPeriod(int value)               { m_period_slow=value;        }
   void              SlowMethod(ENUM_MA_METHOD value)    { m_method_slow=value;        }
   };

Cuando se genera un asesor experto sobre la base de este módulo usando el MQL5 Wizard y lo ejecutamos en el gráfico, estos cuatro métodos son llamados automáticamente al inicializarlo. Esta regla es simple:

Regla de creación del parámetro en el módulo: para cada parámetro que hemos declarado en el controlador, debemos crear un miembro privado en la clase para almacenar su valor y un miembro público para establecer un valor en él. El nombre del método debe coincidir con el nombre del parámetro.

Y lo último sería establecer valores por defecto para nuestros parámetros que serán usados en caso de que los métodos para establecer el valor no sean llamados. Cada variable declarada o miembro de clase deben inicializarse. Esta técnica permite evitar muchos errores.

Para la inicialización automática, el que mejor se adapta es el constructor de clase. Siempre es el primero en ser llamado cuando se crea un objeto. Para los valores por defecto, vamos a usar los que escribimos en el controlador del módulo.

class MA_Cross : public CExpertSignal
  {
private:
   //--- Parámetros del módulo configurables
   int               m_period_fast;    // Período de la media móvil rápida
   ENUM_MA_METHOD    m_method_fast;     // Tipo de ajuste de la media móvil rápida
   int               m_period_slow;    // Período de la media móvil lenta
   ENUM_MA_METHOD    m_method_slow;     // Tipo de ajuste de la media móvil lenta

public:
   //--- Constructor de la clase
                     MA_Cross(void);
   //--- Destructor de la clase
                    ~MA_Cross(void);
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
MA_Cross::MA_Cross(void) : m_period_fast(13),          // El período por defecto de la media móvil rápida es 3
                           m_method_fast(MODE_SMA),    // Método de ajuste por defecto de la media móvil rápida
                           m_period_slow(21),          // El período por defecto de de la media móvil rápida es 21
                           m_method_slow(MODE_SMA)     // Método de ajuste por defecto de la media móvil lenta
  {
  }

Aquí, los miembros de la clase son inicializados usando la lista de inicialización.

Como puede ver, no hemos usado aún indicadores de media móvil. Encontramos una simple regla: debe haber tantos métodos y miembros en la clase que implementen el módulo como parámetros se establecen en el controlador del módulo. ¡No hay nada complicado! Sin embargo, no olvide establecer los valores por defecto de los parámetros del constructor.


4. Verifique que los parámetros de entrada son correctos

Hemos creado parámetros para nuestro módulo de trading, hemos escrito métodos para establecer valores en ellos y ahora viene la siguiente fase importante: debe verificarse el grado en que los parámetros son correctos. En nuestro caso, debemos verificar los períodos de las medias móviles y el tipo de ajuste para su cálculo. Para este propósito debe escribir su propio método ValidationSettings() en la clase. Este método se define en la clase matriz CExpertBase y es redefinido obligatoriamente en todas sus clases subordinadas.

Pero si no tiene ningún conocimiento sobre programación orientada a objetos, recuerde -en nuestra clase debemos escribir la función ValidationSettings(), que no requiere parámetros y devuelve verdadero o falso.

class MA_Cross : public CExpertSignal
  {
private:
   //--- Constructor de la clase
                     MA_Cross(void);
   //--- Destructor de la clase
                    ~MA_Cross(void);
   //--- Verificando si los datos de entrada son correctos
   bool              ValidationSettings();
...
   };
//+------------------------------------------------------------------+
//| Verifica los parámetros de entrada y devuelve                    |
//| verdadero si todo es OK                                          |
//+------------------------------------------------------------------+
bool MA_Cross:: ValidationSettings()
  {
   //--- Llama al método de clase básica
   if(!CExpertSignal::ValidationSettings())  return(false);
   //--- Verifica los períodos, el número de barras para el cálculo de la media móvil >=1
   if(m_period_fast<1 || m_period_slow<1)
     {
      PrintFormat("¡Valor incorrecto para uno de los períodos! FastPeriod=%d, SlowPeriod=%d",
                  m_period_fast,m_period_slow);
      return false;
     }
//--- El período de la media móvil debe ser mayor que el de la media móvil rápida
   if(m_period_fast>m_period_slow)
     {
      PrintFormat("SlowPeriod=%d debe ser mayor que FastPeriod=%d!",
                  m_period_slow,m_period_fast);
      return false;
     }
//--- El tipo de ajuste de la media móvil rápida debe ser uno de los cuatro valores de la enumeración
   if(m_method_fast!=MODE_SMA && m_method_fast!=MODE_EMA && m_method_fast!=MODE_SMMA && m_method_fast!=MODE_LWMA)
     {
      PrintFormat("¡Tipo inválido del ajuste de la media móvil rápida!");
      return false;
     }
//--- El tipo de ajuste de la media móvil lenta debe ser uno de los cuatro valores de la enumeración
   if(m_method_slow!=MODE_SMA && m_method_slow!=MODE_EMA && m_method_slow!=MODE_SMMA && m_method_slow!=MODE_LWMA) 
     {
      PrintFormat("¡Tipo inválido del ajuste de la media móvil lenta!");
      return false;
     }
//--- Se han completado todas las comprobaciones, todo es ok
   return true;
  }
Como puede ver, en la parte pública de la clase MA_Cross hemos añadido la declaración del método ValidationSettings() y luego hemos añadido el cuerpo del método de la siguiente forma:
bool MA_Cross:: ValidationSettings()

Primero viene el tipo de retorno y después el nombre de la clase, a continuación el operador de resolución del alcance:: y todo esto es seguido por el nombre del método previamente declarado. No olvide que el nombre y tipo de los parámetros deben coincidir en la declaración y descripción del método de la clase. Sin embargo, el compilador le avisará de este error.

Tenga en cuenta que primero es llamado el método de la clase básica y a continuación se verifican los parámetros de entrada.

//--- llama al método de la clase básica
   if(!CExpertSignal::ValidationSettings())  return(false);
//--- Nuestro código para comprobar los valores de los parámetros

Si no añade esta línea, el asesor experto generado no podrá inicializar nuestro módulo de señales de trading.


5. ¿Dónde están nuestros indicadores?

Es el momento de trabajar con los indicadores, ya que todo el trabajo previo con los parámetros que estos requieren ha sido completado. Cada módulo de señales de trading contiene el método InitIndicators(), que es llamado automáticamente cuando ejecutamos el asesor experto generado. En este método debemos proporcionar los indicadores de medias móviles para nuestro módulo.

Primero, declaramos el método InitIndicators() en la clase y pegamos su borrador:

public:
   //--- Constructor de la clase
                     MA_Cross(void);
   //--- Destructor de la clase
                    ~MA_Cross(void);
   //--- Métodos para establecer
   void              FastPeriod(int value)               { m_period_fast=value;        }
   void              FastMethod(ENUM_MA_METHOD value)    { m_method_fast=value;        }
   void              SlowPeriod(int value)               { m_period_slow=value;        }
   void              SlowMethod(ENUM_MA_METHOD value)    { m_method_slow=value;        }
   //--- Comprobando si los datos de entrada son correctos
   bool              ValidationSettings();
   //--- Creando indicadores y series de tiempo para el módulo de las señales
   bool              InitIndicators(CIndicators *indicators);
  };
...
//+------------------------------------------------------------------+
//| Crea indicadores                                                 |
//| Entrada:  un puntero a una colección de indicadores              |
//| Salida: verdadero si tiene éxito, falso en caso contrario        |
//+------------------------------------------------------------------+
bool MA_Сross::InitIndicators(CIndicators* indicators)
  {
//--- Comprobación estándar para el conjunto de indicadores para NULL
   if(indicators==NULL)                           return(false);
//--- Inicializando indicadores y series de tiempo en filtros adicionales
   if(!CExpertSignal::InitIndicators(indicators)) return(false);
//--- Creando nuestros indicadores de medias móviles
   ... Algo de código aquí
//--- Llegados a esta parte la función tuvo éxito, devuelve verdadero
   return(true);
  }

Por tanto, no hay nada complicado, declaramos el método y a continuación simplemente creamos el cuerpo del método como hicimos para el método ValidationSettings(). Sobre todo, no olvide insertar el nombre de la clase y el operador :: en la definición de la función. Tenemos un borrador que podemos insertar en un código para crear medias móviles. Pero vamos a hacerlo de forma adecuada. Para cada indicador creamos una función separada en la clase, que devuelve verdadero si tiene éxito. La función puede tener cualquier nombre, pero hagamos que refleje su propósito y vamos a llamar a las funciones CreateFastMA() y CreateSlowMA().

protegido:
   //--- Creando indicadores de media móvil
   bool              CreateFastMA(CIndicators *indicators);
   bool              CreateSlowMA(CIndicators *indicators);
  };
//+------------------------------------------------------------------+
//| Crea indicadores                                                 |
//| Entrada:  un puntero a una colección de indicadores              |
//| Salida: verdadero si tiene éxito, falso en caso contrario        |
//+------------------------------------------------------------------+
bool MA_Cross::InitIndicators(CIndicators *indicators)
  {
//--- Comprobación estándar para el conjunto de indicadores para NULL
   if(indicators==NULL) return(false);
//--- Inicializando indicadores y series de tiempo en filtros adicionales
   if(!CExpertSignal::InitIndicators(indicators)) return(false);
//--- Creando nuestros indicadores de medias móviles
   if(!CreateFastMA(indicators))                  return(false);
   if(!CreateSlowMA(indicators))                  return(false);
//--- Llegados a esta parte la función tuvo éxito, devuelve verdadero
   return(true);
  }
//+------------------------------------------------------------------+
//| Crea el indicador de media móvil rápido                          |
//+------------------------------------------------------------------+
bool MA_Cross::CreateFastMA(CIndicators *indicators)
  {
... Algo de código
//--- Llegados a esta parte la función tuvo éxito, devuelve verdadero
   return(true);
  }
//+------------------------------------------------------------------+
//| Crea el indicador de media móvil lento                           |
//+------------------------------------------------------------------+
bool MA_Cross::CreateSlowMA(CIndicators *indicators)
  {
... Algo de código
//--- Llegados a esta parte la función tuvo éxito, devuelve verdadero
   return(true);
  }

Eso es todo, solo necesitamos escribir código que genere los indicadores de medias móviles e integre de alguna forma los controladores de estos indicadores en el módulo de trading para que el módulo pueda usar los valores de estos indicadores. Por esta razón, un puntero a una variable de tipo CIndicators es pasado como parámetro. Lo siguiente se encuentra en la documentación sobre este respecto:

CIndicators es una clase para agrupar instancias de series de tiempo y clases de indicadores técnicos. La clase CIndicators permite la creación de clases de indicadores técnicos, su almacenamiento y gestión (sincronización de datos, control y gestión de memoria).

Esto significa que debemos crear nuestros indicadores y ubicarlos en este grupo. Como los indicadores de la forma CIndicator y sus subordinados pueden almacenarse en el grupo, debemos usar este hecho. Usaremos CiCustom que es el subordinado mencionado anteriormente. Para cada media móvil comunicamos un objeto del tipo CiCustom en la parte privada de la clase:

class MA_Cross : public CExpertSignal
  {
private:
   CiCustom          m_fast_ma;            // El indicador como un objeto
   CiCustom          m_slow_ma;            // El indicador como un objeto
   //--- Parámetros del módulo configurables
   int              m_period_fast;   // Período de la media móvil rápida
   ENUM_MA_METHOD    m_method_fast;    // Tipo de ajuste de la media móvil rápida
   int              m_period_slow;   // Período de la media móvil lenta
   ENUM_MA_METHOD    m_method_slow;    // Tipo de ajuste de la media móvil lenta

Por supuesto, puede crear sus propias clases de indicador, que derivarán de CIndicator, e implementar todos los métodos necesarios para su uso con el MQL5 Wizard. Pero en este caso queremos mostrarle cómo puede usar cualquier indicador personalizado en el módulo de señales de trading usando CiCustom.

Así es como quedaría en el código:

//+------------------------------------------------------------------+
//| Crea el indicador de la media móvil rápida                       |
//+------------------------------------------------------------------+
bool MA_Cross::CreateFastMA(CIndicators *indicators)
  {
//--- Comprobando el puntero
   if(indicators==NULL) return(false);
//--- Añadiendo un objeto al grupo
   if(!indicators.Add(GetPointer(m_fast_ma)))
     {
      printf(__FUNCTION__+": Error al añadir un objeto de la media móvil rápida");
      return(false);
     }
//--- Estableciendo parámetros de la media móvil rápida
   MqlParam parameters[4];
//---
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="Examples\\Custom Moving Average.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_period_fast;      // Periodo
   parameters[2].type=TYPE_INT;
   parameters[2].integer_value=0;                  // Shift
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_method_fast;      // Método de promediación
//--- Inicialización del objeto  
   if(!m_fast_ma.Create(m_symbol.Name(),m_period,IND_CUSTOM,4,parameters))
     {
      printf(__FUNCTION__+": Error al inicializar el objeto de la media móvil rápida");
      return(false);
     }
//--- Número de buffers
   if(!m_fast_ma.NumBuffers(1)) return(false);
//--- Llegados a esta parte, la función ha tenido éxito, devuelve verdadero
   return(true);
  }

En el método CreateFastMA(), primero verificamos el puntero de la colección de indicadores y luego añadimos un puntero de la media móvil rápida m_fast_ma a esta colección. A continuación declaramos la estructura MqlParam, que está especialmente diseñada para almacenar parámetros de indicadores personalizados y rellenarlos con valores.

Usamos la media móvil personalizada del paquete del terminal estándar como indicador de media móvil personalizado. Debe elegirse un nombre de indicador que esté relacionado con la carpeta data_folder/MQL5/Indicators/. Como Custom Moving Average.mq5 del paquete estándar se encuentra en data_folder/MQL5/Indicators/Examples/, especificamos su ruta incluyendo la carpeta Examples:

parameters[0].string_value="Examples\\Custom Moving Average.ex5";

Si observa el código para este indicador puede ver todos los datos necesarios:

//--- parámetros de entrada
input int            InpMAPeriod=13;       // Período
input int            InpMAShift=0;         // Cambio
input ENUM_MA_METHOD InpMAMethod=MODE_SMMA;  // Método

Los valores de la estructura contienen los pares tipo-valor:

  1. tipo de parámetro - string (para transferir el nombre del indicador)
  2. el nombre del archivo ejecutable del indicador personalizado - "Custom Moving Averages.exe"
  3. tipo de parámetro - int (valor del período)
  4. período de la media móvil
  5. tipo de parámetro - int (valor de cambio)
  6. cambio horizontal en la media en barras
  7. tipo de parámetro - int (el valor de enumeración es un entero)
  8. método de promediación

Tras rellenar la estructura el indicador es inicializado por el método Create() de todos los parámetros requeridos: nombre de símbolo y período de tiempo en el que es calculado, el tipo de indicador de la enumeración de ENUM_INDICATOR, el número de parámetros de indicador y la estructura MqlParam con los valores del parámetro. Y el último especifica el número de buffers de indicador usando el método NumBuffers().

El método CreateSlowMA() para crear la media móvil lenta es simple. Al usar indicadores personalizados en el módulo, no olvide que el asesor experto generado por el MQL5 Wizard se ejecutará también en el probador. Por tanto, al comienzo de nuestro archivo añadimos la propiedad #property tester_indicator que se comunica al probador la ubicación de los indicadores requeridos:

#include "..\ExpertSignal.mqh"   // La clase CExpertSignal está en el archivo ExpertSignal
#property tester_indicator "Examples\\Custom Moving Average.ex5"

Si usamos varios indicadores distintos debemos añadir esta línea a cada uno de ellos. Por tanto, hemos añadido los indicadores. Para mayor comodidad, vamos a ver dos métodos para recibir valores de medias móviles:

   //--- Comprobando si los datos de entrada son correctos
   bool              ValidationSettings(void);
   //--- Creando indicadores y series de tiempo para el módulo de señales
   bool              InitIndicators(CIndicators *indicators);
   //--- Acceso a los datos del indicador
   double            FastMA(const int index)             const { return(m_fast_ma.GetData(0,index)); }
   double            SlowMA(const int index)             const { return(m_slow_ma.GetData(0,index)); }

Como puede ver, los métodos son muy simples. Utilizaron el método GetData() de la clase matriz SIndicator, que devuelve un valor a partir del buffer de indicador especificado en la posición señalada.

Si necesita clases para trabajar con indicadores clásicos del paquete estándar, estas se encuentran disponibles en la sección clases para el trabajo con indicadores. Estamos preparados para seguir con la etapa final.


6. Definir los métodos LongCondition y ShortCondition

Todo está listo para hacer que nuestro módulo funcione y genere señales de trading. Esta funcionalidad es proporcionada por dos métodos que deben ser descritos en cada subordinado de CExpertSignal:

Si la función devuelve un valor nulo, significa que no hay señal de trading. Si se dan las condiciones para la señal, puede estimar la intensidad de la señal y devolver cualquier valor que no exceda 100. La evaluación de la intensidad de la señal le permitirá elaborar con cierta flexibilidad sistemas de trading basados en varios módulos y modelos de mercado. Puede leer más sobre esto en el MQL5 Wizard: nueva versión.

Como estamos escribiendo un simple módulo de señales de trading, podemos acordar que las señales buy y sell tengan el mismo valor (100). Vamos a añadir los métodos necesarios en la declaración de la clase.

   ...
   bool              InitIndicators(CIndicators *indicators);
   //--- Acceso a los datos de los indicadores
   double            FastMA(const int index)             const { return(m_fast_ma.GetData(0,index)); }
   double            SlowMA(const int index)             const { return(m_slow_ma.GetData(0,index)); }
   //--- Comprobando las condiciones buy y sell
   virtual int       LongCondition();
   virtual int       ShortCondition();

También vamos a crear la descripción de las funciones. Así es cómo la señal buy es verificada (todo es similar con la señal sell)

//+------------------------------------------------------------------+
//| Devuelve la intensidad de la señal buy                           |
//+------------------------------------------------------------------+
int MA_Cross::LongCondition()
  {
   int signal=0;
//--- Para operar con ticks idx=0, para operar con barras idx=1
   int idx=StartIndex();
//--- Valores de las medias móviles en la última barra formada
   double last_fast_value=FastMA(idx);
   double last_slow_value=SlowMA(idx);
//--- Valores de las medias móviles en la última barra formada menos una
   double prev_fast_value=FastMA(idx+1);
   double prev_slow_value=SlowMA(idx+1);
//---Si la media móvil rápida se cruza con la media móvil lenta desde abajo hacia arriba sobre las dos barras cerradas
   if((last_fast_value>last_slow_value) && (prev_fast_value<prev_slow_value))
     {
      signal=100; // hay una señal para buy
     }
//--- Devuelve el valor de la señal
   return(signal);
  }

Observe que hemos declarado la variable idx a la que se le asigna el valor devuelto por la función StartIndex() de la clase matriz CExpertBase. La función StartIndex() devuelve 0 si el asesor experto está diseñado para trabajar en todos los ticks y en este caso el análisis comienza con la barra actual. Si el asesor experto está diseñado para trabajar a precios abiertos, StartIndex() devuelve 1 y el análisis comienza con la última barra formada.

Por defecto, StartIndex() devuelve 1, lo que significa que el asesor experto generado por el MQL5 Wizard solo se ejecutará con la apertura de una nueva barra e ignorará los ticks entrantes durante la formación de la barra actual.

Más adelante, en el retoque final, veremos cómo activar este modo y cómo este puede ser usado.

El módulo está listo para usarse, luego vamos a crear un robot de trading en el MQL5 Wizard basándonos en este módulo.


Comprobando un asesor experto en el probador de estrategias

Para probar la eficiencia de nuestro modulo vamos a generar un asesor experto basándonos en el MQL5 Wizard y vamos a ejecutarlo en el gráfico. Las pestañas "Entradas" de la ventana de inicio que aparece contienen los parámetros del módulo MA_Cross.

Todos los demás parámetros han sido también añadidos por el MQL5 Wizard mientras se generaba elasesor experto en el módulo de gestión de dinero seleccionado y en el módulo de mantenimiento de la posición (Trailing Stop). De esta forma, tuvimos que escribir un módulo de señales de trading y obtuvimos una solución. Esta es la principal ventaja de usar MQL5 Wizard. 

Vamos ahora a probar el robot de trading en el probador de estrategias de Meta Trader 5. Vamos a intentar realizar una optimización rápida de los parámetros clave.

En los ajustes de estos parámetros se requieren más de medio millón de pasadas para una optimización completa. Por tanto, elegimos la optimización rápida (algoritmo genético) y utilizamos adicionalmente la red en la nube de MQL5 para acelerar la optimización. La optimización se ha realizado en 10 minutos y ya tenemos los resultados.


Como puede comprobar, la creación de un robot de trading y la optimización de los parámetros de entrada ha llevado mucho menos tiempo de lo que hubiera requerido escribir la lógica del servicio de gestión de la posición, el depurado y la búsqueda de los mejores algoritmos. 


El toque final

Puede prescindir de este apartado o volver a él más tarde cuando se sienta completamente cómodo con la técnica de escribir un módulo de señales de trading.

Si abre el código fuente del asesor experto generado por el MQL5 Wizard encontrará la variable global Expert_EveryTick con el valor falso. Con base en esta variable, la función StartIndex() devuelve su valor. Comunica al asesor experto el módulo que debe ejecutar.

//+------------------------------------------------------------------+
//|                                                 TestMA_Cross.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- señales disponibles
#include <Expert\MySignals\MA_Cross.mqh>
//---  trailing disponible 
#include <Expert\Trailing\TrailingNone.mqh>
//--- gestión de dinero disponible
#include <Expert\Money\MoneyFixedLot.mqh>
//+------------------------------------------------------------------+
//| Entradas                                                         |
//+------------------------------------------------------------------+
//--- entradas para el experto
input string         Expert_Title             ="TestMA_Cross"; // Nombre del documento
ulong               Expert_MagicNumber       =22655;           // Identificador del Expert Advisor
bool                  Expert_EveryTick             =false;     // Funcionamiento del Expert Advisor dentro de la barra
//--- entradas para la señal principal
input int            Signal_ThresholdOpen     =10;             // Valor umbral de la señal para abrir [0...100]
input int            Signal_ThresholdClose    =10;             // Valor umbral de la señal para cerrar [0...100]

Si establece Expert_EveryTick con valor verdadero y compila el código, el robot de trading analizará cada tick entrante y tomará decisiones sobre los valores de la barra incompleta actual. Haga esto solo si entiende cómo funciona. No todos los sistemas de trading están pensados para trabajar dentro de la barra.

Puede añadir también una entrada de palabra clave para el parámetro Expert_EveryTick y tendrá un nuevo parámetro de enrtada del asesor experto que podrá establecer al inicio del este último sobre un gráfico o en el probador.

input bool          Expert_EveryTick         =false;          // Funcionamiento del asesor experto dentro de la barra

Y ahora es el momento de resumir lo que hemos hecho.


6 pasos para crear un módulo de señales de trading

Si ha dominado MQL5, ya no necesitará escribir más un asesor experto desde cero. Cree un módulo de señales de trading y, basándose en este módulo, genere automáticamente un robot de trading con los módulos de gestión de volumen de transacciones y de rastreo habilitados. Incluso si no está familiarizado con la POO o no quiere profundizar mucho en la estructura de las clases trade, puede seguir los siguientes 6 pasos:

  1. Crear una nueva clase usando el MQL5 Wizard en una carpeta independiente MQL5/Include/MySignals/. Nuestro módulo de señales de trading se almacenará ahí.
  2. Crear un módulo handle que describa los parámetros, su tipo y sus valores por defecto.
  3. Declarar parámetros de módulo en la clase y añadir métodos para la inicialización en el constructor.
  4. Comprobar los parámetros de entrada y no olvidar llamar a ValidationSettings() de la clase básica CExpertSignal.
  5. Crear objetos-indicadores y añadir un método de inicialización predefinido InitIndicators().
  6. Identificar las condiciones de las señales de trading en los métodos LongCondition() yShortCondition().

Todos estos pasos son muy simples y requieren poco conocimiento de programación en MQL5. Solo necesita escribir un módulo una vez siguiendo las instrucciones y la verificación posterior de cualquier idea de trading le llevará no más de una hora, sin tener que pasar agotadoras horas creando código y compilando. 


De lo simple a lo complejo

Recuerde que la estrategia de trading implementada por su robot de trading usada en el MQL5 Wizard es tan compleja como el módulo de señales de trading que utilice. Pero antes de empezar a construir un complejo sistema de trading con base en un conjunto de reglas de entrada y salida, divídalo en varios sistemas simples y verifique cada uno por separado.

Basándose en módulos simples podrá crear complejas estrategias de trading usando los módulos de señales de trading ya diseñados, ¡pero este es un tema para otro artículo!