English Русский 中文 Español Deutsch 日本語
preview
Como construir e otimizar um sistema de trading baseado em volume (Chaikin Money Flow - CMF)

Como construir e otimizar um sistema de trading baseado em volume (Chaikin Money Flow - CMF)

MetaTrader 5Negociação |
112 5
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Introdução

Bem-vindo a um novo artigo onde exploramos novos indicadores em termos de criação, construção de sistemas de trading baseados em seus conceitos e otimização desses sistemas para obter melhores percepções e resultados relacionados a lucros e riscos. Neste artigo, apresentaremos um novo indicador técnico baseado em volume chamado indicador Chaikin Money Flow (CMF).

É melhor mencionar aqui algo importante: o principal objetivo deste tipo de artigo é compartilhar novas ferramentas técnicas que podem ser usadas sozinhas ou em conjunto com outras ferramentas, com base na natureza dessas ferramentas, além de testá-las e otimizá-las para obter melhores resultados e verificar se podem ser úteis ou não.

Produziremos este novo indicador (CMF) de acordo com os tópicos abaixo:

  1. Chaikin Money Flow: compreender o conhecimento básico desta ferramenta técnica definindo-a, como pode ser calculada e como pode ser utilizada.
  2. Indicador Chaikin Money Flow personalizado: aprender como programar nosso indicador personalizado por modificação ou aplicação das nossas preferências.
  3. Estratégias Chaikin Money Flow: analisaremos algumas estratégias simples de trading que fazem parte do nosso sistema de trading.
  4. Sistema de trading Chaikin Money Flow: construir, testar e otimizar esses sistemas simples de trading.
  5. Conclusão

Aviso legal: Todas as informações fornecidas são “como estão” apenas para fins educacionais e não foram preparadas para uso em trading ou como aconselhamento. As informações não garantem qualquer tipo de resultado. Se você optar por utilizar estes materiais em qualquer uma de suas contas de trading, fará isso por sua própria conta e risco e será o único responsável.



Chaikin Money Flow

O Chaikin Money Flow (CMF) é um indicador técnico baseado em volume considerando a ação do preço. Ele pode ser usado sozinho ou em conjunto com outras ferramentas para fornecer melhores insights, como veremos. O (CMF) é um indicador desenvolvido por Marc Chaikin para monitorar a acumulação e distribuição de um instrumento ao longo do tempo. A ideia principal por trás do CMF é que, conforme o preço de fechamento se aproxima da máxima, há acumulação. Por outro lado, conforme o fechamento se aproxima da mínima, é um sinal de distribuição. Um resultado positivo de CMF ocorre quando o preço fecha consistentemente acima do ponto médio da barra com volume crescente. Um resultado negativo ocorre quando o fechamento permanece abaixo do ponto médio com volume crescente.

Abaixo estão as etapas pelas quais o indicador CMF é calculado:

  • Calculando o Multiplicador de Fluxo de Dinheiro
Money Flow Multiplier = [(Close - Low) - (High - Close)] / (High - Low)
  • Calculando o Volume de Fluxo de Dinheiro

Money Flow Volume = Money Flow Multiplier * Period's Volume

  • Calculando o CMF

CMF de n períodos = soma de n períodos do Volume de Fluxo de Dinheiro / soma de n períodos do Volume

Após calcular o indicador, ele exibirá uma leitura entre +1 e -1 e podemos encontrá-lo como na figura a seguir:

cmfInd

Como podemos ver, o indicador pode ser uma linha que oscila ao redor do zero; mudanças no CMF e no momentum de compra ou venda podem ser identificadas por qualquer cruzamento acima ou abaixo de 0. Valores positivos acima de zero indicam força compradora; por outro lado, o indicador cairá abaixo de zero se a força vendedora começar a assumir o controle. Quando o CMF oscila ao redor da linha zero, podemos indicar poder de compra e venda relativamente iguais ou ausência de tendência clara. O CMF é utilizado como ferramenta para identificar e avaliar tendências no instrumento negociado.

Na próxima seção, podemos ajustar nosso indicador como quisermos, como desenhá-lo em forma de histograma em vez de linha, ou qualquer outra forma que acreditarmos ajudar na melhoria de nossa negociação. Esse é o objetivo de personalizar qualquer indicador de acordo com nossas preferências por meio da escrita ou edição de seu código.


Indicador Chaikin Money Flow personalizado

Nesta seção, mostraremos como programar um indicador CMF personalizado passo a passo para utilizá-lo posteriormente em nosso sistema simples de trading. Como veremos, nossa personalização será simples, já que o objetivo principal é abrir a mente do leitor para novos pontos de vista e ideias sobre o indicador e estratégias que podem ser utilizadas com base neste indicador de volume.

A seguir estão as etapas que seguiremos para programar esse indicador personalizado:

Especificar parâmetros adicionais ao lado de #property para identificar os valores abaixo referentes ao comportamento e aparência do indicador.

  • A descrição do indicador utilizando a constante description e especificando "Chaikin Money Flow".
#property description "Chaikin Money Flow"
  • A posição do indicador como uma janela separada no gráfico utilizando a constante (indicator_separate_window).
#property indicator_separate_window
  • Número de buffers para cálculo do indicador usando a constante (indicator_buffers), definido aqui como 4.
#property indicator_buffers 4
  • Número de séries gráficas no indicador usando a constante (indicator_plots), definido aqui como 1.
#property indicator_plots   1
  • Espessura da linha na série gráfica usando a constante (indicator_width1), onde 1 é o número da série gráfica; definido aqui como 3.
#property indicator_width1  3
  • Níveis horizontais 1, 2 e 3 na janela do indicador separado utilizando as constantes (indicator_level1), (indicator_level2) e (indicator_level3) para os níveis (0), (0.20) e (-0.20).
#property indicator_level1  0
#property indicator_level2  0.20
#property indicator_level3  -0.20
  • O estilo dos níveis horizontais do indicador utilizando (indicator_levelstyle), aqui definido como STYLE_DOT
#property indicator_levelstyle STYLE_DOT
  • A espessura dos níveis horizontais do indicador utilizando (indicator_levelwidth), aqui definida como 0
#property indicator_levelwidth 0
  • A cor dos níveis horizontais do indicador utilizando (indicator_levelcolor), definida como clrBlack
#property indicator_levelcolor clrBlack
  • O tipo de plotagem gráfica utilizando (indicator_type1), definido como DRAW_HISTOGRAM
#property indicator_type1   DRAW_HISTOGRAM
  • A cor de exibição da linha N utilizando (indicator_color1), aqui utilizamos clrBlue
#property indicator_color1  clrBlue
  • Especificar entradas para definir as preferências do usuário em termos de períodos e tipos de volume que serão usados no cálculo do indicador utilizando a função input
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
  • Declarar o array cmfBuffer
double                    cmfBuffer[];

Na função OnInit() para configurar o indicador,

Vincular um array dinâmico unidimensional do tipo double ao buffer do indicador CMF utilizando (SetIndexBuffer), cujos parâmetros são:

  • index: para especificar o índice do buffer, que será 0
  • buffer[]: para especificar o array, que será o cmfBuffer 
  • data_type: para especificar o tipo de dado armazenado no array do indicador.
SetIndexBuffer(0,cmfBuffer,INDICATOR_DATA);

Definir o valor da propriedade correspondente do indicador utilizando (IndicatorSetInteger) e seus parâmetros são:

  • prop_id: para definir o identificador, que será (INDICATOR_DIGITS).
  • prop_value: para definir o valor decimal a ser configurado, que será (5).
IndicatorSetInteger(INDICATOR_DIGITS,5);

Definir quando a plotagem começará a ser desenhada utilizando a função (PlotIndexSetInteger) com os parâmetros:

  • plot_index: para especificar o índice do estilo de plotagem, que será (0).
  • prop_id: para especificar o identificador da propriedade, que será (PLOT_DRAW_BEGIN), como parte da enumeração ENUM_PLOT_PROPERTY_INTEGER.
  • prop_value: para especificar o valor a ser definido como propriedade, que será (0).
PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,0);

Especificar o nome e o período do indicador utilizando a função (IndicatorSetString) com os parâmetros:

  • prop_id: para especificar o identificador, que pode ser um da enumeração ENUM_CUSTOMIND_PROPERTY_STRING, e especificar (INDICATOR_SHORTNAME).
  • prop_value: para especificar o valor textual do indicador, que será ("Chaikin Money Flow("+string(periods)+")").
IndicatorSetString(INDICATOR_SHORTNAME,"Chaikin Money Flow("+string(periods)+")");

A seção OnCalculate para calcular o valor do CMF

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[])

Verificar se há dados para calcular os valores do CMF ou não utilizando a função if

   if(rates_total<periods)
      return(0);

Quando houver dados e o CM já tiver sido calculado anteriormente, não iniciarei o cálculo do valor atual

   int initPos = prev_calculated -1;
   if(initPos<0) initPos = 0;

Calcular o CMF seguindo as etapas:

  • Selecionar o tipo de volume.
  • Loop para calcular o valor do CMF para cada candle.
  • Declarar sumAccDis, sumVol.
  • Loop para calcular as variáveis declaradas (sumAccDis, sumVol) após declarar e definir a variável (thisTickVolume).
  • Armazenar o resultado no buffer cmfBuffer.
  • Retornar os valores calculados.
   if(volumeTypeInp==VOLUME_TICK)
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = tick_volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   else
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   
   return (rates_total-periods-10);

