Tutorial da Rede Neural FANN2MQL

26 outubro 2015, 10:27
Julien
0
912

Em primeiro lugar:

Por favor, instale a Biblioteca fann2MQL, pode ser baixado Aqui.

Introdução

Até agora, houve apenas um exemplo de como usar a Biblioteca Fann2MQL, onde os traders podem usar a Biblioteca de Código Fonte Aberto da Rede Neural "FANN" em seus códigos de MQL.

Mas o exemplo escrito pelo criador da Biblioteca Fann2MQL não é fácil de entender. Não é para iniciantes.

Então eu escrevi um outro exemplo, uma maneira mais fácil em seu conceito e totalmente comentada.

Não está diretamente relacionado com a negociação e não vai utilizar qualquer dado financeiro. É um exemplo simples de aplicação estática.

Neste exemplo, nós vamos ensinar uma rede neural simples para reconhecer um padrão simples:

O padrão a ser ensinado será composto por 3 números: a, b e c.

Se a < b && b < c então a expectativa de saída = 1
Se a < b && b > c então a expectativa de saída = 0
Se a > b && b > c então a expectativa de saída = 0
Se a > b && b < c então a expectativa de saída = 1

Você pode raciocinar com esses números como sendo coordenadas de vetor (vetor indo para cima ou para baixo), por exemplo, ou a direção do mercado. Neste caso, o padrão pode ser interpretado como:

UP UP = UP

UP DOWN = DOWN

DOWN DOWN = DOWN

DOWN UP = UP


Primeiro, vamos criar uma rede neural.

Em seguida, vamos mostrar a rede alguns exemplos de padrões para que ela possa aprender e deduzir as regras.

Finalmente, vamos mostrar a rede novos padrões que ela nunca viu e perguntar-lhe quais são as suas conclusões. Se ele entendeu as regras, então estará capacitada a reconhecer estes padrões.

o código comentado:

// Nós incluímos a biblioteca Fann2MQl
#include <Fann2MQL.mqh>

#property copyright "Copyright © 2009, Julien Loutre"
#property link      "http://www.thetradingtheory.com"

#property  indicator_separate_window
#property  indicator_buffers 0

// o número total de camadas, aqui, há uma camada de entrada,
// 2 camadas ocultas e uma camada de saída = 4 camadas.
int nn_layer = 4;
int nn_input = 3; // Número de neurônios de entrada. Nosso teste padrão é feito de 3 números, 
                  // significando 3 neurônios de entrada.
int nn_hidden1 = 8; // número de neurônios na primeira camada oculta
int nn_hidden2 = 5; // número na segunda camada oculta
int nn_output = 1; // número de saídas

// trainingData[][] conterá os exemplos 
// Vamos usar para ensinar as regras aos neurônios.
double      trainingData[][4];  // IMPORTANTE! size = nn_input + nn_output


int maxTraining = 500;  // número máximo de tempo do treinamento, 
                        // os neurônios com alguns exemplos
double targetMSE = 0.002; // o MSE (Mean-Square Error /Erro Quadrático Médio) dos neurônios, deveríamos 
                          // obter no máximo (você vai entender isso mais abaixo no código)

int ann; // Esta variável será o identificador da rede neuronal.

// Quando o indicador é removido, temos de eliminar todas as redes neurais 
// a partir da memória do computador.
int deinit() {
   f2M_destroy_all_anns();
   return(0);
}

