English Русский 中文 Español Deutsch 日本語
preview
Desenvolvimento de ferramentas para análise de Price Action (Parte 30): EA CCI Zero Line

Desenvolvimento de ferramentas para análise de Price Action (Parte 30): EA CCI Zero Line

MetaTrader 5 — Sistemas de negociação |
32 0
Christian Benjamin
Christian Benjamin

Introdução

Neste artigo, vamos desenvolver uma ferramenta em MQL5 que simplifica a negociação com base em Price Action. O sistema automatiza uma estratégia que combina quatro elementos: dois indicadores CCI (Commodity Channel Index), uma média móvel exponencial (EMA) de 34 períodos, o Average True Range (ATR) e a análise pura de Price Action. Depois de apresentar cada indicador e detalhar a estratégia de negociação, vamos acompanhar passo a passo o desenvolvimento do EA, realizar testes, analisar os resultados e apresentar nossas conclusões. A tabela na última página do artigo traz um resumo das outras ferramentas que já desenvolvemos.

Confira o sumário a seguir.


Visão geral do indicador

Commodity Channel Index (CCI)

O Índice do Canal de Commodities (CCI) é um oscilador de momentum que compara o preço atual com sua média recente para identificar possíveis condições de sobrecompra, sobrevenda ou reversão de tendência. A fórmula do Commodity Channel Index (CCI) é a seguinte:

CCI = (Preço típico - SMA) / (0,015 × Desvio médio)

O preço típico (TP) é calculado somando a máxima, a mínima e o preço de fechamento de um período e dividindo o resultado por três:

           h + l + c  
TP =       ---------
               3

SMA: média móvel simples
A média móvel simples (SMA) soma os preços típicos de um intervalo definido, por exemplo, das últimas 20 sessões, e divide o total pelo número de períodos. Esse cálculo suaviza as oscilações diárias dos preços e facilita a identificação da tendência predominante.

Desvio médio
É a média dos desvios absolutos dos preços típicos em relação à média móvel simples.

Essa medida indica quanto os preços típicos se afastam, em média, da SMA, oferecendo uma estimativa prática da variabilidade dos preços, ou seja, da volatilidade, durante o período analisado.

Fator constante de 0,015

Esse coeficiente fixo ajusta a escala dos valores do CCI, fazendo com que a grande maioria fique aproximadamente entre -100 e +100. Isso facilita a interpretação do indicador e destaca os desvios acentuados que podem indicar condições de sobrecompra ou sobrevenda mais intensas.

Desenvolvido por Donald Lambert e publicado pela primeira vez em 1980 para analisar movimentos cíclicos nos mercados de commodities, o indicador passou a ser amplamente utilizado também em ações, moedas e outras classes de ativos. Nesta estratégia, utilizamos dois indicadores CCI simultaneamente: o CCI rápido (25), para avaliar o momentum de curto prazo, e o CCI lento (50), para medir a força geral do mercado. A maioria dos valores do CCI permanece entre -100 e +100, e os movimentos além desses limites costumam indicar uma força de alta ou de baixa fora do comum. Embora esses níveis extremos possam servir como filtro de volatilidade, o principal gatilho da nossa estratégia é o cruzamento da linha zero. Quando o indicador passa do território negativo para o positivo, isso pode sinalizar uma transição para momentum de alta. Já o cruzamento no sentido contrário indica um aumento da pressão vendedora.

Como os cruzamentos da linha zero ocorrem antes dos rompimentos dos níveis de ±100, eles permitem que o EA faça entradas mais cedo, embora isso também possa gerar sinais falsos. Para reduzir esse ruído, o EA pode buscar uma confirmação adicional: a permanência do CCI acima de +100 reforça os sinais de compra, enquanto uma queda abaixo de -100 reforça os sinais de venda. Em outras palavras, os cruzamentos da linha zero geram os sinais principais, enquanto os níveis de ±100 funcionam como um filtro secundário, sem substituir os sinais principais.

Fig. 2. Indicador CCI

A seguir, veremos como criar dois handles do CCI em MQL5, gerenciá-los corretamente e obter os valores dos indicadores.

  • Criação dos handles do CCI em OnInit():

handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);

  • Cópia dos valores dos buffers com CopyBuffer() em OnTick():

CopyBuffer(handleCCI_Long,  0, 0, 2, cciL);
CopyBuffer(handleCCI_Short, 0, 0, 1, cciS);

Média móvel exponencial (EMA) de 34 períodos

