English Русский 中文 Deutsch 日本語 Português
preview
Aplicación práctica de las redes neuronales en el trading (Parte 2). Visión por computadora

Aplicación práctica de las redes neuronales en el trading (Parte 2). Visión por computadora

MetaTrader 5Trading | 16 abril 2021, 14:13
1 448 0
Andrey Dibrov
Andrey Dibrov

Introducción

Un problema esencial a la hora de preparar los datos al entrenar redes neuronales para su uso en el trading es la preparación de la información de entrada necesaria, por ejemplo, si utilizamos una docena de indicadores. Estos indicadores, además, podrían suponer un conjunto de varios gráficos informativos. Si calculamos estos indicadores a cierta profundidad, obtendremos como resultado hasta cien entradas y, en algunos casos, incluso más. ¿Podemos facilitar el entrenamiento de una red neuronal utilizando la visión por computadora? Para resolver este problema, usaremos redes neuronales convolucionales, que ahora se utilizan para resolver problemas de clasificación y reconocimiento.


La arquitectura de la red neuronal convolucional

En el artículo, usaremos una red neuronal convolucional cuya arquitectura se muestra en la figura siguiente. Esta describe el principio de construcción general de Convolutional Neural Network (CNN)

En esta variante, tenemos:

  1. Una entrada CNN que representa una imagen de 449x449 píxeles.
  2. Una primera capa convolucional de 96 mapas de características. Cada mapa representa una imagen de 447x447. Un núcleo de convolución de 3x3.
  3. Una capa de submuestreo de 96 mapas de características de 223x223 con un núcleo de 2x2.
  4. Una segunda capa convolucional de 32 mapas de características. Cada mapa representa una imagen de 221x221. Un núcleo de convolución de 3x3.
  5. Una capa de submuestreo de 32 mapas de características de 110x110 con un núcleo de 2x2.
  6. Una tercera capa convolucional de 16 mapas de características. Cada mapa representa una imagen de 108x108. Un núcleo de convolución de 3x3.
  7. Una capa de submuestreo de 16 mapas de características de 54x54 con un núcleo de 2x2. No se muestra en la figura.
  8. Una capa completamente conectada de 64 neuronas.
  9. Una capa de salida de una neurona. Estas dos capas suponen el bloque de clasificación.

CNN

Adelantándonos un poco, diremos que aquellos que se familiarizan por primera vez con las redes neuronales convolucionales no deberían temer la aparente complejidad de la construcción. La red neuronal de la estructura establecida se construye automáticamente. Solo necesitamos configurar los parámetros básicos. 

Preparando una matriz de imágenes para entrenar y poner a prueba una red neuronal

Antes de preparar una serie de imágenes, debemos decidir qué queremos de nuestra red neuronal. Lo ideal sería entrenar la red con los puntos de pivote. Y ya partiendo de ello, captar las imágenes en las que se dibujará la última barra extrema. Como resulta que este experimento no posee valor aplicado, nos centraremos en otro conjunto de imágenes. No obstante, tras leer el material de este artículo, en el futuro podremos experimentar con dicha matriz. Incluso resultará útil a la hora de asegurarnos de que las redes neuronales funcionen bien en las tareas de clasificación en imágenes completas. Pero las respuestas de la red que obtenemos en una serie temporal continua requieren una optimización adicional. 

Como no queremos complicar nuestro experimento, nos centraremos en dos categorías de imágenes:

  • Buy - cuando el precio sube o cuando el precio ha alcanzado el mínimo diario
  • Sell - cuando el precio baja o cuando el precio ha alcanzado el máximo diario

Buy   Buy1  Buy2  Buy3

Como podemos ver, para entrenar la red, debemos determinar el movimiento en cualquier dirección como el precio que alcanza nuevos valores extremos posteriores en la dirección de la tendencia; en este momento, tomaremos una instantánea del gráfico. El momento del viraje de la tendencia también es importante para el aprendizaje de la red. También tomaremos una instantánea del gráfico cuando el precio alcance el máximo o mínimo diario.

En primer lugar, vamos a preparar un gráfico para obtener la imagen deseada. Para hacer esto, utilizaremos la plantilla CNN.tpl. Ubicaremos esta en la carpeta \AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Profiles\Templates.

CNN.tpl

En las propiedades del gráfico, determinaremos el texto como "White".

CNN.tpl


Experimentando un poco, podremos colocar otros indicadores. Estos indicadores no han sido elegidos al azar. Vamos a establecer el tamaño del gráfico de forma aleatoria, pero deberemos encontrar el óptimo según las capacidades del hardware.

Para crear la matriz de imágenes, utilizaremos un script.

