English Русский 中文 Español Deutsch 日本語
Indicadores MTF como ferramenta de análise técnica

Indicadores MTF como ferramenta de análise técnica

MetaTrader 5Indicadores | 8 maio 2019, 09:12
4 825 2
Alexander Lasygin
Alexander Lasygin

Introdução

A maioria de nós concorda com a opinião de que o processo de análise da situação atual do mercado começa com uma revisão dos períodos gráficos maiores, o que acontece até passarmos para o gráfico em que fazemos trading. Esta análise é uma das condições para uma negociação bem-sucedida e uma abordagem profissional. Geralmente para isso abrimos várias janelas ou alternamos entre períodos gráficos, se usarmos o mesmo conjunto de ferramentas. Assim, o processo de análise primária fica concluído.

Como devemos proceder a seguir? Temos que continuar ignorando o que acontece nos períodos gráficos maiores ou alternando janelas e períodos? Seria bom se trabalhássemos no período H1 e outros superiores, porque assim teríamos tempo para realizar uma avaliação cuidadosa. Mas, e se usássemos M1-M15? Precisamos da informação fornecidas por eles, que, às vezes, chega a ser vital, aqui e agora. Isso especialmente envolve as estratégias MTF baseadas na avaliação simultânea de diferentes períodos gráficos, como as ondas de Wolfe ou as três telas de Elder.


Os traders forçados a agir dessa maneira estão sob forte tensão mental e visual, fazendo trades em períodos gráficos pequenos e, assim, perdendo os sinais lucrativos de períodos maiores. Por essa razão, tudo isso leva a tomar decisões precipitadas, como o fechamento prematuro de trades ou a desaproveitamento do momento de reversão do mercado. As consequências desse tipo de erros são bem conhecidas. Nessas situações, só podemos confiar em nossa experiência ou na velocidade na obtenção de informações.

Porém este problema tem solução — um indicador que recebe informações de diferentes períodos gráficos para um ou vários instrumentos de negociação e os exibe de forma abrangente na tela, permitindo assim uma avaliação eficiente das condições do mercado. Eles, além de informações básicas, podem exibir o estado real da tendência do mercado e recomendar decisões de negociação.

Características do algoritmo

Ao contrário dos algoritmos clássicos, este processa informações gerais de todos os intervalos de tempo ou de instrumentos de negociação e os transfere para o período atual. Quaisquer indicadores (de tendência, de volume, osciladores, etc.) ou suas combinações podem ser multiperíodo. Eles são calculados de acordo com seu algoritmo básico e transmitem informações, levando em conta o intervalo de tempo especificado nas configurações.

A configuração não difere de suas contrapartes clássicas, a única diferença é que contém um parâmetro (ou grupo) em que é indicada uma lista de intervalos de tempo e, em alguns casos, uma lista de instrumentos de negociação que fornecem informações necessárias para compor uma imagem clara do que acontece. A exibição do resultado pode ser realizada na janela principal do gráfico (ou separadamente), bem como ter uma visualização de sinal, combinando grupos de acordo com o tipo de instrumento.

Classificação de indicadores multiperíodo

Eles são apresentados em todas as classes padrão, muitos deles são complexos, ou seja, combinam informações de cálculo e de ajuda com elementos gráficos. Os seguintes grupos podem ser destacados:
1. Informativos: exibem dados e informações adicionais sem sinais e construções gráficas. Um exemplo clássico desse tipo é o indicador MultiTimeFrame. Ele mostra o tempo de fechamento do candle de cada período gráfico, o Ask e o Bid dos pares de moedas selecionados, o estado do prórpio candle (UP, DOWN, DOJI) e o volume. A tela dos indicadores deste tipo exibe inúmeras informações úteis, mas são de pouca utilidade na negociação, pois são apenas para visualização.

Figura 1

Este grupo também é representado por instrumentos completamente independentes que podem ser utilizados para tomar decisões de negociação. Eles exibem resultados de análise de vários indicadores padrão, sem precisar instalá-los no gráfico (o cálculo é realizado automaticamente), eles acompanham isso com recomendações de trading.


Figura 2



Figura 3

2. De linha: exibem na tela plotagens do mesmo instrumento, mas em diferentes períodos gráficos. Este é um envelope MA padrão (13) com diferentes períodos gráficos:

Figura 4

Outro tipo de construção gráfica apresenta um grupo de gráficos com diferentes períodos de cálculo. Esta abordagem é implementada a partir de matemática simples, ou seja, o estocástico (5.3.3) em M5 tem parâmetros (15.3.9) com o M15, já com o M30 tem outros parâmetros (30.3.18).

Figura 5

A solução acima mencionada se enquadra na classe MTF, porém, com alguma reserva. Tal abordagem nem sempre é possível de implementar, além disso, em alguns casos, as desvantagens dessa ferramenta são tão significativas que seu uso é inútil. Abaixo discutiremos suas vantagens e desvantagens mais detalhadamente, ao implementarmos este método.

Os instrumentos de sinal podem ser considerados um grupo separado. Para liberar a tela de trabalho das construções gráficas, ele forma linhas pontilhadas (de sinal) ou blocos gráficos que refletem a direção da tendência ou outros parâmetros. Este é o MTF_Coral padrão:

Figura 6

Também podemos selecionar um grupo que pode ser chamado de "Janela na janela". Este grupo é caracterizado pelo fato de mostrar na mesma janela os gráficos de outros períodos ou indicadores juntamente com o gráfico principal.


Figura 7


Figura 7.1

Outro exemplo da solução All_Woodies CCI.


Figura 7.2.


Separadamente, é necessário observar os indicadores de volatilidade do MTF. O MTF Candles pertence a esse tipo de indicadores.

Figura 8

Formas de implementação

Acima, vimos os principais tipos de indicadores MTF. Agora abordaremos, em exemplos simples, as principais formas de implementar o tipo linear. Também analisaremos os recursos de cada solução.
Indicadores multiperiódicos.
Com a ajuda do MA como exemplo, consideraremos o problema de criar uma variante com alteração no período de cálculo para exibir três períodos gráficos diferentes.
Definimos os principais parâmetros e nossas variáveis:

//---- indicator settings
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots   3
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_type3   DRAW_LINE
#property indicator_color1  Blue
#property indicator_color2  Red
#property indicator_color3  Lime
#property indicator_width1  1
#property indicator_width2  1
#property indicator_width3  1
//---- input parameters
input ENUM_TIMEFRAMES    tf1             = 1;              // Time Frame (1)
input ENUM_TIMEFRAMES    tf2             = 5;              // Time Frame (2)
input ENUM_TIMEFRAMES    tf3             = 15;             // Time Frame (3)
input int                maPeriod        = 13;             // MA period
input int                Shift           = 0;              // Shift
input ENUM_MA_METHOD     InpMAMethod     = MODE_SMA;      // Moving average method
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;   // Applied price
//---- indicator buffers
double ExtBuf1[];
double ExtBuf2[];
double ExtBuf3[];
//---- handles for moving averages
int    ExtHandle1;
int    ExtHandle2;
int    ExtHandle3;
//--- bars minimum for calculation
int    ExtBarsMinimum;
//---
int period1=0;
int period2=0;
int period3=0;

Agora, inicializamos os dados dos arrays levando em consideração o timeframe no qual ele está localizado <= definidos nas variáveis.

