English Русский 中文 Deutsch 日本語
preview
Creación de un modelo de restricción de tendencia de velas (Parte 9): Asesor Experto de múltiples estrategias (III)

Creación de un modelo de restricción de tendencia de velas (Parte 9): Asesor Experto de múltiples estrategias (III)

MetaTrader 5Ejemplos |
32 0
Clemence Benjamin
Clemence Benjamin

Introducción

En el trading algorítmico, identificar puntos de entrada óptimos dentro de una tendencia predominante sigue siendo un desafío importante, ya que muchas estrategias tienen dificultades para capturar el momento adecuado o generan señales falsas frecuentes, lo que resulta en un rendimiento subóptimo. Este problema es particularmente pronunciado en las tendencias diarias, donde pequeñas fluctuaciones pueden alterar la precisión de la ejecución.

La divergencia ofrece una solución sólida al actuar como un filtro para identificar posibles reversiones o continuaciones a través de discrepancias entre los movimientos de precios y los indicadores de impulso. Al integrar la detección de divergencias en el asesor experto Trend Constraint, los operadores pueden mejorar significativamente su precisión a la hora de localizar niveles de entrada.

Este enfoque mejora la precisión comercial y también garantiza un comercio consistente y eficiente cuando se combina con las capacidades avanzadas de MQL5. En este artículo, exploraremos los fundamentos de la divergencia, los pasos para integrarla en los Asesores Expertos MQL5, las mejoras del asesor experto Trend Constraint con nuevas condiciones de ejecución comercial y conocimientos adquiridos a partir de pruebas retrospectivas para demostrar su aplicación práctica.

Contenido principal:

  1. Los fundamentos de la divergencia
  2. Pasos para integrar la detección de divergencias
  3. Mejoras en el asesor experto Trend Constraint: Implementación de nuevas condiciones de ejecución de operaciones para aprovechar la divergencia.
  4. Resultados de las pruebas retrospectivas y aplicación práctica
  5. Conclusión


Los fundamentos de la divergencia

La divergencia es un concepto clave en el análisis técnico, que proporciona a los operadores información sobre posibles reversiones o continuaciones de precios al comparar los movimientos de precios con la dirección de un indicador. 

Importancia de la divergencia:

La divergencia ocurre cuando el precio de un activo se mueve en oposición a un indicador técnico, lo que a menudo señala un debilitamiento de la tendencia o una reversión inminente. Este concepto es particularmente útil para identificar cuándo una tendencia podría estar a punto de sufrir una corrección o un cambio total.

Tipos de divergencia:

  1. La divergencia alcista se observa cuando el precio del activo alcanza mínimos más bajos, pero el indicador, como el RSI, comienza a mostrar mínimos más altos, lo que sugiere que el impulso bajista está disminuyendo. Esto podría significar que la presión de venta está disminuyendo, lo que podría generar un movimiento de precios al alza.
  2. La divergencia bajista se observa cuando el precio alcanza máximos más altos, pero el indicador muestra máximos más bajos, lo que indica que el impulso ascendente está disminuyendo. Esto podría presagiar una caída en los precios.

Verificación de antecedentes:

La divergencia es un concepto fundamental en el análisis técnico que influye en el comportamiento del mercado y en las estrategias de los operadores. Bart y Masse (1981), en «Divergence of Opinion and Risk», hacen hincapié en cómo las discrepancias en la opinión del mercado pueden aumentar el riesgo y la volatilidad de los precios, lo que refleja el papel de la divergencia en el análisis técnico.

Las pruebas empíricas de Tilehnouei y Shivaraj (2013) sugieren que herramientas como el MACD pueden superar al RSI en determinados contextos, ya que ofrecen información valiosa sobre el impulso del mercado a través de señales de divergencia. A partir de esta investigación, podemos decir que la integración de la divergencia con otros indicadores, como la interacción entre RSI, MACD y la acción del precio, refuerza su utilidad en un enfoque comercial integral, como lo respaldan varias fuentes de la industria.

En la siguiente sección, daremos un paso práctico para implementar la idea de divergencia en el desarrollo de nuestro EA.


Pasos para integrar la detección de divergencias

Para incorporar la detección de divergencias en un Asesor Experto (EA) de MQL5, comenzamos calculando los valores del Índice de Fuerza Relativa (Relative Strength Index, RSI) utilizando funciones como iRSI() y comparándolos con la acción del precio. Los precios extremos significativos se identifican utilizando iHigh() e iLow() durante un período determinado. Para este proyecto, clasificaré la divergencia en dos tipos: regular (reversión) y oculta.

Divergencia regular:

Las señales de divergencia regulares indican posibles cambios de tendencia: una configuración alcista ocurre cuando el precio alcanza un mínimo más bajo mientras que el indicador forma un mínimo más alto, y una configuración bajista surge cuando el precio alcanza un máximo más alto, pero el indicador muestra un máximo más bajo.

