English Русский 中文 Deutsch 日本語
preview
Automatización de estrategias de trading en MQL5 (Parte 27): Creando un patrón armónico «Crab» de acción del precio con señales visuales

Automatización de estrategias de trading en MQL5 (Parte 27): Creando un patrón armónico «Crab» de acción del precio con señales visuales

MetaTrader 5 — Sistemas comerciales |
33 8
Allan Munene Mutiiria
Allan Munene Mutiiria

Introducción

En nuestro artículo anterior (Parte 26), desarrollamos un sistema de Pin Bar con promedios en MetaQuotes Language 5 (MQL5) que utilizaba patrones de velas «pin bar» para iniciar operaciones y gestionar múltiples posiciones mediante una estrategia de promedios, y que incluía un panel de control dinámico para la supervisión en tiempo real. En la Parte 27, creamos un sistema de patrones «Crab» que identifica los patrones armónicos «Crab» alcistas y bajistas utilizando puntos de pivote y niveles de Fibonacci, automatizando las operaciones con niveles precisos de entrada, stop loss y take profit, y mejorándolo con objetos gráficos como triángulos y líneas de tendencia para una representación clara de los patrones. Trataremos los siguientes temas:

  1. Comprender el patrón armónico «Crab»
  2. Implementación en MQL5
  3. Backtesting
  4. Conclusión

Al final, tendrás una sofisticada estrategia MQL5 para operar con patrones armónicos, lista para personalizarla. ¡Manos a la obra!


Comprender el patrón armónico «Crab»

El patrón «Crab» es una formación armónica de trading definida por cinco puntos de oscilación clave —X, A, B, C y D— y se presenta en dos variantes: un patrón alcista y un patrón bajista. En un patrón «Crab» alcista, la estructura forma una secuencia de mínimo-máximo-mínimo-máximo-mínimo, en la que el punto X es un mínimo oscilante, el punto A un máximo oscilante, el punto B un mínimo oscilante (que retrocede un 0,618 de XA), el punto C un máximo oscilante (que se extiende entre el 0,382 y el 0,886 de AB), y el punto D, un mínimo oscilante (que se extiende hasta 1,618 de XA, situado por debajo de X). Por el contrario, un patrón «Crab» bajista forma una secuencia de máximo-mínimo-máximo-mínimo-máximo, en la que el punto X es un máximo de oscilación, el punto A un mínimo de oscilación, el punto B un máximo de oscilación, el punto C un mínimo de oscilación y el punto D un máximo de oscilación (que se extiende hasta 1,618 veces la distancia XA y se sitúa por encima de X). A continuación se muestran los tipos de patrones visualizados.

Patrón armónico alcista «Crab»:

PATRÓN ARMÓNICO ALCISTA «CRAB»

Patrón armónico bajista «Crab»:

PATRÓN ARMÓNICO BAJISTA «CRAB»

Para identificar los patrones, a continuación se presenta nuestro enfoque estructurado:

  • Definición del tramo XA: El movimiento impulsivo inicial desde el punto X hasta el punto A establece la base del patrón, determina la dirección (a la baja en el caso alcista, al alza en el caso bajista) y sirve de referencia para los cálculos de Fibonacci.
  • Determinación del tramo AB: El punto B debería retroceder aproximadamente el 0,618 del tramo XA, lo que confirmaría una corrección sin revertir el movimiento inicial de forma demasiado agresiva.
  • Análisis del tramo BC: Este tramo debería extenderse entre el 0,382 y el 0,886 del tramo AB, creando un fuerte movimiento contrario que prepara la extensión final.
  • Configuración del tramo CD: El tramo final debe extenderse 1,618 veces el tramo XA, lo que marca la zona de posible reversión en el punto D, donde se completa el patrón y se genera una señal de operación.

Al aplicar estos criterios geométricos y basados en Fibonacci, nuestro sistema de negociación detectará de forma sistemática patrones «Crab» válidos en los datos de precios. Una vez identificado el patrón, el sistema mostrará la formación en el gráfico mediante triángulos, líneas de tendencia, etiquetas para los puntos X, A, B, C y D, y líneas punteadas para los niveles de entrada y de cierre de beneficios. Esta configuración permitirá la ejecución automática de operaciones en el punto D con un stop loss calculado y órdenes de cierre de ganancias en varios niveles, aprovechando la naturaleza de reversión de alta probabilidad del patrón para realizar entradas efectivas en el mercado. ¡Pasemos a la implementación!


Implementación en MQL5

