Padrões de reversão: Testando o padrão 'Ombro-Cabeça-Ombro'

11 fevereiro 2019, 08:00
Dmitriy Gizlyk
0
1 963

Sumário

Introdução

O artigo Padrões de reversão: Testando o padrão 'topo/fundo duplo' apresentava uma estratégia de negociação baseada no padrão de reversão Topo/Fundo Duplo. Continuando o tópico iniciado, agora, neste artigo, proponho considerar outro padrão de reversão chamado de Ombro-Cabeça-Ombro.


1. Aspectos teóricos da formação de padrões

Os padrões O-C-O (Ombro-Cabeça-Ombro) e O-C-O-I (Ombro-Cabeça-Ombro-Invertido) estão entre os mais conhecidos e amplamente usados na análise gráfica clássica. O nome do padrão em si descreve claramente sua estrutura gráfica. O padrão O-C-O é formado no final de uma tendência de alta e dá sinais para venda de um instrumento. O padrão em si consiste em três picos consecutivos. O pico do médio está acima de dois picos vizinhos, como uma cabeça acima dos ombros. O pico do meio é chamado de cabeça e os outros dois são os ombros. A linha que conecta os fundos entre os picos é chamada de linha do pescoço. Além disso, os sinais do padrão são considerados mais fortes quando a linha do pescoço é inclinada para a esquerda. Além de ser um padrão de alta, o O-C-O-I é uma cópia espelhada do padrão O-C-O.

Padrão 'Ombro-Cabeça-Ombro'

Na maioria das vezes, o padrão é formado quando há um rompimento no nível de suporte/resistência. A seguir, os traders de tendência percebem uma pequena correção a partir do ombro esquerdo como uma boa oportunidade para aumentar posições, levando a um retorno do preço à tendência atual e rompendo o nível do ombro esquerdo. Após penetrar o suporte/resistência atual, a tendência já enfraquecida é quebrada por traders de contra-tendência posicionando, o que leva a uma nova correção. Assim, é formada uma inversão e o preço retorna abaixo do nível novamente (aparece a cabeça). Outra tentativa de retomar a tendência mostra sua fraqueza, é formado um pequeno movimento (ombro direito). Nesse ponto, os participantes do mercado notam mais uma mudança de direção. Os traders de tendência saem de suas posições em masa, enquanto os de contra-tendência posicionam as suas. Isso causa um movimento poderoso, gerando uma nova tendência.


2. Estratégia de negociação usando o padrão

Assim como no caso do padrão 'Topo/Fundo Duplo', para o padrão 'Ombro-Cabeça-Ombro' existem diferentes estratégias. Elas se assemelham às estratégias de negociação para o padrão anterior.

2.1. Cenário 1

A primeira estratégia é baseada no rompimento da linha do pescoço. Neste caso, a ordem é aberta após o preço Close - do tempo gráfico analisado - quebrar a linha. Aqui, o stop-loss é definido no pico da cabeça ou com um pequeno recuo. Adicionalmente, deve-se considerar o spread do instrumento.

Estratégia 1

Uma das desvantagens dessa abordagem é que, durante movimentos bruscos nos preços, a barra pode se fechar longe da linha do pescoço, podendo causar a perda do lucro potencial e aumentar o risco de perda num determinado padrão.

Como uma variação desse cenário, pode-se considerar a abertura de trade após o preço ultrapassar certa distância fixa - a partir da linha do pescoço - em direção a uma tendência emergente.

2.2. Cenário 2

Uma segunda variação é abrir uma posição no recuo de preço em direção à linha do pescoço depois que ela é quebrada. Neste cenário, o stop-loss é definido no último pico, o que o torna mais curto e permite abrir um trade maior com o mesmo risco. O que geralmente aumenta a relação lucro potencial/perda potencial.

Cenário 2

