Indicador NRTR e módulos de negociação baseados nele para o Assistente MQL5

Dmitrii Troshin | 8 janeiro, 2018


Introdução

Estudaremos um indicador que constrói um canal de preço dinâmico. Um EA é criado com base neste canal. Sabe-se que sistemas desse tipo funcionam bem durante tendências, mas dão muitos sinais falsos em movimentos laterais. Portanto, para melhorar os resultados, são necessários indicadores de tendência adicionais. A escolha deste indicador não é uma tarefa fácil, pois, muitas vezes, depende de condições específicas do mercado. Portanto, uma boa solução é encontrar uma maneira de conectar rapidamente qualquer indicador ao sistema de negociação utilizado.

Para fazer isso, usamos a seguinte abordagem. Criamos, para nosso indicador, um módulo de sinais de negociação especial para o Assistente MQL5. No futuro, para todos os indicadores escolhidos por nós, poder-se-a criar rapidamente um módulo semelhante que forneça apenas os sinais "Sim"/"Não" sobre a presença ou ausência de uma tendência. Como, na construção do sistema de negociação, é permitido usar vários módulos, podemos facilmente realizar quaisquer combinações de indicadores.

Indicador NRTR

Indicador NRTR (Nick Rypock Trailing Reverse) é um indicador pensado por Konstantin Kopyrkin. Um dado curioso: o nome Nick Rypock é derivado do sobrenome Kopyrkin escrito para trás. 

Voltemos ao indicador. Ele é um canal de preço dinâmico. O autor ilustra sua ideia base com a seguinte figura:


O sistema de negociação baseado no indicador NRTR é uma estratégia de rompimento. Um sinal de compra é gerado quando o preço excede o máximo anterior por um determinado período; um sinal de venda é gerado quando o preço cai abaixo do mínimo. Durante a mudança de tendência, esses sistemas podem usar preços da tendência anterior, quer dizer, máximos e mínimos anteriores. Para evitar isso, em nosso sistema, o período de cálculo é definido dinamicamente.

O próprio autor define o NRTR como um indicador de tendência de rompimento do canal de preço dinâmico.

O princípio de operação do indicador é o seguinte: na tendência de alta, a linha do indicador (canal) está posicionada num certo nível abaixo do máximo, no intervalo de tempo especificado. A linha de queda está posicionada acima dos preços, a uma distância constante do mínimo, num determinado intervalo de tempo.

Além disso, o período do canal de preço utilizado para o cálculo do indicador é aumentado de forma dinâmica, desde o início da tendência. Com esta abordagem, o preço do período de cálculo anterior não afeta o indicador.

A figura mostra como o indicador primeiro segue a tendência a uma certa distância. Logo, ele se posiciona a uma distância fixa dos máximos locais H1 e H2. O máximo local H3 é inferior ao anterior e não é usado no cálculo.

Então, o preço quebra o canal no ponto L3. Este é um sinal de vende. O valor do ponto L3 se torna o novo mínimo. O novo período começa neste ponto, ou seja, todos os preços anteriores são descartados e não são usados ​​em cálculos. À medida que a tendência se desenvolve, o mínimo é atualizado para L3-L4-L5. O período do canal de preço dinâmico cresce até a tendência mudar ou o comprimento do período atingir o valor máximo definido.

A largura do canal é calculada como uma porcentagem do valor extremo ou pode depender da volatilidade dos preços. Ambas as abordagens são implementadas neste artigo.

Um sinal de compra/venda é gerado quando o preço rompe a linha do canal. Se o preço romper a linha de suporte, será gerado um sinal de compra. Se o preço romper a linha de resistência, será gerado um sinal de venda.

Agora, precisamos traduzir os princípios de operação do indicador em linguagem MQL5. Vamos a isso.

Escrevemos um indicador: do simples ao complexo

Primeiro de tudo, definimos o comportamento do indicador. Ele será construído com base nos preços de fechamento close. É claro que a construção em dados históricos será interpretada de forma inequívoca. Mas e se o preço romper a linha de suporte/resistência numa vela incompleta? Nesta versão, a tendência não muda e, portanto, nenhum sinal é fornecido até a vela não se formar completamente. Por um lado, podemos perder uma parte do movimento. Por exemplo, se o movimento começar com uma enorme vela rompendo o canal, abriremos a posição só na próxima vela. Por outro lado, dessa maneira, estamos nos protegendo de inúmeros rompimentos falsos.

