English Русский 中文 Deutsch 日本語 Português
Utilizando redes neuronales en MetaTrader

Utilizando redes neuronales en MetaTrader

MetaTrader 4Ejemplos | 17 febrero 2012, 16:14
4 022 1
Mariusz Woloszyn
Mariusz Woloszyn

Introducción

Los más probable es que algunos de ustedes hayan estudiado la posibilidad de usar redes neuronales en sus asesores.

Este tema se ha convertido en algo especialmente actual tras la impresionante victoria de Better, con su sistema basado en redes neuronales, en el Automated Trading Championship del año 2007. Muchos foros de Internet se han llenado de temas relacionados con redes neuronales y su aplicación en el comercio en Fórex.

Por desgracia, la implementación programática de las redes neuronales en MQL4 es un asunto complicado. Será necesario el uso de ciertas habilidades de programación, y el resultado no será muy efectivo, especialemente si usted quiere comprobar su resultado final en el simulador con una gran cantidad de datos.

En este artículo mostraré de qué forma usted podrá usar la biblioteca de libre difusión Fast Artificial Neural Network Library (FANN) en sus programas en MQL4, evitando además ciertas trampas y limitaciones.

A continuación supondremos que el lector ya está familiarizado con las redes neuronales artificiales y la terminología correspondiente, así que nos concentraremos en los aspectos prácticos de la implementación concreta de las redes neuronales en MQL4.

Particularidades de la biblioteca FANN

Para comprender en profundidad las posibilidades del uso de la biblioteca FANN hay que familiarizarse con la documentación y las funciones más utilizadas. El típico ejemplo de uso de FANN incluye la creación de una red neuronal sencilla de distribución directa, su entrenamiento con un cierto conjunto de datos y su inicio. Para su uso posterior, la red neuronal creada y entrenada puede guardarse en un archivo para su recuperación correspondiente. Para crear una red, hay que usar la función fann_create_standard().

Veámosla:

FANN_EXTERNAL struct fann *FANN_API fann_create_standard(unsigned int num_layers, int lNnum, ... )

donde num_layers es el número total de capas en la red neuronal, incluyendo las capas de entrada y salida. La variable lNnum y las siguientes indican el número de neuronas en cada capa, comenzando por la capa de entrada y terminando por la de salida. Para crear un red con una capa oculta de 5 neuronas, una capa de entrada con 10 entradas y una capa de salida con una neurona, hay que llamarla de la siguiente forma:

fann_create_standard(3,10,5,1);

Ahora que ya hemos creado la red, la siguiente opercación consistirá en entrenarla con ciertos datos de entrada y salida.

El método más sencillo de entrenamiento es el entrenamiento incrementado, que se puede implementar con el uso de la siguiente función:

FANN_EXTERNAL void FANN_API fann_train(
        struct  fann    *       ann,

        
        fann_type       *       input,

        
        fann_type       *       desired_output  )



Esta función usa el puntero a la estructura struct fann (que ha sido retornado anteriormente por la función fann_create_standard()), así como los vectores de entrada y salida, que tienen el tipo fann_type.

El tipo se define al compilar la biblioteca (puede ser double or float). En esta implementación los vectores de entrada y salida son matrices del tipo double.

Después de que la red haya sido entrenada, la pondremos a prueba en la práctica. La función que implementa este paso tiene el aspecto siguiente:

FANN_EXTERNAL fann_type * FANN_API fann_run(    struct  fann    *       ann,

        
        fann_type       *       input   )


Esta función usa el puntero a una estructura del tipo struct fann, que representa una red neuronal creada y un vector de entrada input (matriz del tipo double). El valor retornado es un vector de salida en forma de matriz. Incluso si solo hay parámetro de salida, siempre podremos obtener la matriz (si es uno, unidimensional), y no el propio valor.

La mayoría de las funciones de la biblioteca FANN usan un puntero a la estructura struct fan, y puesto que MQL4 no da soporte a estructuras como tipos de datos, no es posible trabajar con ellas directamente. Para evitar estas restricciones, deberemos construir de alguna forma un "envoltorio" (wrapper).

El método más sencillo es crear una matriz de punteros a las estructuras del tipo struct fann, que contenga los valores correspondientes y hacer referencia a ellas con un índice de tipo entero int. De esta forma podremos sustituir los tipos de variable que no tienen soporte y crear una "biblioteca-envoltorio", que puede ser fácilmente integrada con MQL.

Construyendo un envoltorio FANN (wrapping)

Por lo que tengo entendido, MQL4 no da soporte a funciones con un número variable de argumentos y deberemos resolver este problema. Por otra parte, si la función C (que tiene un número variable de argumentos) se llama con un gran número de argumentos, es algo normal, puesto que este problema lo podemos resolver si indicamos de la forma correspondiente la descripción de la función importada.