Para crear el programa en MQL5, abre el MetaEditor, ve al Navegador, busca la carpeta «Indicators», haz clic en la pestaña «Nuevo» y sigue las instrucciones para crear el archivo. Una vez creado, en el entorno de programación, tendremos que declarar algunas variables globales que utilizaremos a lo largo de todo el 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

Comenzamos la implementación del patrón Crab incluyendo la biblioteca «<Trade\Trade.mqh>» y creando una instancia de «obj_Trade» como objeto CTrade para facilitar la gestión de órdenes, como el envío de solicitudes de compra y venta. A continuación, procedemos a definir los parámetros de entrada para la personalización por parte del usuario: «PivotLeft» y «PivotRight», con 5 barras cada uno, para especificar el periodo de análisis retrospectivo para identificar los puntos de pivote de oscilación; «Tolerance», con un valor de 0,10, para el margen de desviación de Fibonacci; «LotSize», con un valor de 0,01, para el volumen de la operación; y «AllowTrading», con el valor «true», para habilitar la ejecución automática.

A continuación, definimos la estructura «Pivot» con «time» (datetime), «price» (double) e «isHigh» (bool) para almacenar los puntos de oscilación; declaramos «pivots» como un array de «Pivot» e inicializamos las variables globales «g_patternFormationBar» con el valor -1 para realizar un seguimiento de las barras de formación de patrones y «g_lockedPatternX» con el valor 0 para bloquear el tiempo del pivote X a efectos de confirmación, sentando así las bases para la identificación de patrones. Para la visualización, disponemos de funciones que permiten dibujar líneas, etiquetas y 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
   }
}

Aquí implementamos funciones de visualización para que el programa dibuje objetos gráficos que representen el patrón armónico «Crab» y sus niveles de negociación. En primer lugar, creamos la función «DrawTriangle», que utiliza ObjectCreate para dibujar un triángulo relleno (OBJ_TRIANGLE) con tres puntos definidos por momentos («t1», «t2», «t3») y precios («p1», «p2», «p3»), estableciendo OBJPROP_COLOR en el color especificado, «OBJPROP_STYLE» en «STYLE_SOLID», «OBJPROP_WIDTH» al ancho indicado, «OBJPROP_FILL» para activar o desactivar el relleno, y «OBJPROP_BACK» para establecer la posición en primer plano o en segundo plano mediante la función ObjectSetInteger.

A continuación, procedemos a implementar la función «DrawTrendLine», que crea una línea de tendencia («OBJ_TREND») entre dos puntos utilizando «ObjectCreate», configurando «OBJPROP_COLOR», «OBJPROP_STYLE» (continua, punteada, etc.) y OBJPROP_WIDTH mediante «ObjectSetInteger» para personalizar el aspecto de la línea. A continuación, desarrollamos la función «DrawDottedLine», que traza una línea punteada horizontal (OBJ_TREND) a un precio determinado desde «t1» hasta «t2» y utiliza la misma lógica. Por último, implementamos la función «DrawTextEx», que crea una etiqueta de texto (OBJ_TEXT) en las coordenadas («t», «p») mediante la función de creación de objetos y utiliza el mismo formato que las funciones anteriores, lo que garantiza una representación visual clara del patrón Crab y de los niveles de negociación en el gráfico. Ahora podemos pasar al controlador de eventos OnTick e intentar encontrar puntos pivote que podamos utilizar más adelante para la identificación de patrones. Esta es la lógica que utilizamos para lograrlo.

//+------------------------------------------------------------------+
//| 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
      }
   }
}

Procedemos a implementar la lógica inicial del controlador de eventos OnTick para que nuestro programa detecte los puntos de pivote de los swings, lo que sentará las bases para identificar los patrones armónicos «Crab». En primer lugar, comprobamos si hay una nueva barra comparando «lastBarTime» (variable estática, inicializada en 0) con «currentBarTime» de iTime en el turno 1, para evitar utilizar la barra actual, que aún está incompleta, para el símbolo y el período actuales; si no ha cambiado, salimos de la función, y si se detecta una nueva barra, actualizamos «lastBarTime». A continuación, procedemos a borrar el array «pivots» con ArrayResize para garantizar un análisis nuevo. A continuación, recuperamos el recuento total de barras con Bars, establecemos el rango de detección del pivote desde «start» (igual a «PivotLeft») hasta «end» (el total de barras menos «PivotRight») y recorremos las barras desde «end - 1» hasta «start».