Declarar a função (AccDis) usada no código para ter 4 variáveis (high, low, close e volume) para ajudar no cálculo do Money Flow Multiplier e Money Flow Volume do candle, que são usados para calcular o CMF.

double AccDis(double high,double low,double close,long volume)
{
   double res=0;
   
   if(high!=low)
      res=(2*close-high-low)/(high-low)*volume;
   
   return(res);
}

Agora, concluímos nosso código para criar o indicador CMF personalizado, e o código completo em um único bloco pode ser encontrado abaixo:

#property description "Chaikin Money Flow"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   1
#property indicator_width1  3
#property indicator_level1  0
#property indicator_level2  0.20
#property indicator_level3  -0.20
#property indicator_levelstyle STYLE_DOT
#property indicator_levelwidth 0
#property indicator_levelcolor clrBlack
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrBlue
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
double                    cmfBuffer[];
void OnInit()
{
   SetIndexBuffer(0,cmfBuffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,5);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,0);
   IndicatorSetString(INDICATOR_SHORTNAME,"Chaikin Money Flow("+string(periods)+")");
}
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[])
{
   if(rates_total<periods)
      return(0);
   int initPos = prev_calculated -1;
   if(initPos<0) initPos = 0;
   if(volumeTypeInp==VOLUME_TICK)
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = tick_volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   else
   {
      for(int pos = initPos;pos<=rates_total-periods;pos++)
      {
         double sumAccDis = 0;
         long sumVol = 0;
         
         for(int i = 0; i < periods && !IsStopped(); ++i)
         {
            long thisTickVolume = volume[pos+i];
            sumVol += thisTickVolume;
            sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume);
         }
         
         cmfBuffer[pos+periods-1] = sumAccDis/sumVol;
      }
   }
   return (rates_total-periods-10);
}
double AccDis(double high,double low,double close,long volume)
{
   double res=0;
   
   if(high!=low)
      res=(2*close-high-low)/(high-low)*volume;
   
   return(res);
}

Depois de montar esse código e compilá-lo, você encontrará o arquivo na pasta de indicadores. Quando você o adicionar ao gráfico, verá o indicador ali, exatamente como na figura abaixo.

CMFInd

Como pode ser visto, esse indicador se comporta exatamente como o do nosso código personalizado. Você pode adaptá-lo conforme suas necessidades, dependendo do que auxiliará em seu sistema. Até agora, temos um indicador CMF personalizado que pode ser utilizado em nosso sistema de trading, com base em estratégias simples, que abordaremos na próxima seção.


Estratégias Chaikin Money Flow

Nesta seção, compartilharei algumas estratégias simples que você pode usar com o indicador CMF. Essas estratégias seguem conceitos e regras diferentes para realizarmos nossos testes de forma sólida e metódica em termos de desempenho e otimização. São estratégias simples em conceito, mas você pode desenvolvê-las conforme suas preferências para testá-las e observar como podem ser úteis.

Usaremos as três estratégias simples a seguir:

  • Estratégia CMF cruzamento da linha zero
  • Estratégia CMF sobrecompra e sobrevenda
  • Estratégia CMF validação de tendência

A estratégia CMF cruzamento da linha zero:

Essa estratégia é bastante direta. Ela utiliza o valor do indicador CMF para determinar quando comprar ou vender. Se o valor anterior do CMF for negativo e o valor atual ou último for positivo, é sinal de compra. O oposto ocorre quando o valor anterior do CMF é positivo e o atual ou último é negativo.

Resumidamente,

CMF anterior < 0 e CMF atual > 0 --> Compra

CMF anterior > 0 e CMF atual < 0 --> Venda 

A estratégia CMF sobrecompra e sobrevenda:

Essa estratégia é um pouco diferente da anterior. Ela observa outros níveis para identificar regiões sobrecompradas ou sobrevendidas. Essas regiões podem variar ao longo do tempo ou entre diferentes instrumentos, e é possível identificá-las pela inspeção visual do movimento do indicador antes de definir seus valores. Quando o valor do CMF estiver abaixo ou igual a -0.20, será gerado sinal de compra, e o inverso também vale. Quando o valor do CMF estiver acima ou igual a 0.20, será gerado sinal de venda.

Resumidamente,

CMF <= -0.20 --> Compra

CMF >= 0.20 --> Venda

A estratégia CMF validação de tendência:

Essa estratégia segue outra abordagem para obtenção dos sinais: combinaremos outro indicador que poderá confirmar a tendência ou ao menos o movimento dos preços, seja para cima ou para baixo, conciliando-o com o sinal do cruzamento de zero. Faremos essa combinação usando a média móvel com o indicador CMF. Quando o preço de fechamento anterior estiver abaixo do valor anterior da média móvel e, ao mesmo tempo, o preço atual de ask estiver acima da média móvel e o valor atual do CMF estiver acima de zero, será gerado sinal de compra — e o contrário para venda. Quando o preço de fechamento anterior estiver acima do valor anterior da média móvel e, ao mesmo tempo, o preço atual de bid estiver abaixo da média móvel e o valor atual do CMF estiver abaixo de zero, será gerado sinal de venda.

Resumidamente,

prevClose < prevMA, ask > MA, e CMF > 0 --> Compra

prevClose > prevMA, bid < MA, e CMF < 0 --> Venda

Testaremos e otimizaremos cada uma dessas estratégias aplicando diferentes conceitos para obter os melhores resultados possíveis. Veremos como isso funciona na próxima seção.


Sistema de trading Chaikin Money Flow

Nesta seção, mostrarei como podemos programar cada estratégia passo a passo. Depois, testarei e mostrarei como podemos otimizá-las para obter melhores resultados. Então, continue acompanhando esta parte realmente interessante do artigo. Antes de iniciar a codificação das estratégias, criarei um pequeno EA para exibir os valores CMF impressos no gráfico. Isso será nossa base para todos os EAs. Também garantirá que utilizemos um indicador preciso com valores corretos em cada EA para cada estratégia.

A seguir, o que faremos com este EA simples:

Definir entradas do EA para configurar os períodos desejados e o tipo de volume que será utilizado na função de chamada do indicador

input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type

Declarar uma variável inteira para o cmf

int cmf;

No evento OnInit(), inicializaremos nosso indicador CMF personalizado chamando-o utilizando a função iCustom, cujos parâmetros são:

  • Symbol: Aqui você informará o nome do símbolo como uma string. Será (_Symbol) para ser aplicado ao símbolo atual.
  • Period: Aqui você informará o período como um ENUM_TIMEFRAMES. Será PERIOD_CURRENT para aplicar o período de tempo atual.
  • Name: Aqui você informará o nome do indicador personalizado que está chamando com o caminho correto na sua máquina local, portanto, você precisará informar a pasta/custom_indicator_name. Será "Chaikin_Money_Flow".
  • ...: Aqui você informará a lista de parâmetros de entrada do indicador, se existirem. Serão as entradas periods e volumeTypeInp.

Em seguida, utilizaremos o valor de retorno (INIT_SUCCEEDED) quando a inicialização for bem-sucedida para seguir para a próxima parte do código.

int OnInit()
  {
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }

No evento OnDeinit(), informaremos a mensagem de que o EA foi removido quando o evento de desinicialização ocorrer.

void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }

No evento OnTick(), declararemos um array para o cmfInd[] com tipo de dado double

double cmfInd[];

Obter dados de um buffer especificado do indicador cmf utilizando a função CopyBuffer. Seus parâmetros:

  • indicator_handle: para especificar o handle double (cmf) que foi declarado anteriormente. 
  • buffer_num: para especificar o número do buffer do cmf (0). 
  • start_pos: para especificar a posição inicial (0).
  • count: para especificar a quantidade a ser copiada (3).
  • buffer[]: para especificar o array cmfInd[] a ser copiado.
CopyBuffer(cmf,0,0,3,cmfInd);

Definir a flag AS_SERIES para o cmfInd[] para indexar o elemento na série temporal. Você pode usar o array[] e a flag, que será true em caso de sucesso, como parâmetros.

ArraySetAsSeries(cmfInd,true);

Definir uma variável double para o valor atual do CMF e normalizá-lo com limite de 5 casas decimais, você pode defini-la.

double cmfVal = NormalizeDouble(cmfInd[0], 5);

Comentar no gráfico o valor atual do cmf que foi definido anteriormente utilizando a função Comment.

Comment("CMF value = ",cmfVal);

Podemos encontrar o código completo em um único bloco de código abaixo.

//+------------------------------------------------------------------+
//|                                                      CMF_Val.mq5 |
//+------------------------------------------------------------------+
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
int cmf;
int OnInit()
  {
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
    double cmfInd[];
    CopyBuffer(cmf,0,0,3,cmfInd);
    ArraySetAsSeries(cmfInd,true);
    double cmfVal = NormalizeDouble(cmfInd[0], 5);
    Comment("CMF value = ",cmfVal);
  }

Após compilar esse código e inseri-lo no gráfico como um EA, podemos encontrar seu resultado igual ao abaixo:

CMF_Val

Como você pode ver, o valor do CMF no gráfico é o mesmo do indicador inserido (0.15904). Podemos utilizar isso como ponto de partida para outros EAs e estratégias.

A estratégia CMF cruzamento da linha zero:

Como dissemos, precisamos codificar a estratégia de cruzamento da linha zero para que as operações sejam geradas automaticamente com base nesse cruzamento. O programa precisa verificar continuamente cada novo candle. Quando o cruzamento ocorrer acima de zero, precisamos que ele envie uma ordem de compra. Quando o cruzamento ocorrer abaixo de zero, precisamos que o EA envie uma ordem de venda.

