Decompondo as entradas em indicadores

Dmitriy Gizlyk | 28 dezembro, 2017


Introdução

Ao olhar para a série de negócios lucrativos de um trader bem-sucedido, você tem vontade de seguir sua estratégia? Ou, talvez, observando seu histórico de negociação, você quer saber como pode se livrar dos trades mal-sucedidos? Eu acredito que muitos de vocês responderão a pelo menos uma das perguntas positivamente. Neste artigo, quero sugerir métodos de decomposição de históricos de negociação em indicadores, além de compartilhar como escolher os indicadores que ajudarão a aumentar seu desempenho.

1. Definição do problema

Em nosso artigo anterior, falei sobre um expert advisor com base no filtro de Kalman. Durante o teste, não só mostrou lucro, mas também 2 gargalos na estratégia: saída tardia e uma série de trades desfavoráveis em fase de correção.

Assim, nosso objetivo é reduzir o número de trades mal-sucedidos nessa estratégia. Para fazer isso, mantemos os valores de uma série de indicadores, no momento da abertura da posição. Em seguida, analisamos e comparamos os valores desses indicadores com resultados dos trades. O resto é escolher os indicadores que ajudarão a melhorar os desempenhos na negociação.

Primeiro, elaboremos um plano de ação.

  1. Determinamos o período de teste. Testamos nele e salvamos o relatório.
  2. Realizamos o parsing do relatório do teste e criamos uma matriz de trades (com o resultado das operações).
  3. Determinamos a lista de indicadores a serem utilizados e o formato de salvamento de dados. Preparamos as classes para posterior uso.
  4. Preparamos formulários para apresentação de resultados.
  5. Montamos o expert advisor analítico.
  6. Executamos o EA analítico no testador de estratégias e analisamos relatórios.
  7. Adicionamos os indicadores necessários ao EA.
  8. Testamos o EA atualizado e comparamos os resultados.

2. Primeiro teste do EA analisado

No artigo mencionado acima, o EA realizou 150 transações no prazo de um mês a contar do teste. Isso não é suficiente para uma análise estatística. Para melhorar a representatividade dos resultados, aumentamos o período de teste oito vezes. Sem qualquer optimização, definimos o período para a plotagem da função autorregressiva em 3 120 barras (cerca de 3 meses), e executamos o teste.

Primeiro teste.Primeiro teste

Após os resultados do teste, obtivemos um gráfico de saldo obviamente desfavorável, onde, depois de 1-2 trades lucrativos, vemos uma série de perdas. Em geral, a percentagem de transações rentáveis ​​constituiu um pouco menos de 34%. Embora, o lucro médio exceda a perda média em 45%. Isso não é suficiente para fazer um lucro durante todo o período de teste.

Primeiro teste

O gráfico de preços mostra que, na ausência de uma tendência claramente definida (em fase de correção), o EA abre e fecha posições desfavoráveis. Nossa tarefa é reduzir esse tipo de transações e, sempre que possível, excluí-las completamente.

Gráfico de teste

Em primeiro lugar, é necessário salvar o relatório de teste para posterior processamento. No entanto, há uma nuance, isto é, por motivos de segurança, em MQL5 o trabalho com arquivos é estritamente controlado. Os arquivos para executar operações por meio dos recursos da linguagem MQL5 devem estar no arquivo área restrita [sandbox]. Portanto, o relatório deve ser salvo nele. Mas, como vamos iniciar o programa no Testador de estratégias, devemos levar em consideração que cada agente trabalha em seu área restrita [sandbox]. Consequentemente, para que, durante o teste em qualquer agente, o programa possa acessar o relatório, nós o salvamos na pasta compartilhada do terminal.

Para descobrir o caminho para a pasta compartilhada do terminal de cliente, abrimos o menu "File" no MetaEditor e selecionamos "Open Common Data Folder".

Caminho para a área restrita

Na janela aberta, vamos para a pasta "Files".

Caminho para a área restrita.

Em seguida, copiamos a linha do caminho completa para a área de transferência pressionando "Ctrl + C".

Caminho para a área restrita.

Conhecido o caminho para a área restrita, podemos salvar nosso relatório de teste. Para fazer isso, no "Strategy Tester", escolhemos a guia "Result" e, em qualquer espaço, clicamos no botão direito do mouse. No menu que aparece, selecionamos "Report" -> "HTML (Internet Explorer)".

Salvando um relatório.

Depois de realizar estas operações, será aberta uma janela de sistema para salvar o arquivo. Em primeiro lugar, na caixa de edição do nome do arquivo, colocamos o caminho de nossa área restrita e clicamos em "Salvar". Esta operação altera a pasta para salvar o arquivo.

Salvando um relatório.

Na etapa a seguir, especifique o nome sob o qual o relatório de teste será salvo, e salvamos o arquivo.

Salvando um relatório.

Depois de salvar o relatório no área restrita, avançamos para a seguinte etapa, isto é, a criação de uma matriz de transações para análise subsequente.

3. Criamos uma matriz de transações

3.1. Ideia geral sobre a análise sintática

Na seção anterior, salvamos o relatório de teste do EA. Agora, temos que gerar a partir dele uma matriz de transações para processamento. No navegador, vemos uma lista de trades, mas os programas MQL5 não podem baixar matrizes de dados diretamente de um arquivo HTML. Por isso, é necessário realizar a análise sintática do relatório.

Lista das transações no relatório.

Basicamente, um arquivo HTML é um texto separado por marcas que descrevem a formatação e design. Tendo aberto o relatório num editor de texto, você pode encontrar facilmente nele duas marcas "<table>", o que significa que todos os dados no relatório são divididos em 2 tabelas de dados. Informações sobre as transações se encontram na segunda tabela. No seu início, há informações sobre ordens e depois - informações sobre trades.

Visualização HTML do relatório.

As linhas da tabela são marcadas pelas marcas "<tr>...</tr>". Dentro das linhas, as informações são separadas em células pelas marcas "<td>...</td>".

3.2. Classe para salvar informações sobre o trade

Determinamos o formato de apresentação dos dados do relatório. Passamos agora para o formato de armazenamento de dados em nossa matriz. Como o EA a ser analisado funciona somente num único instrumento, o fato de salvar o nome do instrumento é opcional. No entanto, precisamos dele para inicializar os indicadores. No final das contas, nossa a estrutura de registro de transações terá as seguintes posições:

  • hora de abertura de posição;
  • volume de abertura de posição;
  • direção do trade;
  • volume de fechamento da posição;
  • montante da comissão;
  • valor do swap;
  • montante do lucro.

Determinamos os principais aspectos desta e desta etapa do trabalho. Comecemos a escrever o código. Primeiro, criamos a classe de transação CDeal.

class CDeal       :  public CObject
  {
private:
   datetime          OpenTime;         // Time of open position
   double            OpenedVolume;     // Volume of opened position
   ENUM_POSITION_TYPE Direct;          // Direct of opened position
   double            ClosedVolume;     // Closed volume
   double            Comission;        // Comission to position
   double            Swap;             // Swap of position
   double            Profit;           // Profit of position
   
public:
                     CDeal();
                    ~CDeal();
  };

Inicializaremos a classe ao registrar uma nova transação aberta, quando conhecidos a hora de abertura de posição, o volume e direção do trade. Portanto, transmitiremos aos parâmetros da função de inicialização seus valores e comissões (se disponíveis). Na inicialização, zeraremos os outros parâmetros. Como resultado, a função de inicialização da classe será a seguinte:

CDeal::CDeal(ENUM_POSITION_TYPE type,datetime time,double volume,double comission=0.0)  : ClosedVolume(0),
                                                                                          Swap(0),
                                                                                          Profit(0)
  {
   OpenTime      = time;
   OpenedVolume  = volume;
   Direct        = type;
   Comission     = comission;
  }

Em trabalhos futuros, teremos que verificar o estado das transações já salvas. Para fazer isso, criamos a função IsClosed, ela verificará se uma transação já está fechada na base de dados. Nela, serão comparados os volumes de abertura com os de fechamento da transação. O fato de eles serem iguais significa que a transação está fechada, e a função deve retornar true. Se a transação não estiver fechada, a função retornará false e o volume restante no mercado.

bool CDeal::IsClosed(double &opened_volume)
  {
   opened_volume=OpenedVolume-ClosedVolume;
   return (opened_volume<=0);
  }

No caso de nós precisarmos verificar apenas o estado de uma transação e não haver necessidade de saber o volume não fechado, criamos mais uma função com o mesmo nome.

bool CDeal::IsClosed(void)
  {
   double opened_volume;
   return IsClosed(opened_volume);
  }

Para fecha corretamente a transação, precisamos saber seu tipo. Criamos a função GetType. Ela retornará o valor private da variável Direct. A função é bastante curta, portanto, pode ser reescrita no corpo da classe.

ENUM_POSITION_TYPE Type(void) {  return Direct; }

Verificado o estado, as transações não fechadas devem ser fechadas. Para fazer isso, criamos a função Close. Para ela serão transmitidos os parâmetros: volume de fechamento, lucro da transação, comissão e o swap acumulado. Se o volume transmitido for superior ao volume não fechado da transação, a função retornará false. Em outros casos, os parâmetros passados ​​serão armazenados nas variáveis ​​de classe correspondentes, e a função retornará true.

