English Русский 中文 Español Deutsch 日本語
Guia Prático MQL5 - Sinais de negociação de canais móveis

Guia Prático MQL5 - Sinais de negociação de canais móveis

MetaTrader 5Exemplos | 11 outubro 2016, 10:57
3 079 0
Denis Kirichenko
Denis Kirichenko

Introdução

O artigo anterior «Guia Prático MQL5 - Programando os canais móveis» descreveu um método para traçar canais equidistantes, frequentemente chamado de canais móveis. Com o intuito de solucionar a tarefa, foram usados a ferramenta «Equidistant Channel» e o recurso OOP.

Este artigo dará ênfase nos sinais, que poderão ser identificados por meio destes canais. Vamos tentar criar uma estratégia de negociação fundamentada nesses sinais.

Existem diversos artigos publicados no MQL5, nos quais descrevem a geração de sinais de negociação usando chamadas para os módulos prontos da Biblioteca Padrão. Espera-se que este artigo complemente os materiais e amplie aos usuários a variação das classes padrão.

Para aqueles que começaram a se familiarizar com esta estratégia, são bem vindos a estudar este material, do simples ao mais complexo. Primeiro, cria-se uma estratégia básica, depois a tornamos mais complexa e incluimos quando possível.


1. O indicador do canal equidistante

No artigo anterior sobre canais móveis, o próprio expert advisor plota os canais, criando os objetos gráficos. Por um lado, esta abordagem facilita certas tarefas para o programador, já pelo outro, ela torna determinadas coisas impossíveis. Por exemplo, se o EA trabalha em modo de otimização, então ele pode não detectar quaisquer objetos gráficos na tabela, bem como não teria nenhum gráfico. Descrição das limitações durante o teste:


Objetos Gráficos em Testes

Durante os testes/otimizações, os objetos gráficos não são plotados. Então, ao se referir às propriedades de um objeto criado durante os testes ou a otimização, um Expert Advisor receberá valores igual a zero.

Esta limitação não se aplica aos testes no modo visual.


Por isso, será utilizada uma abordagem diferente, criando um indicador que reflete tanto os fratais, quanto o canal atual.

Este indicador é chamado de EquidistantChannels. Ele consiste essencialmente de dois blocos. O primeiro cálcula os buffers do fractal, e o segundo — os buffers dos canais.

Aqui está o código do handler de evento Calculate.

---------//+---------------------------------------------------------+
//| Função de interação do indicador personalizado                   |
---------//+---------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- se não houver código de barras na chamada anterior
   if(prev_calculated==0)
     {
      //--- zerar os buffers 
      ArrayInitialize(gUpFractalsBuffer,0.);
      ArrayInitialize(gDnFractalsBuffer,0.);
      ArrayInitialize(gUpperBuffer,0.);
      ArrayInitialize(gLowerBuffer,0.);
      ArrayInitialize(gNewChannelBuffer,0.);
     }
//--- Cálculo para fractais [Inicio]
   int startBar,lastBar;
//---
   if(rates_total<gMinRequiredBars)
     {
      Print("Not enough data for calculation");
      return 0;
     }
//---
   if(prev_calculated<gMinRequiredBars)
      startBar=gLeftSide;
   else
      startBar=rates_total-gMinRequiredBars;
//---
   lastBar=rates_total-gRightSide;
   for(int bar_idx=startBar; bar_idx<lastBar && !IsStopped(); bar_idx++)
     {
      //---
      if(isUpFractal(bar_idx,gMaxSide,high))
         gUpFractalsBuffer[bar_idx]=high[bar_idx];
      else
         gUpFractalsBuffer[bar_idx]=0.0;
      //---
      if(isDnFractal(bar_idx,gMaxSide,low))
         gDnFractalsBuffer[bar_idx]=low[bar_idx];
      else
         gDnFractalsBuffer[bar_idx]=0.0;
     }
//--- Cálculo para os fractais [final]

//--- Cálculo para as extremidades do canal [Iniciar]
   if(prev_calculated>0)
     {
      //--- se o conjunto não foi inicializado
      if(!gFracSet.IsInit())
         if(!gFracSet.Init(
            InpPrevFracNum,
            InpBarsBeside,
            InpBarsBetween,
            InpRelevantPoint,
            InpLineWidth,
            InpToLog
            ))
           {
            Print("Fractal set initialization error!");
            return 0;
           }
      //--- cálculo
      gFracSet.Calculate(gUpFractalsBuffer,gDnFractalsBuffer,time,
                         gUpperBuffer,gLowerBuffer,
                         gNewChannelBuffer
                         );
     }
//--- Cálculo para as extremidades do canal [final]

//--- valor de retorno do prev_calculated para a próxima chamada
   return rates_total;
  }

O bloco com o cálculo dos valores do buffer do fractal é destacado em amarelo e o bloco com o cálculo dos buffers dos canais — em verde. É fácil perceber que o segundo bloco será ativado somente na segunda escala do handler. Esta implementação do segundo bloco permite obter os buffers completos dos fractais.

Agora, algumas palavras sobre o conjunto de pontos fractais — o objeto CFractalSet. Devido à alteração do método de exibição do canal, foi necessário modificar a classe CFractalSet. O método fundamental foi o CFractalSet::Calculate, que calcula o buffer do canal do indicador. O código é fornecido no arquivo CFractalPoint.mqh.



Agora existe uma base — um provedor de sinais a partir do canal equidistante. A operação do indicador é apresentada no vídeo.


2. Estratégia básica

Começaremos com algo simples, o qual poderá ser melhorado e revisto com a ajuda do OOP (Objeto Orientado a Programação). Temos de ter alguma estratégia básica.

Esta estratégia irá considerar algumas regras bem simples de negociação. Entradas no mercado serão feitas pelas extremidades do canal. Quando o preço atingir a extremidade inferior, uma posição de compra será aberta, ao tocar a extremidade "superior" - será aberta a posição de venda. A Fig 1 mostra que o preço atingiu a extremidade inferior, então o robô compra um certo volume. Os níveis de negociação (stop loss e take profit) têm um tamanho fixo, também foram posicionados automaticamente. Se existe posição aberta, os sinais de entrada repetidos serão ignorados.

Fig.1 Sinal de entrada

Fig.1Sinal de Entrada