//+------------------------------------------------------------------+
//|                                                        CNNet.mq5 |
//|                                   Copyright 2021, Andrey Dibrov. |
//|                           https://www.mql5.com/en/users/tomcat66 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, Andrey Dibrov."
#property link      "https://www.mql5.com/en/users/tomcat66"
#property version   "1.00"
#property strict
#property script_show_inputs

input string Date="2017.01.02 00:00";
input string DateOut="2018.12.13 23:00";
input string DateTest="2019.01.02 00:00";
input string Dataset="Train";

string Date1;
int count,countB,countS;
int day;
double DibMin;
double DibMax;
int HandleDate;
long WIDTH;
long HEIGHT;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   MqlDateTime stm;
   ChartSetInteger(0,CHART_SHIFT,false);
   ChartSetInteger(0,CHART_AUTOSCROLL,false);
   ChartSetInteger(0,CHART_SHOW_OBJECT_DESCR,false);
   WIDTH=ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
   ChartSetInteger(0,CHART_SHOW_PRICE_SCALE,false);

   if(Dataset=="Test")
     {
      HandleDate=FileOpen(Symbol()+"Date.csv",FILE_CSV|FILE_READ|FILE_WRITE|FILE_ANSI,";");
      ChartNavigate(0,CHART_END,-(iBarShift(NULL,PERIOD_H1,StringToTime(DateTest))));
      Sleep(1000);

      for(int i=iBarShift(NULL,PERIOD_H1,StringToTime(DateTest)); i>0; i--)
        {
         Date1=TimeToString(iTime(NULL,PERIOD_H1,i));
         if(DateTest<=Date1)
           {
            if(ChartNavigate(0,CHART_END,-i))
              {
               Sleep(20);
               if(ChartScreenShot(0, (string)count + ".png", (int)WIDTH, (int)WIDTH, ALIGN_LEFT))
                 {
                  FileWrite(HandleDate,TimeToString(iTime(NULL,PERIOD_H1,i)));
                  count++;
                  Sleep(20);
                 }
              }
           }
        }
     }
   if(Dataset=="Train")
     {
      ChartNavigate(0,CHART_END,-iBarShift(NULL,PERIOD_H1,StringToTime(Date)));
      Sleep(1000);
      for(int i=iBarShift(NULL,PERIOD_H1,StringToTime(Date)); i>=iBarShift(NULL,PERIOD_H1,StringToTime(DateOut)); i--)
        {
         TimeToStruct(iTime(NULL,PERIOD_H1,i),stm);
         Date1=TimeToString(iTime(NULL,PERIOD_H1,i));
         if(DateOut>=Date1 && Date<=Date1)
           {
            if(ChartNavigate(0,CHART_END,-i))
              {
               Sleep(20);
               if(day != stm.day)
                 {
                  FileCopy("Sell" + (string)countS + ".png", 0, "Buy" + (string)(countB+1) + ".png", FILE_REWRITE);
                  FileDelete("Sell" + (string)countS + ".png", 0);
                  FileCopy("Buy" + (string)countB + ".png", 0, "Sell" + (string)(countS+1) + ".png", FILE_REWRITE);
                  FileDelete("Buy" + (string)countB + ".png", 0);
                  countB ++;
                  countS ++;
                 }
               day = stm.day;
               if(stm.hour == 0)
                 {
                  DibMin = iOpen(NULL, PERIOD_H1, i);
                  DibMax = iOpen(NULL, PERIOD_H1, i);
                 }
               if(iLow(NULL, PERIOD_H1, i+1) < DibMin)
                 {
                  DibMin = iLow(NULL, PERIOD_H1, i+1);
                  countS ++;
                  ChartScreenShot(0, "Sell" + (string)countS + ".png", (int)WIDTH, (int)WIDTH, ALIGN_LEFT);
                 }
               if(iHigh(NULL, PERIOD_H1, i+1) > DibMax)
                 {
                  DibMax = iHigh(NULL, PERIOD_H1, i+1);
                  countB ++;
                  ChartScreenShot(0, "Buy"  +(string)countB + ".png", (int)WIDTH, (int)WIDTH, ALIGN_LEFT);
                 }
               Sleep(20);
              }
           }
         else
            break;
        }
     }
  }

El script funciona en dos modos: "Train", en el que creamos una matriz de imágenes para el entrenamiento, y "Test", en el que creamos una matriz de imágenes para recibir las respuestas NN según las cuales se obtendrá el indicador. Usaremos este indicador en el futuro para optimizar la estrategia comercial. 

Iniciamos el script en el modo "Train".

Train