N.B.: este indicador tem muitas variações; este artigo descreve a versão original proposta pelo autor.

Uma implementação do indicador está disponível no CodeBase, mas seu período é apenas parcialmente dinâmico. O período é reiniciado quando a tendência muda, mas, em teoria, ele pode crescer indefinidamente. Isto é, a linha de suporte é calculada como MathMax() do valor anterior e o preço close atual. Nesse caso, a linha de suporte só pode subir, enquanto a linha de resistência está sempre caindo. Na versão original, todos os valores anteriores são considerados como obsoletos e, portanto, são ignorados. Aqui max/min são calculados como ArrayMaximum/Minimum(close,i,dynamic_period). Nesta abordagem, as linhas de suporte e resistência podem estar subindo e caindo. Portanto, a linha de suporte pode diminuir profundamente em alguns casos em movimentos laterais lentos, quando os períodos dinâmicos são curtos. Mas essas tendências suaves a longo prazo são raras, e não existem métodos ideais. E, como já mencionado acima, o nosso princípio base é manter a intenção original do autor.

O próximo ponto é a série temporal. Em MQL5, as matrizes de preços (close) têm o valor padrão ArraySetAsSeries = false. Em MQL4, as séries de preços tinham sinalizador de TimeSeries, e Close[0] era o preço de fechamento da barra mais à direita (enquanto a barra mais à esquerda normalmente não pode ser vista). Repare que neste artigo é ArraySetAsSeries(close,true).

Passemos agora à implementação do indicador. Trabalharemos com quatro buffers de indicador: dois deles serão usados ​​para linhas de suporte/resistência, e os outros dois, para sinais de compra e venda.

#property indicator_chart_window
#property indicator_buffers 4 
#property indicator_plots   4

//Indicators lienes style
#property indicator_type1  DRAW_LINE
#property indicator_color1 Green
#property indicator_style1 STYLE_DASH

#property indicator_type2  DRAW_LINE
#property indicator_color2 Red
#property indicator_style2 STYLE_DASH

#property indicator_type3  DRAW_ARROW
#property indicator_color3 Green

#property indicator_type4  DRAW_ARROW
#property indicator_color4 Red

Declaramos os buffers de indicador e parâmetros externos do indicador

input int    period =12;      //período dinâmico
input double percent =0.2;    //porcentagem de recuo

double Buff_Up[],Buff_Dn[];  
double Sign_Up[],Sign_Dn[];

Os sinais aparecerão como setas. Os parâmetros restantes são definidos na função OnInit(). Eu uso DRAW_ARROW com parâmetro 236,238 da fonte Wingdings. Parâmetros para o sinal "ascendente":

   SetIndexBuffer(2,Sign_Up,INDICATOR_DATA);
   PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetInteger(2,PLOT_ARROW,236);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,1);
   ArraySetAsSeries(Sign_Up,true);