Aqui está o código completo que fará o trabalho:
//+------------------------------------------------------------------+
//|                                           CMF_Zero_Crossover.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level
input int maPeriodInp=20; //MA Period
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int cmf;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",maPeriodInp,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double cmfInd[];
      CopyBuffer(cmf,0,0,3,cmfInd);
      ArraySetAsSeries(cmfInd,true);
      double cmfVal = NormalizeDouble(cmfInd[0], 5);
      double cmfPreVal = NormalizeDouble(cmfInd[1], 5);
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      if(cmfPreVal<0 && cmfVal>0)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(cmfPreVal>0 && cmfVal<0)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
	}
     }
  }
//+------------------------------------------------------------------+

Apenas para informar, há diferenças neste código:

Incluir o arquivo trade para chamar funções de negociação

#include <trade/trade.mqh>

Adicionar mais entradas a serem definidas pelo usuário, cmfPosLvls para nível de sobrevenda, cmfNegLvls para nível de sobrecompra, tamanho do lote, stop-loss, take-profit

input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;

Declaração dos objetos de negociação, variável inteira barsTotal

CTrade trade;
int barsTotal;

No evento OnTick(), só precisamos verificar se há um novo candle para manter o código em execução. Podemos fazer isso utilizando uma condição if depois de termos definido uma variável inteira chamada bars.

int bars=iBars(_Symbol,PERIOD_CURRENT);
if(barsTotal != bars)

Se houver um novo candle, precisamos executar o restante do código com as seguintes diferenças:

Atualizar barsTotal para ser igual a bars

barsTotal=bars;

Declarar o valor anterior do cmf

double cmfPreVal = NormalizeDouble(cmfInd[1], 5);

Declarar as variáveis double ask e bid

double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);

Definir a condição de compra: se (cmfPreVal<0 e cmfVal>0), precisamos declarar SL, TP e enviar uma ordem de compra

      if(cmfPreVal<0 && cmfVal>0)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

Definir a condição de venda: se (cmfPreVal>0 e cmfVal<0), precisamos declarar SL, TP e enviar uma ordem de venda

      if(cmfPreVal>0 && cmfVal<0)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }

Depois de montar esse código e executá-lo anexando-o ao gráfico, podemos ver que as posições são abertas da mesma forma que no exemplo abaixo, que é da fase de testes.

Posição de compra:

buy

Posição de venda:

sell

Agora, precisamos testar todas as estratégias em EURUSD por um ano, de 1/1/2023 a 31/12/2023. Vamos usar 300 pontos para SL e 900 para TP. Nossa abordagem principal de otimização é testar outro conceito ou adicionar outra ferramenta, como a média móvel. Você também pode testar outros timeframes para ver qual terá melhor desempenho. Esse tipo de otimização também vale a pena ser testado.

Com relação aos resultados dos testes de estratégia para compará-las, focaremos nas seguintes medidas principais:

  • Lucro líquido (Net profit): É calculado subtraindo o prejuízo bruto do lucro bruto. O maior valor é o melhor.
  • Rebaixamento relativo do saldo (Balance DD relative): É a perda máxima que a conta sofre durante as operações. O menor valor é o melhor.
  • Fator de lucro (Profit factor): É a razão entre o lucro bruto e o prejuízo bruto. O maior valor é o melhor.
  • Payoff esperado (Expected Payoff): É o lucro ou prejuízo médio por operação. O maior valor é o melhor.
  • Fator de recuperação (Recovery factor): Mede o quão bem a estratégia testada se recupera após perdas. O maior valor é o melhor.
  • Índice de Sharpe (Sharpe Ratio): Determina o risco e a estabilidade do sistema de trading testado, comparando o retorno com o retorno livre de risco. O maior Índice de Sharpe é o melhor.

Os resultados para o teste no timeframe de 15 minutos serão os mesmos mostrados abaixo:

15min-Backtest1

 15min-Backtest2

15min-Backtest3

De acordo com os resultados do teste em 15 minutos, encontramos os seguintes valores importantes:

  • Lucro líquido (Net Profit): 29019.10 USD.
  • Rebaixamento relativo do saldo (Balance DD relative): 23%.
  • Fator de lucro (Profit factor): 1.09.
  • Payoff esperado (Expected payoff): 19.21.
  • Fator de recuperação (Recovery factor): 0.88.
  • Índice de Sharpe (Sharpe Ratio): 0.80.

Parece que a estratégia pode ser lucrativa com o timeframe de 15 minutos, mas o rebaixamento é alto, o que pode aumentar o risco. Então, vamos tentar outra estratégia com um conceito diferente: a CMF sobrecompra e sobrevenda.

A estratégia CMF sobrecompra e sobrevenda:

Como dissemos, precisamos codificar essa estratégia para que o EA envie ordens de compra e venda automaticamente com base na aproximação das áreas de sobrecompra e sobrevenda. As áreas que estamos observando são 0.20 para sobrecompra e -0.20 para sobrevenda. Portanto, precisamos que o EA monitore cada valor do CMF e o compare com essas áreas. Se o CMF for menor ou igual a -0.20, o EA deverá enviar uma ordem de compra. Se o valor do CMF for maior ou igual a 0.20, o EA deverá enviar uma ordem de venda.

Você pode ver as etapas de criação dessa estratégia ou EA no código completo a seguir:

//+------------------------------------------------------------------+
//|                                                 CMF_MA_OB&OS.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int cmf;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double cmfInd[];
      CopyBuffer(cmf,0,0,3,cmfInd);
      ArraySetAsSeries(cmfInd,true);
      double cmfVal = NormalizeDouble(cmfInd[0], 5);
      double cmfPreVal = NormalizeDouble(cmfInd[1], 5);
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      if(cmfVal<=cmfNegLvls)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(cmfVal>=cmfPosLvls)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

As diferenças nesse código serão as seguintes:

Definir duas entradas para as áreas de sobrecompra e sobrevenda, com base no que o usuário desejar. Usar por enquanto os valores padrão (0.20 e -0.20).

input double cmfPosLvls = 0.20; // CMF OB Level
input double cmfNegLvls = -0.20; // CMF OS Level

Condições da estratégia

Abrir uma posição de compra quando o valor do CMF for menor ou igual a cmfNegLvls

      if(cmfVal<=cmfNegLvls)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

Abrir uma posição de venda quando o valor do CMF for maior ou igual a cmfPosLvls

      if(cmfVal>=cmfPosLvls)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }

Depois de montar esse código e executá-lo no gráfico, podemos ver onde as posições são abertas de acordo com as condições da estratégia, como nos exemplos da fase de testes.

Posição de compra:

buy

Posição de venda:

sell

Vamos testar essa estratégia usando a mesma abordagem que utilizamos no teste anterior da estratégia CMF cruzamento da linha zero. Faremos o teste em EURUSD de 1/1/2023 a 31/12/2023 no timeframe de 15 minutos, com 300 pontos de SL e 900 de TP. As figuras a seguir mostram os resultados desse teste:

15min-Backtest1

 15min-Backtest2

15min-Backtest3

De acordo com os resultados do teste em 15 minutos, encontramos os seguintes valores importantes:

  • Lucro líquido (Net Profit): 58029.90 USD.
  • Rebaixamento relativo do saldo (Balance DD relative): 55.60%.
  • Fator de lucro (Profit factor): 1.06.
  • Payoff esperado (Expected payoff): 14.15.
  • Fator de recuperação (Recovery factor): 0.62.
  • Índice de Sharpe (Sharpe Ratio): 0.69.

Agora que estamos vendo mais lucros com um rebaixamento maior, vamos tentar otimizá-la adicionando ou combinando outro indicador técnico com o CMF cruzamento da linha zero para validação de tendência; veremos o que acontece na próxima estratégia.

A estratégia CMF validação de tendência:

Como dissemos, precisamos adicionar ou combinar outro indicador técnico, a média móvel, com o CMF cruzamento da linha zero para validar o movimento em ambas as direções (alta e baixa). O EA precisa verificar cada preço de fechamento, ask e o CMF. O valor é usado para decidir a posição de cada um em relação aos outros. Quando o último preço de fechamento estiver abaixo do último valor da média móvel, o preço atual de ask estiver acima do valor atual da média móvel e o valor do CMF estiver acima de zero, precisamos abrir uma ordem de compra. Por outro lado, quando o último preço de fechamento estiver acima do último valor da média móvel, o preço atual de bid estiver abaixo do valor atual da média móvel e o valor do CMF estiver abaixo de zero, precisamos abrir uma ordem de venda.

Aqui está o código completo para este EA:

//+------------------------------------------------------------------+
//|                                          CMF_trendValidation.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int                 periods=20; // Periods
input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK;  // Volume Type
input int maPeriodInp=20; //MA Period
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int cmf;
int ma;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   cmf = iCustom(_Symbol,PERIOD_CURRENT,"Chaikin_Money_Flow",periods,volumeTypeInp);
   ma = iMA(_Symbol,PERIOD_CURRENT,maPeriodInp,0,MODE_SMA,PRICE_CLOSE);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double cmfInd[];
      double maInd[];
      CopyBuffer(cmf,0,0,3,cmfInd);
      CopyBuffer(ma,0,0,3,maInd);
      ArraySetAsSeries(cmfInd,true);
      ArraySetAsSeries(maInd,true);
      double cmfVal = NormalizeDouble(cmfInd[0], 5);
      double maVal= NormalizeDouble(maInd[0],5);
      double cmfPreVal = NormalizeDouble(cmfInd[1], 5);
      double maPreVal = NormalizeDouble(maInd[1],5);;
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      double prevClose = iClose(_Symbol,PERIOD_CURRENT,1);
      if(prevClose<maPreVal && ask>maVal)
        {
         if(cmfVal>0)
           {
            double slVal=ask - slLvl*_Point;
            double tpVal=ask + tpLvl*_Point;
            trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
           }
        }
      if(prevClose>maPreVal && bid<maVal)
        {
         if(cmfVal<0)
           {
            double slVal=bid + slLvl*_Point;
            double tpVal=bid - tpLvl*_Point;
            trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
           }
        }
     }
  }