A média móvel exponencial (EMA) é um indicador de tendência que reage rapidamente às mudanças nos preços, atribuindo maior peso aos candles mais recentes. Essa ponderação exponencial reduz o ruído de curto prazo e facilita a identificação da tendência predominante. No gráfico de uma hora, é comum utilizar a EMA de 34 períodos, pois ela abrange aproximadamente uma semana de negociação. As operações devem seguir apenas a direção indicada pela EMA 34: abrir posições compradas quando o preço estiver acima da EMA e posições vendidas quando estiver abaixo dela. Essa regra atua como um filtro contínuo de tendência e evita que a estratégia abra operações contra a tendência.

Fig. 3. EMA de 34 períodos

A seguir, veremos como criar um handle para a EMA 34 em MQL5.

  • Declaração:

input int EMAPeriod = 34;

  • Inicialização:

handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

  • Cópia do buffer (seguindo a mesma lógica):

double ema34[1];
if(CopyBuffer(handleEMA, 0, 0, 1, ema34) < 1) return;
double ema_now = ema34[0];

Average True Range (ATR)

O Average True Range (ATR) é um indicador de volatilidade desenvolvido por J. Welles Wilder Jr. e apresentado em seu livro de 1978, "New Concepts in Technical Trading Systems". Ele calcula a média do True Range (amplitude verdadeira) ao longo de um período definido, cujo valor padrão é 14, permitindo avaliar a amplitude das oscilações naturais dos preços no mercado.

Neste artigo, utilizamos o Average True Range (ATR) para calcular e definir os níveis de stop-loss e take-profit. A seguir, veremos como implementar essa funcionalidade em MQL5:

  • Declaração

int handleATR = INVALID_HANDLE;
input int ATR_Period = 14;

  • Inicialização

handleATR = iATR(_Symbol, _Period, ATR_Period);
if(handleATR == INVALID_HANDLE) return INIT_FAILED;

  • Cópia do buffer

double atrBuf[1];
if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) > 0)
{
   double atrValue = atrBuf[0];
   // use atrValue here…
}

  • Liberação dos recursos

if(handleATR != INVALID_HANDLE)
    IndicatorRelease(handleATR);


Lógica da estratégia

Nesta seção, vamos explicar como nosso EA CCI Zero Line gera sinais de entrada combinando dois indicadores CCI (Commodity Channel Index) e uma média móvel exponencial (EMA). Quando o CCI cruza a linha zero de baixo para cima, isso indica um fortalecimento do momentum de alta, sugerindo um possível sinal de compra ou um aumento da pressão compradora. Por outro lado, quando o CCI cruza a linha zero de cima para baixo, isso sinaliza uma mudança do momentum do mercado para baixa, sugerindo uma possível tendência de baixa ou um aumento da pressão vendedora.

Quando o preço sobe acima da EMA 34, isso geralmente indica um fortalecimento do momentum de alta, sugerindo uma possível tendência de alta ou oportunidade de compra. Por outro lado, quando o preço cai abaixo da EMA 34, isso indica um enfraquecimento do momentum e o possível início de uma tendência de baixa, o que pode representar um sinal de venda.

Indicadores utilizados

  • CCI 25: Índice do Canal de Commodities rápido
  • CCI 50: Índice do Canal de Commodities lento
  • EMA 34: média móvel exponencial de 34 períodos calculada com base nos preços de fechamento

Critérios para o sinal de compra

  • CCI 25 acima da linha zero: o CCI 25 deve estar acima da linha zero, indicando momentum de alta no curto prazo.
  • CCI 50 cruza a linha zero: o CCI 50 deve cruzar a linha zero de baixo para cima, confirmando uma mudança na tendência de prazo mais longo.
  • Confirmação pelo preço: o candle atual deve fechar acima da EMA 34, confirmando o cenário de alta.

Fig. 4. Condições do sinal de compra

Quando as três condições são atendidas em uma nova barra, o EA registra um sinal de compra.

Critérios para o sinal de venda

  • CCI 25 abaixo da linha zero: o CCI 25 deve estar abaixo da linha zero, indicando momentum de baixa no curto prazo.
  • CCI 50 cruza a linha zero: o CCI 50 deve cruzar a linha zero de cima para baixo, confirmando uma mudança para uma tendência de baixa de prazo mais longo.
  • Confirmação pelo preço: o candle atual deve fechar abaixo da EMA 34, confirmando o cenário de baixa.