La variable "Date" es la fecha inicial de la muestra de imágenes para el entrenamiento. "DateOut' es la fecha final de la muestra de imágenes para el entrenamiento. "DateTest" es la fecha de inicio de la muestra de imágenes para obtener las respuestas por horas de la red neuronal. La fecha final de las respuestas será la hora de inicio del script. 

En la carpeta ...\MQL5\Files del directorio de datos, obtendremos el conjunto de imágenes Buy... y Sell.... Son 6125 en total.

Tren

A continuación, preparamos los directorios para los conjuntos de entrenamiento, validación y prueba. Para mayor comodidad, crearemos en el "escritorio" la carpeta "CNN", y en ella, tres carpetas "Train", "Val", "Test".

CNN

En los directorios "Train" y "Val", crearemos los subdirectorios "Buy" y "Sell". En el directorio "Test", el subdirectorio "Resp".

Train

De la carpeta ...\MQL5\Files, cortamos todos los archivos "Buy..." y los pegamos en la carpeta ...\Train\Buy. Obtendremos 3139 imágenes. Después, realizamos el mismo proceso con los archivos "Sell...", y los ubicamos en el directorio ...\Train\Sell. Hemos obtenido 2986 imágenes. De las carpetas "Buy" y "Sell", cortamos un 30% de las últimas imágenes (con los mayores números) y las ubicamos en las carpetas correspodientes del directorio "Val".

Obtenemos 

  • ...\Train\Buy - 2198 imágenes, 
  • ...\Val\Buy   - 941   imágenes,
  • ...\Train\Sell - 2091 imágenes,
  • ...\Val\Sell   - 895   imágenes.

De esta forma, hemos preparado el conjunto de imágenes para entrenar a nuestra red. En este caso, las imágenes han resultado tener un tamaño de 449x449 píxeles.

Vamos a preparar el conjunto de imágenes para la prueba. Ejecutamos el script en el modo "Test".

Test

En la carpeta ...\MQL5\Files del directorio de datos, obtenemos un conjunto de instantáneas consecutivas por horas. En estos momentos, tenemos 12558. Por ahora, no las dividimos de ninguna forma, pues es la red neuronal la que se encarga de ello. Más bien, nos da una recomendación sobre la probabilidad de que esta o aquella imagen se corresponda con las condiciones en las que entrenamos la red. Movimiento hacia arriba y viraje hacia abajo. Movimiento hacia abajo y viraje hacia arriba. 

Cortamos estos archivos y los pegamos en la carpeta ...CNN\Test\Resp.

Test

Bien, ya hemos preparado el conjunto de imágenes para la prueba posterior de las respuestas y la optimización de la estrategia. Los archivos con la fecha y la hora  EURUSDDate que quedan en la carpeta ...\MQL5\Files los trasladamos a la carpeta CNN.

Como conclusión, querríamos señalar una característica de MetaTrader 5. Sería más adecuado y fiable preparar el conjunto de imágenes usando un asesor experto en el simulador de estrategias, sin embargo, por algún motivo, MetaTrader 5 en el simulador de estrategias no hace capturas de pantalla. En cualquier caso, esta característica no nos perjudicará al implementar un robot comercial.


Entrenando la red neuronal

Para trabajar con redes neuronales convolucionales, usaremos el entorno Anaconda. Necesitamos configurar este entorno para que funcione con CPU y GPU (si tenemos una tarjeta gráfica NVIDIA). Esta tarjeta gráfica es necesaria para acelerar el proceso de entrenamiento. Aunque la tarjeta impone algunas restricciones a la creación de la arquitectura NN. Estamos hablando de la dependencia de la tarjeta gráfica respecto al volumen de RAM. Pero claro, en cuanto a velocidad del aprendizaje, ganamos significativamente. Por ejemplo, en nuestro caso, si una época de entrenamiento dura 20 minutos en la CPU, en la GPU se demora entre 1 y 2 minutos. Si necesitamos entrenar una NN durante 40 épocas, tardaremos 13 horas y 1,5 horas. El uso de la GPU puede acelerar significativamente el proceso de búsqueda de redes neuronales en la etapa de investigación.

  1. Vamos a descargar e instalar la última versión de Anaconda Navigator. Lo hacemos todo por defecto.
  2. Desde el menú "Inicio\Anaconda3", iniciamos "Anaconda Promt".
  3. Introducimos el comando "pip install tensorflow". Instalamos la biblioteca programática para el aprendizaje de máquinas desarrollada por la compañía Google.
    tensorflow

  4. Introducimos el comando "pip install keras". Instalamos la biblioteca de red neuronal Keras.
    keras

  5. Creamos un nuevo entorno "conda" para GPU.  Introducimos el comando conda create --name PythonGPU. Y activamos el entorno - activate PythonGPU.
     
    GPU

  6. Para instalar tensorflow gpu, introducimos el comando - conda create -n PythonGPU python=3.6 tensorflow-gpu. Por favor, note que debemos instalar tensorflow gpu para python 3.6.
      tensorflow gpu

  7. Para instalar keras gpu, introducimos el comando - conda install -c anaconda keras-gpu.
    keras gpu

  8. Instalamos la interfaz Jupyter para programar en el entorno Python GPU. CPU Jupyter ya estaba instalado al instalar de Anaconda. Introducimos el comando - conda install jupyterJupyter

  9. Instalamos dos bibliotecas más, Pandas y Pillow - conda install -c anaconda pandas. Y después - conda install pillow. Estas bibliotecas también deben instalarse para la CPU si no vamos a usar una tarjeta gráfica.
    pandas 

  10. pillow

  11. Ahora, podemos comenzar a entrenar nuestra red neuronal convolucional. En la carpeta CNN creada anteriormente, ubicamos los dos archivos "Train.ipynb" y "Test.ipynb". Son archivos en formato Jupyter Notebook con los que trabajaremos más adelante. Iniciamos Jupyter Notebook (PythonGPU) y abrimos el archivo Train.  CNN  