//+------------------------------------------------------------------+

As principais diferenças neste código são as seguintes:

Adicionar outra entrada para o usuário determinar o período da média móvel utilizada

input int maPeriodInp=20; //MA Period

Declaração de uma variável inteira para a média móvel

int ma;

Definir a variável ma-created usando a função iMA, cujos parâmetros são:

  • symbol: definir o nome do símbolo, será (_Symbol) para ser aplicado no símbolo atual. 
  • period: definir o período da média móvel e será (PERIOD_CURRENT) para ser aplicado no timeframe em uso.
  • ma_period: definir o período de cálculo da média, será a entrada do usuário (maPeriodInp).
  • ma_shift: definir o deslocamento horizontal, se necessário.
  • ma_method: especificar o tipo de suavização ou tipo de média móvel, será (MODE_SMA) para usar a média simples.
  • applied_price: especificar o tipo de preço, será (PRICE_CLOSE).
ma = iMA(_Symbol,PERIOD_CURRENT,maPeriodInp,0,MODE_SMA,PRICE_CLOSE);

Declarar o array maInd[]

double maInd[];

Obter dados de um buffer específico do indicador MA utilizando a função CopyBuffer.

CopyBuffer(ma,0,0,3,maInd);

Definir a flag AS_SERIES para maInd[] para indexar o elemento como série temporal.

ArraySetAsSeries(maInd,true);

Definir os valores atuais e anteriores da MA

double maVal= NormalizeDouble(maInd[0],5);
double prevClose = iClose(_Symbol,PERIOD_CURRENT,1);

Condições para posição de compra: prevClose<maPreVal, ask>maVal e cmfVal>0

      if(prevClose<maPreVal && ask>maVal)
        {
         if(cmfVal>0)
           {
            double slVal=ask - slLvl*_Point;
            double tpVal=ask + tpLvl*_Point;
            trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
           }
        }

Condições para posição de venda: prevClose>maPreVal, bid<maVal e cmfVal<0

      if(prevClose>maPreVal && bid<maVal)
        {
         if(cmfVal<0)
           {
            double slVal=bid + slLvl*_Point;
            double tpVal=bid - tpLvl*_Point;
            trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
           }
        }

Depois de executar este EA, podemos encontrar as ordens abertas conforme mostrado nas imagens abaixo:

Posição de compra:

buy

Posição de venda:

sell

Testaremos essa estratégia usando a mesma abordagem dos testes anteriores de CMF cruzamento zero e CMF OB/OS. Testaremos EURUSD de 1/1/2023 a 31/12/2023 no timeframe de 15 minutos com 300 pontos de SL e 900 de TP. As figuras a seguir mostram o resultado desse teste:

15min-Backtest1

15min-Backtest2

15min-Backtest3

De acordo com os resultados do teste em 15 minutos, encontramos os seguintes valores importantes:

  • Lucro líquido (Net Profit): 40723.80 USD.
  • Balance DD relative: 6.30%.
  • Profit factor: 1.91.
  • Expected payoff: 167.59.
  • Recovery factor: 3.59.
  • Sharpe Ratio: 3.90.

Observando os resultados de cada estratégia, fica claro que CMF Trend Validation é a melhor em termos de medições principais.


Conclusão

Como vimos ao longo deste artigo, volume é um conceito muito importante no trading, especialmente quando combinado com outras ferramentas importantes. Além disso, percebemos que a otimização é uma tarefa essencial ao testar qualquer estratégia, pois pode ser afetada por pequenas variações. Recomendo que você faça seus próprios testes alterando diferentes aspectos como timeframe, SL e TP, além de adicionar outras ferramentas até encontrar resultados satisfatórios.

Partimos do pressuposto de que agora você entende como usar o indicador de volume Chaikin Money Flow, como personalizá-lo e programá-lo, e como construir, otimizar e testar EAs com base em diferentes estratégias simples:

  • A estratégia CMF zero crossover.
  • A estratégia CMF OB e OS.
  • A estratégia CMF Trend Validation.

