English Русский 中文 Español Deutsch 日本語
preview
Aplicação prática de redes neurais no trading (Parte 2). Visão computacional

Aplicação prática de redes neurais no trading (Parte 2). Visão computacional

MetaTrader 5Negociação | 20 abril 2021, 10:48
1 526 0
Andrey Dibrov
Andrey Dibrov

Introdução

Um problema básico na hora de preparar dados ao treinar redes neurais usadas em trading é a preparação das informações de entrada. Por exemplo, se usarmos uma dúzia de indicadores. Esses indicadores, aliás, serão um conjunto de diversos gráficos informativos. Se calcularmos esses indicadores até uma certa profundidade, obteremos até cem entradas e, em alguns casos, até mais. Será que podemos facilitar o treinamento de uma rede neural usando a visão computacional? Para resolver este problema, usaremos redes neurais convolucionais, que agora são usadas para resolver problemas de classificação e reconhecimento.


Arquitetura de rede neural convolucional

O artigo usará uma rede neural convolucional, e na figura abaixo mostramos sua arquitetura. Além disso, ela caracteriza o princípio geral de construção Convolutional Neural Network (CNN)

Nesse caso, temos:

  1. Entrada CNN que representa uma imagem de 449x449 pixels.
  2. A primeira camada convolucional feita de 96 mapas de recursos. Cada mapa representa uma imagem 447x447. O núcleo da convolução é 3x3.
  3. Uma camada de subamostra feito de 96 mapas de recursos com um tamanho de 223x223 com um núcleo 2x2.
  4. Segunda camada convolucional de 32 mapas de recursos. Cada mapa representa uma imagem de 221x221. O núcleo da convolução é 3x3.
  5. Camada de uma subamostra de 32 mapas de recursos com um tamanho de 110x110 com um núcleo 2x2.
  6. A terceira camada convolucional feita de 16 mapas de recursos. Cada cartão representa uma imagem 108x108. O núcleo da convolução é 3x3.
  7. Uma camada de subamostra de 16 mapas de recursos, 54x54 de tamanho, com um núcleo 2x2. Não mostrado na figura.
  8. Camada totalmente conectada de 64 neurônios.
  9. Camada de saída de um neurônio. Essas duas camadas representam a unidade de classificação.

CNN

Olhando para a frente, vou ressaltar o seguinte. As primeiras pessoas a se familiarizarem com as redes neurais convolucionais não devem temer o aparente embaraço e complexidade que acarreta a sua construção. Uma rede neural de uma determinada arquitetura é construída automaticamente. Precisamos apenas definir os parâmetros básicos. 

Preparando uma matriz de imagens para treinar e testar uma rede neural

Antes de preparar uma matriz de imagens, precisamos decidir o que queremos de nossa rede neural. O ideal seria treinar a rede com base nos pivôs, e com base nessa informação, pegar as imagens usadas para desenhar a barra extrema. Visto que, como se viu, este experimento não tem valor aplicado, nos concentraremos em outro conjunto de imagens. Embora, após ler este artigo, no futuro você possa experimentar essa matriz. Isso também poderá ser útil para garantir que as redes neurais tenham um bom desempenho ao classificar usando imagens completas. Mas as respostas da rede obtidas numa série de tempo contínua requerem otimização adicional. 

Não complicaremos nosso experimento e nos concentraremos em duas categorias de imagens:

  • Buy - quando o preço sobe ou quando o preço atinge o mínimo diário
  • Sell - quando o preço cai ou quando o preço atinge a alta diária

Buy   Buy1  Buy2  Buy3

Como podemos ver para treinamento de rede, definiremos o movimento em qualquer direção como o preço que atinge novos valores extremos sequenciais na direção da tendência e, neste momento, tiraremos um instantâneo do gráfico. O momento de reversão da tendência também é importante para o aprendizado de rede. Também tiraremos um instantâneo do gráfico quando o preço atingir a máxima ou mínima diária.

Vamos primeiro preparar um gráfico para obter a imagem desejada. Para fazer isso, usaremos o modelo CNN.tpl. Vamos colocá-lo na pasta \AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Profiles\Templates.