Fig. 5. Condições do sinal de venda

Quando as três condições são atendidas em uma nova barra, o EA registra um sinal de venda.


Desenvolvimento do EA

Ao ser iniciado, o EA cria os handles dos indicadores necessários para obter os dados do gráfico em tempo real. Essa etapa ocorre na função OnInit(). São criados dois handles para o CCI: um com período maior (50), utilizado para identificar a tendência mais ampla, e outro com período menor (25), que funciona como indicador de confirmação. Também é criado um handle para a EMA de 34 períodos, utilizada como filtro de tendência. Para isso, o código utiliza funções como iCCI() e iMA(), que obtêm esses indicadores por meio do mecanismo interno do MetaTrader.

A criação correta desses handles é fundamental. Se algum deles não puder ser criado e a função retornar INVALID_HANDLE, o EA interrompe a execução, evitando sinais falsos e erros durante o funcionamento. O sistema também permite criar um handle adicional para o ATR, mas somente quando o gerenciamento de stop-loss e take-profit com base nesse indicador estiver habilitado pelo parâmetro UseATR.

handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
handleEMA       = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(UseATR)
    handleATR = iATR(_Symbol, _Period, ATR_Period);

  • Essa abordagem garante que o EA disponha de todas as fontes de dados necessárias para tomar decisões de negociação com base nesses dados a cada tick.

O EA também permite restringir as operações a horários específicos, como a sessão de Tóquio para o par USDJPY. Isso ajuda a evitar negociações em períodos de baixo volume, quando os movimentos dos preços podem ser menos previsíveis. Essa funcionalidade é implementada por meio de uma verificação do horário atual do servidor dentro da função OnTick(). Quando o filtro de sessão está habilitado, o código converte o horário atual em uma estrutura MqlDateTime e compara a hora obtida com os horários de início e término definidos pelo usuário. Se a hora atual estiver fora desse intervalo, o EA encerra o processamento do tick, sem prosseguir com a análise ou executar operações.

if(UseTokyoSessionFilter && _Symbol == "USDJPY")
{
    datetime now = TimeTradeServer();
    MqlDateTime tm;
    TimeToStruct(now, tm);
    int hr = tm.hour;
    if(hr < TokyoStartHour || hr >= TokyoEndHour)
        return;
}

  • Restringir as operações aos horários definidos ajuda a reduzir o risco de slippage e de sinais falsos, comuns em mercados com baixa liquidez fora dos períodos de maior atividade.

Para evitar que as mesmas condições sejam verificadas várias vezes durante a formação de um único candle, o EA utiliza uma lógica simples, mas eficiente: armazena a marca de tempo da última barra processada e só prossegue quando uma nova barra é formada. Isso é feito por meio da variável estática lastTime, que preserva seu valor entre as chamadas da função. A cada novo tick, o código compara o horário da barra atual, obtido por iTime(), com o valor armazenado. Se forem iguais, a função é encerrada antecipadamente. Dessa forma, todos os cálculos e decisões são realizados apenas uma vez por barra.

static datetime lastTime=0;
datetime t = iTime(_Symbol, _Period, 0);
if(t == lastTime) return; // same bar, skip
lastTime = t;

  • Essa abordagem melhora o desempenho, evita a duplicação de sinais e mantém a execução alinhada à forma como os traders normalmente analisam cada timeframe, aumentando a eficiência e a precisão da estratégia de negociação.

A cada nova barra, o EA chama CopyBuffer() para obter os valores mais recentes do CCI 25, CCI 50 e EMA 34, incluindo os dados da barra anterior para identificar os cruzamentos. O CCI 50, mais lento, identifica a tendência de prazo mais longo, enquanto o CCI 25, mais rápido, confirma o momentum necessário para a entrada. A EMA ajuda a filtrar o ruído e a confirmar que o preço está se movimentando na direção da tendência. Se o gerenciamento de stop-loss e take-profit com base no ATR estiver habilitado, o EA também obtém o valor atual do ATR 14.

double cciL[2], cciS[1], emaVal[1];
if(CopyBuffer(handleCCI_Long, 0, 0, 2, cciL) < 2) return;
if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS) < 1) return;
if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) < 1) return;

double atrValue = 0.0;
if(UseATR)
{
    double atrBuf[1];
    if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return;
    atrValue = atrBuf[0];
}

  • Em conjunto, esses valores orientam todas as decisões de negociação: os indicadores CCI identificam reversões e continuações de tendência, a EMA confirma se o movimento está alinhado à tendência e o ATR permite ajustar dinamicamente os níveis de risco.

A lógica central da estratégia consiste em identificar o momento em que o CCI de período mais longo cruza a linha zero, sinalizando uma possível mudança de tendência. O cruzamento de baixo para cima, quando o indicador passa do território negativo para o positivo, sugere um movimento de alta. Já o cruzamento de cima para baixo indica momentum de baixa. O código identifica esses eventos comparando os valores anteriores e atuais do buffer do CCI lento. Quando detecta um cruzamento, o EA aplica filtros adicionais: o CCI rápido deve confirmar a direção do sinal, permanecendo positivo em um cenário de alta e negativo em um cenário de baixa, enquanto o preço atual deve estar, respectivamente, acima ou abaixo da EMA.

double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

if(crossUp && cciS[0] > 0 && price > emaVal[0])
    RegisterSignal(true, price, atrValue);
if(crossDown && cciS[0] < 0 && price < emaVal[0])
    RegisterSignal(false, price, atrValue);

  • Essa combinação de confirmações ajuda a filtrar sinais falsos e permite abrir operações somente quando vários indicadores apontam na mesma direção e o filtro de tendência confirma o movimento.

Quando um sinal válido é identificado, o EA calcula os níveis correspondentes de stop-loss e take-profit. Se o gerenciamento baseado no ATR estiver habilitado, esses níveis são calculados dinamicamente, multiplicando o valor atual do ATR por um multiplicador definido pelo usuário, como 1,5. O stop-loss é posicionado a uma determinada distância do preço de entrada: abaixo dele nas posições compradas e acima nas posições vendidas. O take-profit é definido a uma distância proporcionalmente maior, conforme a relação risco/retorno, normalmente equivalente a 1,5 vezes a distância entre o preço de entrada e o stop-loss.

double dist = UseATR ? atrValue * ATR_Multiplier : SLBufferPoints * _Point;
double slPrice = isBuy ? price - dist : price + dist;
double tpPrice = isBuy ? price + dist * RiskRewardRatio : price - dist * RiskRewardRatio;

  • Em seguida, o EA adiciona marcadores visuais ao gráfico: setas próximas ao candle atual para indicar os pontos de entrada e linhas horizontais para mostrar os níveis de stop-loss e take-profit.

ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice);
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode);
ObjectSetInteger(0, name, OBJPROP_COLOR, isBuy ? BuyArrowColor : SellArrowColor);
ObjectCreate(0, "SL_Line", OBJ_HLINE, 0, 0, slPrice);
ObjectCreate(0, "TP_Line", OBJ_HLINE, 0, 0, tpPrice);

  • Esses elementos permitem conferir visualmente os sinais e verificar se a lógica do EA está coerente com as condições reais do mercado.

Depois de desenhar os marcadores, o EA emite um alerta com os detalhes da operação, incluindo o preço de entrada e os níveis de stop-loss e take-profit.

Alert("CCI ZeroLine EMA + R:R 1:1.5 " + (isBuy ? "BUY" : "SELL") +
      StringFormat(" @%.5f | SL: %.5f | TP: %.5f", price, slPrice, tpPrice));

O EA também atualiza os arrays internos de acompanhamento, que armazenam os níveis de stop-loss e take-profit de cada operação, além de uma flag booleana que indica se o desfecho da operação já foi registrado. Esses arrays são essenciais para acompanhar as operações em andamento, especialmente durante os testes com dados históricos ou na negociação em tempo real, pois permitem ao EA gerenciar várias posições abertas simultaneamente e avaliar seu desempenho.

signalSL[totalSignals] = slPrice;
signalTP[totalSignals] = tpPrice;
resolved[totalSignals] = false;

A função ResolveSignals() é responsável por acompanhar as operações ativas. Ela percorre todos os sinais ainda em aberto e verifica as máximas e mínimas das barras recentes para identificar se os níveis de stop-loss ou take-profit foram atingidos. Se a máxima ultrapassar o nível de take-profit, a operação é registrada como lucrativa. Se a mínima romper o nível de stop-loss, a operação é registrada como perdedora. Esse acompanhamento atualiza os contadores do total de sinais e de operações lucrativas, fornecendo estatísticas sobre o desempenho da estratégia. Esse monitoramento é especialmente importante nos testes com dados históricos, pois o registro preciso dos resultados influencia métricas gerais de desempenho, como a taxa de acerto, o fator de lucro e o drawdown.

double high = iHigh(_Symbol, _Period, idx);
double low = iLow(_Symbol, _Period, idx);
if(high >= signalTP[i]) { winSignals++; resolved[i]=true; }
if(low <= signalSL[i]) { resolved[i]=true; }

Quando o EA é removido do gráfico ou o gráfico é fechado, a função OnDeinit() libera todos os handles dos indicadores por meio de IndicatorRelease(). Ela também remove todos os objetos gráficos criados durante a execução, como setas e linhas horizontais. O gerenciamento adequado dos recursos evita vazamentos de memória e o acúmulo de objetos desnecessários no gráfico, permitindo que este ou outros EAs sejam iniciados posteriormente sem encontrar objetos gráficos residuais de execuções anteriores.

IndicatorRelease(handleCCI_Long);
IndicatorRelease(handleCCI_Short);
ObjectDelete(0, "SL_Line");
ObjectDelete(0, "TP_Line");

Código completo

//+------------------------------------------------------------------+
//|                                                  CCI Zero-Line EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/ru/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ru/users/lynnchris"
#property version   "1.0"
#property strict

#include <Trade\Trade.mqh>
#include <Tools\Datetime.mqh>

CTrade trade;

//--- Session filter for USDJPY (Tokyo session)
input bool UseTokyoSessionFilter = false;  // enable Tokyo session filter
input int  TokyoStartHour        = 0;      // session start hour (server time)
input int  TokyoEndHour          = 9;      // session end hour   (server time, exclusive)

//--- SL/TP settings
input bool   UseATR           = true;   // true = ATR-based SL/TP, false = fixed points
input int    ATR_Period       = 14;     // ATR look-back
input double ATR_Multiplier   = 1.5;    // SL/TP distance = ATR × this
input double SLBufferPoints   = 10.0;   // fallback SL offset in pips if UseATR=false
input double RiskRewardRatio  = 1.5;    // TP = SL × 1.5 (1:1.5 RR)

//--- Indicator periods
input int CCI_LongPeriod   = 50;  // CCI long period (zero-line cross)
input int CCI_ShortPeriod  = 25;  // CCI short period (confirmation)
input int EMAPeriod        = 34;  // EMA period for trend filter

//--- Arrow colors
input color BuyArrowColor  = clrLime;
input color SellArrowColor = clrRed;

//--- Indicator handles
int handleCCI_Long = INVALID_HANDLE;
int handleCCI_Short= INVALID_HANDLE;
int handleEMA      = INVALID_HANDLE;
int handleATR      = INVALID_HANDLE;

//--- Signal-tracking arrays
int    totalSignals = 0;
int    winSignals   = 0;
int    signalBar[];
double signalSL[];
double signalTP[];
bool   resolved[];

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   handleCCI_Long  = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL);
   handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
   handleEMA       = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   if(handleCCI_Long == INVALID_HANDLE ||
      handleCCI_Short== INVALID_HANDLE ||
      handleEMA      == INVALID_HANDLE)
      return(INIT_FAILED);

   if(UseATR)
     {
      handleATR = iATR(_Symbol, _Period, ATR_Period);
      if(handleATR == INVALID_HANDLE)
         return(INIT_FAILED);
     }

   if(RiskRewardRatio <= 0 ||
      ATR_Multiplier   <= 0 ||
      (SLBufferPoints <= 0 && !UseATR))
      return(INIT_FAILED);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(handleCCI_Long  != INVALID_HANDLE)
      IndicatorRelease(handleCCI_Long);
   if(handleCCI_Short != INVALID_HANDLE)
      IndicatorRelease(handleCCI_Short);
   if(handleEMA       != INVALID_HANDLE)
      IndicatorRelease(handleEMA);
   if(UseATR && handleATR != INVALID_HANDLE)
      IndicatorRelease(handleATR);

   ObjectDelete(0, "SL_Line");
   ObjectDelete(0, "TP_Line");
  }

//+------------------------------------------------------------------+
//| Strategy Tester summary                                          |
//+------------------------------------------------------------------+
double OnTester()
  {
   double winRate = totalSignals > 0
                    ? 100.0 * winSignals / totalSignals
                    : 0.0;
   PrintFormat("=== Backtest Win-Rate ===\nSignals: %d  Wins: %d  Win-Rate: %.2f%%",
               totalSignals, winSignals, winRate);
   return(winRate);
  }

