Introdução à linguagem MQL5 (Parte 16): Criação de EAs com padrões de análise técnica
Introdução
Sejam novamente bem-vindos à Parte 16 da série "Introdução à linguagem MQL5"! Esta parte promete ser especialmente interessante, pois continuaremos aplicando tudo o que aprendemos até aqui por meio da abordagem baseada em projetos, como de costume. Vamos desenvolver um projeto prático que combina padrões de análise técnica e programação em MQL5 para criar um EA, ajudando você a aprofundar seus conhecimentos com a experiência prática.
Vamos nos concentrar no padrão "Cabeça e Ombros", uma das formações técnicas mais conhecidas para identificar uma possível reversão de tendência. Nosso projeto será implementado na forma de um EA capaz de reconhecer automaticamente essa formação e executar operações com base nela. Além disso, ele também funcionará como um indicador, destacando visualmente o padrão "Cabeça e Ombros" no gráfico, o que ajudará você a identificá-lo e compreendê-lo mais rapidamente em condições reais de negociação.
Neste artigo, você aprenderá:
- Como automatizar operações baseadas em padrões gráficos
- Como identificar corretamente o padrão "Cabeça e Ombros"
- Como detectar e traçar programaticamente os pontos de swing
- Como trabalhar com objetos gráficos em MQL5
- Como definir o ponto de entrada, o Stop Loss e o Take Profit
-
Como evitar sinais duplicados
1. O que são padrões gráficos
Os padrões gráficos são formações visuais nos gráficos de preços que podem ser utilizadas para projetar possíveis movimentos futuros do mercado. Essas formações muitas vezes indicam se a tendência pode continuar ou se reverter, surgem da interação constante entre compradores e vendedores. O padrão "Cabeça e Ombros", por exemplo, geralmente sinaliza uma possível reversão de uma tendência de alta para uma tendência de baixa, enquanto o padrão "Bandeira", quando aparece durante uma tendência de alta, costuma indicar uma provável continuação do movimento. Aprender a identificar padrões gráficos como triângulos, retângulos e topos duplos amplia sua capacidade de reconhecer possíveis oportunidades de negociação com base no comportamento anterior dos preços.
Analogia
Assim como pegadas na areia, os padrões gráficos são pistas visíveis deixadas pela disputa constante entre compradores e vendedores. Da mesma forma que um rastreador experiente pode usar pegadas de animais para descobrir qual animal passou por determinado local e em que direção seguiu, os padrões gráficos ajudam você a entender o que aconteceu no mercado e a antecipar o que pode acontecer em seguida. Esses padrões nos gráficos de preços mostram o comportamento coletivo dos participantes do mercado e frequentemente indicam se a tendência atual tende a continuar ou a se reverter.
1.1. Categorias de padrões
Os padrões gráficos geralmente são divididos em três categorias principais:
1.1.1.Padrões de reversão
Os padrões gráficos conhecidos como padrões de reversão podem ser utilizados para identificar possíveis pontos de reversão no mercado. Eles indicam que a tendência atual pode estar chegando ao fim e que uma nova tendência, no sentido oposto, pode estar prestes a começar. Por exemplo, se o mercado estiver em queda, um padrão de reversão pode indicar que o preço está se preparando para iniciar uma tendência de alta. Da mesma forma, esses padrões podem sinalizar uma possível reversão em um mercado em alta. O padrão "Cabeça e Ombros" e o padrão "Fundo Duplo" são exemplos clássicos de padrões de reversão.
Analogia
Os padrões de reversão funcionam como sinais de uma possível mudança na direção do mercado. Eles indicam que uma tendência, seja de alta ou de baixa, pode estar perdendo força e se aproximando de uma reversão, assim como pegadas que mudam de direção na praia mostram que alguém alterou seu caminho. Esses padrões ajudam os traders a identificar possíveis mudanças no movimento do preço, indicando quando compradores ou vendedores começam a ganhar força.

1.1.2.Padrões de continuação
Os padrões gráficos conhecidos como padrões de continuação indicam situações em que o mercado provavelmente continuará se movendo na direção atual. Eles costumam surgir durante períodos de consolidação ou breves pausas no movimento do preço antes da retomada da tendência. Por exemplo, em uma tendência de alta, um padrão de continuação pode indicar que o preço continuará subindo após uma breve correção lateral. Em uma tendência de baixa, esse tipo de padrão sugere que o preço provavelmente continuará caindo após uma pausa. Retângulos e bandeiras são exemplos clássicos de padrões de continuação.
Analogia
Assim como pegadas, os padrões de continuação podem mostrar uma breve pausa sem deixar de apontar na mesma direção. Imagine que você esteja seguindo uma pessoa que caminha pela praia. Ao acompanhar uma sequência contínua de pegadas, percebe que a sequência de pegadas se interrompe por um instante, talvez para a pessoa amarrar os cadarços ou olhar ao redor, e depois continuam na mesma direção. Isso indica que houve apenas uma breve pausa, sem mudança de rumo.Da mesma forma, os padrões de continuação indicam que o mercado fez uma pausa temporária, mas provavelmente continuará se movendo na direção atual. Esses padrões mostram que o momentum não se reverteu, apenas perdeu força momentaneamente, independentemente de o mercado estar subindo ou caindo.

1.1.3. Padrões neutros
Os padrões gráficos que representam um período de consolidação durante o qual o mercado pode romper em qualquer direção são conhecidos como padrões neutros. Esses padrões refletem um equilíbrio entre compradores e vendedores, sem que nenhum dos lados tenha predominância. Por isso, os traders precisam aguardar a confirmação do rompimento antes de agir, enquanto o preço permanece dentro de uma faixa estreita. Embora não permitam prever a direção do rompimento, os padrões neutros ajudam os traders a se preparar para um possível movimento. Um exemplo comum desse tipo de formação é o triângulo simétrico.
Analogia
Os padrões neutros no trading são como observar alguém na praia hesitando, sem saber qual será seu próximo passo. Assim como não é possível prever para onde essa pessoa irá até que ela tome uma decisão, os padrões neutros refletem um momento de indecisão no mercado, em que compradores e vendedores permanecem em equilíbrio. Embora não haja uma direção claramente predominante, esses padrões indicam que um rompimento pode ocorrer tanto para cima quanto para baixo.