CNN.tpl

Definimos o texto como "White" nas propriedades do gráfico.

CNN.tpl


Você pode colocar outros indicadores. Esses indicadores foram tomados por mim arbitrariamente. Definimos o tamanho do gráfico arbitrariamente, mas precisamos encontrar o ideal, dependendo dos recursos do hardware.

Vamos usar um script para criar uma matriz de imagens.

//+------------------------------------------------------------------+
//|                                                        CNNet.mq5 |
//|                                   Copyright 2021, Andrey Dibrov. |
//|                           https://www.mql5.com/ru/users/tomcat66 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, Andrey Dibrov."
#property link      "https://www.mql5.com/ru/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;
        }
     }
  }

O script funciona em dois modos "Train" (criamos uma série de imagens para treinamento) e "Test" (criamos uma série de imagens para receber respostas da NN, com base nas quais o indicador será obtido). Usaremos este indicador no futuro para otimizar a estratégia de negociação. 

Vamos rodar o script no modo "Train".

Train

Variável "Date" — data de início da amostra de imagens para treinamento. “DateOut' — data final da amostra de imagens para treinamento. "DateTest" — data de início da amostragem de imagem para receber respostas de hora em hora da rede neural. A data final das respostas será a hora da inicialização do script. 

Na pasta ...\MQL5\Files do diretório de dados obtemos o conjunto de imagens Buy... e Sell.... Um total de 6125.

Tren

A seguir, prepararemos catálogos para o conjunto de treinamento, validação e teste. Por conveniência, vamos criar uma pasta "CNN" no "Desktop" e nela três pastas "Train", "Val", "Test".

CNN

Nos catálogos "Train" e "Val", criamos os subdiretórios "Buy" e "Sell". No diretório "Test", criamos o subdiretório "Resp".

Train

Da pasta ...\MQL5\Files tiramos todos os arquivos "Buy..." e os colocamos na pasta ...\Train\Buy. Temos 3139 imagens. Faremos o mesmo procedimento com os arquivos "Sell..." e colocá-los no diretório ...\Trein\Sell. Temos 2986 imagens. Na pasta "Buy" e "Sell" recortamos 30% das últimas imagens (com os números mais altos) e as colocamos nas pastas correspondentes do catálogo "Val".

Temos 

  • ...\Train\Buy - 2198 imagens, 
  • ...\Val\Buy   - 941   imagens,
  • ...\Train\Sell - 2091 imagens,
  • ...\Val\Sell   - 895   imagens.

Assim, preparamos um conjunto de imagens para treinamento de nossa rede. As imagens, neste caso, tinham 449x449 pixels de tamanho.

Vamos preparar um conjunto de imagens para teste. Vamos executar o script no modo "Test".

Test

Na pasta ...\MQL5\Files do diretório de dados, obteremos um conjunto de instantâneos sequenciais por hora. No momento são 12558. Não os separamos, pois a própria rede neural deve realizar essa separação. Quer dizer, ela nos dá uma recomendação dizendo com que probabilidade esta ou aquela imagem corresponde às condições sob as quais treinamos a rede. Movimento para cima e para baixo. Movimento para baixo e reversão para cima. 

Vamos recortar esses arquivos e colá-los na pasta ...CNN\Test\Resp.

Test

Assim, preparamos um conjunto de imagens para testar ainda mais as respostas e otimizar a estratégia. Permanecemos na pasta... \MQL5\Files arquivos com datas e horas movemos EURUSDDate para a pasta CNN.

Com conclusão, gostaria de ressaltar um recurso do MetaTrader 5. Seria mais conveniente e confiável preparar um conjunto de imagens usando um Expert Advisor no testador de estratégia, no entanto, por algum motivo, o MetaTrader 5 no testador de estratégia não faz "captura de tela" Mas, em qualquer caso, ao implementar um robô de negociação, esse recurso não nos prejudicará.


Treinamento de rede neural