bool CDeal::Close(double volume,double profit,double comission=0.0,double swap=0.0)
  {
   if((OpenedVolume-ClosedVolume)<volume)
      return false;
   ClosedVolume   += volume;
   Profit         += profit;
   Comission      += comission;
   Swap           += swap;
   return true;
  }

Mais tarde, na análise das transações, precisaremos de uma função que retorna a pedido o lucro do trade. Vamos chamar essa função GetProfit.

double CDeal::GetProfit(void)
  {
   return (Comission+Swap+Profit);
  }

Além disso, para a recepção atempada de dados sobre o estado dos indicadores, precisamos saber o tempo da transação. Para isso, vamos criar a função GetTime.

datetime          GetTime(void)  {  return OpenTime;  }

3.3. Classe de análise sintática de relatório

Depois de criar uma classe para armazenar informações sobre cada transação, prosseguimos diretamente com a análise sintática do relatório. Para fazer isso, criamos a classe CParsing. Na classe, declaramos:

  • objeto da classe CArrayObj  - para armazenar a matriz de transações;
  • objeto da classe CFileTxt - para trabalhar com o arquivo do relatório;
  • variável do tipo string - para armazenar o nome do instrumento.

Além das funções de inicialização e desinitialização, a classe terá mais duas funções:

  • ReadFile - diretamente para análise sintática;
  • GetSymbol - para retorno do nome do instrumento, mediante solicitação.

class CParsing
  {
private:
   CArrayObj        *car_Deals;     //Array of deals
   CFileTxt         *c_File;        //File to parsing
   
   string            s_Symbol;      //Symbol of deals
   
public:
                     CParsing(CArrayObj *&array);
                    ~CParsing();
                    
   bool              ReadFile(string file_name);
   string            GetSymbol(void)   {  return s_Symbol;  }
  };

O principal objetivo das funções desta classe é a criação de uma série de transações para posterior processamento. Assim, a matriz criada deve estar disponível para funcionar no programa principal. Para esta finalidade, declaramos o objeto da classe CArrayObj para armazenar a matriz de transações no programa principal, e, durante a inicialização, para classe transferimos sua referência. Como resultado, a função de inicialização será a seguinte:

CParsing::CParsing(CArrayObj *&array)  :  s_Symbol(NULL)
  {
   if(CheckPointer(array)==POINTER_INVALID)
     {
      array=new CArrayObj();
     }
   car_Deals=array;
  }

Na função de desinitialização, escrevemos a remoção do objeto da classe CFileTxt. O encerramento do arquivo é especificado na função de desinitialização da classe pai CFile; não fornecemos isso aqui.

CParsing::~CParsing()
  {
   if(CheckPointer(c_File)!=POINTER_INVALID)
      delete c_File;
  }

Avançamos diretamente para a análise sintática. Ao chamar a função de análise sintática ReadFile nos parâmetros, especificamos o nome de arquivo de relatório. A primeira coisa que fazemos na função é verificar se o parâmetro passado não está vazio. Além disso, verificamos a disponibilidade de uma matriz para armazenar informações sobre as transações. No caso de pelo menos uma condição não ser cumprida, encerramos a execução da função e retornamos a false.

bool CParsing::ReadFile(string file_name)
  {
   //---
   if(file_name==NULL || file_name=="" || CheckPointer(car_Deals)==POINTER_INVALID)
      return false;

Em seguida, inicializamos o objeto da classe CFileTxt e tentamos abrir o arquivo transferido para o parâmetro da função. Se ocorrer um erro, saímos da função com o resultado false.

   if(CheckPointer(c_File)==POINTER_INVALID)
     {
      c_File=new CFileTxt();
      if(CheckPointer(c_File)==POINTER_INVALID)
         return false;
     }
   //---
   if(c_File.Open(file_name,FILE_READ|FILE_COMMON)<=0)
      return false;

Depois de abrir o arquivo, conferimos todo seu conteúdo na variável do tipo string. Se o arquivo estiver vazio, saímos da função com resultado false.

   string html_report=NULL;
   while(!c_File.IsEnding())
      html_report+=c_File.ReadString();
   c_File.Close();
   if(html_report==NULL || html_report=="")
      return false;

Na próxima etapa, procuramos um símbolo que não ocorra no texto do relatório e possa ser utilizado como separador. Na ausência de tal símbolo, saímos da função com o resultado false.

   string delimiter  =  NULL;
   ushort separate   =  0;
   for(uchar tr=1;tr<255;tr++)
     {
      string temp =  CharToString(tr);
      if(StringFind(html_report,temp,0)>0)
         continue;
      delimiter   =  temp;
      separate    =  tr;
      break;
     }
   if(delimiter==NULL)
      return false;

Como mencionado acima, na estrutura de arquivo HTML, as tabelas são fechadas pela marca "</table>".  Substituímos essa marca pelo separador e dividimos todo o relatório em linhas de acordo com ele. Assim, dividimos a tabela desejada em uma linha separada.

   if(StringReplace(html_report,"</table>",delimiter)<=0)
      return false;
   //---
   s_Symbol=NULL;
   car_Deals.Clear();
   //---
   string html_tables[];
   int size=StringSplit(html_report,separate,html_tables);
   if(size<=1)
      return false;

Repetindo este procedimento com a marca "</tr>", dividimos a tabela em linhas.

   if(StringReplace(html_tables[size-2],"</tr>",delimiter)<=0)
      return false;
   size=StringSplit(html_tables[size-2],separate,html_tables);
   if(size<=1)
      return false;

Agora, processamos a matriz de linhas obtida num ciclo. Primeiro, ignoramos todas as linhas que contêm informações sobre ordens. Sendo assim, vamos nós orientar pela linha com o texto "Deals", que divide as ordens e transações, norelatório.

   bool found_start=false;
   double opened_volume=0;
   for(int i=0;i<size;i++)
     {
      //---
      if(!found_start)
        {
         if(StringFind(html_tables[i],"Deals",0)>=0)
            found_start=true;
         continue;
        }

Após isso, dividimos cada linha em células e convertemos as informações no formato apropriado.

      string columns[];
      int temp=StringFind(html_tables[i],"<td>",0);
      if(temp<0)
         continue;
      if(temp>0)
         html_tables[i]=StringSubstr(html_tables[i],temp);
      StringReplace(html_tables[i],"<td>","");
      StringReplace(html_tables[i],"</td>",delimiter);
      temp=StringSplit(html_tables[i],separate,columns);
      if(temp<13)
         continue;
      //---
      ENUM_POSITION_TYPE   e_direction =  (ENUM_POSITION_TYPE)(columns[3]=="buy" ? POSITION_TYPE_BUY : columns[3]=="sell" ?
 POSITION_TYPE_SELL : -1);
      if(e_direction==-1)
         continue;
      //---
      datetime             dt_time     =  StringToTime(columns[0]);
      StringReplace(columns[5]," ","");
      double               d_volume    =  StringToDouble(columns[5]);
      StringReplace(columns[8]," ","");
      double               d_comission =  StringToDouble(columns[8]);
      StringReplace(columns[9]," ","");
      double               d_swap      =  StringToDouble(columns[9]);
      StringReplace(columns[10]," ","");
      double               d_profit    =  StringToDouble(columns[10]);
      if(s_Symbol==NULL || s_Symbol=="")
        {
         s_Symbol=columns[2];
         StringTrimLeft(s_Symbol);
         StringTrimRight(s_Symbol);
        }

Na próxima etapa, verificamos se a transação é uma operação de fechamento de posição. Se o resultado for positivo, fechamos as posições em nossa base de acordo com o método FIFO.

      if(opened_volume>0 && StringFind(columns[4],"out",0)>=0)
        {
         int total=car_Deals.Total();
         double total_volume=MathMin(opened_volume,d_volume);
         for(int d=0;(d<total && e_direction!=(-1) && total_volume>0);d++)
           {
            CDeal *deal=car_Deals.At(d);
            if(CheckPointer(deal)==POINTER_INVALID)
               continue;
            //---
            if(deal.Type()==e_direction)
               continue;
            //---
            double deal_unclosed=0;
            if(deal.IsClosed(deal_unclosed))
               continue;
            double close_volume     =  MathMin(deal_unclosed,total_volume);
            double close_comission  =  d_comission/d_volume*close_volume;
            double close_swap       =  d_swap/total_volume*close_volume;
            double close_profit     =  d_profit/total_volume*close_volume;
            if(deal.Close(close_volume,close_profit,close_comission,close_swap))
              {
               opened_volume  -= close_volume;
               d_volume       -= close_volume;
               total_volume   -= close_volume;
               d_comission    -= close_comission;
               d_swap         -= close_swap;
               d_profit       -= close_profit;
              }
           }
        }

Em seguida, verificamos se a abertura de posição foi feita. Se necessário, criamos uma nova transação em nosso banco de dados.

      if(d_volume>0 && StringFind(columns[4],"in",0)>=0)
        {
         CDeal *deal = new CDeal(e_direction,dt_time,d_volume,d_comission);
         if(CheckPointer(deal)==POINTER_INVALID)
            return false;
         if(!car_Deals.Add(deal))
            return false;
         opened_volume  += d_volume;
        }
     }

Se pelo menos uma transação for salva, no final a função retornará true, caso contrário - "false".

   return (car_Deals.Total()>0);
  }