Como no caso do O-C-O, nem sempre é visível o retorno do preço à linha do pescoço após sua penetração. Este fato faz com que seja omitida uma parte significativa dos padrões. Consequentemente, ao usar esse cenário, perdemos parte dos padrões e ficamos fora do mercado mais tempo.

Ao usar ambos os cenários, recomenda-se que o take-profit mínimo seja colocado a uma distância a partir da linha do pescoço igual ao movimento do preço desde a cabeça até a linha do pescoço.

Take-Profit


3. Criando o EA para testar a estratégia

Um leitor atento pode perceber que as abordagens ao negociar usando os padrões Topo/Fundo Duplo e O-C-O são semelhantes. Utilizaremos essa circunstância, tomando o algoritmo do artigo anterior como base para criar um novo EA.

A maior parte do trabalho preparatório já foi realizada por nós no artigo [1]. Para pesquisa de padrões, criamos a classe CHS_Pattern. Visando tirar proveito dos avanços que alcançamos no artigo anterior, iremos torná-la herdeira da classe CPattern. Assim, teremos acesso a todos os métodos da classe pai. Aqui adicionaremos apenas os elementos que faltam e reescreveremos os métodos de inicialização de classe, a pesquisa de padrões e o ponto de entrada.

class CHS_Pattern : public CPattern
  {
protected:
   s_Extremum     s_HeadExtremum;         //Ponto extremo da cabeça
   s_Extremum     s_StartRShoulder;       //Ponto inicial do ombro direito

public:
                     CHS_Pattern();
                    ~CHS_Pattern();
//--- Inicialização da classe
   virtual bool      Create(CTrends *trends, double min_correction, double max_correction);
//--- Métodos de busca de padrão e do ponto de entrada
   virtual bool      Search(datetime start_time);
   virtual bool      CheckSignal(int &signal, double &sl, double &tp1, double &tp2);
//---
   s_Extremum        HeadExtremum(void)      const {  return s_HeadExtremum;     }
   s_Extremum        StartRShoulder(void)    const {  return s_StartRShoulder;   }
  };

No método de inicialização de classe, primeiro inicializaremos a classe pai e depois prepararemos as estruturas adicionadas.

bool CHS_Pattern::Create(CTrends *trends,double min_correction,double max_correction)
  {
   if(!CPattern::Create(trends,min_correction,max_correction))
      return false;
//---
   s_HeadExtremum.Clear();
   s_StartRShoulder.Clear();
//---
   return true;
  }

No método de pesquisa de padrões, primeiro verificaremos se suficiente o número de extremos encontrados para determinar o padrão.

bool CHS_Pattern::Search(datetime start_time)
  {
   if(CheckPointer(C_Trends)==POINTER_INVALID || C_Trends.Total()<6)
      return false;

Em seguida, determinaremos o número de sequência do extremo correspondente à data de início definida para a pesquisa de padrões. Se, após a data especificada, não for formado nenhum extremo, sairemos do método com o resultado false.

   int start=C_Trends.ExtremumByTime(start_time);
   if(start<0)
      return false;

Depois, no ciclo, carregaremos os dados dos últimos 6 extremos e verificaremos se os movimentos de preço correspondem ao padrão desejado. Após encontrado o padrão, sairemos do método com o resultado true. Se o padrão não for encontrado, iremos mover-nos um padrão em direção ao momento atual e repetiremos o ciclo. Se, após iterar todos os extremos, não for encontrado o padrão, sairemos do método com o resultado false.

   b_found=false; 
   for(int i=start;i>=0;i--)
     {
      if((i+5)>=C_Trends.Total())
         continue;
      if(!C_Trends.Extremum(s_StartTrend,i+5) || !C_Trends.Extremum(s_StartCorrection,i+4) ||
         !C_Trends.Extremum(s_EndCorrection,i+3)|| !C_Trends.Extremum(s_HeadExtremum,i+2) ||
         !C_Trends.Extremum(s_StartRShoulder,i+1) || !C_Trends.Extremum(s_EndTrend,i))
         continue;
//---
      double trend=MathAbs(s_StartCorrection.Price-s_StartTrend.Price);
      double correction=MathAbs(s_StartCorrection.Price-s_EndCorrection.Price);
      double header=MathAbs(s_HeadExtremum.Price-s_EndCorrection.Price);
      double revers=MathAbs(s_HeadExtremum.Price-s_StartRShoulder.Price);
      double r_shoulder=MathAbs(s_EndTrend.Price-s_StartRShoulder.Price);
      if((correction/trend)<d_MinCorrection || header>(trend-correction)   ||
         (1-fmin(header,revers)/fmax(header,revers))>=d_MaxCorrection      ||
         (1-r_shoulder/revers)<d_MinCorrection || (1-correction/header)<d_MinCorrection)
         continue;
      b_found= true; 
//---
      break;
     }
//---
   return b_found;
  }

