Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 19): Analisador ZigZag
Introdução
As linhas de tendência formam a base da análise técnica e da avaliação da ação do preço. Elas atuam como o esqueleto dos padrões de preço nos quais traders de Forex, criptomoedas, commodities, ações e derivativos se baseiam. Quando desenhadas corretamente, as linhas de tendência revelam com eficiência as tendências do mercado e ajudam os traders a identificar oportunidades potenciais.
Seja você um day trader ou um trader de curto prazo, as linhas de tendência desempenham um papel fundamental em muitos sistemas e estratégias de negociação. Nesta série de desenvolvimento do Kit de Ferramentas de Análise da Ação do Preço, apresentamos uma ferramenta chamada Analisador ZigZag. Essa ferramenta concentra-se no desenho de linhas de tendência utilizando o indicador ZigZag para identificar pontos de oscilação (swings) que servem de base para a construção das linhas de tendência. O MQL5 é uma linguagem poderosa para automatizar sistemas de negociação, permitindo desenvolver ferramentas avançadas como o Analisador ZigZag, que se adaptam às condições do mercado e auxiliam na tomada de decisões em tempo real.
Começaremos explorando as linhas de tendência, depois examinaremos o indicador ZigZag, apresentaremos o algoritmo do sistema, mostraremos o código em MQL5, analisaremos os resultados e concluiremos com nossas considerações finais. Veja o índice abaixo:
Linhas de Tendência
Uma linha de tendência é uma linha inclinada que conecta pontos significativos do preço, normalmente fundos mais altos ou topos mais baixos, e se projeta para o futuro como um nível de suporte ou resistência. Ela indica a direção e o ímpeto prováveis dos movimentos de preço. Para desenhar uma linha de tendência, comece identificando a direção geral do mercado. Uma tendência de alta é caracterizada por uma sequência de topos mais altos e fundos mais altos, enquanto uma tendência de baixa é marcada por topos mais baixos e fundos mais baixos. Para fins ilustrativos, considere um gráfico mostrando um padrão de baixa, no qual você conectaria os topos descendentes para estabelecer a linha de tendência. Durante a consolidação dos preços, a formação de múltiplas linhas de tendência pode revelar padrões que fortalecem os sinais de negociação. Abaixo está uma representação ilustrativa.

Fig 1. Linhas de Tendência
O gráfico ilustra dois conceitos fundamentais: suporte e resistência. Um nível de suporte é uma região onde o movimento descendente dos preços frequentemente perde força porque os compradores tornam-se suficientemente ativos para absorver a pressão de venda. Quando linhas de tendência são utilizadas, o suporte normalmente é representado por linhas ascendentes que conectam pontos de mínima importantes. Em contraste, um nível de resistência é uma região onde o movimento ascendente dos preços frequentemente desacelera devido ao aumento da pressão de venda. Com linhas de tendência, a resistência é representada por linhas descendentes que conectam pontos de máxima significativos. Os traders frequentemente desenham linhas de tendência em vários horizontes de tempo para identificar se o mercado está em alta ou em baixa no curto, médio ou longo prazo.As linhas de tendência são uma ferramenta valiosa na análise técnica porque ajudam os participantes do mercado a avaliar a direção geral do preço de um ativo. Ao conectar máximas ou mínimas significativas em um período de tempo escolhido, essas linhas representam visualmente se o mercado está em alta, em baixa ou lateralizado. Essa percepção é especialmente útil para traders e investidores de curto prazo que utilizam tendências de preço para orientar suas decisões.
Indicador ZigZag
Embora a origem exata do indicador Zig Zag não esteja documentada de forma definitiva, algumas fontes atribuem sua criação a Bill Wolfe, um trader do S&P 500 conhecido por desenvolver as Wolfe Waves, um método semelhante às Ondas de Elliott, mas com técnicas de análise gráfica distintas. As Wolfe Waves consistem em cinco ondas que ilustram a oferta e a demanda movendo-se em direção a um preço de equilíbrio, consulte. O indicador Zig Zag destaca reversões significativas de preço que ultrapassam um limite especificado, normalmente expresso como uma porcentagem. Quando o preço oscila além desse limite, o indicador plota um novo ponto e desenha uma linha reta a partir do ponto anterior, filtrando efetivamente as oscilações menores. Ao fazer isso, torna as tendências subjacentes mais fáceis de identificar em diferentes períodos gráficos.
Os traders podem ajustar o limite percentual (por exemplo, 4% ou 5%) para capturar mais ou menos oscilações de preço, dependendo da volatilidade do ativo ou do seu estilo pessoal de negociação. Essa flexibilidade permite refinar a forma como o indicador Zig Zag identifica possíveis pontos de reversão. Na análise baseada em ondas, como a teoria das Ondas de Elliott, o Zig Zag pode ajudar a esclarecer a estrutura de cada onda. Em última análise, experimentar diferentes configurações costuma ser necessário para encontrar o equilíbrio ideal entre filtrar o ruído do mercado e detectar movimentos de preço realmente significativos.