No início dos cálculos, na função OnCalculate(), são verificados a disponibilidade dos dados e a o início do primeiro cálculo do indicador.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
{
  
  int start =0;                                           //ponto de cálculo
  
  int trend =0;                                           //valor da tendência ascendente 1, descendente -1
  static int trend_prev =0;
  
  double value =0;                                        //valor do indicador
  static double value_prev =0;
  
  int dyn_period =1;                                     //valor do período                                    
  static int curr_period =1;
  
  
  double maxmin =0;                                       //variável técnica para os cálculos
  
  ArraySetAsSeries(close,true);
  
  if(rates_total<period) return(0);
  
     if(prev_calculated==0)                              // verificação do início primeiro cálculo do indicador
  {
      start=rates_total-1;                               // número inicial para o cálculo de todas as barras
      trend_prev =1;
      value=close[start]*(1-0.01*percent);      
  }
  
  else
     {
      start=rates_total-prev_calculated;                  // número inicial para o cálculo das barras novas
     }

trend =trend_prev;
value =value_prev;
dyn_period =curr_period;

As principais variáveis ​​também são definidas aqui. Para os valores da tendência e do canal, são definidas duas variáveis, um das quais é estática. A variável estática armazenará o valor até o próximo ciclo de cálculo. A variável só mudará numa barra completamente formada. Se o preço romper o canal numa barra não formada, serão alteradas apenas as variáveis ​​locais não estáticas. Se o preço retornar ao canal, a tendência anterior será mantida.

Agora, vamos escrever o ciclo de cálculos principal levando em consideração as observações descritas acima.

trend =trend_prev;
value=value_prev;
dyn_period =curr_period;    
//-------------------------------------------------------------------+
//                        Ciclo de cálculo principal   
//-------------------------------------------------------------------+  
for(int i=start;i>=0;i--)
{
    Buff_Up[i] =0.0;
    Buff_Dn[i] =0.0;
    Sign_Up[i] =0.0;
    Sign_Dn[i] =0.0;
    
    if(curr_period>period) curr_period=period;
    if(dyn_period>period) dyn_period=period;
    
 //if trend ascending   
    if(trend>0)
    {
    maxmin =close[ArrayMaximum(close,i,dyn_period)];
    value =maxmin*(1-percent*0.01);
    
    if(close[i]<value)
      {
      maxmin =close[i];
      value =maxmin*(1+percent*0.01);
      trend =-1;
      dyn_period =1;
      }
    }
  
//  if trend descending
    else
    {
    maxmin =close[ArrayMinimum(close,i,dyn_period)];
    value =maxmin*(1+percent*0.01);
    if(close[i]>value)
      {
      maxmin =close[i];
      value =maxmin*(1-percent*0.01);
      trend =1;
      dyn_period =1;
      }
    }  
 // trend changes 
  
      if(trend>0) Buff_Up[i] =value;
      if(trend<0) Buff_Dn[i] =value;

      if(trend_prev<0  &&  trend>0) 
      {
      Sign_Up[i] =value;
      Buff_Up[i] =0.0;
      }
      if(trend_prev>0 && trend<0)
      {
      Sign_Dn[i] =value;
      Buff_Dn[i] =0.0;
      }

  dyn_period++;
  
  if(i)
  {
  trend_prev =trend;
  value_prev =value;
  if(dyn_period==2)curr_period =2;
  else curr_period++;
  }

}

O período dinâmico neste ciclo é limitado ao valor especificado. Quando o preço de fechamento quebra o canal, são encontrados novos valores de suporte e resistência, e é verificado se a tendência tem mudado. O último operador if() verifica se a barra está totalmente formada. Somente se a barra estiver completamente formada, são atualizados os valores trend_prev e value_prev, e, portanto, pode ser gerado um sinal de compra/venda. O período dinâmico também pode ser redefinido aqui.

O código completo do indicador está disponível no arquivo anexado NRTR.mq5.

Confiramos o trabalho do indicador colocando no gráfico dois NRTR com diferentes parâmetros: o primeiro NRTR tem período de 12 e largura de 0.1%; o segundo, período de 120 e largura de 0.2%.


A figura mostra que se o período é pequeno, a linha de suporte pode subir ou descer. Isto acontece devido ao fato de os valores dos preços "caírem fora" do período dinâmico. Se o período for grande, a linha de suporte geralmente não é decrescente.

Volatilidade e NRTR

Na abordagem anterior, usamos uma porcentagem fixa de desvio do canal de preço. Seria mais lógico se o corredor se expandir com o aumento da volatilidade e se contrair, se ela diminuir. ATR (average true range) é um indicador popular para determinar a volatilidade do mercado. O valor ATR pode ser usado para definir a largura do canal. Pode ser calculado de forma independente ou pode ser usado o indicador técnico padrão do terminal.

Vamos substituir a porcentagem de desvio pelo valor do indicador ATR, para vincular a largura do canal à volatilidade do mercado. Um coeficiente ainda será usado para dimensionamento. Por padrão, ele será igual a 1. Para o indicador ATR, declaramos mais um buffer de indicador double Buff_ATR[]. O parâmetro percentual é substituído pelo coeficiente K =1. Vamos criar um ponteiro para o ATR, a fim de receber seus valores:

handle_atr =iATR(_Symbol,PERIOD_CURRENT,period);

O período do ATR pode diferir do período dinâmico ativo. Uma solução lógica é torná-los iguais, de modo que o número de parâmetros não seja alterado.

Aqui está o código das linhas recém-adicionadas.

#property indicator_buffers 5 
#property indicator_plots   4
.............................
input double K =1;            //coeficiente de escala
double Buff_ATR[];
int handle_atr;
.............................
SetIndexBuffer(4,Buff_ATR,INDICATOR_CALCULATIONS);
ArraySetAsSeries(Buff_ATR,true);
         
handle_atr =iATR(_Symbol,PERIOD_CURRENT,period);
.....................................................
int OnCalculate(){
.....................................................
  if(CopyBuffer(handle_atr,0,0,start+1,Buff_ATR)==-1)
  {
  return(0);
  Print("Falha ao copiar os dados para o buffer do ATR");
  }
.....................................................

//if trend ascending  
  if(trend>=0)
  {
  maxmin =close[ArrayMaximum(close,i,dyn_period)];
  value =maxmin-K*Buff_ATR[i];
  
  if(close[i]<value)
   {
   maxmin =close[i];
   value =maxmin+K*Buff_ATR[i];
   trend =-1;
   dyn_period =1;
   }
  }
 
}

Os valores da linha do canal são calculados como value = maxmin(+-)K*Buff_ATR[i], respectivamente. O código completo do indicador está disponível no arquivo NRTRvolatile.mq5 anexado.

Vamos executar ambos os indicadores com os mesmos parâmetros num gráfico e comparamos seu comportamento.


A figura mostra que se a volatilidade é baixa e os valores ATR são pequenos, a linha NRTRvolatile é quase "encaixada" no gráfico de preços. Logo, a linha se afasta do preço quando a volatilidade aumenta.

Agora passamos a criar um Expert Advisor baseado nesse indicador. Como mencionado acima, usaremos um módulo de sinais de negociação.

Módulo de negociação para o Assistente MQL5

Às vezes, é mais conveniente escrever esses módulos com base em métodos existentes usando o método copiar/colar Mas, no nosso caso, é melhor começar desde o início, em vez de descrever todos os lugares a serem substituídos ou modificados.

Descrevemos a estrutura geral de todos os módulos.

  • Descritor do módulo
  • Parâmetros de negociação e funções para sua inicialização
  • Verificação de parâmetros de entrada
  • Vinculação do indicador selecionado ao módulo
  • Descrição da estratégia de negociação

Primeiro de tudo, na pasta de sinais, criamos uma subpasta separada, para armazenar nossos próprios sinais. Por exemplo, Include\Expert\MySignals. Clicamos com o botão direito do mouse na pasta selecionada escolhemos "Novo arquivo" no menu de contexto. Aparece o assistente MQL5. No menu, selecionamos "Nova classe". Ele será chamado NRTRsignal. Todos os sinais são herdados da classe base CExpertSignal, indicamos isto no assistente.


Ao código gerado pelo assistente, adicionamos a localização da classe base CExpertSignal: #include "..\ExpertSignal.mqh"

//+------------------------------------------------------------------+
//|                                                   SignalNRTR.mqh |
//|                                                       Orangetree |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Orangetree"
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "..\ExpertSignal.mqh"                  // classe CExpertSignal 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class SignalNRTR : public CExpertSignal
  {
private:

public:
                     SignalNRTR();
                    ~SignalNRTR();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
SignalNRTR::SignalNRTR()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
SignalNRTR::~SignalNRTR()
  {
  }
//+------------------------------------------------------------------+

Esse foi o começo.

Agora precisamos criar um descritor de módulo para que o Assistente MQL5 possa reconhecer nosso código como um módulo de sinais.

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signals of indicator 'NRTR'                                |
//| Type=SignalAdvanced                                              |
//| Name=NRTR                                                        |
//| ShortName=NRTR                                                   |
//| Class=SignalNRTR                                                 |
//| Page=????                                                        |
//| Parameter=PeriodDyn,int,12,Período do canal dinâmico           |
//| Parameter=PercentDev,double,0.1,Largura do corredor em porcentagem      |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class SignalNRTR.                                                |
//| Purpose: Class of generator of trade signals based on            |
//|          the 'NRTR' indicator.                                   |
//| Is derived from the CExpertSignal class.                         |
//+------------------------------------------------------------------+

O descritor começa com "wizard description start" e termina com " wizard description end". O nome do módulo e os parâmetros externos estão dentro dele. Assim que compilamos o módulo juntamente com o descritor, ele será adicionado ao menu do Assistente - Novo arquivo/Expert Advisor(gerar)/Parâmetros comuns/Propriedades de sinal do Expert Advisor/Adicionar.

Módulo

À nossa classe vamos adicionar as variáveis ​​para armazenar parâmetros e métodos externos para inicializá-las.

Os nomes dos métodos para inicializar parâmetros externos devem corresponder aos nomes dos parâmetros externos escritos no descritor.

class SignalNRTR : public CExpertSignal
  {
protected:
   int m_period_dyn;                                   //Período do canal
   double m_percent_dev;           //Largura do canal como uma porcentagem do preço
 
public:
                     SignalNRTR();
                    ~SignalNRTR();
   //--- methods of setting adjustable parameters
   void              PeriodDyn(int value)                 { m_period_dyn=value;}
   void              PercentDev(double value)             { m_percent_dev=value;}
   
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
SignalNRTR::SignalNRTR() : m_period_dyn(12),
                           m_percent_dev(0.1)
  {
  //--- initialization of protected data
   m_used_series=USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
  }

Os membros da classe são inicializados usando uma lista de inicialização. Os métodos "private" que foram gerados pelo Assistente podem ser substituídos por "protected", no entanto, não é necessário.

O método virtual Bool ValidationSettings() na classe CExpertBase permite verificar a entrada de parâmetros.

Precisamos adicionar à nossa classe o protótipo do método e substituí-lo. Por exemplo, precisamos verificar se o período é maior que um e o valor percentual do desvio é positivo.

//+------------------------------------------------------------------+
//| O método verifica os parâmetros de entrada                       |
//+------------------------------------------------------------------+
bool SignalNRTR:: ValidationSettings()
  {
   // chamamos o método da classe base
   if(!CExpertSignal::ValidationSettings())  return(false);
   
   // o período deve ser superior a 1
   if(m_period_dyn<2)
   {
   Print("Período deve ser superior a 1");
   return false;
   }
   // A largura do corredor deve ser positiva
   if(m_percent_dev<=0)
   {
   Print("A largura do corredor deve ser positiva");
   return false;
   }
   
   return true;
  }

Observe que o método da classe base é chamado primeiro.

Em seguida, usamos o método InitIndicators() para conectar certo indicador ao nosso módulo. Criamos um protótipo deste método em nossa classe: virtual bool InitIndicators(CIndicators *indicators), e, em seguida, adicionamos sua descrição. Usaremos procedimentos padrão para verificar os ponteiros dos indicadores e inicializar indicadores e timeseries em filtros adicionais.

//+------------------------------------------------------------------+
//| Create indicators.                                               |
//+------------------------------------------------------------------+
bool SignalNRTR::InitIndicators(CIndicators *indicators)
   {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);  
//--- create and initialize NRTR indicator
if(!InitNRTR(indicators))
      return(false);
//--- ok
   return(true);
   }

Na linha InitNRTR(indicators), criamos e inicializamos nosso indicador. Também é necessário adicionar o protótipo e descrição da função InitNRTR(indicators).

//+------------------------------------------------------------------+
//| Create NRTR indicators.                                          |
//+------------------------------------------------------------------+  
bool SignalNRTR::InitNRTR(CIndicators *indicators)
   {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- add object to collection
   if(!indicators.Add(GetPointer(m_nrtr)))
     {
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
//--- criação de parâmetros do NRTR
   MqlParam parameters[3];
//---
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="Orangetree\\NRTR.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_period_dyn;      // período
   parameters[2].type=TYPE_DOUBLE;
   parameters[2].double_value=m_percent_dev;      // largura do canal
//--- initialize object
   if(!m_nrtr.Create(m_symbol.Name(),m_period,IND_CUSTOM,3,parameters))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
//--- ok
   return(true);   
   }

O indicador é criado com ajuda da estrutura MqlParam e do método  Create().

O código necessário já está preparado. Agora precisamos escrever o algoritmo de negociação. Vamos usar os métodos LongCondition() e ShortCondition(). Adicionamos os métodos para obter os sinais do indicador.

   //--- methods of getting data
   double            UpSignal(int index)                   { return(m_nrtr.GetData(2,index));}
   double            DnSignal(int index)                   { return(m_nrtr.GetData(3,index));}

A função GetData() recebe o valor do buffer do indicador com base em seu índice e no índice da barra. O indicador já contém o sinal de inversão da tendência. É por isso que as condições para a abertura da posição são simples. Compramos se o indicador gerar um sinal "ascendente", e venderemos se um sinal "descendente" for gerado.

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int SignalNRTR::LongCondition(void)
   {
   int idx   =StartIndex();
   if(UpSignal(idx))
      return 100;
   else return 0;
   }
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int SignalNRTR::ShortCondition(void)
   {
   int idx   =StartIndex();
   if(DnSignal(idx))
      return 100;
   else return 0;
   }

Nestas funções são definidos tanto o algoritmo de negociação quanto seu comportamento. Aqui é usada a função StartIndex().

Na descrição da função virtual int StartIndex(), fala-se que "Se estiver definido o sinalizador para analisar da barra atual, será retornado 0 (análise será realizada a partir da barra atual). Se o sinalizador da barra atual não estiver definido, será retornado 1 (análise será realizada a partir da última barra formada)." Nosso sistema de negociação usa penas sinais a partir de barras formadas. A função StartIndex() retorna 1, por padrão, precisamente de acordo com nossa estratégia. No expert advisor obtido logo com base no módulo, isto será refletido no parâmetro Expert_EveryTick com valor false.

Deste modo fica concluída a criação do módulo de sinais de negociação.

Para verificar que tudo esteja bem, usaremos o testador de estratégias.


Ao mesmo tempo, otimizaremos os parâmetros de negociação. A otimização foi realizada durante o último mês 25.09.17 - 18.10.17, em EURUSD, timeframe H1. Apenas foram otimizados alguns parâmetros do indicador, nomeadamente, período e largura do canal. Stop-Loss e Take-Profit = 0.


A figura mostra o resultado para o período de 48 com a largura do canal de 0,25%.

Combinação do NRTR com diferentes indicadores de tendência

Suponhamos que queremos confirmar os sinais de nosso indicador. Como o indicador funciona bem durante a tendência, adicionaremos um indicador de tendência ao sistema. Neste artigo, são disponibilizados módulos de sinais do Assistente MQL5 já prontos para usar. Procedemos com o mesmo método e adicionamos um indicador de tendência através de um módulo de sinais.

Identificar uma tendência não é uma tarefa fácil. Portanto, não discutiremos qual dos indicadores de tendência é melhor ou pior. Simplesmente, selecionamos qualquer um dos fornecidos com o terminal, pois o objetivo principal é elaborar uma técnica para conectar um indicador usando um módulo de negociação. Não estamos interessados ​​no resultado comercial nesta etapa.

Na seção anterior, foram descritas todas as etapas da criação do módulo. Portanto, é mais conveniente usar o método "copiar/colar" aqui e avaliar suas vantagens. Simplesmente usaremos um módulo pronto e substituiremos as linhas necessárias.

Vamos escolher o indicador técnico ADX da categoria "Tendência". O ponteiro CiADX pode ser usado como referência para ele, em vez do ponteiro para indicadores personalizados, isto é, o CiCustom. Consequentemente, existe uma maneira ligeiramente diferente de criá-lo através do método Create, e não é necessário especificar um caminho para ele.

protected:
   CiADX m_adx;                                    // object-indicator
   int m_period_adx;                               //Período do ADX
....................... outro código.....................................

//--- initialize object
   if(!m_adx.Create(m_symbol.Name(),m_period,m_period_adx))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }

O indicador tem apenas um parâmetro, isto é, o período. Isto deve ser refletido no descritor do módulo e nas funções de configuração de parâmetros. Também precisamos adicionar funções para obter os valores dos buffers do ADX.

//| Parameter=PeriodADX,int,14,Período do indicador ADX 
.....................................................

//--- methods of setting adjustable parameters
void              PeriodADX(int value)                { m_period_adx=value;}
.....................................................

//--- methods of getting data
double            MainADX(int index)                   { return(m_adx.Main(index));}
double            ValueIDPlus(int index)               { return(m_adx.Plus(index));}
double            ValueIDMinus(int index)              { return(m_adx.Minus(index));}

Na verdade, passamos todas as etapas necessárias, que foram descritas na seção de criação do módulo de negociação. Nós substituímos NRTR por ADX quando necessário. Vamos usar as condições de negociação padrão descritas no indicador ADX sem verificá-las. O indicador clássico oferece as seguintes condições:

  • Comprar, se +DI >-DI e ADX está crescendo.
  • Vender, se +DI <-DI e ADX está crescendo.
//+------------------------------------------------------------------+
//| "Voting" that trend is "Down".                                   |
//+------------------------------------------------------------------+
int SignalADX::LongCondition(void)
   {
   int idx   =StartIndex();
   if(ValueIDPlus(idx)>ValueIDMinus(idx)&&MainADX(idx)>MainADX(idx+1))
      return (100);
   else
      return (0);
   }
//+------------------------------------------------------------------+
//| "Voting" that trend is "UP".                                     |
//+------------------------------------------------------------------+
int SignalADX::ShortCondition(void)
   {
   int idx   =StartIndex();
   if(ValueIDPlus(idx)<ValueIDMinus(idx)&&MainADX(idx)>MainADX(idx+1))
      return (100);
   else
      return (0);
   }

Este é o princípio básico. Geramos um sinal de compra, se pensarmos que o indicador mostra uma tendência de alta, criamos um sinal de venda se a tendência estiver caindo. Por conveniência, o peso do sinal é ajustado para 100.

Abrimos o Assistente MQL5 novamente. Ao criar um EA, selecionamos dois sinais de negociação, isto é, SignalNTRTR e ADXTrendSignal. Se forem utilizados múltiplos sinais, será calculado seu valor médio. Portanto, para ambos os sinais, atribuímos fatores de peso iguais a um. O valor limite para abertura é definido como 100. Todos os outros parâmetros, exceto o período e largura do canal, são redefinidos para zero. Executamos o testador da estratégias para garantir que o EA funcione corretamente.


Fim do artigo

Ressumindo: Estudamos o indicador de tendência de rompimento do canal de preço dinâmico NRTR. Duas versões do indicador foram desenvolvidas, isto é, uma com porcentagem fixa de desvio de tendência dos extremos do preço e a outra com valores de desvio dependendo da volatilidade do mercado.

Ambas as versões do indicador estão anexadas abaixo. Um módulo de sinais de negociação foi criado com base no NRTR, e um EA foi gerado usando o Assistente MQL5.

O módulo do indicador ADX foi criado para visualizar como usar o indicador NRTR conjuntamente com indicadores de tendência. No Assistente MQL5, foi gerado um EA de teste com base no NRTR + ADX. Foi otimizada s combinação do NRTR + um indicador de tendência, porque a seleção de indicadores de tendência é pessoal e não está descrita neste artigo. A abordagem é baseada numa filosofia modular e combinacional.

Ao usar os arquivos anexados, certifique-se de especificar corretamente o caminho para indicadores e sinais de negociação de acordo com sua pasta de instalação. Por exemplo, no arquivo SignalNRTR, no meu caso:

   parameters[0].string_value="NRTR.ex5";

Especifique o caminho de acordo com a pasta de instalação de seus indicadores.

Arquivos:

#NomeTipoDescrição
1NRTR.mq5IndicadorCódigo-fonte do indicador analisado
2NRTRvolatile.mq5IndicadorCódigo-fonte do indicador que leva em consideração a volatilidade dos preços
3SignalNRTR.mqhMódulo de negociaçãoMódulo de sinais de negociação. Ele é usado para gerar EAs no Assistente MQL5
 ADXTrendSignal.mqhMódulo de negociação Módulo de teste do indicador de tendência

Os arquivos na pasta MQL5.zip estão localizados de acordo com os diretórios do MetaEditor.