Avançamos para a próxima etapa do trabalho.

4. Preparação de classes para trabalhar com indicadores

Como dissemos anteriormente, um dos objetivos visa eliminar as transações desfavoráveis caso não exista uma tendência forte. Regularmente, levantam-se dúvidas sobre a definição da tendência, incluindo aqui, neste site (por exemplo, nos artigos [3] e [4]). Não pretendo descobrir métodos extraordinários para determinar a tendência. Eu só quero apresentar uma técnica de comparação de transações e indicadores para posterior análise e otimização consciente de sistemas de negociação. Por isso, consideramos aqui os indicadores mais difundidos que já estão disponíveis no pacote de distribuição do terminal.

4.1. Classe para inclusão do indicador ATR

Primeiro, consideraremos o indicador de tipo oscilador Average True Range. Como se sabe, durante as tendências, a volatilidade do mercado aumenta. O crescimento do valor do oscilador é um sinal disso. Quais são os valores que precisamos salvar? Como o EA analisado coloca ordens apenas na abertura da vela, sugiro manter tanto o valor do indicador na última vela fechada quanto a relação entre este valor e o anterior. O primeiro valor mostra a volatilidade atual, enquanto o segundo - a dinâmica da variação na volatilidade.

O indicador estudado tem só um buffer e é considerado típico em sua classe. Portanto, faz sentido para nós realizar uma única classe para trabalhar com esses indicadores.

A maneira de salvar os valores dos indicadores será semelhante à das transações: primeiro, criamos uma classe para armazenar os valores de indicadores para um trade, em seguida, realizamos uma classe de nível superior para trabalho imediato com o indicador de acordo com solicitações externas e armazenamento de dados na matriz.

Vamos chamar a primeira classe como CValue. Ela conterá 3 variáveis ​​private para armazenar: as informações sobre o valor do indicador (Value), a relação dos dois últimos valores do indicador (Dinamic) e o número de bilhete da ordem para a qual foram salvos os valores (Deal_Ticket). Nós precisaremos do número do bilhete para comparação subsequente dos valores dos indicadores com as ordens durante a análise. Todos os valores necessários para armazenar o valor na instância da classe serão transmitidos durante sua inicialização. Para extrair as informações desejadas, criamos umas funções GetTicket, GetValue e GetDinamic que retornarão os valores das variáveis correspondentes. Além disso, criamos uma função GetValues que retornará simultaneamente o valor do indicador e sua dinâmica.

class CValue       : public CObject
  {
private:
   double            Value;            //Indicator's value
   double            Dinamic;          //Dinamics value of indicator
   long              Deal_Ticket;      //Ticket of deal 
   
public:
                     CValue(double value, double dinamic, long ticket);
                    ~CValue(void);
   //---
   long              GetTicket(void)   {  return Deal_Ticket;  }
   double            GetValue(void)    {  return Value;        }
   double            GetDinamic(void)  {  return Dinamic;      }
   void              GetValues(double &value, double &dinamic);
  };

Em seguida, criamos uma classe de nível superior para armazenamento da matriz de dados COneBufferArray. No bloco private, ela conterá a matriz de dados armazenados e o identificador do indicador. Lembre que decidimos criar uma classe genérica para trabalhar com todos os indicadores de um buffer. Mas a chamada de diferentes indicadores acarreta o uso de um conjunto diferente de parâmetros. Portanto, na minha opinião, a opção mais fácil é inicializar o indicador no programa principal, e só então inicializar a classe e transferir para ela o identificador do indicador desejado. Para posterior identificação do indicador no relatório, introduzimos a variável s_Name.

class COneBufferArray   :  CObject
  {
private:
   CArrayObj        *IndicatorValues;     //Array of indicator's values
   
   int               i_handle;            //Handle of indicator
   string            s_Name;
   string            GetIndicatorName(int handle);
   
public:
                     COneBufferArray(int handle);
                    ~COneBufferArray();
   //---
   bool              SaveNewValues(long ticket);
   //---
   double            GetValue(long ticket);
   double            GetDinamic(long ticket);
   bool              GetValues(long ticket, double &value, double &dinamic);
   int               GetIndyHandle(void)  {  return i_handle;     }
   string            GetName(void)        {  return (s_Name!= NULL ? s_Name : "...");       }
  };

Para salvar os dados a partir de uma solicitação externa, criamos uma função SaveNewValues ​​que conterá apenas um parâmetro, isto é, o bilhete da ordem. No início da função, verificamos o estado das matrizes para armazenamento de dados e do identificador do indicador. Em caso de um erro, a função retornará false.

bool COneBufferArray::SaveNewValues(long ticket)
  {
   if(CheckPointer(IndicatorValues)==POINTER_INVALID)
      return false;
   if(i_handle==INVALID_HANDLE)
      return false;

Depois disso, obtemos os dados do indicador. Se não for possível carregar os valores do indicador, a função retorna false.

   double ind_buffer[];
   if(CopyBuffer(i_handle,0,1,2,ind_buffer)<2)
      return false;

Na seguinte etapa, criamos a instância da classe CValue e transferimos para ele os valores requeridos. Em caso de erro, a função retorna false.

   CValue *object=new CValue(ind_buffer[1], (ind_buffer[0]!=0 ? ind_buffer[1]/ind_buffer[0] : 1), ticket);
   if(CheckPointer(object)==POINTER_INVALID)
      return false;

Se a classe ainda não souber o nome do indicador, devemos obtê-lo do gráfico chamando a função GetIndicatorName (o código da função é mostrado o anexo).

   if(s_Name==NULL)
      s_Name=GetIndicatorName(i_handle);

Finalmente, adicionamos à matriz a instância da classe de dados recém-criada e, tendo retornado o resultado da operação, saímos da função.

   return IndicatorValues.Add(object);
  }

Para retorno de dados da matriz a pedido, criamos umas funções GetValue, GetDinamic e GetValues que retornarão os valores requeridos pelo número do bilhete da ordem. 

O código completo das classes pode ser encontrado no apêndice.

Eu apliquei esta classe para coletar dados pelos indicadores CCI, Volumes, Force, Chaikin oscillator e desvio padrão.

4.2. Classe para inclusão do indicador MACD

Vamos adicionar à nossa coleção um indicador padrão mais, nomeadamente, o MACD. Como é bem conhecido, ele é utilizado para determinar a força e direção da tendência.

Em contraste com os indicadores considerados anteriormente, MACD tem dois buffers de indicador (Main e Signal). Por isso, vamos salvar as informações de duas linhas. Usando o algoritmo mostrado para os indicadores acima, o código da classe para armazenamento de dados toma a seguinte forma:

class CMACDValue      : public CObject
  {
private:
   double            Main_Value;        //Main line value
   double            Main_Dinamic;      //Dinamics value of main lime
   double            Signal_Value;      //Signal line value
   double            Signal_Dinamic;    //Dinamics value of signal lime
   long              Deal_Ticket;       //Ticket of deal 
   
public:
                     CMACDValue(double main_value, double main_dinamic, double signal_value, double signal_dinamic, long ticket);
                    ~CMACDValue(void);
   //---
   long              GetTicket(void)         {  return Deal_Ticket;     }
   double            GetMainValue(void)      {  return Main_Value;      }
   double            GetMainDinamic(void)    {  return Main_Dinamic;    }
   double            GetSignalValue(void)    {  return Signal_Value;    }
   double            GetSignalDinamic(void)  {  return Signal_Dinamic;  }
   void              GetValues(double &main_value, double &main_dinamic, double &signal_value, double &signal_dinamic);
  };

Alterações correspondentes tiveram lugar na classe para trabalho com a matriz de dados. Ao contrário da classe universal, conforme descrito na seção 4.1, esta classe funcionará com um determinado indicador, portanto, na inicialização da classe, serão transferidos para ele os parâmetros necessários para sua inicialização, em vez de passar o identificador do indicador. A inicialização do indicador será realizada diretamente na classe.

class CMACD
  {
private:
   CArrayObj        *IndicatorValues;     //Array of indicator's values
   
   int               i_handle;            //Handle of indicator
   
public:
                     CMACD(string symbol, ENUM_TIMEFRAMES timeframe, uint fast_ema, uint slow_ema, uint signal, ENUM_APPLIED_PRICE applied_price);
                    ~CMACD();
   //---
   bool              SaveNewValues(long ticket);
   //---
   double            GetMainValue(long ticket);
   double            GetMainDinamic(long ticket);
   double            GetSignalValue(long ticket);
   double            GetSignalDinamic(long ticket);
   bool              GetValues(long ticket, double &main_value, double &main_dinamic, double &signal_value, double &signal_dinamic);                
  };

Toda a lógica das funções permanece a mesma, as mudanças afetaram apenas o número de buffers de indicador e variáveis ​​armazenadas.

bool CMACD::SaveNewValues(long ticket)
  {
   if(CheckPointer(IndicatorValues)==POINTER_INVALID)
      return false;
   if(i_handle==INVALID_HANDLE)
      return false;
   double main[], signal[];
   if(!CopyBuffer(i_handle,0,1,2,main)<2 || !CopyBuffer(i_handle,1,1,2,signal)<2)
      return false;
   CMACDValue *object=new CMACDValue(main[1], (main[0]!=0 ? main[1]/main[0] : 1), signal[1], (signal[0]!=0 ? signal[1]/signal[0] : 1), ticket);
   if(CheckPointer(object)==POINTER_INVALID)
      return false;
   return IndicatorValues.Add(object);
  }