Vamos a analizar el código programático por bloques.

Cargamos los módulos necesarios de las bibliotecas de red neuronal.

from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Conv2D, MaxPooling2D
from tensorflow.python.keras.layers import Activation, Dropout, Flatten, Dense
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.models import load_model

Establecemos los parámetros necesarios.

# Directory with data for training
train_dir = 'train'
# Directory with data for validation
val_dir = 'val'
# Image dimensions
img_width, img_height = 449, 449
# Image-based tensor dimension for input to the neural network
# backend Tensorflow, channels_last
input_shape = (img_width, img_height, 3)
# Number of epochs
epochs = 20
# Mini-sample size
batch_size = 7
# Number of images for training
nb_train_samples = 4289
# Количество изображений для проверки
nb_validation_samples = 1836
# Number of images for testing
#nb_test_samples = 3736

Vamos a crear la arquitectura de la red.

  • Establecemos la arquitectura secuencial de la CNN (convolutional neural network)
  • La imagen de entrada es de 449х449 píxeles, y tiene tres canales (rojo, verde y azul). Usaremos una imagen a color. Podremos experimentar con imágenes de dos colores.
  • Creamos la primera capa de convolución para trabajar con los datos 2D: 96 mapas de características, cada uno con su propio núcleo de convolución de 3x3. Cada neurona de la capa convolucional está conectada a una sección cuadrada de 3x3 de la imagen. 
  • Capa de activación usando la función "relu", que es menos intensiva en computación
  • Para reducir la dimensión, añadimos una capa de submuestra con un núcleo 2x2 con selección del valor máximo de este cuadrado
  • A continuación, añadimos dos capas de convolución más con 32 y 16 núcleos y dos capas de activación y submuestra.
  • Convertimos los datos de 2D a 1D
  • Transferimos los datos transformados a una capa completamente conectada con 64 neuronas
  • Usando la función de capa de regularización Dropout (0.5), intentamos evitar el sobreajuste
  • Añadimos una capa de salida completamente conectada con una neurona. Como estamos usando dos clases de imágenes, obtendremos una respuesta de la NN de forma binaria. También podemos experimentar con varias clases. Por ejemplo, dos tendencias y flat. Entonces habría tres neuronas en la capa de salida. Y, naturalmente, tendríamos que dividir las imágenes en tres clases.
  • La función de activación "sigmoid". Es adecuada para la clasificación y ha resultado mejor que otras en nuestros experimentos

Podemos modernizar fácilmente este ejemplo de arquitectura, por ejemplo, aumentando el número de capas y su dimensión, cambiando de lugar según la secuencia de las dimensiones, la dimensión de los núcleos de convolución y las funciones de activación, o trabajando con capas completamente conectadas. No obstante, aquí surge un dilema: cuando usamos una GPU, si queremos aumentar la arquitectura de una red neuronal, es necesario aumentar la RAM de la tarjeta gráfica. O bien reducir el tamaño de las imágenes. O sacrificar nuestro tiempo y usar la CPU.

