Diversas maneiras de se encontrar uma tendência no MQL5

Dmitriy Skub | 26 dezembro, 2013

Introdução

Qualquer investidor conhece a regra "A tendência é sua amiga, siga a tendência", mas praticamente todo mundo tem sua própria ideia do que é uma tendência. Praticamente todo investidor ouviu ou leu histórias terríveis, que contam como investidores, que negociaram contra a tendência, se arruinaram.

Qualquer comerciante daria muito pela oportunidade de detectar precisamente uma tendência em qualquer dado momento. Talvez, este seja o Santo Graal que todo mundo esteja procurando por. Este artigo considerará diversas maneiras de detectar uma tendência. Para ser mais preciso - como programar diversas maneiras clássicas para detectar uma tendência pelos meios do MQL5.


1. O que é uma tendência e porque sabê-la

Primeiro de tudo, vamos formular o conceito geral de uma tendência.

Tendência - é uma inclinação (direção) de mudança de preço no mercado. A partir desta definição geral de tendência, vem as consequências:

Vamos ilustrar este conceito:

Figura 1. Análise de Tendência

Figura 1. Análise de Tendência

Olhando para a figura, você pode ver que a tendência total desde o fim de 2005, até maio de 2006, está crescendo (seta verde no gráfico). Mas se nós considerarmos pedaços menores do gráfico de preço, você verá que em fevereiro de 2006 a tendência era claramente decrescente (seta vermelha no gráfico), e durante quase janeiro inteiro o preço estava no corredor lateral (seta amarela).

Então, antes de você identificar uma tendência você tem que determinar qual período de tempo você está interessado. Para negócios, o período de tempo primeiro de tudo determina o tempo de manutenção de posição do mercado, da sua abertura a seu fechamento. Em adição a isto, dependentes são níveis de paradas protetivas e fechamentos esperados, assim como a frequência de operações de negociação.

O propósito deste artigo é ajudar investidores novos a usar completamente as ferramentas de detecção de tendência, providas pela plataforma MetaTrader 5. Este artigo também mira proporcionar conhecimento básico em escrever indicadores simples, que automatizam o processo. O objetivo final é escrever especialistas simples, que usem estes indicadores para negociações automatizadas.

Para definitividade, vamos considerar o gráfico de preço diário (Período de tempo D1 no terminal) do instrumento mais líquido no mercado Forex - EURUSD. O tempo de manutenção de posição em tal período pode variar de vários dias a vários meses. Consequentemente, o objetivo - é pegar centenas e até mesmo milhares de pontos, e paradas protetivas de perdas são localizadas a distância de várias centenas de pontos.

Em geral, tudo descrito abaixo pode ser usado em qualquer período de tempo. Entretanto, mantenha em mente que por menor que seja o gráfico do período de tempo, maior o impacto na negociação tem o componente de barulho, causado pelas notícias, especulações de mercado dos participantes principais, afetando a volatilidade do mercado.

Se levarmos em consideração, que maior seja a tendência, mais provavelmente ela muda, então, quando negociando com a tendência, é mais provável ganhar do que perder dinheiro. Agora você tem de entender como detectar uma tendência em um gráfico de preço. Isto será discutido neste artigo.


2. Como detectar uma tendência

Aqui estão algumas maneiras conhecidas de detecção de tendência:

  1. Por Médias Móveis
  2. Por picos de zigue-zague
  3. Por indicações de ADX
  4. Por NRTR
  5. Por cores dos candelabros Heiken Ashi

Consideraremos consistentemente todos estes métodos, suas vantagens e suas desvantagens. Então iremos compará-las no mesmo período da história.

2.1. Detecção de tendência usando média móvel

Talvez, a maneira mais fácil de detectar uma tendência e sua direção - utilizando médias móveis. Uma das primeiras ferramentas de análise técnica - média móvel - ainda é usada em variações diferentes e é a base da maioria dos indicadores. Investidores usam tanto uma média móvel quanto todo um conjunto delas, o qual é chamado algumas vezes de "leque".

Vamos formular uma regra simples para uma média móvel:

Neste caso, usaremos o preço de fechamento da barra para reduzir o número de mudanças "falsas" de tendência, quando o preço flutua para cima e para baixo perto da média móvel (o chamado quite).

Vamos ilustrar este método:

Figura 2. Identificando uma Tendência Usando Média Móvel

Figura 2. Identificando uma Tendência Usando Média Móvel

Aqui nós usamos o gráfico D1 do EURUSD e uma média móvel simples com o período 200, construído nos preços de encerramento (linha vermelha no gráfico). Na parte inferior da figura você pode ver o indicador de tendência desenvolvido especialmente - MATrendDetector. Esta direção da tendência é indicada pela posição do histograma indicador, relativamente ao eixo zero. +1 corresponde a tendência crescente. -1 - tendência decrescente. Mais a frente discutiremos este e outros outros indicadores, usados neste artigo.

Você pode ver que quando a barra fecha acima/abaixo da média móvel, o preço então se vira na posição oposta. Isto é, este método dá vários sinais falsos. É por isto que seu uso em especialistas e indicadores é muito limitado, apenas como um filtro de tendência bem grosseiro.

2.2. Detecção de tendência usando três médias móveis

O que pode ser feito para melhorar a qualidade da detecção de tendência usando médias móveis? Por exemplo, você pode usar duas ou mais médias móveis com períodos diferentes. Então, a regra da detecção de tendência para qualquer número (mais de uma) de médias móveis com diferentes períodos será visto a seguir:

Aqui nós usamos os seguintes termos:

Tal "ordem correta das médias" também é chamada de leque abaixo/acima de abertura das médias, por causa da semelhança visual.

Vamos ilustrar este método:

Figura 3. Detecção de Tendência Usando Várias Médias Móveis

Figura 3. Detecção de tendência usando várias médias móveis

Aqui nós usamos o gráfico D1 do EURUSD e médias móveis simples com período 200 (linha vermelha grossa), 50 (linha amarela de grossura média) e 21 (linha roxa fina), construído nos preços de fechamento.

Na parte inferior da figura você pode o indicador de tendência desenvolvido especialmente - FanTrendDetector. Esta direção da tendência é indicada pela posição do histograma indicador, relativamente ao eixo zero. +1 corresponde a tendência crescente. -1 - tendência decrescente. Se o valor do histograma é igual a zero, isto significa que a tendência não pode ser detectada. Também existe o indicador MATrendDetector para comparação.

É evidente que o número de alarmes falsos da mudança de tendência foi diminuído. Mas o atraso da detecção da média foi aumentado. Isto faz sentido - até que todas as médias móveis se alinhem na ordem "correta", pode levar algum tempo. O que é melhor e o que não é - depende do sistema de negociação, que utiliza estes métodos.

