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
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:
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 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.

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.

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.

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:

Patrón 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 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:

Informe de backtesting:

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
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Red neuronal en la práctica: Iniciando la cadena
Del nivel básico al intermedio: Objetos y subventanas (III)
Particularidades del trabajo con números del tipo double en MQL4
Del nivel básico al intermedio: Objetos y subventanas (II)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
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.
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, 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á».
... 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
Artículo publicado 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:
Autor: Allan Munene Mutiiria