// Regular divergence conditions in code
bool CheckRegularBearishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
    double priceHigh1 = iHigh(_Symbol, timeframe, 2);
    double priceHigh2 = iHigh(_Symbol, timeframe, 8);
    double rsiHigh1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2);
    double rsiHigh2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8);
    
    if(priceHigh1 > priceHigh2 && rsiHigh1 < rsiHigh2) return true;
    return false;
}

Para ilustrar estas divergencias regulares, aquí hay dos imágenes que representan la divergencia alcista y la divergencia bajista, respectivamente:

Índice Boom 300 H4 - Divergencia alcista

Índice Boom 300 H4-Divergencia alcista: (un precio mínimo más bajo B frente a un valor RSI mínimo más alto en D)


Índice Boom 300 H4 - Divergencia bajista

Índice Boom 300 H4: Divergencia bajista (un precio máximo más alto B frente a un valor RSI máximo más bajo en D).

Divergencia oculta:

La divergencia oculta, por otro lado, sugiere una continuación de la tendencia. Una divergencia alcista oculta ocurre en una tendencia alcista cuando el precio forma un mínimo más alto mientras el indicador forma un mínimo más bajo, mientras que una divergencia bajista oculta aparece en una tendencia bajista cuando el precio forma un máximo más bajo y el indicador forma un máximo más alto.

//RSI and Price Levels declaration and hidden divergence condition
bool CheckHiddenBullishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
    double priceLow1 = iLow(_Symbol, timeframe, 2);
    double priceLow2 = iLow(_Symbol, timeframe, 8);
    double rsiLow1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2);
    double rsiLow2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8);
    
    if(priceLow1 > priceLow2 && rsiLow1 < rsiLow2) return true;
    return false;
}

A continuación se muestran imágenes que ilustran la divergencia oculta alcista y la divergencia oculta bajista. Asegúrese de compararlos con las descripciones proporcionadas anteriormente y practique la identificación de patrones similares en sus propios gráficos.

Índice Boom 300 H4 - Divergencia oculta alcista

Índice Boom 300 H4: Divergencia oculta alcista

Índice Boom 300 H4 - Divergencia oculta bajista

Índice Boom 300 H4: Divergencia bajista oculta

La integración de estos tipos de divergencias en el EA requiere condiciones de codificación para detectarlas en cada tick o cierre de barra, utilizando herramientas como iRSI() o iMACD() para los cálculos de los indicadores. Una vez detectadas, las señales de divergencia se alinean con las restricciones de tendencia, identificadas utilizando el sentimiento diario del mercado.


Mejoras en el asesor experto Trend Constraint: Implementación de nuevas condiciones de ejecución de operaciones para aprovechar la divergencia.

Sobre la sección anterior, cuando finalmente se detecta la divergencia, necesitaremos un indicador adicional para confirmar y señalar el proceso de ejecución de la orden. Hay muchas opciones para esto, pero consideraremos implementar MACD y RSI para eso. A continuación se muestra una lista de otros indicadores de confirmación opcionales: 

  1. Bandas de Bollinger
  2. Oscilador estocástico
  3. Volumen de equilibrio (On Balance Volume, OBV) y precio promedio ponderado por volumen (Volume Weighted Average Price , VWAP)
  4. Índice dinámico promedio (Average Dynamic Index, ADX)

Media móvil de convergencia/divergencia (Moving Average Convergence Divergence, MACD):

Por qué usarlo: MACD puede confirmar cambios de impulso. Si se detecta una divergencia con el RSI, el MACD puede proporcionar una confirmación secundaria de la fortaleza o debilitamiento de la tendencia. 

Cómo funciona:

El EA buscará cruces de líneas MACD o cambios en el histograma que se alineen con los hallazgos de divergencia. Por ejemplo, una divergencia bajista podría confirmarse si la línea MACD cruza por debajo de la línea de señal o el histograma comienza a disminuir.

Aspectos destacados del indicador MACD:

Para tener una vista previa del indicador incorporado como el MACD, podemos acceder dentro de la carpeta de "Examples" dentro de la carpeta de Indicadores en el software MetaEditor 5, consulte la imagen explicativa a continuación.

Acceda al código fuente de MACD en MetaEditor 5

MetaEditor 5: Cómo acceder al archivo fuente del MACD

El motivo por el que accedemos al código es para que podamos entender fácilmente cómo están diseñados los buffers para que podamos adaptarlos fácilmente en nuestro Asesor Experto. A continuación se muestra un fragmento de código para las declaraciones almacenadas en búfer dentro del indicador que nos interesa.