Neste caso, o valor dos períodos das médias não é selecionado de nenhuma maneira, mas eles são mais amplamente usados por investidores e pelo autor deste artigo. Por selecionar um conjunto de vantagens e seus números, você pode tentar melhorar as características deste método de detecção de tendência para um par de moedas.

2.3. Detecção de tendência usando máximos e mínimos do indicador zigue-zague

Agora vamos abordar detecção de tendência de uma perspectiva dos clássicos de análise técnica. A saber, nós usaremos a seguinte regra de Charles Dow:

Nós encontraremos o máximo/mínimo local pelos picos do indicador Zigue-zague.

Vamos ilustrar este método:

Figura 4. Detecção de Tendência Usando o Indicador Zigue-zague

Figura 4. Detecção de tendência usando o indicador zigue-zague

Aqui nós usamos o gráfico D1 do EURUSD e Zigue-zague com os seguintes parâmetros. ExtDepth = 5, ExtDeviation = 5, ExtBackstep = 3.

Na parte inferior da figura você pode o indicador de tendência desenvolvido especialmente - ZigZagTrendDetector.

A principal desvantagem deste método de detecção de tendência - em tempo real é impossível entender quando o extremo já está formado ou não. No história, o extremo pode ser visto muito bem, e você pode entender aonde eles são formados. Entretanto, quando o preço muda em tempo real, o extremo formado pode subitamente desaparecer ou aparecer de novo. Para ver isto, apenas olhe na representação das linhas de Zigue-zague no modo de teste visual de qualquer especialista.

Esta desvantagem torna este método inútil para uso prático em negociação. Mas é extremamente útil para análises técnicas de dados históricos para achar padrões e para avaliar a qualidade de vários sistemas de negociação.

2.4. Detecção de tendência usando o indicador ADX

A maneira considerada a seguir - é detecção de tendência usando o indicador ADX (Índice de Movimento Direcional de Média). Este indicador não é usado apenas para detectar a direção da tendência, mas também para avaliar sua forçar. Esta é uma característica bem valiosa do indicador ADX. A força da tendência é determinada pela linha ADX principal - se o valor é maior que 20 (o nível aceito de modo geral, mas não necessariamente o melhor no momento), então a tendência é forte suficiente.

A direção da tendência é determinada pelas linhas +DI e -DI para um a outra. Este indicador usa a suavização de todas as três linhas com média exponencial, e portante tem um atraso de resposta a mudança de tendência.

Vamos formular a regra de detecção de tendência:

Neste caso, a linha da tendência ADX não é usada para detectar uma tendência. É necessário reduzir o número de sinais falsos deste indicador. Se a tendência for fraca (ADX for menor que 20), é melhor esperar até que fique mais forte, e só então inicie negócio com a tendência.

Vamos ilustrar este método:

Figura 5. Identificando uma Tendência Usando o Indicador ADX

Figura 5. Identificando uma Tendência Usando o Indicador ADX

Aqui nós usamos o gráfico D1 do EURUSD e o indicador ADX com os seguintes parâmetros. PeriodADX = 21 (linha grossa azul - valor da força da tendência ADX, linha fina verde - valor de +DI, linha fina vermelha - valor de -DI).

Na parte inferior da figura você pode o indicador de tendência desenvolvido especialmente - ADXTrendDetector. Para comparação, no gráfico superior (carmesim) do indicador ADXTrendDetector o filtro de força da tendência foi desabilitado (ADXTrendLevel = 0) e no gráfico inferior (azul) - foi habilitado (ADXTrendLevel = 20).

Note, aquela parte do chamado "quique" na detecção da direção da tendência foi abandonada, quando nós ligamos o filtro de força da tendência. É desejável usar este filtro em trabalhos reais. Mais melhorias da qualidade do indicador podem ser alcançadas pela seleção habilidosa de parâmetros externos de acordo com a situação atual no mercado (plano/alcance/tendência) e dependendo da natureza do movimento do par de moedas.

Em geral, este indicador provém uma boa oportunidade para construir sistema de negociação traçadores de tendência, como o filtro de entrada.

2.5. Detecção de tendência usando o indicador NRTR

O método a seguir de detecção de tendência - usando o indicador NRTR (Nick Rypock Trailing Reverse). Este indicador é sempre localizado a uma distância constante do extremo preço alcançado - preços menores em tendências acima e preços menores em tendências abaixo. A ideia principal deste indicador - pequenos movimentos corretivos contra a tendência principal devem ser ignorados, e movimentos contra a tendência principal, excedendo certo nível, mudam os sinais sobre a direção da tendência.

Para esta afirmação vem a regra de detecção da direção da tendência:

Para reduzir a influência de reversões falsas da tendência na flutuação de preço, nós usaremos preços de fechamento para checar a posição da linha NRTR.

Vamos ilustrar este método:

Figura 6. Identificando uma Tendência Usando o Indicador NRTR

Figura 6. Identificando uma tendência usando o indicador NRTR

Estes grandes pontos azuis correspondem a tendência acima, enquanto os grandes pontos vermelhos - a tendência abaixo. Na parte inferior do gráfico é demonstrado nosso indicador de tendência NRTRTrendDetector, descrito abaixo.

2.6. Detecção de tendência usando três candelabros Heiken Ashi

Outra maneira popular de detectar uma tendência - é usando os candelabros Heiken Ashi. Gráficos Heiken Ashi são os gráficos de candelabro Japoneses modificados. Seus valores são parcialmente aferidos com a vela anterior.

Vamos ilustrar este método:

Figura 7. Detecção de Tendência Pelas Cores dos Candelabros Heiken Ashi

Figura 7. Detecção de tendência pelas cores dos candelabros Heiken Ashi

Como você pode ver, este método também não é livre de sinais "falsos", quando o preço flutua em um corredor lateral. Mas pior é que este indicador pode redesenhar não apenas a last bar, mas também a penúltima. Ou seja, o sinal na qual nós entramos, pode ser revertido na próxima barra. Isto é devido ao fato que quando a cor do candelabro é determinada, duas barras são analisadas, então é recomendado usar este método em conjunção com outros sinais suporte.


3. Indicadores de tendência

Agora vamos criar indicadores de tendência.

3.1. Indicadores de tendência baseados em média móvel

O indicador mais fácil, assim como a maneira mais fácil de determinar uma tendência, baseado na média móvel: Vamos considerar, de quais partes ele consiste: Todo o código-fonte do indicador está no arquivo MATrendDetector.MQ5, anexado a este artigo.

No começo da programação do indicador vem a linha, que conecta a biblioteca para calcular as várias médias móveis. Esta biblioteca muda com o Terminal do Cliente e é pronta para usar imediatamente após a instalação. Aqui está a linha:

#include 

Nós usaremos uma função dela, que calcula uma média móvel simples:

double SimpleMA(const int position, const int period, const double &price[])

Aqui você define os parâmetros de entrada:

A função retorna o valor calculado da média móvel.

A próxima parte do texto contém as configurações inicias para demonstrar o indicador na tela:

//---------------------------------------------------------------------
#property indicator_separate_window
//---------------------------------------------------------------------
#property indicator_applied_price       PRICE_CLOSE
#property indicator_minimum             -1.4
#property indicator_maximum             +1.4
//---------------------------------------------------------------------
#property indicator_buffers             1
#property indicator_plots               1
//---------------------------------------------------------------------
#property indicator_type1               DRAW_HISTOGRAM
#property indicator_color1              Black
#property indicator_width1              2
//---------------------------------------------------------------------

Os seguintes parâmetros são configurados:

Os dois últimos parâmetros permitem você configurar uma escala fixa para demonstrar o gráfico indicador. Isto é possível porque nós conhecemos os valores mínimos e máximos do nosso indicador - de -1 a +1, inclusive. Isto é feito para o gráfico ser aparentemente bonito, para não sobrepor bordas de janelas e títulos do indicador na janela.

Depois vem a parte para dar entrada os parâmetros externos do indicador, os quais podem ser mudados durante a colocação do indicador no gráfico e mais tarde, quando este já estiver funcionando:

input int   MAPeriod = 200;

Existe apenas um parâmetro - o valor do período da média móvel.

A próxima parte essencial do indicador - funções, que processam vários eventos, que ocorre quando o indicador funciona no gráfico.

Primeiro vem a função de inicialização - OnInit(). É nomeado imediatamente após carregar o indicador. No nosso indicador, ela aparenta como o seguinte:

void OnInit()
{
  SetIndexBuffer( 0, TrendBuffer, INDICATOR_DATA );
  PlotIndexSetInteger( 0, PLOT_DRAW_BEGIN, MAPeriod );
}

A função SetIndexBuffer() conecta conjuntos previamente declarados, nos quais nós iremos armazenar os valores da tendência TrendBuffer[], com um dos tampões dos indicadores. Nós temos apenas um tampão de indicador, e seu índice é igual a zero.

A função PlotIndexSetInteger() configura o número de barras iniciais sem representá-las na janela do indicador.

Uma vez que é matematicamente impossível calcular uma média móvel simples em um número de barras menor que o seu período, vamos especificar o número de barras, igual ao período da média móvel.

A seguir vem a função que processa eventos sobre a necessidade de recalcular um indicador - OnCalculate():

int OnCalculate(const int _rates_total, 
                const int _prev_calculated,
                const int _begin, 
                const double& _price[ ] )
{
  int  start, i;

//   If number of bars on the screen is less than averaging period, calculations can't be made:
  if( _rates_total < MAPeriod )
  {
    return( 0 );
  }

//  Determine the initial bar for indicator buffer calculation:
  if( _prev_calculated == 0 )
  {
    start = MAPeriod;
  }
  else
  {
    start = _prev_calculated - 1;
  }

//      Loop of calculating the indicator buffer values:
  for( i = start; i < _rates_total; i++ )
  {
    TrendBuffer[ i ] = TrendDetector( i, _price );
  }

  return( _rates_total );
}

Essa função é nomeada pela primeira vez após a inicialização do indicador e cada próxima vez que os dados de preço mudarem. Por exemplo, quando vem um novo turno no símbolo para qual o indicador é calculado. Vamos considerar isto em detalhes.

Primeiramente, cheque se existem números de barras suficientes no gráfico - se for menor que o período da média móvel, então não existe nada para calcular e esta função termina com o operador "return". Se o número de barras for suficiente para os cálculos, determine a barra inicial, a partir de qual o indicador será calculado. Isto é feito de modo a não recalcular todos os valores indicadores a cada turno de preço.

Aqui nós usamos o mecanismo, provido pelo terminal. Toda vez que você nomear uma função manipuladora, cheque o valor do argumento da função _prev_calculated - este é o número de barras, processadas na chamada anterior da função OnCalculate(). Se for zero, então recalcule todos os valores do indicador. Caso contrário, recalcule apenas a última barra com o índice _prev_calculated - 1.

O ciclo de cálculo dos valores do tampão do indicador é realizado pelo operar for - no seu corpo, nós chamamos a função de detecção de tendência TrendDetector para cada valor de tampão de indicador recalculado. Assim, predominando apenas esta função, nós podemos implementar algorítimos diferentes para calcular a direção de tendência. Neste caso, o resto das partes do indicador de fato permanecem imutadas (é possível que, parâmetros externos venham a mudar).

Agora vamos considerar a função da detecção de tendência em si - TrendDetector.

