Automação de estratégias de trading em MQL5 (Parte 27): Identificação e Visualização do Padrão Harmônico "Crab" com Base em Price Action
Introdução
No artigo anterior (Parte 26), desenvolvemos um sistema de ajuste do preço médio baseado em pin bars na linguagem MetaQuotes Language 5 (MQL5). Esse sistema utiliza padrões de candles do tipo pin bar para abrir operações e gerenciar múltiplas posições por meio de uma estratégia de preço médio, além de contar com um dashboard dinâmico para monitoramento em tempo real. Na Parte 27, desenvolveremos um sistema capaz de identificar padrões harmônicos "Crab" de alta e de baixa com base em pontos de reversão e níveis de Fibonacci, automatizando as operações com níveis precisos de entrada, stop-loss e take-profit. O sistema também contará com recursos de visualização, como triângulos e linhas de tendência, para representar claramente os padrões no gráfico. Neste artigo, abordaremos os seguintes tópicos:
Ao final, você terá uma estratégia de trading baseada em padrões harmônicos, cuidadosamente desenvolvida em MQL5 e pronta para ser ajustada às suas necessidades. Vamos à implementação!
Estudo da estrutura do padrão harmônico "Crab"
O padrão "Crab" é uma formação harmônica utilizada no trading, definida por cinco pontos principais de reversão do preço, identificados como X, A, B, C e D. Ele apresenta duas variantes: uma de alta e outra de baixa. No padrão "Crab" de alta, a estrutura segue a sequência "fundo-topo-fundo-topo-fundo". O ponto X corresponde a um swing low, o ponto A a um swing high, o ponto B a um swing low (retração de 0,618 do movimento XA), o ponto C a um swing high (extensão entre 0,382 e 0,886 do movimento AB) e o ponto D a um swing low (extensão de 1,618 do movimento XA, situado abaixo de X). Já o padrão "Crab" de baixa apresenta a sequência inversa, "topo-fundo-topo-fundo-topo". Nesse caso, o ponto X corresponde a um swing high, o ponto A a um swing low, o ponto B a um swing high, o ponto C a um swing low e o ponto D a um swing high (extensão de 1,618 do movimento XA, situado acima de X). A seguir, apresentamos as representações visuais das duas variantes.
Padrão harmônico "Crab" de alta:

Padrão harmônico "Crab" de baixa:

Para identificar esses padrões, adotaremos a seguinte abordagem estruturada:
- Definição do segmento XA: O movimento impulsivo inicial do ponto X até o ponto A estabelece a base do padrão, definindo sua direção (para baixo no caso da tendência de alta e para cima no caso da tendência de baixa) e servindo como referência para os cálculos de Fibonacci.
- Formação do segmento AB: O movimento até o ponto B deve representar uma retração de aproximadamente 0,618 do comprimento de XA, confirmando a correção sem uma reversão excessivamente acentuada em relação ao movimento inicial.
- Análise do segmento BC: O comprimento do segmento BC deve ficar entre 0,382 e 0,886 vezes o comprimento de AB, formando um movimento acentuado em sentido contrário que prepara a extensão final.
- Definição do segmento CD: O segmento final deve atingir uma extensão de 1,618 do comprimento de XA, delimitando a zona potencial de reversão no ponto D, onde o padrão se completa e gera um sinal de negociação.
Com base nesses critérios geométricos e nas proporções de Fibonacci, nosso sistema de trading identificará de forma sistemática padrões "Crab" válidos nos dados de preços. Após a identificação, o sistema representará a formação no gráfico por meio de triângulos, linhas de tendência, rótulos para os pontos X, A, B, C e D, além de linhas tracejadas para indicar os níveis de entrada e take-profit. Essa configuração permitirá automatizar a abertura de operações no ponto D, com stop-loss calculado e múltiplos níveis de take-profit, aproveitando a alta probabilidade de reversão associada ao padrão para entrar no mercado em condições favoráveis. Vamos à implementação!
Implementação em MQL5
Para criar o programa em MQL5, abra o MetaEditor, acesse o Navegador, localize a pasta "Indicadores" (Indicators), selecione "Novo" (New) e siga as instruções para criar o arquivo. Após essa etapa, precisaremos declarar algumas variáveis globais no ambiente de desenvolvimento, que serão utilizadas ao longo do programa.
//+------------------------------------------------------------------+ //| Crab Pattern EA.mq5. | //| Copyright 2025, Forex Algo-Trader, Allan. | //| "https://t.me/Forex_Algo_Trader" | //+------------------------------------------------------------------+ #property copyright "Forex Algo-Trader, Allan" #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #property description "This EA trades based on Crab Strategy" #property strict #include <Trade\Trade.mqh> //--- Include Trade library for order management CTrade obj_Trade; //--- Instantiate trade object for executing orders //--- Input parameters for user configuration input int PivotLeft = 5; // Number of bars to the left for pivot identification input int PivotRight = 5; // Number of bars to the right for pivot identification input double Tolerance = 0.10; // Allowed deviation for Fibonacci levels (10% of XA move) input double LotSize = 0.01; // Lot size for opening new trade positions input bool AllowTrading = true; // Enable or disable automated trading functionality //--------------------------------------------------------------------------- //--- Crab pattern definition: //--- Bullish Crab: //--- Pivots (X-A-B-C-D): X swing low, A swing high, B swing low, C swing high, D swing low. //--- Normally XA > 0; Ideal B = A - 0.5*(A-X); Legs within specified ranges. //--- Bearish Crab: //--- Pivots (X-A-B-C-D): X swing high, A swing low, B swing high, C swing low, D swing high. //--- Normally XA > 0; Ideal B = A + 0.5*(X-A); Legs within specified ranges. //--------------------------------------------------------------------------- struct Pivot { //--- Define structure for pivot points datetime time; //--- Store time of pivot bar double price; //--- Store price (high for swing high, low for swing low) bool isHigh; //--- Indicate true for swing high, false for swing low }; Pivot pivots[]; //--- Declare array to store pivot points int g_patternFormationBar = -1; //--- Store bar index of pattern formation (-1 if none) datetime g_lockedPatternX = 0; //--- Store X pivot time for locked pattern
Começaremos a implementação do padrão "Crab" incluindo a biblioteca "<Trade\Trade.mqh>" e criando uma instância da classe CTrade chamada "obj_Trade", para facilitar o gerenciamento de ordens, como o envio de solicitações de compra e venda. Em seguida, definiremos cinco parâmetros de entrada que permitirão personalizar o sistema: "PivotLeft" e "PivotRight", ambos com valor 5, estabelecem a quantidade de barras consideradas na identificação dos pivôs de máxima e mínima; "Tolerance", com valor 0,10, define a tolerância para desvios em relação aos níveis de Fibonacci; "LotSize", com valor 0,01, determina o volume das operações; e "AllowTrading", definido como true, habilita a abertura automática de operações.
Na sequência, definiremos a estrutura "Pivot", com os campos "time" (datetime), "price" (double) e "isHigh" (bool), para armazenar os pontos de reversão. Também declararemos "pivots" como um array de elementos do tipo "Pivot" e inicializaremos duas variáveis globais: "g_patternFormationBar", com valor -1, para acompanhar as barras em que os padrões se formam, e "g_lockedPatternX", com valor 0, para armazenar a marca de tempo do pivô X utilizada na confirmação. Com isso, teremos a base necessária para identificar os padrões. Para a visualização, poderemos utilizar funções que desenham linhas, rótulos e triângulos.
//+------------------------------------------------------------------+ //| Draw filled triangle on chart | //+------------------------------------------------------------------+ void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3, color cl, int width, bool fill, bool back) { if (ObjectCreate(0, name, OBJ_TRIANGLE, 0, t1, p1, t2, p2, t3, p3)) { //--- Create triangle with three points ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set triangle color ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); //--- Set solid line style ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width ObjectSetInteger(0, name, OBJPROP_FILL, fill); //--- Enable or disable fill ObjectSetInteger(0, name, OBJPROP_BACK, back); //--- Set background or foreground } } //+------------------------------------------------------------------+ //| Draw trend line on chart | //+------------------------------------------------------------------+ void DrawTrendLine(string name, datetime t1, double p1, datetime t2, double p2, color cl, int width, int style) { if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) { //--- Create trend line between two points ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set line color ObjectSetInteger(0, name, OBJPROP_STYLE, style); //--- Set line style (solid, dotted, etc.) ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width } } //+------------------------------------------------------------------+ //| Draw dotted horizontal line on chart | //+------------------------------------------------------------------+ void DrawDottedLine(string name, datetime t1, double p, datetime t2, color lineColor) { if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p)) { //--- Create horizontal dotted line ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); //--- Set line color ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); //--- Set dotted style ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); //--- Set line width to 1 } } //+------------------------------------------------------------------+ //| Draw anchored text label for pivots | //+------------------------------------------------------------------+ void DrawTextEx(string name, string text, datetime t, double p, color cl, int fontsize, bool isHigh) { if (ObjectCreate(0, name, OBJ_TEXT, 0, t, p)) { //--- Create text label at specified coordinates ObjectSetString(0, name, OBJPROP_TEXT, text); //--- Set label text content ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set text color ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize); //--- Set font size ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold"); //--- Set font to Arial Bold if (isHigh) { //--- Check if pivot is swing high ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_BOTTOM); //--- Anchor label above pivot } else { //--- Handle swing low ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_TOP); //--- Anchor label below pivot } ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text } }
Nesta etapa, implementaremos as funções de visualização do programa, responsáveis por desenhar os objetos gráficos que representam o padrão harmônico "Crab" e seus níveis operacionais. Primeiro, criaremos a função "DrawTriangle", que utiliza ObjectCreate para desenhar um triângulo preenchido (OBJ_TRIANGLE), definido por três pontos com coordenadas de tempo ("t1", "t2", "t3") e preço ("p1", "p2", "p3"). Usaremos a função ObjectSetInteger para configurar as propriedades: OBJPROP_COLOR receberá a cor especificada; "OBJPROP_STYLE" será definido como "STYLE_SOLID"; "OBJPROP_WIDTH" receberá a largura desejada; "OBJPROP_FILL" permitirá ativar ou desativar o preenchimento; e "OBJPROP_BACK" determinará se o objeto será exibido em segundo plano ou em primeiro plano.
Em seguida, implementaremos a função "DrawTrendLine", que utiliza ObjectCreate para desenhar uma linha de tendência (OBJ_TREND) entre dois pontos. Com ObjectSetInteger, configuraremos as propriedades OBJPROP_COLOR, OBJPROP_STYLE (linha contínua, tracejada etc.) e OBJPROP_WIDTH, permitindo personalizar a aparência da linha. Depois, desenvolveremos a função "DrawDottedLine", que segue a mesma lógica para desenhar uma linha horizontal tracejada (OBJ_TREND) em um nível de preço especificado, entre os pontos temporais "t1" e "t2". Por fim, implementaremos a função "DrawTextEx", que utiliza ObjectCreate para inserir um rótulo de texto (OBJ_TEXT) nas coordenadas de tempo e preço ("t", "p"), seguindo o mesmo padrão de configuração das funções anteriores. Com esses recursos, poderemos representar claramente o padrão "Crab" e seus níveis operacionais no gráfico. Agora, podemos avançar para o manipulador de eventos OnTick e identificar os pivôs que serão utilizados posteriormente no reconhecimento dos padrões. A seguir, veremos a lógica adotada para isso.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { static datetime lastBarTime = 0; //--- Store time of last processed bar datetime currentBarTime = iTime(_Symbol, _Period, 1); //--- Get time of current confirmed bar if (currentBarTime == lastBarTime) return; //--- Exit if no new bar lastBarTime = currentBarTime; //--- Update last processed bar time ArrayResize(pivots, 0); //--- Clear pivot array for fresh analysis int barsCount = Bars(_Symbol, _Period); //--- Retrieve total number of bars int start = PivotLeft; //--- Set starting index for pivot detection int end = barsCount - PivotRight; //--- Set ending index for pivot detection for (int i = end - 1; i >= start; i--) { //--- Iterate through bars to identify pivots bool isPivotHigh = true; //--- Assume bar is a swing high bool isPivotLow = true; //--- Assume bar is a swing low double currentHigh = iHigh(_Symbol, _Period, i); //--- Get current bar high price double currentLow = iLow(_Symbol, _Period, i); //--- Get current bar low price for (int j = i - PivotLeft; j <= i + PivotRight; j++) { //--- Check surrounding bars if (j < 0 || j >= barsCount) continue; //--- Skip out-of-bounds indices if (j == i) continue; //--- Skip current bar if (iHigh(_Symbol, _Period, j) > currentHigh) isPivotHigh = false; //--- Invalidate swing high if (iLow(_Symbol, _Period, j) < currentLow) isPivotLow = false; //--- Invalidate swing low } if (isPivotHigh || isPivotLow) { //--- Check if bar is a pivot Pivot p; //--- Create new pivot structure p.time = iTime(_Symbol, _Period, i); //--- Set pivot bar time p.price = isPivotHigh ? currentHigh : currentLow; //--- Set pivot price p.isHigh = isPivotHigh; //--- Set pivot type int size = ArraySize(pivots); //--- Get current pivot array size ArrayResize(pivots, size + 1); //--- Resize pivot array pivots[size] = p; //--- Add pivot to array } } }
Começaremos implementando a lógica inicial do manipulador OnTick, responsável por detectar pivôs de máxima e mínima que servirão de base para a identificação dos padrões harmônicos "Crab". Primeiro, verificaremos se uma nova barra foi formada, comparando "lastBarTime", uma variável estática inicializada com zero, com "currentBarTime", obtido pela função iTime com deslocamento 1. Dessa forma, evitaremos utilizar a barra atual, ainda em formação, do ativo e período gráfico em análise. Se não houver uma nova barra, encerraremos a execução da função. Caso contrário, atualizaremos "lastBarTime" e limparemos o array "pivots" com ArrayResize, preparando-o para uma nova análise. Em seguida, obteremos o número total de barras com Bars e definiremos os limites da busca por pivôs: "start", igual a "PivotLeft", e "end", igual ao número total de barras menos "PivotRight". Percorreremos então as barras de "end - 1" até "start".
Para cada barra, consideraremos inicialmente que ela pode ser tanto um pivô de máxima ("isPivotHigh" igual a true) quanto um pivô de mínima ("isPivotLow" igual a true). Obteremos seus preços máximo e mínimo com iHigh e iLow e examinaremos as barras vizinhas nos intervalos definidos por "PivotLeft" e "PivotRight". Se alguma dessas barras apresentar uma máxima superior ou uma mínima inferior, descartaremos a respectiva condição de pivô. Se a barra continuar atendendo aos critérios de pivô de máxima ou mínima, criaremos uma estrutura "Pivot" e preencheremos seus campos: "time" receberá a marca de tempo obtida com iTime; "price" receberá o preço máximo ou mínimo, conforme o valor de "isPivotHigh"; e "isHigh" indicará se o pivô corresponde a uma máxima. Em seguida, ampliaremos o array "pivots" com ArrayResize e armazenaremos o novo elemento. Ao exibir os dados do array na tela, obteremos o seguinte resultado.

Com esses dados, podemos identificar os pontos de reversão do preço e, quando houver pivôs suficientes, analisar sua sequência para detectar os padrões. A seguir, apresentaremos a lógica que implementaremos para isso.
int pivotCount = ArraySize(pivots); //--- Get total number of pivots if (pivotCount < 5) { //--- Check if insufficient pivots g_patternFormationBar = -1; //--- Reset pattern formation bar g_lockedPatternX = 0; //--- Reset locked X pivot return; //--- Exit function } Pivot X = pivots[pivotCount - 5]; //--- Extract X pivot (earliest) Pivot A = pivots[pivotCount - 4]; //--- Extract A pivot Pivot B = pivots[pivotCount - 3]; //--- Extract B pivot Pivot C = pivots[pivotCount - 2]; //--- Extract C pivot Pivot D = pivots[pivotCount - 1]; //--- Extract D pivot (latest) bool patternFound = false; //--- Initialize pattern detection flag if (X.isHigh && !A.isHigh && B.isHigh && !C.isHigh && D.isHigh) { //--- Check bearish Crab pattern double diff = X.price - A.price; //--- Calculate XA leg difference if (diff > 0) { //--- Ensure positive XA move double idealB = A.price + 0.618 * diff; //--- Compute ideal B (0.618 retracement) if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance double AB = B.price - A.price; //--- Calculate AB leg length double BC = B.price - C.price; //--- Calculate BC leg length if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range double extension = D.price - A.price; //--- Calculate AD extension if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price > X.price) { //--- Verify 1.618 extension and D > X patternFound = true; //--- Confirm bearish pattern } } } } } if (!X.isHigh && A.isHigh && !B.isHigh && C.isHigh && !D.isHigh) { //--- Check bullish Crab pattern double diff = A.price - X.price; //--- Calculate XA leg difference if (diff > 0) { //--- Ensure positive XA move double idealB = A.price - 0.618 * diff; //--- Compute ideal B (0.618 retracement) if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance double AB = A.price - B.price; //--- Calculate AB leg length double BC = C.price - B.price; //--- Calculate BC leg length if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range double extension = A.price - D.price; //--- Calculate AD extension if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price < X.price) { //--- Verify 1.618 extension and D < X patternFound = true; //--- Confirm bullish pattern } } } } }
Para identificar os padrões, utilizaremos critérios baseados nas proporções de Fibonacci. Primeiro, obteremos o número total de pivôs com "arraySize(pivots)" e armazenaremos esse valor em "pivotCount". Se houver menos de cinco pivôs, definiremos "g_patternFormationBar" como -1 e "g_lockedPatternX" como 0 e encerraremos a execução da função, pois o padrão "Crab" exige cinco pontos: X, A, B, C e D. Em seguida, extrairemos os cinco pivôs mais recentes do array "pivots" e os identificaremos como X (o mais antigo), A, B, C e D (o mais recente), formando a estrutura do padrão.
Começaremos verificando a presença de um padrão "Crab" de baixa, cuja sequência deve ser: máxima em X, mínima em A, máxima em B, mínima em C e máxima em D. Calcularemos o comprimento do segmento XA pela diferença "X.price - A.price" e verificaremos se o resultado é positivo. Em seguida, calcularemos o nível ideal do ponto B como "A.price + 0.618 * diff" e utilizaremos MathAbs para confirmar se o desvio de B em relação a esse nível está dentro da tolerância "Tolerance * diff". Depois, verificaremos se o comprimento de BC corresponde a 38,2% a 88,6% do comprimento de AB e se a extensão AD atinge 161,8% de XA, com D situado acima de X. Se todas as condições forem atendidas, definiremos "patternFound" como true. Na sequência, verificaremos a presença de um padrão "Crab" de alta, com a sequência inversa: mínima em X, máxima em A, mínima em B, máxima em C e mínima em D. Calcularemos o comprimento de XA como "A.price - X.price" e confirmaremos que o resultado é positivo. Também verificaremos se B está no nível de retração de 61,8%, se BC corresponde a 38,2% a 88,6% do comprimento de AB e se AD atinge uma extensão de 161,8% de XA, com D abaixo de X. Se o padrão atender a todos os critérios, definiremos "patternFound" como true. Após identificar um padrão válido, poderemos representá-lo no gráfico.
string patternType = ""; //--- Initialize pattern type if (patternFound) { //--- Check if pattern detected if (D.price > X.price) patternType = "Bearish"; //--- Set bearish pattern (sell signal) else if (D.price < X.price) patternType = "Bullish"; //--- Set bullish pattern (buy signal) } if (patternFound) { //--- Process valid Crab pattern Print(patternType, " Crab pattern detected at ", TimeToString(D.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS)); //--- Log pattern detection string signalPrefix = "CR_" + IntegerToString(X.time); //--- Generate unique prefix for objects color triangleColor = (patternType == "Bullish") ? clrBlue : clrRed; //--- Set triangle color based on pattern DrawTriangle(signalPrefix + "_Triangle1", X.time, X.price, A.time, A.price, B.time, B.price, triangleColor, 2, true, true); //--- Draw XAB triangle DrawTriangle(signalPrefix + "_Triangle2", B.time, B.price, C.time, C.price, D.time, D.price, triangleColor, 2, true, true); //--- Draw BCD triangle }
Para classificar e visualizar os padrões identificados, inicializaremos a variável "patternType" com uma string vazia, que posteriormente indicará se o padrão é de alta ou de baixa. Se "patternFound" for true, determinaremos o tipo comparando "D.price" com "X.price". Quando D estiver acima de X, definiremos "patternType" como "Bearish", indicando um sinal de venda. Quando D estiver abaixo de X, atribuiremos o valor "Bullish", indicando um sinal de compra. Após confirmar a validade do padrão "Crab", registraremos sua identificação com a função Print, exibindo o valor de "patternType" e a marca de tempo do pivô D, formatada com TimeToString para apresentar a data e o horário com precisão de segundos.
Por fim, criaremos um identificador exclusivo chamado "signalPrefix", concatenando o prefixo "CR_" com o valor de "X.time" convertido em string. Definiremos "triangleColor" como azul para padrões de alta e vermelho para padrões de baixa. Em seguida, chamaremos "DrawTriangle" duas vezes para representar a estrutura do padrão: primeiro, desenharemos o triângulo XAB, conectando os pontos X, A e B; depois, o triângulo BCD, conectando B, C e D. Utilizaremos "signalPrefix" com os sufixos "_Triangle1" e "_Triangle2", respectivamente, além das coordenadas de tempo e preço dos pivôs correspondentes, da cor definida em "triangleColor" e da largura 2. Também ativaremos o preenchimento dos triângulos e sua exibição em segundo plano, facilitando a identificação visual dos padrões "Crab" e a análise dos sinais para a tomada de decisões de trading. Com isso, concluímos mais uma etapa da implementação.

A imagem mostra que conseguimos representar corretamente o padrão identificado no gráfico. Agora, precisamos adicionar as linhas de tendência para delinear toda a sua estrutura e adicionar rótulos que facilitem a identificação dos níveis do padrão.
DrawTrendLine(signalPrefix + "_TL_XA", X.time, X.price, A.time, A.price, clrBlack, 2, STYLE_SOLID); //--- Draw XA trend line DrawTrendLine(signalPrefix + "_TL_AB", A.time, A.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw AB trend line DrawTrendLine(signalPrefix + "_TL_BC", B.time, B.price, C.time, C.price, clrBlack, 2, STYLE_SOLID); //--- Draw BC trend line DrawTrendLine(signalPrefix + "_TL_CD", C.time, C.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw CD trend line DrawTrendLine(signalPrefix + "_TL_XB", X.time, X.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw XB trend line DrawTrendLine(signalPrefix + "_TL_BD", B.time, B.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw BD trend line double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Retrieve symbol point size double offset = 15 * point; //--- Calculate label offset (15 points) double textY_X = X.isHigh ? X.price + offset : X.price - offset; //--- Set X label Y coordinate double textY_A = A.isHigh ? A.price + offset : A.price - offset; //--- Set A label Y coordinate double textY_B = B.isHigh ? B.price + offset : B.price - offset; //--- Set B label Y coordinate double textY_C = C.isHigh ? C.price + offset : C.price - offset; //--- Set C label Y coordinate double textY_D = D.isHigh ? D.price + offset : D.price - offset; //--- Set D label Y coordinate DrawTextEx(signalPrefix + "_Text_X", "X", X.time, textY_X, clrBlack, 11, X.isHigh); //--- Draw X pivot label DrawTextEx(signalPrefix + "_Text_A", "A", A.time, textY_A, clrBlack, 11, A.isHigh); //--- Draw A pivot label DrawTextEx(signalPrefix + "_Text_B", "B", B.time, textY_B, clrBlack, 11, B.isHigh); //--- Draw B pivot label DrawTextEx(signalPrefix + "_Text_C", "C", C.time, textY_C, clrBlack, 11, C.isHigh); //--- Draw C pivot label DrawTextEx(signalPrefix + "_Text_D", "D", D.time, textY_D, clrBlack, 11, D.isHigh); //--- Draw D pivot label datetime centralTime = (X.time + B.time) / 2; //--- Calculate central label time double centralPrice = D.price; //--- Set central label price if (ObjectCreate(0, signalPrefix + "_Text_Center", OBJ_TEXT, 0, centralTime, centralPrice)) { //--- Create central pattern label ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_TEXT, patternType == "Bullish" ? "Bullish Crab" : "Bearish Crab"); //--- Set pattern name ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_COLOR, clrBlack); //--- Set text color ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_FONTSIZE, 11); //--- Set font size ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_FONT, "Arial Bold"); //--- Set font type ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text }
Para completar a representação do padrão, adicionaremos linhas e rótulos. Primeiro, utilizaremos a função "DrawTrendLine" para desenhar seis linhas de tendência, identificadas pelo prefixo exclusivo "signalPrefix", conectando os principais pontos de reversão. Os segmentos XA, AB, BC, CD, XB e BD terão suas extremidades definidas pelas coordenadas de tempo e preço dos respectivos pivôs, como "X.time" e "X.price". Todas as linhas utilizarão a cor "clrBlack", largura 2 e estilo STYLE_SOLID, delineando a estrutura XABCD e os segmentos auxiliares. Em seguida, calcularemos o deslocamento dos rótulos obtendo o tamanho de um ponto do ativo com "SymbolInfoDouble(_Symbol, SYMBOL_POINT)" e multiplicando esse valor por 15. Com esse deslocamento, determinaremos as coordenadas verticais dos rótulos ("textY_X", "textY_A", "textY_B", "textY_C", "textY_D"), somando ou subtraindo o valor conforme cada pivô represente uma máxima ("isHigh" igual a true) ou uma mínima. Assim, os rótulos aparecerão acima das máximas e abaixo das mínimas.
Em seguida, utilizaremos "DrawTextEx" para criar os rótulos dos pivôs X, A, B, C e D. Cada objeto será identificado por "signalPrefix" combinado com um sufixo correspondente, como "_Text_X", e exibirá a letra do respectivo pivô. Os rótulos serão posicionados na marca de tempo de cada pivô, com a coordenada vertical ajustada pelo deslocamento calculado. Utilizaremos a cor "clrBlack", tamanho de fonte 11 e o valor de "isHigh" para definir a ancoragem do texto. Por fim, calcularemos a posição do rótulo central, definindo "centralTime" como o ponto médio entre "X.time" e "B.time" e "centralPrice" como "D.price". Criaremos então um objeto de texto com ObjectCreate, utilizando o nome "signalPrefix + '_Text_Center'". A propriedade OBJPROP_TEXT receberá o texto "Bullish Crab" ou "Bearish Crab", conforme o valor de "patternType". Também configuraremos "OBJPROP_COLOR" como "clrBlack", "OBJPROP_FONTSIZE" como 11, "OBJPROP_FONT" como "Arial Bold" e "OBJPROP_ALIGN" como "ALIGN_CENTER", utilizando ObjectSetString e ObjectSetInteger. Com isso, teremos uma representação completa da estrutura e do tipo do padrão "Crab" no gráfico. Ao executar o programa, obteremos o seguinte resultado.

A imagem mostra que adicionamos as linhas de contorno e os rótulos ao padrão, tornando sua estrutura mais fácil de identificar. O próximo passo será definir os níveis operacionais correspondentes.
datetime lineStart = D.time; //--- Set start time for trade level lines datetime lineEnd = D.time + PeriodSeconds(_Period) * 2; //--- Set end time for trade level lines double entryPriceLevel, TP1Level, TP2Level, TP3Level, tradeDiff; //--- Declare trade level variables if (patternType == "Bullish") { //--- Handle bullish trade levels entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Set entry at ask price TP3Level = C.price; //--- Set TP3 at C pivot price tradeDiff = TP3Level - entryPriceLevel; //--- Calculate total trade distance TP1Level = entryPriceLevel + tradeDiff / 3; //--- Set TP1 at 1/3 of distance TP2Level = entryPriceLevel + 2 * tradeDiff / 3; //--- Set TP2 at 2/3 of distance } else { //--- Handle bearish trade levels entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Set entry at bid price TP3Level = C.price; //--- Set TP3 at C pivot price tradeDiff = entryPriceLevel - TP3Level; //--- Calculate total trade distance TP1Level = entryPriceLevel - tradeDiff / 3; //--- Set TP1 at 1/3 of distance TP2Level = entryPriceLevel - 2 * tradeDiff / 3; //--- Set TP2 at 2/3 of distance } DrawDottedLine(signalPrefix + "_EntryLine", lineStart, entryPriceLevel, lineEnd, clrMagenta); //--- Draw entry level line DrawDottedLine(signalPrefix + "_TP1Line", lineStart, TP1Level, lineEnd, clrForestGreen); //--- Draw TP1 level line DrawDottedLine(signalPrefix + "_TP2Line", lineStart, TP2Level, lineEnd, clrGreen); //--- Draw TP2 level line DrawDottedLine(signalPrefix + "_TP3Line", lineStart, TP3Level, lineEnd, clrDarkGreen); //--- Draw TP3 level line datetime labelTime = lineEnd + PeriodSeconds(_Period) / 2; //--- Set time for trade level labels string entryLabel = patternType == "Bullish" ? "BUY (" : "SELL ("; //--- Start entry label text entryLabel += DoubleToString(entryPriceLevel, _Digits) + ")"; //--- Append entry price DrawTextEx(signalPrefix + "_EntryLabel", entryLabel, labelTime, entryPriceLevel, clrMagenta, 11, true); //--- Draw entry label string tp1Label = "TP1 (" + DoubleToString(TP1Level, _Digits) + ")"; //--- Create TP1 label text DrawTextEx(signalPrefix + "_TP1Label", tp1Label, labelTime, TP1Level, clrForestGreen, 11, true); //--- Draw TP1 label string tp2Label = "TP2 (" + DoubleToString(TP2Level, _Digits) + ")"; //--- Create TP2 label text DrawTextEx(signalPrefix + "_TP2Label", tp2Label, labelTime, TP2Level, clrGreen, 11, true); //--- Draw TP2 label string tp3Label = "TP3 (" + DoubleToString(TP3Level, _Digits) + ")"; //--- Create TP3 label text DrawTextEx(signalPrefix + "_TP3Label", tp3Label, labelTime, TP3Level, clrDarkGreen, 11, true); //--- Draw TP3 label
Nesta etapa, continuaremos com o cálculo e a visualização dos níveis operacionais do padrão identificado. Primeiro, definiremos "lineStart" com a marca de tempo do pivô D ("D.time") e "lineEnd" com um instante correspondente a dois períodos gráficos após esse pivô, calculado com "PeriodSeconds(_Period) * 2". Em seguida, declararemos as variáveis "entryPriceLevel", "TP1Level", "TP2Level", "TP3Level" e "tradeDiff", que serão utilizadas nos cálculos das operações. Para um padrão de alta ("patternType == 'Bullish'"), definiremos "entryPriceLevel" com o preço Ask atual, obtido por meio de SymbolInfoDouble, e "TP3Level" com o preço do pivô C. Calcularemos "tradeDiff" como "TP3Level - entryPriceLevel" e determinaremos "TP1Level" e "TP2Level" acrescentando ao preço de entrada, respectivamente, um terço e dois terços de "tradeDiff". Para um padrão de baixa, utilizaremos o preço Bid como nível de entrada e definiremos "TP3Level" com o preço do ponto C. Nesse caso, calcularemos "tradeDiff" como "entryPriceLevel - TP3Level" e determinaremos "TP1Level" e "TP2Level" subtraindo do preço de entrada, respectivamente, um terço e dois terços dessa diferença.
Em seguida, utilizaremos a função "DrawDottedLine" para desenhar quatro linhas horizontais tracejadas, desde "lineStart" até "lineEnd": uma linha de entrada no nível "entryPriceLevel", na cor magenta, e três linhas de take-profit nos níveis "TP1Level" (verde-floresta intenso), "TP2Level" (verde) e "TP3Level" (verde-escuro). Por fim, definiremos "labelTime" como "lineEnd" acrescido de metade de um período gráfico e criaremos rótulos com os preços formatados por DoubleToString, como "BUY (price)" ou "SELL (price)" para a entrada e "TP1 (price)", entre outros, para os níveis de take-profit. Utilizaremos "DrawTextEx" para posicionar esses rótulos em "labelTime", com as cores correspondentes, tamanho de fonte 11 e ancoragem acima dos respectivos níveis de preço. Após compilar o programa, obteremos o seguinte resultado.
Padrão de baixa:

Padrão de alta:

As imagens mostram que os níveis operacionais foram representados corretamente no gráfico. Agora, resta implementar a abertura das posições reais de trading.
int currentBarIndex = Bars(_Symbol, _Period) - 1; //--- Retrieve current bar index if (g_patternFormationBar == -1) { //--- Check if no pattern is locked g_patternFormationBar = currentBarIndex; //--- Lock current bar as formation bar g_lockedPatternX = X.time; //--- Lock X pivot time Print("Pattern detected on bar ", currentBarIndex, ". Waiting for confirmation on next bar."); //--- Log detection return; //--- Exit function } if (currentBarIndex == g_patternFormationBar) { //--- Check if still on formation bar Print("Pattern is repainting; still on locked formation bar ", currentBarIndex, ". No trade yet."); //--- Log repainting return; //--- Exit function } if (currentBarIndex > g_patternFormationBar) { //--- Check if new bar after formation if (g_lockedPatternX == X.time) { //--- Verify same X pivot for confirmation Print("Confirmed pattern (locked on bar ", g_patternFormationBar, "). Opening trade on bar ", currentBarIndex, "."); //--- Log confirmed pattern g_patternFormationBar = currentBarIndex; //--- Update formation bar to current if (AllowTrading && !PositionSelect(_Symbol)) { //--- Check trading allowed and no open position double entryPriceTrade = 0, stopLoss = 0, takeProfit = 0; //--- Declare trade parameters point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Update point value bool tradeResult = false; //--- Initialize trade result flag if (patternType == "Bullish") { //--- Process bullish trade entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Set entry at ask price double diffTrade = TP2Level - entryPriceTrade; //--- Calculate trade distance stopLoss = entryPriceTrade - diffTrade * 3; //--- Set stop loss (3x distance) takeProfit = TP2Level; //--- Set take profit at TP2 tradeResult = obj_Trade.Buy(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute buy trade if (tradeResult) { //--- Check trade success Print("Buy order opened successfully."); //--- Log successful buy } else { //--- Handle trade failure Print("Buy order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason } } else if (patternType == "Bearish") { //--- Process bearish trade entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Set entry at bid price double diffTrade = entryPriceTrade - TP2Level; //--- Calculate trade distance stopLoss = entryPriceTrade + diffTrade * 3; //--- Set stop loss (3x distance) takeProfit = TP2Level; //--- Set take profit at TP2 tradeResult = obj_Trade.Sell(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute sell trade if (tradeResult) { //--- Check trade success Print("Sell order opened successfully."); //--- Log successful sell } else { //--- Handle trade failure Print("Sell order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason } } } else { //--- Trading not allowed or position exists Print("A position is already open for ", _Symbol, ". No new trade executed."); //--- Log no trade } } else { //--- Pattern has changed g_patternFormationBar = currentBarIndex; //--- Update formation bar g_lockedPatternX = X.time; //--- Update locked X pivot Print("Pattern has changed; updating lock on bar ", currentBarIndex, ". Waiting for confirmation."); //--- Log pattern change return; //--- Exit function } } } else { //--- No valid pattern detected g_patternFormationBar = -1; //--- Reset formation bar g_lockedPatternX = 0; //--- Reset locked X pivot }
Primeiro, obteremos o índice da barra atual com "Bars(_Symbol, _Period) - 1" e armazenaremos o resultado em "currentBarIndex". Em seguida, se não houver nenhum padrão bloqueado ("g_patternFormationBar == -1"), definiremos "g_patternFormationBar" como "currentBarIndex" e armazenaremos a marca de tempo do pivô X em "g_lockedPatternX", utilizando "X.time". Também registraremos uma mensagem informando que o padrão foi identificado e aguarda confirmação, encerrando a execução da função. Se o padrão ainda estiver na mesma barra em que foi detectado ("currentBarIndex == g_patternFormationBar"), registraremos que o padrão está passando por repainting e encerraremos a execução, evitando a abertura prematura de operações.
Quando uma nova barra for formada ("currentBarIndex > g_patternFormationBar") e o pivô X corresponder ao valor armazenado em "g_lockedPatternX", confirmaremos o padrão e fixaremos seu estado, atualizando "g_patternFormationBar". Em seguida, verificaremos se a negociação está habilitada por "AllowTrading" e se não existem posições abertas, utilizando PositionSelect. Para um padrão de alta, definiremos "entryPriceTrade" com o preço Ask e calcularemos "diffTrade" como "TP2Level - entryPriceTrade". O "stopLoss" será posicionado abaixo do preço de entrada, a uma distância equivalente a três vezes "diffTrade", enquanto "takeProfit" receberá o valor de "TP2Level". Abriremos então uma posição de compra com "obj_Trade.Buy", utilizando o volume definido em "LotSize" e o comentário "Crab Signal", e registraremos se a operação foi executada com sucesso ou se houve falha. Para um padrão de baixa, utilizaremos o preço Bid e posicionaremos o "stopLoss" acima do preço de entrada, a uma distância equivalente a três vezes a diferença calculada para a operação. Em seguida, abriremos uma posição de venda com "obj_Trade.Sell". Se a negociação estiver desabilitada ou já existir uma posição aberta, nenhuma nova operação será executada. Caso o padrão mude, atualizaremos os valores armazenados e aguardaremos uma nova confirmação. Se nenhum padrão for identificado, redefiniremos "g_patternFormationBar" e "g_lockedPatternX" para seus valores iniciais. Dessa forma, somente padrões "Crab" confirmados poderão gerar operações, com níveis definidos para o gerenciamento de risco. Por fim, precisaremos apenas implementar a remoção dos objetos gráficos dos padrões quando o programa for retirado do gráfico.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, "CR_"); //--- Remove all chart objects with "CR_" prefix ArrayResize(pivots, 0); //--- Clear pivots array g_patternFormationBar = -1; //--- Reset pattern formation bar index g_lockedPatternX = 0; //--- Reset locked pattern X pivot time ChartRedraw(0); //--- Redraw chart to reflect changes }
Nesta etapa, implementaremos o manipulador de eventos OnDeinit para executar a limpeza necessária quando o EA for removido do gráfico. Primeiro, utilizaremos ObjectsDeleteAll para excluir todos os objetos gráficos com o prefixo "CR_", removendo os elementos visuais associados aos padrões "Crab", como triângulos, linhas de tendência e rótulos. Em seguida, utilizaremos ArrayResize para reduzir o tamanho do array "pivots" a zero, eliminando os dados armazenados dos pivôs. Também redefiniremos "g_patternFormationBar" como -1 e "g_lockedPatternX" como 0, reinicializando as variáveis de acompanhamento dos padrões. Por fim, chamaremos ChartRedraw para atualizar o gráfico e refletir a remoção dos objetos. Essa limpeza libera os recursos utilizados e evita que elementos gráficos permaneçam no gráfico após a remoção do EA. Após compilar o programa, obteremos o seguinte resultado.
Sinal de baixa:

Sinal de alta:

A imagem mostra que conseguimos representar graficamente o padrão harmônico e que o sistema também permite executar operações com base nele assim que houver confirmação. Com isso, alcançamos nosso objetivo de identificar, visualizar e negociar o padrão "Crab". Resta apenas realizar o backtesting do programa, que será apresentado na próxima seção.
Backtesting
Após realizar um backtesting detalhado, obtivemos os seguintes resultados.
Gráfico do backtesting:

Relatório do backtesting:

Conclusão
Neste artigo, desenvolvemos um sistema de reconhecimento do padrão harmônico "Crab" em MQL5. O sistema utiliza os movimentos do preço para identificar padrões de alta e de baixa com base em níveis precisos de Fibonacci, automatizando as operações com pontos de entrada calculados, stop-loss e múltiplos níveis de take-profit. Esses elementos são representados no gráfico por meio de objetos gráficos dinâmicos, como triângulos e linhas de tendência.
Aviso de isenção de responsabilidade: O conteúdo deste artigo tem finalidade exclusivamente educacional. O trading envolve riscos financeiros significativos, e a volatilidade do mercado pode resultar em perdas. Antes de utilizar este programa em condições reais de mercado, é fundamental realizar um backtesting rigoroso e adotar práticas adequadas de gerenciamento de risco.
Com os conceitos e métodos de implementação apresentados, você poderá adaptar o sistema de reconhecimento do padrão "Crab" ao seu estilo de trading e aprimorar suas estratégias algorítmicas. Bons trades!
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/19099
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.
absolutamente nada :-)
A saliência direita foi aumentada em 1,68, ou seja, é a mesma borboleta, mas com mais um nível de Fibonacci adicionado. Se for fazer isso, o código é o mesmo.
Qualquer padrão não tem a ver com o código, mas sim com o sinal.
Os tipos de sinais dos padrões são diferentes — isso significa que um padrão é adequado para uma situação, e outro padrão, para outra.
Qualquer padrão não tem a ver com código, mas sim com um sinal.
Os tipos de sinais dos padrões são diferentes — isso significa que um padrão é adequado para uma situação, enquanto outro padrão é adequado para outra.
... e, mesmo assim, o resultado continua o mesmo = 50/50
no artigo sobre o padrão, e não sobre a filosofia do sinal.
A filosofia do sinal já foi descrita há muito tempo, explorada e explicada de forma extremamente clara, por exemplo, no filme cult “O Lobo de Wall Street”, no diálogo entre Mark Hann e Jordan Belfort:
Vídeo com tradução para o russo aqui: https://www.youtube.com/watch?v=fRVwyZ6t7Fk&t=112s
Citação:“Ninguém sabe se o preço das ações vai subir, descer, ficar lateral ou girar em círculos — tudo isso é humera, quimera, só o humer vai entender”
... e, mesmo assim, o resultado continua o mesmo = 50/50
só que, além de um único sinal do padrão, há um conjunto de sinais de diferentes tipos (sinais de entrada/saída, de filtragem), há gestão de capital — e, no fim das contas, a probabilidade na saída está longe de ser 50/50
Artigo publicado : Automatização de estratégias de trading em MQL5 (Parte 27): Criando um padrão harmônico “Crab” de ação do preço com sinais visuais:
Autor: Allan Munene Mutiiria