Na próxima etapa, reescreveremos o método de busca do ponto de entrada. No início do método, verificaremos se anteriormente na instância da classe analisada foi encontrado algum padrão. Se o padrão não for encontrado, sairemos do método com o resultado false.

bool CHS_Pattern::CheckSignal(int &signal, double &sl, double &tp1, double &tp2)
  {
   if(!b_found)
      return false;

Em seguida, verificaremos quantas barras foram formadas depois que o padrão foi formado. Se o padrão acabou de ser formado, sairemos do método com o resultado false.

   string symbol=C_Trends.Symbol();
   if(symbol=="Not Initilized")
      return false;
   datetime start_time=s_EndTrend.TimeStartBar+PeriodSeconds(C_Trends.Timeframe());
   int shift=iBarShift(symbol,e_ConfirmationTF,start_time);
   if(shift<0)
      return false;

Depois disso, carregaremos o histórico de cotações necessário e prepararemos variáveis auxiliares.

   MqlRates rates[];
   int total=CopyRates(symbol,e_ConfirmationTF,0,shift+1,rates);
   if(total<=0)
      return false;
//---
   signal=0;
   sl=tp1=tp2=-1;
   bool up_trend=C_Trends.IsHigh(s_EndTrend);
   int shift1=iBarShift(symbol,e_ConfirmationTF,s_EndCorrection.TimeStartBar,true);
   int shift2=iBarShift(symbol,e_ConfirmationTF,s_StartRShoulder.TimeStartBar,true);
   if(shift1<=0 || shift2<=0)
      return false;
   double koef=(s_StartRShoulder.Price-s_EndCorrection.Price)/(shift1-shift2);
   bool break_neck=false;

De seguida, no ciclo, procuraremos uma ruptura na linha do pescoço com o subsequente ajuste de preço. Após quebrada a linha do pescoço, determinaremos os possíveis níveis de lucro. Logo depois, buscando um recuo em relação à linha do pescoço, monitoraremos se o preço atingiu o nível potencial de lucro. Se o preço atingir o nível potencial de take-profit antes de uma reversão em relação à linha do pescoço, o padrão será considerado inválido e sairemos do método com o resultado false. Se for detectado o ponto de entrada, determinaremos o nível de stop-loss e sairemos do método com o resultado true. Convém recordar que o ponto de entrada é considerado válido se não foi formado antes das duas últimas barras do período gráfico analisado, caso contrário, sairemos do método com o resultado false.

   for(int i=0;i<total;i++)
     {
      if(up_trend)
        {
         if((tp1>0 && rates[i].low<=tp1) || rates[i].high>s_HeadExtremum.Price)
            return false;
         double neck=koef*(shift2-shift-i)+s_StartRShoulder.Price;
         if(!break_neck)
           {
            if(rates[i].close>neck)
               continue;
            break_neck=true;
            tp1=neck-(s_HeadExtremum.Price-neck)*0.9;
            tp2=neck-(neck-s_StartTrend.Price)*0.9;
            tp1=fmax(tp1,tp2);
            continue;
           }
         if(rates[i].high>neck)
           {
            if(sl==-1)
               sl=rates[i].high;
            else
               sl=fmax(sl,rates[i].high);
           }
         if(rates[i].close>neck || sl==-1)
            continue;
         if((total-i)>2)
            return false;
//---
         signal=-1;
         break;
        }
      else
        {
         if((tp1>0 && rates[i].high>=tp1) || rates[i].low<s_HeadExtremum.Price)
            return false;
         double neck=koef*(shift2-shift-i)+s_StartRShoulder.Price;
         if(!break_neck)
           {
            if(rates[i].close<neck)
               continue;
            break_neck=true;
            tp1=neck+(neck-s_HeadExtremum.Price)*0.9;
            tp2=neck+(s_StartTrend.Price-neck)*0.9;
            tp1=fmin(tp1,tp2);
            continue;
           }
         if(rates[i].low<neck)
           {
            if(sl==-1)
               sl=rates[i].low;
            else
               sl=fmin(sl,rates[i].low);
           }
         if(rates[i].close<neck || sl==-1)
            continue;
         if((total-i)>2)
            return false;
//---
         signal=1;
         break;
        }
     }   
//---
   return true;
  }

O código completo de todos os métodos e funções pode ser encontrado no anexo.

O código do EA para o teste de estratégias foi retirado do artigo [1], ele está praticamente inalterado. Houve alterações apenas nas partes de substituição da classe para trabalhar com o padrão. O código completo do EA pode ser encontrado no anexo.

Quanto aos resultados, o EA foi testado numa base de 10 meses do ano 2018. Os parâmetros de teste são mostrados nas imagens abaixo.

Parâmetros de teste Parâmetros de teste

Os resultados mostram que o EA tem a capacidade de gerar lucros no intervalo de tempo analisado. Mais de 57% dos trades foram fechados com lucro, além disso, o fator de lucro foi de 2,17. Mas o número de trades desanima, pois em 10 meses apenas foram feitas 14 operações.

Resultados do teste Resultados do teste


4. Combinando dois padrões num só EA

Como resultado do trabalho realizado em dois artigos, obtivemos dois EA lucrativos trabalhando em padrões gráficos de reversão. Por esta razão, é natural que para aumentar a eficiência geral da negociação, eu gostaria de combinar as duas estratégias num só programa. Posso desde já dizer que é com bom motivo que construímos uma nova classe para pesquisar padrões usando o herdeiro da classe anterior. Essa abordagem facilitará muito o nosso trabalho ao juntar as estratégias num só EA.

Primeiro, identificamos as classes. A classe base CObject possui o método virtual Type. Adicionamos este método à descrição de nossas classes. Na classe CPattern, ele retornará o valor 101 e na classe CHS_Pattern - 102. No momento, usamos apenas dois padrões, para que possamos nos restringir a constantes numéricas. Ao aumentar o número de padrões, recomendo usar enumerações, o que melhora a legibilidade do código.

Depois disso, complementamos o método de comparação de classes com um comparador de tipos. Como resultado, o código do método fica assim:

int CPattern::Compare(const CPattern *node,const int mode=0) const
  {
   if(Type()>node.Type())
      return -1;
   else
      if(Type()<node.Type())
         return 1;
//---
   if(s_StartTrend.TimeStartBar>node.StartTrend().TimeStartBar)
      return -1;
   else
      if(s_StartTrend.TimeStartBar<node.StartTrend().TimeStartBar)
         return 1;
//---
   if(s_StartCorrection.TimeStartBar>node.StartCorrection().TimeStartBar)
      return -1;
   else
      if(s_StartCorrection.TimeStartBar<node.StartCorrection().TimeStartBar)
         return 1;
//---
   if(s_EndCorrection.TimeStartBar>node.EndCorrection().TimeStartBar)
      return -1;
   else
      if(s_EndCorrection.TimeStartBar<node.EndCorrection().TimeStartBar)
         return 1;
//---
   return 0;
  }