//--- indicator buffers
double ExtMacdBuffer[];
double ExtSignalBuffer[];
double ExtFastMaBuffer[];
double ExtSlowMaBuffer[];

int    ExtFastMaHandle;
int    ExtSlowMaHandle;

Ahora, con nuestros buffers en mente, revisemos el proceso de desarrollo paso a paso que se describe a continuación.

Desarrollo de la estrategia de divergencia: 

Paso 1: Declaraciones y parámetros de entrada

 Comenzamos declarando los parámetros de entrada para la estrategia de divergencia, los buffers MACD e inicializando la clase CTrade.

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

input bool UseDivergenceStrategy = true;        // Enable/Disable 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

double ExtMacdBuffer[]; // MACD values
double ExtSignalBuffer[]; // Signal line values
int macd_handle; // MACD indicator handle

Paso 2: Inicializar el indicador MACD

Inicializa el controlador MACD y asigna memoria intermedia durante OnInit().

int OnInit()
{
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to initialize MACD. Error: ", GetLastError());
        return INIT_FAILED;
    }
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);
    return INIT_SUCCEEDED;
}

Paso 3: Detección de divergencias

La divergencia ocurre cuando la acción del precio de un activo no coincide con un indicador, en este caso, el MACD. Esta estrategia detecta cuatro tipos de divergencias: alcista regular, alcista oculta, bajista regular y bajista oculta. Cada tipo tiene condiciones específicas, comparando los máximos o mínimos de precios con los máximos o mínimos del MACD correspondientes para determinar la presencia de divergencia.

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 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);
}

Paso 4: Lógica de negociación

En primer lugar, la estrategia garantiza que el trading de divergencia esté habilitado y que no haya más de 3 posiciones abiertas basadas en divergencia. Recupera datos del buffer MACD, vuelve a intentarlo en caso de error y solo ejecuta operaciones en barras completas. Además, alinea las operaciones con las tendencias de velas diarias, lo que garantiza que solo se compren en días alcistas y se vendan en días bajistas.

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

    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
            CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
        {
            double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
            double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
            bool isDailyBullish = dailyClose > dailyOpen;
            bool isDailyBearish = dailyClose < dailyOpen;

            if (isDailyBullish && 
                (CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) || 
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }

            if (isDailyBearish && 
                (CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) || 
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
}

Paso 5: Ejecución de la orden

Una vez que se detecta una divergencia, la estrategia ejecuta operaciones con parámetros predefinidos como tamaño de lote, stop loss y take profit. La función ExecuteDivergenceOrder calcula los niveles apropiados según la dirección del comercio y utiliza el objeto comercial para colocar órdenes de compra o venta.

void ExecuteDivergenceOrder(bool isBuy)
{
    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.");
    }
}

Paso 6: Gestión de pedidos

Para evitar el exceso de operaciones, la estrategia utiliza la función de utilidad CountOrdersByMagic para contar todas las posiciones abiertas con el número mágico especificado. Esto garantiza el cumplimiento del límite de posición máxima establecido para operaciones basadas en divergencia.

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;
}

Número mágico: 

Otro aspecto importante es darle identidad a nuestras posiciones. En este caso, asignamos un número mágico único a las operaciones regidas por la estrategia de divergencia.

    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);

Paso 7: Protección de beneficios

Introdujimos una función de protección de ganancias que incorpora una lógica de bloqueo de ganancias para nuestro asesor experto en restricciones de tendencias para asegurar dinámicamente las ganancias de las operaciones abiertas. La función LockProfits analiza todas las posiciones activas e identifica aquellas con ganancias superiores a un umbral de 100 puntos. Para cada posición que cumple los requisitos, calcula un nuevo nivel de stop-loss basado en el parámetro profitLockerPoints (por ejemplo, 20 puntos desde el precio de entrada).

Este ajuste garantiza que el stop-loss se acerque al precio actual, fijando efectivamente las ganancias. Para las posiciones de compra, el stop-loss se desplaza por encima del precio de entrada, mientras que en las posiciones de venta, se desplaza por debajo. La función actualiza el stop-loss solo si el nuevo nivel ofrece mejor protección, lo que garantiza una gestión óptima del riesgo. Las modificaciones exitosas se registran con un mensaje para su seguimiento. Esta característica ayuda a asegurar las ganancias y al mismo tiempo permite que las operaciones tengan potencial para un mayor crecimiento.

Aquí está la función de bloqueo de ganancias:

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

Paso 8: Integrar en OnTick()

Llame a la lógica comercial de divergencia dentro del bucle principal del EA.

void OnTick()
{
    CheckDivergenceTrading();
}

Paso 9: Apagar

void OnDeinit(const int reason)
{
    IndicatorRelease(macd_handle);
}

Integración de la estrategia en el asesor experto principal Trend Constrant.