Para cada barra, suponemos que se trata de un máximo de oscilación («isPivotHigh» verdadero) y un mínimo de oscilación («isPivotLow» verdadero), obtenemos sus precios máximos y mínimos con iHigh e «iLow», y comprobamos las barras adyacentes dentro de «PivotLeft» y «PivotRight» utilizando «iHigh» e iLow para invalidar el pivote si alguna barra vecina tiene un máximo más alto o un mínimo más bajo. Por último, si la barra sigue siendo un pivote válido (máximo o mínimo), creamos una estructura «Pivot», establecemos su «tiempo» con «iTime», su «precio» en el máximo o el mínimo según «isPivotHigh» y el indicador «isHigh», y luego la añadimos a la matriz «pivots» con «ArrayResize» y la almacenamos. Cuando imprimimos la matriz, obtenemos el siguiente resultado.

DATOS DE PIVOTES

Con los datos, podemos extraer los puntos de pivote y, si tenemos suficientes, podemos analizarlos y detectar los patrones. Esta es la lógica que implementamos para lograrlo.

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 los patrones, utilizamos criterios basados en la secuencia de Fibonacci. En primer lugar, recuperamos el número total de pivotes con «ArraySize (pivotes)» y lo almacenamos en «pivotCount», saliendo con «g_patternFormationBar» y «g_lockedPatternX» restablecidos a -1 y 0, respectivamente, si se encuentran menos de 5 pivotes, ya que el patrón Crab requiere los puntos X, A, B, C y D. A continuación, procedemos a extraer los últimos cinco pivotes del array «pivots», asignándoles «X» (el más temprano), «A», «B», «C» y «D» (el más tardío) para representar la estructura del patrón.

A continuación, buscamos un patrón «Crab» bajista comprobando la secuencia (X: máximo, A: mínimo, B: máximo, C: mínimo, D: máximo), calculando la diferencia del tramo XA («X.precio - A.precio»), asegurándonos de que sea positiva, calculando el punto B ideal como «A.precio + 0,618 * diff» y confirmando que B se encuentra dentro de «Tolerancia * diff» utilizando MathAbs; a continuación, validamos el tramo BC (entre 0,382 y 0,886 de AB) y la extensión AD (1,618 de XA con D por encima de X), estableciendo «patternFound» en «true» si se cumplen todas las condiciones. Por último, comprobamos si existe un patrón «Crab» alcista (X: mínimo; A: máximo; B: mínimo; C: máximo; D: mínimo), calculando XA como «A.precio - X.precio», asegurándonos de que sea positivo, verificando que B se encuentre en el retroceso del 0,618, que BC esté entre el 0,382 y el 0,886 de AB, y que AD sea el 1,618 de XA con D por debajo de X, y estableciendo «patternFound» en «true» si es válido. Si se detecta el patrón, podemos pasar a visualizarlo en el 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 clasificar y visualizar los patrones detectados en el gráfico, inicializamos «patternType» como una cadena vacía para indicar si el patrón es alcista o bajista. A continuación, si «patternFound» es verdadero, determinamos el tipo de patrón comparando «D.price» con «X.price»: establecemos «patternType» en «Bearish» si D está por encima de X (lo que indica una señal de venta) o en «Bullish» si D está por debajo de X (lo que indica una señal de compra). A continuación, cuando se confirma un patrón Crab válido, registramos la detección con Print, mostrando el «patternType» y la hora del pivote D mediante «TimeToString», formateada con la fecha, los minutos y los segundos.

Por último, creamos un identificador único «signalPrefix» como «CR_» concatenado con «X.time» convertido a cadena de caracteres, establecemos «triangleColor» en azul para los patrones alcistas o en rojo para los bajistas, y llamamos a «DrawTriangle» dos veces para visualizar el patrón: primero para el triángulo XAB (que une X, A y B) y luego para el triángulo BCD (que une B, C y D), utilizando «signalPrefix» con los sufijos «_Triangle1» y «_Triangle2», las horas y precios de pivote respectivos, «triangleColor», un ancho de 2, y activando la visualización del relleno y el fondo, lo que garantiza una identificación clara y una representación visual de los patrones «Crab» detectados para la toma de decisiones de trading. Este es el resultado hasta este punto.

DISEÑO CON TRIÁNGULOS