int TrendDetector(int _shift, const double& _price[])
{
  double  current_ma;
  int     trend_direction = 0;

  current_ma = SimpleMA(_shift, MAPeriod, _price);

  if(_price[_shift] > current_ma)
  {
    trend_direction = 1;
  }
  else if(_price[_shift] < current_ma)
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

A função realiza as seguintes funções:

Se a função retornou 0, isto significa que a tendência não pode ser detectada.

O resultado do trabalho do indicador pode ser visto na Figura 2 e Figura 3.

3.2. Indicadores de tendência baseados no leque de médias móveis

Agora vamos ver como na base do indicador, você pode criar um indicador um pouco mais complexo, que use um leque de médias móveis para detectar uma tendência.

Todo o código fonte do indicador está no arquivo FanTrendDetector.MQ5, anexado a este artigo.

A diferença deste indicador para o anterior são as seguintes:

input int MA1Period = 200; // period value of senior moving average
input int MA2Period = 50;  // period value of medium moving average
input int MA3Period = 21;  // period value of junior moving average
int TrendDetector(int _shift, const double& _price[])
{
  double  current_ma1, current_ma2, current_ma3;
  int     trend_direction = 0;

  current_ma1 = SimpleMA(_shift, MA1Period, _price);
  current_ma2 = SimpleMA(_shift, MA2Period, _price);
  current_ma3 = SimpleMA(_shift, MA3Period, _price);

  if(current_ma3 > current_ma2 && current_ma2 > current_ma1)
  {
    trend_direction = 1;
  }
  else if(current_ma3 < current_ma2 && current_ma2 < current_ma1)
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

A função checa se as médias móveis são localizadas na ordem correta, comparando elas entre elas usando o operador if...else e suas ordens. Se as médias estiverem arranjadas em ordem crescente, então ele retorna 1 - tendência acima. Se as médias estiverem arranjadas em ordem decrescente, então ele retorna -1 - tendência abaixo. Se ambas condições, checado no bloco if, forem falsas, ele retorna zero (tendência não pode ser detectada). A função tem dois argumentos de entrada - a mudança no tampão da barra analisada e o tampão em si com uma série de preço.

O resto das partes do indicador são as mesmas como no anterior.

3.3. Indicador de tendência baseado no indicador zigue-zague

Agora vamos considerar o indicador, que usa fraturas de Zigue-zague para determinar os extremos e detectar a direção da tendência de acordo com Charles Dow. Todo o código-fonte do indicador está no arquivo ZigZagTrendDetector.MQ5, anexado a este artigo.

As variáveis externas são atribuídas com valores de parâmetros do indicador externo Zigue-zague:

//---------------------------------------------------------------------
//  External parameters:
//---------------------------------------------------------------------
input int   ExtDepth = 5;
input int   ExtDeviation = 5;
input int   ExtBackstep = 3;
//---------------------------------------------------------------------

Uma diferença importante deste indicador - o número de tampões de indicador. Aqui além do tampão de amostra, nós usamos outros dois tampões de cálculo. Portanto, nós mudamos a configuração apropriada no código do indicador:

#property indicator_buffers  3

Adicione dois tampões adicionais, Eles armazenarão extremos, obtidos do indicador externo ZigZag:

double ZigZagHighs[];  // zigzag's upper turnarounds
double ZigZagLows[];   // zigzag's lower turnarounds

Também é necessário fazer mudanças ao manipulador de eventos da inicialização do indicador - configure estes dois tampões adicionais como tampões de cálculo:

//  Buffers to store zigzag's turnarounds
SetIndexBuffer(1, ZigZagHighs, INDICATOR_CALCULATIONS);
SetIndexBuffer(2, ZigZagLows, INDICATOR_CALCULATIONS);

No código de cálculo da função OnCalculate nós também temos de prover fraturas de leitura zigue-zague em nossos tampões: Isto é feito como seguinte:

//  Copy upper and lower zigzag's turnarounds to buffers:
  CopyBuffer(indicator_handle, 1, 0, _rates_total - _prev_calculated, ZigZagHighs);
  CopyBuffer(indicator_handle, 2, 0, _rates_total - _prev_calculated, ZigZagLows);

//  Loop of calculating the indicator buffer values:
  for(i = start; i < _rates_total; i++)
  {
    TrendBuffer[i] = TrendDetector(i);
  }

A função TrendDetector parece com isto:

//---------------------------------------------------------------------
//  Determine the current trend direction:
//---------------------------------------------------------------------
//  Returns:
//    -1 - Down trend
//    +1 - Up trend
//     0 - trend is not defined
//---------------------------------------------------------------------
double    ZigZagExtHigh[2];
double    ZigZagExtLow[2];
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int    trend_direction = 0;

//  Find last four zigzag's turnarounds:
  int    ext_high_count = 0;
  int    ext_low_count = 0;
  for(int i = _shift; i >= 0; i--)
  {
    if(ZigZagHighs[i] > 0.1)
    {
      if(ext_high_count < 2)
      {
        ZigZagExtHigh[ext_high_count] = ZigZagHighs[i];
        ext_high_count++;
      }
    }
    else if(ZigZagLows[i] > 0.1)
    {
      if(ext_low_count < 2)
      {
        ZigZagExtLow[ext_low_count] = ZigZagLows[i];
        ext_low_count++;
      }
    }

//  If two pairs of extrema are found, break the loop:
    if(ext_low_count == 2 && ext_high_count == 2)
    {
      break;
    }
  }

//  If required number of extrema is not found, the trend can't be determined:
  if(ext_low_count != 2 || ext_high_count != 2)
  {
    return(trend_direction);
  }

//  Check Dow's condition fulfillment:
  if(ZigZagExtHigh[0] > ZigZagExtHigh[1] && ZigZagExtLow[0] > ZigZagExtLow[1])
  {
    trend_direction = 1;
  }
  else if(ZigZagExtHigh[0] < ZigZagExtHigh[1] && ZigZagExtLow[0] < ZigZagExtLow[1])
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

Aqui nós procuramos os quatro últimos extremos do zigue-zague. Note que a procura volta na história. É por isto que o índice no ciclo for diminui até zero a cada repetição de procura. Se os extremos são achados, eles são comparados entre eles para consistência da definição da tendência, de acordo com Dow. Existem duas localizações possíveis de extremos - para a tendência ascendente e para tendência descendente. Estas variantes são checadas pelos operadores if...else.

3.4. Indicador de tendência baseado no indicador ADX.

Considere o indicador de tendência ADXTrendDetector, que usa o indicador ADX. Todo o código-fonte do indicador está no arquivo ADXTrendDetector.MQ5, anexado a este artigo. Os parâmetros externos são atribuídos com valores de parâmetros do indicador externo ADX:

//---------------------------------------------------------------------
//      External parameters
//---------------------------------------------------------------------
input int  PeriodADX     = 14;
input int  ADXTrendLevel = 20;

A função TrendDetector parece com isto:

//---------------------------------------------------------------------
//  Determine the current trend direction:
//---------------------------------------------------------------------
//  Returns:
//    -1 - Down trend
//    +1 - Up trend
//     0 - trend is not defined
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int     trend_direction = 0;
  double  ADXBuffer[ 1 ];
  double  PlusDIBuffer[ 1 ];
  double  MinusDIBuffer[ 1 ];

//  Copy ADX indicator values to buffers:
  CopyBuffer(indicator_handle, 0, _shift, 1, ADXBuffer);
  CopyBuffer(indicator_handle, 1, _shift, 1, PlusDIBuffer);
  CopyBuffer(indicator_handle, 2, _shift, 1, MinusDIBuffer);

//  If ADX value is considered (trend strength):
  if(ADXTrendLevel > 0)
  {
    if(ADXBuffer[0] < ADXTrendLevel)
    {
      return(trend_direction);
    }
  }

//  Check +DI and -DI positions relative to each other:
  if(PlusDIBuffer[0] > MinusDIBuffer[0])
  {
    trend_direction = 1;
  }
  else if(PlusDIBuffer[0] < MinusDIBuffer[0])
  {
    trend_direction = -1;
  }

  return( trend_direction );
}

Usando o CopyBuffer(), obtenha os valores necessários dos tampões indicadores do indicador externo ADX para um número de barras, dados pelo argumento _shift. A seguir, vamos analisar as posições das linhas +D1 e -D1 em relação a si mesmas. Se necessário, considere a força da tendência - se for menor que o definido, então a tendência não é detectada.

3.5. Indicador de Tendência Baseado no Indicador NRTR.

A estrutura do indicador de tendência NRTRTrendDetector, baseada no NRTR, é semelhante ao anterior. Todo o código-fonte do indicador está no arquivo NRTRTrendDetector.MQ5, anexado a este artigo.

A primeira diferença - no bloco de parâmetros externos:

//---------------------------------------------------------------------
//      External parameters:
//---------------------------------------------------------------------
input int     ATRPeriod =  40;    // ATR period, in bars
input double  Koeff     = 2.0;    // Coefficient of ATR value change   
//---------------------------------------------------------------------

A segunda diferença - na função TrendDetector de detecção de direção da tendência:

//---------------------------------------------------------------------
//      Determine the current trend direction:
//---------------------------------------------------------------------
//  Returns:
//    -1 - Down trend
//    +1 - Up trend
//     0 - trend is not defined
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int     trend_direction = 0;
  double  Support[1];
  double  Resistance[1];

//      Copy NRTR indicator values to buffers::
  CopyBuffer(indicator_handle, 0, _shift, 1, Support);
  CopyBuffer(indicator_handle, 1, _shift, 1, Resistance);

//  Check values of indicator lines:
  if(Support[0] > 0.0 && Resistance[0] == 0.0)
  {
    trend_direction = 1;
  }
  else if(Resistance[0] > 0.0 && Support[0] == 0.0)
  {
    trend_direction = -1;
  }

  return( trend_direction );
}