Fig 2. Indicador ZigZag
A Figura 2 acima mostra um gráfico com o indicador Zig Zag. Ela demonstra como as linhas de tendência podem ser construídas a partir de seus pontos de oscilação (swings). O indicador Zig Zag simplifica a análise de tendências ao filtrar pequenas flutuações de preço e destacar os principais pontos de reversão. Ele marca máximas e mínimas significativas, que servem como pontos de referência para o desenho das linhas de tendência. Em uma tendência de alta, conectar esses fundos de oscilação cria uma linha de suporte, enquanto, em uma tendência de baixa, conectar os topos de oscilação forma uma linha de resistência. Esse método enfatiza a direção geral do mercado ao eliminar os ruídos. Ajustar a sensibilidade do indicador pode refinar ainda mais a precisão dessas linhas de tendência, proporcionando análises de negociação mais precisas.
Algoritmo do Sistema
1. Declarações Globais e Parâmetros de Entrada
Nesta seção inicial, configuramos os principais parâmetros de entrada e as variáveis globais que permitem personalizar o analisador sem a necessidade de alterar o código. Definimos parâmetros como o timeframe do gráfico e propriedades específicas do indicador ZigZag, como depth, deviation e backstep. Essas configurações determinam como o analisador identifica os pontos de oscilação. Também declaramos arrays para armazenar os dados do ZigZag e registrar o tempo e o preço dos principais pivôs.
Valorizamos essa configuração por sua simplicidade e estrutura modular. Por exemplo, alterando o parâmetro do timeframe do gráfico, você pode alternar facilmente entre tendências de curto e longo prazo. As propriedades do ZigZag permitem ajustar a sensibilidade do indicador, possibilitando adaptar a análise a diferentes condições de mercado ou ativos. Utilizamos variáveis globais e arrays como um ponto central para todos os dados dinâmicos. Essa abordagem melhora o desempenho e torna o código mais fácil de manter. Quando novos dados chegam, eles são armazenados de forma eficiente e ficam prontos para análise, tornando a personalização e a depuração muito mais simples.
De modo geral, ao centralizar essas declarações e parâmetros críticos, criamos uma ferramenta flexível e clara que permite adaptar o analisador às suas necessidades, mantendo os processos internos robustos e confiáveis.
// Input parameters input ENUM_TIMEFRAMES InpTimeFrame = PERIOD_CURRENT; // Timeframe to analyze input int ZZ_Depth = 12; // ZigZag depth input int ZZ_Deviation = 5; // ZigZag deviation input int ZZ_Backstep = 3; // ZigZag backstep input int LookBackBars = 200; // Bars to search for pivots input int ExtendFutureBars = 100; // Bars to extend trendlines into the future // Global indicator handle for ZigZag int zzHandle; // Arrays for ZigZag data and pivot storage double zzBuffer[]; datetime pivotTimes[]; double pivotPrices[]; bool pivotIsPeak[]; // Variable to detect new bars datetime lastBarTime = 0;
- InpTimeFrame: The timeframe for analysis.
- ZZ_Depth, ZZ_Deviation, ZZ_Backstep: Parameters that influence the sensitivity and behavior of the ZigZag indicator.
Arrays como zzBuffer armazenam a saída do indicador ZigZag. Arrays adicionais (pivotTimes, pivotPrices, pivotIsPeak) são reservados para armazenar os detalhes de cada pivô detectado. Uma variável (lastBarTime) ajuda a determinar se uma nova barra foi iniciada, garantindo que a análise seja atualizada somente quando necessário.
2. Função de Inicialização (OnInit)
Na Função de Inicialização, executamos o código uma única vez quando o indicador é carregado. Nosso principal objetivo é criar e configurar o indicador ZigZag utilizando os parâmetros definidos pelo usuário. Chamamos a função iCustom para instanciar o indicador ZigZag e armazenar seu identificador (handle). Se o indicador não for inicializado corretamente e retornar um identificador inválido, registramos um erro e interrompemos o processamento.
Utilizamos essa função para garantir que tudo esteja configurado corretamente antes do início de qualquer análise. Ela atua como um mecanismo de proteção, impedindo que o restante do código seja executado caso exista algum problema na configuração do indicador. Essa verificação antecipada economiza tempo e evita erros desnecessários durante a execução. Ao centralizar a inicialização nessa função, facilitamos o gerenciamento e a depuração do sistema caso algo dê errado. A função também garante que todas as configurações definidas pelo usuário sejam aplicadas desde o início, estabelecendo uma base sólida para a análise que será realizada.
int OnInit() { zzHandle = iCustom(_Symbol, InpTimeFrame, "ZigZag", ZZ_Depth, ZZ_Deviation, ZZ_Backstep); if(zzHandle == INVALID_HANDLE) { Print("Error creating ZigZag handle"); return(INIT_FAILED); } return(INIT_SUCCEEDED); }
- Criação do Indicador: Utiliza iCustom para carregar o indicador ZigZag com parâmetros específicos.
- Tratamento de Erros: Verifica se o identificador retornado é válido. Registra uma mensagem de erro e interrompe a inicialização caso ela falhe.
3. Função de Finalização (OnDeinit)
Quando o indicador é removido ou a plataforma é encerrada, é essencial limpar os objetos criados e liberar os recursos alocados. A função OnDeinit cuida dessa tarefa, removendo quaisquer objetos gráficos (como linhas de tendência ou linhas horizontais) desenhados no gráfico e liberando o identificador do indicador ZigZag. Isso garante que nenhum elemento indesejado permaneça no gráfico e que os recursos do sistema sejam gerenciados corretamente.
Limpeza de Recursos: Remove todos os objetos criados no gráfico para evitar acúmulo de elementos.Liberação do Identificador: Libera o identificador do indicador ZigZag utilizando IndicatorRelease.
void OnDeinit(const int reason) { ObjectDelete(0, "Downtrend_HighLine"); ObjectDelete(0, "Uptrend_LowLine"); ObjectDelete(0, "Major_Resistance"); ObjectDelete(0, "Major_Support"); ObjectDelete(0, "Minor_Resistance"); ObjectDelete(0, "Minor_Support"); IndicatorRelease(zzHandle); }
4. Função Principal de Execução (OnTick)
Este é o núcleo do algoritmo, executado a cada novo tick. A função primeiro verifica se uma nova barra foi formada comparando o timestamp da barra atual com o último tempo armazenado. Se nenhuma nova barra for detectada, a função é encerrada imediatamente para economizar poder de processamento. Quando uma nova barra é confirmada, ela remove os objetos antigos do gráfico, obtém os dados mais recentes do ZigZag utilizando CopyBuffer e, em seguida, chama duas funções auxiliares: uma para desenhar as linhas de tendência e outra para desenhar os níveis de suporte e resistência.
void OnTick() { datetime currentBarTime = iTime(_Symbol, InpTimeFrame, 0); if(currentBarTime == lastBarTime) return; lastBarTime = currentBarTime; // Remove previous objects ObjectDelete(0, "Downtrend_HighLine"); ObjectDelete(0, "Uptrend_LowLine"); ObjectDelete(0, "Major_Resistance"); ObjectDelete(0, "Major_Support"); ObjectDelete(0, "Minor_Resistance"); ObjectDelete(0, "Minor_Support"); if(CopyBuffer(zzHandle, 0, 0, LookBackBars, zzBuffer) <= 0) { Print("Failed to copy ZigZag data"); return; } ArraySetAsSeries(zzBuffer, true); DrawZigZagTrendlines(); DrawSupportResistance(); }
- Verificação de Nova Barra: Utiliza a função iTime para detectar a mudança de barra.
- Atualização do Buffer: Atualiza o zzBuffer com os dados mais recentes do ZigZag.
- Atualização do Gráfico: Remove os objetos gráficos anteriores para preparar o desenho dos novos. Chama as funções responsáveis por desenhar as linhas de tendência e as linhas de suporte e resistência.
5. Desenhando Linhas de Tendência Baseadas no ZigZag (DrawZigZagTrendlines)
Essa função é responsável por identificar os pontos de oscilação significativos nos dados do ZigZag e utilizá-los para calcular as linhas de tendência. Ela percorre o buffer para coletar máximas e mínimas, verificando se o valor atual do ZigZag corresponde à máxima ou à mínima da barra. Depois que os pontos de oscilação são coletados, o código utiliza regressão linear para determinar a linha de tendência de cada conjunto de pontos. O cálculo da regressão utiliza as seguintes fórmulas:
1. Inclinação (m)

Fig 3. Fórmula da Regressão
2. Intercepto (b)

Fig 4. Intercepto
- (t) representa os valores de tempo (ou a variável independente).
- (p) representa os valores de preço (ou a variável dependente).
- (N) é o número de pontos de dados utilizados na regressão.
Extração dos Pontos de Oscilação: Percorre o zzBuffer para coletar até 10 máximas e mínimas.
Exclusão do Último Ponto de Oscilação: Descarta o ponto de oscilação mais recente para reduzir o ruído.
Análise de Regressão: Calcula a inclinação (m) e o intercepto (b) utilizando as fórmulas apresentadas acima.
void DrawZigZagTrendlines() { double highPrices[10], lowPrices[10]; datetime highTimes[10], lowTimes[10]; int highCount = 0, lowCount = 0; // Extract swing points from the ZigZag buffer for(int i = 0; i < LookBackBars - 1; i++) { if(zzBuffer[i] != 0) { if(iHigh(_Symbol, InpTimeFrame, i) == zzBuffer[i] && highCount < 10) { highPrices[highCount] = zzBuffer[i]; highTimes[highCount] = iTime(_Symbol, InpTimeFrame, i); highCount++; } else if(iLow(_Symbol, InpTimeFrame, i) == zzBuffer[i] && lowCount < 10) { lowPrices[lowCount] = zzBuffer[i]; lowTimes[lowCount] = iTime(_Symbol, InpTimeFrame, i); lowCount++; } } } // Exclude the most recent swing if possible int usedHighCount = (highCount >= 4) ? highCount - 1 : highCount; int usedLowCount = (lowCount >= 4) ? lowCount - 1 : lowCount; double mHigh = 0, bHigh = 0, mLow = 0, bLow = 0; bool validHigh = false, validLow = false; // Regression for highs if(usedHighCount >= 3) { double sumT = 0, sumP = 0, sumTP = 0, sumT2 = 0; for(int i = 0; i < usedHighCount; i++) { double t = (double)highTimes[i]; double p = highPrices[i]; sumT += t; sumP += p; sumTP += t * p; sumT2 += t * t; } int N = usedHighCount; double denominator = N * sumT2 - sumT * sumT; if(denominator != 0) { mHigh = (N * sumTP - sumT * sumP) / denominator; bHigh = (sumP - mHigh * sumT) / N; } else bHigh = sumP / N; validHigh = true; } // Regression for lows if(usedLowCount >= 3) { double sumT = 0, sumP = 0, sumTP = 0, sumT2 = 0; for(int i = 0; i < usedLowCount; i++) { double t = (double)lowTimes[i]; double p = lowPrices[i]; sumT += t; sumP += p; sumTP += t * p; sumT2 += t * t; } int N = usedLowCount; double denominator = N * sumT2 - sumT * sumT; if(denominator != 0) { mLow = (N * sumTP - sumT * sumP) / denominator; bLow = (sumP - mLow * sumT) / N; } else bLow = sumP / N; validLow = true; } // Define time limits for trendlines datetime pastTime = iTime(_Symbol, InpTimeFrame, LookBackBars - 1); datetime futureTime = lastBarTime + ExtendFutureBars * PeriodSeconds(); // Draw trendlines if both regressions are valid if(validHigh && validLow) { // When slopes have the same sign, use average slope for parallel lines if(mHigh * mLow > 0) { double mParallel = (mHigh + mLow) / 2.0; double bHighParallel = highPrices[0] - mParallel * (double)highTimes[0]; double bLowParallel = lowPrices[0] - mParallel * (double)lowTimes[0]; datetime highStartTime = pastTime; double highStartPrice = mParallel * (double)highStartTime + bHighParallel; double highEndPrice = mParallel * (double)futureTime + bHighParallel; if(!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) Print("Failed to create High Trendline"); else { ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); } datetime lowStartTime = pastTime; double lowStartPrice = mParallel * (double)lowStartTime + bLowParallel; double lowEndPrice = mParallel * (double)futureTime + bLowParallel; if(!ObjectCreate(0, "Uptrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) Print("Failed to create Low Trendline"); else { ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_COLOR, clrGreen); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_RIGHT, true); } } else // Draw separate trendlines if slopes differ { datetime highStartTime = pastTime; double highStartPrice = mHigh * (double)highStartTime + bHigh; double highEndPrice = mHigh * (double)futureTime + bHigh; if(!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) Print("Failed to create High Trendline"); else { ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); } datetime lowStartTime = pastTime; double lowStartPrice = mLow * (double)lowStartTime + bLow; double lowEndPrice = mLow * (double)futureTime + bLow; if(!ObjectCreate(0, "Uptrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) Print("Failed to create Low Trendline"); else { ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_COLOR, clrGreen); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_RIGHT, true); } } } else { // Draw only available regression if only one set of points is valid if(validHigh) { datetime highStartTime = pastTime; double highStartPrice = mHigh * (double)highStartTime + bHigh; double highEndPrice = mHigh * (double)futureTime + bHigh; if(!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) Print("Failed to create High Trendline"); else { ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); } } if(validLow) { datetime lowStartTime = pastTime; double lowStartPrice = mLow * (double)lowStartTime + bLow; double lowEndPrice = mLow * (double)futureTime + bLow; if(!ObjectCreate(0, "Uptrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) Print("Failed to create Low Trendline"); else { ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_COLOR, clrGreen); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_RIGHT, true); } } } }
Desenho das Linhas de Tendência (Se ambas as tendências forem válidas)
- Inclinações com o Mesmo Sinal: Utiliza uma inclinação média para linhas paralelas.
- Inclinações com Sinais Diferentes: Desenha linhas de tendência individuais. Utiliza ObjectCreate e ObjectSetInteger para posicionar e estilizar as linhas.
6. Desenhando Níveis de Suporte e Resistência (DrawSupportResistance)
A parte do código responsável pelos níveis de suporte e resistência foi projetada para identificar dinamicamente os principais níveis horizontais de preço com base nos pontos de oscilação confirmados fornecidos pelo indicador ZigZag. Além de simplesmente definir a máxima confirmada mais alta como resistência principal e a mínima confirmada mais baixa como suporte principal, o algoritmo vai além, filtrando máximas e mínimas significativas para determinar tanto os níveis principais quanto os secundários. Esse processo é fundamental porque suporte e resistência são conceitos essenciais da análise técnica. O suporte representa um nível em que o preço tende a parar de cair devido ao aumento da demanda, enquanto a resistência indica um nível em que o preço normalmente encontra dificuldade para continuar subindo devido à pressão da oferta.
Nesta implementação, o código examina todos os pontos de oscilação confirmados, classificando-os e comparando-os cuidadosamente para isolar os valores mais extremos. Isso garante que os níveis desenhados no gráfico não representem apenas flutuações temporárias, mas sim zonas relevantes que historicamente influenciaram reversões de preço. Ao identificar o segundo maior e o segundo menor valores como resistência e suporte secundários, respectivamente, o algoritmo fornece camadas adicionais de informação. Esses níveis secundários podem servir como sinais antecipados ou objetivos intermediários, oferecendo aos traders uma visão mais detalhada das possíveis reações do preço antes que ele alcance os níveis principais mais importantes.
Essa abordagem é extremamente útil em um mercado em constante mudança. À medida que novos dados de preço são recebidos, o indicador ZigZag identifica novos pontos de oscilação, atualizando automaticamente os níveis de suporte e resistência para refletir a estrutura mais recente do mercado. Esse ajuste em tempo real é essencial para os traders, ajudando-os a permanecer alinhados com as mudanças nas barreiras de preço que podem influenciar suas decisões de entrada ou saída. Esses níveis são representados visualmente no gráfico por linhas horizontais com cores distintas, tornando-os fáceis de identificar rapidamente. Essa clareza adicional permite que os traders reconheçam com facilidade as principais regiões onde o preço pode reagir, ajudando-os a posicionar ordens de stop-loss ou definir metas de lucro com maior confiança. Em última análise, esta parte do código não apenas calcula os níveis de suporte e resistência, mas transforma dados brutos do mercado em informações claras e úteis para a tomada de decisão, tornando-se uma ferramenta essencial para qualquer trader que utilize análise técnica.
void DrawSupportResistance() { double confirmedHighs[10], confirmedLows[10]; int confHighCount = 0, confLowCount = 0; for(int i = 0; i < LookBackBars - 1; i++) { if(zzBuffer[i] != 0) { if(iHigh(_Symbol, InpTimeFrame, i) == zzBuffer[i] && confHighCount < 10) { confirmedHighs[confHighCount] = zzBuffer[i]; confHighCount++; } else if(iLow(_Symbol, InpTimeFrame, i) == zzBuffer[i] && confLowCount < 10) { confirmedLows[confLowCount] = zzBuffer[i]; confLowCount++; } } } int usedHighCount = (confHighCount >= 4) ? confHighCount - 1 : confHighCount; int usedLowCount = (confLowCount >= 4) ? confLowCount - 1 : confLowCount; double majorResistance = -1e9, majorSupport = 1e9; double minorResistance = -1e9, minorSupport = 1e9; double tempHigh = -1e9, tempLow = -1e9; for(int i = 0; i < usedHighCount; i++) { if(confirmedHighs[i] > majorResistance) { tempHigh = majorResistance; majorResistance = confirmedHighs[i]; } else if(confirmedHighs[i] > tempHigh) { tempHigh = confirmedHighs[i]; } } if(tempHigh > -1e9) minorResistance = tempHigh; for(int i = 0; i < usedLowCount; i++) { if(confirmedLows[i] < majorSupport) { tempLow = majorSupport; majorSupport = confirmedLows[i]; } else if(confirmedLows[i] < tempLow) { tempLow = confirmedLows[i]; } } if(tempLow < 1e9) minorSupport = tempLow; if(usedHighCount > 0) { if(!ObjectCreate(0, "Major_Resistance", OBJ_HLINE, 0, 0, majorResistance)) Print("Failed to create Major Resistance"); else ObjectSetInteger(0, "Major_Resistance", OBJPROP_COLOR, clrMagenta); if(minorResistance > -1e9 && minorResistance < majorResistance) { if(!ObjectCreate(0, "Minor_Resistance", OBJ_HLINE, 0, 0, minorResistance)) Print("Failed to create Minor Resistance"); else ObjectSetInteger(0, "Minor_Resistance", OBJPROP_COLOR, clrFuchsia); } } if(usedLowCount > 0) { if(!ObjectCreate(0, "Major_Support", OBJ_HLINE, 0, 0, majorSupport)) Print("Failed to create Major Support"); else ObjectSetInteger(0, "Major_Support", OBJPROP_COLOR, clrAqua); if(minorSupport < 1e9 && minorSupport > majorSupport) { if(!ObjectCreate(0, "Minor_Support", OBJ_HLINE, 0, 0, minorSupport)) Print("Failed to create Minor Support"); else ObjectSetInteger(0, "Minor_Support", OBJPROP_COLOR, clrBlue); } } }
A coleta de dados percorre o buffer para extrair as máximas e mínimas confirmadas. A determinação dos níveis utiliza comparações para isolar os valores mais extremos. A resistência principal e o suporte principal são definidos como os valores mais extremos, enquanto os valores imediatamente seguintes tornam-se os níveis secundários. A representação gráfica cria linhas horizontais (OBJ_HLINE) para cada nível identificado. São aplicadas cores distintas a cada linha para facilitar sua diferenciação.
Código em MQL5
//+------------------------------------------------------------------+ //| ZigZag Analyzer.mq5| //| Copyright 2025, MetaQuotes Ltd.| //| https://www.mql5.com/en/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict // Input parameters input ENUM_TIMEFRAMES InpTimeFrame = PERIOD_CURRENT; // Timeframe to analyze input int ZZ_Depth = 12; // ZigZag depth input int ZZ_Deviation = 5; // ZigZag deviation input int ZZ_Backstep = 3; // ZigZag backstep input int LookBackBars = 200; // Bars to search for pivots input int ExtendFutureBars = 100; // Bars to extend trendlines into the future // Global indicator handle for ZigZag int zzHandle; // Arrays for ZigZag data and pivot storage double zzBuffer[]; datetime pivotTimes[]; double pivotPrices[]; bool pivotIsPeak[]; // Variable to detect new bars datetime lastBarTime = 0; //+------------------------------------------------------------------+ //| Initialization function | //+------------------------------------------------------------------+ int OnInit() { zzHandle = iCustom(_Symbol, InpTimeFrame, "ZigZag", ZZ_Depth, ZZ_Deviation, ZZ_Backstep); if(zzHandle == INVALID_HANDLE) { Print("Error creating ZigZag handle"); return(INIT_FAILED); } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectDelete(0, "Downtrend_HighLine"); ObjectDelete(0, "Uptrend_LowLine"); ObjectDelete(0, "Major_Resistance"); ObjectDelete(0, "Major_Support"); ObjectDelete(0, "Minor_Resistance"); ObjectDelete(0, "Minor_Support"); IndicatorRelease(zzHandle); } //+------------------------------------------------------------------+ //| Tick function | //+------------------------------------------------------------------+ void OnTick() { datetime currentBarTime = iTime(_Symbol, InpTimeFrame, 0); if(currentBarTime == lastBarTime) return; lastBarTime = currentBarTime; // Remove previous objects ObjectDelete(0, "Downtrend_HighLine"); ObjectDelete(0, "Uptrend_LowLine"); ObjectDelete(0, "Major_Resistance"); ObjectDelete(0, "Major_Support"); ObjectDelete(0, "Minor_Resistance"); ObjectDelete(0, "Minor_Support"); if(CopyBuffer(zzHandle, 0, 0, LookBackBars, zzBuffer) <= 0) { Print("Failed to copy ZigZag data"); return; } ArraySetAsSeries(zzBuffer, true); DrawZigZagTrendlines(); DrawSupportResistance(); } //+------------------------------------------------------------------+ //| Draw ZigZag-based Trendlines | //+------------------------------------------------------------------+ void DrawZigZagTrendlines() { double highPrices[10], lowPrices[10]; datetime highTimes[10], lowTimes[10]; int highCount = 0, lowCount = 0; // Extract swing points from the ZigZag buffer for(int i = 0; i < LookBackBars - 1; i++) { if(zzBuffer[i] != 0) { if(iHigh(_Symbol, InpTimeFrame, i) == zzBuffer[i] && highCount < 10) { highPrices[highCount] = zzBuffer[i]; highTimes[highCount] = iTime(_Symbol, InpTimeFrame, i); highCount++; } else if(iLow(_Symbol, InpTimeFrame, i) == zzBuffer[i] && lowCount < 10) { lowPrices[lowCount] = zzBuffer[i]; lowTimes[lowCount] = iTime(_Symbol, InpTimeFrame, i); lowCount++; } } } // Exclude the most recent swing if possible int usedHighCount = (highCount >= 4) ? highCount - 1 : highCount; int usedLowCount = (lowCount >= 4) ? lowCount - 1 : lowCount; double mHigh = 0, bHigh = 0, mLow = 0, bLow = 0; bool validHigh = false, validLow = false; // Regression for highs if(usedHighCount >= 3) { double sumT = 0, sumP = 0, sumTP = 0, sumT2 = 0; for(int i = 0; i < usedHighCount; i++) { double t = (double)highTimes[i]; double p = highPrices[i]; sumT += t; sumP += p; sumTP += t * p; sumT2 += t * t; } int N = usedHighCount; double denominator = N * sumT2 - sumT * sumT; if(denominator != 0) { mHigh = (N * sumTP - sumT * sumP) / denominator; bHigh = (sumP - mHigh * sumT) / N; } else bHigh = sumP / N; validHigh = true; } // Regression for lows if(usedLowCount >= 3) { double sumT = 0, sumP = 0, sumTP = 0, sumT2 = 0; for(int i = 0; i < usedLowCount; i++) { double t = (double)lowTimes[i]; double p = lowPrices[i]; sumT += t; sumP += p; sumTP += t * p; sumT2 += t * t; } int N = usedLowCount; double denominator = N * sumT2 - sumT * sumT; if(denominator != 0) { mLow = (N * sumTP - sumT * sumP) / denominator; bLow = (sumP - mLow * sumT) / N; } else bLow = sumP / N; validLow = true; } // Define time limits for trendlines datetime pastTime = iTime(_Symbol, InpTimeFrame, LookBackBars - 1); datetime futureTime = lastBarTime + ExtendFutureBars * PeriodSeconds(); // Draw trendlines if both regressions are valid if(validHigh && validLow) { // When slopes have the same sign, use average slope if(mHigh * mLow > 0) { double mParallel = (mHigh + mLow) / 2.0; double bHighParallel = highPrices[0] - mParallel * (double)highTimes[0]; double bLowParallel = lowPrices[0] - mParallel * (double)lowTimes[0]; datetime highStartTime = pastTime; double highStartPrice = mParallel * (double)highStartTime + bHighParallel; double highEndPrice = mParallel * (double)futureTime + bHighParallel; if(!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) Print("Failed to create High Trendline"); else { ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); } datetime lowStartTime = pastTime; double lowStartPrice = mParallel * (double)lowStartTime + bLowParallel; double lowEndPrice = mParallel * (double)futureTime + bLowParallel; if(!ObjectCreate(0, "Uptrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) Print("Failed to create Low Trendline"); else { ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_COLOR, clrGreen); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_RIGHT, true); } } else { datetime highStartTime = pastTime; double highStartPrice = mHigh * (double)highStartTime + bHigh; double highEndPrice = mHigh * (double)futureTime + bHigh; if(!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) Print("Failed to create High Trendline"); else { ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); } datetime lowStartTime = pastTime; double lowStartPrice = mLow * (double)lowStartTime + bLow; double lowEndPrice = mLow * (double)futureTime + bLow; if(!ObjectCreate(0, "Uptrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) Print("Failed to create Low Trendline"); else { ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_COLOR, clrGreen); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_RIGHT, true); } } } else { if(validHigh) { datetime highStartTime = pastTime; double highStartPrice = mHigh * (double)highStartTime + bHigh; double highEndPrice = mHigh * (double)futureTime + bHigh; if(!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) Print("Failed to create High Trendline"); else { ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); } } if(validLow) { datetime lowStartTime = pastTime; double lowStartPrice = mLow * (double)lowStartTime + bLow; double lowEndPrice = mLow * (double)futureTime + bLow; if(!ObjectCreate(0, "Uptrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) Print("Failed to create Low Trendline"); else { ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_COLOR, clrGreen); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_LEFT, true); ObjectSetInteger(0, "Uptrend_LowLine", OBJPROP_RAY_RIGHT, true); } } } } //+------------------------------------------------------------------+ //| Draw Support and Resistance Levels | //+------------------------------------------------------------------+ void DrawSupportResistance() { double confirmedHighs[10], confirmedLows[10]; int confHighCount = 0, confLowCount = 0; for(int i = 0; i < LookBackBars - 1; i++) { if(zzBuffer[i] != 0) { if(iHigh(_Symbol, InpTimeFrame, i) == zzBuffer[i] && confHighCount < 10) { confirmedHighs[confHighCount] = zzBuffer[i]; confHighCount++; } else if(iLow(_Symbol, InpTimeFrame, i) == zzBuffer[i] && confLowCount < 10) { confirmedLows[confLowCount] = zzBuffer[i]; confLowCount++; } } } int usedHighCount = (confHighCount >= 4) ? confHighCount - 1 : confHighCount; int usedLowCount = (confLowCount >= 4) ? confLowCount - 1 : confLowCount; double majorResistance = -1e9, majorSupport = 1e9; double minorResistance = -1e9, minorSupport = 1e9; double tempHigh = -1e9, tempLow = -1e9; for(int i = 0; i < usedHighCount; i++) { if(confirmedHighs[i] > majorResistance) { tempHigh = majorResistance; majorResistance = confirmedHighs[i]; } else if(confirmedHighs[i] > tempHigh) { tempHigh = confirmedHighs[i]; } } if(tempHigh > -1e9) minorResistance = tempHigh; for(int i = 0; i < usedLowCount; i++) { if(confirmedLows[i] < majorSupport) { tempLow = majorSupport; majorSupport = confirmedLows[i]; } else if(confirmedLows[i] < tempLow) { tempLow = confirmedLows[i]; } } if(tempLow < 1e9) minorSupport = tempLow; if(usedHighCount > 0) { if(!ObjectCreate(0, "Major_Resistance", OBJ_HLINE, 0, 0, majorResistance)) Print("Failed to create Major Resistance"); else ObjectSetInteger(0, "Major_Resistance", OBJPROP_COLOR, clrMagenta); if(minorResistance > -1e9 && minorResistance < majorResistance) { if(!ObjectCreate(0, "Minor_Resistance", OBJ_HLINE, 0, 0, minorResistance)) Print("Failed to create Minor Resistance"); else ObjectSetInteger(0, "Minor_Resistance", OBJPROP_COLOR, clrFuchsia); } } if(usedLowCount > 0) { if(!ObjectCreate(0, "Major_Support", OBJ_HLINE, 0, 0, majorSupport)) Print("Failed to create Major Support"); else ObjectSetInteger(0, "Major_Support", OBJPROP_COLOR, clrAqua); if(minorSupport < 1e9 && minorSupport > majorSupport) { if(!ObjectCreate(0, "Minor_Support", OBJ_HLINE, 0, 0, minorSupport)) Print("Failed to create Minor Support"); else ObjectSetInteger(0, "Minor_Support", OBJPROP_COLOR, clrBlue); } } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+
Resultados
Antes de analisarmos os resultados, vamos primeiro adicionar o indicador ZigZag ao gráfico. Para compreender melhor o processo, consulte o diagrama em GIF abaixo. Esse recurso visual ajudará a ilustrar cada etapa envolvida. Ao acompanhar o diagrama, você verá como o indicador é integrado e configurado. Depois que isso estiver claro, poderemos prosseguir para a análise dos resultados.

Fig 5. Inicializando o Indicador ZigZag
Esta sequência de imagens ilustra os resultados obtidos, sendo que cada diagrama é acompanhado de explicações detalhadas. Primeiramente, o diagrama abaixo mostra o resultado obtido após executar a ferramenta Analisador ZigZag no Step Index. Como pode ser observado, as linhas de tendência são desenhadas corretamente quando o EA é aplicado ao gráfico. Tanto a linha inferior quanto a linha superior apresentam inclinação descendente, indicando uma tendência de baixa. Além disso, as imagens abaixo mostram claramente os níveis de suporte e resistência, tanto principais quanto secundários.

Fig 6. Resultado no Step Index
Esta captura de tela fornece informações adicionais sobre a análise. Na imagem, é possível observar pontos de oscilação importantes, como topos mais baixos e fundos mais baixos, utilizados para construir as linhas de tendência. Os níveis principais e secundários de suporte e resistência estão claramente marcados. Em especial, as interseções entre as linhas de tendência e os níveis principais destacam fortes pontos de reversão. O topo mais baixo encontra a resistência principal, enquanto o fundo mais baixo encontra o suporte principal, onde ocorre uma reversão.

Fig 7. Step Index
Este gráfico do USDCHF mostra as linhas de tendência desenhadas. Elas estão presentes no gráfico, sem se sobrepor excessivamente aos demais elementos. O mercado respeita os níveis de suporte e resistência, o que valida a eficácia do sistema. Destaquei os pontos onde as linhas de tendência tocam os pontos de oscilação do mercado.

Fig 8. USDCHF
Por fim, podemos observar seu desempenho no índice Volatility 25. As linhas de tendência são desenhadas de forma clara, conectando os pontos de oscilação. As linhas destacam o movimento do mercado com precisão. Essa clareza reforça a confiabilidade do sistema em diferentes condições de mercado. Os resultados confirmam a eficácia da nossa abordagem.

Fig 9. Índice Volatility 25
Conclusão
A ferramenta Analisador ZigZag é uma maneira poderosa de automatizar a análise da ação do preço utilizando MQL5. Nossos testes indicam que as linhas de tendência são desenhadas de forma eficaz em mercados com tendência. Acredito que essa abordagem possa levar ao desenvolvimento de ferramentas adicionais capazes de detectar padrões como bandeiras e triângulos. Quando executado, o Analisador ZigZag serve como um ponto de partida para iniciantes no mercado Forex observarem tendências e identificarem possíveis zonas de suporte ou resistência. Ele também é valioso para traders experientes, pois as linhas de tendência são elementos centrais da análise de Price Action. Esta ferramenta é excelente para aprendizado e pode ser personalizada para atender a diferentes estratégias de negociação, incorporando também outros métodos de confirmação.
| Data | Nome da Ferramenta | Descrição | Versão | Atualizações | Notas |
|---|---|---|---|---|---|
| 01/10/24 | Projetor de Gráfico | Script para sobrepor a ação de preço do dia anterior com efeito fantasma. | 1.0 | Lançamento Inicial | Ferramenta número 1 |
| 18/11/24 | Comentário Analítico | Fornece informações do dia anterior em formato tabular, bem como antecipa a direção futura do mercado. | 1.0 | Lançamento Inicial | Ferramenta número 2 |
| 27/11/24 | Mestre Analítico | Atualização regular das métricas de mercado a cada duas horas | 1.01 | Segundo Lançamento | Ferramenta número 3 |
| 02/12/24 | Previsor Analítico | Atualização regular das métricas de mercado a cada duas horas com integração ao telegram | 1.1 | Terceira Edição | Ferramenta número 4 |
| 09/12/24 | Navegador de Volatilidade | O EA analisa as condições de mercado usando os indicadores Bandas de Bollinger, RSI e ATR | 1.0 | Lançamento Inicial | Ferramenta número 5 |
| 19/12/24 | Coletor de Sinais de Reversão à Média | Analisa o mercado usando estratégia de reversão à média e fornece sinal | 1.0 | Lançamento Inicial | Ferramenta número 6 |
| 9/01/25 | Pulso de Sinais | Analisador de múltiplos timeframes | 1.0 | Lançamento Inicial | Ferramenta número 7 |
| 17/01/25 | Painel de Métricas | Painel com botão para análise | 1.0 | Lançamento Inicial | Ferramenta número 8 |
| 21/01/25 | Fluxo Externo | Análises por meio de bibliotecas externas | 1.0 | Lançamento Inicial | Ferramenta número 9 |
| 27/01/25 | VWAP | Preço Médio Ponderado por Volume | 1.3 | Lançamento Inicial | Ferramenta número 10 |
| 02/02/25 | Heikin Ashi | Suavização de tendência e identificação de sinais de reversão | 1.0 | Lançamento Inicial | Ferramenta número 11 |
| 04/02/25 | FibVWAP | Geração de sinais por meio de análise em python | 1.0 | Lançamento Inicial | Ferramenta número 12 |
| 14/02/25 | DIVERGÊNCIA RSI | Ação de preço versus divergências do RSI | 1.0 | Lançamento Inicial | Ferramenta número 13 |
| 17/02/25 | Parabolic Stop and Reverse (PSAR) | Automatização da estratégia PSAR | 1.0 | Lançamento Inicial | Ferramenta número 14 |
| 20/02/25 | Script Quarters Drawer | Desenho de níveis de quarters no gráfico | 1.0 | Lançamento Inicial | Ferramenta número 15 |
| 27/02/25 | Detector de Intrusão | Detecta e alerta quando o preço atinge níveis de quarters | 1.0 | Lançamento Inicial | Ferramenta número 16 |
| 27/02/25 | Ferramenta TrendLoom | Painel de análise multi timeframe | 1.0 | Lançamento Inicial | Ferramenta número 17 |
| 11/03/25 | Painel de Quartos | Painel com botões para ativar ou desativar níveis de quarters | 1.0 | Lançamento Inicial | Ferramenta número 18 |
| 26/03/25 | Analisador ZigZag | Desenhando linhas de tendência utilizando o indicador ZigZag | 1.0 | Lançamento Inicial | Ferramenta número 19 |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/17625
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.
EA Forex baseado em rede neural N-BEATS Network
Criando um Painel Administrativo de Negociação em MQL5 (Parte IX): Organização do Código (IV): Classe do Painel de Gerenciamento de Negociações
Redes neurais em trading: modelo de difusão adaptativa em grafos (módulo de atenção)
Dominando Logs (Parte 6): Armazenando logs em um banco de dados
- 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
Acho esse indicador muito interessante. Ele me ajudaria bastante na minha análise técnica, já que dedico a maior parte do tempo a ela para identificar a tendência correta, os níveis de suporte e resistência. Baixei o código e ele compilou corretamente, mas, quando o adicionei ao gráfico, nenhuma informação apareceu. Estou fazendo algo errado? Anexei um vídeo novamente. Atenciosamente.
>Oi, Cristina, tudo bem?
Acho esse indicador muito interessante. Ele me ajudaria bastante na minha análise técnica, já que dedico a maior parte do tempo a ela para identificar a tendência correta, os níveis de suporte e resistência. Baixei o código e ele compilou corretamente, mas, quando o adicionei ao gráfico, nenhuma informação apareceu. Estou fazendo algo errado? Anexei um vídeo novamente. Atenciosamente.
Oi, Diego Herrera
>Oi, Cristina, tudo bem?
Acho esse indicador muito interessante. Ele me ajudaria bastante na minha análise técnica, já que dedico a maior parte do tempo a ela para identificar a tendência correta, os níveis de suporte e resistência. Baixei o código e ele compilou corretamente, mas, quando o adicionei ao gráfico, nenhuma informação foi exibida. Estou fazendo algo errado? Anexei um vídeo novamente. Atenciosamente.
>Oi, Cristina, tudo bem?
Acho esse indicador muito interessante. Ele me ajudaria bastante na minha análise técnica, já que dedico a maior parte do tempo a ela para identificar a tendência correta, os níveis de suporte e resistência. Baixei o código e ele compilou corretamente, mas, quando o adicionei ao gráfico, nenhuma informação apareceu. Estou fazendo algo errado? Anexei um vídeo novamente. Atenciosamente.
Tente isso