model = Sequential()
model.add(Conv2D(96, (3, 3), input_shape=input_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(16, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))

Copiamos la red. Usamos la función de error binary-crossentropy. En este caso, utilizaremos una respuesta que consta de dos clases que deberían adoptar los valores 0 o 1, de forma ideal. En realidad, nuestros valores se distribuirán de 0 a 1. Seleccionamos el optimizador Gradient Descent. En este caso, partiendo de los experimentos, es el más adecuado para entrenar una NN. Eligimos la métrica precisión (accuracy): el porcentaje de respuestas correctas. 

model.compile(loss='binary_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])

Normalizamos los datos sobre la intensidad de los píxeles de las imgánes.

datagen = ImageDataGenerator(rescale=1. / 255)

Usamos los generadores Keras para leer los datos del disco y crear las matrices de entrenamiento y validación para la NN a partir de nuestras imágenes. De nuevo, class_mode será binary. Shuffle será False. No vamos a mezclar nuestras imágenes.

train_generator = datagen.flow_from_directory(
    train_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode='binary',
    shuffle=False)
val_generator = datagen.flow_from_directory(
    val_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode='binary',
    shuffle=False)

La función callbacks guarda la NN entrenada tras cada época. Esto nos permite seleccionar la red más adecuada en cuanto a los valores de error y el porcentaje de aciertos.

callbacks = [ModelCheckpoint('cnn_Open{epoch:1d}.hdf5')]

Vamos a pasar directamente al entrenamiento de la red.

model.fit(
    train_generator,
    steps_per_epoch=nb_train_samples // batch_size,
    epochs=epochs,
    validation_data=val_generator,
    validation_steps=nb_validation_samples // batch_size,
    callbacks=callbacks)

12. Iniciamos el programa para su ejecución como se muestra en la figura.

Run


Si lo hemos hecho todo correctamente, la red neuronal comenzará a aprender.

Fit

Una vez finalizado el proceso de entrenamiento, en la carpeta CNN habrá 20 NN entrenadas.

CNN

Vamos a ver los resultados del entrenamiento y a decidir qué red neuronal seleccionaremos para su uso posterior.


NN

A primera vista, en la época 18, la red neuronal ha aprendido con una tasa de error del 30% y un 85% de resultados correctos. Sin embargo, al verificar la NN con el conjunto de validación, vemos que el error crece y el porcentaje de respuestas positivas disminuye. Esto significa que debemos elegir la red neuronal entrenada en la undécima era. Como podemos ver, aquí los resultados de validación más adecuados son val_loss: 0.6607 y val_accuracy: 0.6129. En términos ideales, por supuesto, sería necesario esforzarse por obtener valores de error que tiendan a 0 (o al menos por debajo del 35-40%), y una precisión que tienda a 1 (o al menos por encima del 55-60%). Entonces, podríamos no realizar la optimización, o llevarla a cabo según los parámetros mínimos para mejorar la calidad del comercio. Pero incluso con estos resultados de aprendizaje, podemos crear un sistema comercial positivo.


Interpretación de la respuesta de la red neuronal

Vamos a ver ahora si todo nuestro trabajo tiene algún valor práctico.

Iniciamos Jupyter Notebook sin soporte de GPU y abrimos el archivo Test.jpynb desde el directorio CNN.

Test

Vamos a analizar el código por bloques.

Cargamos los módulos necesarios de las bibliotecas de red neuronal.

from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Conv2D, MaxPooling2D
from tensorflow.python.keras.layers import Activation, Dropout, Flatten, Dense
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.models import load_model
import pandas as pd

Establecemos los parámetros necesarios.

predict_dir = 'Test'
img_width, img_height = 449, 449
nb_predict_samples = 12558

Leemos desde el archivo la fecha y la hora de las imágenes verificadas.

Date=pd.read_csv('EURUSDDate.csv', delimiter=';',header=None)

Cargamos la NN guardada tras la undécima época.

model=load_model('cnn_Open11.hdf5')

Normalizamos las imágenes.

datagen = ImageDataGenerator(rescale=1. / 255)

Leemos los datos usando el generador.

predict_generator = datagen.flow_from_directory(
    predict_dir,
    target_size=(img_width, img_height),
    shuffle=False)

Recibimos las respuestas de la NN.

indicator=model.predict(predict_generator, nb_predict_samples )

Mostramos el resultado. El proceso de recepción de las respuestas es largo, y esta notificación nos informará de que ha terminado.

print(indicator)

Guardamos en un archivo el resultado obtenido.

Date=pd.DataFrame(Date)
Date['0'] =indicator
Date.to_csv('Indicator.csv',index=False, header=False,sep=';')

Iniciamos el programa, y cuando este finaliza su trabajo, obtenemos el archivo Indicator.csv en la carpeta CNN.

Indicator

Y lo trasladamos a la carpeta C:\Users\...\AppData\Roaming\MetaQuotes\Terminal\Common\Files.

En el gráfico EURUSD H1, ubicamos el indicador NWI.

NWI

 

//+------------------------------------------------------------------+
//|                                                          NWI.mq5 |
//|                                 Copyright © 2019, Andrey Dibrov. |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2019, Andrey Dibrov."
#property link      "https://www.mql5.com/en/users/tomcat66"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Red
#property indicator_color2  DodgerBlue


int Handle;
int i;
int h;
input int Period=5;
double    ExtBuffer[];
double    SignBuffer[];
datetime Date1;
datetime Date0;
string File_Name="Indicator.csv";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
   SetIndexBuffer(0,ExtBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,SignBuffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,5);
   Handle=FileOpen(File_Name,FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");
   //FileClose(Handle);
  }
//+------------------------------------------------------------------+
//| Relative Strength Index                                          |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   MqlDateTime stm;
   Date0=StringToTime(FileReadString(Handle));
   i=iBarShift(NULL,PERIOD_H1,Date0,false);
   Handle=FileOpen(File_Name,FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");
   ArraySetAsSeries(ExtBuffer,true);
   ArraySetAsSeries(SignBuffer,true);
   while(!FileIsEnding(Handle) && !IsStopped())
     {
      Date1=StringToTime(FileReadString(Handle));
      ExtBuffer[i]=StringToDouble(FileReadString(Handle));
      h=Period-1;
      if(i>=0)
        {
         while(h>=0)
           {
            SignBuffer[i]=SignBuffer[i]+ExtBuffer[i+h];
            h--;
           }
        }
      SignBuffer[i]=SignBuffer[i]/Period;
      TimeToStruct(Date1,stm);
      i--;
     }
   FileClose(Handle);
   return(rates_total);
  }
//+------------------------------------------------------------------+

Para mayor simplicidad, interpretaremos las respuestas de la CNN usando la intersección de la línea del indicador principal con una media simple. Usaremos el asesor TestCNN.

//+------------------------------------------------------------------+
//|                                                      TestCNN.mq5 |
//|                                 Copyright © 2019, Andrey Dibrov. |
//+------------------------------------------------------------------+
#property copyright " Copyright © 2019, Andrey Dibrov."
#property link      "https://www.mql5.com/en/users/tomcat66"
#property version   "1.00"
#property strict

#include<Trade\Trade.mqh>

CTrade  trade;

input int Period=5;
input int H1;
input int H2;
input int H3;
input int H4;
input int LossBuy;
input int ProfitBuy;
input int LossSell;
input int ProfitSell;

ulong TicketBuy1;
ulong TicketSell0;

datetime Count;

double Per;
double Buf_0[];
double Buf_1[];
bool send1;
bool send0;

int h=4;
int k;
int K;
int bars;
int Handle;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");

   while(!FileIsEnding(Handle)&& !IsStopped())
     {
      StringToTime(FileReadString(Handle));
      bars++;
     }
   FileClose(Handle);
   ArrayResize(Buf_0,bars);
   ArrayResize(Buf_1,bars);
   Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");

   while(!FileIsEnding(Handle)&& !IsStopped())
     {
      Count=StringToTime(FileReadString(Handle));
      Buf_0[k]=StringToDouble(FileReadString(Handle));
      h=Period-1;
      if(k>=h)
        {
         while(h>=0)
           {
            Buf_1[k]=Buf_1[k]+Buf_0[k-h];
            h--;
           }
         Buf_1[k]=Buf_1[k]/Period;
        }
      k++;
     }
   FileClose(Handle);

   int deviation=10;
   trade.SetDeviationInPoints(deviation);
   trade.SetTypeFilling(ORDER_FILLING_RETURN);
   trade.SetAsyncMode(true);

//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   MqlDateTime stm;
   TimeToStruct(TimeCurrent(),stm);

   int    digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
   double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
   double PriceAsk=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   double PriceBid=SymbolInfoDouble(_Symbol,SYMBOL_BID);

   double SL1=NormalizeDouble(PriceBid-LossBuy*point,digits);
   double TP1=NormalizeDouble(PriceAsk+ProfitBuy*point,digits);
   double SL0=NormalizeDouble(PriceAsk+LossSell*point,digits);
   double TP0=NormalizeDouble(PriceBid-ProfitSell*point,digits);

   if(LossBuy==0)
      SL1=0;

   if(ProfitBuy==0)
      TP1=0;

   if(LossSell==0)
      SL0=0;

   if(ProfitSell==0)
      TP0=0;

//---------Buy1
   if(send1==false && K>0 && Buf_0[K-1]>Buf_1[K-1] && Buf_0[K]<Buf_1[K] && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2) && stm.hour>H1 && stm.hour<H2 && H1<H2)
     {
      send1=trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,PriceAsk,SL1,TP1);
      TicketBuy1 = trade.ResultDeal();
     }

   if(send1==true && K>0 && Buf_0[K-1]<Buf_1[K-1] && Buf_0[K]>Buf_1[K] && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2))
     {
      trade.PositionClose(TicketBuy1);
      send1=false;
     }