La función final para el "envoltorio" tiene el siguiente aspecto.

/* Crea una red neuronal estándar de conexión completa con distribución inversa del error (backpropagation neural network)
* num_layers - número total de capas, incluyendo las capas de entrada y salida.
* l1num - número de neuronas en la 1-era capa (entradas)
* l2num, l3num, l4num - número de neuronas en las capas ocultas y en la de salida (dependiendo del número num_layers).
* Retorna:
* índice del manejador de la neurored, -1 en caso de error
*/

int __stdcall f2M_create_standard(unsigned int num_layers, int l1num, int l2num, int l3num, int l4num);

Hemos cambiado fann_ a f2M_ (para FANN en MQL), aquí se usa un número fijo de argumentos (4 capas) y el valor retornado, el índice de la matriz interna de la neurored del tipo struct fann, que es utilizado por la biblioteca FANN.

Lo mismo para la función de entrenamiento:

/* Realiza una iteración de entrenamiento usando los conjuntos de datos de entrada y salida establecidos.
* El entrenamiento siempre se incrementa, puesto que se presenta solo un patrón.
* ann - manejador de la neurored, obtenido antes con la función f2M_create_*
* *input_vector - matriz de datos de entrada
* *output_vector - matriz de datos de salida
* Retorna:
* 0 en caso de éxito y -1 en caso de error
*/

int __stdcall f2M_train(int ann, double *input_vector, double *output_vector);

y

/* Ejecutar el procedimiento de cálculo de los valores de salida de la neurored FANN
* ann - manejador de la neurored retornado por la función f2M_create_*
* *input_vector - matriz de datos de entrada
* Retorna:
* 0 en caso de éxito, un valor negativo en caso de error
* Observación:
* Para obtener el resultado del cálculo del vector de salida, use la función f2M_get_output().
* Además, los valores antiguos de las salidas se reescribirán
*/

int __stdcall f2M_run(int ann, double *input_vector);

Y por fin, para eliminar una neurored ya creada, hay que llamar la función:

/* Eliminar neurored fann
* ann - manejador de la neurored retornado por la función f2M_create_*
* Retorna:
* 0 en caso de éxito, -1 en caso de error
* ADVERTENCIA: los procesadores no pueden ser usados de nuevo, si ann!=(_ann-1)
* Los otros procesadores pueden ser usados de nuevo solo después de eliminar la última neurored.
*/

int __stdcall f2M_destroy(int ann);

Para liberar los manejadores de las neuroredes, hay que eliminar todas las neuroredes en el orden inverso al que fueron creadas.

Asimismo, se puede usar un método alternativo, la siguiente función:

/* Eliminar todas las neuroredes creadas por la biblioteca FANN
* Retorna:
* 0 en caso de éxito y -1 en caso de error
*/
int __stdcall f2M_destroy_all_anns();

Sin embargo, estoy seguro de que algunos de ustedes preferirán guardar su neurored entrenada para usarla posteriormente. Esto se puede hacer con la ayuda de la función:

/* Guardar la neurored al completo en un archivo de configuración
* ann - manejador de la neurored, definido por la función f2M_create*
* Retorna:
* 0 en caso de éxito -1, en caso de error
*/
int __stdcall f2M_save(int ann,char *path);

La neurored guardada puede ser cargada (o más bien reconstruida) con la ayuda de la función:

/* Carga la neurored del archivo
* path - ruta al archivo con la expansión".net"
* Retorna:
* manejador de la neurored, -1 en caso de error
*/
int __stdcall f2M_create_from_file(char *path);

Ahora que conocemos las funciones esenciales, podemos intentar usarlas en nuestros asesores, pero primero debemmos instalar la biblioteca Fann2MQL.

Instalando la biblioteca Fann2MQL

Para que sea más cómodo usar la biblioteca, he creado un programa de instalación, que contiene todas las bibliotecas originales y compiladas y el archivo Fann2MQL.mqh con la descripción de todas las funciones de la biblioteca. La instalación es bastante sencilla. En primer lugar, usted verá la información sobre las condiciones de usode la biblioteca Fann2MQL en el marco de la licencia GPL:


Instalando la biblioteca Fann2MQL, paso 1

Después elija una carpeta para instalar la biblioteca. Usted podrá usar por defecto la ruta Program Files\Fann2MQL\ o instalarla directamente en el catálogo MetaTrader\experts\. Todos los archivos serán copiados en la carpeta indicada, en el caso necesario, usted podrá copiarlos manualmente en el lugar que desee.



Instalando la biblioteca Fann2MQL, paso 2

El programa de instalación ubicará los archivos en las siguientes carpetas:


Catálogo include\ folder


Catálogo libraries\ folder


Catálogo src\ folder