//+------------------------------------------------------------------+
//| Tick handler                                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
// Tokyo session filter for USDJPY
   if(UseTokyoSessionFilter && _Symbol == "USDJPY")
     {
      datetime now = TimeTradeServer();
      MqlDateTime tm;
      TimeToStruct(now, tm);
      int hr = tm.hour;
      if(hr < TokyoStartHour || hr >= TokyoEndHour)
         return;
     }

// Only act on new bar
   static datetime lastTime = 0;
   datetime t = iTime(_Symbol, _Period, 0);
   if(t == lastTime)
      return;
   lastTime = t;

// Copy indicator buffers
   double cciL[2], cciS[1], emaVal[1];
   if(CopyBuffer(handleCCI_Long,  0, 0, 2, cciL)  < 2)
      return;
   if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS)  < 1)
      return;
   if(CopyBuffer(handleEMA,       0, 0, 1, emaVal) < 1)
      return;

// ATR if needed
   double atrValue = 0.0;
   if(UseATR)
     {
      double atrBuf[1];
      if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1)
         return;
      atrValue = atrBuf[0];
     }

   double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

// Detect zero-line cross
   bool crossUp   = (cciL[1] < 0 && cciL[0] > 0);
   bool crossDown = (cciL[1] > 0 && cciL[0] < 0);

// Confirm with short CCI & EMA trend filter
   if(crossUp   && cciS[0] >  0 && price > emaVal[0])
      RegisterSignal(true,  price, atrValue);
   if(crossDown && cciS[0] <  0 && price < emaVal[0])
      RegisterSignal(false, price, atrValue);

// Resolve pending signals for SL/TP hits
   ResolveSignals();
  }

//+------------------------------------------------------------------+
//| Register new buy/sell signal                                     |
//+------------------------------------------------------------------+
void RegisterSignal(bool isBuy, double price, double atrVal)
  {
   double dist    = UseATR
                    ? atrVal * ATR_Multiplier
                    : SLBufferPoints * _Point;
   double slPrice = isBuy ? price - dist : price + dist;
   double tpPrice = isBuy
                    ? price + dist * RiskRewardRatio
                    : price - dist * RiskRewardRatio;

// Arrow placement just outside the candle
   datetime barTime = iTime(_Symbol, _Period, 0);
   double   barHigh = iHigh(_Symbol, _Period, 0);
   double   barLow  = iLow(_Symbol, _Period, 0);
   double   offset  = 5 * _Point;  // e.g. 5-pip offset

   double arrowPrice = isBuy
                       ? barLow  - offset
                       : barHigh + offset;

// Use numeric arrow codes directly
   int arrowCode = isBuy ? 233 : 234;

   string name = (isBuy ? "BUY_" : "SELL_")
                 + TimeToString(TimeTradeServer(), TIME_SECONDS);
   ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice);
   ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode);
   ObjectSetInteger(0, name, OBJPROP_COLOR,     isBuy ? BuyArrowColor : SellArrowColor);
   ObjectSetInteger(0, name, OBJPROP_WIDTH,     2);

// Draw SL/TP lines
   DrawLine("SL_Line", slPrice, clrRed);
   DrawLine("TP_Line", tpPrice, clrLime);

// Alert
   Alert("CCI ZeroLine EMA + R:R 1:1.5 "
         + (isBuy ? "BUY" : "SELL")
         + StringFormat(" @%.5f | SL: %.5f | TP: %.5f",
                        price, slPrice, tpPrice));

// Track for backtest metrics
   totalSignals++;
   ArrayResize(signalBar,  totalSignals);
   ArrayResize(signalSL,   totalSignals);
   ArrayResize(signalTP,   totalSignals);
   ArrayResize(resolved,   totalSignals);

   signalBar[totalSignals-1] = 0;
   signalSL[totalSignals-1]  = slPrice;
   signalTP[totalSignals-1]  = tpPrice;
   resolved[totalSignals-1]  = false;
  }

//+------------------------------------------------------------------+
//| Resolve pending signals (SL/TP checks)                           |
//+------------------------------------------------------------------+
void ResolveSignals()
  {
   int bars = Bars(_Symbol, _Period);
   for(int i = 0; i < totalSignals; i++)
     {
      if(resolved[i])
         continue;
      signalBar[i]++;
      int idx = signalBar[i];
      if(idx >= bars)
         continue;

      double high = iHigh(_Symbol, _Period, idx);
      double low  = iLow(_Symbol, _Period, idx);

      if(high >= signalTP[i])
        {
         winSignals++;
         resolved[i] = true;
        }
      else
         if(low <= signalSL[i])
           {
            resolved[i] = true;
           }
     }
  }