Assim fica concluída a alteração do código de classes. Agora procedemos à alteração do código do EA. Aqui também haverá alguns complementos. Primeiro, criamos uma função que gera uma nova instância da classe correspondente, dependendo do parâmetro recebido. O código desta função é bastante simples e consiste apenas em usar o comutador switch que chama o método para criação da classe requerida.

CPattern *NewClass(int type)
  {
   switch(type)
     {
      case 0:
        return new CPattern();
        break;
      case 1:
        return new CHS_Pattern();
        break;
     }
//---
   return NULL;
  }

As seguintes alterações são feitas na função OnTick. Nesse caso, as alterações afetam apenas o bloco de pesquisa de novos padrões. O bloco para encontrar pontos para abrir posições em padrões já encontrados não requer mudanças devido à herança de classes.

Aqui realizamos um ciclo no qual procuramos padrões no histórico sequencialmente, primeiro de um tipo e depois de outro. Para fazer isso, nos pontos de criação de uma nova instância da classe, substituímos a chamada da função que acabamos de adicionar, e nos parâmetros passamos o número da passagem de ciclo atual. O próprio algoritmo do EA é mantido inalterado.

void OnTick()
  {
//---
.........................
.........................
.........................
//---
   for(int pat=0;pat<2;pat++)
     {
      Pattern=NewClass(pat);
      if(CheckPointer(Pattern)==POINTER_INVALID)
         return;
      if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
        {
         delete Pattern;
         continue;
        }
//---
      datetime ss=start_search;
      while(!IsStopped() && Pattern.Search(ss))
        {
         ss=fmax(ss,Pattern.EndTrendTime()+PeriodSeconds(e_TimeFrame));
         bool found=false;
         for(int i=2;i<ar_Objects.Total();i++)
           {
            CPattern *temp=ar_Objects.At(i);
            if(Pattern.Compare(temp,0)==0)
              {
               found=true;
               break;
              }
           }
         if(found)
            continue;
         if(!CheckPattern(Pattern))
            continue;
         if(!ar_Objects.Add(Pattern))
            continue;
         Pattern=NewClass(pat);
         if(CheckPointer(Pattern)==POINTER_INVALID)
            break;
         if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
           {
            delete Pattern;
            break;
           }
        }
      if(CheckPointer(Pattern)!=POINTER_INVALID)
         delete Pattern;
     }
//---
   return;
  }

Isso conclui as alterações do código do EA. O código completo do novo EA pode ser encontrado no anexo, no arquivo TwoPatterns.mq5.

Depois de fazer as alterações necessárias, realizaremos testes com os mesmos parâmetros.

Parâmetros de teste Parâmetros de teste

Os resultados do teste mostram como se complementam as estratégias. Durante o período de testes, o EA fez 128 trades, dos quais mais de 60% foram fechados com lucro. Como resultado, o fator de lucro do trabalho do EA foi de 1,94 e o fator de recuperação - de 3,85. Os resultados completos dos testes são mostrados nas imagens abaixo.

Resultados do testeResultados do teste

5. Testando o sistema de negociação

Para testar a estabilidade do nosso sistema de negociação, foi testado um EA em 6 instrumentos sem alterar os parâmetros do teste.

InstrumentoLucroFator de lucro
EURUSD743.571.94
EURJPY125.131.47
GBPJPY
33.931.04
EURGBP-191.7
0.82
GBPUSD-371.050.60
USDJPY
-657.380.31

Como pode ser visto nos resultados mostrados na tabela, o EA lucrou em metade dos instrumentos testados, enquanto nos restantes teve perdas. Este fato confirma mais uma vez que os gráficos de preços de cada instrumento são individuais e, antes de utilizar o EA, é necessário otimizá-lo de acordo com condições específicas. Por exemplo, a otimização do parâmetro "Stop Loss Backstep" permitiu aumentar os lucros em 4 instrumentos e reduzir as perdas em 2 instrumentos. O que, em geral, aumentou a rentabilidade em toda a cesta de instrumentos. Os resultados são mostrados na tabela abaixo.