Si usted ha elegido para la instalación la carpeta "Fann2MQL", copie su contenido (los catálogos include y libraries) en el catálogo donde esté MetaTrader. El programa de instalación copia los archivos de la biblioteca en el catálogo de bibliotecas de sistema de Windows (normalmente es Windows\system32).

Los códigos fuente de la de la biblioteca Fann2MQL se encuentran en la carpeta srс. Si usted quiere analizar en profundidad las implementaciones de la biblioteca, eche un vistazo al código fuente. Asimismo, usted podrá mejorar el código añadiendo funciones adicionales, si así lo desea. Si ha modificado los códigos fuente y ha logrado implementar algo interesante, por favor, envíemelo.

Utilizando las redes neuronales en el asesor

Después de que la biblioteca Fann2MQL esté instalada, usted podrá comenzar a escribir sus propios expertos e indicadores. Existen muchas variantes de uso de las neuroredes. Usted podrá usarlas para analizar los futuros movimientos del precio, pero la calidad de estos pronósticos es bastante dudosa. También puede escribir su propia estrategia utilizando los métodos de aprendizaje por refuerzo (

Reinforcement learning), por ejemplo Q-Learning o algo semejante.


Usted puede tratar de usar las neuroredes como filtro de señalesen su experto o combinar estos métodos de forma conjunta, añadiéndole todo lo que quiera. El único límite es su imaginación.

Aquí mostraré un ejemplo de cómo se puede usar una reurored como filtro sencillo de las señales creadas por MACD No hay que concebir este experto como solución acabada, se trata de un sencillo ejemplo de uso de la biblioteca Fann2MQL. Usando el funcionamiento del experto NeuroMACD.mq4 como ejemplo, mostraremos cómo la biblioteca Fann2MQL puede usarse de forma efectiva en MQL.

Para cualquier asesor primero se declaran las variables globales, las definiciones y la sección include. El comienzo del experto NeuroMACD.mq4 tiene el aspecto siguiente:

// Incluimos la biblioteca FANN2MQL
#include <Fann2MQL.mqh>

// Definimos las variables globales
#define ANN_PATH "C:\\ANN\\"
// Nombre del asesor
#define NAME "NeuroMACD"

//---- Parámetros de entrada
extern double Lots=0.1;
extern double StopLoss=180.0;
extern double TakeProfit=270.0;
extern int FastMA=18;
extern int SlowMA=36;
extern int SignalMA=21;
extern double Delta=-0.6;
extern int AnnsNumber=16;
extern int AnnInputs=30;
extern bool NeuroFilter=true;
extern bool SaveAnn=false;
extern int DebugLevel=2;
extern double MinimalBalance=100;
extern bool Parallel=true;

// Variables globales 

// Ruta al catálogo con las neuroredes
string AnnPath;

// Magic number para el comercio
int MagicNumber=65536;

// AnnsArray[ann#] - matriz de las neuroredes
int AnnsArray[];

// bandera del estado de la carga de todas las neuroredes
bool AnnsLoaded=true;

// AnnOutputs[ann#] - matriz de las salidas de la neurored
double AnnOutputs[];

// InputVector[] - matriz de los datos de entrada de la neurored
double InputVector[];

// Número de ticket de la posición larga
int LongTicket=-1;

// Número de ticket de la posición corta
int ShortTicket=-1;

// Valores guardados de las entradas para la posición larga y corta.
double LongInput[];
double ShortInput[];

La directiva "include" revela la necesidad de cargar el archivo de encabezamiento Fann2MQL.mqh, que contiene la descripción de todas las funciones de la biblioteca Fann2MQL. Después de esto, todas las funciones de la biblioteca estarán disponibles para su uso en un programa MQL.

La constante ANN_PATH establece la ruta en la que se encuentran los archivos con las redes entrenadas. En este caso, deberemos crear esta carpeta en el catálogo raíz en el disco C, es decir C:\ANN.

La constante de línea NAME contiene el nombre de la neurored, la usaremos en lo sucesivo para guardar y cargar las neuroredes entrenadas. Los parámetros de entrada son obvios y no se describirán en lo sucesivo, al igual que las variables globales.

El punto de entrada de cualquier asesor es su función init():