Aqui nós lemos os valores de dois tampões do indicador externo NRTR com índices 0 e 1. Os valores no tampão Support são diferentes de zero quando tem uma tendência crescente, e o valor no tampão Resistence são diferente de zero quando tem uma tendência decrescente.

3.6. Indicador de tendência baseado em candelabros Heiken Ashi

Agora vamos considerar o indicador de tendência, que usa os candelabros Heiken Ashi.

Neste caso, nós não nomearemos o indicador externo, mas calcularemos as velas por nós mesmos. Isto melhorará a performance do indicador e liberar o CPU para tarefas mais importantes. Todo o código fonte do indicador está no arquivo HeikenAshiTrendDetector.MQ5, anexado a este artigo.

Uma vez que o indicador Heiken Ashi não assume configuração de parâmetros externos, nós podemos bloquear os operadores input. Mudanças substanciais nos esperam no manipulador do evento recalculação de indicadores. Aqui nós usaremos uma variante alternativa de manipulador, que provém acesso a todos os conjuntos de preço do gráfico atual.

A função OnCalculate() se parece agora com isto:

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& TickVolume[],
              const long& Volume[], 
              const int& Spread[])
{
  int     start, i;
  double  open, close, ha_open, ha_close;

//  Determine the initial bar for indicator buffer calculation:
  if(_prev_calculated == 0)
  {
    open = Open[0];
    close = Close[0];
    start = 1;
  }
  else
  {
    start = _prev_calculated - 1;
  }

//  Loop of calculating the indicator buffer values:
  for(i = start; i < _rates_total; i++)
  {
//  Heiken Ashi candlestick open price:
    ha_open = (open + close) / 2.0;

//  Heiken Ashi candlestick close price:
    ha_close = (Open[i] + High[i] + Low[i] + Close[i]) / 4.0;

    TrendBuffer[i] = TrendDetector(ha_open, ha_close);

    open = ha_open;
    close = ha_close;
  }

  return(_rates_total);
}

Para determinar a cor das velas Heiken Ashi nós precisamos de duas peças - a abertura e fechamento, depois contar apenas eles.

Após detectar a direção da tendência através da nomeação da função TrendDetector, salve os valores atuais de preço do candelabro Heiken Ashi em variáveis intermediárias open e close. A função TrendDetector aparenta bem simples. Você pode inseri-la no OnCalculate, mas para maior versatilidade no caso de desenvolvimento e complexidade futuros de algorítimos, nós deixamos esta função. Aqui está esta função:

int TrendDetector(double _open, double _close)
{
  int    trend_direction = 0;

  if(_close > _open)         // if candlestick is growing, then it is the up trend
  {
    trend_direction = 1;
  }
  else if(_close < _open)     // if candlestick is falling, then it is the down trend
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

Os argumentos da função são dois preços do candelabro Heiken Ashi - abertura e fechamento, pelos quais sua direção é determinada.


4. Exemplo do uso do indicador de detecção de tendência em Especialista

Vamos criar um Conselheiro Especialista, que use indicadores diferentes. Será interessante comparar os resultados dos especialistas, que usam diferentes maneiras de detecção de tendência. Primeiro, cheque os resultados com os parâmetros padrões, então, tente ajustá-los para achar os melhores.

Neste caso, o propósito de criar um Conselheiros Especialistas - é comparar métodos de detecção de tendência pela precisão e velocidade. Portanto, vamos formular os princípios gerais da criação de todos os Conselheiros Especialistas:

Todos os indicadores de tendência que nós criamos contêm tampão indicador com índice zero, o qual armazena os dados requeridos sobre a direção da tendência. Nós usaremos no Conselheiro Especialista para obter um sinal de abrir/fechar uma posição.

Por necessitarmos das funções de negociação, incluímos a biblioteca correspondente, que é instalada junto com o MetaTrader 5. Esta biblioteca contém a classe CTrade e diversos métodos de trabalho com posições e pedidos. Isto simplifica a rotina de trabalho com funções de comércio. Esta biblioteca é inclusa na seguinte linha:

#include 

Nós usaremos dois de seus métodos: abertura e fechamento de posição. O primeiro método permite você abrir uma posição de uma dada direção e volume:

PositionOpen(const string symbol, 
             ENUM_ORDER_TYPE order_type,
             double volume, double price,
             double sl, double tp, const string comment )

Os argumentos de entrada são os seguintes:

O segundo método permite você fechar uma posição:

PositionClose( const string symbol, ulong deviation )

Os argumentos de entrada são os seguintes:

Vamos considerar em detalhes a estrutura do Conselheiro Especialista que utiliza o indicador MATrendDetector. Todo o código fonte do Conselheiro Especialista está no arquivo HeikenAshiTrendDetector.MQ5, anexado a este artigo. O primeiro importante bloco do especialista - é o bloco de configuração dos parâmetros externos.

input double Lots = 0.1;
input int    MAPeriod = 200;

O parâmetro Lots de um Conselheiro Especialista - é o tamanho do lote, usado quando a posição é aberta. Para obter resultados comparativos de diferentes métodos de detecção de tendência, nós usamos o lote permanente sem administração de dinheiro. Todos os outros parâmetros externos são usados pelos indicadores de tendência, discutidos acima. A lista e propósito são exatamente os mesmos que o indicador correspondente.

O segundo importante bloco do Conselheiro Especialista - manipulador de eventos da inicialização do Conselheiro Especialista.

//---------------------------------------------------------------------
//      Initialization event handler:
//---------------------------------------------------------------------
int OnInit()
{
//  Create external indicator handle for future reference to it:
  ResetLastError();
  indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Examples\\MATrendDetector", MAPeriod);

// If initialization was unsuccessful, return nonzero code:
  if(indicator_handle == INVALID_HANDLE)
  {
    Print("MATrendDetector initialization error, Code = ", GetLastError());
    return(-1);
  }
  return(0);
}

Aqui crie um manipulador para referir ao indicador de tendência e, se a criação foi bem sucedida, retorne código zero. Se houve falha durante a criação do indicador do manipulador (por exemplo, o indicador não foi compilado no formato EX5), nós imprimimos a mensagem sobre isto e retornamos o código não-zero. Neste caso, o Conselheiro Especialista interrompe o seu trabalho futuro e é descarregado do terminal, com a mensagem correspondente no Diário.

O próximo bloco do Conselheiro Especialista - identificador de eventos de desinicialização do Consultor Especialista.

//---------------------------------------------------------------------
//      Indicator deinitialization event handler:
//---------------------------------------------------------------------
void OnDeinit(const int _reason)
{
//  Delete indicator handle:
  if(indicator_handle != INVALID_HANDLE)
  {
    IndicatorRelease(indicator_handle);
  }
}

Aqui o identificador do indicador é deletado e sua memória alocada é liberada.

Você não precisa realizar nenhuma outra ação para desinicializar o Conselheiro Especialista.

Depois vem o bloco principal do Conselheiro Especialista - identificador de evento sobre a nova teca pelo atual símbolo.

//---------------------------------------------------------------------
//  Handler of event about new tick by the current symbol:
//---------------------------------------------------------------------
int    current_signal = 0;
int    prev_signal = 0;
bool   is_first_signal = true;
//---------------------------------------------------------------------
void OnTick()
{
//  Wait for beginning of a new bar:
  if(CheckNewBar() != 1)
  {
    return;
  }

//  Get signal to open/close position:
  current_signal = GetSignal();
  if(is_first_signal == true)
  {
    prev_signal = current_signal;
    is_first_signal = false;
  }

//  Select position by current symbol:
  if(PositionSelect(Symbol()) == true)
  {
//  Check if we need to close a reverse position:
    if(CheckPositionClose(current_signal) == 1)
    {
      return;
    }
  }

//  Check if there is the BUY signal:
  if(CheckBuySignal(current_signal, prev_signal) == 1)
  {
    CTrade  trade;
    trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, Lots, SymbolInfoDouble(Symbol(), SYMBOL_ASK ), 0, 0);
  }

//  Check if there is the SELL signal:
  if(CheckSellSignal(current_signal, prev_signal) == 1)
  {
    CTrade  trade;
    trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, Lots, SymbolInfoDouble(Symbol(), SYMBOL_BID ), 0, 0);
  }