En la imagen se puede observar que podemos representar y visualizar correctamente el patrón detectado. Ahora tenemos que seguir trazando las líneas de tendencia para que se vean claramente dentro de los límites y añadirles una etiqueta que permita identificar más fácilmente los niveles.

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 representar la estructura del patrón, seguimos añadiendo líneas y etiquetas. En primer lugar, trazamos seis líneas de tendencia utilizando «DrawTrendLine» con el «signalPrefix» único para conectar los puntos de pivote clave: XA, AB, BC, CD, XB y BD, cada una con extremos definidos por sus respectivos tiempos y precios de pivote (p. ej., «X.time», «X.price»), utilizando «clrBlack» como color, un ancho de 2 y STYLE_SOLID para un estilo de línea continua, delineando la estructura XABCD y los tramos de apoyo. A continuación, procedemos a calcular el desplazamiento de una etiqueta recuperando el tamaño en puntos del símbolo con «SymbolInfoDouble(_Symbol, SYMBOL_POINT) » y multiplicándolo por 15, determinando las coordenadas Y de las etiquetas de los puntos de pivote («textY_X», «textY_A», «textY_B», «textY_C», «textY_D») sumando o restando el desplazamiento en función de si cada punto de pivote es un máximo de oscilación («isHigh» verdadero) o un mínimo, asegurándonos de que las etiquetas aparezcan por encima de los máximos y por debajo de los mínimos.

A continuación, utilizamos «DrawTextEx» para crear etiquetas de texto para los pivotes X, A, B, C y D, cada una con «signalPrefix» y sufijos como «_Text_X», que muestran la letra correspondiente, situadas en el momento del pivote y con la coordenada Y ajustada, utilizando «clrBlack», tamaño de fuente 11 y el estado «isHigh» del pivote para el anclaje. Por último, calculamos la posición central de la etiqueta en «centralTime» como el punto medio entre «X.time» y «B.time», y «centralPrice» en «D.price», creando un objeto de texto con ObjectCreate denominado «signalPrefix + '_Text_Center'», estableciendo OBJPROP_TEXT en «Bullish Crab» o «Bearish Crab» según «patternType», y configurando «OBJPROP_COLOR» en «clrBlack», «OBJPROP_FONTSIZE» en 11, «OBJPROP_FONT» en «Arial Bold» y «OBJPROP_ALIGN» en «ALIGN_CENTER» con ObjectSetString y «ObjectSetInteger». Esto garantiza una representación visual completa de la estructura y el tipo del patrón «Crab» en el gráfico. Cuando ejecutamos el programa, esto es lo que vemos.

PATRÓN «CRAB» CON BORDES Y ETIQUETAS

En la imagen podemos ver que hemos añadido los contornos y las etiquetas al patrón, lo que lo hace más claro y ilustrativo. Lo siguiente que tenemos que hacer es determinar los niveles de negociación de la figura.

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

A continuación, seguimos definiendo y visualizando los niveles de negociación para el patrón detectado. En primer lugar, establecemos «lineStart» en el tiempo del pivote D («D.time») y «lineEnd» en dos períodos más adelante utilizando «PeriodSeconds(_Period) * 2», y declaramos las variables «entryPriceLevel», «TP1Level», «TP2Level», «TP3Level» y «tradeDiff» para los cálculos de la operación. A continuación, para un patrón alcista («patternType == 'Bullish'»), establecemos «entryPriceLevel» en el precio Ask actual con SymbolInfoDouble, «TP3Level» al precio del pivote C, calculamos «tradeDiff» como «TP3Level - entryPriceLevel» y calculamos «TP1Level» y «TP2Level» como un tercio y dos tercios de «tradeDiff» sumados a «entryPriceLevel»; para un patrón bajista, utilizamos el precio Bid, fijamos «TP3Level» en el precio de C, calculamos «tradeDiff» como «entryPriceLevel - TP3Level» y calculamos «TP1Level» y «TP2Level» restando un tercio y dos tercios de la diferencia de la operación.

A continuación, trazamos cuatro líneas horizontales punteadas con la función «DrawDottedLine»: una línea de entrada en «entryPriceLevel» en color magenta, y líneas de cierre de beneficios en «TP1Level» (verde bosque), «TP2Level» (verde) y «TP3Level» (verde oscuro), que se extienden desde «lineStart» hasta «lineEnd». Por último, establecemos «labelTime» en «lineEnd» más medio período, creamos textos de etiqueta con los precios formateados mediante DoubleToString (por ejemplo, «COMPRAR (precio)» o «VENDER (precio)» para la entrada, «TP1 (precio)», etc.), y utilizamos «DrawTextEx» para dibujar estas etiquetas en «labelTime» con los colores correspondientes, tamaño de fuente 11, y ancladas por encima de los niveles de precio. Tras la compilación, obtenemos el siguiente resultado.