int init()
  {
   int i,ann;

   if(!is_ok_period(PERIOD_M5)) 
     {
      debug(0,"Wrong period!");
      return(-1);
     }

   AnnInputs=(AnnInputs/3)*3; // Deberá ser un número entero, 
                              // divisible por 3 sin resto

   if(AnnInputs<3) 
     {
      debug(0,"¡Los datos de entrada son insuficientes!");
     }
// Calculamos MagicNumber y la ruta AnnPath
   MagicNumber+=(SlowMA+256*FastMA+65536*SignalMA);
   AnnPath=StringConcatenate(ANN_PATH,NAME,"-",MagicNumber);

// Inicializamos las neuroredes
   ArrayResize(AnnsArray,AnnsNumber);
   for(i=0;i<AnnsNumber;i++) 
     {
      if(i%2==0) 
        {
         ann=ann_load(AnnPath+"."+i+"-long.net");
           } else {
         ann=ann_load(AnnPath+"."+i+"-short.net");
        }
      if(ann<0)
         AnnsLoaded=false;
      AnnsArray[i]=ann;
     }
   ArrayResize(AnnOutputs,AnnsNumber);
   ArrayResize(InputVector,AnnInputs);
   ArrayResize(LongInput,AnnInputs);
   ArrayResize(ShortInput,AnnInputs);

// Inicializamos los flujos (Intel TBB threads)
   f2M_parallel_init();

   return(0);
  }

Primero se comprueba que el marco temporal en el que funciona el experto sea correcto (PERIOD_M5).

La variable AnnInputs contiene el número de entradas de la neurored. Puesto que vamos a usar 3 conjuntos de datos con diferentes argumentos, deberá ser divisible por 3 sin resto. AnnPath se indica de tal forma que contenga la información sobre el nombre del asesor NAME y MagicNumber, que se calcula como la función de los parámetros de entrada SlowMA, FastMA y SignalMA, que en lo sucesivo se usan al calcular el indicador MACD.

Puesto que la ruta AnnPath ya ha sido establecida, el experto intenta cargar las redes neuronales con la ayuda de la función ann_load(), que se describirá más abajo. La mitad de las neuroredes cargadas está destinada al filtrado de las posiciones largas, y la otra mitad al filtrado de las cortas.

La variable AnnsLoaded se usa para registrir el hecho de la correcta inicialización de todas las neuroredes. Como seguramente habrá adivinado, el asesor que estamos analizando intenta cargar varias neuroredes.

No estoy seguro de que esto sea imprescindible, pero por si acaso, para usar todo el potencial de la biblioteca Fann2MQL, que procesa paralelamente varias neuroredes, se ha añadido la posibilidad de usar varios núcleos del procesador. Esta posibilidad se ha implementado con el uso de la tecnología Intel® Threading Building Blocks. Para inicializar esta interfaz se usa la función f2M_parallel_init().

Este es el método que yo uso para inicializar las neuroredes:

int ann_load(string path)
  {
   int ann=-1;

   /* Cargar neurored */
   ann=f2M_create_from_file(path);
   if(ann!=-1) 
     {
      debug(1,"Нейросеть: '"+path+"' успешно загружена. Su manejador: "+ann);
     }
   if(ann==-1) 
     {

      /* Crear neurored */
      ann=
          f2M_create_standard(4,AnnInputs,AnnInputs,AnnInputs/2+1,1);
      f2M_set_act_function_hidden(ann,FANN_SIGMOID_SYMMETRIC_STEPWISE);
      f2M_set_act_function_output(ann,FANN_SIGMOID_SYMMETRIC_STEPWISE);
      f2M_randomize_weights(ann,-0.4,0.4);
      debug(1,"Neurored: '"+path+"' creada con éxito. Su manejador: "+ann);
     }
   if(ann==-1) 
     {
      debug(0,"¡INICIALIZANDO NEURORED!");
     }
   return(ann);
  }

Como se puede ver, en el caso de que la llamada f2M_create_from_file() termine sin éxito - lo cual se indica con un resultado negativo de la función - con la ayuda de la función f2M_create_standard() se crea una neurored con argumentos tales que la neurored creada tenga 4 capas (incluyendo una de entrada y una de salida). AnnInput - es el número de neuronas en la capa de entrada, AnnInput - es el número de neuronas en la primera capa oculta, AnnInput/2+1 - es el número de neuronas en la segunda capa oculta y 1 neurona en la capa de salida.

La funciónf2M_set_act_function_hidden() se usa para instalar la función de activación de las neuronas de la capa oculta como SIGMOID_SYMMETRIC_STEPWISE (ver los tipos de funciones de activación en la documentación de la biblioteca FANN), la misma función de activación se indica también para las neuronas de la capa de salida.

A continuación, va la llamada de la función f2m_randomize_weights(), que se usa para inicializar los pesos de las conexiones de la neuronas dentro de la red. Aquí se usa el diapasón <-0.4; 0.4>, pero usted puede utilizar cualquier otro, dependiendo de su tarea.

Como ya habrá notado, ya se ha utilizado varias veces la función debug(). Este es uno de los métodos más sencillos para la comprobación adicional del estado actual del experto. Usando esto y el parámetro de entrada DebugLevel, usted podrá ajustar la muestra de los mensajes de depuración.

void debug(int level,string text)
  {
   if(DebugLevel>=level) 
     {
      if(level==0)
         text="ERROR: "+text;
      Print(text);
     }
  }