Para trabalhar com redes neurais convolucionais, usaremos o ambiente Anaconda. Precisamos configurar este ambiente para funcionar com CPU e GPU (se você tiver uma placa de vídeo NVIDIA). Precisamos dessa placa de vídeo para acelerar o processo de aprendizado. Embora imponha algumas restrições à criação da arquitetura da NN. Esta é a dependência da quantidade de RAM da placa de vídeo. Mas, na velocidade do aprendizado, ganhamos significativamente. Por exemplo, no meu caso, se numa CPU uma época de treinamento dura 20 minutos, numa GPU leva 1-2 minutos. Se precisarmos treinar a NN por 40 épocas, teremos 13 horas e 1,5. E se tudo isso está em fase de pesquisa, então é claro que o uso da GPU acelera significativamente o processo de busca da NN.

  1. Carregamose instalamos a versão mais recente do Anaconda Navigator. Fazemos tudo por padrão.
  2. No menu "Iniciar\Anaconda3" rodamos "Anaconda Promt".
  3. Inserimos o comando "pip install tensorflow". Instalamos a biblioteca de software para aprendizado de máquina desenvolvido pela empresa Google.
    tensorflow

  4. Inserimos o comando "pip install keras". Instalamos a biblioteca de rede neural Keras.
    keras

  5. Criamos um novo ambiente conda para GPU. Escrevemos o comando conda create --name PythonGPU. E ativamos o ambiente - activate PythonGPU.
     
    GPU

  6. Para instalação de tensorflow gpu digitamos o comando - conda create -n PythonGPU python=3.6 tensorflow-gpu. Quero salientar que é necessário instalar tensorflow gpu para python 3.6.
      tensorflow gpu

  7. Para instalar keras gpu, digitamos o comando - conda install -c anaconda keras-gpu.
    keras gpu

  8. Vamos instalar a interface Jupyter para programação no ambiente de GPU Python. Para CPU Jupyter foi instalado durante a instalação do Anaconda. Digitamos o comando - conda install jupyterJupyter

  9. Definimos as duas bibliotecas Pandas e Pillow - conda install -c anaconda pandas. E em seguida - conda install pillow. Essas bibliotecas também precisam ser instaladas para a CPU se você não for usar uma placa gráfica.
    pandas 

  10. pillow

  11. Agora podemos começar a treinar nossa rede neural convolucional. Na pasta CNN criada anteriormente colocamos os dois arquivos "Train.ipynb" e "Test.ipynb". Esses são arquivos no formato Jupyter Notebook, com os quais trabalharemos no futuro. Iniciamos Jupyter Notebook (PythonGPU) e abrimos o arquivo Train. CNN  

Analisamos o código do programa bloco a bloco.

Carregamos os módulos necessários de bibliotecas de rede neural.

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

Definimos os hiperparâmetros necessários.

# 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
# Number of images for validation
nb_validation_samples = 1836
# Number of images for testing
#nb_test_samples = 3736

Criamos uma arquitetura de rede.

  • Configuramos uma arquitetura de rede neural convolucional (CNN, convolutional neural network)
  • A imagem de entrada tem 449x449 pixels, três canais (vermelho, verde e azul). Estamos usando uma imagem colorida. Poderemos experimentar com duas cores
  • Criamos a primeira camada de convolução para trabalhar com dados 2D - 96 mapas de recursos, cada um com seu próprio kernel de convolução 3x3. Cada neurônio da camada convolucional é conectado a uma seção quadrada 3x3 da imagem 
  • Camada de ativação usando a função "relu" - uma vez que é menos intensiva em termos de computação
  • Para reduzir o tamanho, adicionamos uma camada de subamostragem com um kernel 2x2 com a seleção do valor máximo a partir deste quadrado
  • Em seguida, adicionamos mais duas camadas de convolução com 32 e 16 núcleos e duas camadas de ativação e subamostra.
  • Convertemos dados de 2D em 1D
  • Transferimos os dados transformados para uma camada totalmente conectada com 64 neurônios
  • Com ajuda da função de camada de regularização Dropout (0,5), tentamos evitar o retreinamento da rede
  • Adicionamos uma camada de saída totalmente conectada com um neurônio. Como estamos usando duas classes de imagens, receberemos uma resposta binária da NN. Podemos experimentar várias classes também. Por exemplo, duas tendências e uma lateralização. Então, haveria três neurônios na camada de saída. E, naturalmente, teríamos que dividir as imagens em três classes.
  • Função de ativação "sigmoid". Adequado para classificação e melhor recomendado em nossos experimentos