Patrón bajista:

BAJISTA

Patrón alcista:

ALCISTA

En las imágenes se puede observar que hemos representado correctamente los niveles de negociación. Lo que tenemos que hacer ahora es abrir las posiciones de negociación propiamente dichas, y eso es todo.

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
}

En primer lugar, recuperamos el índice de la barra actual con «Bars(_Symbol, _Period) - 1» y lo almacenamos en «currentBarIndex». A continuación, si no hay ningún patrón bloqueado («g_patternFormationBar == -1»), asignamos a «g_patternFormationBar» el valor de «currentBarIndex», bloqueamos la hora del pivote X en «g_lockedPatternX» con «X.time», registramos la detección indicando que hay que esperar a la confirmación y salimos. A continuación, si seguimos en la barra de formación («currentBarIndex == g_patternFormationBar»), registramos que el patrón se está actualizando y salimos para evitar operaciones prematuras.

Por último, si se ha formado una nueva barra («currentBarIndex > g_patternFormationBar») y el pivote X coincide con «g_lockedPatternX», confirmamos el patrón, lo registramos, actualizamos «g_patternFormationBar» y comprobamos si se permite operar mediante «AllowTrading» y si no hay posiciones abiertas a través de PositionSelect; en el caso de un patrón alcista, establecemos «entryPriceTrade» en el precio Ask, calculamos «diffTrade» como «TP2Level - entryPriceTrade», fijamos «stopLoss» tres veces esta distancia por debajo, establecemos «takeProfit» en «TP2Level» y ejecutamos una compra con «obj_Trade.Buy» utilizando «LotSize» y el comentario «Crab Signal», registrando si ha sido un éxito o un fracaso; para un patrón bajista, utilizamos el precio Bid, fijamos «stopLoss» tres veces por encima y ejecutamos una venta con «obj_Trade.Sell»; si no se permite operar o ya existe una posición, registramos que no se ha realizado ninguna operación; si el patrón cambia, actualizamos el bloqueo y esperamos; si no se encuentra ningún patrón, restablecemos «g_patternFormationBar» y «g_lockedPatternX», asegurándonos de que los patrones Crab confirmados activen operaciones con una gestión de riesgos precisa. Por último, solo tenemos que eliminar los patrones del gráfico cuando retiremos el programa del 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
}

Aquí implementamos el controlador de eventos OnDeinit para garantizar una limpieza adecuada cuando el EA se elimina del gráfico. En primer lugar, eliminamos todos los objetos del gráfico que tengan el prefijo «CR_» mediante ObjectsDeleteAll para borrar elementos visuales como triángulos, líneas de tendencia y etiquetas asociadas a los patrones «Crab». A continuación, procedemos a redimensionar el array «pivots» a 0 con ArrayResize para borrar los datos de pivotes almacenados. A continuación, restablecemos «g_patternFormationBar» a -1 y «g_lockedPatternX» a 0 para borrar las variables de seguimiento de patrones. Por último, llamamos a ChartRedraw para actualizar el gráfico, asegurándonos de que refleje la eliminación de todos los objetos y datos. Esto garantiza una salida limpia, liberando recursos y evitando que queden elementos residuales. Tras la compilación, obtenemos el siguiente resultado.

Señal bajista:

SEÑAL BAJISTA

Señal alcista:

SEÑAL ALCISTA

En la imagen podemos ver que trazamos el patrón armónico y que, una vez confirmado, seguimos pudiendo operar en consecuencia, logrando así nuestro objetivo de identificar, trazar y operar con el patrón. Lo que queda por hacer es realizar pruebas retrospectivas del programa, y eso se trata en la siguiente sección.


Backtesting

Tras realizar un exhaustivo backtesting, hemos obtenido los siguientes resultados.

Gráfico de backtesting:

GRÁFICO

Informe de backtesting:

INFORME


Conclusión

En conclusión, hemos desarrollado un sistema patrón Crab en MQL5, que aprovecha la evolución del precio para identificar patrones armónicos «Crab» alcistas y bajistas con proporciones de Fibonacci precisas, automatizando las operaciones con puntos de entrada, stop loss y take-profit multinivel calculados, que se visualizan mediante objetos gráficos dinámicos como triángulos y líneas de tendencia.

Descargo de responsabilidad: Este artículo tiene fines exclusivamente educativos. Operar en los mercados financieros conlleva riesgos financieros significativos, y la volatilidad del mercado puede ocasionar pérdidas. Es fundamental realizar pruebas retrospectivas exhaustivas y una gestión de riesgos cuidadosa antes de implementar este programa en mercados reales.