Si el primer argumento level (nivel de depuración) en la función debug() es mayor que DebugLevel, entonces la función no muestra nada. Si es menor o igual que DebugLevel, entonces se muestra la línea text. Si DebugLevel=0, entonces a la línea mostrada se añade la línea "ERROR: ". De esta forma, usted podrá separar los mensajes de depuración en diferentes niveles.

Los mensajes más importantes y probables están relacionados con los errores, por eso se les asigna el valor 0. Se seguirán mostrando mientras que DebugLevel no sea negativo, no recomendamos ponerlo como negativo. Con un nivel 1 se muestra cierta información importante, por ejemplo la confirmación de la carga exitosa de la neurored o su creación. En el nivel 2 y superior, la significación de la información mostrada disminuye paulatinamente.

Antes de explicar detalladamente el contenido de la función start(), bastante larga, debemos mostrarles ciertas funciones que son necesarias para preparar las entradas de la neurored y realizar su inicio:

void ann_prepare_input()
  {
   int i;

   for(i=0;i<=AnnInputs-1;i=i+3) 
     {
      InputVector[i]=
         10*iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                  MODE_MAIN,i*3);
      InputVector[i+1]=
         10*iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                  MODE_SIGNAL,i*3);
      InputVector[i+2]=InputVector[i-2]-InputVector[i-1];
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double ann_run(int ann,double &vector[])
  {
   int ret;
   double out;
   ret=f2M_run(ann,vector);
   if(ret<0) 
     {
      debug(0,"¡¡¡ERROR al iniciar la neurored!!! ann="+ann);
      return(FANN_DOUBLE_ERROR);
     }
   out=f2M_get_output(ann,0);
   debug(3,"f2M_get_output("+ann+") resultado: "+out);
   return(out);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int anns_run_parallel(int anns_count,int &anns[],double &input_vector[])
  {
   int ret;

   ret=f2M_run_parallel(anns_count,anns,input_vector);

   if(ret<0) 
     {
      debug(0,"f2M_run_parallel("+anns_count+") resultado: "+ret);
     }
   return(ret);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void run_anns()
  {
   int i;

   if(Parallel) 
     {
      anns_run_parallel(AnnsNumber,AnnsArray,InputVector);
     }

   for(i=0;i<AnnsNumber;i++) 
     {
      if(Parallel) 
        {
         AnnOutputs[i]=f2M_get_output(AnnsArray[i],0);
           } else {
         AnnOutputs[i]=ann_run(AnnsArray[i],InputVector);
        }
     }
  }
//+------------------------------------------------------------------+


La función ann_prepare_input() se usa para preparar los valores de entrada de las redes. Aunque su objetivo es simple, hay que destacar que los datos de entrada deberán ser normalizados de la forma correspondiente. En este caso, no se trata de nada refinado, solo he utilizado MACD y los valores de las señales que nunca abandonan el diapasón de datos necesario. Este aspecto se debe tomar en cuenta al programar tareas reales. En un ejemplo real habrá que prestar más atención a esta cuestión.

Como ya habrá entendido, los factores más importantes al trabajar con las neuroredes son la elección de los argumentos de entrada necesarios como entradas de la neurored, su preparación y normalización.

La biblioteca Fann2MQL tiene la posibilidad de ampliar la funcionalidad habitual de MetaTrader con la ayuda del procesamiento paralelo de las redes neuronales. La responsable de ello es la variable Parallel. La función run_anns() pone en marcha todas las neuroredes inicializadas, quita de ellas los parámetros de salida y las ubica en la matriz n AnnOutput[]. La función anns_run_parallel es capaz de ejecutar este trabajo en el modo multiflujo. Llama f2m_run_parallel(), en la que el primer argumento es el número de neuroredes para el procesamiento, el segundo argumento es la matriz que contiene todos los manejadores de todas las neuroredes que usted quiere iniciar, y el tercer argumento es el vector de los datos de entrada.

Todas las neuroredes que deben ser iniciadas tienen datos de entrada idénticos. La obtención de los valores de salida de la neurored se realiza con la ayuda de la llamada múltiple de la función f2m_get_output().

Ahora analizamos la función start():

int
start()
  {
   int i;
   bool BuySignal=false;
   bool SellSignal=false;

   double train_output[1];

   /* Is trade allowed? */
   if(!trade_allowed()) 
     {
      return(-1);
     }

   /* Preparamos e iniciamos la neurored */
   ann_prepare_input();
   run_anns();

/* Calculamos los últimos valores de MACD y los anteriores.
* No usamos la barra actual incompleta
*/
   double MacdLast=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                         MODE_MAIN,1);
   double MacdPrev=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                         MODE_MAIN,2);

   double SignalLast=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                           MODE_SIGNAL,
                           1);
   double SignalPrev=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                           MODE_SIGNAL,
                           2);

   /* señal BUY-compra */
   if(MacdLast>SignalLast && MacdPrev<SignalPrev) 
     {
      BuySignal=true;
     }
   /* señal SELL-venta */
   if(MacdLast<SignalLast && MacdPrev>SignalPrev) 
     {
      SellSignal=true;
     }

   /* No hay posiciones largas */
   if(LongTicket==-1) 
     {
      /* señal BUY */
      if(BuySignal) 
        {
         /* si se establece NeuroFilter, 
            usamos la sabiduría de la neurored para tomar la decisión:) */
         if(!NeuroFilter || ann_wise_long()>Delta) 
           {
            LongTicket=
          OrderSend(Symbol(),OP_BUY,Lots,Ask,3,
                    Bid-StopLoss*Point,
                    Ask+TakeProfit*Point,
                    NAME+"-"+"L ",MagicNumber,0,Blue);
           }
         /* Recordamos las entradas de la neurored */
         for(i=0;i<AnnInputs;i++) 
           {
            LongInput[i]=InputVector[i];
           }
        }
        } else {
      /* Procesamiento de las posiciones largas*/
      OrderSelect(LongTicket,SELECT_BY_TICKET);
      if(OrderCloseTime()==0) 
        {
         // Order is opened
         if(SellSignal && OrderProfit()>0) 
           {
            OrderClose(LongTicket,Lots,Bid,3);
           }
        }
      if(OrderCloseTime()!=0) 
        {
         // Order is closed
         LongTicket=-1;
         if(OrderProfit()>=0) 
           {
            train_output[0]=1;
              } else {
            train_output[0]=-1;
           }
         for(i=0;i<AnnsNumber;i+=2) 
           {
            ann_train(AnnsArray[i],LongInput,train_output);
           }
        }
     }

   /* No hay posiciones cortas */
   if(ShortTicket==-1) 
     {
      if(SellSignal) 
        {
         /* si se establece NeuroFilter, 
            usamos la sabiduría de la neurored para tomar la decisión:) */
         if(!NeuroFilter || ann_wise_short()>Delta) 
           {
            ShortTicket=
          OrderSend(Symbol(),OP_SELL,Lots,Bid,3,
                    Ask+StopLoss*Point,
                    Bid-TakeProfit*Point,NAME+"-"+"S ",
                    MagicNumber,0,Red);
           }
         /* Remember network input */
         for(i=0;i<AnnInputs;i++) 
           {
            ShortInput[i]=InputVector[i];
           }
        }
        } else {
      /* Trabajando con posiciones cortas */
      OrderSelect(ShortTicket,SELECT_BY_TICKET);
      if(OrderCloseTime()==0) 
        {
         // Order is opened
         if(BuySignal && OrderProfit()>0) 
           {
            OrderClose(LongTicket,Lots,Bid,3);
           }
        }
      if(OrderCloseTime()!=0) 
        {
         // Order is closed
         ShortTicket=-1;
         if(OrderProfit()>=0) 
           {
            train_output[0]=1;
              } else {
            train_output[0]=-1;
           }
         for(i=1;i<AnnsNumber;i+=2) 
           {
            ann_train(AnnsArray[i],ShortInput,train_output);
           }
        }
     }

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