void OnInit()
  {
   int timeframe;
//---- indicator buffers mapping
   SetIndexBuffer(0,ExtBuf1,INDICATOR_DATA);
   SetIndexBuffer(1,ExtBuf2,INDICATOR_DATA);
   SetIndexBuffer(2,ExtBuf3,INDICATOR_DATA);
//---
   timeframe =_Period;
//---
   if(tf1>=timeframe)
   {
   period1=maPeriod*(int)MathFloor(tf1/timeframe);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,period1-1);             //sets first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_SHIFT,Shift);                      //line shifts when drawing
   PlotIndexSetString(0,PLOT_LABEL,"MA("+string(period1)+")");   //name for DataWindow
   ExtHandle1=iMA(NULL,0,period1,0,InpMAMethod,InpAppliedPrice); //get MA's handles
   }
//---- 
   if(tf2>=timeframe)
   {
   period2=maPeriod*(int)MathFloor(tf2/timeframe);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,period2-1);             //sets first bar from what index will be drawn
   PlotIndexSetInteger(1,PLOT_SHIFT,Shift);                      //line shifts when drawing
   PlotIndexSetString(1,PLOT_LABEL,"MA("+string(period2)+")");   //name for DataWindow 
   ExtHandle2=iMA(NULL,0,period2,0,InpMAMethod,InpAppliedPrice); //get MA's handles
   }
//---- 
   if(tf3>=timeframe)
   {
   period3=maPeriod*(int)MathFloor(tf3/timeframe);
   PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,period3-1);             //sets first bar from what index will be drawn
   PlotIndexSetInteger(2,PLOT_SHIFT,Shift);                      //line shifts when drawing
   PlotIndexSetString(2,PLOT_LABEL,"MA("+string(period3)+")");   //name for DataWindow 
   ExtHandle3=iMA(NULL,0,period3,0,InpMAMethod,InpAppliedPrice); //get MA's handles
   }
//--- set accuracy
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- bars minimum for calculation
   int per=MathMax(period3,MathMax(period1,period2));
   ExtBarsMinimum=per+Shift;
//--- initialization done
  }