//  Save current signal:
  prev_signal = current_signal;
}

Vamos considerar as funções auxiliares, que são usadas pelo Conselheiro Especialista.

Primeiro de tudo, nosso Conselheiro Especialista tem de checar o sinal para abrir outra barra nova no gráfico. Para isso, a função CheckNewBar é usada:

//---------------------------------------------------------------------
//  Returns flag of a new bar:
//---------------------------------------------------------------------
//  - if it returns 1, there is a new bar
//---------------------------------------------------------------------
int CheckNewBar()
{
  MqlRates  current_rates[1];

  ResetLastError();
  if(CopyRates(Symbol(), Period(), 0, 1, current_rates)!= 1)
  {
    Print("CopyRates copy error, Code = ", GetLastError());
    return(0);
  }

  if(current_rates[0].tick_volume>1)
  {
    return(0);
  }

  return(1);
}

A presença de uma nova barra determina o valor do volume do turno. Quando abrindo uma nova barra, o volume para tal é igual a zero inicialmente (uma vez que tenham existidos citações). Com o novo turno chegando, o tamanho se torna igual a 1.

Nesta função nós criaremos o conjunto current_rates[] das estruturas MqlRates, consistindo de um elemento, copia o preço atual e avoluma informações a este, e então checa o valor do volume do turno.

Em nosso manipulador de evento sobre o novo turno pelo símbolo atual, nós usaremos esta função da seguinte maneira:

//  Wait for beginning of a new bar:
if(CheckNewBar()!= 1)
{
  return;
}

Então, novas barras abrem, e você pode obter um sinal sobre a direção atual da tendência. Isto é feito como seguinte:

//  Get signal to open/close position:
  current_signal = GetSignal();
  if(is_first_signal == true)
  {
    prev_signal = current_signal;
    is_first_signal = false;
  }

Uma vez que nós precisamos acompanhar as mudanças na tendência, é necessário lembrar o valor da tendência na barra anterior. Neste pedaço de código acima, nós usamos a variável prev_signal. Também, você deve usar a bandeira, sinalizado que este é o primeiro sinal (não existem anteriores ainda). Esta é a variável is_first_signal. Se esta bandeira tem o valor verdadeiro, nós inicializamos a variável prev_signal com o valor inicial.

Aqui nós usamos a função GetSignal, que retorna a direção atual da tendência, obtida do nosso indicador. Ela é assim:

//---------------------------------------------------------------------
//      Get signal to open/close position:
//---------------------------------------------------------------------
int GetSignal()
{
  double    trend_direction[1];

//  Get signal from trend indicator:
  ResetLastError();
  if(CopyBuffer(indicator_handle, 0, 0, 1, trend_direction) != 1)
  {
    Print("CopyBuffer copy error, Code = ", GetLastError());
    return(0);
  }

  return((int)trend_direction[0]);
}

A data do indicador de tendência é copiada do tampão zero para nosso conjunto trend_direction, consistindo de um elemento. E o valor do elemento de conjunto é retornado da função. Também o tipo duplo é lançado no tipo int para evitar avisos do compilador.

Antes de abrir nova posição, você deve checar se é necessário fechar a posição oposta, aberta anteriormente. Você também deve checar se já existe uma posição aberta na mesma direção. Tudo isto é feito pelo seguinte pedaço de código:

//  Select position by current symbol:
  if(PositionSelect(Symbol()) == true)
  {
//  Check if we need to close a reverse position:
    if(CheckPositionClose(current_signal) == 1)
    {
      return;
    }
  }

De modo a obter acesso a posição, primeiro ela deve ser selecionada - isto é feito usando a função PositionSelect() para o símbolo atual. Se a função retornar verdadeira, então a posição existe e foi selecionada de maneira bem sucedida, então você pode manipulá-la.

Para fechar a posição oposta a função CheckPositionClose é usada:

//---------------------------------------------------------------------
//  Check if we need to close position:
//---------------------------------------------------------------------
//  Returns:
//    0 - no open position
//    1 - position already opened in signal's direction
//---------------------------------------------------------------------
int CheckPositionClose(int _signal)
{
  long    position_type = PositionGetInteger(POSITION_TYPE);

  if(_signal == 1)
  {
//  If there is the BUY position already opened, then return:
    if(position_type == (long)POSITION_TYPE_BUY)
    {
      return(1);
    }
  }

  if(_signal==-1)
  {
//  If there is the SELL position already opened, then return:
    if( position_type == ( long )POSITION_TYPE_SELL )
    {
      return(1);
    }
  }

//  Close position:
  CTrade  trade;
  trade.PositionClose(Symbol(), 10);

  return(0);
}

Primeiro, cheque quando a posição está aberta na direção da tendência. Caso esteja, a função retorna 1, e a posição atual não é fechada. Se a posição está aberta na direção de tendência oposta, então você deve fechá-la. Isto é feito pelo método PositionClose descrito acima. Uma vez que a posição não existe mais, ela retorna zero.

Uma vez que todas as checagens e ações necessárias para posição existente foram feitas, você deve checar a presença de um novo sinal. Isto é feito pelo seguinte pedaço de código:

//  Check if there is the BUY signal:
if(CheckBuySignal(current_signal, prev_signal)==1)
{
  CTrade  trade;
  trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, Lots, SymbolInfoDouble(Symbol(), SYMBOL_ASK), 0, 0);
}

Se existir o sinal para Compra, a posição longa aberta com volume dado pelo preço atual SYMBOL_ASK. Uma vez que todas as posições forem fechadas pelo sinal oposto, então o Obter Lucro e Parada Protetora não são usados. O Conselheiro Especialista está "sempre no mercado".

Na negociação real é recomendado usar uma ordem protetora de Parar Perdas (Stop Loss) no caso de circunstâncias imprevistas, como perda de conexão com o servidor DC e outras condições de força maior.

Para o sinal Venda tudo é semelhante:

//  Check if there is the SELL signal:
if(CheckSellSignal(current_signal, prev_signal) == 1)
{
  CTrade  trade;
  trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, Lots, SymbolInfoDouble(Symbol(), SYMBOL_BID), 0, 0);
}

A única diferença é no preço de venda - SYMBOL_BID.

A presença de um sinal é checada pela função CheckBuySignal - para compra e pela função CheckSellSignal - para venda. Estas funções são bastante simples e claras:

//---------------------------------------------------------------------
//  Check if signal has changed to BUY:
//---------------------------------------------------------------------
//  Returns:
//    0 - no signal
//    1 - there is the BUY signal
//---------------------------------------------------------------------
int CheckBuySignal(int _curr_signal, int _prev_signal)
{
//  Check if signal has changed to BUY:
  if((_curr_signal==1 && _prev_signal==0) || (_curr_signal==1 && _prev_signal==-1))
  {
    return(1);
  }

  return(0);
}

//---------------------------------------------------------------------
//  Check if there is the SELL signal:
//---------------------------------------------------------------------
//  Returns:
//    0 - no signal
//    1 - there is the SELL signal
//---------------------------------------------------------------------
int CheckSellSignal(int _curr_signal, int _prev_signal)
{
//  Check if signal has changed to SELL:
  if((_curr_signal==-1 && _prev_signal==0) || (_curr_signal==-1 && _prev_signal==1))
  {
    return(1);
  }

  return(0);
}

Aqui nós checamos se a tendência mudou para a posição oposta ou se a direção da tendência apareceu. Se quaisquer destas condições forem satisfeitas, a função retorna uma presença de sinal.

De maneira geral, tal esquema de Conselheiro Especialista dá uma estrutura bastante universal, que pode ser facilmente atualizada e expandida para comportar algorítimos mais complexos.

Outro Conselheiro Especialista é construído exatamente igual. Existem diferenças significativas apenas no bloco de parâmetros externos - eles devem corresponder ao indicador de tendência usado e devem ser passados como argumento quando criando um manipulador de indicador.

Vamos considerar os resultados do nosso primeiro Conselheiro Especialista em dados históricos. Nós usaremos a história do EURUSD, em uma extensão de 01.04.2004 a 06.08.2010 nas barras diárias. Após executar o Conselheiro Especialista no Testador Estratégico com parâmetros padrões, nós obtivemos os seguintes resultados:

Figura 8. Resultado do Teste do Conselheiro Especialista Utilizando o Indicador MATrendDetector

Figura 8. Resultado do teste do Conselheiro Especialista utilizando o indicador MATrendDetector

Relatório do Testador Estratégico
MetaQuotes-Demo (Build 302)

Configurações
Especialista: MATrendExpert
Símbolo: EURUSD
Período: Diariamente (01.04.2004 - 06.08.2010)
Entradas: Lots=0.100000

MAPeriod=200
Corretor: MetaQuotes Software Corp.
Moeda: USD
Depósito Inicial: 10 000.00

Resultados
Barras: 1649 Ticks: 8462551
Lucro total da rede: 3 624.59 Lucro bruto: 7 029.16 Prejuízo bruto: -3 404.57
Fator lucro: 2.06 Pagamento esperado: 92.94
Fator de Recuperação: 1.21 Taxa de Sharpe: 0,14

Queda de balanço:
Queda absoluta de balanço: 2 822.83 Queda máxima de balanço: 2 822.83 (28.23%) Queda relativa de balanço: 28.23% (2 822.83)
Queda de lucro líquido:
Queda absoluta de lucro líquido: 2 903.68 Queda máxima de lucro líquido: 2 989.93 (29.64%) Queda relativa de lucro líquido: 29.64% (2 989.93)

Total de negócios: 39 Negócios curtos ( ganho 0%): 20 (20.00%) Negócios longos ( ganho 0%): 19 (15.79%)
Total de acordos: 78 Negócios lucrativos ( % do total): 7 (17.95%) Negócios com prejuízo ( % do total): 32 (82.05%)

Negócio com maior lucro: 3 184.14 Negócios com maior prejuízo (% do total): -226,65

Média de negociação lucrativa: 1 004.17 Negócios de negociação com prejuízo (% do total): -106,39

Máximo de ganhos consecutivos ($): 4 (5 892.18) Máximo de prejuízo consecutivo ($): 27 (-2 822.83)

Máximo de lucro consecutivo (contagem): 5 892.18 (4) Máximo de prejuízo consecutivo (contagem): -2 822.83 (27)

Médias de ganhos consecutivos: 2 Médias de perdas consecutivas: 8


Em geral parece bom, exceto a sessão do início do teste até 22.09.2004. Não existem garantias que esta sessão será repetida no futuro. Se você olhar no gráfico deste período, você pode ver que houve um movimento lateral predominante em uma extensão limitada. Sob essas condições, o nosso simples especialista em comércio não foi tão bom. Aqui esta a figura deste período com os negócios nela colocados:

Figura 9. Seleção com Movimento Lateral

Figura 9. Seleção com Movimento Lateral

Também existe a média móvel SMA200 no gráfico.

Agora vamos ver o qual se mostrará o Conselheiro Especialista mais "avançado" usando indicador com várias médias móveis - no mesmo intervalo e parâmetros padrões:

Figura 10. Resultado do Teste do Conselheiro Especialista Utilizando o Indicador FanTrendDetector

Figura 10. Resultado do teste do Conselheiro Especialista utilizando o indicador FanTrendDetector

Relatório do Testador Estratégico
MetaQuotes-Demo (Build 302)

Configurações
Especialista: FanTrendExpert
Símbolo: EURUSD
Período: Diariamente (01.04.2004 - 06.08.2010)
Entradas: Lots=0.100000

MA1Period=200

MA2Period=50

MA3Period=21
Corretor: MetaQuotes Software Corp.
Moeda: USD
Depósito Inicial: 10 000.00

Resultados
Barras: 1649 Ticks: 8462551
Lucro total da rede: 2 839.63 Lucro bruto: 5 242.93 Prejuízo bruto: -2 403.30
Fator lucro: 2,18 Pagamento esperado: 149,45
Fator de Recuperação: 1,06 Taxa de Sharpe: 0,32

Queda de balanço:
Queda absoluta de balanço: 105,20 Queda máxima de balanço: 1 473.65 (11.73%) Queda relativa de balanço: 11.73% (1 473.65)
Queda de lucro líquido:
Queda absoluta de lucro líquido: 207,05 Queda máxima de lucro líquido: 2 671.98 (19.78%) Queda relativa de lucro líquido: 19.78% (2 671.98)

Total de negócios: 19 Negócios curtos ( ganho 0%): 8 (50.00%) Negócios longos ( ganho 0%): 11 (63.64%)
Total de acordos: 38 Negócios lucrativos ( % do total): 11 (57.89%) Negócios com prejuízo ( % do total): 8 (42.11%)

Negócio com maior lucro: 1 128.30 Negócios com maior prejuízo (% do total): -830,20

Média de negociação lucrativa: 476,63 Negócios de negociação com prejuízo (% do total): -300,41

Máximo de ganhos consecutivos ($): 2 (1 747.78) Máximo de prejuízo consecutivo ($): 2 (-105.20)

Máximo de lucro consecutivo (contagem): 1 747.78 (2) Máximo de prejuízo consecutivo (contagem): -830,20 (1)

Médias de ganhos consecutivos: 2 Médias de perdas consecutivas: 1

Muito melhor! Se você olhar a nossa sessão "problema", a qual o especialista anterior desistiu antes, a figura será a seguinte:

Figura 11. Resultados do FanTrendExpert na Sessão com Movimento Lateral

Figura 11. Resultados do FanTrendExpert na Sessão com Movimento Lateral

Compare ela com a Figura 9 - é óbvio que, o número de alarmes falsos de mudança da tendência diminuiu. Mas o número de acordos foi diminuído pela metade, o que é bastante lógico. Quando analisando a curva de balanço/equidade de ambos os Conselheiros Especialistas, você pode ver que vários acordos foram fechados abaixo de ótimos em termos de obtenção de lucro máximo. Portanto, a próxima atualização do Conselheiro Especialista - é a melhoria do algorítimo de fechamento de negócio. Mas isto está além do escopo deste artigo. Os leitores devem realizar isto por si mesmos.


5. Resultados de teste dos Conselheiros Especialistas

Vamos testar todos nossos especialistas. Os resultados em toda a história disponível se estendem desde 1993 até 2010, no par EURUSD e o período D1 é apresentado abaixo.

Figura 12. Testando MATrendExpert

Figura 12. Testando MATrendExpert

Figura 13. Testando FanTrendExpert

Figura 13. Testando FanTrendExpert

Figura 14. Testando ADXTrendExpert (ADXTrendLevel = 0)

Figura 14. Testando ADXTrendExpert (ADXTrendLevel = 0)

Figura 15. Testando ADXTrendExpert (ADXTrendLevel = 20)

Figura 15. Testando ADXTrendExpert (ADXTrendLevel = 20)

Figura 16. Testando NRTRTrendExpert

Figura 16. Testando NRTRTrendExpert

Figura 17. Testando Heiken Ashi

Figura 17. Testando Heiken Ashi

Vamos considerar os resultados dos testes.

Como líderes existem dois Conselheiros Especialistas mais comuns - um em média móvel e o no "leque" de médias móveis. De fato, estes especialistas são os mais próximos a regra de acompanhar a tendência (e, então, o preço), simplesmente por usar séries amaciadas de preços para o último período de tempo. Porque nós usamos médias móveis um tanto "pesadas" com o período de 200, o impacto da volatilidade do mercado parecer ser diminuído.

Baixo número de negociações destes Conselheiros Especialistas não é uma desvantagem, uma vez que tempo de retenção de posição deve durar até vários meses - seguindo a tendência de 200 dias. De modo interessante, como MATrendExpert altera áreas da tendências, aonde o balanço está crescendo, plano (no contexto do especialista), aonde o dinheiro está sendo perdido.

Método de detecção de tendência no indicador ADX também dá bons resultados. Aqui o PeriodADX foi mudado um pouco para o valor de 17, o que dá resultados mais uniformes por toda história. Efeito de filtro pela força da tendência não é significante. Talvez seja necessário você ajustar o parâmetro ADXTrendLevel, ou até o configurar dinamicamente dependendo da volatilidade atual do mercado. Existem vários períodos de queda e, portanto, medidas adicionais para equalizar o balanço são requeridas.

Indicador NRTR demonstrou praticamente zero rentabilidade usando as configurações padrões, tanto em toda extensão de teste, quanto em intervalos longos escolhidos ao acaso. De certa maneira isto é um sinal de estabilidade deste método de detecção da tendência. Talvez, ajustar os parâmetros torne este Conselheiro Especialista lucrativo, ou seja, é necessária otimização.

Conselheiro Especialista baseado no Heiken Ashi foi obviamente não lucrativo. Apesar de parecer bonito na história, provavelmente por causa do redesenhamento em tempo real, os resultados de teste estão longe do ideal. Talvez resultados melhores serão alcançados usando uma versão atenuada deste indicador - Heiken Ashi Atenuado, o qual não é inclinado a redesenhar.

Definitivamente, todos os Conselheiros Especialistas serão beneficiados com um sistema para condução de uma posição de abertura com retirada do nível de parada dinâmica e com a criação de um nível alvo. Também será bom ter um sistema de administração de capital, permitindo minimizar a queda e, possivelmente, aumentando o lucro em um intervalo longo.


Conclusão

Assim, não é tão difícil escrever um código que detecta a tendência. A coisa principal aqui - trabalhando e uma ideia sensível, explorando algumas leis do mercado. E o quanto mais fundamentais que estas leis venham a ser, mais confiante você estará em sistemas de comércio baseado nestas leis - não quebrarão após um curto período de tempo.