Puesto que el código ya contiene comentarios comprensibles, explicaré brevemente su funcionamiento.

La función trade_allowed() comprueba si el asesor puede comerciar. El valor de la variable AnnsLoaded indica si todas las neuroredes han sido iniciadas correctamente, después se comprueba el marco temporal y el balance mínimo, y al final del todo se comprueba que el comercio se realice solo en el primer tick de la nueva barra.

Las dos funciones siguientes, usadas para preparar los parámetros de entrada e iniciar el funcionamiento de la neurored, ya han sido descritas más arriba. A continuación calculamos los valores de la línea principal y de la línea de señal del indicador MACD para las barras última y penúltima totalmente construidas; para su posterior procesamiento, las ubicamos en las variables. La barra actual se ignora, puesto que aún no se ha construido, y probablemente cambiará.

Los valores SellSignal y BuySignal se calculan de acuerdo con las señales MACD: los cruces de la línea principal y de señal. Ambas señales se usan para procesar las posiciones largas y breves. Puesto que son simétricas, a continuación describiremos solo el caso de las posiciones largas.

La variable LongTicket contiene el número del ticket de la posición larga abierta actual. Si es igual a -1, no hay posiciones largas, por eso, si se establece BuySignal, esto puede indicar una buena oportunidad de apertura de una posición larga. Si la variable NeuroFilter no ha sido establecida, sencillamente se manda la orden de compra y se abre una posición larga sin filtrado de operaciones con uso de red neuronal.

En este caso, la variable LongInput es necesaria para, en su uso posterior, recordar en lo sucesivo el vector de entrada InputVector, preparado con la ayuda de ann_prepare_input().