//---------Sell0

   if(send0==false && K>0 && Buf_0[K-1]<Buf_1[K-1] && Buf_0[K]>Buf_1[K] && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2) && stm.hour>H3 && stm.hour<H4 && H3<H4)
     {
      send0=trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,1,PriceBid,SL0,TP0);
      TicketSell0 = trade.ResultDeal();
     }

   if(send0==true && K>0 && Buf_0[K-1]>Buf_1[K-1] && Buf_0[K]<Buf_1[K] && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2))
     {
      trade.PositionClose(TicketSell0);
      send0=false;
     }
   K++;
  }
//+------------------------------------------------------------------+


Optimizaremos simultáneamente según el periodo de la línea de señal, el tiempo y las órdenes stop de las transacciones en ambas direcciones.


Optim

Optim1

Hemos entrenado la red neuronal en el periodo temporal ubicado antes de la línea del gráfico y realizado la optimización en la línea roja vertical. Después, hemos realizado la prueba con las respuestas optimizadas de la NN. Los gráficos anteriores muestran los dos resultados de optimización aleatorios positivos que el optimizador ha mostrado como el resultado de mayor prioridad.


Representando las capas de redes neuronales y mejorando la calidad de la CNN

La red neuronal se presenta como una especie de caja negra. No obstante, esto no es del todo cierto, ya que podemos ver qué características destaca la CNN en los mapas de características en las capas. Y esto nos ofrece aún más información para analizar y mejorar la calidad de la CNN. Echemos un vistazo.