En el artículo anterior de esta serie desarrollamos un asesor experto basado en el canal de Donchian, marcando dos estrategias en un solo experto. Hoy incorporamos la tercera estrategia y utilizamos datos de tipo booleano para activar y desactivar las estrategias.

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Constraint Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = true;        // Enable/Disable Divergence Strategy

Establecemos la estrategia "Divergence" como verdadera para no confundirnos durante el Probador de estrategias.

Finalmente, integramos cuidadosamente nuestra estrategia en el código principal:

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

#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 = true;        // Enable/Disable Divergence Strategy

// 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()
{
    // 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;
    }

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

    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(rsi_handle);
    IndicatorRelease(handle);
    IndicatorRelease(macd_handle);
}

//+------------------------------------------------------------------+
//| Check and execute Trend Following EA trading logic               |
//+------------------------------------------------------------------+
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 and execute Breakout EA trading logic                      |
//+------------------------------------------------------------------+
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.");
        }
    }
}

//+------------------------------------------------------------------+
//| DIVERGENCE TRADING STRATEGY                                      |
//+------------------------------------------------------------------+

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();
    LockProfits(); // Call this function to check and lock profits
}


Resultados de las pruebas retrospectivas y aplicación práctica

Para realizar nuestra prueba, debemos localizar Trend Constraint en la sección Experts del terminal. Asegúrese de estar ejecutando una cuenta de demostración. Se abre una ventana del Probador de estrategias, donde se pueden ajustar los parámetros de entrada para diferentes optimizaciones. Vea las imágenes siguientes con la configuración predeterminada:

Configuración de entradas

Asesor experto Trend Constraint: Configuración de demostración 

Ejecutamos el Probador de estrategias y las operaciones se ejecutaron exitosamente. El sistema limitaba el número máximo de posiciones a tres por sesión, lo que impedía de manera efectiva la ejecución incontrolada de múltiples órdenes. A continuación se muestra una imagen que muestra un segmento del proceso de prueba retrospectiva en acción:

EA en Tester

Trend Constraint: Prueba en EURUSD M15

La imagen siguiente muestra que nuestras funciones de gestión de posiciones funcionan según lo previsto. Según la lógica implementada, hay un máximo de tres órdenes activas, cada una etiquetada con el comentario «Divergence Sell» (Venta por divergencia), lo que las hace fácilmente identificables y alineadas con la estrategia.


Trend Constraint: Máximo de 3 posiciones


Conclusión

Exploramos varios tipos de divergencias y las implementamos en código como parte de una estrategia de confluencia que combina indicadores como el RSI y el MACD para generar señales de trading precisas. Estas señales se perfeccionaron aún más incorporando restricciones diarias de tendencia de velas japonesas, lo que garantiza que se alineen con las tendencias generales del mercado para mejorar su fiabilidad. Nuestro Asesor Experto (EA) Trend Constraint ahora cuenta con tres estrategias distintas y configurables, lo que permite a los usuarios adaptar el EA a sus preferencias de negociación y adaptarse a diversas condiciones del mercado.

Para mejorar la gestión de las operaciones, hemos introducido funciones como números mágicos únicos para cada posición, lo que permite un control preciso de las operaciones abiertas y limita el número de posiciones por estrategia. Además, se desarrolló una función personalizada de bloqueo de ganancias para asegurar las ganancias mediante el ajuste dinámico de los niveles de stop-loss si el mercado se invierte antes de alcanzar el objetivo de Take Profit. Esta funcionalidad garantiza tanto la gestión de riesgos como la flexibilidad, lo que hace que el EA sea robusto y adaptable. A continuación se adjuntan los archivos fuente del EA. Le animamos a que los explore, pruebe varias configuraciones y comparta sus comentarios en la sección correspondiente. Nota: Estos ejemplos tienen fines exclusivamente educativos, y todas las pruebas deben realizarse en cuentas demo.

¡Feliz desarrollo, traders!

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

Archivos adjuntos |
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Indicador de previsión de volatilidad con Python Indicador de previsión de volatilidad con Python
Hoy pronosticaremos la volatilidad extrema futura utilizando una clasificación binaria. Asimismo, crearemos un indicador de previsión de volatilidad extrema usando el aprendizaje automático.
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.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 5): Volatility Navigator EA Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 5): Volatility Navigator EA
Determinar la dirección del mercado puede ser sencillo, pero saber cuándo entrar puede resultar complicado. Como parte de la serie titulada «Desarrollo de un kit de herramientas para el análisis de la acción del precio», me complace presentar otra herramienta que proporciona puntos de entrada, niveles de toma de ganancias y colocación de órdenes stop loss. Para lograrlo, hemos utilizado el lenguaje de programación MQL5. Profundicemos en cada paso de este artículo.