Si la variable LongTicket contiene el número de ticket correcto, el experto comprueba si está abierta en este momento la posición o ha sido cerrada al alcanzar los niveles de Stop Loss o Take Profit.

Si la orden no está cerrada, no pasa nada, no obstante, si la orden está cerrada, el vector train_output[], que solo tiene un valor de salida, se establece en -1 si la orden ha sido cerrada con Stop Loss, o 1 si la orden ha sido cerrada con beneficio con Take Profit.

Este valor después se transmite a la función ann_train(), y todas las neuroredes son capaces de procesar posiciones largas y aprender de los resultados. Puesto que como parámetro de entrada se usa la matriz LongInput, en el momento de apertura de posición, contiene los valores de la matriz InputVector. De esta forma, la neurored toma la señal que conlleva beneficios o pérdidas. Cuando la neurored ha sido entrenada, indicando NeuroFilter como true se provoca el filtrado de operaciones con la ayuda de neurored. La función ann_wise_long() es usada por la neurored para calcular los valores medios de los resultados de todas las neuroredes que procesan posiciones largas. El parámetro Delta se usa como valor umbral para tomar decisiones sobre la corrección de la señal filtrada. Como tantos otros valores, se ha obtenido mediante optimización.

Ahora que ya sabemos cómo funciona todo esto, mostraremos cómo se puede utilizar. Pareja de prueba EURUSD. He tomado los datos históricos de uno de los brókeres, convertidos en un marco temporal de M5.

Para el aprendizaje y optimización, se ha usado el periodo 2007.12.31-2009.01.01(conjunto de entrenamiento), la simulación se ha realizado en el periodo 2009.01.01-2009.03.22 (conjunto de entramiento). En el primer inicio hemos intentado encontrar los valores más rentables para los argumentos StopLoss, TakeProfit, SlowMA, FastMA y SignalMA, que luego he implementado en el archivo NeuroMACD.mq4.

La variable NeuroFIlter se establece como false, al igual que SaveAnn, la variable AnnsNumber era igual a 0 para bloquear el funcionamiento de la neurored.

En el proceso de optimización he usado un algoritmo genético. Con los valores obtenidos, el informe final tiene el aspecto siguiente:

Informe de los datos de entrenamiento tras la optmización de parámetros


Como se puede ver, este asesor se ha iniciado en una minicuenta con un tamaño de lote de 0.01 y un balance inicial de 200. Sin embargo, usted puede cambiar estos parámetros de acuerdo con los ajustes de la cuenta o sus propias preferencias.

Ahora tenemos operaciones rentables y no rentables, así que podemos establecer SaveAnn en true, y AnnsNumber como igual a 30. Después de ello, he iniciado el simulador de nuevo. Los resultados han sido exactamente los mismos, con la excepción de que el proceso completo ha sido bastante más lento (ahora se han usado neuroredes), y el catálogo C:\ANN se ha llenado con neuroredes entrenadas, como se muestra en el dibujo, más abajo:

Sin embargo, primero hay que asegurarse de que el catálogo C:\ANN existe.


Contenido de la carpeta C:\\ANN\\.


Hemos entrenado la neurored y ahora es el mejor momento de ponerla a prueba. Primero comprobaremos su funcionamiento con el conjunto entrenado. Cambiaremos NeuroFilter a true y SaveAnn a false e iniciaremos el simulador de estrategias.

Los resultados que obtenido se muestran más abajo. Destacaremos que los resultados pueden diferenciarse de los que usted obtenga, debido a que las conexiones entre neuronas al inicializar la red neuronal se llenan con los valores aleatorios f2M_randomize_weights() en la función ann_load()).



Informe obtenido con el conjunto de entrenamiento usando el filtro de operaciones basado en una reurored.

Ahora el beneficio total (net profit) es algo mayor (20.03 en lugar de 16.92), sin embargo, profit factor es significativamente superior (1.25 en lugar de 1.1). El número de transacciones es bastante menor () y el número medio de operaciones no rentables consecutivas se ha reducido de 7 a 2.

No obsatante, esto solo muestra que está funcionando el filtrado con neurored, pero no descubre nada sobre su comportamiento con los datos que ya han sido usados en el proceso de aprendizaje.

El resultado obtenido ya con la muestra de prueba (periodo 2009.01.01 - 2009.30.28) se indica más abajo:



Resultado obtenido con la muestra de prueba usando el filtrado de operaciones con neurored.

El número de operaciones realizadas es bastante pequeño y resulta difícil hablar de la calidad de esta estrategia. En este trabajo nos habíamos marcado el objetivo de mostrar el uso de neuroredes en los programas MQL, no la meta de describir el mejor asesor rentable.