Também vale a pena mencionar que a Biblioteca padrão tem crescido bastante. A mesma contém muitas classes prontas que podem ser utilizadas. Primeiramente, tentaremos «conectar» a classe do sinal CExpertSignal. De acordo com a documentação, esta é a classe base para a criação de geradores de sinais de negociação.

Essa classe foi nomeada com muita precisão. Esta não é a CTradeSignal ou CSignal, mas sim, é a classe de sinais que são concebidas para uso no código do EA — CExpertSignal.

Não prolongaremos este conteúdo. O artigo «Assistente do MQL5: Como Criar um Módulo de Sinais de Negociação» contém uma descrição detalhada dos métodos da classe de sinal.


2.1 O sinal da classe CSignalEquidChannel

Desta maneira, as classes derivadas do sinal são as seguintes:

---------//+---------------------------------------------------------+
//| Classe CSignalEquidChannel                                       |
//| Objetivo: Classe de sinais de negociação baseada no canal        |
//|          equidistante.                                           |
//| Derivada da classe CExpertSignal.                                |
---------//+---------------------------------------------------------+
class CSignalEquidChannel : public CExpertSignal
  {
protected:
   CiCustom          m_equi_chs;          // objeto do indicador "EquidistantChannels"   
   //--- parâmetros ajustáveis
   int               m_prev_frac_num;     // fractais anteriores
   bool              m_to_plot_fracs;     // fractais exibidos?
   int               m_bars_beside;       // barras à esquerda/direita do fractal
   int               m_bars_between;      // barras intermediárias
   ENUM_RELEVANT_EXTREMUM m_relevant_pnt; // ponto relevante
   int               m_line_width;        // largura da linha
   bool              m_to_log;            // manter o log?
   double            m_pnt_in;            // tolerância interna, pips
   double            m_pnt_out;           // tolerância externa, pips
   bool              m_on_start;          // inicio da flag de sinalização
   //--- calculado
   double            m_base_low_price;    // base da mínima do preço 
   double            m_base_high_price;   // base da máxima do preço
   double            m_upper_zone[2];     // zona superior: [0]-tolerância interna, [1]-externa  
   double            m_lower_zone[2];     // zona inferior
   datetime          m_last_ch_time;      // tempo de ocorrência do último canal
   //--- "pesos" dos modelos do mercado (0-100)
   int               m_pattern_0;         //  "Atingindo o limite inferior do canal - comprar, o superior - vender"

   //--- === Métodos === --- 
público:
   //--- Construtor/destrutor
   void              CSignalEquidChannel(void);
   void             ~CSignalEquidChannel(void){};
   //--- métodos para definir parâmetros ajustáveis
   void              PrevFracNum(int _prev_frac_num)   {m_prev_frac_num=_prev_frac_num;}
   void              ToPlotFracs(bool _to_plot)        {m_to_plot_fracs=_to_plot;}
   void              BarsBeside(int _bars_beside)      {m_bars_beside=_bars_beside;}
   void              BarsBetween(int _bars_between)    {m_bars_between=_bars_between;}
   void              RelevantPoint(ENUM_RELEVANT_EXTREMUM _pnt) {m_relevant_pnt=_pnt;}
   void              LineWidth(int _line_wid)          {m_line_width=_line_wid;}
   void              ToLog(bool _to_log)               {m_to_log=_to_log;}
   void              PointsOutside(double _out_pnt)    {m_pnt_out=_out_pnt;}
   void              PointsInside(double _in_pnt)      {m_pnt_in=_in_pnt;}
   void              SignalOnStart(bool _on_start)     {m_on_start=_on_start;}
   //--- métodos de ajuste dos "pesos" dos modelos de mercado
   void              Pattern_0(int _val) {m_pattern_0=_val;}
   //--- método de verificação das configurações
   virtual bool      ValidationSettings(void);
   //--- método de criação do indicador e das timeseries.
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- métodos de verificação, caso os modelos de mercado sejam elaborados
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);
   virtual double    Direction(void);
   //---
protected:
   //--- método de inicialização do indicador
   bool              InitCustomIndicator(CIndicators *indicators);
   //- obter o valor do limite superior do canal
   double            Upper(int ind) {return(m_equi_chs.GetData(2,ind));}
   //- obter o valor da limite inferior do canal
   double            Lower(int ind) {return(m_equi_chs.GetData(3,ind));}
   //- obter a flag do surgimento do canal
   double            NewChannel(int ind) {return(m_equi_chs.GetData(4,ind));}
  };
---------//+---------------------------------------------------------+

Algumas nuances a serem observadas.

O principal gerador de sinal nesta classe é a configuração do canal equidistante, é o único na versão atual e por enquanto, não haverá nenhum outro. Por sua vez, contém a classe para trabalhar com indicador técnico do tipo personalizadoCiCustom.

O modelo básico é usado como o modelo de sinal: "ao tocar na margem inferior do canal — comprar; na parte superior — vender". Desde que tocar com extrema precisão, por assim dizer, não seja o evento mais provável, então se usa um certo buffer com extremidades ajustáveis. O parâmetro de tolerância externa m_pnt_out determina o quão longe o preço tem a permissão de ir além das extremidades do canal; o parâmetro de tolerância interno m_pnt_in — estabelece qual medida o preço está autorizado a permanecer longe da extremidade. A lógica é bastante simples. Suponha que o preço atingiu os limites do canal, que se aproximou dele ou até o ultrapassou levemente. Fig.2 Apresentação esquemática de um buffer. O preço e a extremidade acionam o modelo.

Fig.2 Acionando o modelo básico do sinal

Fig.2 Acionando o modelo básico do sinal


O array do parâmetro m_upper_zone[2] delineia as extremidades do buffer superior; o m_lower_zone[2] — o inferior.

O nível de preço $1.11552 no exemplo serve como a extremidade superior do canal (linha vermelha). O nível de $1,11452 é responsável pelo limite inferior do buffer e o $1,11702 — pelo superior. Desta forma, a flexibilidade da tolerância externa é 150 pontos e a interna é de 100 pontos. O preço é exibido pela curva azul.

O parâmetro m_on_start permite ignorar os sinais do primeiro canal ao executar o robô no gráfico, pois neste caso, o canal já havia sido desenhado. Se a flag é reiniciada, o robô irá funcionar apenas no próximo canal e não irá processar sinais de negociação na atual.

Os parâmetros m_base_low_price e m_base_high_price armazenam os valores das mínimas dos preço e das máximas nas barras atuais. A barra é considerada como zero, se a negociação for realizada em cada tick, ou na barra anterior se a negociação for permitida apenas com o aparecimento de uma nova barra.

Agora, algumas palavras sobre os métodos. Aqui devemos notar que o desenvolvedor proporciona um nível suficientemente amplo em relação a liberdade de ação, pois cerca de metade dos métodos são virtuais. Isto significa que o comportamento das classes descendentes podem ser implementadas conforme seja necessário.

Vamos começar com o método Direction(), que quantitativamente estima o potencial da direção de negociação:

---------//+---------------------------------------------------------+
//| Determinar a direção "pesada"                                    |
---------//+---------------------------------------------------------+
double CSignalEquidChannel::Direction(void)
  {
   double result=0.;
//--- aparecimento de um novo canal
   datetime last_bar_time=this.Time(0);
   bool is_new_channel=(this.NewChannel(0)>0.);
//--- Se os sinais do primeiro canal são ignorados
   if(!m_on_start)
      //--- Se o primeiro canal é normalmente exibido durante a inicialização
      if(m_prev_frac_num==3)
        {
         static datetime last_ch_time=0;
         //--- se um novo canal apareceu
         if(is_new_channel)
           {
            last_ch_time=last_bar_time;
            //--- se este é o primeiro lançamento
            if(m_last_ch_time==0)
               //--- armazenar a hora da barra, onde o primeiro canal apareceu
               m_last_ch_time=last_ch_time;
           }
         //---se os horários combinam
         if(m_last_ch_time==last_ch_time)
            return 0.;
         else
         //--- limpar a flag
            m_on_start=true;
        }
//--- indicador de barras atuais
   int actual_bar_idx=this.StartIndex();
//--- definir as extremidades
   double upper_vals[2],lower_vals[2]; // [0]-a barra antecende a atual, [1]-barra atual
   ArrayInitialize(upper_vals,0.);
   ArrayInitialize(lower_vals,0.);
   for(int idx=ArraySize(upper_vals)-1,jdx=0;idx>=0;idx--,jdx++)
     {
      upper_vals[jdx]=this.Upper(actual_bar_idx+idx);
      lower_vals[jdx]=this.Lower(actual_bar_idx+idx);
      if((upper_vals[jdx]==0.) || (lower_vals[jdx]==0.))
         return 0.;
     }
//--- obter os preços
   double curr_high_pr,curr_low_pr;
   curr_high_pr=this.High(actual_bar_idx);
   curr_low_pr=this.Low(actual_bar_idx);
//--- Se os preços são obtidos
   if(curr_high_pr!=EMPTY_VALUE)
      if(curr_low_pr!=EMPTY_VALUE)
        {
         //--- armazenar os preços
         m_base_low_price=curr_low_pr;
         m_base_high_price=curr_high_pr;
         //---Definir preços para as zonas dos buffer
         //--- zona superior: [0]-tolerância interna, [1]-externa 
         this.m_upper_zone[0]=upper_vals[1]-m_pnt_in;
         this.m_upper_zone[1]=upper_vals[1]+m_pnt_out;
         //--- zona inferior: [0]-tolerância interna, [1]-externa 
         this.m_lower_zone[0]=lower_vals[1]+m_pnt_in;
         this.m_lower_zone[1]=lower_vals[1]-m_pnt_out;
         //---normalização
         for(int jdx=0;jdx<ArraySize(m_lower_zone);jdx++)
           {
            this.m_lower_zone[jdx]=m_symbol.NormalizePrice(m_lower_zone[jdx]);
            this.m_upper_zone[jdx]=m_symbol.NormalizePrice(m_upper_zone[jdx]);
           }
         //---verificar se há zonas de convergência
         if(this.m_upper_zone[0]<=this.m_lower_zone[0])
            return 0.;
         //--- Resultado
         result=m_weight*(this.LongCondition()-this.ShortCondition());
        }
//---
   return result;
  }
---------//+---------------------------------------------------------+

O primeiro bloco na estrutura do método, verifica se é necessário ignorar o primeiro canal no gráfico, caso o mesmo esteja presente.

O segundo bloco obtém os preços atuais e determina as zonas do buffer. Esta é a verificação de convergência das zonas. Se o canal for demasiadamente estreito, ou se as zonas buffer são muito largas, existe a possibilidade puramente matemática de que o preço possa entrar em ambas as zonas. Portanto, tal situação deve ser examinada.

O alvo é destacado na linha azul. Aqui, conseguimos uma estimativa quantitativa da direção de negociação, caso haja alguma.

Agora, vamos considerar o método LongCondition().

---------//+---------------------------------------------------------+
//| Verificar a condição para a compra                               |
---------//+---------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
//--- se a mínima de preço é definida
   if(m_base_low_price>0.)
      //--- se a mínima de preço está no nível da extremidade inferior
      if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
        {
         if(IS_PATTERN_USAGE(0))
            result=m_pattern_0;
        }
//---
   return result;
  }
---------//+---------------------------------------------------------+

Para comprar, verifique se o preço entrou na zona do buffer inferior. Feito isso, verifique a permissão para ativar o modelo de mercado. Mais detalhes sobre as estruturas do tipo "IS_PATTERN_USAGE(k)" podem ser encontradas no artigo «Gerador de Sinal de Negócios baseado em um Indicador Personalizado».

O método ShortCondition() funciona de modo similar ao mencionado acima. O foco está somente na zona do buffer superior.

---------//+---------------------------------------------------------+
//| Verificar a condição para venda                                  |
---------//+---------------------------------------------------------+
int CSignalEquidistantChannel::ShortCondition(void)
  {
   int result=0;
//--- se a máxima de preço é definida
   if(m_base_high_price>0.)
      //--- se a máxima de preço está no nível da extremidade superior
      if((m_base_high_price>=m_upper_zone[0]) && (m_base_high_price<=m_upper_zone[1]))
        {
         if(IS_PATTERN_USAGE(0))
            result=m_pattern_0;       
        }
//---
   return result;
  }
---------//+---------------------------------------------------------+

A classe inicializa um indicador personalizado usando o método InitCustomIndicator():

---------//+---------------------------------------------------------+
//| Inicialização de indicadores personalizados                      |
---------//+---------------------------------------------------------+
bool CSignalEquidChannel::InitCustomIndicator(CIndicators *indicators)
  {
//--- adicionar um objeto para a coleção
   if(!indicators.Add(GetPointer(m_equi_chs)))
     {
      PrintFormat(__FUNCTION__+": error adding object");
      return false;
     }
//--- especificar os parâmetros do indicador
   MqlParam parameters[8];
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="EquidistantChannels.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_prev_frac_num;   // 1) fractais anteriores
   parameters[2].type=TYPE_BOOL;
   parameters[2].integer_value=m_to_plot_fracs;   // 2) visualizar fractais?
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_bars_beside;     // 3) barras à esquerda/direita do fratal
   parameters[4].type=TYPE_INT;
   parameters[4].integer_value=m_bars_between;    // 4) barra intermediária
   parameters[5].type=TYPE_INT;
   parameters[5].integer_value=m_relevant_pnt;    // 5) ponto relevante
   parameters[6].type=TYPE_INT;
   parameters[6].integer_value=m_line_width;    // 6) largura da linha
   parameters[7].type=TYPE_BOOL;
   parameters[7].integer_value=m_to_log;          // 7) manter o log?

//--- inicialização de objeto
   if(!m_equi_chs.Create(m_symbol.Name(),_Period,IND_CUSTOM,8,parameters))
     {
      PrintFormat(__FUNCTION__+": error initializing object");
      return false;
     }
//--- número de buffers
   if(!m_equi_chs.NumBuffers(5))
      return false;
//--- ok
   return true;
  }
---------//+---------------------------------------------------------+

O primeiro valor no array do parâmetro deve ser o nome do indicador, assim como uma string.

A classe também contém um método virtual ValidationSettings(). Este método é semelhante ao anterior e verifica se os parâmetros do indicador do canal são definidos corretamente. Existem também métodos de serviço que recebem os valores correspondentes aos buffers do indicador personalizado.

No momento, todos estão relacionados com a classe de sinal derivada.


2.2 Classe de negociação de estratégia CEquidChannelExpert

A implementação básica da ideia vai exigir a escrita de uma classe derivada da classe padrão CExpert. Na etapa atual, o código será o mais compacto possível, porque é de fato necessário alterar apenas o comportamento do handler principal — o método Processing(). Ele é virtual, então temos a oportunidade de escrever todas as estratégias.

---------//+---------------------------------------------------------+
//| Classe CEquidChannelExpert.                                      |
//| Objetivo: Classe  para EA de negociação de  canal equidistante.  |
//| Derivado da classe CExper.                                       |
---------//+---------------------------------------------------------+
class CEquidChannelExpert : public CExpert
  {
   //--- === Membros de dados === --- 
privado:


   //--- === Métodos === --- 
público:
   //--- construtor/destrutor
   void              CEquidChannelExpert(void){};
   void             ~CEquidChannelExpert(void){};

protected:
   virtual bool      Processing(void);
  };
---------//+---------------------------------------------------------+

Aqui está o método:

---------//+---------------------------------------------------------+
//| Módulo principal                                                 |
---------//+---------------------------------------------------------+
bool CEquidChannelExpert::Processing(void)
  {
//--- cálculo da direção
   m_signal.SetDirection();
//--- checar se existe posições abertas
   if(!this.SelectPosition())
     {
      //--- módulo de abertura da posição
      if(this.CheckOpen())
         return true;
     }
//--- se não houver operações de negociação
   return false;
  }

Tudo é muito simples. Primeiramente, o sinal de objeto calcula a direção da possível negociação, após a presença de uma posição aberta que é verificada. Se não houver posição, ele procura por uma oportunidade para poder abrir. Se houver uma posição, então não faz nada.

O código da estratégia básica é implementado no arquivo BaseChannelsTrader.mq5.




Tal como no exemplo do funcionamento apresentado no vídeo.


Fig.3 Resultados da estratégia básica para 2013-2015.

Fig.3 Resultados da estratégia básica para 2013-2015

A execução foi feita no testador de estratégia, sendo averiguada de hora em hora para o símbolo EURUSD. Pode-se notar no balanço do gráfico que em certos intervalos a estratégia básica trabalhou pelo "princípio de serra": um negócio sem lucro, seguido por um rentável. Os valores dos parâmetros personalizados utilizados nos ensaios estão disponíveis no arquivo base_signal.set. Ele também contém os parâmetros do canal, além dos valores que permaneceram inalterados em todas as versões da estratégia.

Foi utilizado o modo de teste "Cada tick baseado em ticks reais".

Essencialmente, existem duas formas de melhorar o desempenho da negociação na estratégia. A primeira é a otimização, no qual consiste em selecionar uma combinação de valores dos parâmetros para maximizar o lucro, e etc. A segunda maneira, concerne na preocupação em encontrar os fatores que afetam o desempenho do EA. Se o primeiro método não está associado com a mudança da lógica da estratégia de negociação, o segundo não pode ser feito sem ele.

Na próxima seção, a estratégia básica será editada e os fatores de desempenho serão procurados.


3. Fatores de desempenho

Algumas palavras sobre a disposição. Por conveniência colocamos todos os arquivos da estratégia numa única pasta do projeto. Por conseguinte, a implementação da estratégia básica está localizada na subpasta Base (Fig.4) e assim por diante.

Fig.4 Exemplo da hierarquia das pastas do projeto para a estratégia de canais

Fig.4 Exemplo da hierarquia das pastas do projeto para a estratégia de canais

Além disso, assumimos que cada novo fator é uma nova etapa para realizar as alterações nos arquivos de origem que compõem o código do EA.

3.1 Usando o trailing

Antes de começar, é sugerido o trailing padrão para a estratégia. Com o objeto da classe CTrailingFixedPips, o que permite manter posições abertas numa "distância" fixa (em pontos). Isto moverá tanto o preço do stop loss quanto o preço do take profit. Para desativar o trailing do take profit, é definido um valor zero para o parâmetro correspondente (InpProfitLevelPips).

Fizemos as seguintes alterações no código:

Adicionamose um grupo de parâmetros personalizados para o arquivo ChannelsTrader1.mq5 do expert:

//---
sinput string Info_trailing="+===-- Trailing --====+"; // +===-- Trailing --====+
input int InpStopLevelPips=30;          // nível para StopLoss, em pips
input int InpProfitLevelPips=50;        // nível para TakeProfit, em pips

No bloco de inicialização, escrevemos um objeto do tipo CTrailingFixedPips para ser criado, incluído e definido nos parâmetros da estratégia.

//--- Objeto do trailing
   CTrailingFixedPips *trailing=new CTrailingFixedPips;
   if(trailing==NULL)
     {
      //--- erro
      printf(__FUNCTION__+": error creating trailing");
      myChannelExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Adicionando o objeto de trailing
   if(!myChannelExpert.InitTrailing(trailing))
     {
      //--- erro
      PrintFormat(__FUNCTION__+": error initializing trailing");
      myChannelExpert.Deinit();
      return INIT_FAILED;
     }
//--- parâmetros do trailing
   trailing.StopLevel(InpStopLevelPips);
   trailing.ProfitLevel(InpProfitLevelPips);

Uma vez que o trailing será utilizado, é necessário modificar o método principal CEquidChannelExpert::Processing(), no arquivo EquidistantChannelExpert1.mqh.

---------//+---------------------------------------------------------+
//| Módulo principal                                                 |
---------//+---------------------------------------------------------+
bool CEquidChannelExpert::Processing(void)
  {
//--- cálculo da direção
   m_signal.SetDirection();
//--- Se não existe uma posição
   if(!this.SelectPosition())
     {
      //--- módulo de abertura da posição
      if(this.CheckOpen())
         return true;
     }
//---se existe uma posição
   else
     {
      //--- verificar se a modificação da posição é viável
      if(this.CheckTrailingStop())
         return true;
     }
//--- se não houver operações de negociação
   return false;
  }

É isso aí. O trailing foi adicionado. Os arquivos atualizados da estratégia estão localizados na subpasta separada ChannelsTrader1.

Vamos nos certificar se a inovação possui qualquer impacto sobre a eficácia.

Assim, foram feitas várias execuções da estratégi ano modo de otimização, com os mesmos intervalos do histórico e parâmetros, assim como para a estratégia básica. Os parâmetros do stop loss e take profit foram ajustados:

 Variável ComeçoPasso
Finalização
Nível do StopLoss, em pips
0
10
100
Nível do TakeProfit, em pips
0
10
150

Os resultados da otimização podem ser encontrados no arquivo ReportOptimizer-signal1.xml. A melhor execução é apresentada na Figura 5, onde o nível de StopLoss = 0 e TakeProfit = 150.

Fig.5 Resultados da estratégia com o uso do trailing para 2013-2015.

Fig.5 Resultados da estratégia com o uso do trailing para 2013-2015.

É fácil perceber a semelhança entre a última figura e a Fig 3. Deste modo, pode-se dizer que o uso do trailing, neste intervalo de valores, não aprimora o resultado.


3.2 Tipo de canal

Observamos que o tipo de canal afeta os resultados de desempenho. A ideia geral é esta: é melhor vender no canal descendente e comprar no ascendente. Se o canal é lateral (não inclinado), então é possível negociar com base em ambas as margens.

A enumeração ENUM_CHANNEL_TYPE define o tipo de canal:

---------//+---------------------------------------------------------+
//| Tipo de canal                                                    |
---------//+---------------------------------------------------------+
enum ENUM_CHANNEL_TYPE
  {
   CHANNEL_TYPE_ASCENDING=0,  // ascendente
   CHANNEL_TYPE_DESCENDING=1, // descendente
   CHANNEL_TYPE_FLAT=2,       // lateral
  };
---------//+---------------------------------------------------------+

Definindo o parâmetro de tolerância de pesquisa para o tipo de canal no bloco de inicialização do arquivo de origem ChannelsTrader2.mq5 do EA.

//--- parâmetros do filtro
   filter0.PointsInside(_Point*InpPipsInside);
   filter0.PointsOutside(_Point*InpPipsOutside);
   filter0.TypeTolerance(_Point*InpTypePips);
   filter0.PrevFracNum(InpPrevFracNum);
   ...

Este parâmetro controla a velocidade da mudança de preço em pontos. Vamos assumir que ele é igual a 7 pips. Então se o canal "cresce" 6 pips em cada barra, ele não é o suficiente para ser considerado ascendente. Logo ele simplesmente será considerado lateral (não inclinado).

Adicionamos a identificação do tipo de canal no método Direction() da fonte do sinal SignalEquidChannel2.mqh para o sinal final.

//--- se o canal for novo
   if(is_new_channel)
     {
      m_ch_type=CHANNEL_TYPE_FLAT;                // lateralidade (não inclinado) do canal
      //--- se foi configurado a tolerância para o tipo
      if(m_ch_type_tol!=EMPTY_VALUE)
        {
         //--- Tipo de canal
         //--- velocidade da mudança
         double pr_speed_pnt=m_symbol.NormalizePrice(upper_vals[1]-upper_vals[0]);
         //---Se a velocidade é suficiente
         if(MathAbs(pr_speed_pnt)>m_ch_type_tol)
           {
            if(pr_speed_pnt>0.)
               m_ch_type=CHANNEL_TYPE_ASCENDING;  // canal ascendente
            else
               m_ch_type=CHANNEL_TYPE_DESCENDING; // canal descendente             
           }
        }
     }

Inicialmente, o canal é considerado lateral - nem ascendente e nem descendente. Se o valor do parâmetro de tolerância para a identificação do tipo de canal não foi definido, então ele não retorna para determinar a velocidade de mudança.

 A condição para a compra irá incluir uma verificação para saber se o canal não é descendente.

---------//+---------------------------------------------------------+
//| Verificar a condição para a compra                               |
---------//+---------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
//--- se a mínima de preço é definida
   if(m_base_low_price>0.)
      //--- Se o canal não é descendente
      if(m_ch_type!=CHANNEL_TYPE_DESCENDING)
         //--- se a mínima de preço está no nível da extremidade inferior
         if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
           {
            if(IS_PATTERN_USAGE(0))
               result=m_pattern_0;
           }
//---
   return result;
  }
---------//+---------------------------------------------------------+

Uma averiguação deste tipo é realizada na condição de venda para ver se o canal não é ascendente.