InstrumentoLucroFator de lucroStop Loss Backstep
EURUSD1020.281.78350
EURJPY532.541.52400
GBPJPY208.691.17300
EURGBP91.451.05450
GBPUSD-315.870.55100
USDJPY-453.080.33100


6. Complementando sinais de negociação em instrumentos não rentáveis

De acordo com os resultados de teste de nosso EA no capítulo anterior, dois instrumentos extravasaram do estabelecido e não foi possível fazer com que seus resultados se tornassem positivos, nomeadamente os pares GBPUSD e USDJPY. Isso pode indicar uma força insuficiente dos padrões considerados relativamente à reversão da tendência desses instrumentos. O rebaixamento no saldo devido a um movimento constante negativo faz pensar que o trade vira após recebido o sinal.

Quais os objetivos a serem definidos para um trade de reversão e onde definir o stop-loss? Para responder a essas perguntas, consideramos o resultado de testes anteriores. Os testes mostraram que os trades abertos eram frequentemente fechados com um stop-loss antes de poderem alcançar o nível do primeiro take-profit. Consequentemente, virando o trade, podemos trocar de posição o stop-loss e o take-profit 1.

O estudo detalhado dos gráficos de trades não lucrativos mostrou que, na faixa de preço dos sinais falsos, são formados canais frequentemente. Na análise gráfica clássica, os canais são as formas da continuação da tendência. Por essa razão, podemos esperar um movimento de preços compatível com o movimento anterior. Ao usar o código do método CheckSignal da nossa classe de pesquisa de padrões, é fácil ver que o tamanho do movimento anterior é fixado pelo take-profit 2. Tudo o que precisamos é definir o take-profit 2 à mesma distância do preço atual, só que numa direção diferente.

Essa abordagem nos dá a oportunidade de mudar o sentido do trade, fazendo alterações apenas no código do EA, sem alterar o código das classes de pesquisa de padrões.

Para implementar essa funcionalidade, adicionamos ao nosso EA mais outro parâmetro ReverseTrade, que servirá como um sinalizador para ativar/desativar a função de negociação de reversão.

input bool  ReverseTrade   =  true; //Reverse Deals

O fato de adicionar um parâmetro não altera a lógica da abertura de trades. Façamos alterações na função CheckPattern. No bloco para declarar variáveis locais, adicionamos uma variável temp que vamos usar como repositório temporário ao mudar o stop-loss e take-profit 1.

bool CheckPattern(CPattern *pattern)
  {
   int signal=0;
   double sl=-1, tp1=-1, tp2=-1;
   if(!pattern.CheckSignal(signal,sl,tp1,tp2))
      return false;
//---
   double price=0;
   double to_close=100;
   double temp=0;
//---

Em seguia, no corpo do comutador switch adicionamos uma verificação do status do sinalizador ReverseTrade. Se o sinalizador estiver com o status false, usamos a lógica antiga. Ao usar negociação de reversão, alteramos o valor de stop-loss e take-profit 1. Em seguida, recalculamos os valores de take-profit em pontos do instrumento e transferimos os valores obtidos para a classe CLimitTakeProfit. Após a conclusão bem-sucedida de todas as iterações, abrimos um trade oposto ao sinal recebido.

//---
   switch(signal)
     {
      case 1:
        CLimitTakeProfit::Clear();
        if(!ReverseTrade)
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
           if((tp1-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(CLimitTakeProfit::AddTakeProfit((uint)((tp1-price)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100)))
                 to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100);
           if(to_close>0 && (tp2-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(!CLimitTakeProfit::AddTakeProfit((uint)((tp2-price)/_Point),to_close))
                 return false;
           if(Trade.Buy(d_Lot,_Symbol,price,sl-i_SL*_Point,0,NULL))
              return false;
          }
        else
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
           temp=tp1;
           tp1=sl-i_SL*_Point;
           sl=temp;
           tp1=(price-tp1)/_Point;
           tp2=(tp2-price)/_Point;
           if(tp1>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(CLimitTakeProfit::AddTakeProfit((uint)(tp1),((tp2-tp1)>=1? 50 : 100)))
                 to_close-=((tp2-tp1)>=1 ? 50 : 100);
           if(to_close>0 && tp2>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(!CLimitTakeProfit::AddTakeProfit((uint)(tp2),to_close))
                 return false;
           if(Trade.Sell(d_Lot,_Symbol,price,sl,0,NULL))
              return false;
          }
        break;
      case -1:
        CLimitTakeProfit::Clear();
        if(!ReverseTrade)
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
           if((price-tp1)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(CLimitTakeProfit::AddTakeProfit((uint)((price-tp1)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100)))
                 to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100);
           if(to_close>0 && (price-tp2)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(!CLimitTakeProfit::AddTakeProfit((uint)((price-tp2)/_Point),to_close))
                 return false;
           if(Trade.Sell(d_Lot,_Symbol,price,sl+i_SL*_Point,0,NULL))
              return false;
          }
        else
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
           temp=tp1;
           tp1=sl+i_SL*_Point;
           sl=temp;
           tp1=(tp1-price)/_Point;
           tp2=(price-tp2)/_Point;
           if(tp1>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(CLimitTakeProfit::AddTakeProfit((uint)(tp1),((tp2-tp1)>=1 ? 50 : 100)))
                 to_close-=((tp2-tp1)>=1 ? 50 : 100);
           if(to_close>0 && tp2>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(!CLimitTakeProfit::AddTakeProfit((uint)(tp2),to_close))
                 return false;
           if(Trade.Buy(d_Lot,_Symbol,price,sl,0,NULL))
              return false;
          }
        break;
     }
//---
   return true;
  }

Isso conclui o complemento do código do EA. O código completo de todas as classes e funções pode ser encontrado no anexo.

Testemos a função para a negociação de reversão. O primeiro teste será no par GBPUSD e será usado o mesmo período gráfico e intervalo de tempo. Os parâmetros do EA são mostrados nas imagens abaixo.

GBPUSD teste de reversãoGBPUSD teste de reversão

Os resultados do teste mostraram o sucesso dessa decisão. No intervalo de tempo testado, o EA com a função de negociação de reversão ativada mostrou um lucro. Dos 127 trades, 95 (75,59%) fecharam com lucro. O fator de lucro foi de 2,35. Os resultados completos dos testes são mostrados nas imagens abaixo.

GBPUSD resultado do teste de reversãoGBPUSD resultado do teste de reversão

Para consolidar os resultados, realizaremos um teste similar no par USDJPY. Os parâmetros de teste são mostrados nas capturas de tela.

USDJPY teste de reversãoUSDJPY teste de reversão

O segundo teste da função de reversão também se mostrou bem-sucedido. Dos 108 trades, 66 (61,11%) foram fechados com lucro, e o fator lucro foi de 2,45. Os resultados completos dos testes são mostrados nas imagens.

USDJPY resultado do teste de reversãoUSDJPY resultado do teste de reversão

O teste mostrou que, devido à viragem de posições, as estratégias não lucrativas podem se tornar rentáveis. Mas deve ser lembrado que nem sempre uma estratégia que é rentável em certo instrumento traz lucro em outro.


Fim do artigo

O trabalho realizado no artigo demonstra a viabilidade de estratégias baseadas no uso de padrões clássicos de análise gráfica. Como resultado, conseguimos um EA que pode gerar lucros durante um longo período em vários instrumentos. No entanto, ao usar estratégias de negociação, devem ser levadas em consideração as especificidades do instrumento, uma vez que uma estratégia pode ser ou não ser lucrativa dependendo das condições. Às vezes, reverter o sentido dos trades ajuda a corrigir a situação. Mas é necessário levar em conta as condições de negociação.

É claro que esses são apenas os primeiros passos na construção de um sistema lucrativo. Adicionalmente, é possível otimizar outros aspectos do EA complementando sua funcionalidade (transferência do stop-loss para breakeven, trailing-stop, etc.). Obviamente, não recomendaria usar o EA em instrumentos que já sabemos que não são lucrativos.

Espero que minha experiência ajude o leitor a construir sua própria estratégia de negociação.

É óbvio que o EA apresentado no artigo apenas mostra como funciona da estratégia, e, portanto, deverá ser melhorado se for usado em mercados reais.

Links

  1. Padrões de reversão: Testando o padrão 'topo/fundo duplo'
  2. Implementando Take Profit na forma de ordens limitadas sem alterar o código original do EA

Programas utilizados no artigo:

# Nome Tipo Descrição
1 ZigZag.mqh Biblioteca de classe Classe do indicador Zig Zag
2 Trends.mqh Biblioteca de classe Classe de busca de tendências
3 Pattern.mqh Biblioteca de classe Classe para trabalhar com o padrão 'Topo/Fundo Duplo'
4 HS_Pattern Biblioteca de classe Classe para trabalhar com o padrão 'Ombro-Cabeça-Ombro'
5 LimitTakeProfit.mqh Biblioteca de classe Classe para substituir take-profit por ordens limitadas
6 Header.mqh Biblioteca Arquivo de cabeçalho do EA
7 Head-Shoulders.mq5 Expert Advisor EA baseado na estratégia 'Ombro-Cabeça-Ombro'
8 TwoPatterns.mq5 Expert Advisor EA com o desenvolvimento de dois padrões

Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/5358

Arquivos anexados |
MQL5.zip (1352.35 KB)
Aplicando a teoria da probabilidade na negociação de gaps Aplicando a teoria da probabilidade na negociação de gaps

Neste artigo, nós aplicaremos a teoria da probabilidade e métodos da estatística matemática para criar e testar estratégias de negociação. Nós também veremos o risco de negociação ótimo usando as diferenças entre o preço e o passeio aleatório. Está provado que, se os preços se comportarem como um passeio aleatório de deslocamento de zero (sem tendência direcional), então a negociação com lucro é impossível.

Reversão: criemos um ponto de entrada e programemos um algoritmo de negociação manual Reversão: criemos um ponto de entrada e programemos um algoritmo de negociação manual

Este é o último artigo da série sobre estratégia de reversão. Nele, tentaremos resolver um problema que levou a resultados inconsistentes relativamente a testes em artigos anteriores. Adicionalmente, escreveremos e testaremos nosso próprio algoritmo para negociar manualmente usando a estratégia de reversão em qualquer mercado.

Desenvolvimento de um utilitário de navegação e seleção de símbolos em MQL5 e MQL4 Desenvolvimento de um utilitário de navegação e seleção de símbolos em MQL5 e MQL4

Traders experientes estão bem cientes do fato de que a maioria das coisas demoradas na negociação não são abrir e monitorar posições, mas sim selecionar símbolos e procurar pontos de entrada. Neste artigo, nós vamos desenvolver um EA simplificando a busca por pontos de entrada em instrumentos de negociação fornecidos pela sua corretora.

Como criar e testar símbolos de ativos MOEX personalizados no MetaTrader 5 Como criar e testar símbolos de ativos MOEX personalizados no MetaTrader 5

O artigo descreve a criação de um símbolo de ativo personalizado da bolsa de valores usando a linguagem MQL5, em particular, descreve o uso de cotações no popular site "Finam". Outra opção considerada neste artigo é a possibilidade de trabalhar com um formato arbitrário de arquivos de texto, usados na criação do símbolo personalizado. Isso permite trabalhar com quaisquer símbolos financeiros e fontes de dados, depois de criar um símbolo personalizado, podemos usar todos os recursos do Testador de Estratégia do MetaTrader 5 a fim de testarmos os algoritmos de negociação para os instrumentos da bolsa.