int init() {
   int i;
   double MSE;
   
   Print("=================================== START EXECUTION ================================");
   
   IndicatorBuffers(0);
   IndicatorDigits(6);
   
   // Nós redimensionamos o array trainingData, para que possamos usá-lo.
   // Nós vamos mudar o seu tamanho em um de cada vez.
   ArrayResize(trainingData,1);
   
   Print("##### INIT #####");
   
   // Criamos novas redes de neurônios
   ann = f2M_create_standard(nn_layer, nn_input, nn_hidden1, nn_hidden2, nn_output);
   
   // Vamos verificar se foi criado com sucesso. 0 = OK, -1 = erro
   debug("f2M_create_standard()",ann);
   
   // Nós definimos a função de ativação. Não se preocupe com isso. Apenas faça.
        f2M_set_act_function_hidden (ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
        f2M_set_act_function_output (ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
        
        // Alguns estudos mostram que estatisticamente os melhores resultados são alcançados usando este intervalo; 
     // mas você pode tentar diferente e ver se fica melhor ou o pior
        f2M_randomize_weights (ann, -0.77, 0.77);
        
        // Eu só imprimi no console o número de neurônios de entrada e saída. 
      // Apenas para verificar. Apenas para fins de depuração.
   debug("f2M_get_num_input(ann)",f2M_get_num_input(ann));
   debug("f2M_get_num_output(ann)",f2M_get_num_output(ann));
        
   
   Print("##### REGISTER DATA #####");
   
   //Agora nós preparamos alguns exemplos de dados (com expectativa de saída) 
   // e os adicionamos ao conjunto de treinamento.
   // Uma vez que temos de adicionar todos os exemplos que queremos, nós vamos enviar 
   // estes dados de treinamento configurados aos neurônios, para que eles possam aprender.
   // prepareData() tem alguns argumentos:
   // - Ação para fazer (treinar ou computar)
   // - os dados (aqui, 3 dados por configuração)
   // - O último argumento é a expectativa de saída.
   // Aqui, esta função leva os dados de exemplo e a expectativa de saída, 
   // e adiciona nas configurações de aprendizado.
   // Verifique o comentário associado a esta função para obter mais detalhes.
   //
   // Aqui é o padrão que estamos ensinando:
   // Existem 3 números. Vamos chamá-los de a, b e c.
   // Você pode raciocinar com esses números como sendo coordenadas de vetor 
  // Por exemplo (vetor indo para cima ou para baixo)
   // Se a < b && b < c então saída = 1
   // Se a < b && b > c então saída = 0
   // Se a > b && b > c então saída = 0
   // Se a > b && b < c então saída = 1
   
   
   // UP UP = UP / Se a < b && b < c então saída = 1 
   prepareData("train",1,2,3,1);
   prepareData("train",8,12,20,1);
   prepareData("train",4,6,8,1);
   prepareData("train",0,5,11,1);

   // UP DOWN = DOWN / Se a < b && b > c então saída = 0
   prepareData("train",1,2,1,0);
   prepareData("train",8,10,7,0);
   prepareData("train",7,10,7,0);
   prepareData("train",2,3,1,0);

   // DOWN DOWN = DOWN / Se a > b && b > c então saída = 0
   prepareData("train",8,7,6,0);
   prepareData("train",20,10,1,0);
   prepareData("train",3,2,1,0);
   prepareData("train",9,4,3,0);
   prepareData("train",7,6,5,0);

   // DOWN UP = UP / Se a > b && b < c então saída = 1
   prepareData("train",5,4,5,1);
   prepareData("train",2,1,6,1);
   prepareData("train",20,12,18,1);
   prepareData("train",8,2,10,1);
   
   // Agora imprimiremos a formação integral configurada ao console, para verificar como se parece.
   // Apenas para fins de depuração.
   printDataArray();
   
   
   Print("##### TRAINING #####");
   
   // Precisamos treinar os neurônios muitas vezes, ordenadamente, 
   // para que sejam bons naquilo que foram solicitados para fazer.
   // Aqui vou treiná-los com os mesmos dados (nossos exemplos) várias vezes, 
   // até que compreendam plenamente as regras que estamos tentando ensiná-los, ou até 
   // O treinamento ser repetido o número 'maxTraining' de vezes  
   // (neste caso maxTraining = 500)
   // Quanto melhor for entendida a regra, menor será o Erro Quadrático Médio.
   // A função de teach() retorna o Erro Quadrático Médio (ou MSE)
   // 0.1 ou inferior é um número suficiente para regras simples
   // 0,02 ou menor é melhor para regras complexas como a que 
   // estamos tentando ensiná-los (é um reconhecimento modelo, o que não é tão fácil. )
   for (i=0;i<maxTraining;i++) {
      MSE = teach(); // Toda vez que o loop é executado, a função teach é ativada. 
                     // Confira os comentários associados a esta função para entender mais.
      if (MSE < targetMSE) { // Se o MSE é menor do que nós definimos (aqui targetMSE = 0,02)
         debug("training finished. Trainings ",i+1); // Então imprimimos o console 
                                                     // em quantos treinamentos 
                                                     // forem necessários para os neurônios compreenderem
         i = maxTraining; // e vamos sair deste ciclo
      }
   }
   
   // Nós imprimimos no console o valor MSE após o treinamento ter sido concluído.
   debug("MSE",f2M_get_MSE(ann));
   
   
   Print("##### RUNNING #####");
   // E agora podemos solicitar aos neurônios a análise de novos dados que eles nunca viram.
   // Será que eles vão reconhecer os padrões corretamente?
   // Você pode ver que eu usei a mesma função prepareData() aqui, 
   // com o primeiro argumento definido para "compute".
   // O último argumento foi dedicado à expectativa de saída, 
   // quando utilizamos esta função para registrar exemplos anteriores
   // é inútil, então deixamos a zero.
   // Se você preferir, você pode chamar diretamente a função compute().
   // Neste caso, a estrutura é computar (inputVector[]);
   // Então, ao invés de prepareData ("compute",1,3,1,0); você faria algo como:
   // double inputVector[]; // declara um novo array.
   // ArrayResize(inputVector,f2M_get_num_input(ann)); 
   // Redimensionar o array ao número de entrada do neurônio.
   // inputVector[0] = 1; // Adiciona os dados no array. 
   // inputVector[1] = 3;
   // inputVector[2] = 1;
   // result = compute(inputVector); // Chamar a função compute(), com o array de entrada.
   // a função prepareData() chama a função compute(), 
   // que imprime o resultado no console, 
   // afim de verificarmos se os neurônios estavam certos ou não.
   debug("1,3,1 = UP DOWN = DOWN. Should output 0.","");
   prepareData("compute",1,3,1,0);
   
   debug("1,2,3 = UP UP = UP. Should output 1.","");
   prepareData("compute",1,2,3,0);
   
   debug("3,2,1 = DOWN DOWN = DOWN. Should output 0.","");
   prepareData("compute",3,2,1,0);
   
   debug("45,2,89 = DOWN UP = UP. Should output 1.","");
   prepareData("compute",45,2,89,0);
   
   debug("1,3,23 = UP UP = UP. Should output 1.","");
   prepareData("compute",1,3,23,0);
   
   debug("7,5,6 = DOWN UP = UP. Should output 1.","");
   prepareData("compute",7,5,6,0);
   
   debug("2,8,9 = UP UP = UP. Should output 1.","");
   prepareData("compute",2,8,9,0);
   
   Print("=================================== END EXECUTION ================================");
   return(0);
}

int start() {
   return(0);
}

/*************************
** printDataArray()
** Imprimir os dados utilizadas no treinamento dos neurônios
** Este é inútil. Apenas criado para fins de depuração.
*************************/
void printDataArray() {
   int i,j;
   int bufferSize = ArraySize(trainingData)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
   string lineBuffer = "";
   for (i=0;i<bufferSize;i++) {
      for (j=0;j<(f2M_get_num_input(ann)+f2M_get_num_output(ann));j++) {
         lineBuffer = StringConcatenate(lineBuffer, trainingData[i][j], ",");
      }
      debug("DataArray["+i+"]", lineBuffer);
      lineBuffer = "";
   }
}


/*************************
** prepareData()
** Prepara os dados para um treinamento ou computação.
** coloca os dados num array 
** e os envia à função de treinamento ou execução.
** Atualiza de acordo com o número de entrada/saída que o seu código precisa.
*************************/
void prepareData(string action, double a, double b, double c, double output) {
   double inputVector[];
   double outputVector[];
   // nós redimensionamos os array no tamanho certo
   ArrayResize(inputVector,f2M_get_num_input(ann));
   ArrayResize(outputVector,f2M_get_num_output(ann));
   
   inputVector[0] = a;
   inputVector[1] = b;
   inputVector[2] = c;
   outputVector[0] = output;
   if (action == "train") {
      addTrainingData(inputVector,outputVector);
   }
   if (action == "compute") {
      compute(inputVector);
   }
   // Se você tiver mais do que 3 entradas, basta alterar a estrutura desta função.
}


/*************************
** addTrainingData()
** Adiciona um único conjunto de dados de treinamento 
** (exemplo dados + expectativa de saída) para a configuração do treinamento global
*************************/
void addTrainingData(double inputArray[], double outputArray[]) {
   int j;
   int bufferSize = ArraySize(trainingData)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
   
   // registra os dados de entrada ao array principal 
   for (j=0;j<f2M_get_num_input(ann);j++) {
      trainingData[bufferSize][j] = inputArray[j];
   }
   for (j=0;j<f2M_get_num_output(ann);j++) {
      trainingData[bufferSize][f2M_get_num_input(ann)+j] = outputArray[j];
   }
   
   ArrayResize(trainingData,bufferSize+2);
}


/*************************
** teach()
** Obtém todos os dados de treinamento e usá-os para treinar os neurônios de uma vez.
** A fim de treinar corretamente os neurônios, você precisa executar
** esta função muitas vezes, até que o Erro Quadrático Médio fique abaixo do limite.
*************************/
double teach() {
   int i,j;
   double MSE;
   double inputVector[];
   double outputVector[];
   ArrayResize(inputVector,f2M_get_num_input(ann));
   ArrayResize(outputVector,f2M_get_num_output(ann));
   int call;
   int bufferSize = ArraySize(trainingData)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
   for (i=0;i<bufferSize;i++) {
      for (j=0;j<f2M_get_num_input(ann);j++) {
         inputVector[j] = trainingData[i][j];
      }
      outputVector[0] = trainingData[i][3];
      //f2M_train() está mostrando apenas um exemplo de cada vez aos neurônios.
      call = f2M_train(ann, inputVector, outputVector);
   }
   // Uma vez que temos de mostrar um exemplo, 
   // vamos verificar se eles são bons, verificando o Erro Quadrático Médio (MSE) dos neurônios. 
   // Se é baixo, eles aprenderam bem!
   MSE = f2M_get_MSE(ann);
   return(MSE);
}


/*************************
** compute()
** Computa um conjunto de dados e retorna o resultado computado
*************************/
double compute(double inputVector[]) {
   int j;
   int out;
   double output;
   ArrayResize(inputVector,f2M_get_num_input(ann));
   
   // Envia novos dados aos neurônio
   out = f2M_run(ann, inputVector);
   // e verifica o que eles dizem sobre isso usando f2M_get_output().
   output = f2M_get_output(ann, 0);
   debug("Computing()",MathRound(output));
   return(output);
}


/*************************
** debug()
** Dados de impressão ao console
*************************/
void debug(string a, string b) {
   Print(a+" ==> "+b);
}

A saída


Saída da rede Neural no console.

Conclusão

Também você pode ler o artigo: "Utilizando Redes Neurais Em MetaTrader", escrito por Mariusz Woloszyn, autor da Biblioteca Fann2MQL.

Levei 4 dias para entender como usar a bilbioteca FANN em MetaTrader, através de análise da pouca documentação que está disponível aqui e no google.

Espero que este exemplo seja útil para você e evitará que você perca muito tempo experimentando. Mais artigos seguirão nas próximas semanas.

Se você tiver dúvidas por favor me pergunte e eu responderei.

Traduzido do Inglês pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/en/articles/1574

Arquivos anexados |
ASNN_1_learner.mq4 (11.74 KB)
Projeto Meta COT - Novos Horizontes para Analise do Relatório da CFTC em MetaTrader 4 Projeto Meta COT - Novos Horizontes para Analise do Relatório da CFTC em MetaTrader 4

O artigo é sobre o uso das informações do relatório da CFTC (Commodity Futures Trading Commission) em MetaTrader. O artigo descreve em datalhes a proposta do projeto META COT, mostra como carregar e processar as informações necessárias. O Expert Advisor incluído no projeto nos ajudará a analisar a eficácia do conceito apresentado no artigo. Por fim, vamos tirar algumas conclusões e oferecer sugestões úteis.

Desenvolvedores, Protejam-se a Si Mesmo! Desenvolvedores, Protejam-se a Si Mesmo!

Proteção da propriedade intelectual ainda é um grande problema. Este artigo descreve os princípios básicos de proteção dos programas MQL4. Usando estes princípios, você pode garantir que os resultados dos seus desenvolvimentos não sejam roubados por um ladrão, ou pelo menos para complicar o "trabalho" dele, tanto que ele simplesmente se recusará a fazê-lo.

Verificar o Mito: O Dia de Negociação Depende de Como Foi as Operações na Sessão Asiática Verificar o Mito: O Dia de Negociação Depende de Como Foi as Operações na Sessão Asiática

Neste artigo vamos verificar a afirmação bem conhecida de que "O Dia de Negociação Depende de Como Foi as Operações na Sessão Asiática".

Pesquisa de Recorrências Estatísticas das Direções das Velas Pesquisa de Recorrências Estatísticas das Direções das Velas

É possível prever o comportamento do mercado num próximo curto intervalo de tempo, com base em tendências recorrentes das direções das velas em momentos específicos ao longo do dia? Isto é, se tal ocorrência é encontrada primeiramente. Esta questão provavelmente surge na mente de cada trader. A finalidade deste artigo é uma tentativa de prever o comportamento do mercado com base nas recorrências estatísticas das direções das velas durante intervalos de tempo específicos.