Podemos facilmente modernizar este exemplo de arquitetura - aumentar o número de camadas, suas dimensões, mudar de lugar dependendo da sequência de dimensões, dimensão dos núcleos de convolução, funções de ativação, trabalhar com camadas totalmente conectadas. Porém, aqui surge um dilema - ao usar uma GPU, é necessário aumentar a RAM da placa de vídeo - se quisermos aumentar a arquitetura de uma rede neural: reduzimos o tamanho das imagens ou sacrificamos nosso tempo e usamos a 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'))

Compilamos a rede. Usamos uma função de erro binary-crossentropy. Nesse caso, usaremos uma resposta composta por duas classes, que devem assumir os valores 0 ou 1, idealmente. Na realidade, nossos valores serão distribuídos de 0 a 1. O otimizador é descida de gradiente. Nesse caso, com base em experimentos, ela é mais adequada para treinar NN. Escolhemos a métrica precisão (accuracy) - a porcentagem de respostas corretas. 

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

Normalizamos os dados sobre a intensidade dos pixels da imagem.

datagen = ImageDataGenerator(rescale=1. / 255)

Usamos geradores Keras para ler dados do disco e criar matrizes de treinamento e validação para NN a partir de nossas imagens. Novamente, escolhemos binary para class_mode. Para Shuffle escolhemos False. Não misturamos nossas imagens.

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)

A função de retorno de chamada armazena a NN treinada após cada época. Isso nos permite selecionar a rede mais adequada com base em valores de erro e porcentagens de acerto.

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

Vamos prosseguir diretamente para o treinamento da rede.

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 o programa conforme mostrado na figura.

Run


Se tudo foi feito corretamente, a rede neural começará a aprender.

Fit

Após o término do processo de treinamento, haverá 20 NNs treinadas na pasta CNN.

CNN

Examinamos os resultados do aprendizado e decidimos qual rede neural escolher para uso posterior.


NN

À primeira vista, na época 18, a rede neural aprendeu com uma taxa de erro de 30% e 85% de resultados corretos. Porém, ao verificar se o conjunto da NN é válido, vemos que o erro aumenta e a porcentagem de respostas positivas cai. Isso significa que precisamos escolher uma rede neural treinada na 11ª época. Como podemos ver, aqui os resultados de validação mais apropriados são val_loss: 0,6607 e val_accuracy: 0,6129. Idealmente, é necessário tentar obter valores de erro tendendo a 0 (ou pelo menos abaixo de cerca de 35-40%), valores de precisão tendendo a 1(ou pelo menos acima de cerca de 55-60%). Nesse caso, a otimização não poderia ter sido feita, ou poderia ter sido realizada de acordo com os parâmetros mínimos para melhorar a qualidade da negociação. Mas mesmo com esses resultados de aprendizagem, podemos criar um sistema de negociação positivo.


Interpretando a resposta da rede neural

Vamos agora verificar se todos os nossos esforços têm algum significado prático.

Vamos iniciar Jupyter Notebook sem suporte de GPU e abrimos o arquivo Test.jpynb do diretório CNN.

Test

Vamos ver bloco por bloco.

Carregamos os módulos necessários de bibliotecas de rede neural.

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

Definimos os parâmetros necessários.

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

Lemos a data e a hora das imagens testadas do arquivo.

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

Carregamos a NN salva após a 11ª época.

model=load_model('cnn_Open11.hdf5')

Normalizamos as imagens.

datagen = ImageDataGenerator(rescale=1. / 255)

Usamos o gerador para lermos os dados do disco.

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