2. Configuração do projeto
2.1. Como o EA funciona
O EA deste projeto foi desenvolvido para identificar automaticamente o padrão "Cabeça e Ombros" no mercado e executar operações com base nessa estrutura. Seja no padrão convencional "Cabeça e Ombros" ou em sua versão invertida, o EA identificará uma formação válida, determinará o melhor ponto de entrada e executará a operação na direção esperada do rompimento.
Mas o EA não se limita a isso. Para tornar o padrão mais visível e fácil de interpretar no gráfico, também utilizaremos objetos gráficos para marcar o ombro esquerdo, a cabeça e o ombro direito. Esses marcadores visuais ajudam o trader a confirmar a formação e tornam a leitura mais clara ao analisar o gráfico ou testar a estratégia com dados históricos.Além da automação e do reconhecimento do padrão, esse recurso acrescenta uma etapa visual de confirmação, facilitando a depuração, o aprendizado e até mesmo o monitoramento em tempo real.
2.1.1. Lógica de compra
Para iniciar uma operação de compra, o EA identificará um padrão "Cabeça e Ombros Invertido" por meio de seis pontos de swing específicos dispostos em sequência:
- Máxima de swing (X) - máxima inicial, identificada pela letra X.
- Mínima de swing (A) - mínima mais baixa após X, identificada pela letra A.
- Máxima de swing (B) - máxima formada durante a recuperação, abaixo de X, mas acima de A, identificada pela letra B.
- Mínima de swing (C) - mínima abaixo do ponto A, identificada pela letra C (esta é a "cabeça").
- Máxima de swing (D) - máxima de swing aproximadamente no mesmo nível do ponto B, identificada pela letra D (faz parte da "linha de pescoço").
- Mínima de swing (E) - mínima mais alta em relação ao ponto C e aproximadamente no mesmo nível do ponto A (este é o segundo "ombro").
Assim que essa estrutura for identificada:
- O EA aguardará o fechamento de um candle acima do ponto D (linha de pescoço).
- Quando isso ocorrer, o EA executará uma operação de compra.
- O Stop Loss (SL) será posicionado na mínima do ponto E.
- O Take Profit (TP) será inicialmente definido no ponto X (a primeira máxima de swing).
No entanto, se a distância entre o ponto de entrada e o TP (X) for menor que a distância até o Stop Loss, o EA desconsiderará X como alvo e, em seu lugar, definirá um alvo fixo correspondente a uma relação risco/retorno de 1:3, calculado com base na distância até o SL. Isso garante que a estratégia mantenha uma relação risco/retorno mínima e evita operações cujo retorno potencial não compense o risco assumido.

2.1.1. Lógica de venda
Para iniciar uma operação de venda, o EA identificará o padrão convencional "Cabeça e Ombros" por meio de seis pontos de swing específicos dispostos em sequência:
- Mínima de swing (X) - mínima inicial, identificada pela letra X.
- Máxima de swing (A) - máxima mais alta após X, identificada pela letra A.
- Mínima de swing (B) - mínima formada durante o recuo, acima de X, mas abaixo de A, identificada pela letra B.
- Máxima de swing (C) - máxima acima do ponto A, identificada pela letra C (esta é a "cabeça").
- Mínima de swing (D) - mínima de swing aproximadamente no mesmo nível do ponto B, identificada pela letra D (faz parte da "linha de pescoço").
- Máxima de swing (E) - máxima mais baixa em relação ao ponto C e aproximadamente no mesmo nível do ponto A (este é o segundo "ombro").
Assim que essa estrutura for confirmada:
- O EA aguardará o fechamento de um candle abaixo do ponto D (linha de pescoço).
- Quando isso ocorrer, o EA executará uma operação de venda.
- O Stop Loss (SL) será posicionado na máxima do ponto E.
- O Take Profit (TP) será inicialmente definido no ponto X (a primeira mínima de swing).
Se a distância entre o ponto de entrada e o TP (X) for menor que a distância até o Stop Loss, o EA desconsiderará X como alvo e, em seu lugar, definirá um alvo fixo com relação risco/retorno de 1:3 em relação à distância até o SL.