Além disso, você entendeu qual delas é melhor com base nos resultados de otimização e testes. Espero que este artigo tenha sido útil e, se quiser ler mais artigos meus, como construção de sistemas de trading com os indicadores técnicos mais populares e outros, você pode solicitá-los através da minha publicação page.

Você pode encontrar meus arquivos de código-fonte anexados conforme listado abaixo:

Nome do Arquivo Descrição
Chaikin_Money_Flow É o indicador CMF personalizado criado
CMF_Zero_Crossover É o EA da estratégia CMF com cruzamento zero
CMF_OBOS É o EA da estratégia CMF com sobrecompra e sobrevenda
CMF_trendValidation É o EA da estratégia CMF com validação de movimento/tendência


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16469

Últimos Comentários | Ir para discussão (5)
Javier Santiago Gaston De Iriarte Cabrera
Muito bem, cara!
Francis Laquerre
Francis Laquerre | 18 dez. 2024 em 04:49
Ele não funciona no meta editor. Por favor, me ajude!
Kyle Young Sangster
Kyle Young Sangster | 27 abr. 2025 em 06:33
Bom trabalho com o artigo. Gosto de seu estilo de redação: conciso e claro. Também gosto de sua insistência para que os programadores coloquem a "mão na massa", por assim dizer, e aprendam fazendo. Essa é realmente a única maneira
DayTradingSuccess
DayTradingSuccess | 2 nov. 2025 em 17:53
Executei o CMF com validação de tendência no EURUSD de 1º de janeiro de 2023 a 31 de dezembro de 2023 e não obtive os mesmos resultados que você. Instalei o indicador personalizado e o "CMF with trend validation".mq5 e o drawdown máximo foi de 50% em vez de 6%. Como isso pode ser possível?
Silk Road Trading LLC
Ryan L Johnson | 2 nov. 2025 em 19:48
DayTradingSuccess indicador personalizado e o "CMF with trend validation".mq5 e o drawdown máximo foi de 50% em vez de 6%. Como isso pode ser possível?

Uma possibilidade provável é que você esteja negociando com uma corretora diferente. Nos EUA, suas negociações forex devem ser compensadas no mercado interbancário. No Egito, o autor provavelmente está negociando CFDs que são cativos de uma corretora específica e/ou de seus provedores de liquidez.

Até mesmo duas corretoras na mesma jurisdição podem ter feeds de preços ligeiramente diferentes.

Integrando Discord com MetaTrader 5: Construindo um Bot de Trading com Notificações em Tempo Real Integrando Discord com MetaTrader 5: Construindo um Bot de Trading com Notificações em Tempo Real
Neste artigo, veremos como integrar o MetaTrader 5 a um servidor Discord para receber notificações de negociações em tempo real de qualquer lugar. Veremos como configurar a plataforma e o Discord para habilitar o envio de alertas ao Discord. Também abordaremos questões de segurança que surgem em conexão com o uso de WebRequests e webhooks para esse tipo de solução de alertas.
Implementação do modelo de tabela em MQL5: Aplicação do conto MVC Implementação do modelo de tabela em MQL5: Aplicação do conto MVC
Neste artigo, analisaremos o desenvolvimento do modelo de tabela na linguagem MQL5, usando o conceito arquitetônico MVC (Model-View-Controller), que separa a lógica dos dados, a apresentação e o controle, o que ajuda a criar um código estruturado, flexível e escalável. Examinaremos a implementação das classes para construir o modelo de tabela, incluindo o uso de listas ligadas para armazenar dados.
Classes de tabela e cabeçalho baseadas no modelo de tabela em MQL5: Aplicação do conceito MVC Classes de tabela e cabeçalho baseadas no modelo de tabela em MQL5: Aplicação do conceito MVC
Esta é a segunda parte do artigo dedicada à implementação de um modelo de tabela em MQL5, utilizando o paradigma arquitetural MVC (Model-View-Controller). O artigo aborda o desenvolvimento das classes da tabela e de seu cabeçalho, com base no modelo de tabela criado anteriormente. As classes desenvolvidas servirão como base para a futura implementação dos componentes de visualização (View) e controle (Controller), que serão abordados nos próximos artigos.
Automatizando Estratégias de Trading em MQL5 (Parte 2): O Sistema Kumo Breakout com Ichimoku e Awesome Oscillator Automatizando Estratégias de Trading em MQL5 (Parte 2): O Sistema Kumo Breakout com Ichimoku e Awesome Oscillator
Neste artigo, criamos um Expert Advisor (EA) que automatiza a estratégia Kumo Breakout utilizando o indicador Ichimoku Kinko Hyo e o Awesome Oscillator. Percorremos o processo de inicialização dos identificadores de indicadores, detecção das condições de breakout e codificação das entradas e saídas automatizadas de trades. Além disso, implementamos trailing stops e lógica de gerenciamento de posição para aprimorar o desempenho e a adaptabilidade do EA às condições de mercado.