El efecto real del uso de neuroredes en este caso se ve solo en comparación con los resultados del funcionamiento del asesor con los datos de prueba, usando (NeuroFilter=true) y sin usar (NeuroFilter=false) el filtro con neurored.


Resultado con el conjunto de prueba sin filtrado de operaciones con neurored.

Las diferencias son obvias. ¡Como puede ver, el filtrado con neurored ha convertido un asesor no rentable en uno rentable!


Conclusiones

Espero que este artículo les haya enseñado cómo usar redes neuronales en el terminal MetaTrader.

Con la ayuda de la sencilla bilioteca gratuita Fann2MQL (disponible en forma de códigos fuente) usted podrá añadir fácilmente el módulo de una neurored a cualquier asesor y comenzar a escribir sus propios programas, basados completa o parcialmente en el uso de redes neuronales.

La velocidad de los cálculos puede multiplicarse por varias veces (dependiendo del número de núcleos) gracias a la posibilidad de usar cálculos multiflujo, lo cual es especialmente importante al optimizar ciertos parámetros.

A veces ha sucedido que el proceso de optimización del aprendizaje con refuerzo (Reinforcement Learning) se ha acortado de 4 días a "solo" 28 horas en un procesador Intel de 4 núcleos.

Mientras escribía este artículo, decidí poner Fann2MQL en la página http://fann2mql.wordpress.com/. Allí podrá encontrar la última versión de la biblioteca Fann2MQL, y también es posible que encuentre sus futuras versiones, así como la documentación de todas las funciones.

Prometo guardar las condicones de uso de la biblioteca (licencia GPL) para todas las versiones, así que puede enviarme sus comentarios y propuestas de perfeccionamiento y mejora, y aquellas que resulten interesantes se implementarán en las siguientes versiones.

Tenga en cuenta que en este artículo se muestran solo los principios de uso de la biblioteca Fann2MQL. Existen otros muchos argumentos interesantes

Podrá encontrar mucha más información sobre FANN en la página Fast Artificial Neural Network Library: http://leenissen.dk/fann/!

Post Scriptum

Después de finalizar este artículo he detectado un error importante en NeuroMACD.mq4. Al usar la función OrderClose () para una posición corta, se indicaba el ticket de una posición larga.

Esto ha causado el desajuste de la estrategia, que daba preferencia al mantenimiento de posiciones cortas y el cierre de posiciones largas.

/* Maintain short position */
OrderSelect(ShortTicket,SELECT_BY_TICKET);
if(OrderCloseTime()==0) 
  {
// Order is opened
   if(BuySignal && OrderProfit()>0) 
     {
      OrderClose(LongTicket,Lots,Bid,3);
     }
  }

En la versión corregida del script se ha remediado este error y se ha eliminado OrderClose(). Esto no ha provocado un cambio esencial en la imagen general de los resultados del filtrado con neurored, sin embargo, la forma de la curva de balance es ahora distinta. Se adjuntan ambas versiones.

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

Archivos adjuntos |
NeuroMACD-fixed.mq4 (10.13 KB)
NeuroMACD.mq4 (10.12 KB)
adrianveidt39
adrianveidt39 | 26 mar. 2021 en 00:12
Hello, I try to implement an ann in MQL4, when I try to execute my script I get this error “Cannot call ‘Fann2MQL.dll::f2M_create_standard’, DLL is not allowed", do you know how can I solve this? compiling the script is error free.
Una rápida inmersión en MQL5 Una rápida inmersión en MQL5
¿Ha decidido estudiar el lenguaje de programación de estrategias de trading MQL5 pero no sabe nada sobre él? Hemos intentado describir el terminal de MQL5 y Meta Trader 5 desde el punto de vista de una persona iniciada y para ello hemos escrito este corto artículo introductorio. En este artículo encontrará una breve descripción de las posibilidades de este lenguaje, así como algunos consejos sobre cómo trabajar con MetaEditor 5 y el terminal.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
MetaTrader 5 - ¡Más de lo que puedas imaginar! MetaTrader 5 - ¡Más de lo que puedas imaginar!
El terminal de cliente de MetaTrader 5 ha sido desarrollado desde cero y mejora con creces a su predecesor. La nueva plataforma ofrece oportunidades ilimitadas para operar en cualquier mercado financiero. Además, se ha ampliado su funcionalidad para ofrecer aún más características y facilidad de uso. Todo ello hace que sea muy difícil la enumeración de todas las ventajas de MetaTrader 5. Hemos intentado describir brevemente todas estas ventajas en un único artículo y nos ha sorprendido ver que el resultado ¡no ha sido nada breve!
Estimamos la rentabilidad futura usando intervalos de confianza Estimamos la rentabilidad futura usando intervalos de confianza
En este artículo, nos adentraremos en la aplicación de técnicas de bootstrapping como forma de evaluar la rentabilidad futura de una estrategia automatizada.