O metodo principal CEquidChannelExpert::Processing() do arquivo EquidistantChannelExpert2.mqh será o mesmo da versão básica, uma vez que o trailing foi excluído.

Comprovando a eficácia deste fator, apenas um parâmetro foi otimizado.

 Variável ComeçoPasso
Finalização
Tolerância para o tipo, em pips
0
5
150

Os resultados da otimização podem ser encontrados no arquivo ReportOptimizer-signal2.xml. A melhor execução é apresentada na Fig.6.

Fig.6 Resultados da estratégia com o uso do tipo de canal para 2013-2015.

Fig.6 Resultados da estratégia com o uso do tipo de canalpara 2013-2015.


É fácil perceber que os resultados dos testes foram ligeiramente melhores do que os resultados da estratégia básica. Acontece que, a um determinado valor base de parâmetros, um filtro como o tipo de canal influencia no resultado final. 


3.3 Largura do canal

Observamos que a largura do canal pode influenciar o tipo de estratégia em si. Se o canal se tornou estreito, então quando a extremidade for rompida, será possível negociar na direção do rompimento e não contra ela. Isso resulta numa estratégia de rompimento. Se o canal se tornando largo, é possível negociar com base nas suas extremidades. Esta é a estratégia de recuperação. Na estratégia atual a negociação é realizada com base nos limites do canal.

Obviamente, aqui é necessário um critério para determinar se o canal é estreito ou largo. Para não executar ordens n extremidades é sugerido adicionar algo entre os dois, de modo que não considere o canal analisado estreito ou largo. Como resultado, 2 critérios são necessários:

  1. Largura suficiente de um canal estreito;
  2. Largura suficiente de um canal largo.

Se o canal não for nenhuma destas duas situações, então pode ser sensato em abster-se da entrada no mercado.

Fig.7 Largura do canal, diagrama

Fig.7 Largura do canal, diagrama

Ocorre um problema geométrico com a determinação da largura do canal, tal como nos eixos gráficos, eles são medidos em valores diferentes. Fica fácil medir o comprimento dos segmentos AB e CD, mas existe um problema com o cálculo do segmento CE (Fig.7).

Escolhemos o método mais simples para a normatização, ainda que polêmico e menos preciso. A fórmula é a seguinte:

comprimento CE ≃ comprimento CD / (1.0 + velocidade de canal)

A largura do canal é medida usando enumeração ENUM_CHANNEL_WIDTH_TYPE:

---------//+---------------------------------------------------------+
//| Largura do canal                                                 |
---------//+---------------------------------------------------------+
enum ENUM_CHANNEL_WIDTH_TYPE
  {
   CHANNEL_WIDTH_NARROW=0,   // estreito
   CHANNEL_WIDTH_MID=1,      // médio
   CHANNEL_WIDTH_BROAD=2,    // largo
  };

Adicionado os critérios de largura do canal ao grupo dos parâmetros personalizados "Channels" no arquivo de origem ChannelsTrader3.mq5 do expert.

//---
sinput string Info_channels="+===-- Channels --====+"; // +===-- Channels --====+
input int InpPipsInside=100;            // tolerância interna, pips
input int InpPipsOutside=150;           // tolerância externa, pips
input int InpNarrowPips=250;            // Limite do canal, pips
input int InpBroadPips=1200;            // Largura de canal, pips
...

Se o critério do canal estreito tem um valor maior do que a largura do canal, acontecerá um erro de inicialização.

//--- parâmetros do filtro
   filter0.PointsInside(_Point*InpPipsInside);
   filter0.PointsOutside(_Point*InpPipsOutside);
   if(InpNarrowPips>=InpBroadPips)
     {
      PrintFormat(__FUNCTION__+": error specifying narrow and broad values");
      return INIT_FAILED;
     }
   filter0.NarrowTolerance(_Point*InpNarrowPips);
   filter0.BroadTolerance(_Point*InpBroadPips);

O momento de determinar o grau da largura do canal é apresentado no corpo do método Direction().

//--- Largura do canal 
   m_ch_width=CHANNEL_WIDTH_MID;               // média
   double ch_width_pnt=((upper_vals[1]-lower_vals[1])/(1.0+pr_speed_pnt));
//--- Se o critério do canal estreito é especificado
   if(m_ch_narrow_tol!=EMPTY_VALUE)
      if(ch_width_pnt<=m_ch_narrow_tol)
         m_ch_width=CHANNEL_WIDTH_NARROW;      // estreito    
//--- Se o critério do canal largo é especificado
   if(m_ch_narrow_tol!=EMPTY_VALUE)
      if(ch_width_pnt>=m_ch_broad_tol)
         m_ch_width=CHANNEL_WIDTH_BROAD;       // largo 

Inicialmente, o canal é considerado como médio. Em seguida, é verificado se é estreito ou largo.

É também necessário mudar tanto os métodos de determinação, bem como a negociação pela direção. Portanto, a condição para a compra ficará da seguinte forma:

---------//+---------------------------------------------------------+
//| Verificar a condição para a compra                               |
---------//+---------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
//--- se o canal é estreito - negocie no rompimento da extremidade superior
   if(m_ch_width==CHANNEL_WIDTH_NARROW)
     {
      //--- se a máxima de preço é definida
      if(m_base_high_price>0.)
         //--- se a máxima de preço está no nível da extremidade superior
         if(m_base_high_price>=m_upper_zone[1])
           {
            if(IS_PATTERN_USAGE(0))
               result=m_pattern_0;
           }
     }
//--- ou se o canal é largo - negocie na recuperação a partir da margem inferior
   else if(m_ch_width==CHANNEL_WIDTH_BROAD)
     {
      //--- se a mínima de preço é definida
      if(m_base_low_price>0.)
         //--- se a mínima de preço está no nível da extremidade inferior
         if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
           {
            if(IS_PATTERN_USAGE(0))
               result=m_pattern_0;
           }
     }
//---
   return result;
  }
---------//+---------------------------------------------------------+

O método consiste em duas partes. A primeiraanalisa a oportunidade de negociar no rompimento do canal estreito. Nota-se que na variante atual, o rompimento é considerado como o preço atingindo na parte superior da zona do buffer superior. A segunda parte verifica se o preço já tenha entrou na zona do buffer inferior para que a estratégia de recuperação entre em jogo.