//+------------------------------------------------------------------+
//| Draw a horizontal SL/TP line                                     |
//+------------------------------------------------------------------+
void DrawLine(string name, double price, color clr)
  {
   if(ObjectFind(0, name) >= 0)
      ObjectDelete(0, name);
   ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
   ObjectSetString(0, name, OBJPROP_TEXT, name);
  }
//+------------------------------------------------------------------+


Testes

A seguir, apresentamos os resultados dos testes do nosso EA com dados históricos.

Testei o EA no USDJPY. Abaixo estão os registros do log.

  • Parâmetros de entrada
2025.06.30 09:34:54.140   UseTokyoSessionFilter=false
2025.06.30 09:34:54.140   TokyoStartHour=0
2025.06.30 09:34:54.140   TokyoEndHour=9
2025.06.30 09:34:54.140   UseATR=true
2025.06.30 09:34:54.140   ATR_Period=14
2025.06.30 09:34:54.140   ATR_Multiplier=1.5
2025.06.30 09:34:54.140   SLBufferPoints=10.0
2025.06.30 09:34:54.140   RiskRewardRatio=1.0
2025.06.30 09:34:54.140   CCI_LongPeriod=50
2025.06.30 09:34:54.140   CCI_ShortPeriod=25
2025.06.30 09:34:54.140   EMAPeriod=34
2025.06.30 09:34:54.140   BuyArrowColor=65280
2025.06.30 09:34:54.140   SellArrowColor=255

A configuração mostra que o filtro da sessão de Tóquio está desativado, permitindo que o EA opere fora do horário definido para essa sessão. Quando habilitado, porém, as operações ficam restritas ao intervalo das 00:00 às 09:00, no horário de Tóquio. O EA utiliza o ATR de 14 períodos com um multiplicador de 1,5 para ajustar os níveis de stop-loss e take-profit à volatilidade atual do mercado. Um buffer adicional de 10 pontos é aplicado ao stop-loss para reduzir o risco de encerramento prematuro das operações. A relação risco/retorno está definida em 1:1, equilibrando o potencial de lucro e de perda.

A estratégia utiliza dois indicadores CCI: o CCI 50, mais lento, e o CCI 25, mais rápido, além da EMA 34. Os sinais são representados visualmente por setas: as verdes indicam sinais de compra e as azuis, sinais de venda.

Os parâmetros de entrada podem ser ajustados conforme suas preferências. As configurações apresentadas são as que utilizei durante os testes e que, na minha avaliação, produziram bons resultados.

  • Sinais

2025.06.30 09:34:54.447 2025.01.02 15:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @157.01800 | SL: 157.45075 | TP: 156.58525
2025.06.30 09:34:56.655 2025.01.09 06:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @158.04800 | SL: 158.30343 | TP: 157.79257
2025.06.30 09:40:35.572 2025.01.29 07:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @155.21100 | SL: 155.47704 | TP: 154.94496
2025.06.30 09:40:36.759 2025.01.31 07:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @154.68200 | SL: 154.25439 | TP: 155.10961
2025.06.30 09:40:45.870 2025.02.26 06:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.42600 | SL: 149.05400 | TP: 149.79800
2025.06.30 09:40:46.737 2025.02.28 03:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.51900 | SL: 150.03650 | TP: 149.00150
2025.06.30 09:40:49.726 2025.03.06 01:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.14200 | SL: 149.68200 | TP: 148.60200
2025.06.30 09:40:58.522 2025.03.21 16:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @148.97200 | SL: 149.35418 | TP: 148.58982
2025.06.30 09:41:01.363 2025.04.01 00:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.93500 | SL: 149.57425 | TP: 150.29575
2025.06.30 09:41:16.557 2025.04.28 13:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @143.33500 | SL: 143.69232 | TP: 142.97768
2025.06.30 09:41:36.021 2025.06.18 14:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @144.85500 | SL: 145.08911 | TP: 144.62089
2025.06.30 09:41:37.454 2025.06.23 19:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @146.15000 | SL: 146.65711 | TP: 145.64289
2025.06.30 09:41:38.360 2025.06.25 23:00:00   Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @145.21700 | SL: 145.47286 | TP: 144.96114

  • Taxa de acerto