Recebemos respostas da NN.

indicator=model.predict(predict_generator, nb_predict_samples )

Mostramos o resultado. O processo de obtenção de feedback é longo e nos notificará que acabou.

print(indicator)

Salvamos o resultado num arquivo.

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

Vamos iniciar o programa e após sua operação na pasta CNN obteremos o arquivo Indicator.csv.

Indicator

E vamos transferi-lo para a pasta C:\Users\...\AppData\Roaming\MetaQuotes\Terminal\Common\Files.

Vamos colocar o indicador NWI no gráfico EURUSD H1.

NWI

 

//+------------------------------------------------------------------+
//|                                                          NWI.mq5 |
//|                                 Copyright © 2019, Andrey Dibrov. |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2019, Andrey Dibrov."
#property link      "https://www.mql5.com/ru/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 Период=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=Период-1;
      if(i>=0)
        {
         while(h>=0)
           {
            SignBuffer[i]=SignBuffer[i]+ExtBuffer[i+h];
            h--;
           }
        }
      SignBuffer[i]=SignBuffer[i]/Период;
      TimeToStruct(Date1,stm);
      i--;
     }
   FileClose(Handle);
   return(rates_total);
  }
//+------------------------------------------------------------------+

Para simplificar, interpretaremos as respostas da CNN usando a interseção da linha do indicador principal com uma média simples. Vamos usar o Expert Advisor TestCNN.

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

#include<Trade\Trade.mqh>

CTrade  trade;

input int Период=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=Период-1;
      if(k>=h)
        {
         while(h>=0)
           {
            Buf_1[k]=Buf_1[k]+Buf_0[k-h];
            h--;
           }
         Buf_1[k]=Buf_1[k]/Период;
        }
      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++;
  }
//+------------------------------------------------------------------+


Vamos otimizar simultaneamente pelo período da linha de sinal, tempo e ordens stop para trades em ambas as direções.


Optim

Optim1

A rede neural foi treinada ao longo de um período localizado antes da linha do gráfico, e a otimização foi realizada para a linha vermelha vertical; depois, foi realizado um teste para as respostas otimizadas a partir da NN. Os gráficos acima mostram dois resultados de otimização positivos aleatórios que o otimizador mostrou como o resultado de maior prioridade.


Renderizando camadas da rede neural e melhorando a qualidade da CNN

A rede neural é apresentada como uma espécie de caixa preta. No entanto, isso não é totalmente verdade, já que podemos ver que recursos a CNN está usando nos mapas de recursos em camadas. Isso nos dá mais informações para análises e melhorando ainda mais a qualidade da CNN. Vamos ver isso.

Vamos iniciar o programa Visual.ipynb a partir da pasta CNN.

Carregamos os módulos necessários de bibliotecas de rede neural.

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

Carregamos o modelo salvo.

model=load_model('cnn_Open11.hdf5')

Vamos dar uma olhada na arquitetura da rede.

model.summary()

Conv2d

Estamos interessados em saber o que essas camadas convolucionais estão estudando.

Vamos carregar alguma imagem.

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

Convertemos a imagem numa matriz numpy e normalizamos.

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

Vamos cortar o modelo em alguma camada convolucional. Os números da camada convolucional são 0,3,6. Vamos começar do zero. Na verdade, criamos um modelo novo, mas já treinado, e dele receberemos um resultado intermediário, antes da classificação.

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

Depois, podemos olhar para a 3ª camada e a 6ª.

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

Vamos exibir informações sobre nosso modelo simplificado.

model.summary()

Conv2d-0

Ou seja, vamos olhar para a primeira camada convolucional numerada 0.

Vamos obter uma resposta da rede neural.

model=model.predict(x)

E imprimiremos um dos mapas de recursos usando o número 18.

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


PLT

Como podemos ver neste mapa, a rede neural destacou as velas de alta. Aqui surpreendente que, neste estágio, a CNN tem dificuldade em distinguir entre velas baixistas e pontos parabólicos. Uma vez que eles nos os definimos com a mesma cor. Agora podemos concluir que todos os elementos individuais em nosso gráfico devem ser apresentados com cores diferentes. 

