English Русский Deutsch 日本語
preview
Automatización de estrategias de trading en MQL5 (parte 31): Creación de un sistema de patrón armónico de 3 Drivesbasado en la acción del precio

Automatización de estrategias de trading en MQL5 (parte 31): Creación de un sistema de patrón armónico de 3 Drivesbasado en la acción del precio

MetaTrader 5 — Trading |
38 3
Allan Munene Mutiiria
Allan Munene Mutiiria

Introducción

En nuestro artículo anterior (parte 30), desarrollamos un sistema de patrón AB=CD en MetaQuotes Language 5 (MQL5) que detectaba patrones armónicos AB=CD alcistas y bajistas utilizando ratios de Fibonacci, automatizaba las operaciones con niveles calculados de entrada, stop loss y take profit, y los visualizaba mediante objetos gráficos como triángulos y líneas de tendencia. En la parte 31, vamos a crear un sistema para el patrón de 3 Drives que identifica patrones armónicos alcistas y bajistas de 3 Drives mediante puntos de pivote y retrocesos y extensiones de Fibonacci específicos. El sistema ejecuta operaciones con opciones personalizables de take profit y stop loss, y además incorpora triángulos, líneas de tendencia y etiquetas para representar claramente los patrones. Hoy abordaremos los siguientes temas:

  1. Comprender el marco del patrón armónico de 3 Drives
  2. Implementación en MQL5
  3. Backtesting
  4. Conclusión

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


Comprender el marco del patrón armónico de 3 Drives

El patrón de 3 Drives es una formación armónica de trading definida por seis puntos de oscilación clave —A, B, C, D, E y F— que se presenta en variantes alcistas y bajistas, y está diseñada para identificar zonas de reversión mediante una serie de tres impulsos de precio consecutivos con retrocesos de Fibonacci y extensiones de Fibonacci específicos. En un patrón alcista de 3 Drives, la estructura forma una secuencia máximo-mínimo-máximo-mínimo-máximo-mínimo, en la que A es un máximo de oscilación, B un mínimo de oscilación (primer impulso), C un máximo de oscilación, D un mínimo de oscilación (segundo impulso), E un máximo de oscilación y F un mínimo de oscilación (tercer impulso, por debajo de D y B), con cada retroceso (BC, DE) en aproximadamente el 0,618 o el 0,786 del impulso anterior y cada impulso (CD, EF) extendiéndose entre el 1,13 y el 1,618 del retroceso anterior; un patrón bajista invierte esta secuencia, con F por encima de D y B. Aquí podemos ver una representación gráfica de los patrones:

Patrón armónico bajista de 3 Drives:

PATRÓN ARMÓNICO BAJISTA DE 3 IMPULSOS

Patrón armónico alcista de 3 Drives:

PATRÓN ARMÓNICO ALCISTA DE 3 IMPULSOS

Nuestro enfoque consiste en detectar estos pivotes de oscilación dentro de un rango de barras especificado, validar los tramos del patrón según los criterios de Fibonacci, visualizar la estructura A-B-C-D-E-F con objetos gráficos como triángulos y líneas de tendencia, y ejecutar operaciones en el punto F con niveles de stop loss personalizables (basados en Fibonacci o fijos) y niveles de take profit (0,382, 0,618 o pivote E) para sacar partido de las reversiones previstas. ¡Procedamos ahora a la implementación!


Implementación en MQL5

Para crear el programa en MQL5, abra MetaEditor, vaya al Navegador, localice la carpeta «Indicadores», haga clic en la pestaña «Nuevo» y siga las instrucciones para crear el archivo. Una vez creado, en el entorno de programación, tendremos que declarar algunas variables globales que usaremos a lo largo de todo el programa.

//+------------------------------------------------------------------+
//|                                          3 Drives 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 3 Drives Strategy"
#property strict

//--- Include the trading library for order functions
#include <Trade\Trade.mqh>   //--- Include Trade library
CTrade obj_Trade;            //--- Instantiate a obj_Trade object
//--- Enumeration for TP levels
enum ENUM_TAKE_PROFIT_LEVEL {
   TP1 = 1, // 0.382 Fibonacci Retracement
   TP2 = 2, // 0.618 Fibonacci Retracement
   TP3 = 3  // Pivot E Price
};
//--- Enumeration for SL types
enum ENUM_STOP_LOSS_TYPE {
   SL_FIBO = 1, // Fibonacci Extension
   SL_FIXED = 2 // Fixed Points
};
//--- Input parameters for user configuration
input int PivotLeft = 5;                              // Number of bars to the left for pivot check
input int PivotRight = 5;                             // Number of bars to the right for pivot check
input double Tolerance = 0.10;                        // Allowed deviation (10% of move)
input double LotSize = 0.01;                          // Lot size for new orders
input bool AllowTrading = true;                       // Enable or disable trading
input ENUM_TAKE_PROFIT_LEVEL TakeProfitLevel = TP2;   // Take Profit Level
input ENUM_STOP_LOSS_TYPE StopLossType = SL_FIBO;     // Stop Loss Type
input double SL_FiboExtension = 1.618;                // Fibonacci Extension for SL
input double SL_FixedPoints = 50;                     // Fixed Points for SL (in points)
//---------------------------------------------------------------------------
//--- 3 Drives pattern definition:
//
//--- Bullish 3 Drives:
//--- Pivots (A-B-C-D-E-F): A swing high, B swing low (drive 1), C swing high, D swing low (drive 2), E swing high, F swing low (drive 3).
//--- Retracements at 0.618, drives at 1.272 extensions.
//
//--- Bearish 3 Drives:
//--- Pivots (A-B-C-D-E-F): A swing low, B swing high (drive 1), C swing low, D swing high (drive 2), E swing low, F swing high (drive 3).
//--- Retracements at 0.618, drives at 1.272 extensions.
//---------------------------------------------------------------------------
//--- Structure for a pivot point
struct Pivot {
   datetime time; //--- Bar time of the pivot
   double price;  //--- Pivot price (High for swing high, low for swing low)
   bool isHigh;   //--- True if swing high; false if swing low
};
//--- Global dynamic array for storing pivots in chronological order
Pivot pivots[]; //--- Declare a dynamic array to hold identified pivot points
//--- Global variables to lock in a pattern (avoid trading on repaint)
int g_patternFormationBar = -1; //--- Bar index where the pattern was formed (-1 means none)
datetime g_lockedPatternA = 0;  //--- The key A pivot time for the locked pattern
//--- Global array to track traded patterns (using A.time as identifier)
datetime tradedPatterns[];