Ciclo principal de verificação de inicialização e de cálculo:

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[])
  {
//--- check for rates total
   if(rates_total<ExtBarsMinimum)
      return(0); // not enough bars for calculation
//--- not all data may be calculated
   int calculated=BarsCalculated(ExtHandle1);
   if(calculated<rates_total&&period1!=0)
     {
      Print("Not all data of ExtHandle1 is calculated (",calculated,"bars ). Error",GetLastError());
      return(0);
     }
   calculated=BarsCalculated(ExtHandle2);
   if(calculated<rates_total&&period2!=0)
     {
      Print("Not all data of ExtHandle2 is calculated (",calculated,"bars ). Error",GetLastError());
      return(0);
     }
   calculated=BarsCalculated(ExtHandle3);
   if(calculated<rates_total&&period3!=0)
     {
      Print("Not all data of ExtHandle3 is calculated (",calculated,"bars ). Error",GetLastError());
      return(0);
     }
//--- we can copy not all data
   int to_copy;
   if(prev_calculated>rates_total || prev_calculated<0) to_copy=rates_total;
   else
     {
      to_copy=rates_total-prev_calculated;
      if(prev_calculated>0) to_copy++;
     }
//---- get ma buffers
   if(IsStopped()) return(0); //Checking for stop flag
   if(period1!=0)
   if(CopyBuffer(ExtHandle1,0,0,to_copy,ExtBuf1)<=0)
     {
      Print("getting ExtHandle1 is failed! Error",GetLastError());
      return(0);
     }
   if(IsStopped()) return(0); //Checking for stop flag
   if(period2!=0)
   if(CopyBuffer(ExtHandle2,0,0,to_copy,ExtBuf2)<=0)
     {
      Print("getting ExtHandle2 is failed! Error",GetLastError());
      return(0);
     }
   if(IsStopped()) return(0); //Checking for stop flag
   if(period3!=0)
   if(CopyBuffer(ExtHandle3,0,0,to_copy,ExtBuf3)<=0)
     {
      Print("getting ExtHandle3 is failed! Error",GetLastError());
      return(0);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Vemos nosso resultado.

Figura 9

Já consideramos um exemplo em que é necessário usar um indicador em diferentes períodos gráficos usando um aumento no período de cálculo. Com uma pequena alteração, também podemos usá-lo com a capacidade de definir os períodos para cada linha de forma independente. Esta forma de escrita pode não parecer apropriada. Não há nada mais fácil do que calcular o período por conta própria e iniciar vários indicadores ao mesmo tempo. Há casos em que esta opção com todas as suas falhas é ótima, incluindo quando é necessário observar simultaneamente dois (três) osciladores não normalizados numa janela. Devido à sua amplitude, esses osciladores são colocados em relação à linha central, o que dificulta sua interpretação. Esta opção elimina essa desvantagem.


Indicadores multiperíodo

Além das conhecidas funções em MQL4 iClose(), iHigh(), iLow(), iOpen(), iTime(), iVolume(), em MQL5 surgiram CopyTime(), CopyClose(), CopyHigh(), CopyLow(), CopyOpen(), CopyTime(), CopyVolume(), e funções iCustom, iMA, iCCI, iMACD, etc. que são implementadas através de CopyBuffer(). Cada uma delas tem suas vantagens e desvantagens. No nosso caso, falaremos apenas de MQL5. Para programar, podemos precisar de toda a lista de frames de M1 a MN1, são 26 opções. Se usarmos vários símbolos ou instrumentos de negociação, esse número aumenta muitas vezes. Na maioria dos casos, não há necessidade de copiar todo o histórico. Para indicadores informativos, o número de barras é limitado a dois. Portanto, para não tornar maior o texto do código desnecessariamente, é aconselhável gravar esses comandos com funções separadas e chamá-los repetidamente.
Para a função de série temporal CopyClose(), a função fica assim:

//+------------------------------------------------------------------+
double _iClose(string symbol,int tf,int index)
{
   if(index < 0) return(-1);

   double buf[];
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   if(CopyClose(symbol,timeframe, index, 1, buf)>0) 
        return(buf[0]);
   else return(-1);
}
//+------------------------------------------------------------------+

Para chamada do WPR:

//+------------------------------------------------------------------+
double _iWPR(string symbol,
                int tf,
                int period,
                int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   int handle=iWPR(symbol,timeframe,period);
   if(handle<0)
     {
      Print("Objeto iWPR não criado: Erro ",GetLastError());
      return(-1);
     }
   else
      return(_CopyBuffer(handle,shift));
  }
//+------------------------------------------------------------------+
double _CopyBuffer(int handle,int shift)
  {
   double buf[];
if(CopyBuffer(handle,0,shift,1,buf)>0)
         return(buf[0]);

   return(EMPTY_VALUE);
  }
//+------------------------------------------------------------------+

Nos casos em que eles têm várias linhas, a função _CopyBuffer pode ser escrita como:

//+------------------------------------------------------------------+
double _CopyBuffer(int handle,int index,int shift)
  {
   double buf[];
   switch(index)
     {
      case 0: if(CopyBuffer(handle,0,shift,1,buf)>0)
         return(buf[0]); break;
      case 1: if(CopyBuffer(handle,1,shift,1,buf)>0)
         return(buf[0]); break;
      case 2: if(CopyBuffer(handle,2,shift,1,buf)>0)
         return(buf[0]); break;
      case 3: if(CopyBuffer(handle,3,shift,1,buf)>0)
         return(buf[0]); break;
      case 4: if(CopyBuffer(handle,4,shift,1,buf)>0)
         return(buf[0]); break;
default: break;
     }
   return(EMPTY_VALUE);
  }
//+------------------------------------------------------------------+

enquanto a função _iWPR muda de string

return(_CopyBuffer(handle,shift)

para

return(_CopyBuffer(handle,0,shift)

Para ambos os casos, a função TFMigrate() é semelhante a:

//+------------------------------------------------------------------+
ENUM_TIMEFRAMES TFMigrate(int tf)
  {
   switch(tf)
     {
      case 0: return(PERIOD_CURRENT);
      case 1: return(PERIOD_M1);
      case 5: return(PERIOD_M5);
      case 15: return(PERIOD_M15);
      case 30: return(PERIOD_M30);
      case 60: return(PERIOD_H1);
      case 240: return(PERIOD_H4);
      case 1440: return(PERIOD_D1);
      case 10080: return(PERIOD_W1);
      case 43200: return(PERIOD_MN1);
      
      case 2: return(PERIOD_M2);
      case 3: return(PERIOD_M3);
      case 4: return(PERIOD_M4);      
      case 6: return(PERIOD_M6);
      case 10: return(PERIOD_M10);
      case 12: return(PERIOD_M12);
      case 20: return(PERIOD_M20);
      case 16385: return(PERIOD_H1);
      case 16386: return(PERIOD_H2);
      case 16387: return(PERIOD_H3);
      case 16388: return(PERIOD_H4);
      case 16390: return(PERIOD_H6);
      case 16392: return(PERIOD_H8);
      case 16396: return(PERIOD_H12);
      case 16408: return(PERIOD_D1);
      case 32769: return(PERIOD_W1);
      case 49153: return(PERIOD_MN1);      
      default: return(PERIOD_CURRENT);
     }
  }
//+------------------------------------------------------------------+

Como dissemos para esse tipo, no cálculo é mais frequentemente exigido um número limitado de elementos (barras). Mas, às vezez, é bom calcular todo o histórico. Aqui devemos estar atentos. É necessário entender que o número de barras no histórico do período gráfico menor é maior do que o do período maior. Isso deve ser considerado ao criar essa ferramenta. A maneira mais fácil é determinar seu menor número e usar esse valor no cálculo. O mais difícil é determinar este valor para cada período gráfico separadamente. Também muitas vezes a informação é necessária somente após a barra ser fechada e não há necessidade de recalcular os períodos gráficos maiores em cada tick do menor. Dado este aspecto, isto reduz significativamente o potencial desta ferramenta, que é bastante grande devido às suas características.
Escrever indicadores de informação (Fig. 1, Fig. 2, Fig. 3) não é diferente de escrever os clássicos, portanto, imediatamente passaremos a considerar o mais interessante, do meu ponto de vista, classe de indicadores gráficos. Se para os indicadores de informação é preciso apenas de informações atuais sobre o estado do mercado e sobre nosso conjunto de instrumentos, então para os indicadores gráficos também exigem serem plotados. Nós todos sabemos que para a formação do período M5, são necessárias 5 barras do período M1, para M15 três barras M5 e assim por diante. Isto é, durante a formação da linha no M5 com o M15, a linha é desenhada durante 3 barras. A posição da linha não é fixa e muda até que o candle M15 se feche. Por esta razão, é necessário ligar o tempo à abertura do candle. Consideremos a opção como fazê-lo com o exemplo do mesmo MA.

//---- indicator settings
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  Blue
#property indicator_width1  1
//---- input parameters
input ENUM_TIMEFRAMES    tf              = 5;              // Time Frame 
input int                maPeriod        = 13;             // MA period
input int                Shift           = 0;              // Shift
input ENUM_MA_METHOD     InpMAMethod     = MODE_SMA;       // Moving average method
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;    // Applied price
input  int               Bars_Calculated = 500;
//---- indicator buffers
double ExtMA[];
//---- handles for moving averages
int    MA_Handle;
//--- bars minimum for calculation
int    ExtBarsMinimum;
ENUM_TIMEFRAMES _tf;
int pf;
//--- armazenamos o número de valores no indicador Moving Average 
int    bars_calculated=0; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   _tf=tf;
   ENUM_TIMEFRAMES timeframe;
   int draw_shift=Shift;// valor inicial PLOT_SHIFT
   int draw_begin=maPeriod;// valor inicial PLOT_DRAW_BEGIN
//---
   timeframe=_Period;
   if(_tf<=timeframe)_tf=timeframe;// se o período gráfico for menor ou igual ao atual, definimos se PERIOD_CURRENT
   pf=(int)MathFloor(_tf/timeframe);// calculamos o coeficiente para PLOT_DRAW_BEGIN,PLOT_SHIFT e o número de barras de cálculo.
   draw_begin=maPeriod*pf;// calculamos PLOT_DRAW_BEGIN
   draw_shift=Shift*pf;// calculamos PLOT_SHIFT
//---- indicator buffers mapping
   SetIndexBuffer(0,ExtMA,INDICATOR_DATA);
//--- 
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,draw_begin-pf);                      //sets first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_SHIFT,draw_shift);                              //line shifts when drawing
   PlotIndexSetString(0,PLOT_LABEL,"MA("+string(tf)+" "+string(maPeriod)+")");//name for DataWindow
//---
   MA_Handle=iMA(NULL,_tf,maPeriod,0,InpMAMethod,InpAppliedPrice);            //get MA's handles
   if(MA_Handle==INVALID_HANDLE)
     {
      Print("getting MA Handle is failed! Error",GetLastError());
      return(INIT_FAILED);
     }
//--- set accuracy
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- bars minimum for calculation
   ExtBarsMinimum=draw_begin+draw_shift;// calculamos o número mínimo necessário de barras para calcular
//--- initialization done
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- check for rates total
   if(rates_total<ExtBarsMinimum)
      return(0); // not enough bars for calculation
   int limit;
//--- indexação em arrays como em timeseries  
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(ExtMA,true);
//--- detect start position
//--- calculando a quantidade de dados copiados necessária
//--- e o número inicial limit para o ciclo de recálculo de barras
   if(prev_calculated>rates_total || prev_calculated<=0|| calculated!=bars_calculated)// verificando a primeira inicialização do cálculo do indicador
     {
      limit=rates_total-ExtBarsMinimum-1; // número inicial para calcular todas as barras
     }
   else
     {
      limit=(rates_total-prev_calculated)+pf+1; // número inicial para calcular novas barras
     }
   if(Bars_Calculated!=0)   limit=MathMin(Bars_Calculated,limit);

Em vez de pesquisar a barra pelo número ( iBarShift ()), copiaremos imediatamente os valores pelo tempo.

//--- main cycle
   for(int i=limit;i>=0 && !IsStopped();i--)
     {
      ExtMA[i]=_CopyBuffer(MA_Handle,time[i]);
     }
//---
   bars_calculated=calculated;
Para isso, usamos a função acima mencionada.
//+--------- CopyBuffer MA Handle ----------------------------------+
double _CopyBuffer(int handle,datetime start_time)
  {
   double buf[];
   if(CopyBuffer(handle,0,start_time,1,buf)>0)
      return(buf[0]);

   return(EMPTY_VALUE);
  }
//+-------------------- END -----------------------------------------+

Nosso resultado fica assim:

Figura 10

Este método pode ser usado com sucesso para todos os tipos de indicadores lineares. A principal desvantagem é claramente visível na imagem - degraus notórios. Para МАs, isso é uma vantagem (níveis de suporte e resistência mais claramente definidos), no entanto, para osciladores com os quais usamos os padrões, isso dificulta a tarefa de identificá-los e plotá-los. Por exemplo, para WPR e CCI, esta solução é totalmente inadmissível, pois o tipo de linha muda tanto que se torna irreconhecível.

Para resolver este problema, basta calcular a última barra, levando em consideração os fatores de ponderação. Para maior versatilidade, adicionamos a variável global Interpolate, permitindo o uso de ambas as soluções.

input bool               Interpolate     = true;
//--- main cycle
   for(int i=limit;i>=0 && !IsStopped();i--)
     {
      int n;
      datetime t=time[i];
      ExtMA[i]=_CopyBuffer(MA_Handle,t);
      if(!Interpolate) continue;
      //---
      datetime times= _iTime(t);
      for(n = 1; i+n<rates_total && time[i+n]>= times; n++) continue;
      double factor=1.0/n;
      for(int k=1; k<n; k++)
      ExtMA[i+k]=k*factor*ExtMA[i+n]+(1.0-k*factor)*ExtMA[i];
     }
//---

Nesta variante, tornou-se necessário adicionar a função _iTime para determinar o número da barra com base em seu tempo de abertura no gráfico atual.

//+------------------------------------------------------------------+
datetime _iTime(datetime start_time)
{
   if(start_time < 0) return(-1);
   datetime Arr[];
   if(CopyTime(NULL,_tf, start_time, 1, Arr)>0)
        return(Arr[0]);
   else return(-1);
}
//+------------------------------------------------------------------+

Agora nossa linha se tornou mais familiar.

Figura 11

Embora pareça inadequado escrever sistemas tão complexos e exigentes, eles têm vantagens e, às vezes, são indispensáveis. Em casos em que usada a média clássica (MA, Alligator, etc.) com um aumento no período de cálculo, é observado um pequeno atraso em comparação com a versão MTF. Isso é especialmente perceptível em pequenos períodos do valor esperado.

Figura 12

Figura 13

Para indicadores simples (como MA e Alligator), isso pode não ser tão significativo, para indicadores usando sistemas complexos de dois ou mais MAs (como MACD, AO, etc.), isso pode ser significativo. Além disso, a AO ou AC acima mencionados e semelhantes não têm a capacidade de alterar o período de cálculo da média. Para indicadores cuja linha não é suavizada (WPR, CCI, etc.) com um aumento trivial no período de cálculo, é difícil alcançar qualquer resultado decente, eles são muito barulhentos.


Figura 14


Figura 15

Nas imagens das Fig. 14-15, vê-se claramente que eles podem ser usados com sucesso usando suavização, nos casos em que essa possibilidade não é prevista no algoritmo.
Este tipo, além de sua função imediata, também pode executar uma especialmente prática, tecnicamente. Eles são capazes de compensar as deficiências do testador de estratégias MT5 no modo de visualização. Ao criar EA de MTF para negociação ou para análise da efetividade desse tipo de estratégia, deparamos-nos com o fato de não podermos observar simultaneamente a posição de indicadores de diferentes períodos gráficos na tela e, como resultado do teste, obtemos um conjunto de marcadores dependendo do número de períodos utilizados. Usemos como exemplo o EA da estratégia «Tela tripla» do artigo «Guia prático MQL5: desenvolvendo uma estrutura para um sistema de negócios baseado na estratégia de tela tripla"» escrito por Anatoli Kazharski Lembre-se de em que consiste essa estratégia na versão clássica: o primeiro período gráfico é o maior, por exemplo, semanal, diário ou de 4 horas. Com isso, é determinada a tendência principal. O segundo período gráfico difere do primeiro em 1 ou 2 ordens. Com sua ajuda, determinamos o fim da correção. O terceiro período gráfico difere em mais uma ordem. Segundo ele, definimos um ponto de entrada vantajoso.

Na primeira janela, geralmente M30-W1, colocamos o MACD (12,26,1) e o EMA com um período de 13. Na segunda tela, M5-D1, respectivamente, temos o Oscilador Estocástico (5,3,3). Na terceira tela, pode ser de M1 a H4, colocamos ordens Stop na direção da tendência principal.

Figura 16

O autor se desviou um pouco dessa variação, mas o conceito de «Três telas« é preservado. Oportunamente e no final do teste, observamos algo como isto:


Figura 17

Esta opção não nos permite analisar completamente o trabalho do EA (estratégias).
Escrevamos nossa própria versão do EA usando ferramentas mais próximas da versão clássica da estratégia. Criar EAs com base em dados de indicadores não é diferente de trabalhar com seus equivalentes clássicos. O código principal fica assim.

#define EXPERT_MAGIC 1234502
//---
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>
#include <Trade\OrderInfo.mqh>
//+------------------------------------------------------------------+
enum compTF
  {
   A, //m1-m5-m15
   B, //m5-m15-h1
   C, //m15-h1-h4
   E  //h1-h4-d1
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
input string s0="//--- input parameters Lots+Trailing ---//";
input double InpLots          =0.1; // Lots
input int    InpTakeProfit    =150; // Take Profit (in pips)
input int    InpStopLoss      =60;  // StopLoss (in pips)
input int    InpLevel_S       =20;  // Level Signal
input compTF InpTFreg         =2;
input string s1="//--- input parameters MA ---//";
input int                Signal_MA_PeriodMA      =21;                             // Period of averaging
input string s2="//--- input parameters MACD ---//";
input int                Signal_MACD_PeriodFast  =12;                             // Period of fast EMA
input int                Signal_MACD_PeriodSlow  =26;                             // Period of slow EMA
input string s3="//--- input parameters Stochastic ---//";
input int                Signal_Stoch_PeriodK    =5;                              //  K-period
input int                Signal_Stoch_PeriodD    =3;                              //  D-period
input string s4="//--- input parameters Trailing ---//";
//--- inputs for trailing
input int    InpTrailingStop  =25;  // Trailing Stop Level (in pips)
input int    InpOffset        =5;   // Distance from the price (in pips)
//---
int ExtTimeOut=10; // tempo em segundos entre operações de negociação
int barsCalculated=3;
datetime t=0;
datetime time[];
//+------------------------------------------------------------------+
//| AC Sample expert class                                           |
//+------------------------------------------------------------------+
class CSampleExpert
  {
protected:
   double            m_adjusted_point;             // valor de 3 ou 5 caracteres
   CTrade            m_trade;                      // objeto de negociação
   CSymbolInfo       m_symbol;                     // informação sobre o símbolo
   CPositionInfo     m_position;                   // posição de negociação
   CAccountInfo      m_account;                    // informações da conta
   COrderInfo        m_order;                      // informações da ordem
   //--- indicators
   ENUM_TIMEFRAMES   mas_tf[3];                   // array do conjunto de timeframes
   int               handle_MACD;                 // MACD indicator handle
   int               handle_MA;                   // MA indicator handle
   int               handle_Stochastic;           // Stochastic indicator handle
   //--- indicator buffers
   double            macd_buff[];           // MACD indicator main buffer
   double            ma_buff[];             // MA indicator main buffer
   double            stoch_buff[];          // Stochastic indicator main buffer
   //---
   double            close[];
   double            open[];
   double            low[];
   double            high[];
   //--- dados do indicador para processamento
   double            macd_ind_0;
   double            macd_ind_1;
   double            ma_ind;
   double            stoch_ind_0;
   double            stoch_ind_1;
   int               level_stoch;
   //--- dados de traling stop para processamento
   double            m_traling_stop;
   double            m_take_profit;
   double            m_stop_losse;
public:
                     CSampleExpert(void);
                    ~CSampleExpert(void);
   bool              Init(void);
   void              Deinit(void);
   bool              Processing(void);

protected:
   bool              InitCheckParameters(const int digits_adjust);
   bool              Copy(void);              // 
   bool              InitIndicators(void);
   bool              LongModified(void);
   bool              ShortModified(void);
   bool              LongOpened(void);          // verificamos as condições de Long
   bool              ShortOpened(void);         // verificamos as condições de Short
   bool              OpenSellStop(void);        // colocamos a ordem SELLSTOP
   bool              OpenBuyStop(void);         // colocamos a ordem BUYSTOP
   bool              OrderModifySellStop(void); // alteramos a ordem SELLSTOP
   bool              OrderModifyBuyStop(void);  // alteramos a ordem BUYSTOP
   bool              DellSellStop(void);        // removemos a ordem SELLSTOP
   bool              DellBuyStop(void);         // removemos a ordem BUYSTOP
  };
//--- global expert
CSampleExpert ExtExpert;
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSampleExpert::CSampleExpert(void) : m_adjusted_point(0),
                                     handle_MACD(INVALID_HANDLE),
                                     handle_Stochastic(INVALID_HANDLE),
                                     handle_MA(INVALID_HANDLE),
                                     macd_ind_0(0),
                                     macd_ind_1(0),
                                     stoch_ind_0(0),
                                     stoch_ind_1(0),
                                     ma_ind(0),
                                     m_traling_stop(0),
                                     m_take_profit(0)
  {
   ArraySetAsSeries(macd_buff,true);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSampleExpert::~CSampleExpert(void)
  {
  }
//+------------------------------------------------------------------+
//| Inicialização e verificação de parâmetros de entrada             |
//+------------------------------------------------------------------+
bool CSampleExpert::Init(void)
  {
//--- inicializando informações gerais
   m_symbol.Name(Symbol());                  // symbol
   m_trade.SetExpertMagicNumber(EXPERT_MAGIC); // magic
   m_trade.SetMarginMode();
   m_trade.SetTypeFillingBySymbol(Symbol());
//--- tuning for 3 or 5 digits
   int digits_adjust=1;
   if(m_symbol.Digits()==3 || m_symbol.Digits()==5)
      digits_adjust=10;
   m_adjusted_point=m_symbol.Point()*digits_adjust;
//--- definindo o desvio padrão para negociação 
   m_traling_stop    =InpTrailingStop*m_adjusted_point;
   m_take_profit     =InpTakeProfit*m_adjusted_point;
   m_stop_losse      =InpStopLoss*m_adjusted_point;
//--- set default deviation for trading in adjusted points
   m_trade.SetDeviationInPoints(3*digits_adjust);
//---
   int x=InpTFreg;
   switch(x)
     {
      case 0: {mas_tf[0]=PERIOD_M1;mas_tf[1]=PERIOD_M5;mas_tf[2]=PERIOD_M15;}
      break;
      case 1: {mas_tf[0]=PERIOD_M5;mas_tf[1]=PERIOD_M15;mas_tf[2]=PERIOD_H1;}
      break;
      case 2: {mas_tf[0]=PERIOD_M15;mas_tf[1]=PERIOD_H1;mas_tf[2]=PERIOD_H4;}
      break;
      case 3: {mas_tf[0]=PERIOD_H1;mas_tf[1]=PERIOD_H4;mas_tf[2]=PERIOD_D1;}
      break;
     }
//---
   if(!InitCheckParameters(digits_adjust))
      return(false);
   if(!InitIndicators())
      return(false);
//--- resultado
   return(true);
  }
//+------------------------------------------------------------------+
//| Verificação de parâmetros de entrada                             |
//+------------------------------------------------------------------+
bool CSampleExpert::InitCheckParameters(const int digits_adjust)
  {
//--- verificando os dados de origem
   if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel())
     {
      printf("Take Profit deve ser maior que %d",m_symbol.StopsLevel());
      return(false);
     }
   if(InpTrailingStop*digits_adjust<m_symbol.StopsLevel())
     {
      printf("Trailing Stop deve ser maior que %d",m_symbol.StopsLevel());
      return(false);
     }
//--- verificando a quantidade correta do lote
   if(InpLots<m_symbol.LotsMin() || InpLots>m_symbol.LotsMax())
     {
      printf("Lots deve estar no intervalo de %f a %f",m_symbol.LotsMin(),m_symbol.LotsMax());
      return(false);
     }
   if(MathAbs(InpLots/m_symbol.LotsStep()-MathRound(InpLots/m_symbol.LotsStep()))>1.0E-10)
     {
      printf("O montante não corresponde ao passo do lote %f",m_symbol.LotsStep());
      return(false);
     }
//--- warning
   if(InpTakeProfit<=InpTrailingStop)
      printf("Warning: Trailing Stop deve ser menor que Take Profit");
//--- resultado
   return(true);
  }
//+------------------------------------------------------------------+
//| Inicialização de indicadores                                     |
//+------------------------------------------------------------------+
bool CSampleExpert::InitIndicators(void)
  {
//--- criando o indicador MACD
   if(handle_MACD==INVALID_HANDLE)
     {
      //---
      handle_MACD=iCustom(NULL,0,"MTF\\Oscillators\\MTF_MACD",mas_tf[2],Signal_MACD_PeriodFast,Signal_MACD_PeriodSlow);
      //---
      if(handle_MACD==INVALID_HANDLE)
        {
         printf("Erro ao criar o indicador MACD");
         return(false);
        }
     }
//--- criando indicador MA
   if(handle_MA==INVALID_HANDLE)
     {
      //---
      handle_MA=iCustom(NULL,0,"MTF\\Trend\\MA_MultiTF",mas_tf[2],Signal_MA_PeriodMA,0,MODE_EMA,PRICE_CLOSE);
      //---
      if(handle_MA==INVALID_HANDLE)
        {
         printf("Erro ao criar o indicador MA");
         return(false);
        }
     }
//--- criando o indicador Stochastic
   if(handle_Stochastic==INVALID_HANDLE)
     {
      //---
      handle_Stochastic=iCustom(NULL,0,"MTF\\Oscillators\\MTF_Stochastic",mas_tf[1],Signal_Stoch_PeriodK,Signal_Stoch_PeriodD);
      //---
      if(handle_Stochastic==INVALID_HANDLE)
        {
         printf("Erro ao criar o indicador Stochastic");
         return(false);
        }
     }
//--- resultado
   return(true);
  }
//+------------------------------------------------------------------+
//| Alteramos a posição longa                                        |
//+------------------------------------------------------------------+
bool CSampleExpert::LongModified(void)
  {
   bool res=false;
//--- verificando o trailing-stop
   if(InpTrailingStop>0)
     {
      if(m_symbol.Bid()-m_position.PriceOpen()>m_adjusted_point*InpTrailingStop)
        {
         double sl=NormalizeDouble(m_symbol.Bid()-m_traling_stop,m_symbol.Digits());
         double tp=m_position.TakeProfit();
         if(m_position.StopLoss()<sl || m_position.StopLoss()==0.0)
           {
            //--- alterando a posição
            if(m_trade.PositionModify(Symbol(),sl,tp))
               printf("Long position by %s to be modified",Symbol());
            else
              {
               printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment());
               printf("Modify parameters : SL=%f,TP=%f",sl,tp);
              }
            //--- mudou e deve sair do EA
            res=true;
           }
        }
     }
//--- resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| Alteramos a posição curta                                        |
//+------------------------------------------------------------------+
bool CSampleExpert::ShortModified(void)
  {
   bool   res=false;
//--- verificando o trailing-stop
   if(InpTrailingStop>0)
     {
      if((m_position.PriceOpen()-m_symbol.Ask())>(m_adjusted_point*InpTrailingStop))
        {
         double sl=NormalizeDouble(m_symbol.Ask()+m_traling_stop,m_symbol.Digits());
         double tp=m_position.TakeProfit();
         if(m_position.StopLoss()>sl || m_position.StopLoss()==0.0)
           {
            //--- alterando a posição
            if(m_trade.PositionModify(Symbol(),sl,tp))
               printf("Short position by %s to be modified",Symbol());
            else
              {
               printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment());
               printf("Modify parameters : SL=%f,TP=%f",sl,tp);
              }
            //--- mudou e deve sair do EA
            res=true;
           }
        }
     }
//--- resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| Verificando as condições de abertura de posição longa            |
//+------------------------------------------------------------------+
bool CSampleExpert::LongOpened(void)
  {
   bool res=false;
   level_stoch=InpLevel_S;
//--- verificamos a possibilidade de uma posição longa (BUY)
   if(stoch_ind_1<level_stoch && stoch_ind_0>level_stoch && macd_ind_1>macd_ind_0 && ma_ind<close[1] && ma_ind<open[1])//&& ma_ind<close[1] && ma_ind<open[1]
     {
      //--- saindo do EA
      res=true;
     }
//--- resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| abrindo posição Buy Stop                                         |
//+------------------------------------------------------------------+
bool CSampleExpert::OpenBuyStop(void)
  {
   bool res=false;
   double tp=0,sl=0;
//---
   if(LongOpened())
     {
      res=true;
      //-- declaração e inicialização da solicitação e do resultado
      MqlTradeRequest request={0};
      MqlTradeResult  result={0};
      //--- parâmetros para colocar uma ordem pendente
      request.action   =TRADE_ACTION_PENDING;                             // tipo de operação de negociação
      request.symbol   =Symbol();                                         // símbolo
      request.deviation=5;                                                // desvio admissível em relação ao preço
      request.volume   =InpLots;                                          // volume por lote
      request.magic    =EXPERT_MAGIC;                                     // MagicNumber da ordem
      double offset=InpOffset;                                            // recuo a partir do preço atual para colocar uma ordem, em pontos
      double price;                                                       // preço de ativação da ordem
      double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);                // tamanho do ponto
      int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);           // número de casas decimais (precisão)
      //--- tipo de operação
      request.type=ORDER_TYPE_BUY_STOP;                                   // tipo de ordem
      price=high[1]+offset*m_adjusted_point;                              // preço para abertura
      request.price=NormalizeDouble(price,digits);                        // preço normalizado de abertura
      tp=price+m_take_profit;
      sl=price-m_stop_losse;
      request.sl       =NormalizeDouble(sl,_Digits);                       // inserimos o novo valor de Buy Loss na estrutura
      request.tp       =NormalizeDouble(tp,_Digits);                       // inserimos o novo valor de Take Profit na estrutura
      //--- envio do pedido
      if(!OrderSend(request,result))
        {res=false;printf("OrderSend error %d",GetLastError());}           // se a solicitação não for enviada, será exibido um código de erro
      //--- informações sobre a operação
      printf("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
      //--- saindo do EA
     }
//--- resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| Verificando as condições de abertura de posição curta            |
//+------------------------------------------------------------------+
bool CSampleExpert::ShortOpened(void)
  {
   bool res=false;
   level_stoch=100-InpLevel_S;
//--- verificamos a possibilidade de uma posição curta  (SELL) 
   if(stoch_ind_1>level_stoch && stoch_ind_0<level_stoch && macd_ind_1<macd_ind_0 && ma_ind>close[1] && ma_ind>open[1])
     {
      //--- saindo do EA
      res=true;
     }
//--- resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| abrindo posição Sell Stop                                        |
//+------------------------------------------------------------------+
bool CSampleExpert::OpenSellStop(void)
  {
   bool res=false;
   double tp=0,sl=0;
//---
   if(ShortOpened())
     {
      res=true;
      //-- declaração e inicialização da solicitação e do resultado
      MqlTradeRequest request={0};
      MqlTradeResult  result={0};
      //--- parâmetros para colocar uma ordem pendente
      request.action   =TRADE_ACTION_PENDING;                             // tipo de operação de negociação
      request.symbol   =Symbol();                                         // símbolo
      request.deviation=5;                                                // desvio admissível em relação ao preço
      request.volume=InpLots;                                             // volume por lote
      request.magic=EXPERT_MAGIC;                                         // MagicNumber da ordem
      int offset=InpOffset;                                                // recuo a partir do preço atual para colocar uma ordem, em pontos
      double price;                                                       // preço de ativação da ordem
      int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);           // número de casas decimais (precisão)
      request.type=ORDER_TYPE_SELL_STOP;                                  // tipo de ordem
      price=low[1]-offset*m_adjusted_point;                                          // preço para abertura
      request.price=NormalizeDouble(price,digits);                        // preço normalizado de abertura
      tp=price-m_take_profit;
      sl=price+m_stop_losse;
      request.sl       =NormalizeDouble(sl,_Digits);                       // inserimos um novo valor de StopLoss na estrutura
      request.tp       =NormalizeDouble(tp,_Digits);                       // inserimos um novo valor de TakeProfit na estrutura
      //--- envio do pedido
      if(!OrderSend(request,result))
        {res=false;printf("OrderSend error %d",GetLastError());}     // se a solicitação não for enviada, será exibido um código de erro
      //--- informações sobre a operação
      printf("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
      //--- saindo do EA
     }
//--- resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| a função principal retorna true se alguma posição                |
//| for processada                                                   |
//+------------------------------------------------------------------+
bool CSampleExpert::Processing(void)
  {
//   MqlDateTime dt;
//--- frequência de atualização
   if(!m_symbol.RefreshRates())
      return(false);
//--- indicadores de atualização
   if(BarsCalculated(handle_Stochastic)<barsCalculated)
      return(false);
   if(CopyBuffer(handle_Stochastic,0,0,barsCalculated,stoch_buff)!=barsCalculated)
      return(false);
//---
   if(BarsCalculated(handle_MACD)<barsCalculated)
      return(false);
   if(CopyBuffer(handle_MACD,0,0,barsCalculated,macd_buff)!=barsCalculated)
      return(false);
//---
   if(BarsCalculated(handle_MA)<barsCalculated)
      return(false);
   if(CopyBuffer(handle_MA,0,0,barsCalculated,ma_buff)!=barsCalculated)
      return(false);
//---
   if(!Copy())return(false);
//--- para simplificar a codificação e acelerar o acesso
//--- os dados são colocados em variáveis internas
   macd_ind_0   = macd_buff[1];
   macd_ind_1   = macd_buff[0];
   ma_ind       = ma_buff[1];
   stoch_ind_0  = stoch_buff[1];
   stoch_ind_1  = stoch_buff[0];

//--- é importante sair disso corretamente ...   
//--- verifique primeiro se a posição existe, para isso, tente selecioná-la
   bool bord=false,sord=false;
   ulong ticket;
//+--------- se abertas posições e ativado traling stop -----------+
   if(m_position.Select(Symbol()) && PositionsTotal()>0 && m_traling_stop!=0)
     {
      if(m_position.PositionType()==POSITION_TYPE_BUY)
        {
         bord=true;
         //--- mudar a posição longa
         if(LongModified())
            return(true);
        }
      else
        {
         sord=true;
         //--- mudar a posição curta
         if(ShortModified())
            return(true);
        }
     }
//+----- negociação com ordens  STOP ------------------------------------+
// neste ciclo, iteramos todas as ordens pendentes definidas
   for(int i=0;i<OrdersTotal();i++)
     {
      // selecionamos cada uma das ordens e obtemos seu ticket
      ticket=OrderGetTicket(i);
      // colocamos as ordens Buy Stop
      if(m_order.OrderType()==ORDER_TYPE_BUY_STOP)
        {
         // definimos um sinalizador indicando a presença de uma ordem Buy Stop
         bord=true;
         //--- é importante entrar no mercado corretamente, movemos a ordem se necessário 
         if(bord)OrderModifyBuyStop(); // alterar ordem BUYSTOP
        }
      // colocamos ordens Sell Stop
      if(m_order.OrderType()==ORDER_TYPE_SELL_STOP)
        {
         // definimos um sinalizador indicando a presença de uma ordem Sell Stop
         sord=true;
         //--- é importante entrar no mercado corretamente, movemos a ordem se necessário 
         if(sord)OrderModifySellStop(); // alteramos a ordem SELLSTOP
        }
     }
//--- se não houver ordens, colocamos ------------------------------------+
   if(!sord)if(OpenSellStop()){sord=true;return(true);}
   if(!bord)if(OpenBuyStop()){bord=true;return(true);}
//--- sair sem processamento de posição
   return(false);
  }
//+------------------------------------------------------------------+
//| Alteramos os parâmetros da ordem de negociação Sell Stop         |
//| previamente definida                                             |
//+------------------------------------------------------------------+
bool CSampleExpert::OrderModifySellStop(void)
  {
   bool res=true;
   ulong ticket;
   double tp=0,sl=0;
   double offset=InpOffset;                                            // recuo a partir do preço atual para colocar uma ordem, em pontos
   double price;                                                       // preço de ativação da ordem
   int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);           // número de casas decimais (precisão)
                                                                       // declaração e inicialização da solicitação e do resultado
   MqlTradeRequest request={0};
   MqlTradeResult  result={0};
   int total=OrdersTotal(); // número de ordens pendentes definidas
//--- pesquisa detalhada de todas as ordens pendentes colocadas
   for(int i=total-1; i>=0; i--)
     {
      // selecionamos cada uma das ordens e obtemos seu ticket
      ticket=OrderGetTicket(i);
      // colocamos ordens Sell Stop
      if(m_order.OrderType()==ORDER_TYPE_SELL_STOP)
        {
         ulong  magic=OrderGetInteger(ORDER_MAGIC);               // MagicNumber da ordem
         //--- se o MagicNumber corresponder
         if(magic==EXPERT_MAGIC)
           {
               price=low[1]-offset*m_adjusted_point;                         // preço para abertura 
            if(price>m_order.PriceOpen()) // verificamos condições
            if(low[1]>low[2]) // verificamos condições
              {
               request.action=TRADE_ACTION_MODIFY;                           // tipo de operação de negociação
               request.order = OrderGetTicket(i);                            // ticket da ordem
               request.symbol   =Symbol();                                   // símbolo
               request.deviation=InpOffset;                                  // desvio admissível em relação ao preço
               price=low[1]-offset*m_adjusted_point;                         // preço para abertura 
               request.price=NormalizeDouble(price,digits);                  // preço normalizado de abertura 
               tp=price-m_take_profit;
               sl=price+m_stop_losse;
               request.sl       =NormalizeDouble(sl,_Digits);                // inserimos um novo valor de StopLoss na estrutura
               request.tp       =NormalizeDouble(tp,_Digits);                // inserimos um novo valor de TakeProfit na estrutura
               //--- envio do pedido
               if(!OrderSend(request,result))
                 {
                  // definimos um sinalizador indicando que a ordem Sell Stop não foi excluída
                  res=true; printf("OrderSend error %d",GetLastError());
                 }  // se a solicitação não for enviada, será exibido um código de erro
               //--- informações sobre a operação   
               printf("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
              }
           }
        }
     }
   return(res);
  }
//+------------------------------------------------------------------+
//| Alteramos os parâmetros da ordem de negociação Buy Stop          |
//| previamente definida                                             |
//+------------------------------------------------------------------+
bool CSampleExpert::OrderModifyBuyStop(void)
  {
   bool res=true;
   ulong ticket;
   double tp=0,sl=0;
   double offset=InpOffset;                                            // recuo a partir do preço atual para colocar uma ordem, em pontos
   double price;                                                       // preço de ativação da ordem
   int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);           // número de casas decimais (precisão)
                                                                       //-- declaração e inicialização da solicitação e do resultado
   MqlTradeRequest request={0};
   MqlTradeResult  result={0};
   int total=OrdersTotal(); // número de ordens pendentes definidas
//--- pesquisa detalhada de todas as ordens pendentes colocadas
   for(int i=total-1; i>=0; i--)
     {
      // selecionamos cada uma das ordens e obtemos seu ticket
      ticket=OrderGetTicket(i);
      // colocamos as ordens Buy Stop
      if(m_order.OrderType()==ORDER_TYPE_BUY_STOP)
        {
         ulong  magic=OrderGetInteger(ORDER_MAGIC);               // MagicNumber da ordem
         //--- se o MagicNumber corresponder
         if(magic==EXPERT_MAGIC)
           {
               price=high[1]+offset*m_adjusted_point;                        // preço para abertura 
            if(price<m_order.PriceOpen()) // verificamos condições
              {
               request.action=TRADE_ACTION_MODIFY;                           // tipo de operação de negociação
               request.symbol   =Symbol();                                   // símbolo
               request.action=TRADE_ACTION_MODIFY;                           // tipo de operação de negociação
               request.order = OrderGetTicket(i);                            // ticket da ordem
               request.symbol   =Symbol();                                   // símbolo
               request.deviation=InpOffset;                                  // desvio admissível em relação ao preço
               // --- definição do nível de preço, take-profit e stop-loss 

               request.price=NormalizeDouble(price,digits);                  // preço normalizado de abertura
               tp=price+m_take_profit;
               sl=price-m_stop_losse;
               request.sl       =NormalizeDouble(sl,_Digits);                       // inserimos o novo valor de Buy Loss na estrutura
               request.tp       =NormalizeDouble(tp,_Digits);                       // inserimos o novo valor de Take Profit na estrutura
               //--- envio do pedido
               if(!OrderSend(request,result))
                 {
                  // definimos um sinalizador indicando que a ordem Buy Stop não foi excluída
                  res=true;
                  printf("OrderSend error %d",GetLastError());
                 }  // se a solicitação não for enviada, será exibido um código de erro
               //--- informações sobre a operação   
               printf("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
              }
           }
        }
     }
   return(res);
  }
//+------------------------------------------------------------------+
//| Função de inicialização do EA                                    |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- cirando todos os objeto necessários
   if(!ExtExpert.Init())
      return(INIT_FAILED);
//--- ok
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de processamento de ticks                                 |
//+------------------------------------------------------------------+
void OnTick(void)
  {
   static datetime limit_time=0; // último tempo de processamento de negociação + timeout
//--- não processar, se tempo limite
   if(TimeCurrent()>=limit_time)
     {
      //--- verificando dados
      if(Bars(Symbol(),Period())>barsCalculated)
        {
         //--- alterando o tempo limite para um timeouti em segundos, se processado
         if(ExtExpert.Processing())
            limit_time=TimeCurrent()+ExtTimeOut;
        }
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSampleExpert::Copy(void)
  {
//--- quantidade copiada 
   int copied=3;
//---
   ArrayResize(high,copied);
   ArrayResize(low,copied);
   ArrayResize(close,copied);
   ArrayResize(open,copied);
//+------ Definindo a direção da indexação de array ---------------------+
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(open,true);
//--- copiamos os preços das barras 
   if(CopyHigh(NULL,mas_tf[0],0,copied,high)<0)
     {printf("No copied High",GetLastError());return(false);}
//---
   if(CopyLow(NULL,mas_tf[0],0,copied,low)<0)
     {printf("No copied Low",GetLastError());return(false);}
//---
   if(CopyClose(NULL,mas_tf[2],0,copied,close)<0)
     {printf("No copied Close",GetLastError());return(false);}
//---
   if(CopyOpen(NULL,mas_tf[2],0,copied,open)<0)
     {printf("No copied Open",GetLastError());return(false);}
//---
   return(true);
  }
//+------------------------------------------------------------------+


O uso de tais ferramentas dá uma imagem diferente:



Figura 18

Nesta variação, podemos não apenas ver o trabalho do EA, mas também analisar possíveis deficiências em seu trabalho, o que já muda radicalmente nossa atitude em relação às capacidades desse testador.


Fim do artigo

Apesar de os autores dos módulos de software MQL5 não fornecerem a possibilidade de criar tais algoritmos diretamente, estes indicadores merecem continuar vivendo. Em alguns casos, as possibilidades de analisar simultaneamente o estado do mercado em diferentes períodos gráficos, as de melhorar a eficiência do testador de estratégias e as de aprimorar a qualidade da suavização são indispensáveis. Essas variantes de código não são isentas de falhas, o que cria um grande campo para atividades relativamente à linguagem de programação MQL.

Arquivos anexados.

Nome Tipo Caminho
MA_MultiPeriod Trend MQL5\Indicators\MA_MultiPeriod.mq5
MA_MultiTF Trend MQL5\Indicators\MTF\Trend\MA_MultiTF.mq5
MTF_BB Trend MQL5\Indicators\MTF\Trend\MTF_BB.mq5
MTF_Envelopes Trend MQL5\Indicators\MTF\Trend\MTF_Envelopes.mq5
MTF_ParabolicSAR Trend MQL5\Indicators\MTF\Trend\MTF_ParabolicSAR.mq5
MTF_StdDev Trend MQL5\Indicators\MTF\Trend\MTF_StdDev.mq5
MTF_CCI Oscillators MQL5\Indicators\MTF\Oscillators\MTF_CCI.mq5
MTF_Force_Index Oscillators MQL5\Indicators\MTF\Oscillators\MTF_Force_Index.mq5
MTF_MACD Oscillators MQL5\Indicators\MTF\Oscillators\MTF_MACD.mq5
MTF_Momentum Oscillators MQL5\Indicators\MTF\Oscillators\MTF_Momentum.mq5
MTF_RSI Oscillators MQL5\Indicators\MTF\Oscillators\MTF_RSI.mq5
MTF_RVI Oscillators MQL5\Indicators\MTF\Oscillators\MTF_RVI.mq5
MTF_Stochastic Oscillators MQL5\Indicators\MTF\Oscillators\MTF_Stochastic.mq5
MTF_WPR  Oscillators  MQL5\Indicators\MTF\Oscillators\MTF_WPR.mq5
Triple Screen Trading System 1.0  Expert  


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

Arquivos anexados |
MTF.zip (147.76 KB)
Últimos Comentários | Ir para discussão (2)
Higor Nobre
Higor Nobre | 2 fev 2021 em 08:48
Pode me enviar o link deste indicador?
Higor Nobre
Higor Nobre | 2 fev 2021 em 08:48

Pode me enviar o link do primeiro indicador do seu artigo?

Segue anexo.

Como escrever uma biblioteca DLL em MQL5 (Parte II) em 10 minutos: escrevendo no ambiente do Visual Studio 2017 Como escrever uma biblioteca DLL em MQL5 (Parte II) em 10 minutos: escrevendo no ambiente do Visual Studio 2017
O artigo básico inicial não perdeu sua importância e todos os interessados neste tópico simplesmente devem lê-lo. Mas já se passou muito tempo desde então, e agora o Visual Studio 2017 com uma nova interface está à frente, também a própria plataforma MetaTrader 5 vem se desenvolvendo e segue em frente. O artigo descreve as etapas de criação de um projeto dll, abrangendo configurações e colaboração com as ferramentas do terminal MetaTrader 5.
Usando os recursos computacionais do MATLAB 2018 no MetaTrader 5 Usando os recursos computacionais do MATLAB 2018 no MetaTrader 5
Depois da atualizar o pacote MATLAB em 2015, é necessário considerar a maneira moderna de criar bibliotecas DLL. Como o exemplo de um indicador preditivo, o artigo ilustra os recursos de vinculação do MetaTrader 5 e do MATLAB usando versões modernas de plataformas de 64 bits. Ao analisar toda a sequência de conexão do MATLAB, o desenvolvedor MQL5 criará rapidamente aplicativos com recursos computacionais avançados, evitando riscos.
Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte III). Coleção de ordens e posições de mercado, busca e ordenação Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte III). Coleção de ordens e posições de mercado, busca e ordenação
Na primeira parte, começamos a criar uma grande biblioteca multi-plataforma, simplificando o desenvolvimento de programas para as plataformas MetaTrader 5 e MetaTrader 4. Além disso, nós implementamos a coleção do histórico de ordens e negócios. Nosso próximo passo é criar uma classe para uma seleção conveniente e a ordenação de ordens, negócios e posições nas listas de coleção. Nós vamos implementar o objeto da biblioteca base chamada Engine e adicionar uma coleção de ordens e posições de mercado para a biblioteca.
Raspagem de dados da web sobre a rentabilidade dos títulos públicos Raspagem de dados da web sobre a rentabilidade dos títulos públicos
Automatize a coleta de dados sobre a taxa de juros para melhorar o desempenho de um Expert Advisor.