Aprovechando los conceptos y la implementación presentados, puedes adaptar este sistema de patrón Crab a su estilo de negociación, mejorando así sus estrategias algorítmicas. ¡Felices operaciones! 

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/19099

Archivos adjuntos |
Crab_Pattern_EA.mq5 (47.93 KB)
Andrey F. Zelinsky
Andrey F. Zelinsky | 15 may 2026 en 13:17
Maxim Kuznetsov #:

en realidad, nada :-)

El saliente derecho se ha aumentado en 1,68, es decir, es la misma mariposa, pero con un fibonacci más añadido. Si lo vas a hacer, el código es el mismo.

Cualquier patrón no tiene que ver con el código, sino con la señal.

Los tipos de señales de los patrones son diferentes, lo que significa que un patrón es adecuado para una situación y otro patrón, para otra.

Vitaly Muzichenko
Vitaly Muzichenko | 15 may 2026 en 13:45
Andrey F. Zelinsky #:

Cualquier patrón no tiene que ver con el código, sino con la señal.

Los patrones tienen distintos tipos de señales, lo que significa que un patrón es adecuado para una situación y otro patrón, para otra.

... y, aun así, el resultado sigue siendo el mismo: 50/50
Andrey F. Zelinsky
Andrey F. Zelinsky | 15 may 2026 en 13:59
Vitaly Muzichenko #:
... y, sin embargo, el resultado sigue siendo el mismo: 50/50

en el artículo sobre el patrón, no sobre la filosofía de la señal.

La filosofía de la señal se ha descrito, analizado y explicado de forma muy clara hace tiempo, por ejemplo, en la película de culto «El lobo de Wall Street», en el diálogo entre Mark Hann y Jordan Belfort:

El vídeo con la traducción al ruso está aquí: https://www.youtube.com/watch?v=fRVwyZ6t7Fk&t=112s

Cita:«Nadie sabe si el precio de las acciones subirá, bajará, se mantendrá estable o dará vueltas; todo eso es humera, quimera, solo un humer lo entenderá».

Andrey F. Zelinsky
Andrey F. Zelinsky | 15 may 2026 en 14:16
Vitaly Muzichenko #:
... y, aun así, el resultado sigue siendo el mismo: 50/50

solo que, además de una señal del patrón, hay un conjunto de señales de distintos tipos (señales de entrada/salida, de filtrado) y hay gestión del capital; en definitiva, la probabilidad final dista mucho de ser 50/50

christian balsalobre de castro
christian balsalobre de castro | 6 oct 2026 en 08:43
Lo del backtest exhaustivo, no será el que está publicado no? Vamos, con esas pocas operaciones no entiendo lo de backtest exhaustivo, es un tanto pobre.
Red neuronal en la práctica: Iniciando la cadena Red neuronal en la práctica: Iniciando la cadena
Ha llegado el momento de empezar a ver y entender cómo conectar perceptrones en una cadena. Esto nos permitirá implementar lo que conocemos como una red de perceptrones. Esta red constituye la base de todo aquello que conoces y utilizas como alguna forma de «inteligencia artificial». Sin embargo, construir una red de este tipo plantea diversos desafíos. Aquí comenzaremos a ver cuáles son.
Del nivel básico al intermedio: Objetos y subventanas (III) Del nivel básico al intermedio: Objetos y subventanas (III)
Este artículo describe un flujo seguro para manejar la eliminación de objetos gráficos: interceptar eventos, recrear el objeto y recuperar el conjunto mínimo de propiedades. Mostramos cómo evitar errores al trabajar con punteros, impedir recreaciones indebidas durante el cierre y guardar la posición y las dimensiones, actualizándolas mediante DRAG/CHANGE. De este modo, el indicador se mantiene íntegro y el usuario conserva parte de sus personalizaciones.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Del nivel básico al intermedio: Objetos y subventanas (II) Del nivel básico al intermedio: Objetos y subventanas (II)
Este artículo explica cómo capturar y gestionar la eliminación de objetos del gráfico en MQL5 utilizando eventos de MetaTrader 5. Al detectar la eliminación de un objeto creado por el indicador, el código elimina la instancia correspondiente para evitar inconsistencias y subventanas residuales. El enfoque utiliza nombres únicos y un archivo de cabecera, priorizando la simplicidad y la previsibilidad del comportamiento.