Iniciamos el programa Visual.ipynb desde la carpeta CNN.

Cargamos los módulos necesarios de las bibliotecas de red neuronal.

from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Model, load_model
import pandas as pd
from tensorflow.python.keras.preprocessing import image 
import matplotlib.pyplot as plt
import numpy as np

Cargamos el modelo guardado.

model=load_model('cnn_Open11.hdf5')

Observamos la arquitectura de la red.

model.summary()

Conv2d

Nos interesa lo que estudian estas capas convolucionales.

Cargamos alguna imagen.

img_path='Train/Buy/Buy81.png'
img=image.load_img(img_path,target_size=(449,449))
plt.figure(figsize=(8, 8))
plt.imshow(img)
plt.show

Buy81

Convertimos la imagen en una matriz "numpy" y normalizamos.

x=image.img_to_array(img)
x=np.expand_dims(x,axis=0)
x/=255

Recortamos el modelo en alguna capa convolucional. Los números de las capas convolucionales son 0,3,6. Empezamos desde cero. De hecho, creamos un nuevo modelo ya entrenado, del cual recibiremos un resultado intermedio antes de la clasificación.

model=Model(inputs=model.input, outputs=model.layers[0].output)

Después, podremos echar un vistazo a las capas 3 y 6.

#model=Model(inputs=model.input, outputs=model.layers[3].output)
#model=Model(inputs=model.input, outputs=model.layers[6].output)

Mostramos la información sobre nuestro modelo simplificado.

model.summary()

Conv2d-0

Es decir, miramos la primera capa convolucional, con el número 0.

Recibimos la respuesta de la red neuronal.

model=model.predict(x)

E imprimimos uno de los mapas de características, con el número 18.

print(model.shape)
im=model[0,:,:,18]
plt.figure(figsize=(10, 10))
plt.imshow(im)
plt.show()


PLT

Como podemos ver en este mapa, la red neuronal ha resaltado velas alcistas. Y llama la atención de inmediato que en esta etapa a la CNN le resulta difícil distinguir entre velas bajistas y puntos parabólicos, ya que nosotros los hemos establecido del mismo color. Por lo tanto, podemos concluir que todos los elementos individuales de nuestro gráfico deben presentarse en diferentes colores. 

Veamos todos los mapas de características

rows=12
filters=model.shape[-1]
size=model.shape[1]
cols=filters//rows
display_grid=np.zeros((cols*size,rows*size))
for col in range(cols):
    for row in range(rows):
        channel_image=model[0,:,:,col*rows+row]
        channel_image-=channel_image.mean()
        channel_image/=channel_image.std()
        channel_image*=64
        channel_image+=128
        channel_image=np.clip(channel_image,0,255).astype('uint8')
        display_grid[col*size:(col+1)*size,row*size:(row+1)*size]=channel_image
scale=1./size
plt.figure(figsize=(scale*display_grid.shape[1],scale*display_grid.shape[1]))
plt.grid(False)
plt.imshow(display_grid,aspect='auto',cmap='viridis')

Shape

Echemos un vistazo al tercer mapa de características, con el número 2.

Shape2

En este mapa, la CNN destaca todas las velas, pero con diferentes tonos. Debido a esto, la imagen se vuelve, por así decirlo, tridimensional. No obstante, llama de nuevo atención que las sombras de las velas bajistas y de Parabolic sean del mismo color.

Veamos el mapa número 5 de la siguiente capa convolucional, que, como recordamos, tiene el número 3.


Shape5

Aquí, los puntos parabólicos se superponen entre sí y la CNN los identifica como una característica de un modelo plano. Y si miramos la figura anterior, veremos que la red neuronal también renderizó esta sección de manera distinta. Entonces, podemos concluir que las categorías de las imágenes para el entrenamiento se han expandido. Para entrenar una red neuronal, debemos introducir una categoría más: el flat.

Por eso, el examen visual de los mapas de características de la CNN nos permitirá establecer las tareas de entrenamiento con mayor claridad. También ampliará las categorías de las características identificadas por la CNN y, por lo tanto, reducirá los efectos de ruido.

Conclusión

Usando las herramientas y capacidades disponibles de las redes neuronales convolucionales, podemos adoptar un enfoque interesante y poco convencional para el análisis técnico. Al mismo tiempo, podemos simplificar sustancialmente la preparación de los datos para entrenar las redes neuronales. La visualización de los procesos que tienen lugar dentro de la red ayuda a analizar qué datos de entrada influyen más en la calidad del entrenamiento. 

Para finalizar, querríamos hablar de la optimización. Como hemos mencionado anteriormente, la optimización ha resultado bastante sencilla. No obstante, deberíamos haberla adecuado a las tareas que establecimos para nuestra red y, según ellas, deberíamos haber dividido la matriz de entrenamiento en categorías, para ya después, crear un robot comercial con estas condiciones.


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

Archivos adjuntos |
CNN.tpl (5.72 KB)
NWI.mq5 (4.98 KB)
CNNet.mq5 (4.16 KB)
Train.ipynb (11.48 KB)
Test.ipynb (2.92 KB)
Visual.ipynb (10.31 KB)
TestCNN.mq5 (9.38 KB)
Algoritmo de autoadaptación (Parte IV): Funcionalidad adicional y pruebas Algoritmo de autoadaptación (Parte IV): Funcionalidad adicional y pruebas
Seguimos completando el algoritmo con la funcionalidad mínima necesaria y realizando pruebas con el material obtenido. La rentabilidad ha resultado baja, pero los artículos nos muestran un modelo que nos permite comerciar con beneficios de una forma completamente automática con instrumentos comerciales completamente diferentes, y no solo diferentes, sino que también se comercian en mercados fundamentalmente distintos.
Redes neuronales: así de sencillo (Parte 10): Multi-Head Attention (atención multi-cabeza) Redes neuronales: así de sencillo (Parte 10): Multi-Head Attention (atención multi-cabeza)
Ya hemos hablado con anterioridad del mecanismo de auto-atención (self-attention) en las redes neuronales. En la práctica, en las arquitecturas de las redes neuronales modernas, se usan varios hilos de auto-atención paralelos para buscar diversas dependencias entre los elementos de la secuencia. Vamos a ver la implementación de este enfoque y evaluar su influencia en el rendimiento general de la red.
Trabajando con los precios y Señales en la biblioteca DoEasy (Parte 65): Colección de la profundidad de mercado y clase para trabajar con las Señales MQL5.com Trabajando con los precios y Señales en la biblioteca DoEasy (Parte 65): Colección de la profundidad de mercado y clase para trabajar con las Señales MQL5.com
En el presente artículo, crearemos una clase de colección de profundidad de mercado para todos los símbolos y comenzaremos a desarrollar la funcionalidad necesaria para trabajar con el servicio de señales de MQL5.com. Para ello, crearemos una clase de objeto de señal.
Trabajando con los precios en la biblioteca DoEasy (Parte 64): Profundidad del mercado, clases del objeto de instantánea y del objeto de serie de instantáneas del DOM Trabajando con los precios en la biblioteca DoEasy (Parte 64): Profundidad del mercado, clases del objeto de instantánea y del objeto de serie de instantáneas del DOM
En este artículo, vamos a crear dos clases: la clase del objeto de instantánea del DOM y la clase del objeto de serie de instantáneas del DOM, además, simularemos la creación de la serie de datos del DOM.