O método para verificar a possibilidade de vender — ShortCondition() — é criado por analogia.

A princípio, o método CEquidChannelExpert::Processing() no arquivo continua inalterado EquidistantChannelExpert3.mqh.

Existem 2 parâmetros a serem otimizados.

 Variável ComeçoPasso
Finalização
Canal estreito, em pips
100
20
250
Canal largo, em pips
350
50
1250

Os resultados da otimização podem ser encontrados no arquivo ReportOptimizer-signal3.xml. A melhor execução é apresentada na Fig.8.

 Resultados da estratégia considerando alargura do canal paraFig.8 Resultados da estratégia considerando a largura do canal para 2013-2015.

Fig.8 Resultados da estratégia considerando a largura do canal para2013-2015.


Talvez, este seja o fator de maior impacto entre todas as descrições acima. A curva de equilíbrio tem agora uma direção mais pronunciada.


3.4 Limite do Stop Loss e do Take Profit

Se as metas de negociação estão originalmente presente na forma do Stop Loss e do Take Profit, então deve haver a possibilidade de ajustar estes níveis às condições da estratégia atual. Em suma, se existe um canal que faz o seu caminho através da dinâmica sobre o gráfico em determinado ângulo, os níveis do Stop Loss e do Take Profit devem ser movidos em conjunto com as extremidades do canal.

Um conjunto de modelos foram adicionados por conveniência. Agora assemelham-se a isto:

//--- "pesos" dos modelos do mercado (0-100)
   int               m_pattern_0;         //  Modelo de "Recuperaçao da margem do canal" 
   int               m_pattern_1;         //  Modelo de "Rompimento da margem de canal"
   int               m_pattern_2;         //  Modelo do "Novo canal"

As versões anteriores tinham apenas um, o preço tocando qualquer extremidade do canal. Agora, o modelo de recuperação e de rompimento serão diferenciados. Agora há também o terceiro modelo — novo modelo do canal. É necessário nos casos onde existe um novo canal e também uma posição aberta no canal anterior. Se o modelo for desencadeado, a posição será fechada.

A condição para a compra aparece da seguinte maneira:

---------//+---------------------------------------------------------+
//| Verificar a condição para a compra                               |
---------//+---------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
   bool is_position=PositionSelect(m_symbol.Name());
//--- se o canal é estreito - negocie no rompimento da extremidade superior
   if(m_ch_width_type==CHANNEL_WIDTH_NARROW)
     {
      //--- se a máxima de preço é definida
      if(m_base_high_price>0.)
         //--- se a máxima de preço está no nível da extremidade superior
         if(m_base_high_price>=m_upper_zone[1])
           {
            if(IS_PATTERN_USAGE(1))
              {
               result=m_pattern_1;
               //--- Se não existe uma posição
               if(!is_position)
                  //--- no Diário do terminal
                  if(m_to_log)
                    {
                     Print("\nTriggered the \"Breakout of channel border\" model for buying.");
                     PrintFormat("High price: %0."+IntegerToString(m_symbol.Digits())+"f",m_base_high_price);
                     PrintFormat("Trigger price: %0."+IntegerToString(m_symbol.Digits())+"f",m_upper_zone[1]);
                    }
              }
           }
     }
//--- ou se o canal é largo ou médio - negociar a recuperação da margem inferior
   else
     {
      //--- se a mínima de preço é definida
      if(m_base_low_price>0.)
         //--- se a mínima de preço está no nível da extremidade inferior
         if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
           {
            if(IS_PATTERN_USAGE(0))
              {
               result=m_pattern_0;
               //--- Se não existe uma posição
               if(!is_position)
                  //--- no Diário do terminal
                  if(m_to_log)
                    {
                     );
                     PrintFormat("Low price: %0."+IntegerToString(m_symbol.Digits())+"f",m_base_low_price);
                     PrintFormat("Zone up: %0."+IntegerToString(m_symbol.Digits())+"f",m_upper_zone[0]);
                     PrintFormat("Zone down: %0."+IntegerToString(m_symbol.Digits())+"f",m_upper_zone[1]);
                    }
              }
           }
     }
//---
   return result;
  }
---------//+---------------------------------------------------------+

Além disso, há agora uma verificação da condição de venda:

---------//+---------------------------------------------------------+
//| Verifique a condição para fechar uma compra                      |
---------//+---------------------------------------------------------+
bool CSignalEquidChannel::CheckCloseLong(double &price) const
  {
   bool to_close_long=true;
   int result=0;
   if(IS_PATTERN_USAGE(2))
      result=m_pattern_2;
   if(result>=m_threshold_close)
     {
      if(m_is_new_channel)
         //--- Se uma compra está para ser fechada
         if(to_close_long)
           {
            price=NormalizeDouble(m_symbol.Bid(),m_symbol.Digits());
            //--- no Diário do terminal
            if(m_to_log)
              {
               Print("\nTriggered the \"New channel\" model for closing buy.");
               PrintFormat("Close price: %0."+IntegerToString(m_symbol.Digits())+"f",price);
              }
           }
     }
//---
   return to_close_long;
  }
---------//+---------------------------------------------------------+

Para uma posição de venda, a condição para o fechamento será idêntica.


Agora, algumas palavras sobre o Trailing. Uma classe separada CTrailingEquidChannel foi escrita por isso, com a classe CExpertTrailing como parente.

---------//+---------------------------------------------------------+
//| Classe CTrailingEquidChannel.                                    |
//| Objetivo: Classe do Trailing Stops baseado no Canal Equidistante.|
//|              Deriva da classe CExpertTrailing.                   |
---------//+---------------------------------------------------------+
class CTrailingEquidChannel : public CExpertTrailing
  {
protected:
   double            m_sl_distance;       // distância do stop loss
   double            m_tp_distance;       // distância do take profit
   double            m_upper_val;         // limite superior
   double            m_lower_val;         // limite inferior
   ENUM_CHANNEL_WIDTH_TYPE m_ch_wid_type; // tipo de canal pela largura
   //---
público:
   void              CTrailingEquidChannel(void);
   void             ~CTrailingEquidChannel(void){};
   //--- métodos de inicialização dos dados protegidos
   void              SetTradeLevels(double _sl_distance,double _tp_distance);
   //---
   virtual bool      CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp);
   virtual bool      CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp);
   //---
   bool              RefreshData(const CSignalEquidChannel *_ptr_ch_signal);
  };