2025.06.30 09:41:39.206 2025.06.29 23:59:59   Signals: 13  Wins: 10  Win-Rate: 76.92%

O GIF abaixo apresenta os resultados dos testes: as setas vermelhas apontadas para baixo indicam sinais de venda, enquanto as setas verdes apontadas para cima representam sinais de compra. As linhas vermelhas mostram os níveis de stop-loss e as verdes, os níveis de take-profit.


Conclusão

Em resumo, o EA CCI Zero Line apresenta no gráfico todas as informações necessárias para identificar pontos de entrada claros, seguindo regras bem definidas:

  • Utilizamos o CCI 50 para identificar a tendência geral e o CCI 25 para confirmar que o momentum está favorável à entrada, permitindo entradas no mercado somente quando os sinais dos dois indicadores coincidem.
  • A EMA de 34 períodos ajuda a evitar entradas motivadas por oscilações momentâneas. Quando o preço fecha do lado esperado dessa média, temos uma confirmação adicional da consistência da tendência.
  • Com o stop-loss baseado no ATR, a distância até esse nível se ajusta à volatilidade: fica menor em mercados mais estáveis e aumenta quando os movimentos se intensificam. O take-profit mantém uma distância proporcional, preservando uma relação risco/retorno consistente.

Dessa forma, as setas aparecem somente quando o preço, o momentum e a volatilidade oferecem sinais convergentes. Isso ajuda a reduzir os sinais falsos, torna o gerenciamento de riscos mais flexível e permite compreender claramente os critérios utilizados para definir cada nível de entrada e saída. Você pode ajustar os períodos dos indicadores CCI, o período da EMA e o multiplicador do ATR conforme o símbolo e o timeframe, operando com mais confiança graças a uma ferramenta baseada em uma análise robusta em múltiplas camadas. Este EA foi desenvolvido exclusivamente para fins educacionais. Não o utilize no mercado real sem antes realizar testes rigorosos com dados históricos ou avaliá-lo em uma conta de demonstração. Antes de começar a operar com dinheiro real, certifique-se de que os resultados obtidos atendem às suas expectativas.





   
Chart Projector
Analytical Comment
Analytics Master
Analytics Forecaster
Volatility Navigator
Mean Reversion Signal Reaper
Signal Pulse
Metrics Board
External Flow
VWAP
Heikin Ashi FibVWAP
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR)
Script Quarters Drawer
Intrusion Detector
TrendLoom Tool Quarters Board
ZigZag Analyzer Correlation Pathfinder Market Structure Flip Detector Tool
Correlation Dashboard Currency Strength Meter
PAQ Analysis Tool
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity Sweep Opening Range Breakout Tool Boom and Crash Interceptor CCI Zero-Line EA

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

Arquivos anexados |
CCI.mq5 (18.28 KB)
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Recursos do Assistente MQL5 que você precisa conhecer (Parte 72): Uso de padrões MACD e OBV com aprendizado supervisionado Recursos do Assistente MQL5 que você precisa conhecer (Parte 72): Uso de padrões MACD e OBV com aprendizado supervisionado
Dando continuidade ao artigo anterior sobre a combinação dos indicadores MACD e OBV, veremos como aprimorá-la com o uso de machine learning. O MACD e o OBV são indicadores complementares que refletem, respectivamente, a tendência e o volume. Nossa abordagem de machine learning utiliza uma rede neural convolucional (convolution neural network, CNN) que emprega um kernel exponencial (Exponential kernel) para definir as dimensões de seus kernels e o número de canais ao configurar as previsões geradas por essa combinação de indicadores. Como de costume, essa implementação é feita em um arquivo personalizado da classe de sinais (signal class), que interage com o Assistente MQL5 para criar um EA.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Automatização de estratégias de trading em MQL5 (Parte 21): Aprimorando o trading baseado em redes neurais com taxas de aprendizado adaptativas Automatização de estratégias de trading em MQL5 (Parte 21): Aprimorando o trading baseado em redes neurais com taxas de aprendizado adaptativas
Neste artigo, vamos aprimorar uma estratégia de trading baseada em redes neurais em MQL5, utilizando uma taxa de aprendizado adaptativa (adaptive learning rate) para aumentar a precisão das previsões. Vamos desenvolver e implementar essa melhoria e, em seguida, testar seu desempenho. Ao final, apresentaremos recomendações para otimizar o trading algorítmico.