Desenvolvimento de ferramentas para análise de Price Action (Parte 30): EA CCI Zero Line
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
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,015Esse 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.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/18551
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Caminhe em novos trilhos: Personalize indicadores no MQL5
Recursos do Assistente MQL5 que você precisa conhecer (Parte 72): Uso de padrões MACD e OBV com aprendizado supervisionado
Está chegando o novo MetaTrader 5 e MQL5
Automatização de estratégias de trading em MQL5 (Parte 21): Aprimorando o trading baseado em redes neurais com taxas de aprendizado adaptativas
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso