English Русский 中文 Deutsch 日本語 Português
preview
Creación de un modelo de restricción de tendencia de velas (Parte 10): Estrategia de Cruce Dorado y Cruce de la Muerte (EA)

Creación de un modelo de restricción de tendencia de velas (Parte 10): Estrategia de Cruce Dorado y Cruce de la Muerte (EA)

MetaTrader 5Ejemplos |
310 1
Clemence Benjamin
Clemence Benjamin

Introducción

En este artículo analizamos la integración de las estrategias de Cruce Dorado y de la Muerte en el Asesor Experto Trend Constraint, aprovechando todo el potencial de estas técnicas de cruce de medias móviles probadas por el tiempo. Nuestro objetivo es potenciar las capacidades de seguimiento de tendencias en el trading algorítmico mediante la automatización de estas estrategias, garantizando precisión, consistencia y una integración fluida con sistemas de trading más amplios.

Muchos operadores se enfrentan al reto de identificar y aprovechar eficazmente las fuertes tendencias alcistas y bajistas. Aunque el Cruce Dorado, que señala un impulso alcista, y el Cruce de la Muerte, que indica un sentimiento bajista, han demostrado su valor en el trading manual, su falta de automatización suele traducirse en oportunidades perdidas y ejecuciones inconsistentes.

Al incorporar las estrategias de Cruce Dorado y Cruce de la Muerte en un Asesor Experto automatizado, los traders pueden ejecutar de forma sistemática las señales de cambio de tendencia sin las limitaciones que afectan a otras estrategias de seguimiento de tendencias. A diferencia de las estrategias limitadas que dependen del sentimiento diario del mercado, estas estrategias de cruce funcionan de manera independiente para identificar y actuar sobre los giros en cuanto se producen. Esto garantiza que los posibles puntos de inflexión se detecten antes, lo que mejora la capacidad de respuesta y el rendimiento general de las operaciones, al tiempo que se mantiene la alineación con estrategias de tendencia más amplias.

Contenido principal:

  1. Comprendiendo la estrategia de Cruce Dorado y Cruce de la Muerte
  2. Entendiendo la estrategia de Cruce Dorado y Cruce de la Muerte
  3. Adaptación a las condiciones de restricción de tendencias
  4. Implementación de la estrategia utilizando MQL5
  5. Pruebas iniciales de la estrategia
  6. Integración de la nueva estrategia en el Trend Constraint
  7. Resultados de pruebas y optimización
  8. Conclusión


Comprendiendo la estrategia de Cruce Dorado y Cruce de la Muerte

La estrategia de Cruce Dorado y Cruce de la Muerte es un concepto fundamental del análisis técnico, utilizada para señalar posibles tendencias alcistas o bajistas en el mercado basadas en los cruces de medias móviles. Un Cruce Dorado ocurre cuando una media móvil de corto plazo, típicamente la de 50 días, cruza por encima de una media móvil de largo plazo, a menudo la de 200 días. Este evento se considera una fuerte señal alcista, lo que sugiere que podría estar comenzando una tendencia alcista a largo plazo. Los inversores y comerciantes podrían ver esto como una oportunidad para comprar o mantener acciones, anticipando un aumento en el precio. 

Por el contrario, el Cruce de la Muerte es el escenario opuesto, donde la media móvil de corto plazo cae por debajo de la media móvil de largo plazo, señalando una posible tendencia bajista. Esto se considera una advertencia de que el mercado podría entrar en un descenso prolongado. El Cruce de la Muerte puede llevar a los inversores a vender o abrir posiciones cortas, o al menos a actuar con cautela, ya que sugiere un debilitamiento de las condiciones del mercado. Ambas señales no solo se utilizan para tomar decisiones directas de trading, sino también para el análisis más amplio del sentimiento del mercado, aunque no son infalibles y deben combinarse con otros indicadores o formas de análisis para confirmar las tendencias.


Entendiendo la estrategia de Cruce Dorado y Cruce de la Muerte

En el contexto más amplio de las estrategias de seguimiento de tendencias, el Cruce Dorado y el Cruce de la Muerte funcionan como herramientas fundamentales para identificar el inicio y el posible cambio de las tendencias del mercado. El seguimiento de tendencias es una estrategia en la que los traders buscan obtener beneficios de los movimientos sostenidos de los precios del mercado alineando sus operaciones con la dirección de la tendencia establecida. Así es como el Cruce Dorado y el Cruce de la Muerte encajan en este paradigma:

1. Identificación de tendencias: la función principal de estos cruces es proporcionar señales claras para el inicio de nuevas tendencias. El Cruce Dorado señala el inicio de una tendencia alcista, lo que impulsa a los seguidores de tendencias a abrir posiciones largas o aumentar su exposición. Por el contrario, el Cruce de la Muerte indica el inicio de una tendencia bajista, lo que sugiere cerrar posiciones largas o considerar la apertura de cortas.
2. Confirmación de tendencias: estas señales actúan como confirmaciones dentro de las estrategias de seguimiento de tendencias. Un seguidor de tendencias podría utilizar estos cruces junto con otros indicadores, como la acción del precio, el volumen o los indicadores de impulso, para verificar que realmente se está desarrollando una tendencia. Este enfoque multiindicador ayuda a reducir las señales falsas, lo cual es crucial en el seguimiento de tendencias, donde el objetivo es aprovechar la tendencia durante el mayor tiempo posible.
3. Gestión de riesgos: En el seguimiento de tendencias, la gestión de riesgos es fundamental, ya que las tendencias pueden revertirse o detenerse inesperadamente. El Cruce Dorado y el Cruce de la Muerte pueden utilizarse para establecer niveles de stop-loss o para decidir cuándo reducir o aumentar el tamaño de las posiciones. Por ejemplo, una ruptura por debajo de las medias móviles tras un Cruce Dorado podría sugerir ajustar los stops o reducir la exposición.
4. Perspectiva a largo plazo: Estas señales son intrínsecamente de largo plazo debido al uso de medias móviles más extensas, lo que se alinea con la filosofía de seguimiento de tendencias de capturar los grandes movimientos del mercado en lugar de las fluctuaciones a corto plazo. Este enfoque a largo plazo ayuda a filtrar el ruido del mercado y centrarse en los movimientos de precios significativos.
5. Adaptabilidad: Aunque tradicionalmente se basa en medias móviles de 50 y 200 días, la estrategia puede adaptarse a distintos marcos temporales o condiciones de mercado. En mercados de rápido movimiento, los traders pueden usar periodos más cortos para obtener señales más rápidas, mientras que en mercados más estables se suelen preferir periodos más largos para evitar falsas señales provocadas por correcciones menores.

Sin embargo, la relevancia del Cruce Dorado y del Cruce de la Muerte dentro del seguimiento de tendencias puede analizarse críticamente de la siguiente manera:

  • Retraso: Una de las principales críticas es el desfase asociado a las medias móviles. Para cuando se producen estos cruces, es posible que una parte significativa de la tendencia ya se haya desarrollado, lo que puede reducir la rentabilidad de las operaciones basadas únicamente en estas señales.
  • Señales falsas: El seguimiento de tendencias conlleva de forma inherente el riesgo de rupturas falsas o reversiones prematuras de la tendencia. El Cruce Dorado y el Cruce de la Muerte no son inmunes a esto y, por lo tanto, deben formar parte de una estrategia más amplia que incluya otras formas de análisis o señales de confirmación.
  • Entorno de mercado: Su eficacia puede variar significativamente según las condiciones del mercado. En mercados con tendencia, estas señales pueden ser muy efectivas, pero en mercados laterales o volátiles pueden generar numerosos falsos inicios.


Adaptación a las condiciones de restricción de tendencias

En las estrategias a largo plazo, imponer restricciones excesivas puede limitar su potencial. Por ejemplo, consideremos un escenario donde el sentimiento diario del mercado es bajista. Una señal de cruce dorado que aparezca en marcos temporales bajos dentro del mismo día podría indicar un posible giro, sugiriendo que el mercado podría volverse alcista. Cuando se producen estos cambios, otras estrategias restringidas suelen adaptarse a la tendencia emergente, alineándose con la nueva dirección del mercado.

Para aprovechar esta dinámica, las estrategias «death cross» y «golden cross» pueden incorporarse al EA Trend Constraint como módulos independientes. De este modo, el EA puede maximizar su rendimiento capturando entradas de reversión que se alinean con la nueva tendencia emergente.

Este enfoque garantiza:

  1. Flexibilidad en distintos marcos temporales: el EA puede detectar reversiones a corto plazo (por ejemplo, cruces doradas) y alinearlas con movimientos más amplios del mercado, lo que mejora la adaptabilidad.
  2. Puntos de entrada mejorados: la identificación temprana de cambios en el sentimiento de tendencia permite al EA tomar posiciones estratégicas, minimizando el retraso en la respuesta a los cambios del mercado.
  3. Sinergia entre estrategias: Integrar de forma independiente las estrategias de cruce dorado y cruce de la muerte permite que el EA aproveche sus fortalezas sin anular los mecanismos principales de seguimiento de tendencias.

Al adoptar este enfoque de doble capa, el EA Trend Constraint adquiere la capacidad de navegar eficazmente las reversiones del mercado y al mismo tiempo mantener la rentabilidad a largo plazo. Pasemos ahora a la siguiente etapa, donde implementaremos e integraremos esta estrategia.


Implementación de la estrategia utilizando MQL5

En mi enfoque de desarrollo, al crear un EA multiestrategia, siempre diseño primero cada estrategia como un EA independiente. Esto permite realizar pruebas y refinamientos enfocados antes de fusionarlos en una única base de código unificada. Siguiendo este método, primero desglosaremos y desarrollaremos el EA de Cruce Dorado y Cruce de la Muerte de forma independiente antes de integrarlo en el Asesor Experto principal.

Como de costumbre, abra su aplicación MetaEditor 5 desde su escritorio o presione F4 en su terminal MetaTrader 5 para iniciarla directamente. Siga los pasos de desarrollo que se describen a continuación. Para los principiantes, se recomienda escribir el código manualmente en lugar de copiar y pegar, ya que esto ayuda a reforzar la habilidad y profundizar su comprensión.

Comenzaremos aquí con nuestro Cruce Dorado y Cruce de la Muerte:

Encabezado y metadatos:

Esta es la parte más alta del programa, comenzamos definiendo los metadatos para garantizar claridad y profesionalismo. Los metadatos identifican el programa como el EA de Cruce Dorado y Cruce de la Muerte, incluyendo los detalles de copyright, una descripción y el número de versión. Este paso nos ayuda a organizar el proyecto y garantiza que nuestro trabajo se atribuya correctamente cuando se implemente en MetaTrader 5. Al configurar los metadatos de esta manera, establecemos una base profesional y bien documentada para nuestra EA.

//+------------------------------------------------------------------+ 
//|                       Strategic Golden & Death Cross EA.mq5      |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property description "GOLDEN AND DEATH CROSS"
#property version "1.0"

Incluye e inicialización de objetos comerciales:

Para simplificar la ejecución de operaciones, incluimos la biblioteca de operaciones MQL5 con #include<Trade\Trade.mqh>. Esta biblioteca proporciona acceso a potentes funciones comerciales. Instanciamos la clase CTrade como comercio, lo que permite que el EA maneje operaciones como comprar, vender y cerrar posiciones sin implementar manualmente estas rutinas. Esto reduce la complejidad y hace que el código sea más confiable. Con esta configuración, podemos centrarnos en la estrategia y confiar en la biblioteca de gestión comercial para ejecutar nuestras órdenes.

#include<Trade\Trade.mqh>;
CTrade trade;

Parámetros de entrada:

Comenzamos definiendo parámetros ajustables por el usuario para hacer que el EA sea flexible y personalizable.

Por ejemplo:

  • LotSize nos permite controlar el volumen comercial.
  • El deslizamiento (Slippage) especifica desviaciones de precios aceptables durante la ejecución.
  • FastEMAPeriod y SlowEMAPeriod definen los periodos de las medias móviles que constituyen la base de la estrategia de Cruce Dorado y Cruce de la Muerte.
input double LotSize = 1.0;            // Trade volume (lots)
input int Slippage = 20;               // Slippage in points
input int TimerInterval = 10000;       // Timer interval in seconds
input double TakeProfitPips = 100;     // Take Profit in pips
input double StopLossPips = 50;        // Stop Loss in pips
input int FastEMAPeriod = 50;          // Fast EMA period (default 50)
input int SlowEMAPeriod = 200;         // Slow EMA period (default 200)

Al establecer estos parámetros, permitimos a los usuarios ajustar el EA según sus condiciones comerciales específicas.

Inicialización y configuración del temporizador:

Asegurémonos de que el EA se inicializa correctamente. En la función OnInit(), añadimos una comprobación para verificar si existen suficientes barras para calcular la EMA lenta. Si no es así, el EA registra un error y detiene la ejecución. Esto garantiza que la estrategia solo se ejecute cuando haya datos suficientes disponibles.

int OnInit()
{
   //--- create timer
   EventSetTimer(TimerInterval);

   //--- Check if there are enough bars for EMA calculation
   if(Bars(_Symbol,PERIOD_CURRENT)<SlowEMAPeriod)
   {
      Print("Not enough bars for EMA calculation");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

Además, utilizamos EventSetTimer() para llamar a la función OnTimer() periódicamente, ejecutando la lógica de negociación. La función OnDeinit() garantiza que el temporizador se desactive cuando se elimina el EA, liberando recursos.

void OnDeinit(const int reason)
{
   //--- delete timer
   EventKillTimer();
}

Lógica de negociación en OnTimer():

Ahora, entremos en el núcleo de la estrategia dentro de la función OnTimer():

Cálculo de la EMA:

Comenzamos creando manejadores para las EMA rápidas y lentas utilizando iMA() y recuperando sus valores con CopyBuffer(). Estas medias móviles exponenciales (EMA) son esenciales para detectar señales de entrada.

int fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
int slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

double fastEMAArray[], slowEMAArray[];
CopyBuffer(fastEMAHandle, 0, 0, 2, fastEMAArray);
CopyBuffer(slowEMAHandle, 0, 0, 2, slowEMAArray);

Recuperación de datos de mercado:

A continuación, recuperamos datos importantes del mercado, como el precio de venta, el precio de compra y el tamaño del punto, para garantizar cálculos precisos para el stop-loss y el take-profit.

double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

Señales de entrada:

Aquí definimos las condiciones para abrir operaciones de compra y venta. Un Cruce Dorado (la EMA rápida cruza por encima de la EMA lenta) señala una compra, mientras que un Cruce de la Muerte (la EMA rápida cruza por debajo de la EMA lenta) señala una venta.

if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
{
   double sl = NormalizeDouble(ask + StopLossPips * point, _Digits);
   trade.Sell(LotSize, _Symbol, ask, sl);
}
else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
{
   double sl = NormalizeDouble(bid - StopLossPips * point, _Digits);
   trade.Buy(LotSize, _Symbol, bid, sl);
}

Señales de salida:

Asegurémonos de que las posiciones existentes se cierren cuando aparezca la señal opuesta aquí. Esto mantiene la estrategia alineada con las condiciones cambiantes del mercado.

if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
   (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
{
   trade.PositionClose(PositionGetInteger(POSITION_TICKET));
}

Manejo de errores:

Ahora, añadimos rutinas de gestión de errores para registrar cualquier problema con la ejecución de operaciones o el cierre de posiciones, lo que ayuda a la depuración y garantiza un rendimiento fluido.

if(!trade.Sell(LotSize, _Symbol, ask, sl))
{
   Print("Sell order error: ", GetLastError());
}

Aquí está nuestro código completo:

//+------------------------------------------------------------------+ 
//|                          Golden & Death Cross Strategy.mq5       |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+

#property copyright "Clemence Benjamin"
#property description "GOLDEN AND DEATH CROSS"
#property version "1.0"


//+------------------------------------------------------------------+
//| Includes                                                         |
//+------------------------------------------------------------------+
#include<Trade\Trade.mqh>;
CTrade trade;

//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input double LotSize = 1.0;            // Trade volume (lots)
input int Slippage = 20;               // Slippage in points
input int TimerInterval = 1000;          // Timer interval in seconds
input double StopLossPips = 1500;        // Stop Loss in pips
input int FastEMAPeriod = 50;          // Fast EMA period (default 50)
input int SlowEMAPeriod = 200;         // Slow EMA period (default 200)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- create timer
   EventSetTimer(TimerInterval);
   
   //--- Check if there are enough bars to calculate the EMA
   if(Bars(_Symbol,PERIOD_CURRENT)<SlowEMAPeriod)
     {
      Print("Not enough bars for EMA calculation");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- delete timer
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Expert timer function                                            |
//+------------------------------------------------------------------+
void OnTimer()
{
   bool hasPosition = PositionSelect(_Symbol);
   
   int fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   int slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

   if(fastEMAHandle < 0 || slowEMAHandle < 0)
   {
      Print("Failed to create EMA handles. Error: ", GetLastError());
      return;
   }

   double fastEMAArray[], slowEMAArray[];
   if(CopyBuffer(fastEMAHandle, 0, 0, 2, fastEMAArray) <= 0 ||
      CopyBuffer(slowEMAHandle, 0, 0, 2, slowEMAArray) <= 0)
   {
      Print("Failed to copy EMA data. Error: ", GetLastError());
      return;
   }

   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   if(!hasPosition)
   {
      if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
      {
         
         double sl = NormalizeDouble(ask + StopLossPips * point, _Digits);
         if(!trade.Sell(LotSize, _Symbol, ask, sl ))
            Print("Buy order error ", GetLastError());
         else
            Print("Buy order opened with TP ", " and SL ", StopLossPips, " pips");
      }
      else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
      {
         
         double sl = NormalizeDouble(bid - StopLossPips * point, _Digits);
         if(!trade.Buy(LotSize, _Symbol, bid, sl ))
            Print("Sell order error ", GetLastError());
         else
            Print("Sell order opened with TP ",  " and SL ", StopLossPips, " pips");
      }
   }
   else
   {
     if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
         (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
    {
         ulong ticket = PositionGetInteger(POSITION_TICKET);
         if(!trade.PositionClose(ticket))
            Print("Failed to close position (Ticket: ", ticket, "). Error: ", GetLastError());
         else  
            Print("Position closed : ", ticket);
      }
   }
}

//+------------------------------------------------------------------+


Pruebas iniciales de la estrategia

Después de compilar el código con éxito, ya estamos listos para probar el rendimiento de nuestra estrategia usando el Probador de Estrategias. A continuación se muestran imágenes de los resultados de las pruebas antes de integrar la estrategia en el Asesor Experto Trend Constraint.

strategy tester settings.PNG

Configuración del Probador de Estrategias: Índice Boom 500

Configuración de entradas

Configuración de entradas: Índice Boom 500

Índice Boom 500 M5: Prueba de la estrategia de Cruce Dorado y Cruce de la Muerte

Desde la prueba inicial, la lógica de ejecución de órdenes funciona sin problemas, pero es necesario mejorar la estrategia de salida. A partir de la observación manual en el probador de estrategias, noté muchas posiciones que podrían haber asegurado una ganancia pero terminaron cerrando con pequeñas ganancias o incluso pérdidas. Esto ocurrió porque las reversiones presentaron cruces luego de caídas significativas, limitando las ganancias. Durante la consolidación del mercado, muchos cruces falsos provocaron pérdidas. Para evitar caídas tan grandes, es necesario mejorar la estrategia de salida.


Integración de la nueva estrategia en el Trend Constraint

Finalmente, hemos alcanzado el objetivo de crear un Asesor Experto de estrategia múltiple mediante la integración de la estrategia que desarrollamos anteriormente. Para refrescar la memoria sobre las estrategias existentes, puedes volver a consultar (Parte 9). A continuación se muestra una lista de las estrategias que ya se han integrado:

  • Trend Following (Seguimiento de tendencias)
  • Donchian Channel Breakout (Ruptura del canal de Donchian)
  • Divergence Strategy (Estrategia de divergencia)

Hoy agregamos la cuarta estrategia, que, como expliqué anteriormente, funciona de forma independiente y está libre de condiciones restrictivas, lo que le permite capturar todas las oportunidades de reversión. Para lograrlo, modificaremos el Asesor Experto Trend Constraint actual añadiendo un interruptor booleano para la nueva estrategia de Cruce Dorado y Cruce de la Muerte. Además, refactorizaremos otras secciones de código en funciones relevantes del código principal.

Para evitar conflictos con otros términos ya presentes en el programa principal, añadimos un prefijo único a las variables asociadas con la estrategia de Cruce Dorado y Cruce de la Muerte. Por ejemplo, hemos cambiado el nombre de LotSize a GDC_LotSize = 1.0 para garantizar la claridad y evitar confusiones.

A continuación se muestra el código completo y sin errores. Las nuevas incorporaciones y modificaciones se han resaltado claramente para facilitar su comprensión y claridad.

//+------------------------------------------------------------------+
//|                                      Trend Constraint Expert.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamini"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.03"

#include <Trade\Trade.mqh>
CTrade trade;

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Following Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = false;       // Enable/Disable Divergence Strategy
input bool UseGoldenDeathCrossStrategy = true;  // Enable/Disable Golden/Death Cross Strategy

// Input parameters for Golden/Death Cross Strategy
input double GDC_LotSize = 1.0;            // Trade volume (lots) for Golden Death Cross
input int GDC_Slippage = 20;               // Slippage in points for Golden Death Cross
input int GDC_TimerInterval = 1000;        // Timer interval in seconds for Golden Death Cross
input double GDC_StopLossPips = 1500;      // Stop Loss in pips for Golden Death Cross
input int GDC_FastEMAPeriod = 50;          // Fast EMA period for Golden Death Cross
input int GDC_SlowEMAPeriod = 200;         // Slow EMA period for Golden Death Cross

int GDC_fastEMAHandle, GDC_slowEMAHandle;  // Handles for EMA indicators in Golden Death Cross

// Global variables
double prevShortMA, prevLongMA;

// Input parameters for Trend Constraint Strategy
input int    RSI_Period = 14;            // RSI period
input double RSI_Overbought = 70.0;     // RSI overbought level
input double RSI_Oversold = 30.0;       // RSI oversold level
input double Lots = 0.1;                // Lot size
input double StopLoss = 100;            // Stop Loss in points
input double TakeProfit = 200;          // Take Profit in points
input double TrailingStop = 50;         // Trailing Stop in points
input int    MagicNumber = 12345678;    // Magic number for the Trend Constraint EA
input int    OrderLifetime = 43200;     // Order lifetime in seconds (12 hours)

// Input parameters for Breakout Strategy
input int InpDonchianPeriod = 20;       // Period for Donchian Channel
input double RiskRewardRatio = 1.5;     // Risk-to-reward ratio
input double LotSize = 0.1;             // Default lot size for trading
input double pipsToStopLoss = 15;       // Stop loss in pips for Breakout
input double pipsToTakeProfit = 30;     // Take profit in pips for Breakout

// Input parameters for Divergence Strategy
input int DivergenceMACDPeriod = 12;    // MACD Fast EMA period
input int DivergenceSignalPeriod = 9;   // MACD Signal period
input double DivergenceLots = 1.0;      // Lot size for Divergence trades
input double DivergenceStopLoss = 300;   // Stop Loss in points for Divergence
input double DivergenceTakeProfit = 500; // Take Profit in points for Divergence
input int DivergenceMagicNumber = 87654321;     // Magic number for Divergence Strategy
input int DivergenceLookBack = 8;       // Number of periods to look back for divergence
input double profitLockerPoints  = 20;  // Number of profit points to lock

// Indicator handle storage
int rsi_handle;                         
int handle;                             // Handle for Donchian Channel
int macd_handle;

double ExtUpBuffer[];                   // Upper Donchian buffer
double ExtDnBuffer[];                   // Lower Donchian buffer
double ExtMacdBuffer[];                 // MACD buffer
double ExtSignalBuffer[];               // Signal buffer
int globalMagicNumber;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    prevShortMA = 0.0;
    prevLongMA = 0.0;
    // Initialize RSI handle
    rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    if (rsi_handle == INVALID_HANDLE)
    {
        Print("Failed to create RSI indicator handle. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Create a handle for the Donchian Channel
    handle = iCustom(_Symbol, PERIOD_CURRENT, "Free Indicators\\Donchian Channel", InpDonchianPeriod);
    if (handle == INVALID_HANDLE)
    {
        Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Initialize MACD handle for divergence
    globalMagicNumber = DivergenceMagicNumber;
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to create MACD indicator handle for divergence strategy. Error: ", GetLastError());
        return INIT_FAILED;
    }
    
    
    if(UseGoldenDeathCrossStrategy)
{
    // Check if there are enough bars to calculate the EMA
    if(Bars(_Symbol, PERIOD_CURRENT) < GDC_SlowEMAPeriod)
    {
        Print("Not enough bars for EMA calculation for Golden Death Cross");
        return INIT_FAILED;
    }
    GDC_fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, GDC_FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    GDC_slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, GDC_SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    if(GDC_fastEMAHandle < 0 || GDC_slowEMAHandle < 0)
    {
        Print("Failed to create EMA handles for Golden Death Cross. Error: ", GetLastError());
        return INIT_FAILED;
    }
}

    // Resize arrays for MACD buffers
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);

    Print("Trend Constraint Expert initialized.");
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(rsi_handle);
    IndicatorRelease(handle);
    IndicatorRelease(macd_handle);
    Print("Trend Constraint Expert deinitialized.");
}

//+------------------------------------------------------------------+
//| Check Golden/Death Cross Trading Logic                           |
//+------------------------------------------------------------------+
void CheckGoldenDeathCross()
{
    double fastEMAArray[2], slowEMAArray[2];
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

    if(CopyBuffer(GDC_fastEMAHandle, 0, 0, 2, fastEMAArray) <= 0 ||
       CopyBuffer(GDC_slowEMAHandle, 0, 0, 2, slowEMAArray) <= 0)
    {
        Print("Failed to copy EMA data for Golden Death Cross. Error: ", GetLastError());
        return;
    }

    bool hasPosition = PositionSelect(_Symbol);

    if(!hasPosition)
    {
        if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
        {
            double sl = NormalizeDouble(ask + GDC_StopLossPips * point, _Digits);
            if(!trade.Sell(GDC_LotSize, _Symbol, ask, sl ))
                Print("Sell order error for Golden Death Cross ", GetLastError());
            else
                Print("Sell order opened for Golden Death Cross with SL ", GDC_StopLossPips, " pips");
        }
        else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
        {
            double sl = NormalizeDouble(bid - GDC_StopLossPips * point, _Digits);
            if(!trade.Buy(GDC_LotSize, _Symbol, bid, sl ))
                Print("Buy order error for Golden Death Cross ", GetLastError());
            else
                Print("Buy order opened for Golden Death Cross with SL ", GDC_StopLossPips, " pips");
        }
    }
    else
    {
        if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
           (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
        {
            ulong ticket = PositionGetInteger(POSITION_TICKET);
            if(!trade.PositionClose(ticket))
                Print("Failed to close position for Golden Death Cross (Ticket: ", ticket, "). Error: ", GetLastError());
            else  
                Print("Position closed for Golden Death Cross: ", ticket);
        }
    }
}

//+------------------------------------------------------------------+
//| Check Trend Following Strategy                                   |
//+------------------------------------------------------------------+
void CheckTrendFollowing()
{
   if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    double rsi_value;
    double rsi_values[];
    if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0)
    {
        Print("Failed to get RSI value. Error: ", GetLastError());
        return;
    }
    rsi_value = rsi_values[0];

    double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
    double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE);

    bool is_uptrend = ma_short > ma_long;
    bool is_downtrend = ma_short < ma_long;

    if (is_uptrend && rsi_value < RSI_Oversold)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double stopLossPrice = currentPrice - StopLoss * _Point;
        double takeProfitPrice = currentPrice + TakeProfit * _Point;

        // Corrected Buy method call with 6 parameters
        if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy"))
        {
            Print("Trend Following Buy order placed.");
        }
    }
    else if (is_downtrend && rsi_value > RSI_Overbought)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double stopLossPrice = currentPrice + StopLoss * _Point;
        double takeProfitPrice = currentPrice - TakeProfit * _Point;

        // Corrected Sell method call with 6 parameters
        if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell"))
        {
            Print("Trend Following Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check Breakout Strategy                                          |
//+------------------------------------------------------------------+
void CheckBreakoutTrading()
{
    if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    ArrayResize(ExtUpBuffer, 2);
    ArrayResize(ExtDnBuffer, 2);

    if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0)
    {
        Print("Error reading Donchian Channel buffer. Error: ", GetLastError());
        return;
    }

    double closePrice = iClose(_Symbol, PERIOD_CURRENT, 0);
    double lastOpen = iOpen(_Symbol, PERIOD_D1, 1);
    double lastClose = iClose(_Symbol, PERIOD_D1, 1);

    bool isBullishDay = lastClose > lastOpen;
    bool isBearishDay = lastClose < lastOpen;

    if (isBullishDay && closePrice > ExtUpBuffer[1])
    {
        double stopLoss = closePrice - pipsToStopLoss * _Point;
        double takeProfit = closePrice + pipsToTakeProfit * _Point;
        if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy") > 0)
        {
            Print("Breakout Buy order placed.");
        }
    }
    else if (isBearishDay && closePrice < ExtDnBuffer[1])
    {
        double stopLoss = closePrice + pipsToStopLoss * _Point;
        double takeProfit = closePrice - pipsToTakeProfit * _Point;
        if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell") > 0)
        {
            Print("Breakout Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check Divergence Trading                                         |
//+------------------------------------------------------------------+
bool CheckBullishRegularDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}

bool CheckBullishHiddenDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 > priceLow2 && macdLow1 < macdLow2);
}

bool CheckBearishRegularDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2];
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 > priceHigh2 && macdHigh1 < macdHigh2);
}

bool CheckBearishHiddenDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2]; 
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}

void CheckDivergenceTrading()
{
    if (!UseDivergenceStrategy) return;

    // Check if no position is open or if less than 3 positions are open
    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        int barsAvailable = Bars(_Symbol, PERIOD_CURRENT);
        if (barsAvailable < DivergenceLookBack * 2)
        {
            Print("Not enough data bars for MACD calculation.");
            return;
        }

        int attempt = 0;
        while(attempt < 6)
        {
            if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
                CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
                break; 
            
            Print("Failed to copy MACD buffer, retrying...");
            Sleep(1000);
            attempt++;
        }
        if(attempt == 6)
        {
            Print("Failed to copy MACD buffers after ", attempt, " attempts.");
            return;
        }

        if(TimeCurrent() == iTime(_Symbol, PERIOD_CURRENT, 0))
        {
            Print("Skipping trade due to incomplete bar data.");
            return;
        }

        double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);
        double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
        double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
        bool isDailyBullish = dailyClose > dailyOpen;
        bool isDailyBearish = dailyClose < dailyOpen;

        // Only proceed with buy orders if D1 is bullish
        if (isDailyBullish)
        {
            if ((CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) ||
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }
        }

        // Only proceed with sell orders if D1 is bearish
        if (isDailyBearish)
        {
            if ((CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) ||
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
    else
    {
        Print("Divergence strategy: Maximum number of positions reached.");
    }
}

void ExecuteDivergenceOrder(bool isBuy)
{
    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);
    
    double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
    double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;

    if (isBuy)
    {
        if (trade.Buy(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Buy"))
        {
            Print("Divergence Buy order placed.");
        }
    }
    else
    {
        if (trade.Sell(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Sell"))
        {
            Print("Divergence Sell order placed.");
        }
    }
}

int CountOrdersByMagic(int magic)
{
    int count = 0;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magic)
            {
                count++;
            }
        }
    }
    return count;
}

//+------------------------------------------------------------------+
//| Profit Locking Logic                                             |
//+------------------------------------------------------------------+
void LockProfits()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            
            // Convert profit to points
            double profitPoints = MathAbs(currentProfit / _Point);

            // Check if profit has exceeded 100 points
            if (profitPoints >= 100)
            {
                double newStopLoss;
                
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    newStopLoss = entryPrice + profitLockerPoints * _Point; // 20 points above entry for buys
                }
                else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                {
                    newStopLoss = entryPrice - profitLockerPoints * _Point; // 20 points below entry for sells
                }
                else
                {
                    continue; // Skip if not a buy or sell position
                }

                // Modify stop loss only if the new stop loss is more protective
                double currentStopLoss = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    if (currentStopLoss < newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for buy position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
                else // POSITION_TYPE_SELL
                {
                    if (currentStopLoss > newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for sell position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
            }
        }
    }
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if (UseTrendFollowingStrategy)
        CheckTrendFollowing();
    if (UseBreakoutStrategy)
        CheckBreakoutTrading();
    if (UseDivergenceStrategy)
        CheckDivergenceTrading();
    if(UseGoldenDeathCrossStrategy)
        CheckGoldenDeathCross();

}


Resultados de pruebas y optimización

Lanzamiento del EA Trend Constraint.

Asesor Experto Trend Constraint: Añadiendolo al gráfico con la configuración predeterminada

Visualización de la estrategia

Estrategia de Cruce Dorado y Cruce de la Muerte visualizada como parte de otras estrategias en el Probador de estrategias


Conclusión

Es posible ampliar continuamente nuestro código EA, pero hacerlo puede hacer que su procesamiento sea cada vez más complejo y requiera cada vez más recursos. Esto pone de relieve la necesidad de avanzar en nuestras técnicas de gestión de recursos. La integración de modelos de IA en estos conceptos es particularmente beneficiosa, ya que pueden manejar estas complejidades de manera efectiva. En este proyecto integramos con éxito una de las estrategias más populares para gestionar oportunidades de reversión, la estrategia de Cruce Dorado y Cruce de la Muerte, dentro del Asesor Experto Trend Constraint.

Las etapas fundamentales que cubrimos desde la Parte 1 hasta ahora sentaron las bases para este EA. Sin embargo, para lograr resultados óptimos, el modelo del EA debe perfeccionarse optimizando diversos ajustes y modificando características estructurales específicas. Este enfoque lo convierte en una valiosa herramienta educativa y experimental. Tenga en cuenta que este EA no garantiza la rentabilidad; está destinado exclusivamente a fines educativos y de investigación.

En las próximas partes, tengo previsto perfeccionar aún más las estrategias establecidas e introducir técnicas de aprendizaje automático para mejorar las capacidades y el rendimiento del EA.

Volver a la introducción

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

Archivos adjuntos |
Christian Paul Anasco
Christian Paul Anasco | 20 dic 2024 en 03:15
Esto es exactamente lo que me gusta. Un código muy limpio. Fácil de leer y entender.
Cambiando a MQL5 Algo Forge (Parte 3): Uso de repositorios de terceros en su propio proyecto Cambiando a MQL5 Algo Forge (Parte 3): Uso de repositorios de terceros en su propio proyecto
Hoy veremos cómo podemos conectar el código de otra persona desde cualquier repositorio en el almacenamiento MQL5 Algo Forge a nuestro proyecto. En el presente artículo, finalmente abordaremos esta tarea prometedora pero también compleja: cómo conectar y utilizar en la práctica bibliotecas de repositorios de terceros del almacenamiento MQL5 Algo Forge en nuestro proyecto.
Búsqueda dialéctica - Dialectic Search (DA) Búsqueda dialéctica - Dialectic Search (DA)
Hoy nos familiarizaremos con el Algoritmo Dialéctico (DA), un nuevo método de optimización global inspirado en el concepto filosófico de la dialéctica. El algoritmo explota la singular división de la población en pensadores especulativos y prácticos. Las pruebas demuestran un impresionante rendimiento de hasta el 98% en tareas pequeñas y una eficiencia global del 57,95%. El artículo explica estas métricas y presenta una descripción detallada del algoritmo y resultados experimentales con distintos tipos de características.
Kit de herramientas de negociación MQL5 (Parte  5): Ampliación de la libreria EX5 de gestión del historial con funciones de posición Kit de herramientas de negociación MQL5 (Parte 5): Ampliación de la libreria EX5 de gestión del historial con funciones de posición
Descubra cómo crear funciones EX5 exportables para consultar y guardar de forma eficiente datos históricos de posición. En esta guía paso a paso, ampliaremos la libreria de gestión del historial EX5 mediante el desarrollo de módulos que recuperan las propiedades clave de la posición cerrada más recientemente. Entre ellos se incluyen el beneficio neto, la duración de la operación, el stop loss basado en pips, el take profit, los valores de beneficio y otros detalles importantes.
Añadimos un LLM personalizado a un robot comercial (Parte 5): Desarrollar y probar la estrategia de negociación con LLMs (III) Ajuste del adaptador Añadimos un LLM personalizado a un robot comercial (Parte 5): Desarrollar y probar la estrategia de negociación con LLMs (III) Ajuste del adaptador
Con el rápido desarrollo de la inteligencia artificial actual, los modelos de lenguaje (LLM) son una parte importante de la inteligencia artificial, por lo que deberíamos pensar en cómo integrar LLM potentes en nuestro trading algorítmico. Para la mayoría de las personas, es difícil ajustar estos poderosos modelos según sus necesidades, implementarlos localmente y luego aplicarlos al comercio algorítmico. Esta serie de artículos adoptará un enfoque paso a paso para lograr este objetivo.