---------//+---------------------------------------------------------+

O método para obter a informação do sinal do canal é destacado em vermelho.

Os métodos de controle possibilitam o Trailing para as posições compradas e vendidas, no qual os antecessores foram redefinidos usando polimorfismo - o princípio básico do OOP.

Para a classe de Trailing receber os alvos de tempo e preço do canal atual, foi necessário criar uma ligação com a classe de sinais CSignalEquidChannel. Ele foi implementado no ponteiro constante dentro da classe CEquidChannelExpert. Esta abordagem permite a obtenção de toda a informação necessária a partir do sinal, sem o perigo de alterar os parâmetros do sinal em si.

---------//+---------------------------------------------------------+
//| Classe CEquidChannelExpert.                                      |
//| Objetivo: Classe  para EA de negociação de  canal equidistante.  |
//| Derivado da classe CExper.                                       |
---------//+---------------------------------------------------------+
class CEquidChannelExpert : public CExpert
  {
   //--- === Membros de dados === --- 
privado:
   const CSignalEquidChannel *m_ptr_ch_signal;

   //--- === Métodos === --- 
público:
   //--- construtor/destrutor
   void              CEquidChannelExpert(void);
   void             ~CEquidChannelExpert(void);
   //--- ponteiro para o canal do objeto de sinal
   void              EquidChannelSignal(const CSignalEquidChannel *_ptr_ch_signal){m_ptr_ch_signal=_ptr_ch_signal;};
   const CSignalEquidChannel *EquidChannelSignal(void) const {return m_ptr_ch_signal;};

protected:
   virtual bool      Processing(void);
   //--- verificar fechamento das posições de negociação 
   virtual bool      CheckClose(void);
   virtual bool      CheckCloseLong(void);
   virtual bool      CheckCloseShort(void);
   //--- verificação do trailing stop
   virtual bool      CheckTrailingStop(void);
   virtual bool      CheckTrailingStopLong(void);
   virtual bool      CheckTrailingStopShort(void);
  };
---------//+---------------------------------------------------------+

Os métodos responsáveis pelo fechamento e o trailing também foram redefinidos na classe expert.

A princípio, o método CEquidChannelExpert::Processing() no EquidistantChannelExpert4.mqh tem a seguinte aparência:

---------//+---------------------------------------------------------+
//| Módulo principal                                                 |
---------//+---------------------------------------------------------+
bool CEquidChannelExpert::Processing(void)
  {
//--- cálculo da direção
   m_signal.SetDirection();
//--- Se não existe uma posição
   if(!this.SelectPosition())
     {
      //--- módulo de abertura da posição
      if(this.CheckOpen())
         return true;
     }
//---se existe uma posição
   else
     {
      if(!this.CheckClose())
        {
         //--- verificar se a modificação da posição é viável
         if(this.CheckTrailingStop())
            return true;
         //---
         return false;
        }
      else
        {
         return true;
        }
     }
//--- se não houver operações de negociação
   return false;
  }
---------//+---------------------------------------------------------+

Estes parâmetros serão otimizados:
 Variável ComeçoPasso
Finalização
Stop Loss, em pontos
25
5
75
Take Profit, em pontos50
5
200

Os resultados da otimização podem ser encontrado no arquivo ReportOptimizer-signal4.xml. A melhor execução é apresentada na Fig.9.

Fig.9 Resultados da estratégia no período de 2013-2015.

Fig.9 Resultados da estratégia no período de 2013-2015 2013-2015.


Claramente que este fator - os limites dos níveis de preços - não melhorou o desempenho.


Conclusão

O artigo apresentou o processo de desenvolvimento e implementação de uma classe para envio de sinais com base nos canais móveis. Cada versão do sinal foi seguida de uma estratégia de negociaçao com os resultados dos testes.

Deve-se ressaltar que os valores fixos para as definições do canal equidistante foram utilizados ao longo do artigo. Portanto, as conclusões sobre eficiência de um ou outro fator são verdadeiras somente para os valores determinados.

Ainda existem outras maneiras de melhorar os resultados de desempenho. Este artigo abrange parte do trabalho em encontrar tais possibilidades.


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

Arquivos anexados |
reports.zip (18.06 KB)
base_signal.set (1.54 KB)
channelstrader.zip (253.85 KB)
Trabalhando com cesta de moedas no mercado Forex Trabalhando com cesta de moedas no mercado Forex
O artigo descreve como os pares de moedas podem ser divididos em grupos (cestas), bem como a forma de obter dados sobre o seu status (por exemplo, compradas e vendidas) usando certos indicadores e como aplicar esses dados na negociação.
Interfaces Gráficas VII: O Controle Guias (Capítulo 2) Interfaces Gráficas VII: O Controle Guias (Capítulo 2)
Foi introduzido no primeiro capítulo da sétima parte três classes de controles para a criação de tabelas: Tabela com o rótulo de texto (CLabelsTable), tabela com a caixa de edição (CTable) e a tabela renderizada (CCanvasTable). Neste artigo (capítulo dois) nós vamos introduzir o controle Guias.
Rede neural: Expert Advisor auto-otimizável Rede neural: Expert Advisor auto-otimizável
Será que é possível criar um Expert Advisor que, de acordo com os comandos do código, otimize os critérios de abertura e fechamento das posições automaticamente e em intervalos regulares? O que acontecerá se nós implementarmos no EA uma rede neural (um perceptron multi-camada) que, sendo módulo, analise o histórico e avalie a estratégia? É possível dar ao código um comando para uma otimização mensal (semanal, diária ou por hora) de rede neural com um processo subsequente. Assim, é possível criar um Expert Advisor que se auto-otimize.
Interfaces gráficas VII: O Controle Tabela (Capítulo 1) Interfaces gráficas VII: O Controle Tabela (Capítulo 1)
A sétima parte da série interfaces gráficas no MetaTrader lida com três tipos de tabelas: tabela com o rótulo de texto, tabela com a caixa de edição e a tabela renderizada. Outros controles importantes e frequentemente utilizados são as abas/guias que lhe permitem exibir/ocultar os grupos de outros controles e desenvolver uma interfaces mais compacta em suas aplicações MQL.