Observação:O principal objetivo da estratégia de negociação abordada neste projeto é ampliar seus conhecimentos de MQL5, especialmente no que diz respeito ao uso de padrões gráficos e à criação de EAs úteis. Esta estratégia não se destina ao uso com dinheiro real nem à negociação em tempo real. Antes de aplicar qualquer estratégia no mercado real, realize sempre testes abrangentes com dados históricos e consulte seu assessor financeiro.
3. Identificação do padrão "Cabeça e Ombros" no gráfico
Neste ponto, parto do princípio de que você já compreende bem o conceito de padrões gráficos e sabe exatamente o que esperamos do nosso EA. Mesmo antes de o EA executar uma operação, é fundamental conseguir visualizar o padrão "Cabeça e Ombros" no gráfico. Isso ajuda a identificar possíveis erros de execução ou problemas na lógica ainda nas etapas iniciais dos testes, além de permitir confirmar se a lógica do EA corresponde à ação real do preço.
Nesta seção, veremos como destacar e verificar manualmente a estrutura "Cabeça e Ombros" no gráfico utilizando diferentes objetos gráficos, como linhas de tendência, rótulos de texto e formas. Mais adiante, isso servirá como uma base sólida para automatizar a detecção do padrão em nosso EA.
3.1. Obtenção dos dados dos candles
O primeiro passo é obter os dados dos candles para localizar o padrão "Cabeça e Ombros" no gráfico. Esses dados contêm informações detalhadas sobre cada barra, incluindo os preços de abertura, máxima, mínima e fechamento, além do horário correspondente. Esses valores são fundamentais, pois a estrutura do padrão é determinada pela forma como o preço oscila ao longo de vários candles.No entanto, não coletamos essas informações apenas para identificar tendências. Também queremos que o EA funcione como um indicador, já que nosso objetivo é criar um EA capaz de reconhecer esses padrões e executar operações por conta própria. Isso significa que ele também deve conseguir identificar padrões "Cabeça e Ombros" anteriores no gráfico, permitindo que você volte a analisá-los e testá-los.
Em resumo, o EA que estamos desenvolvendo terá duas funções: atuará como um robô de negociação, reconhecendo padrões "Cabeça e Ombros" e executando operações automaticamente, e também funcionará como um indicador de padrões, destacando estruturas semelhantes nos dados históricos. Além da possibilidade de negociação automatizada, essa dupla funcionalidade permite que os traders confirmem visualmente os sinais e analisem como o padrão se formou e se comportou no passado.
Exemplo:input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // MA Time Frame input int bars_check = 1000; // Number of bars to check for swing points // Variable to store how many bars are available on the chart for the selected timeframe int rates_total; double open[]; // Array for opening prices double close[]; // Array for closing prices double low[]; // Array for lowest prices double high[]; // Array for highest prices datetime time[]; // Array for time (timestamps) of each bar //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- // Get the total number of bars available on the selected symbol and timeframe rates_total = Bars(_Symbol, timeframe); // Copy the open prices of the last 'rates_total' bars into the 'open' array CopyOpen(_Symbol, timeframe, 0, rates_total, open); // Copy the close prices of the last 'rates_total' bars into the 'close' array CopyClose(_Symbol, timeframe, 0, rates_total, close); // Copy the low prices of the last 'rates_total' bars into the 'low' array CopyLow(_Symbol, timeframe, 0, rates_total, low); // Copy the high prices of the last 'rates_total' bars into the 'high' array CopyHigh(_Symbol, timeframe, 0, rates_total, high); // Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array CopyTime(_Symbol, timeframe, 0, rates_total, time); }
Explicação:
No início da função, há duas variáveis de entrada definidas pelo usuário. A primeira, timeframe, permite escolher o timeframe exato que será utilizado pelo EA. O valor padrão é PERIOD_CURRENT. Dessa forma, o EA utilizará o mesmo timeframe do gráfico ao qual estiver anexado. Essa flexibilidade permite analisar diferentes timeframes sem alterar o código. O segundo parâmetro, bars_check, informa ao EA quantos candles históricos, ou barras, devem ser considerados na análise do comportamento do preço. No nosso caso, como o valor definido é 1000, o EA procurará possíveis estruturas do padrão nos últimos 1000 candles.
Depois de definir os parâmetros de entrada, o código declara vários arrays e uma variável para armazenar os dados de mercado. A variável rates_total armazenará o número total de barras, ou candles, disponíveis para o símbolo e o timeframe selecionados. Os dados de preço de cada candle são armazenados nos arrays open[], close[], low[] e high[]. Também podemos determinar o horário exato de cada candle por meio das marcas de tempo de cada barra, armazenadas no array time[]. Esses arrays são essenciais porque fornecem ao EA os dados necessários para analisar o gráfico e procurar padrões como "Cabeça e Ombros".
Como este projeto foi desenvolvido como um EA, e não como um indicador personalizado, precisamos obter manualmente os dados dos candles utilizando funções como CopyOpen(), CopyHigh() e outras. Se estivéssemos criando um indicador baseado no método OnCalculate(), essas informações já seriam fornecidas automaticamente por meio dos parâmetros da função, eliminando a necessidade de copiá-las manualmente.
A estrutura do EA também inclui três funções comuns da linguagem MQL5: OnInit(), OnDeinit() e OnTick(). Quando o EA é carregado, o método OnInit() é chamado uma única vez. Neste estágio, ele simplesmente retorna INIT_SUCCEEDED, indicando que o EA foi inicializado com sucesso e está pronto para ser executado. A função OnDeinit(), que por enquanto ainda não contém nenhuma lógica de limpeza, é chamada quando o terminal é fechado ou quando o EA é removido do gráfico.
A função OnTick(), executada sempre que ocorre uma atualização de preço, ou seja, a cada tick, concentra a lógica principal do EA. Dentro dessa função, Bars() determina inicialmente o número de barras atualmente disponíveis no gráfico. Em seguida, os dados de preço e horário são carregados nos arrays correspondentes por meio de cinco funções de cópia: CopyOpen() preenche o array open[] com os preços de abertura, CopyClose() preenche o array close[] com os preços de fechamento, CopyLow() e CopyHigh() armazenam, respectivamente, as mínimas e máximas, enquanto CopyTime() armazena as marcas de tempo de cada candle. Essa preparação é necessária porque reúne todos os dados históricos que o EA utilizará para procurar padrões gráficos e analisar o comportamento do mercado.
3.2. Identificação dos swings
Depois de obtermos com sucesso os dados históricos dos candles, o próximo passo é localizar os principais pontos de swing X, A, B, C e D que compõem a estrutura do padrão "Cabeça e Ombros" ou "Cabeça e Ombros Invertido". O EA precisa identificar esses swings com precisão para reconhecer padrões válidos no gráfico, já que eles representam pontos de reversão no movimento do preço. Para construir o padrão passo a passo, o EA analisa as máximas e mínimas ao longo de um determinado número de barras e identifica os principais pontos de reversão.
Exemplo://+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i]) return false; // If the current high is not the highest, return false. } return true; }
Explicação:
Utilizamos duas funções, IsSwingLow() e IsSwingHigh(), para identificar os pontos de swing do preço. Essas funções determinam se um determinado candle corresponde a uma máxima ou mínima de swing em relação aos candles mais próximos. De acordo com o intervalo de verificação definido, no caso de uma mínima de swing, a função confirma se a mínima do candle atual é inferior às mínimas de um número predeterminado de candles anteriores e posteriores. De forma semelhante, para uma máxima de swing, verifica se a máxima do candle atual é superior às máximas dos candles ao redor. Como esse conceito já foi abordado em detalhes na Parte 14 desta série, não voltaremos a aprofundá-lo aqui.
3.2.1. Identificação de XABCDE
Nesta seção, destacamos a importância de identificar corretamente os principais pontos de swing X, A, B, C, D e E para reconhecer o padrão "Cabeça e Ombros" tanto em operações de compra quanto de venda. Esses pontos representam máximas e mínimas relevantes que definem a estrutura do padrão e orientam as decisões de negociação do EA.
Exemplo:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- // Get the total number of bars available on the selected symbol and timeframe rates_total = Bars(_Symbol, timeframe); // Copy the open prices of the last 'rates_total' bars into the 'open' array CopyOpen(_Symbol, timeframe, 0, rates_total, open); // Copy the close prices of the last 'rates_total' bars into the 'close' array CopyClose(_Symbol, timeframe, 0, rates_total, close); // Copy the low prices of the last 'rates_total' bars into the 'low' array CopyLow(_Symbol, timeframe, 0, rates_total, low); // Copy the high prices of the last 'rates_total' bars into the 'high' array CopyHigh(_Symbol, timeframe, 0, rates_total, high); // Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array CopyTime(_Symbol, timeframe, 0, rates_total, time); //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing low (B). E_time = time[n]; // Time of the swing low (B). E_letter = StringFormat("E%d", n); // Unique name for the text label object. break; } } break; } } break; } } break; } } break; } } } } } } } }Explicação:
//X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). string X_letter; // Unique name for the text label object. //A double A; // Price of the swing high (A). datetime A_time; // Time of the swing high (A). string A_letter; // Unique name for the text label object. //B double B; // Price of the swing low (B). datetime B_time; // Time of the swing low (B). string B_letter; // Unique name for the text label object. //C double C; // Price of the swing low (B). datetime C_time; // Time of the swing low (B). string C_letter; // Unique name for the text label object. //D double D; // Price of the swing low (B). datetime D_time; // Time of the swing low (B). string D_letter; // Unique name for the text label object. //E double E; // Price of the swing low (B). datetime E_time; // Time of the swing low (B). string E_letter; // Unique name for the text label object.
Neste código, utilizamos três tipos diferentes de variáveis para representar cada um dos principais pontos de swing do padrão, identificados como X, A, B, C, D e E. Primeiro, os níveis exatos de preço desses pontos de swing são armazenados em variáveis do tipo double. A variável double A armazena o preço da máxima de swing identificada como A, enquanto a variável double X armazena o preço da mínima de swing identificada como X.Além disso, cada ponto de swing possui uma variável de data e hora para registrar o momento em que ocorreu. Isso permite que o EA posicione corretamente os pontos de swing em ordem cronológica no gráfico. A variável datetime X_time registra o horário da mínima de swing X, enquanto datetime A_time registra o horário da máxima de swing A.
Por fim, é gerado um nome exclusivo para cada ponto de swing por meio de uma variável do tipo string. Esses rótulos, como X_letter ou A_letter, são utilizados para criar objetos de texto no gráfico e indicar visualmente a posição de cada ponto de swing. Esse sistema de identificação permite que o EA organize e exiba melhor os pontos, facilitando ao trader visualizar o padrão à medida que ele se forma.Assim, o EA utiliza três informações para descrever cada ponto de swing: preço, horário e rótulo. Com esses dados, ele consegue identificar e representar corretamente o padrão "Cabeça e Ombros" no gráfico. Essa abordagem combina a detecção do padrão com a exibição de referências visuais intuitivas para o trader.
if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing low (B). E_time = time[n]; // Time of the swing low (B). E_letter = StringFormat("E%d", n); // Unique name for the text label object. ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); break; } } break; } } break; } } break; } } break; } } } } } } }
Resultado:

Explicação:
Para determinar se o programa está configurado para detectar padrões para operações de venda, o código começa verificando se a variável show_sell está definida como true. Quando não é necessário procurar um padrão de venda, essa condição simples evita cálculos desnecessários. Em seguida, o EA verifica se o número total de barras disponíveis no gráfico, rates_total, é maior ou igual ao limite mínimo definido por bars_check. Isso garante que haja dados históricos suficientes para reconhecer o padrão com precisão.
A variável z, normalmente utilizada como tamanho da janela ou intervalo de verificação na detecção dos pontos de swing, percorre uma faixa limitada de valores no laço externo. A ideia é aumentar a flexibilidade e a precisão da detecção dos swings testando diferentes intervalos de verificação, de z = 7 até z = 10. Dessa forma, o algoritmo pode procurar padrões com níveis ligeiramente diferentes de sensibilidade.
O próximo laço for percorre as barras mais recentes do gráfico, desde rates_total - bars_check até rates_total - z. Esse intervalo concentra a busca na região em que os possíveis padrões são mais relevantes para a ação atual do preço, limitando a análise às barras mais recentes. Dentro desse laço, a função IsSwingLow(low, i, z) é utilizada para procurar uma mínima de swing e determinar se a barra na posição i representa uma mínima local com base na janela de verificação z.
Quando uma mínima de swing é encontrada na posição i, o código utiliza StringFormat("X%d", i) para gerar um rótulo exclusivo chamado X_letter, armazena o preço correspondente na variável X e a marca de tempo em X_time. Esses rótulos permitem que o EA destaque visualmente esse ponto no gráfico e atribua a ele um identificador exclusivo para o processamento posterior. O padrão "Cabeça e Ombros" começa a ser construído a partir desse ponto X, que funciona como referência inicial.
Em seguida, para identificar a próxima máxima de swing A, que ocorre depois do ponto X, o código inicia outro laço aninhado a partir de i. Para preservar a sequência cronológica exigida pelo padrão, ele utiliza IsSwingHigh(high, j, z) para determinar se a barra de índice j corresponde a uma máxima de swing e verifica se seu horário time[j] é estritamente posterior a X_time. Quando uma máxima de swing válida é encontrada, o preço, o horário e o rótulo do ponto A são registrados da mesma forma que no ponto X.
Os pontos seguintes, B, C, D e E, são identificados sequencialmente por essa estrutura de laços aninhados. Ao percorrer as barras, o EA localiza cada ponto de swing e verifica se ele corresponde a uma mínima ou máxima de swing válida e se sua marca de tempo é estritamente posterior à do ponto anterior. Para cada swing confirmado, o código cria um rótulo exclusivo e armazena seu preço e duração. Essa verificação sequencial rigorosa garante que os pontos do padrão sejam mantidos na ordem correta.
O laço mais interno utiliza uma janela de verificação um pouco menor, z/2, para localizar o ponto E, a última máxima de swing do padrão. Essa diferença permite ajustar a sensibilidade da detecção para o ponto final. Quando E é encontrado, o código sai imediatamente do laço para evitar buscas adicionais desnecessárias, tornando a execução mais eficiente. Em seguida, são atribuídos o preço, o horário e o rótulo correspondentes ao ponto E.
Com esses laços aninhados e as verificações de tempo, o algoritmo consegue identificar corretamente o padrão "Cabeça e Ombros" ao preservar a sequência dos pontos de swing. O uso de rótulos exclusivos, como "X%d" ou "A%d", permite que o EA crie e gerencie elementos de texto ou objetos gráficos no gráfico, oferecendo ao trader uma representação visual do padrão. Essa abordagem sistemática e estruturada permite que o algoritmo identifique de forma consistente padrões complexos da ação do preço para utilizá-los nas decisões de negociação.
O objetivo do exemplo de código acima foi identificar os seis principais pontos de swing, X, A, B, C, D e E, necessários para detectar o padrão "Cabeça e Ombros". No entanto, a implementação atual ainda não aplica as condições lógicas específicas necessárias para validar essa estrutura. Por enquanto, ela apenas identifica os pontos de swing com base nas oscilações do preço e em sua ordem cronológica. O algoritmo registra os preços, os horários e os rótulos desses pontos, mas ainda não verifica se eles realmente formam um padrão bem definido.
Para validar o padrão "Cabeça e Ombros", utilizaremos alguns critérios importantes baseados na estrutura do preço. O ponto A deve estar acima de X, enquanto B deve ficar entre os dois, acima de X, mas abaixo de A. O ponto C, que representa a cabeça, deve estar acima de B. O pico do ombro direito é formado pelo ponto E, aproximadamente alinhado ao ponto A, enquanto o vale do ombro direito é representado pelo ponto D, localizado próximo ao nível de B. Além disso, antes que qualquer sinal de negociação seja gerado, a estrutura de cada padrão deverá ser validada observando a sequência específica de máximas e mínimas entre esses pontos.
Exemplo:
input color txt_clr = clrBlue; // Texts color //X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). string X_letter; // Unique name for the text label object. int x_a_bars; int x_lowest_index; double x_a_ll; datetime x_a_ll_t; //A double A; // Price of the swing high (A). datetime A_time; // Time of the swing high (A). string A_letter; // Unique name for the text label object. int a_b_bars; int a_highest_index; double a_b_hh; datetime a_b_hh_t; string A_zone; double A_low; //B double B; // Price of the swing low (B). datetime B_time; // Time of the swing low (B). string B_letter; // Unique name for the text label object. int b_c_bars; int b_lowest_index; double b_c_ll; datetime b_c_ll_t; string B_zone; double B_high; //C double C; // Price of the swing low (B). datetime C_time; // Time of the swing low (B). string C_letter; // Unique name for the text label object. int c_d_bars; int c_highest_index; double c_d_hh; datetime c_d_hh_t; //D double D; // Price of the swing low (B). datetime D_time; // Time of the swing low (B). string D_letter; // Unique name for the text label object. int d_e_bars; int d_lowest_index; double d_e_ll; datetime d_e_ll_t; double D_3bar_high; //E double E; // Price of the swing low (B). datetime E_time; // Time of the swing low (B). string E_letter; // Unique name for the text label object. double E_3bar_low; string xa; // Unique name for the trendline for XA. string ab; // Unique name for the trendline for AB. string bc; // Unique name for the trendline for BC. string cd; // Unique name for the trendline for CD. string de; // Unique name for the trendline for DE. string ex; // Unique name for the trendline for EX. //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing high (E). E_time = time[n]; // Time of the swing high (E). E_letter = StringFormat("E%d", n); // Unique name for the text label object. d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point) d_e_ll_t = time[d_lowest_index]; // Store the corresponding time D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range c_d_hh = high[c_highest_index]; // Store the lowest high (C - D lowest point) c_d_hh_t = time[c_highest_index]; // Store the corresponding time b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point) b_c_ll_t = time[b_lowest_index]; // Store the corresponding time B_high = high[b_lowest_index]; // The high price of the bar that formed swing low D a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point) a_b_hh_t = time[a_highest_index]; // Store the corresponding time A_low = low[a_highest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point) x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); } break; } } break; } } break; } } break; } } break; } } } } } } }
Resultado:

Explicação:
O código contém declarações de variáveis utilizadas para armazenar informações importantes sobre a estrutura e os componentes do padrão XABCDE definido pelo usuário na ação do preço. Cada variável desempenha uma função específica na identificação e visualização das máximas e mínimas de swing que compõem o padrão, bem como no posicionamento dos rótulos e no desenho da estrutura no gráfico.O primeiro grupo de variáveis corresponde ao segmento entre os pontos X e A. A variável x_a_bars armazena o número de barras entre esses dois pontos. A variável x_lowest_index armazena o índice da barra que apresenta o menor preço nesse segmento, enquanto x_a_ll armazena o menor preço propriamente dito, ou "mínima mais baixa", e x_a_ll_t registra o horário em que essa mínima mais baixa ocorreu.
O trecho entre A e B é tratado pelo grupo seguinte de variáveis. O número de barras desse segmento é armazenado em a_b_bars. O índice da barra que apresenta a maior máxima é armazenado em a_highest_index, enquanto a máxima propriamente dita, ou "máxima mais alta", é armazenada em a_b_hh, e a variável a_b_hh_t registra o horário em que ela ocorreu. Isso ajuda a confirmar a estrutura em que B representa uma mínima mais alta e A, uma máxima de swing.Em seguida, as variáveis b_c_bars, b_lowest_index, b_c_ll e b_c_ll_t armazenam, respectivamente, o número de barras, o índice da mínima, o preço mínimo e o horário correspondente no segmento entre B e C. Esses dados são utilizados para confirmar a próxima mínima mais baixa da estrutura.
As variáveis do tipo string A_zone e B_zone provavelmente serão utilizadas para armazenar nomes exclusivos dos objetos usados na criação de zonas retangulares próximas aos pontos A e B. Os níveis de preço A_low e B_high definem os limites inferior e superior dessas zonas, que funcionam como referências visuais ou áreas de decisão no gráfico. Da mesma forma que nos segmentos anteriores, d_e_bars, d_lowest_index, d_e_ll e d_e_ll_t armazenam o número de barras, o índice, o preço e o horário referentes ao trecho entre D e E, ajudando a identificar a parte final do padrão.
As variáveis D_3bar_high e E_3bar_low são utilizadas para armazenar, respectivamente, uma máxima mais alta próxima ao ponto D e uma mínima mais baixa próxima ao ponto E, geralmente calculadas com base em estruturas de três barras. Isso ajuda a validar os pontos de swing e a reduzir a ocorrência de falsos sinais.Por fim, as variáveis xa, ab, bc, cd, de e ex representam os identificadores string das linhas de tendência que serão criadas entre os pontos de reversão do padrão, de X até A, de A até B e assim por diante. Esses identificadores garantem que cada objeto de linha de tendência tenha um nome exclusivo, permitindo que toda a estrutura do padrão seja exibida no gráfico de forma precisa e organizada.
Ao analisar o movimento do preço entre os seis pontos principais, identificados pelas letras X, A, B, C, D e E, pressupõe-se que esses pontos correspondam a máximas e mínimas de swing que estabelecem relações estruturais específicas entre si. O primeiro passo do script é localizar a mínima mais baixa, d_e_ll, entre os pontos D e E. Em seguida, ele obtém o horário correspondente e uma máxima de referência, D_3bar_high, localizada três barras antes dessa mínima. De forma semelhante, identifica a máxima mais alta, c_d_hh, entre o ponto C e d_e_ll. Depois disso, reúne todas as máximas e mínimas de swing relevantes com suas respectivas marcas de tempo e repete o procedimento no sentido inverso até chegar ao ponto X. Dessa forma, o código utiliza a análise das barras para reconstruir toda a estrutura de swings X-A-B-C-D-E.
A função Bars() é utilizada para contar o número de candles entre duas marcas de tempo. Já as funções ArrayMaximum() e ArrayMinimum() permitem examinar intervalos específicos entre os pontos de swing, de modo que o código possa analisar as máximas e mínimas existentes dentro de cada trecho. Para localizar a maior máxima ou a menor mínima, essas funções percorrem um determinado número de barras a partir de um deslocamento específico, como i, j, k e assim por diante. Isso permite identificar os pontos de swing relevantes. Por exemplo, ArrayMaximum(high, l, c_d_bars) localiza a maior máxima entre C e D, ajudando a determinar o ponto C. O script aplica o mesmo procedimento sucessivamente aos demais segmentos da estrutura até chegar ao ponto X.
A relação estrutural entre os pontos é validada por meio de diversas condições. Esses critérios comparam as máximas e mínimas dos diferentes segmentos, verificando, por exemplo, se o ponto A está acima de X, se B está abaixo de A, mas ainda acima de X, se C está acima de A e assim por diante. Dessa forma, antes de desenhar a estrutura no gráfico, o código confirma se a ação do preço realmente corresponde ao padrão esperado. Condições como E > A_low e E_3bar_low < a_b_hh são exemplos dessas comparações e garantem que o trecho até o ponto E esteja corretamente posicionado em relação ao restante da estrutura.
Assim que o padrão atende a todos os critérios, o método utiliza ObjectCreate() para marcar graficamente os pontos identificados no gráfico por meio de rótulos de texto, como "X", "A", "B" e assim por diante, além de linhas de tendência que conectam esses pontos. As linhas utilizam objetos OBJ_TREND para desenhar os segmentos XA, AB, BC, CD, DE e EX, enquanto os pontos são identificados por objetos OBJ_TEXT. A legibilidade é aprimorada com o uso de cores, espessuras de linha e do posicionamento dos objetos em segundo plano por meio de OBJPROP_BACK.
Por fim, o código destaca duas zonas importantes de preço, a zona A e a zona B, utilizando retângulos. A zona A se estende da máxima do ponto A até a mínima do ponto E, enquanto a zona B abrange a região entre a mínima do ponto B e a máxima do ponto D. Na tomada de decisões de negociação, como a definição do ponto de entrada, do Stop Loss ou dos alvos, esses retângulos provavelmente serão utilizados como referências visuais para identificar reações do preço ou confluências dentro de determinadas áreas. Essa representação facilita a interpretação dos padrões complexos que o programa detecta automaticamente.
3.2.2. Destaque da estrutura do padrão com formas triangulares
Depois de localizar e marcar todos os principais pontos de swing, identificados como X, A, B, C, D e E, passamos a desenhar formas triangulares para destacar a estrutura do padrão, especialmente em formações como "Cabeça e Ombros". Em vez de acompanhar todos os segmentos do movimento do preço, esses triângulos destacam os picos e vales que formam a "cabeça" e os "ombros" do padrão. O triângulo XAB, que conecta os pontos X, A e B, representa visualmente a primeira parte da formação sem prolongar a linha abaixo da linha de pescoço.
A etapa seguinte consiste em criar o triângulo BCD, que conecta os pontos B, C e D e destaca a segunda parte da estrutura. Por fim, o triângulo DEX conecta os pontos D, E e X, completando visualmente a formação. Essas formas triangulares funcionam como recursos visuais que permitem ao trader identificar mais rapidamente os principais pontos de reversão e a geometria do padrão sem sobrecarregar o gráfico com muitas linhas.
Exemplo:
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // MA Time Frame input int bars_check = 1000; // Number of bars to check for swing points input bool show_sell = true; // Display sell signals input bool show_buy = true; // Display buy signals input color txt_clr = clrBlue; // Texts color input color head_clr = clrCornflowerBlue; // Head color input color shoulder_clr = clrLightSeaGreen; // Shoulder color
if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0); ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0); X_A_B = StringFormat("XAB %d", i); ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr); B_C_D = StringFormat("BCD %d", i); ObjectCreate(chart_id, B_C_D, OBJ_TRIANGLE, 0, b_c_ll_t, b_c_ll, c_d_hh_t, c_d_hh, d_e_ll_t, d_e_ll); ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr); ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true); ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true); D_E_X = StringFormat("DEX %d", i); ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll); ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr); ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true); ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true); }
Resultado:

Explicação:
Esta parte do código se concentra no uso de triângulos e retângulos para representar no gráfico do MetaTrader 5 as estruturas XAB, BCD e DEX. Três variáveis do tipo string, X_A_B, B_C_D e D_E_X, são declaradas para armazenar nomes dinâmicos dos objetos triangulares. Além disso, duas variáveis do tipo datetime, xa_line_t e ex_line_t, são utilizadas para obter coordenadas de tempo específicas e posicionar corretamente partes dos triângulos no gráfico.
Para facilitar a interpretação do trader, o código começa criando duas áreas retangulares chamadas "A ZONE" e "B ZONE". Esses retângulos são criados com ObjectCreate() e destacam regiões importantes da estrutura do preço. A zona A se estende até o ponto E, abrangendo a região entre a máxima localizada no trecho A-B, a_b_hh, e a mínima associada ao ponto A, A_low. De forma semelhante, a zona B abrange a região entre a mínima b_c_ll, localizada entre os pontos B e C, e a máxima B_high, estendendo-se no eixo do tempo até a coordenada correspondente ao trecho entre D e E.
Na sequência, a função ObjectGetTimeByValue() é chamada para determinar as coordenadas de tempo necessárias ao posicionamento dos triângulos XAB e DEX. Essa função localiza, ao longo de um objeto gráfico, o ponto correspondente a um determinado nível de preço e retorna o horário associado. Dessa forma, os marcadores visuais permanecem alinhados aos níveis de preço que devem destacar.
Em seguida, cada triângulo é desenhado como uma parte independente do padrão. Embora não represente integralmente todo o segmento de preço, o triângulo XAB destaca a primeira estrutura de swing. Para evitar excesso de elementos visuais e manter o foco na formação, ele chama a atenção apenas para os principais pontos de reversão associados ao primeiro ombro. De maneira semelhante, o triângulo BCD conecta a mínima B, o pico C e a correção D, destacando a cabeça do padrão. Por fim, o triângulo DEX completa a estrutura ao representar o swing de reversão que reflete o ombro inicial.
Propriedades como OBJPROP_FILL e OBJPROP_BACK são utilizadas para preencher os três triângulos com cor e posicioná-los atrás dos demais elementos do gráfico. As cores, armazenadas em variáveis como shoulder_clr e head_clr, facilitam a identificação visual do padrão. Para permitir o processamento e a identificação de várias ocorrências desse tipo de formação no mesmo gráfico, o índice i é incluído no nome de cada objeto.
3.2.3. Indicação do ponto de entrada, Stop Loss e Take Profit
Depois que as estruturas XAB, BCD e DEX são destacadas por triângulos que evidenciam o formato de "Cabeça e Ombros", a etapa seguinte consiste em definir os parâmetros da operação: ponto de entrada, Stop Loss (SL) e Take Profit (TP). Esses elementos são necessários para transformar o padrão identificado em uma estratégia de negociação completa.A entrada ocorre quando um candle fecha abaixo do ponto D. Isso indica que o preço pode ter reagido à linha de pescoço e começado a se mover na direção prevista pelo padrão. A partir desse momento, o sinal é considerado válido e a operação pode ser executada.
O Stop Loss é posicionado logo acima do ponto E. Como E representa a máxima de swing mais recente antes da conclusão do padrão, colocar o SL nesse nível ajuda a proteger a operação caso a formação falhe e o preço reverta inesperadamente. Além disso, essa posição mantém a lógica do setup, pois a operação só é invalidada quando a própria estrutura do padrão é rompida. Inicialmente, o Take Profit é definido no ponto X, que corresponde ao início do padrão e representa um nível de referência relevante em que o preço já havia revertido anteriormente. No entanto, se a distância entre o ponto de entrada e X não proporcionar uma relação risco/retorno (RRR) de pelo menos 1:1, o nível do TP é ajustado para alcançar uma relação risco/retorno mínima de 1:2. Garantir uma relação favorável entre o retorno potencial e o risco assumido é um dos elementos fundamentais de qualquer estratégia de negociação sustentável.
Exemplo:int n_bars; int n_bars_2; string sl_t; string tp_t; double sl_price; double tp_price;
for(int o = n; o < rates_total - 1; o++) { if(close[o] < d_e_ll && time[o] >= time[n+(z/2)]) { n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time); n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]); if(n_bars_2 <= n_bars) { double sl_zone = MathAbs(E - close[o]); double tp_zone = MathAbs(close[o] - x_a_ll); bool no_cross = false; for(int p = n + (z/2); p < o; p++) { if(close[p] < d_e_ll) { no_cross = true; break; } } if(no_cross == false) { if(tp_zone >= sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = x_a_ll; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price); ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(tp_zone < sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); double n_tp = MathAbs(close[o] - (sl_zone * 2)); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = n_tp; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);; ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } } } break; } }
Resultado:

Explicação:
A lógica desta parte do código se concentra em identificar um setup de negociação válido com base na estrutura "Cabeça e Ombros" destacada anteriormente. Em seguida, objetos gráficos são utilizados para marcar visualmente no gráfico o ponto de entrada, o Stop Loss (SL) e o Take Profit (TP). Duas variáveis inteiras, n_bars e n_bars_2, são utilizadas para calcular e comparar o número de candles entre pontos relevantes. Isso ajuda a determinar se a ação atual do preço ainda se encontra dentro da janela aceitável para o setup. Enquanto as variáveis sl_price e tp_price armazenam os respectivos níveis de preço, as strings sl_t e tp_t são utilizadas para gerar dinamicamente os nomes dos objetos de texto correspondentes ao SL e ao TP.
A partir do ponto de conclusão do padrão, n, a lógica principal é executada em um laço que percorre as barras. A condição (close[o] < d_e_ll && time[o] >= time[n+(z/2)]) verifica se existe um candle que fechou abaixo do nível da linha de pescoço definido pelo ponto D, indicando uma possível entrada. Se essa condição for atendida, o código calcula o número de barras entre os pontos X e E (n_bars) e entre o ponto intermediário do padrão e o candle atual (n_bars_2). Dessa forma, garante-se que o sinal ainda seja válido dentro de um intervalo razoável.
Em seguida, o código compara o preço de entrada com os pontos E e X para determinar, respectivamente, as possíveis distâncias até o Stop Loss (sl_zone) e o Take Profit (tp_zone). Para confirmar que essa entrada corresponde ao primeiro rompimento, é executado outro laço para verificar se nenhum candle anterior ao atual fechou abaixo do ponto D. Se essa verificação for concluída com sucesso, o script prossegue.
Para representar visualmente as zonas de Stop Loss e Take Profit no gráfico, o código utiliza OBJ_RECTANGLE para criar retângulos quando o retorno potencial (TP) é maior ou igual ao risco (SL). Além disso, uma seta de venda (OBJ_ARROW_SELL) é posicionada próxima ao ponto de entrada. As variáveis sl_price e tp_price armazenam os níveis de preço efetivos do SL e do TP. Também são criados rótulos com OBJ_TEXT para exibir claramente esses níveis no gráfico, utilizando as cores e fontes definidas.Se a relação risco/retorno for inferior a 1:1, ou seja, se a distância até o TP for menor que a distância até o SL, o script ajusta os níveis para obter uma relação risco/retorno mais favorável, de pelo menos 1:2, calculando uma nova zona de TP equivalente a três vezes a distância até o SL. Em seguida, objetos gráficos equivalentes são criados e configurados para essa alternativa.
4. Execução de operações com base no padrão
Nesta etapa, nosso objetivo vai além de simplesmente identificar e exibir possíveis situações de negociação. Enquanto a seção anterior se concentrou em marcar no gráfico os níveis de entrada, Stop Loss (SL) e Take Profit (TP), agora passaremos à execução efetiva da operação com base no padrão identificado. O EA deverá abrir automaticamente uma operação quando todas as condições forem atendidas, por exemplo, quando o preço fechar abaixo do ponto D em um setup de venda. O Take Profit deverá ser definido no nível do ponto X ou ajustado para alcançar uma relação risco/retorno de pelo menos 1:2, enquanto o Stop Loss deverá ser posicionado no nível do ponto E. Com essa etapa, o EA deixa de ser apenas uma ferramenta visual e passa a funcionar como um sistema totalmente automatizado, capaz de abrir e gerenciar operações sem intervenção manual.
Exemplo:
#include <Trade/Trade.mqh> CTrade trade; int MagicNumber = 5122025; datetime lastTradeBarTime = 0; double ask_price; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- trade.SetExpertMagicNumber(MagicNumber); //--- return(INIT_SUCCEEDED); }
ask_price = ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); datetime currentBarTime = iTime(_Symbol, timeframe, 0); //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing high (E). E_time = time[n]; // Time of the swing high (E). E_letter = StringFormat("E%d", n); // Unique name for the text label object. d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point) d_e_ll_t = time[d_lowest_index]; // Store the corresponding time D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range c_d_hh = high[c_highest_index]; // Store the lowest high (C - D lowest point) c_d_hh_t = time[c_highest_index]; // Store the corresponding time b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point) b_c_ll_t = time[b_lowest_index]; // Store the corresponding time B_high = high[b_lowest_index]; // The high price of the bar that formed swing low D a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point) a_b_hh_t = time[a_highest_index]; // Store the corresponding time A_low = low[a_highest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point) x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0); ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0); X_A_B = StringFormat("XAB %d", i); ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr); B_C_D = StringFormat("BCD %d", i); ObjectCreate(chart_id, B_C_D, OBJ_TRIANGLE, 0, b_c_ll_t, b_c_ll, c_d_hh_t, c_d_hh, d_e_ll_t, d_e_ll); ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr); ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true); ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true); D_E_X = StringFormat("DEX %d", i); ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll); ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr); ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true); ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true); for(int o = n; o < rates_total - 1; o++) { if(close[o] < d_e_ll && time[o] >= time[n+(z/2)]) { n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time); n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]); if(n_bars_2 <= n_bars) { double sl_zone = MathAbs(E - close[o]); double tp_zone = MathAbs(close[o] - x_a_ll); bool no_cross = false; for(int p = n + (z/2); p < o; p++) { if(close[p] < d_e_ll) { no_cross = true; break; } } if(no_cross == false) { if(tp_zone >= sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = x_a_ll; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price); ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(tp_zone < sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); double n_tp = MathAbs(close[o] - (sl_zone * 3)); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = n_tp; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);; ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(time[o] == time[rates_total-2] && currentBarTime != lastTradeBarTime) { trade.Sell(lot_size,_Symbol,ask_price, sl_price,tp_price); lastTradeBarTime = currentBarTime; } } } break; } } } break; } } break; } } break; } } break; } } break; } } } } } } }
Resultado:

Explicação:
Este bloco de código gerencia a execução das operações por meio da classe CTrade, incorporada ao MQL5, que simplifica tarefas de negociação como abertura, modificação e fechamento de ordens. A biblioteca de negociação que fornece acesso à classe CTrade é incluída no início do código com a diretiva #include <Trade/Trade.mqh>. Em seguida, é criada uma instância dessa classe com a linha CTrade trade;, enquanto a variável int MagicNumber = 5122025; é utilizada para atribuir um identificador exclusivo às operações executadas por este EA. O MagicNumber é importante porque permite distinguir as operações abertas pelo próprio EA daquelas realizadas manualmente ou por outros EAs. O objeto trade utiliza SetExpertMagicNumber; para definir esse MagicNumber.
A chamada SymbolInfoDouble(_Symbol, SYMBOL_ASK); é utilizada para inicializar a variável ask_price com o preço Ask atual do símbolo. A ordem de venda será executada a esse preço. Em seguida, o código utiliza a verificação time[o] == time[rates_total-2] para determinar se o candle atual é o mais recente. Além disso, a condição currentBarTime != lastTradeBarTime confirma que ainda não foi executada nenhuma operação na barra atual. Com essa verificação, o EA não pode enviar mais de uma ordem na mesma barra.
Se ambas as condições forem atendidas, o EA envia a ordem de venda por meio da função Sell(). Ask_price representa o preço de entrada, sl_price corresponde ao nível do Stop Loss, tp_price ao nível do Take Profit, lot_size define o volume da operação em lotes e _Symbol representa o símbolo de negociação atual, como EURUSD. Após a abertura bem-sucedida da operação, o EA atribui o valor de currentBarTime à variável lastTradeBarTime. Essa atualização é fundamental, pois garante que apenas uma operação seja executada para cada padrão válido, impedindo que o EA abra várias operações com base no mesmo candle.
É importante lembrar que a mesma lógica utilizada para identificar e exibir um setup de venda também pode ser aplicada a um setup de compra, bastando inverter as condições em cada etapa. Na estratégia de compra, o EA aguardará o fechamento de um candle acima da linha de pescoço, no ponto D, em vez de abaixo dela. Em seguida, o Take Profit será definido na máxima X e o Stop Loss na mínima E. As regras referentes à relação risco/retorno, às referências visuais e às demais condições permanecem as mesmas, apenas invertidas para uma estrutura de alta. O código-fonte que acompanha a publicação implementa integralmente essa lógica para quem quiser estudá-la em mais detalhes ou modificá-la.
Conclusão
Neste artigo, vimos como criar um EA em MQL5 capaz de identificar padrões de análise técnica, mais especificamente o padrão "Cabeça e Ombros", e executar operações com base neles. Começamos identificando e marcando os principais pontos de swing e, em seguida, utilizamos recursos gráficos como triângulos e retângulos para representar visualmente as estruturas XAB, BCD e DEX. Depois disso, definimos a lógica responsável por determinar os parâmetros específicos da operação, incluindo o ponto de entrada, o Stop Loss e o Take Profit, garantindo também que a relação risco/retorno permanecesse dentro de critérios adequados. Por fim, implementamos a lógica de execução das operações, responsável por abrir ordens reais com base nos sinais confirmados, transformando o EA em um sistema totalmente automatizado.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/18147
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
Desenvolvimento de ferramentas para análise de Price Action (Parte 24): Ferramenta de análise quantitativa de Price Action
Está chegando o novo MetaTrader 5 e MQL5
Recursos do Assistente MQL5 que você precisa conhecer (Parte 66): Uso de padrões FrAMA e Force Index com kernel de produto escalar
- 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
Obrigado, é por isso que sempre fico ansioso pelos seus artigos. Muito esclarecedor!
Você é incrível, Israel! Eu traduzo regularmente seus artigos para o espanhol de forma oficial, e é sempre um verdadeiro prazer trabalhar neles.
Continue com esse trabalho fantástico! ❤️
Você é incrível, Israel! Eu traduzo regularmente seus artigos para o espanhol de forma oficial, e é sempre um verdadeiro prazer trabalhar neles.
Continue com esse trabalho fantástico! ❤️
Olá, Miguel.
Obrigado pelas suas palavras gentis, isso significa muito para mim ❤️
Não sou programador, então demorei um pouco para entender isso, mas a forma como foi explicado e o passo a passo me motivaram bastante. 💯
Olá, Daniels.
Que bom ouvir isso de você! ❤️