Tal lógica de escala se aplica a qualquer quantidade de buffers de indicador. Se você deseja salvar apenas os buffers de indicador selecionados, basta descrever isso na função SaveNewValues ​​da classe correspondente. Porém, não recomendo fazer isso nesta fase, porque ainda não sabemos se existe uma relação entre as transações rentáveis e os valores de buffers de indicadores específicos, e se houver, não conhecemos qual é o seu grau.

Para consolidar o material, por assim dizer, vou dar outro exemplo de armazenamento de dados de indicador con 3 buffers de dados.

4.3. Classe para inclusão do indicador ADX

Indicador ADX é amplamente utilizado para determinar a força e a direção da tendência. Ele se ajusta bem ao nosso problema, e vale a pena adicioná-lo ao nosso "mealheiro."

Existem 3 buffers de indicador neste indicador e, de acordo com o método de escala sugerido acima, aumentamos o número de variáveis ​​salvas. Assim, a classe de armazenamento de dados terá a seguinte aparência:

class CADXValue      : public CObject
  {
private:
   double            ADX_Value;        //ADX value
   double            ADX_Dinamic;      //Dinamics value of ADX
   double            PDI_Value;        //+DI value
   double            PDI_Dinamic;      //Dinamics value of +DI
   double            NDI_Value;        //-DIvalue
   double            NDI_Dinamic;      //Dinamics value of -DI
   long              Deal_Ticket;      //Ticket of deal 
   
public:
                     CADXValue(double adx_value, double adx_dinamic, double pdi_value, double pdi_dinamic, double ndi_value, double ndi_dinamic, long ticket);
                    ~CADXValue(void);
   //---
   long              GetTicket(void)         {  return Deal_Ticket;     }
   double            GetADXValue(void)       {  return ADX_Value;       }
   double            GetADXDinamic(void)     {  return ADX_Dinamic;     }
   double            GetPDIValue(void)       {  return PDI_Value;       }
   double            GetPDIDinamic(void)     {  return PDI_Dinamic;     }
   double            GetNDIValue(void)       {  return NDI_Value;       }
   double            GetNDIDinamic(void)     {  return NDI_Dinamic;     }
   void              GetValues(double &adx_value, double &adx_dinamic, double &pdi_value, double &pdi_dinamic, double &ndi_value, double &ndi_dinamic);
  };

 Um aumento nos dados a serem armazenados implicará mudanças na classe de trabalho com a matriz.

class CADX
  {
private:
   CArrayObj        *IndicatorValues;     //Array of indicator's values
   
   int               i_handle;            //Handle of indicator
   
public:
                     CADX(string symbol, ENUM_TIMEFRAMES timeframe, uint period);
                    ~CADX();
   //---
   bool              SaveNewValues(long ticket);
   //---
   double            GetADXValue(long ticket);
   double            GetADXDinamic(long ticket);
   double            GetPDIValue(long ticket);
   double            GetPDIDinamic(long ticket);
   double            GetNDIValue(long ticket);
   double            GetNDIDinamic(long ticket);
   bool              GetValues(long ticket,double &adx_value,double &adx_dinamic,double &pdi_value,double &pdi_dinamic,double &ndi_value,double &ndi_dinamic);
  };
bool CADX::SaveNewValues(long ticket)
  {
   if(CheckPointer(IndicatorValues)==POINTER_INVALID)
      return false;
   if(i_handle==INVALID_HANDLE)
      return false;
   double adx[], pdi[], ndi[];
   if(!CopyBuffer(i_handle,0,1,2,adx)<2 || !CopyBuffer(i_handle,1,1,2,pdi)<2 || !CopyBuffer(i_handle,1,1,2,ndi)<2)
      return false;
   CADXValue *object=new CADXValue(adx[1], (adx[0]!=0 ? adx[1]/adx[0] : 1), pdi[1], (pdi[0]!=0 ? pdi[1]/pdi[0] : 1), ndi[1], (ndi[0]!=0 ? ndi[1]/ndi[0] : 1), ticket);
   if(CheckPointer(object)==POINTER_INVALID)
      return false;
   return IndicatorValues.Add(object);
  }

Agora, acho que já todos entenderam o princípio de construção de classes para trabalhar com indicadores. Portanto, não vamos escrever em detalhes sobre o código para os seguintes indicadores, a fim de poupar espaço. Do mesmo modo, para a análise, eu adicionei os indicadores BW IFM e Alligator ao "mealheiro". Quem quiser pode ler o código completo das classes no apêndice.

5. Preparamos os formulários dos relatórios para exibir os resultados

Após obter informações dos indicadores de nosso interesse no momento da transação, é hora de pensar sobre a análise dos resultados. O mais ilustrativo, na minha opinião, será plotar os gráficos de dependência entre o lucro das transações e os respectivos valores dos indicadores. Sugiro construir os gráficos de acordo com a técnica proposta por Victor no artigo [2].

Só quero mencionar que como eu estou otimizando uma estratégia de negociação, vou procurar a dependência entre o lucro e os valores dos indicadores. Se o leitor estiver tentando copiar certo tipo de negociação, ele precisará de olhar para a relação entre o número de transações e os valores dos indicadores.

Primeiro, criamos as classes que prepararão as informações para cada indicador.

5.1. Classe universal de indicadores de um buffer

Primeiro, criamos a classe para trabalhar com indicadores de um buffer. Que tipo de informação podemos analisar? Vamos lembrar que temos salvo o valor do buffer de indicador e suas a dinâmica de mudanças. Portanto, podemos analisar:

  • dependência entre o lucro das operações realizadas e os valores do indicador na posição de abertura,
  • impacto da tendência da linha do indicador sobre o lucro,
  • bem como o efeito combinado do valor do indicador e da sua dinâmica sobre o resultado das operações realizadas.

Para desenho de gráficos, criamos a classe CStaticOneBuffer. Esta classe conterá a referência para a matriz de dados armazenados DataArray, matriz de valores Value com etapa definida d_Step, bem como dois matrizes de lucro total separadas para posições longas e curtas. Note que matrizes para calcular o lucro total serão bidimensionais. O tamanho da primeira dimensão corresponderá ao tamanho da matriz Value. A segunda dimensão conterá três elementos: o primeiro será para a dinâmica descendente do indicador, a segunda será para o movimento horizontal do indicador e a terceira, para o movimento ascendente.

Na inicialização da classe, para os parâmetros vamos transferir, por um lado, a referência para a matriz de dados e, por outro, o tamanho da etapa para os valores do indicador.

class CStaticOneBuffer  :  CObject
  {
private:
   COneBufferArray  *DataArray;
   
   double            d_Step;                    //Step in values Array
   double            Value[];                   //Array of values
   double            Long_Profit[][3];          //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Short_Profit[][3];         //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   
   bool              AdValues(double value, double dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticOneBuffer(COneBufferArray *data, double step);
                    ~CStaticOneBuffer();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

Na função de inicialização, salvamos os valores enviados e zeramos as matrizes utilizadas.

CStaticOneBuffer::CStaticOneBuffer(COneBufferArray *data,double step)
  {
   DataArray   =  data;
   d_Step      =  step;
   ArrayFree(Value);
   ArrayFree(Long_Profit);
   ArrayFree(Short_Profit);
  }

Para recolher informações estatísticas, criamos uma função Ad para a qual transmitiremos informações sobre a transação. Dentro da função estarão os parâmetros do indicador correspondentes, e os dados serão guardados nos elementos da matriz requeridos.

bool CStaticOneBuffer::Ad(long ticket,double profit,ENUM_POSITION_TYPE type)
  {
   if(CheckPointer(DataArray)==POINTER_INVALID)
      return false;

   double value, dinamic;
   if(!DataArray.GetValues(ticket,value,dinamic))
      return false;
   value = NormalizeDouble(value/d_Step,0)*d_Step;
   return AdValues(value,dinamic,profit,type);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CStaticOneBuffer::AdValues(double value,double dinamic,double profit,ENUM_POSITION_TYPE type)
  {
   int index=GetIndex(value);
   if(index<0)
      return false;
   
   switch(type)
     {
      case POSITION_TYPE_BUY:
        if(dinamic<1)
           Long_Profit[index,0]+=profit;
        else
           if(dinamic==1)
              Long_Profit[index,1]+=profit;
           else
              Long_Profit[index,2]+=profit;
        break;
      case POSITION_TYPE_SELL:
        if(dinamic<1)
           Short_Profit[index,0]+=profit;
        else
           if(dinamic==1)
              Short_Profit[index,1]+=profit;
           else
              Short_Profit[index,2]+=profit;
        break;
     }
   return true;
  }

Para visualização de gráficos, criamos umas funções HTML_header e HTML_body que gerarão trechos do código do cabeçalho e do corpo da página HTML. Princípios de construção de código de páginas HTML estão detalhados no artigo [ 2 ], não vamos nos concentrar nisto. O código completo da função é mostrado no anexo.

5.2. Classe para exibição de dados do indicador Bill Williams MFI

A seguir, consideraremos o indicador Bill Williams MFI. Seu método de exibição no gráfico lembra os indicadores de um buffer, mas existe uma diferença, isto é, BW IMF tem um buffer de paleta de cores que também tem um valor. Ao mesmo tempo, ao contrário de indicadores de dois buffers, não vamos estar interessado na dinâmica da mudança no buffer de cor. Portanto, aos gráficos acima sugeridos de indicadores de um buffer são adicionados gráficos de dependência entre o lucro e a cor do indicador, bem como gráficos de impacto complexo de valores e dinâmicas do indicador sujeito à cor do indicador atual.

Para a coleta de dados estatísticos e criação de gráficos analíticos, criamos a classe CStaticBWMFI. Estrutura da classe é semelhante à considerada acima. As mudanças tocaram nas matrizes de contagem de lucro, elas agora têm três dimensões. A terceira dimensão recebeu os 4 elementos de acordo com o número de cores utilizadas.

class CStaticBWMFI  :  CObject
  {
private:
   CBWMFI           *DataArray;
   
   double            d_Step;                       //Step in values Array
   double            Value[];                      //Array of values
   double            Long_Profit[][3][4];          //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Short_Profit[][3][4];         //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   
   bool              AdValues(double value, double _color, double dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticBWMFI(CBWMFI *data, double step);
                    ~CStaticBWMFI();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

O código de classe completo pode ser encontrado no anexo.

5.3. Classe para exibição dos dados do indicador MACD

Está na hora de considerarmos o indicador MACD. Como é bem conhecido, ele tem dois buffers: o histograma e a linha de sinal. De acordo com as regras de interpretação deste indicador, são importantes: o valor e direção do histograma, bem como a posição da linha de sinal (acima ou abaixo do histograma). Para a análise abrangente, criaremos uma série de gráficos.

  • Dependência da rentabilidade das transações sobre os valores do histograma e sua direção (separada o complexamente).
  • Dependência da rentabilidade de transações sobre os valores valores de linha de sinal e sua direção.
  • Dependência entre o lucro e a posição da linha de sinal em relação ao histograma.
  • Dependência do lucro sobre o efeito conjunto dos valores do histograma, direção e posição da linha de sinal em relação ao histograma.
Para análise dos dados, criamos a classe CStaticMACD. Na construção da classe, serão aplicados os mesmos princípios utilizados na construção de classes estatísticas anteriores. Ele terá uma matriz tridimensional de estatísticas de lucro segundo os valores de histograma, mas, ao contrário da classe anterior, a terceira dimensão conterá 3 elementos de acordo com a posição da linha de sinal em relação ao histograma (inferior, ao nível e superior). Além disso, adicionamos uma outra matriz bidimensional para contar os lucros segundo os valores da linha de sinal.

class CStaticMACD  :  CObject
  {
private:
   CMACD            *DataArray;
   
   double            d_Step;                       //Step in values Array
   double            Value[];                      //Array of values
   double            SignalValue[];                //Array of values
   double            Long_Profit[][3][3];          //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Short_Profit[][3][3];         //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Signal_Long_Profit[][3];      //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Signal_Short_Profit[][3];     //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   
   bool              AdValues(double main_value, double main_dinamic, double signal_value, double signal_dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetIndex(double value);
   int               GetSignalIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticMACD(CMACD *data, double step);
                    ~CStaticMACD();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

Como você pode ver, a estrutura da classe, o nome e a finalidade das funções permanecem as mesmas. Só o conteúdo das funções foi alterado. Ele pode ser encontrado em anexo.

5.4. Classe para exibição dos dados do indicador ADX

Agora, consideraremos a classe CStaticADX. Ela coletará estatísticas sobre os valores do indicador ADX. Regras de interpretação dos sinais do indicador: a linha +DI mostra a força do movimento positivo, -DI indica a força do movimento negativo, enquanto ADX apresenta a forma média do movimento. Com base nestas regras, serão plotados os gráficos de dependências:

  • dependência do lucro sobre valor da +DI, sua direção e posição em relação ao ADX;
  • dependência do lucro sobre valor da -DI, sua direção e posição em relação ao ADX.

Criada a classe para coleta de estatísticas, decidi recolher mais alguns dados. Como resultado, tive de armazenar informações sobre:

  • valor do indicador;
  • direção das linhas;
  • posição em relação à linha de movimento oposto;
  • direção da linha de movimento oposto;
  • posição em relação à linha do ADX;
  • direção da linha do ADX.
Devido à fragmentação das informações e abordagem usada nas classes anteriores, precisei de matrizes de seis dimensões. Mas, matrizes desse tamanho não são suportadas em linguagem MQL. Para resolver este problema, foi criada uma classe auxiliar CProfitData em que serão armazenadas todas as informações necessárias.

class CProfitData
  {
   public:
   double         Value;
   double         LongProfit[3]/*UppositePosition*/[3]/*Upposite Direct*/[3]/*ADX position*/[3]/*ADX direct*/;
   double         ShortProfit[3]/*UppositePosition*/[3]/*Upposite Direct*/[3]/*ADX position*/[3]/*ADX direct*/;
   
                  CProfitData(void) 
                  {  ArrayInitialize(LongProfit,0); ArrayInitialize(ShortProfit,0);  }
                 ~CProfitData(void) {};
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CStaticADX  :  CObject
  {
private:
   CADX             *DataArray;
   
   double            d_Step;           //Step in values Array
   CProfitData      *PDI[][3];         //Array of values +DI
   CProfitData      *NDI[][3];         //Array of values -DI
   
   bool              AdValues(double adx_value, double adx_dinamic, double pdi_value, double pdi_dinamic, double ndi_value, double ndi_dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetPDIIndex(double value);
   int               GetNDIIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticADX(CADX *data, double step);
                    ~CStaticADX();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

Em outros aspectos, foram preservados abordagens e princípios de construção a partir de classes anteriores. O código de classe completo pode ser encontrado no anexo.

5.5. Classe para exibição dos dados do indicador Alligator

No final deste bloco, vamos criar uma classe para a coleta de estatísticas do indicador Alligator. Sinais deste indicador são baseados em três médias móveis de diferentes períodos. Por isso, ao interpretar os sinais do indicador, certos valores de linhas não têm nenhuma importância para nós. O que é muito mais importante é a direção e posição das linhas.

Para indicar com exatidão os sinais do indicador, introduzimos a definição da tendência de acordo com a posição das linhas. Se a linha LIPS estiver acima da TEETH, e, ao mesmo tempo, a TEETH se encontrar acima da JAW, teremos uma tendência BUY. Se a LIPS estiver abaixo da TEETH, e, ao mesmo tempo, a TEETH se encontrar abaixo da JAW, estaremos perante uma tendência SELL. Em caso de ausência de uma ordem estrita de linhas, consideramos a tendência como indefinida ou FLAT.

Respectivamente, gráficos de dependência serão construídos a partir de sinais de direção de tendência e dinâmica das linhas do indicador.

Após os dados de entrada especificados acima, criamos a classe CStaticAlligator. Princípios de construção da classe são herdados das classes anteriores.

class CStaticAlligator  :  CObject
  {
private:
   CAlligator             *DataArray;
   
   double            Long_Profit[3]/*Signal*/[3]/*JAW direct*/[3]/*TEETH direct*/[3]/*LIPS direct*/;  //Array of long deals profit
   double            Short_Profit[3]/*Signal*/[3]/*JAW direct*/[3]/*TEETH direct*/[3]/*LIPS direct*/; //Array of short feals profit
   
   bool              AdValues(double jaw_value, double jaw_dinamic, double teeth_value, double teeth_dinamic, double lips_value, double lips_dinamic, double profit, ENUM_POSITION_TYPE type);
   
public:
                     CStaticAlligator(CAlligator *data);
                    ~CStaticAlligator();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

O código completo da classe pode ser encontrado no anexo.

6 Construímos um EA para a coleta e análise de informações

Agora, quando todo o trabalho preparatório está concluído, criamos um EA que será iniciado imediatamente no testador de estratégias para coleta de informações e saída de dados analíticos. Primeiro de tudo, nos parâmetros de entrada do EA, especificamos o nome do arquivo de relatório de teste para análise, o timeframe utilizado e todos os parâmetros necessários dos indicadores utilizados.

input string            FileName          =  "Kalman_test.html"   ;
input ENUM_TIMEFRAMES   Timefarame        =  PERIOD_CURRENT       ;
input string            s1                =  "ADX"                ;  //---
input uint              ADX_Period        =  14                   ;
input string            s2                =  "Alligator"          ;  //---
input uint              JAW_Period        =  13                   ;
input uint              JAW_Shift         =  8                    ;
input uint              TEETH_Period      =  8                    ;
input uint              TEETH_Shift       =  5                    ;
input uint              LIPS_Period       =  5                    ;
input uint              LIPS_Shift        =  3                    ;
input ENUM_MA_METHOD    Alligator_Method  =  MODE_SMMA            ;
input ENUM_APPLIED_PRICE Alligator_Price  =  PRICE_MEDIAN         ;
input string            s3                =  "ATR"                ;  //---
input uint              ATR_Period        =  14                   ;
input string            s4                =  "BW MFI"             ;  //---
input ENUM_APPLIED_VOLUME BWMFI_Volume    =  VOLUME_TICK          ;
input string            s5                =  "CCI"                ;  //---
input uint              CCI_Period        =  14                   ;
input ENUM_APPLIED_PRICE CCI_Price        =  PRICE_TYPICAL        ;
input string            s6                =  "Chaikin"            ;  //---
input uint              Ch_Fast_Period    =  3                    ;
input uint              Ch_Slow_Period    =  14                   ;
input ENUM_MA_METHOD    Ch_Method         =  MODE_EMA             ;
input ENUM_APPLIED_VOLUME Ch_Volume       =  VOLUME_TICK          ;
input string            s7                =  "Force Index"        ;  //---
input uint              Force_Period      =  14                   ;
input ENUM_MA_METHOD    Force_Method      =  MODE_SMA             ;
input ENUM_APPLIED_VOLUME Force_Volume    =  VOLUME_TICK          ;
input string            s8                =  "MACD"               ;  //---
input uint              MACD_Fast         =  12                   ;
input uint              MACD_Slow         =  26                   ;
input uint              MACD_Signal       =  9                    ;
input ENUM_APPLIED_PRICE MACD_Price       =  PRICE_CLOSE          ;
input string            s9                =  "Standart Deviation" ;  //---
input uint              StdDev_Period     =  14                   ;
input uint              StdDev_Shift      =  0                    ;
input ENUM_MA_METHOD    StdDev_Method     =  MODE_SMA             ;
input ENUM_APPLIED_PRICE StdDev_Price     =  PRICE_CLOSE          ;
input string            s10               =  "Volumes"            ;  //---
input ENUM_APPLIED_VOLUME Applied_Volume  =  VOLUME_TICK          ;

Logo depois, declaramos as instâncias de todas as classes descritas acima.

CArrayObj         *Deals;
CADX              *ADX;
CAlligator        *Alligator;
COneBufferArray   *ATR;
CBWMFI            *BWMFI;
COneBufferArray   *CCI;
COneBufferArray   *Chaikin;
COneBufferArray   *Force;
CMACD             *MACD;
COneBufferArray   *StdDev;
COneBufferArray   *Volume;
CStaticOneBuffer  *IndicatorsStatic[];
CStaticBWMFI      *BWMFI_Stat;
CStaticMACD       *MACD_Stat;
CStaticADX        *ADX_Stat;
CStaticAlligator  *Alligator_Stat;

6.1. Função de inicialização do EA

Como nosso EA é projetado para analisar dados no testador de estratégias, primeiro, verificamos o ambiente em que ele é executado. Se a inicialização ocorrer fora do testador, devemos interromper a inicialização.

int OnInit()
  {
//---
   if(!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION))
      return INIT_FAILED;

Em seguida, realizamos a análise sintética de dados a partir do arquivo de relatório de teste. Após a leitura de dados a partir do relatório, a instância da classe de análise sintático deixa de ser necessária, portanto, excluimo-la da memória.

   CParsing *Parsing =  new CParsing(Deals);
   if(CheckPointer(Parsing)==POINTER_INVALID)
      return INIT_FAILED;
   if(!Parsing.ReadFile(FileName) || CheckPointer(Deals)==POINTER_INVALID || Deals.Total()<=0)
     {
      delete Parsing;
      return INIT_FAILED;
     }
   delete Parsing;

Depois disso, inicializamos as classes de indicador.

//---
   ADX =  new CADX(_Symbol,Timefarame,ADX_Period);
   if(CheckPointer(ADX)==POINTER_INVALID)
      return INIT_FAILED;
//---
   Alligator =  new CAlligator(_Symbol,Timefarame,JAW_Period,JAW_Shift,TEETH_Period,TEETH_Shift,LIPS_Period,LIPS_Shift,Alligator_Method,Alligator_Price);
   if(CheckPointer(Alligator)==POINTER_INVALID)
      return INIT_FAILED;
//---
   int handle=iATR(_Symbol,Timefarame,ATR_Period);
   if(handle>0)
     {
      ATR      =  new COneBufferArray(handle);
      if(CheckPointer(ATR)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   BWMFI    =  new CBWMFI(_Symbol,Timefarame,BWMFI_Volume);
   if(CheckPointer(BWMFI)==POINTER_INVALID)
      return INIT_FAILED;
//---
   handle=iCCI(_Symbol,Timefarame,CCI_Period,CCI_Price);
   if(handle>0)
     {
      CCI      =  new COneBufferArray(handle);
      if(CheckPointer(CCI)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   handle=iChaikin(_Symbol,Timefarame,Ch_Fast_Period,Ch_Slow_Period,Ch_Method,Ch_Volume);
   if(handle>0)
     {
      Chaikin  =  new COneBufferArray(handle);
      if(CheckPointer(Chaikin)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   handle=iForce(_Symbol,Timefarame,Force_Period,Force_Method,Force_Volume);
   if(handle>0)
     {
      Force    =  new COneBufferArray(handle);
      if(CheckPointer(Force)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   MACD     =  new CMACD(_Symbol,Timefarame,MACD_Fast,MACD_Slow,MACD_Signal,MACD_Price);
   if(CheckPointer(MACD)==POINTER_INVALID)
      return INIT_FAILED;
//---
   handle=iStdDev(_Symbol,Timefarame,StdDev_Period,StdDev_Shift,StdDev_Method,StdDev_Price);
   if(handle>0)
     {
      StdDev   =  new COneBufferArray(handle);
      if(CheckPointer(StdDev)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   handle=iVolumes(_Symbol,Timefarame,Applied_Volume);
   if(handle>0)
     {
      Volume   =  new COneBufferArray(handle);
      if(CheckPointer(Volume)==POINTER_INVALID)
         return INIT_FAILED;
     }

No final da função OnInit, definimos o contador de ordens como 0 e saímos da função.

   cur_ticket   =  0;
//---
   return(INIT_SUCCEEDED);
  }

6.2. Coleta de dados estatísticos

A coleta de dados sobre o estado dos indicadores será realizada na função OnTick. No início da função, verificamos se estão coletadas as informações sobre todas as ordens. Se for assim, saímos da função.

void OnTick()
  {
   if(cur_ticket>=Deals.Total())
      return;

Na próxima etapa, o tempo de execução da transação analisada é comparado com o tempo do tick processado. Se o tempo da transação ainda não tiver chegado, saímos da função.

   CDeal *object  =  Deals.At(cur_ticket);
   if(object.GetTime()>TimeCurrent())
      return;

Se já tivermos passado os anteriores exames, verificamos o estado das instâncias das classes de indicador e salvamos as informações necessárias chamando a função SaveNewValues ​​para cada indicador da classe.

   if(CheckPointer(ADX)!=POINTER_INVALID)
      ADX.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Alligator)!=POINTER_INVALID)
      Alligator.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(ATR)!=POINTER_INVALID)
      ATR.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(BWMFI)!=POINTER_INVALID)
      BWMFI.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(CCI)!=POINTER_INVALID)
      CCI.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Chaikin)!=POINTER_INVALID)
      Chaikin.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Force)!=POINTER_INVALID)
      Force.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(MACD)!=POINTER_INVALID)
      MACD.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(StdDev)!=POINTER_INVALID)
      StdDev.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Volume)!=POINTER_INVALID)
      Volume.SaveNewValues(cur_ticket);

No final da função, incrementamos o contador de ordens processadas e saímos da função.

   cur_ticket++;
   return;
  }

6.3. Exibição de gráficos para análise

Realizamos a análise de dados e exibição de relatório na função OnTester. Ao iniciar a função, verificamos o número de transações para análise.

double OnTester()
  {
   double ret=0.0;
   int total=Deals.Total();

Se for necessário realizar a análise, inicializamos as classes estatísticas.

Para facilitar o processamento posterior, reunimos as classes estatísticas de indicadores de um buffer na matriz. Por isso, em paralelo com inicialização, calculamos os indicadores de um buffer utilizados.

   int total_indy=0;
   if(total>0)
     {
      if(CheckPointer(ADX)!=POINTER_INVALID)
         ADX_Stat=new CStaticADX(ADX,1);
      //---
      if(CheckPointer(Alligator)!=POINTER_INVALID)
         Alligator_Stat=new CStaticAlligator(Alligator);
      //---
      if(CheckPointer(ATR)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(ATR,_Point*10);
         if(CheckPointer(indy)!=POINTER_INVALID)
           {
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
           }
        }
      //---
      if(CheckPointer(BWMFI)!=POINTER_INVALID)
         BWMFI_Stat=new CStaticBWMFI(BWMFI,_Point*100);
      //---
      if(CheckPointer(CCI)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(CCI,10);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(Chaikin)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(Chaikin,100);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(Force)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(Force,0.1);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(MACD)!=POINTER_INVALID)
         MACD_Stat=new CStaticMACD(MACD,_Point*10);
      //---
      if(CheckPointer(StdDev)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(StdDev,_Point*10);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(Volume)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(Volume,100);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
     }

Em seguida, comparamos os dados dos indicadores com as transações correspondentes e agrupamos as informações de acordo com as direções requeridas para a saída de relatórios gráficos. Para este fim, em cada classe estatística, chamamos a função Ad passando para seus parâmetros as informações sobre a transação.

   for(int i=0;i<total;i++)
     {
      CDeal               *deal     =  Deals.At(i);
      ENUM_POSITION_TYPE   type     =  deal.Type();
      double               d_profit =  deal.GetProfit();
      
      for(int ind=0;ind<total_indy;ind++)
         IndicatorsStatic[ind].Ad(i,d_profit,type);
      if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID)
         BWMFI_Stat.Ad(i,d_profit,type);
      if(CheckPointer(MACD_Stat)!=POINTER_INVALID)
         MACD_Stat.Ad(i,d_profit,type);
      if(CheckPointer(ADX_Stat)!=POINTER_INVALID)
         ADX_Stat.Ad(i,d_profit,type);
      if(CheckPointer(Alligator_Stat)!=POINTER_INVALID)
         Alligator_Stat.Ad(i,d_profit,type);
     }

Depois do agrupamento de dados, criamos o arquivo de relatório report.html e guardamo-lo na pasta compartilhada do terminal.

   if(total_indy>0 || CheckPointer(BWMFI_Stat)!=POINTER_INVALID || CheckPointer(MACD_Stat)!=POINTER_INVALID
      || CheckPointer(ADX_Stat)!=POINTER_INVALID || CheckPointer(Alligator_Stat)!=POINTER_INVALID )
     {
      int handle=FileOpen("Report.html",FILE_WRITE|FILE_TXT|FILE_COMMON);
      if(handle<0)
         return ret;

No início do arquivo, escrevemos o cabeçalho do nosso relatório HTML.

      FileWrite(handle,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">");
      FileWrite(handle,"<html> <head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
      FileWrite(handle,"<title>Deals to Indicators</title> <!-- - -->");
      FileWrite(handle,"<script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js\" type=\"text/javascript\"></script>");
      FileWrite(handle,"<script src=\"https://code.highcharts.com/highcharts.js\" type=\"text/javascript\"></script>");
      FileWrite(handle,"<!-- - --> <script type=\"text/javascript\">$(document).ready(function(){");

Em seguida, chamada a função HTML_header de todas as classes estatísticas, inserimos os dados em nosso arquivo para plotagem de gráficos.

      for(int ind=0;ind<total_indy;ind++)
         FileWrite(handle,IndicatorsStatic[ind].HTML_header());
      if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID)
         FileWrite(handle,BWMFI_Stat.HTML_header());
      if(CheckPointer(MACD_Stat)!=POINTER_INVALID)
         FileWrite(handle,MACD_Stat.HTML_header());
      if(CheckPointer(ADX_Stat)!=POINTER_INVALID)
         FileWrite(handle,ADX_Stat.HTML_header());
      if(CheckPointer(Alligator_Stat)!=POINTER_INVALID)
         FileWrite(handle,Alligator_Stat.HTML_header());

Depois disso, chamando alternadamente a função HTML_body de cada classe estatística, criamos um modelo para a saída do relatório. Atenção: chamando esta função, finalizamos o trabalho com a classe estatística, e devemos removê-la para limpeza da memória.

      FileWrite(handle,"});</script> <!-- - --> </head> <body>");
      for(int ind=0;ind<total_indy;ind++)
        {
         FileWrite(handle,IndicatorsStatic[ind].HTML_body());
         delete IndicatorsStatic[ind];
        }
      if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,BWMFI_Stat.HTML_body());
         delete BWMFI_Stat;
        }
      if(CheckPointer(MACD_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,MACD_Stat.HTML_body());
         delete MACD_Stat;
        }
      if(CheckPointer(ADX_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,ADX_Stat.HTML_body());
         delete ADX_Stat;
        }
      if(CheckPointer(Alligator_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,Alligator_Stat.HTML_body());
         delete Alligator_Stat;
        }

No final, terminamos de por as marcas de fim, fechamos o arquivo, limpamos as matrizes e saímos da função.

      FileWrite(handle,"</body> </html>");
      FileFlush(handle);
      FileClose(handle);
     }
//---
   ArrayFree(IndicatorsStatic);
//---
   return(ret);
  }

Não esqueçamos remover as classes restantes na função OnDeinit.

7. Análise de informações

Nosso trabalho está chegando a sua conclusão. É hora de ver nossos resultados. Para fazer isso, voltamos para o testador de estratégias, repetimos todas as configurações que usamos no teste do EA estudado - na segunda seção deste artigo - e executamos nosso EA analítico recém-criado.

Após a conclusão do teste, abrimos a pasta compartilhada do terminal e procuramos lá o arquivo report.html. Abrimo-la no navegador. Agora, vou fornecer exemplos de seu relatório.

7.1. ATR

Relatórios do ATR

Durante a análise de gráficos de dependência entre o lucro e o indicador ATR, não vejo algumas áreas potencialmente lucrativas, e, portanto, a filtragem de transações está fora de questão.

7.2. CCI.

Relatórios do CCI

Os gráficos de dependência entre o lucro e o indicador CCI permitem tirar certo lucro em transações BUY quando o valor do indicador é superior a 200 e a linha do indicador é crescente. Porém, em transações SELL, não há áreas lucrativas.

7.3. Chaikin

Relatórios do Chaikin

O oscilador Chaikin, como o ATR, não mostrou relação entre a os valores do indicador e o lucro das transações.

7.4. indicador de força

Relatórios do indicador de força.

Os gráficos analíticos do indicador de força também não mostraram nenhuma dependência.

7.5. Desvio padrão

Relatórios do SndDev

A análise das dependências dos valores do indicadores stdDev permite revelar algumas áreas rentáveis para ordens de compra, mas, não há hipótese de filtrar transações de venda.

7.6. Indicador de volume

Dependência entre o lucro e o volume.

Não foi possível detectar dependências na análise de dados do indicador de volumes.

7.7. Bill Williams MFI

BW MFI

O indicador BW MFI permite tirar lucro durante a filtragem de transações de compra, só se elas estiverem abertas na cor 0. Porém, não foi possível detectar dependências para as transações de venda.

7.8. MACD.

Relatórios do MACDRelatórios do MACD

Os sinais do indicador MACD permitem filtrar transações de compra rentáveis. Isso pode ser feito realizando operações de compra, quando a linha de sinal está acima do histograma. Mas, a análise não mostra as áreas rentáveis ​​para transações de venda. Ao mesmo tempo, o indicador reduz operações desfavoráveis eliminando transações de venda, quando o histograma está crescendo e as linhas de sinal estão iguais ou abaixo do histograma.

7.9. ADX

Relatórios do ADX

A análise do indicador ADX não permite filtrar a transações.

7.10. Alligator

Relatórios do AlligatorRelatórios do Alligator

O uso do indicador de Alligator para filtração de transações, na minha opinião, é o mais promissor. Padrões para executar transações podem ser encontrados em combinações de posição e direção das linhas. Assim, transações rentáveis para a compra podem ser feitas, se:

  • a posição das linhas do indicador mostra uma tendência de venda e as linhas LIPS e JAW viram para cima;
  • a posição das linhas do indicador indica uma tendência de compra e as linhas LIPS e TEETH apontam para cima;
  • a tendência é indefinida e as linhas TEETH e JAW apontam para baixo. 

Para transações de venda, vamos usar sinais de espelho.

8. Corrigimos o EA inicial

Temos realizado um trabalho muito extenso na análise de transações de nosso EA. Agora, vamos ver como isso vai influenciar o desempenho da nossa estratégia. Para isso, ao módulo de sinais de negociação do artigo [1] adicionamos indicadores com as regras de filtragem da análise acima. Proponho adicionar o MACD e Alligator ao nosso módulo.

Eu recomendaria adicionar os filtro de indicador sequencialmente e executar ciclicamente o procedimento de decomposição de transações em indicadores após a adição de cada filtro. Isto fornecerá uma compreensão mais clara da influência de cada filtro sobre toda a estratégia e ajudará a avaliar seu complexo impacto. Além disso, o fato de, na primeira etapa, a análise não permitir revelar a dependência entre o lucro e os valores de certo indicador não significa que você não vai ver essa dependência em iterações subsequentes. Não faço isso agora simplesmente para não empolar este artigo, que para já é bastante grande.

Primeiro, adicionamos os parâmetros dos indicadores na descrição do módulo.

//| Parameter=JAW_Period,uint,13,JAW Period                                   |
//| Parameter=JAW_Shift,uint,8,JAW Shift                                      |
//| Parameter=TEETH_Period,uint,8,TEETH Period                                |
//| Parameter=TEETH_Shift,uint,5,TEETH Shift                                  |
//| Parameter=LIPS_Period,uint,5,LIPS Period                                  |
//| Parameter=LIPS_Shift,uint,3,LIPS_Shift                                    |
//| Parameter=Alligator_Method,ENUM_MA_METHOD,MODE_SMMA,Method                |
//| Parameter=Alligator_Price,ENUM_APPLIED_PRICE,PRICE_MEDIAN,Alligator Price |
//| Parameter=MACD_Fast,uint,12,MACD Fast                                     |
//| Parameter=MACD_Slow,uint,26,MACD Slow                                     |
//| Parameter=MACD_Signal,uint,9,MACD Signal                                  |
//| Parameter=MACD_Price,ENUM_APPLIED_PRICE,PRICE_CLOSE,MACD Price            |

Assim, no bloco private adicionamos as variáveis para armazenamento de parâmetros, enquanto no bloco public, as funções para sua gravação.

   uint              ci_MACD_Fast;
   uint              ci_MACD_Slow;
   uint              ci_MACD_Signal;
   ENUM_APPLIED_PRICE ce_MACD_Price;
   uint              ci_JAW_Period;
   uint              ci_JAW_Shift;
   uint              ci_TEETH_Period;
   uint              ci_TEETH_Shift;
   uint              ci_LIPS_Period;
   uint              ci_LIPS_Shift;
   ENUM_MA_METHOD    ce_Alligator_Method;
   ENUM_APPLIED_PRICE ce_Alligator_Price;
   void              JAW_Period(uint value)                 {  ci_JAW_Period  =  value;   }
   void              JAW_Shift(uint value)                  {  ci_JAW_Shift   =  value;   }
   void              TEETH_Period(uint value)               {  ci_TEETH_Period=  value;   }
   void              TEETH_Shift(uint value)                {  ci_TEETH_Shift =  value;   }
   void              LIPS_Period(uint value)                {  ci_LIPS_Period =  value;   }
   void              LIPS_Shift(uint value)                 {  ci_LIPS_Shift  =  value;   }
   void              Alligator_Method(ENUM_MA_METHOD value) {  ce_Alligator_Method  =  value;   }
   void              Alligator_Price(ENUM_APPLIED_PRICE value) {  ce_Alligator_Price=  value;   }
   void              MACD_Fast(uint value)                  {  ci_MACD_Fast   =  value;   }
   void              MACD_Slow(uint value)                  {  ci_MACD_Slow   =  value;   }
   void              MACD_Signal(uint value)                {  ci_MACD_Signal =  value;   }
   void              MACD_Price(ENUM_APPLIED_PRICE value)   {  ce_MACD_Price  =  value;   }

Também é necessário adicionar as classes para trabalhar com os indicadores e funções de inicialização de aquisição de dados necessários. Para trabalho com o MACD, eu utilizei a classe padrão. Como para o Alligator não existe uma classe padrão, eu a substitui por três classes de médias móveis dando-lhes nomes de acordo com os nomes das linhas de indicador.

protected:
   CiMACD            m_MACD;           // object-oscillator
   CiMA              m_JAW;
   CiMA              m_TEETH;
   CiMA              m_LIPS;
     
   //--- method of initialization of the indicators
   bool              InitMACD(CIndicators *indicators);
   bool              InitAlligator(CIndicators *indicators);
   //--- methods of getting data
   double            Main(int ind)                     { return(m_MACD.Main(ind));      }
   double            Signal(int ind)                   { return(m_MACD.Signal(ind));    }
   double            DiffMain(int ind)                 { return(Main(ind+1)!=0 ? Main(ind)-Main(ind+1) : 0); }
   int               AlligatorTrend(int ind);
   double            DiffJaw(int ind)                  { return(m_JAW.Main(ind+1)!=0 ? m_JAW.Main(ind)/m_JAW.Main(ind+1) : 1); }
   double            DiffTeeth(int ind)                { return(m_TEETH.Main(ind+1)!=0 ? m_TEETH.Main(ind)/m_TEETH.Main(ind+1) : 1); }
   double            DiffLips(int ind)                 { return(m_LIPS.Main(ind+1)!=0 ? m_LIPS.Main(ind)/m_LIPS.Main(ind+1) : 1); }

No próximo passo, inserimos as mudanças na função InitIndicators para adicionar nossos indicadores à biblioteca do EA.

bool CSignalKalman::InitIndicators(CIndicators *indicators)
  {
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
//--- initialize close serias
   if(CheckPointer(m_close)==POINTER_INVALID)
     {
      if(!InitClose(indicators))
         return false;
     }
//--- create and initialize MACD oscilator
   if(!InitMACD(indicators))
      return(false);
//--- create and initialize Alligator
   if(!InitAlligator(indicators))
      return(false);
//--- create and initialize Kalman Filter
   if(CheckPointer(Kalman)==POINTER_INVALID)
      Kalman=new CKalman(ci_HistoryBars,ci_ShiftPeriod,m_symbol.Name(),ce_Timeframe);
   
//--- ok
   return(true);
  }

Em seguida, introduzimos complementos na função de tomada de decisão. Ao fazer isto, lembre que os indicadores adicionados atuam como filtro. Portanto, só depois de receber o sinal principal é que vamos acessar os indicadores.

int CSignalKalman::LongCondition(void)
  {
   if(!CalculateIndicators())
      return 0;
   int result=0;
   //--- 
   if(cd_correction>cd_forecast)
     {
      if(Signal(1)>Main(1))
         result=80;
      else
        {
         switch(AlligatorTrend(1))
           {
            case 1:
              if(DiffLips(1)>1 && DiffTeeth(1)>1 && DiffJaw(1)<=1)
                 result=80;
              break;
            case -1:
              if(DiffLips(1)>1 || DiffJaw(1)>1)
                 result=80;
              break;
            case 0:
              if(DiffJaw(1)<1)
                {
                 if(DiffLips(1)>1)
                    result=80;
                 else
                    if(DiffTeeth(1)<1)
                       result=80;
                }
              break;
           }
        }
     }
   return result;
  }

Alterações similares são feitas para a função ShortCondition. O código completo de módulo de soluções de negociação pode ser encontrado no anexo.

9. Teste do EA após introduzir alterações

Depois de introduzir alterações no módulo de soluções de negociação, criamos um novo EA (descrição detalhada de criação do EA com uso do módulo de sinais de negociação é fornecida no artigo [5]). Vamos testar o EA recém-criado com os parâmetros semelhantes aos do teste inicial na secção 2 deste artigo.

Como mostraram os resultados do teste, sem alterar os parâmetros do EA, o uso de filtros permitiu aumentar o fator de lucro de 0,75 para 1,12. Ou seja, apesar dos parâmetros desfavoráveis do EA fonte, conseguimos obter lucro. Lembre que no começo, eu tomei parâmetros não-otimizados do EA inicial.

Teste repetidoTeste repetidoTeste repetido

Fim do artigo

Este artigo mostrou o processo de decomposição do histórico de transações em indicadores, o que permitiu providenciar um sistema de filtros com base em indicadores padrão. No final do teste, este sistema mostrou um resultado tangível quanto à rentabilidade do EA analisado. O sistema proposto pode ser aplicado não só na otimização do sistema de negociação existente, mas também na tentativa de criar um novo.

Referências

  1. Uso do filtro de Kalman na previsão da tendência.
  2. Gráficos e diagramas em HTML.
  3. Quanto dura a tendência?
  4. Diversas maneiras de se encontrar uma tendência em MQL5
  5. Examinemos na prática o método adaptativo de acompanhamento do mercado.

Programas utilizados no artigo:

#
 Nome
Tipo 
Descrição 
1Kalman.mqh Biblioteca de classes Classe do filtro de Kalman
2SignalKalman.mqh Biblioteca de classes Módulo de sinais de negociação sobre o filtro de Kalman
3SignalKalman+Filters.mqh Biblioteca de classes Examinemos na prática o método adaptativo de acompanhamento do mercado
4Kalman_expert.mq5 Expert Advisor Expert Advisor original usado na estratégia com o filtro de Kalman
5Kalman+Filters.mq5 Expert Advisor Expert Advisor modificado usado na estratégia com o filtro de Kalman
6Deals_to_Indicators.mq5 Expert Advisor Expert Advisor para decomposição do histórico de transações em indicadores
7Deal.mqh  Biblioteca de classes Classe para salvar informações sobre o trade
8Parsing.mqh Biblioteca de classes Classe para analise sintática do histórico de transações a partir do relatório de teste
9Value.mqh  Biblioteca de classes Classe para armazenar dados sobre o estado do buffer de indicador
10OneBufferArray.mqh Biblioteca de classes Classe para armazenar o histórico de dados de um indicador de buffer
11StaticOneBuffer.mqh Biblioteca de classes Classe para coleta e análise de estatísticas de um indicador de buffer
12ADXValue.mqh Biblioteca de classes Classe para armazenar dados sobre o estado do indicador ADX
13ADX.mqh Biblioteca de classes Classe para armazenar o histórico de dados do indicador ADX
14StaticADX.mqh Biblioteca de classes Classe para coleta e análise estatística do indicador ADX
15AlligatorValue.mqh Biblioteca de classes Classe para armazenar dados sobre o estado do indicador Alligator
16Alligator.mqh Biblioteca de classes Classe para armazenar o histórico de dados do indicador Alligator
17StaticAlligator.mqh Biblioteca de classes Classe para coleta e análise estatística do indicador Alligator
18BWMFIValue.mqh Biblioteca de classes Classe para armazenar dados sobre o estado do indicador BW MFI
19BWMFI.mqh Biblioteca de classes Classe para armazenar o histórico de dados do indicador BW MFI
20StaticBWMFI.mqh Biblioteca de classes Classe para coleta e análise estatística do indicador BW MFI
21MACDValue.mqh Biblioteca de classes Classe para armazenar dados sobre o estado do indicador MACD
22MACD.mqh Biblioteca de classes Classe para armazenar o histórico de dados do indicador MACD
23StaticMACD.mqh Biblioteca de classes Classe para coleta e análise estatística do indicador MACD
24 Reports.zip Arquivo O arquivo contém os resultados dos testes dos EAs no testador de estratégias e um relatório analítico.