Vejamos todos os mapas de recursos

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

Vamos dar uma olhada no terceiro mapa de recursos, o número 2.

Shape2

Neste mapa, a CNN destaca todas as velas, mas com tonalidades diferentes. E, devido a isso, a imagem se torna, por assim dizer, tridimensional. No entanto, chamamos novamente a atenção para o fato de que as sombras das velas de baixa e do Parabolic são da mesma cor.

Vamos dar uma olhada no mapa número 5 da próxima camada convolucional, que temos um número, como nos lembramos - 3.


Shape5

Aqui, os pontos do Parabilic são sobrepostos uns aos outros e a CNN os identifica como um sinal de um padrão lateral. Se olharmos para a figura anterior, veremos que a rede neural também interpretou esta seção de forma diferente. Ou seja, podemos concluir sobre a ampliação das categorias de imagens para treinamento. Para treinar uma rede neural, é necessário introduzir mais uma categoria - a lateralização.

Assim, o exame visual dos mapas de recursos da CNN nos permitirá definir as tarefas de treinamento com mais clareza. E também nos permitirá expandir as categorias de recursos identificados pela CNN e, assim, reduzir os efeitos de ruído.

Fim do artigo

Usando as ferramentas e recursos publicamente disponíveis das redes neurais convolucionais, podemos adotar uma abordagem interessante e não convencional para a análise técnica. Além disso, podemos simplificar muito a preparação de dados para o treinamento de redes neurais. A visualização dos processos que ocorrem na rede ajuda a analisar quais dados de entrada mais afetam a qualidade do treinamento. 

Concluindo, gostaria de falar sobre a otimização. Como mencionei antes, a otimização foi bastante simples. No entanto, deveria ter sido alinhado com as tarefas que definimos para a nossa rede e, de acordo com elas, dividimos a matriz de treinamento em categorias. E já crie um robô de negociação com essas condições.


Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/8668

Arquivos anexados |
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)
Trabalhando preços na biblioteca DoEasy (Parte 63): livro de ofertas, classe de ordem abstrata do livro de ofertas Trabalhando preços na biblioteca DoEasy (Parte 63): livro de ofertas, classe de ordem abstrata do livro de ofertas
Neste artigo, começaremos a desenvolver funcionalidades para trabalhar com o livro de ofertas. Criaremos uma classe de objeto para uma ordem abstrata do livro de ofertas e dos seus herdeiros.
Trabalhando com preços na biblioteca DoEasy (Parte 62): atualização em tempo real da série de ticks, preparação para trabalhar com o livro de ofertas Trabalhando com preços na biblioteca DoEasy (Parte 62): atualização em tempo real da série de ticks, preparação para trabalhar com o livro de ofertas
Neste artigo, atualizaremos em tempo real da coleção de dados de ticks e prepararemos a classe do objeto-símbolo para trabalhar com o livro de ofertas, cujo funcionamento abordaremos no próximo artigo.
Algoritmo auto-adaptável (Parte IV): funcionalidade e testes adicionais Algoritmo auto-adaptável (Parte IV): funcionalidade e testes adicionais
Continuo a complementar o algoritmo com a funcionalidade mínima necessária, vou fazer testes do que obtivemos como resultado. A lucratividade acabou sendo baixa, mas os artigos mostram um modelo que permite negociar com lucro de modo totalmente automático com base em instrumentos de negociação completamente diferentes, e não apenas diferentes, mas também operados em mercados fundamentalmente diferentes.
Força bruta para encontrar padrões (Parte III): novos horizontes Força bruta para encontrar padrões (Parte III): novos horizontes
Este artigo dá continuidade ao tópico sobre força bruta, trazendo novos recursos de análise de mercado para o algoritmo do meu programa e acelerando, assim, a velocidade da análise e a qualidade dos resultados finais, o que fornece a visão da mais alta qualidade de padrões globais dentro da estrutura desta abordagem.