Para sentar las bases del patrón de 3 Drives, en primer lugar, incluimos la biblioteca «<Trade\Trade.mqh>» e instanciamos «obj_Trade» como objeto CTrade para gestionar operaciones de trading, como la ejecución de órdenes de compra y venta. A continuación, definimos las enumeraciones «ENUM_TAKE_PROFIT_LEVEL» (TP1 para 0,382, TP2 para 0,618, TP3 para el precio del pivote E) y «ENUM_STOP_LOSS_TYPE» (SL_FIBO para la extensión de Fibonacci, SL_FIXED para puntos fijos) con el fin de permitir una configuración flexible de las operaciones, y establecemos los parámetros de entrada: «PivotLeft» y «PivotRight» en 5 barras para la detección de pivotes, «Tolerance» en 0,10 para la desviación de Fibonacci, «LotSize» en 0,01, «AllowTrading» como «true», «TakeProfitLevel» como TP2, «StopLossType» como SL_FIBO, «SL_FiboExtension» en 1,618 y «SL_FixedPoints» en 50.

Acto seguido, definimos la estructura «Pivot» con «time», «price» e «isHigh» para almacenar los puntos de oscilación, declaramos «pivots» como un array dinámico e inicializamos las variables globales «g_patternFormationBar» en -1, «g_lockedPatternA» en 0 para el bloqueo del patrón y «tradedPatterns» como un array para realizar un seguimiento de los patrones ya operados utilizando la hora de A. Esta configuración ofrece el marco básico para detectar patrones de 3 Drives y operar con ellos. Para la visualización, podemos definir funciones para dibujar líneas, etiquetas y triángulos.

//+------------------------------------------------------------------+
//| Helper: Draw a filled triangle                                   |
//+------------------------------------------------------------------+
void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3, color cl, int width, bool fill, bool back) {
   //--- Attempt to create a triangle object with three coordinate points
   if(ObjectCreate(0, name, OBJ_TRIANGLE, 0, t1, p1, t2, p2, t3, p3)) {
      //--- Set the triangle's color
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);
      //--- Set the triangle's line style to solid
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      //--- Set the line width of the triangle
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
      //--- Determine if the triangle should be filled
      ObjectSetInteger(0, name, OBJPROP_FILL, fill);
      //--- Set whether the object is drawn in the background
      ObjectSetInteger(0, name, OBJPROP_BACK, back);
   }
}
//+------------------------------------------------------------------+
//| Helper: Draw a trend line                                        |
//+------------------------------------------------------------------+
void DrawTrendLine(string name, datetime t1, double p1, datetime t2, double p2, color cl, int width, int style) {
   //--- Create a trend line object connecting two points
   if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) {
      //--- Set the trend line's color
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);
      //--- Set the trend line's style (solid, dotted, etc.)
      ObjectSetInteger(0, name, OBJPROP_STYLE, style);
      //--- Set the width of the trend line
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   }
}
//+------------------------------------------------------------------+
//| Helper: Draw a dotted trend line                                 |
//+------------------------------------------------------------------+
void DrawDottedLine(string name, datetime t1, double p, datetime t2, color lineColor) {
   //--- Create a horizontal trend line at a fixed price level with dotted style
   if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p)) {
      //--- Set the dotted line's color
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
      //--- Set the line style to dotted
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
      //--- Set the line width to 1
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
   }
}
//+------------------------------------------------------------------+
//| Helper: Draw anchored text label (for pivots)                    |
//| If isHigh is true, anchor at the bottom (label appears above);   |
//| if false, anchor at the top (label appears below).               |
//+------------------------------------------------------------------+
void DrawTextEx(string name, string text, datetime t, double p, color cl, int fontsize, bool isHigh) {
   //--- Create a text label object at the specified time and price
   if(ObjectCreate(0, name, OBJ_TEXT, 0, t, p)) {
      //--- Set the text of the label
      ObjectSetString(0, name, OBJPROP_TEXT, text);
      //--- Set the color of the text
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);
      //--- Set the font size for the text
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize);
      //--- Set the font type and style
      ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold");
      //--- Anchor the text depending on whether it's a swing high or low
      if(isHigh)
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
      else
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_TOP);
      //--- Center-align the text
      ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER);
   }
}

A continuación, implementamos funciones de visualización para crear representaciones gráficas claras del patrón armónico de 3 Drives y sus niveles de trading. En primer lugar, desarrollamos la función «DrawTriangle», que utiliza ObjectCreate para dibujar un triángulo relleno (OBJ_TRIANGLE) definido por tres puntos con tiempos («t1», «t2», «t3») y precios («p1», «p2», «p3»), estableciendo OBJPROP_COLOR en el color especificado, «OBJPROP_STYLE» en «STYLE_SOLID», «OBJPROP_WIDTH» en el ancho indicado, «OBJPROP_FILL» para activar o desactivar el relleno, y «OBJPROP_BACK» para establecer la posición en segundo plano o en primer plano mediante la función ObjectSetInteger. Después creamos la función «DrawTrendLine», que traza una línea de tendencia («OBJ_TREND») entre dos puntos.

A continuación, implementamos la función «DrawDottedLine», que crea una línea horizontal de puntos (OBJ_TREND) en un precio determinado. Por último, desarrollamos la función «DrawTextEx», que crea una etiqueta de texto (OBJ_TEXT) en las coordenadas («t», «p») usando «ObjectCreate», estableciendo «OBJPROP_TEXT» en el texto especificado, «OBJPROP_COLOR», «OBJPROP_FONTSIZE» y «OBJPROP_FONT» en «Arial Bold» mediante ObjectSetString y «ObjectSetInteger», anclándola por encima para los máximos de oscilación o por debajo para los mínimos en función de «isHigh» con «OBJPROP_ANCHOR», y centrándola con «OBJPROP_ALIGN». Ahora podemos pasar al controlador de eventos OnTick y tratar de identificar puntos de pivote que podamos utilizar más adelante para el reconocimiento de patrones. Esta es la lógica que usamos para lograrlo.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   //--- Declare a static variable to store the time of the last processed bar
   static datetime lastBarTime = 0;
   //--- Get the time of the current confirmed bar
   datetime currentBarTime = iTime(_Symbol, _Period, 1);
   //--- If the current bar time is the same as the last processed, exit
   if(currentBarTime == lastBarTime)
      return;
   //--- Update the last processed bar time
   lastBarTime = currentBarTime;
 
   //--- Clear the pivot array for fresh analysis
   ArrayResize(pivots, 0);
   //--- Get the total number of bars available on the chart
   int barsCount = Bars(_Symbol, _Period);
   //--- Define the starting index for pivot detection (ensuring enough left bars)
   int start = PivotLeft;
   //--- Define the ending index for pivot detection (ensuring enough right bars)
   int end = barsCount - PivotRight;
 
   //--- Loop through bars from 'end-1' down to 'start' to find pivot points
   for(int i = end - 1; i >= start; i--) {
      //--- Assume current bar is both a potential swing high and swing low
      bool isPivotHigh = true;
      bool isPivotLow = true;
      //--- Get the high and low of the current bar
      double currentHigh = iHigh(_Symbol, _Period, i);
      double currentLow = iLow(_Symbol, _Period, i);
      //--- Loop through the window of bars around the current bar
      for(int j = i - PivotLeft; j <= i + PivotRight; j++) {
         //--- Skip if the index is out of bounds
         if(j < 0 || j >= barsCount)
            continue;
         //--- Skip comparing the bar with itself
         if(j == i)
            continue;
         //--- If any bar in the window has a higher high, it's not a swing high
         if(iHigh(_Symbol, _Period, j) > currentHigh)
            isPivotHigh = false;
         //--- If any bar in the window has a lower low, it's not a swing low
         if(iLow(_Symbol, _Period, j) < currentLow)
            isPivotLow = false;
      }
      //--- If the current bar qualifies as either a swing high or swing low
      if(isPivotHigh || isPivotLow) {
         //--- Create a new pivot structure
         Pivot p;
         //--- Set the pivot's time
         p.time = iTime(_Symbol, _Period, i);
         //--- Set the pivot's price depending on whether it is a high or low
         p.price = isPivotHigh ? currentHigh : currentLow;
         //--- Set the pivot type (true for swing high, false for swing low)
         p.isHigh = isPivotHigh;
         //--- Get the current size of the pivots array
         int size = ArraySize(pivots);
         //--- Increase the size of the pivots array by one
         ArrayResize(pivots, size + 1);
         //--- Add the new pivot to the array
         pivots[size] = p;
      }
   }
}

Aquí implementamos la lógica inicial del controlador de eventos OnTick. En primer lugar, declaramos una variable estática «lastBarTime», inicializada en 0, para monitorear la última barra procesada y compararla con «currentBarTime», obtenida de iTime con desplazamiento 1 para el símbolo y el periodo actuales; si no ha cambiado, salimos para evitar un procesamiento redundante, y actualizamos «lastBarTime» cuando se detecta una nueva barra. A continuación, eliminamos el array «pivots» con ArrayResize para garantizar un análisis actualizado.

Luego recuperamos el número total de barras con la función Bars, establecemos el rango de detección de pivotes con «start» como «PivotLeft» y «end» como el número 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áximo y mínimo mediante iHigh e iLow, y validamos el pivote comprobando las barras adyacentes dentro de «PivotLeft» y «PivotRight» con «iHigh» e «iLow», invalidando 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 cumple los requisitos para ser considerada un pivote, creamos una estructura «Pivot», establecemos su «time» con «iTime», «price» en el máximo o el mínimo según «isPivotHigh», y el indicador «isHigh»; a continuación, la añadimos al array «pivots» utilizando ArrayResize y la almacenamos. Al imprimir la estructura de pivote, obtenemos el siguiente array de datos.

DATOS DE PIVOTES

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

//--- Determine the total number of pivots found
int pivotCount = ArraySize(pivots);
//--- If fewer than six pivots are found, the pattern cannot be formed
if(pivotCount < 6) {
   //--- Reset pattern lock variables
   g_patternFormationBar = -1;
   g_lockedPatternA = 0;
   //--- Exit the OnTick function
   return;
}

//--- Extract the last six pivots as A, B, C, D, E, F
Pivot A = pivots[pivotCount - 6];
Pivot B = pivots[pivotCount - 5];
Pivot C = pivots[pivotCount - 4];
Pivot D = pivots[pivotCount - 3];
Pivot E = pivots[pivotCount - 2];
Pivot F = pivots[pivotCount - 1];

//--- Initialize a flag to indicate if a valid 3 Drives pattern is found
bool patternFound = false;
//--- Initialize pattern type
string patternType = "";
double used_ext = 0.0;
//--- Define fib ratios
double retr_levels[] = {0.618, 0.786};
double ext_levels[] = {1.13, 1.272, 1.618};
//--- Check for the high-low-high-low-high-low (Bullish reversal) structure
if(A.isHigh && (!B.isHigh) && C.isHigh && (!D.isHigh) && E.isHigh && (!F.isHigh)) {
   //--- Calculate drive 1
   double drive1 = A.price - B.price;
   if(drive1 > 0) {
      //--- Retrace A
      double retraceA = C.price - B.price;
      bool valid_retrA = false;
      double used_retrA = 0.0;
      for(int k=0; k<ArraySize(retr_levels); k++) {
         double ideal_retraceA = retr_levels[k] * drive1;
         if(MathAbs(retraceA - ideal_retraceA) <= Tolerance * drive1) {
            valid_retrA = true;
            used_retrA = retr_levels[k];
            break;
         }
      }
      if(valid_retrA) {
         //--- Drive 2
         double drive2 = C.price - D.price;
         bool valid_drive2 = false;
         double used_ext2 = 0.0;
         for(int k=0; k<ArraySize(ext_levels); k++) {
            double ideal_drive2 = ext_levels[k] * retraceA;
            if(MathAbs(drive2 - ideal_drive2) <= Tolerance * retraceA) {
               valid_drive2 = true;
               used_ext2 = ext_levels[k];
               break;
            }
         }
         if(valid_drive2) {
            //--- Retrace B
            double retraceB = E.price - D.price;
            bool valid_retrB = false;
            double used_retrB = 0.0;
            for(int k=0; k<ArraySize(retr_levels); k++) {
               double ideal_retraceB = retr_levels[k] * drive2;
               if(MathAbs(retraceB - ideal_retraceB) <= Tolerance * drive2) {
                  valid_retrB = true;
                  used_retrB = retr_levels[k];
                  break;
               }
            }
            if(valid_retrB) {
               //--- Drive 3
               double drive3 = E.price - F.price;
               bool valid_drive3 = false;
               for(int k=0; k<ArraySize(ext_levels); k++) {
                  double ideal_drive3 = ext_levels[k] * retraceB;
                  if(MathAbs(drive3 - ideal_drive3) <= Tolerance * retraceB) {
                     valid_drive3 = true;
                     used_ext = ext_levels[k];
                     break;
                  }
               }
               if(valid_drive3 && F.price < D.price && D.price < B.price) {
                  patternFound = true;
                  patternType = "Bullish";
               }
            }
         }
      }
   }
}
//--- Check for the low-high-low-high-low-high (Bearish reversal) structure
if((!A.isHigh) && B.isHigh && (!C.isHigh) && D.isHigh && (!E.isHigh) && F.isHigh) {
   //--- Calculate drive 1
   double drive1 = B.price - A.price;
   if(drive1 > 0) {
      //--- Retrace A
      double retraceA = B.price - C.price;
      bool valid_retrA = false;
      double used_retrA = 0.0;
      for(int k=0; k<ArraySize(retr_levels); k++) {
         double ideal_retraceA = retr_levels[k] * drive1;
         if(MathAbs(retraceA - ideal_retraceA) <= Tolerance * drive1) {
            valid_retrA = true;
            used_retrA = retr_levels[k];
            break;
         }
      }
      if(valid_retrA) {
         //--- Drive 2
         double drive2 = D.price - C.price;
         bool valid_drive2 = false;
         double used_ext2 = 0.0;
         for(int k=0; k<ArraySize(ext_levels); k++) {
            double ideal_drive2 = ext_levels[k] * retraceA;
            if(MathAbs(drive2 - ideal_drive2) <= Tolerance * retraceA) {
               valid_drive2 = true;
               used_ext2 = ext_levels[k];
               break;
            }
         }
         if(valid_drive2) {
            //--- Retrace B
            double retraceB = D.price - E.price;
            bool valid_retrB = false;
            double used_retrB = 0.0;
            for(int k=0; k<ArraySize(retr_levels); k++) {
               double ideal_retraceB = retr_levels[k] * drive2;
               if(MathAbs(retraceB - ideal_retraceB) <= Tolerance * drive2) {
                  valid_retrB = true;
                  used_retrB = retr_levels[k];
                  break;
               }
            }
            if(valid_retrB) {
               //--- Drive 3
               double drive3 = F.price - E.price;
               bool valid_drive3 = false;
               for(int k=0; k<ArraySize(ext_levels); k++) {
                  double ideal_drive3 = ext_levels[k] * retraceB;
                  if(MathAbs(drive3 - ideal_drive3) <= Tolerance * retraceB) {
                     valid_drive3 = true;
                     used_ext = ext_levels[k];
                     break;
                  }
               }
               if(valid_drive3 && F.price > D.price && D.price > B.price) {
                  patternFound = true;
                  patternType = "Bearish";
               }
            }
         }
      }
   }
}

En primer lugar, determinamos el número total de pivotes con «ArraySize(pivots)», lo almacenamos en «pivotCount» y salimos si se encuentran menos de 6 pivotes, restableciendo «g_patternFormationBar» y «g_lockedPatternA» a -1 y 0, ya que el patrón de 3 Drives requiere los puntos A, B, C, D, E y F.

Después extraemos los últimos seis pivotes del array «pivots», asignándoles «A» (el más antiguo), «B», «C», «D», «E» y «F» (el más reciente). A continuación, para un patrón alcista (A máximo, B mínimo, C máximo, D mínimo, E máximo, F mínimo), calculamos el impulso 1 («A.price - B.price»), validamos el retroceso A («C.price - B.price» en 0,618 o 0,786 del impulso 1 dentro de «Tolerance»), el impulso 2 («C.price - D.price» en 1,13, 1,272 o 1,618 del retroceso A), el retroceso B («E.price - D.price» en 0,618 o 0,786 del impulso 2) y el impulso 3 («E.price - F.price» en 1,13, 1,272 o 1,618 del retroceso B), asegurándonos de que «F.price < D.price < B.price», estableciendo «patternFound» en true y «patternType» en «Bullish» si es válido, y almacenando la extensión usada («used_ext»). Por último, para un patrón bajista (A mínimo, B máximo, C mínimo, D máximo, E mínimo, F máximo), aplicamos validaciones similares para el impulso 1 («B.price - A.price»), el retroceso A, el impulso 2, el retroceso B y el impulso 3, asegurándonos de que «F.price > D.price > B.price», y estableciendo «patternFound» en true y «patternType» en «Bearish» si es válido. Si se detecta el patrón, podemos pasar a visualizarlo en el gráfico.

//--- If a valid 3 Drives pattern is detected
if(patternFound) {
   //--- Print a message indicating the pattern type and detection time
   Print(patternType, " 3 Drives pattern detected at ", TimeToString(F.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS));
 
   //--- Create a unique prefix for all graphical objects related to this pattern
   string signalPrefix = "3D_" + IntegerToString(A.time);
 
   //--- Choose triangle color based on the pattern type
   color triangleColor = (patternType=="Bullish") ? clrBlue : clrRed;
 
   //--- Draw the first triangle connecting pivots B, C, D
   DrawTriangle(signalPrefix+"_Triangle1", B.time, B.price, C.time, C.price, D.time, D.price,
                triangleColor, 2, true, true);
   //--- Draw the second triangle connecting pivots D, E, F
   DrawTriangle(signalPrefix+"_Triangle2", D.time, D.price, E.time, E.price, F.time, F.price,
                triangleColor, 2, true, true);
}

Aquí iniciamos la visualización de los patrones detectados en el gráfico. En primer lugar, si se detecta un patrón válido («patternFound» es true), registramos la detección con Print, mostrando el «patternType» («Bullish» o «Bearish») y la hora del pivote F formateada con TimeToString, incluyendo la fecha, los minutos y los segundos. A continuación, creamos un identificador único, «signalPrefix», concatenando «3D_» con «A.time», convertido en una cadena mediante IntegerToString, para garantizar que los objetos del gráfico tengan nombres distintos.

Después establecemos «triangleColor» en azul para los patrones alcistas o en rojo para los bajistas, con el fin de diferenciarlos visualmente. Por último, llamamos a «DrawTriangle» dos veces para visualizar el patrón: primero para dibujar el triángulo BCD que conecta los pivotes B, C y D, y después para trazar el triángulo DEF que conecta los pivotes D, E y F, utilizando «signalPrefix» con los sufijos «_Triangle1» y «_Triangle2», los tiempos y precios respectivos de los pivotes, «triangleColor», un ancho de 2, y activando el rellenado y la visualización en segundo plano con indicadores booleanos true. Obtenemos el siguiente resultado.

TRIÁNGULOS CONFIGURADOS

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 el patrón quede completamente delimitado y visible y añadirle una etiqueta que permita identificar más fácilmente los niveles.

//--- Draw boundary trend lines connecting the pivots for clarity
DrawTrendLine(signalPrefix+"_TL_AB", A.time, A.price, B.time, B.price, clrBlack, 2, STYLE_SOLID);
DrawTrendLine(signalPrefix+"_TL_BC", B.time, B.price, C.time, C.price, clrBlack, 2, STYLE_SOLID);
DrawTrendLine(signalPrefix+"_TL_CD", C.time, C.price, D.time, D.price, clrBlack, 2, STYLE_SOLID);
DrawTrendLine(signalPrefix+"_TL_DE", D.time, D.price, E.time, E.price, clrBlack, 2, STYLE_SOLID);
DrawTrendLine(signalPrefix+"_TL_EF", E.time, E.price, F.time, F.price, clrBlack, 2, STYLE_SOLID);

//--- Retrieve the symbol's point size to calculate offsets for text positioning
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
//--- Calculate an offset (15 points) for positioning text above or below pivots
double offset = 15 * point;

//--- Determine the Y coordinate for each pivot label based on its type
double textY_A = (A.isHigh ? A.price + offset : A.price - offset);
double textY_B = (B.isHigh ? B.price + offset : B.price - offset);
double textY_C = (C.isHigh ? C.price + offset : C.price - offset);
double textY_D = (D.isHigh ? D.price + offset : D.price - offset);
double textY_E = (E.isHigh ? E.price + offset : E.price - offset);
double textY_F = (F.isHigh ? F.price + offset : F.price - offset);

//--- Draw text labels for each pivot with appropriate anchoring
DrawTextEx(signalPrefix+"_Text_A", "A", A.time, textY_A, clrBlack, 11, A.isHigh);
DrawTextEx(signalPrefix+"_Text_B", "B", B.time, textY_B, clrBlack, 11, B.isHigh);
DrawTextEx(signalPrefix+"_Text_C", "C", C.time, textY_C, clrBlack, 11, C.isHigh);
DrawTextEx(signalPrefix+"_Text_D", "D", D.time, textY_D, clrBlack, 11, D.isHigh);
DrawTextEx(signalPrefix+"_Text_E", "E", E.time, textY_E, clrBlack, 11, E.isHigh);
DrawTextEx(signalPrefix+"_Text_F", "F", F.time, textY_F, clrBlack, 11, F.isHigh);

//--- Calculate the central label's time as the midpoint between pivots A and D
datetime centralTime = (A.time + D.time) / 2;
//--- Set the central label's price at pivot F's price
double centralPrice = F.price;
//--- Create the central text label indicating the pattern type
if(ObjectCreate(0, signalPrefix+"_Text_Center", OBJ_TEXT, 0, centralTime, centralPrice)) {
   ObjectSetString(0, signalPrefix+"_Text_Center", OBJPROP_TEXT,
      (patternType=="Bullish") ? "Bullish 3 Drives" : "Bearish 3 Drives");
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_FONTSIZE, 11);
   ObjectSetString(0, signalPrefix+"_Text_Center", OBJPROP_FONT, "Arial Bold");
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_ALIGN, ALIGN_CENTER);
}

Mejoramos aún más la visualización de los patrones detectados añadiendo objetos gráficos detallados que representan con claridad la estructura del patrón. En primer lugar, dibujamos cinco líneas de tendencia de trazo continuo mediante «DrawTrendLine» con el «signalPrefix» único para conectar los puntos de pivote clave: AB, BC, CD, DE y EF, utilizando las horas y los precios de los pivotes (por ejemplo, «A.time», «A.price»), estableciendo OBJPROP_COLOR en «clrBlack», «OBJPROP_WIDTH» en 2 y «OBJPROP_STYLE» en «STYLE_SOLID» con ObjectSetInteger para delinear los tramos del patrón. A continuación, recuperamos el tamaño en puntos del símbolo con SymbolInfoDouble(_Symbol, SYMBOL_POINT) y calculamos un desplazamiento de 15 puntos para el posicionamiento de las etiquetas, determinando las coordenadas Y («textY_A», «textY_B», «textY_C», «textY_D», «textY_E», «textY_F») sumando o restando el desplazamiento en función de si cada pivote es un máximo de oscilación («isHigh» verdadero) o un mínimo, para colocar las etiquetas por encima de los máximos o por debajo de los mínimos.

Acto seguido, utilizamos «DrawTextEx» para crear etiquetas de texto para los pivotes A, B, C, D, E y F con «signalPrefix» y sufijos como «_Text_A», mostrando la letra correspondiente, situada en la hora 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 de la etiqueta central en «centralTime» como el punto medio entre «A.time» y «D.time», y «centralPrice» en «F.price», creando un objeto de texto con «ObjectCreate» denominado «signalPrefix + '_Text_Center'», estableciendo OBJPROP_TEXT en «patrón alcista de 3 Drives» o «patrón bajista de 3 Drives» en función de «patternType», y configurando «OBJPROP_COLOR» en «clrBlack», «OBJPROP_FONTSIZE» en 11, «OBJPROP_FONT» en «Arial Bold» y «OBJPROP_ALIGN» en «ALIGN_CENTER» mediante ObjectSetString y «ObjectSetInteger». Esta lógica garantiza una representación visual completa de la estructura y el tipo del patrón de 3 Drives en el gráfico. Al ejecutar el programa, esta es una visualización del resultado que obtenemos.

PATRÓN CON ETIQUETAS Y LÍNEAS DE CONEXIÓN

En la imagen podemos ver que hemos añadido las líneas de conexión y las etiquetas al patrón, lo que lo hace más claro e ilustrativo. Lo siguiente que tenemos que hacer es determinar los niveles de trading del patrón.

//--- Define start and end times for drawing horizontal dotted lines for trade levels
datetime lineStart = F.time;
datetime lineEnd = F.time + PeriodSeconds(_Period)*2;

//--- Declare variables for entry price and take profit levels
double entryPriceLevel, TP1Level, TP2Level, TP3Level;
//--- Calculate pattern range (drive 3 length)
double patternRange = (patternType=="Bullish") ? (E.price - F.price) : (F.price - E.price);
//--- Calculate trade levels based on whether the pattern is Bullish or Bearish
if(patternType=="Bullish") { //--- Bullish → BUY signal
   //--- Set entry at the pattern tip (F price)
   entryPriceLevel = F.price;
   //--- Set TP3 at pivot E's price
   TP3Level = E.price;
   //--- Set TP1 at 0.382 fib retrace from F to E
   TP1Level = F.price + 0.382 * patternRange;
   //--- Set TP2 at 0.618 fib retrace from F to E
   TP2Level = F.price + 0.618 * patternRange;
} else { //--- Bearish → SELL signal
   //--- Set entry at the pattern tip (F price)
   entryPriceLevel = F.price;
   //--- Set TP3 at pivot E's price
   TP3Level = E.price;
   //--- Set TP1 at 0.382 fib retrace from F to E
   TP1Level = F.price - 0.382 * patternRange;
   //--- Set TP2 at 0.618 fib retrace from F to E
   TP2Level = F.price - 0.618 * patternRange;
}

//--- Draw dotted horizontal lines to represent the entry and TP levels
DrawDottedLine(signalPrefix+"_EntryLine", lineStart, entryPriceLevel, lineEnd, clrMagenta);
DrawDottedLine(signalPrefix+"_TP1Line", lineStart, TP1Level, lineEnd, clrForestGreen);
DrawDottedLine(signalPrefix+"_TP2Line", lineStart, TP2Level, lineEnd, clrGreen);
DrawDottedLine(signalPrefix+"_TP3Line", lineStart, TP3Level, lineEnd, clrDarkGreen);

//--- Define a label time coordinate positioned just to the right of the dotted lines
datetime labelTime = lineEnd + PeriodSeconds(_Period)/2;

//--- Construct the entry label text with the price
string entryLabel = (patternType=="Bullish") ? "BUY (" : "SELL (";
entryLabel += DoubleToString(entryPriceLevel, _Digits) + ")";
//--- Draw the entry label on the chart
DrawTextEx(signalPrefix+"_EntryLabel", entryLabel, labelTime, entryPriceLevel, clrMagenta, 11, true);

//--- Construct and draw the TP1 label
string tp1Label = "TP1 (" + DoubleToString(TP1Level, _Digits) + ")";
DrawTextEx(signalPrefix+"_TP1Label", tp1Label, labelTime, TP1Level, clrForestGreen, 11, true);

//--- Construct and draw the TP2 label
string tp2Label = "TP2 (" + DoubleToString(TP2Level, _Digits) + ")";
DrawTextEx(signalPrefix+"_TP2Label", tp2Label, labelTime, TP2Level, clrGreen, 11, true);

//--- Construct and draw the TP3 label
string tp3Label = "TP3 (" + DoubleToString(TP3Level, _Digits) + ")";
DrawTextEx(signalPrefix+"_TP3Label", tp3Label, labelTime, TP3Level, clrDarkGreen, 11, true);

Para definir y visualizar los niveles de trading del patrón detectado, establecemos «lineStart» en la hora del pivote F («F.time») y «lineEnd» en dos periodos más adelante utilizando «PeriodSeconds(_Period) * 2», y declaramos las variables «entryPriceLevel», «TP1Level», «TP2Level» y «TP3Level» para los cálculos de las operaciones de trading. A continuación, calculamos «patternRange» como la longitud del tercer impulso («E.price - F.price» para un patrón alcista, «F.price - E.price» para uno bajista); para un patrón alcista, establecemos «entryPriceLevel» en «F.price», «TP3Level» en «E.price», «TP1Level» en «F.price + 0.382 * patternRange» y «TP2Level» en «F.price + 0.618 * patternRange»; para un patrón bajista, establecemos «entryPriceLevel» en «F.price», «TP3Level» en «E.price», «TP1Level» en «F.price - 0.382 * patternRange» y «TP2Level» en «F.price - 0.618 * patternRange».

Después 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 take profit 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 periodo, creamos los textos de las etiquetas con los precios formateados mediante DoubleToString (por ejemplo, «SELL (price)» o «BUY (price)» para la entrada, «TP1 (price)», 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 resultado que sigue.

Patrón bajista:

PATRÓN BAJISTA

Patrón alcista:

PATRÓN ALCISTA

En las imágenes se puede observar que hemos representado correctamente los niveles de trading. Lo que tenemos que hacer ahora es pasar a la ejecución real de las operaciones.

//--- Retrieve the index of the current bar
int currentBarIndex = Bars(_Symbol, _Period) - 1;
//--- If no pattern has been previously locked, lock the current pattern formation
if(g_patternFormationBar == -1) {
   g_patternFormationBar = currentBarIndex;
   g_lockedPatternA = A.time;
   //--- Print a message that the pattern is detected and waiting for confirmation
   Print("Pattern detected on bar ", currentBarIndex, ". Waiting for confirmation on next bar.");
   return;
}
//--- If still on the same formation bar, the pattern is considered to be repainting
if(currentBarIndex == g_patternFormationBar) {
   Print("Pattern is repainting; still on locked formation bar ", currentBarIndex, ". No trade yet.");
   return;
}
//--- If we are on a new bar compared to the locked formation
if(currentBarIndex > g_patternFormationBar) {
   //--- Check if the locked pattern still corresponds to the same A pivot
   if(g_lockedPatternA == A.time) {
      Print("Confirmed pattern (locked on bar ", g_patternFormationBar, "). Opening trade on bar ", currentBarIndex, ".");
      //--- Update the pattern formation bar to the current bar
      g_patternFormationBar = currentBarIndex;
      //--- Only proceed with trading if allowed and if there is no existing position
      if(AllowTrading && !PositionSelect(_Symbol)) {
         //--- Check if this pattern has already been traded
         bool alreadyTraded = false;
         for(int k = 0; k < ArraySize(tradedPatterns); k++) {
            if(tradedPatterns[k] == A.time) {
               alreadyTraded = true;
               break;
            }
         }
         if(alreadyTraded) {
            Print("This pattern has already been traded. No new trade executed.");
            return;
         }
         double entryPriceTrade = 0, stopLoss = 0, takeProfit = 0;
         point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
         bool tradeResult = false;
         //--- Select TP level based on user input
         switch(TakeProfitLevel) {
            case TP1:
               takeProfit = TP1Level;
               break;
            case TP2:
               takeProfit = TP2Level;
               break;
            case TP3:
               takeProfit = TP3Level;
               break;
            default:
               takeProfit = TP2Level; // Fallback to TP2
         }
         //--- Calculate SL based on user-selected method
         if(patternType=="Bullish") { //--- BUY signal
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
            if(StopLossType == SL_FIBO) {
               double third_drive = E.price - F.price;
               stopLoss = F.price - (SL_FiboExtension - 1.0) * third_drive;
            } else { // SL_FIXED
               stopLoss = entryPriceTrade - SL_FixedPoints * point;
            }
            // Ensure SL is below entry for BUY
            if(stopLoss >= entryPriceTrade) {
               stopLoss = entryPriceTrade - 10 * point;
            }
            tradeResult = obj_Trade.Buy(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "3 Drives Signal");
            if(tradeResult)
               Print("Buy order opened successfully.");
            else
               Print("Buy order failed: ", obj_Trade.ResultRetcodeDescription());
         }
         //--- For a Bearish pattern, execute a SELL trade
         else if(patternType=="Bearish") { //--- SELL signal
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_BID);
            if(StopLossType == SL_FIBO) {
               double third_drive = F.price - E.price;
               stopLoss = F.price + (SL_FiboExtension - 1.0) * third_drive;
            } else { // SL_FIXED
               stopLoss = entryPriceTrade + SL_FixedPoints * point;
            }
            // Ensure SL is above entry for SELL
            if(stopLoss <= entryPriceTrade) {
               stopLoss = entryPriceTrade + 10 * point;
            }
            tradeResult = obj_Trade.Sell(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "3 Drives Signal");
            if(tradeResult)
               Print("Sell order opened successfully.");
            else
               Print("Sell order failed: ", obj_Trade.ResultRetcodeDescription());
         }
         //--- If trade was successful, mark the pattern as traded
         if(tradeResult) {
            int size = ArraySize(tradedPatterns);
            ArrayResize(tradedPatterns, size + 1);
            tradedPatterns[size] = A.time;
         }
      }
      else {
         //--- If a position is already open, do not execute a new trade
         Print("A position is already open for ", _Symbol, ". No new trade executed.");
      }
   }
   else {
      //--- If the pattern has changed, update the lock with the new formation bar and A pivot
      g_patternFormationBar = currentBarIndex;
      g_lockedPatternA = A.time;
      Print("Pattern has changed; updating lock on bar ", currentBarIndex, ". Waiting for confirmation.");
      return;
   }
}
else {
   //--- If no valid 3 Drives pattern is detected, reset the pattern lock variables
   g_patternFormationBar = -1;
   g_lockedPatternA = 0;
}

Finalizamos la implementación gestionando la ejecución de operaciones de trading y la confirmación del patrón detectado. 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 «currentBarIndex» a «g_patternFormationBar», bloqueamos la hora del pivote A en «g_lockedPatternA» con «A.time», registramos la detección con «Print», indicando que se espera la confirmación, y salimos.

Entonces, si seguimos en la barra de formación («currentBarIndex == g_patternFormationBar»), registramos el repintado y salimos para evitar operaciones de trading prematuras. Por último, si se ha formado una nueva barra («currentBarIndex > g_patternFormationBar») y el pivote A coincide con «g_lockedPatternA», confirmamos el patrón, lo registramos, actualizamos «g_patternFormationBar» y comprobamos si está permitido operar con «AllowTrading» y que no existan posiciones abiertas mediante la función PositionSelect; verificamos que el patrón no se haya operado ya comprobando «tradedPatterns», seleccionamos el nivel de take profit («TP1Level», «TP2Level» o «TP3Level») en función de «TakeProfitLevel», calculamos el stop loss utilizando «SL_FIBO» («F.price ± (SL_FiboExtension - 1.0) * third_drive») o «SL_FIXED» («entryPriceTrade ± SL_FixedPoints * point»), nos aseguramos de que el stop loss sea válido, ejecutamos una orden de compra o venta con «obj_Trade.Buy» o «obj_Trade.Sell» usando «LotSize» y «3 Drives Signal», registramos el éxito o el fallo y marcamos el patrón como operado en «tradedPatterns»; si no está permitido operar, existe una posición o el patrón ya se ha operado, registramos que no se abre ninguna operación; si el patrón cambia, actualizamos el bloqueo y esperamos; si no se encuentra ningún patrón, restablecemos las variables globales. Tras la compilación, obtenemos el resultado que sigue.

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, podemos operar en consecuencia, logrando así nuestro objetivo de identificar, trazar y operar con el patrón. Lo único que queda por hacer es el backtesting del programa, y eso se trata en la siguiente sección.


Backtesting

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

Gráfico de backtest:

GRÁFICO

Informe de backtest:

INFORME


Conclusión

En conclusión, hemos desarrollado un sistema basado en el patrón de 3 Drives en MQL5, basado en la acción del precio para detectar patrones armónicos alcistas y bajistas de 3 Drives con retrocesos y extensiones de Fibonacci precisos, capaz de automatizar las operaciones con puntos personalizables de entrada, stop loss y take profit multinivel, y visualizar los patrones con objetos gráficos como triángulos y líneas de tendencia.

Aviso legal: el presente artículo tiene únicamente fines educativos. Las operaciones de trading conllevan riesgos financieros importantes, y la volatilidad del mercado puede dar lugar a pérdidas. Resulta fundamental realizar un backtesting exhaustivo y aplicar una gestión del riesgo rigurosa antes de implementar este programa en mercados reales.

Aprovechando los conceptos y la implementación presentados, puede adaptar este sistema de patrón de 3 Drives a su estilo de trading y mejorar así sus estrategias algorítmicas. ¡Feliz trading!

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

Archivos adjuntos |
Pawlos6
Pawlos6 | 12 sept 2025 en 20:07
¿Por qué no aparece el EA en el gráfico y cuál debería ser el valor de optimización para utilizar el EA y el importe mínimo de operación?
Allan Munene Mutiiria
Allan Munene Mutiiria | 12 sept 2025 en 23:58
Pawlos6 #:
¿Por qué no aparece el EA en el gráfico y cuál debería ser el número de optimización para utilizar el EA y el importe mínimo de operación?

Hola. Puedes realizar la optimización en la pestaña «Optimización».

Cyberdude
Cyberdude | 16 sept 2025 en 18:43
Este autor tiene un montón de artículos, y esa es la primera señal de que algo no va bien. Aun así, le di el beneficio de la duda y seguí varios artículos muy de cerca, los recreé y los comparé con su EA. Todos los artículos, sin excepción, tenían un EA a medio hacer que no hacía lo que decía. Las capturas de pantalla también son muy sospechosas, ya que el EA solo detecta un patrón cuando ya se encuentra más adelante en el futuro y, a continuación, traza el patrón en el pasado con todo tipo de niveles de TP. He leído algunos de sus artículos y todos han sido una completa pérdida de tiempo.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 40): Pasaporte de ADN del mercado Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 40): Pasaporte de ADN del mercado
Este artículo analiza la identidad única de cada par de divisas desde la perspectiva de su acción del precio histórica. Inspirándonos en el concepto del ADN genético, que codifica el plano específico de cada ser vivo, aplicamos un marco similar a los mercados, considerando la acción del precio como el «ADN» de cada par. Desglosando comportamientos estructurales como la volatilidad, las oscilaciones, los retrocesos, los picos y las características de las sesiones, la herramienta revela el perfil subyacente que distingue a un par de otro. Este enfoque proporciona una visión más profunda del comportamiento del mercado y ofrece a los operadores una forma estructurada de alinear sus estrategias con las tendencias naturales de cada instrumento.
Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 13): Introducción a la teoría de control mediante la factorización de matrices Desarrollo de asesores expertos autooptimizables en MQL5 (Parte 13): Introducción a la teoría de control mediante la factorización de matrices
Los mercados financieros son impredecibles, y las estrategias de trading que parecían rentables en el pasado a menudo fracasan en las condiciones reales del mercado. Esto sucede porque la mayoría de las estrategias son fijas una vez implementadas y no pueden adaptarse ni aprender de sus errores. Tomando prestadas ideas de la teoría de control, podemos utilizar controladores de retroalimentación para observar cómo nuestras estrategias interactúan con los mercados y ajustar su comportamiento hacia la rentabilidad. Nuestros resultados demuestran que añadir un controlador de retroalimentación a una estrategia simple de media móvil mejoró las ganancias, redujo el riesgo y aumentó la eficiencia, lo que demuestra que este enfoque tiene un gran potencial para aplicaciones de trading.
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.
Automatización de estrategias de trading en MQL5 (Parte 30): creación de un patrón armónico AB-CD de acción del precio con información visual Automatización de estrategias de trading en MQL5 (Parte 30): creación de un patrón armónico AB-CD de acción del precio con información visual
En este artículo, desarrollaremos en MQL5 un EA basado en el patrón AB=CD que identifica patrones armónicos AB=CD alcistas y bajistas utilizando puntos de pivote y ratios de Fibonacci, y que ejecuta operaciones con niveles precisos de entrada, stop loss y take profit. Asimismo, mejoraremos la comprensión de los traders con información visual mediante objetos gráficos.