English Русский 中文 Español Deutsch 日本語
preview
Construindo um Modelo de Restrição de Tendência com Candlesticks (Parte 9): Expert Advisor de Múltiplas Estratégias (III)

Construindo um Modelo de Restrição de Tendência com Candlesticks (Parte 9): Expert Advisor de Múltiplas Estratégias (III)

MetaTrader 5Exemplos |
25 0
Clemence Benjamin
Clemence Benjamin

Introdução

No trading algorítmico, identificar pontos de entrada ideais dentro de uma tendência predominante continua sendo um grande desafio, pois muitas estratégias têm dificuldade em capturar o momento certo ou geram sinais falsos frequentes, resultando em desempenho abaixo do ideal. Esse problema é particularmente evidente em tendências diárias, onde pequenas flutuações podem prejudicar a precisão da execução.

A divergência oferece uma solução robusta ao atuar como filtro para identificar potenciais reversões ou continuações por meio de discrepâncias entre movimentos de preço e indicadores de momentum. Ao integrar a detecção de divergência ao Trend Constraint Expert Advisor, os traders podem aprimorar significativamente sua precisão na identificação de níveis de entrada.

Essa abordagem melhora a precisão das operações e também garante uma negociação consistente e eficiente quando combinada com as capacidades avançadas do MQL5. Neste artigo, exploramos os fundamentos da divergência, os passos para integrá-la em Expert Advisors MQL5, as melhorias no Trend Constraint Expert Advisor com novas condições de execução de trade e percepções de backtesting para demonstrar sua aplicação prática.

Conteúdo principal:

  1. Os Fundamentos da Divergência
  2. Etapas para Integrar a Detecção de Divergência
  3. Melhorias no Trend Constraint Expert Advisor: Implementando novas condições de execução de trade para aproveitar a divergência.
  4. Resultados de Back-testing e Aplicação Prática
  5. Conclusão


Os Fundamentos da Divergência

A divergência é um conceito-chave na análise técnica, fornecendo aos traders percepções sobre possíveis reversões ou continuações de preço ao comparar os movimentos do preço com a direção de um indicador. 

Significado da Divergência:

A divergência ocorre quando o preço de um ativo se move em oposição a um indicador técnico, frequentemente sinalizando uma tendência enfraquecida ou uma reversão iminente. Esse conceito é particularmente útil para identificar quando uma tendência pode estar prestes a corrigir ou reverter completamente.

Tipos de Divergência:

  1. A Divergência de Alta (Bullish Divergence) é observada quando o preço do ativo atinge mínimas mais baixas, mas o indicador, como o RSI, começa a mostrar mínimas mais altas, sugerindo que o momentum de queda está enfraquecendo. Isso pode significar que a pressão vendedora está diminuindo, potencialmente preparando uma movimentação de alta.
  2. A Divergência de Baixa (Bearish Divergence) é observada quando o preço atinge máximas mais altas, mas o indicador mostra máximas mais baixas, indicando que o momentum de alta está diminuindo. Isso pode prenunciar uma queda no preço.

Verificação de Referências:

A divergência é um conceito central na análise técnica, influenciando o comportamento do mercado e as estratégias dos traders. Bart e Masse (1981), em "Divergence of Opinion and Risk", enfatizam como discrepâncias na opinião do mercado podem aumentar o risco e a volatilidade dos preços, refletindo o papel da divergência na análise técnica.

Evidências empíricas de Tilehnouei e Shivaraj (2013) sugerem que ferramentas como o MACD podem superar o RSI em determinados contextos, oferecendo percepções valiosas sobre o momentum de mercado através de sinais de divergência. A partir dessa pesquisa, podemos dizer que a integração da divergência com outros indicadores, como a interação entre RSI, MACD e price action, reforça sua utilidade em uma abordagem abrangente de negociação, conforme apoiado por diversas fontes do setor.

Na próxima seção, daremos um passo prático para implementar a ideia de divergência em nosso desenvolvimento de EA.


Etapas para Integrar a Detecção de Divergência

Para incorporar a detecção de divergência em um Expert Advisor (EA) MQL5, começamos calculando os valores do Índice de Força Relativa (RSI) usando funções como iRSI() e comparando-os com o price action. Extremos de preço significativos são identificados usando iHigh() e iLow() ao longo de um determinado período. Para este projeto, classificarei divergência em dois tipos: regular (reversão) e oculta.

Divergência Regular:

A divergência regular sinaliza potenciais reversões de tendência, onde uma configuração de alta ocorre quando o preço faz uma mínima mais baixa enquanto o indicador forma uma mínima mais alta, e uma configuração de baixa surge quando o preço atinge uma máxima mais alta, mas o indicador mostra uma máxima mais baixa.

// 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 essas divergências regulares, aqui estão duas imagens retratando divergência de alta e divergência de baixa, respectivamente:

Boom 300 IndexH4_Divergência de Alta

Boom 300 IndexH4-Divergência de Alta: (Preço com mínima mais baixa B contra valor de RSI mais alto em D)


Boom 300 IndexH4_Divergência de Baixa

Boom 300 IndexH4-Divergência de Baixa: (Preço com máxima mais alta B contra valor de RSI com máxima mais baixa em D)

Divergência Oculta:

A divergência oculta, por outro lado, sugere continuação da tendência. Uma divergência oculta de alta ocorre em uma tendência de alta quando o preço faz uma mínima mais alta enquanto o indicador forma uma mínima mais baixa; já uma divergência oculta de baixa aparece em uma tendência de baixa quando o preço faz uma máxima mais baixa e o indicador forma uma máxima mais alta.

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

Abaixo estão imagens ilustrando divergência oculta de alta e divergência oculta de baixa. Certifique-se de comparar com as descrições fornecidas anteriormente e pratique identificar padrões semelhantes em seus próprios gráficos.

Boom 300 IndexH4_Divergência_Oculta_de_Alta

Boom 300 IndexH4: Divergência Oculta de Alta

Boom 300 IndexH4_Divergência_Oculta_de_Baixa

Boom 300 IndexH4: Divergência Oculta de Baixa

Integrar esses tipos de divergência ao EA exige programar condições para detectá-las a cada tick ou fechamento de barra, usando ferramentas como iRSI() ou iMACD() para os cálculos dos indicadores. Uma vez detectados, os sinais de divergência são alinhados com restrições de tendência, identificadas usando o sentimento diário do mercado.


Melhorias no Trend Constraint Expert Advisor: Implementando novas condições de execução de trades para aproveitar a divergência.

Sobre a seção acima, quando a divergência for finalmente detectada, precisaremos de um indicador adicional para confirmar e sinalizar o processo de execução das ordens. Existem várias opções para isso, mas consideraremos a implementação do MACD e do RSI para tal. Aqui está uma lista de outros indicadores de confirmação opcionais: 

  1. Bandas de Bollinger
  2. Oscilador Estocástico
  3. On Balance Volume (OBV) e Preço Médio Ponderado por Volume (VWAP)
  4. Average Dynamic Index (ADX)

Moving Average Convergence Divergence (MACD):

Por que usar: O MACD pode confirmar mudanças de momentum. Se houver divergência detectada com o RSI, o MACD pode fornecer uma confirmação secundária da força ou enfraquecimento da tendência. 

Como funciona:

O EA buscará cruzamentos das linhas do MACD ou mudanças no histograma que estejam alinhadas com as divergências identificadas. Por exemplo, uma divergência de baixa pode ser confirmada se a linha do MACD cruzar abaixo da linha de sinal ou se o histograma começar a diminuir.

Destaques do indicador MACD:

Para visualizar o indicador incorporado como o MACD, podemos acessá-lo na pasta Examples, dentro da pasta Indicators, no software MetaEditor 5. Veja a imagem explicativa abaixo.

Acessar o código-fonte do MACD no MetaEditor 5

MetaEditor 5: Acessando o arquivo fonte do MACD

O motivo de acessar o código é para descobrirmos facilmente como os buffers são desenhados, para que possamos adaptá-los facilmente ao nosso Expert Advisor. Abaixo está um trecho de código das declarações de buffers dentro do indicador que nos interessam.

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

int    ExtFastMaHandle;
int    ExtSlowMaHandle;

Agora, com os buffers em mente, vamos passar por um processo de desenvolvimento passo a passo, conforme descrito abaixo.

Desenvolvimento da Estratégia de Divergência: 

Passo 1: Declarações e Parâmetros de Entrada

 Começamos declarando parâmetros de entrada para a estratégia de divergência, buffers do MACD e inicializando a classe 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

Passo 2: Inicializar o Indicador MACD

Inicializar o handle do MACD e alocar memória para o buffer durante o 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;
}

Passo 3: Detecção de Divergência

A divergência ocorre quando a ação do preço de um ativo diverge de um indicador, neste caso, o MACD. Esta estratégia detecta quatro tipos de divergências: alta regular, alta oculta, baixa regular e baixa oculta. Cada tipo tem condições específicas, comparando máximas ou mínimas do preço com as correspondentes do MACD para determinar a presença da divergência.

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

Passo 4: Lógica de Operação

Primeiro, a estratégia garante que o trading por divergência esteja habilitado e que não haja mais de 3 posições baseadas em divergência abertas. Ela recupera os dados do buffer do MACD, tenta novamente em caso de falha e só executa operações em barras completas. Além disso, alinha as operações com tendências diárias de candles, garantindo compras apenas em dias de alta e vendas apenas em dias de baixa.

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

Passo 5: Execução de Ordens

Uma vez detectada a divergência, a estratégia executa as operações com parâmetros predefinidos como tamanho do lote, stop loss e take profit. A função ExecuteDivergenceOrder calcula os níveis apropriados com base na direção da operação e usa o objeto trade para enviar ordens de compra ou venda.

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

Passo 6: Gerenciamento de Ordens

Para evitar excesso de operações, a estratégia usa a função utilitária CountOrdersByMagic para contar todas as posições abertas com o número mágico especificado. Isso garante respeito ao limite máximo de posições definido para operações baseadas em divergência.

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 (Magic Number): 

Outro aspecto importante é dar identidade às nossas posições. Neste caso, atribuímos um número mágico exclusivo às operações controladas pela estratégia de divergência.

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

Passo 7: Proteção de Lucros

Introduzimos uma Função de Proteção de Lucros que incorpora uma Lógica de Travamento de Lucros para o nosso Trend Constraint Expert Advisor, a fim de proteger dinamicamente os lucros de operações abertas. A função LockProfits escaneia todas as posições ativas, identificando aquelas com lucro superior a um limite de 100 pontos. Para cada posição qualificada, ela calcula um novo nível de stop-loss baseado no parâmetro profitLockerPoints (por exemplo, 20 pontos a partir do preço de entrada).

Esse ajuste garante que o stop-loss se aproxime do preço atual, efetivamente travando os ganhos. Para posições de compra, o stop-loss é movido acima do preço de entrada, enquanto para vendas ele é movido abaixo. A função atualiza o stop-loss somente se o novo nível oferecer melhor proteção, garantindo uma gestão de risco ideal. As modificações bem-sucedidas são registradas com uma mensagem para acompanhamento. Esse recurso ajuda a proteger os lucros enquanto permite que as operações tenham potencial de crescimento adicional.

Aqui está a função de travamento de lucros:

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

Etapa 8: Integrar em OnTick()

Chame a lógica de trading por divergência dentro do loop principal do EA.

void OnTick()
{
    CheckDivergenceTrading();
}

Passo 9: Encerramento

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

Integração da estratégia no Trend Constraint Expert principal.

No artigo anterior desta série desenvolvemos um expert advisor baseado em Donchian Channel, marcando duas estratégias em um expert. Hoje, incorporamos a terceira estratégia e usamos o tipo de dado booleano para ativar ou desativar as estratégias.

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

Definimos a estratégia de Divergência como verdadeira para não haver confusão durante o teste da estratégia.

Por fim, integramos cuidadosamente nossa estratégia ao 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 Backtesting e Aplicação Prática

Para realizar nosso teste, precisamos localizar o Trend Constraint Expert na aba de Experts do terminal. Certifique-se de estar rodando em uma conta demo. Uma janela do Strategy Tester é aberta e as configurações de entrada podem ser ajustadas para diferentes otimizações. Veja as imagens abaixo com as configurações padrão:

Configurações de Entrada

Trend Constraint Expert: Configurações de demonstração 

Executamos o Strategy Tester e as operações foram realizadas com sucesso. O sistema limitou o número máximo de posições a três por sessão, prevenindo efetivamente múltiplas execuções descontroladas de ordens. Abaixo está uma imagem mostrando um segmento do processo de backtesting em ação:

EA no Tester

Trend Constraint Expert: Testando em EURUSD M15

A imagem abaixo demonstra que nossas funções de gerenciamento de posições estão funcionando conforme o esperado. De acordo com a lógica implementada, um máximo de três ordens estão ativas, cada uma com o comentário Divergence Sell, tornando-as facilmente identificáveis e alinhadas à estratégia.


Trend Constraint Expert: Máximo de 3 posições por


Conclusão

Exploramos diversos tipos de divergências e os implementamos em código como parte de uma estratégia de confluência que combina indicadores como RSI e MACD para gerar sinais de trading precisos. Esses sinais foram ainda refinados com a incorporação das restrições de tendência de candles diários, garantindo que estejam alinhados com as tendências mais amplas do mercado para maior confiabilidade. Nosso Trend Constraint Expert Advisor (EA) agora possui três estratégias distintas e configuráveis, permitindo que o usuário ajuste o EA às suas preferências de negociação e se adapte a diferentes condições de mercado.

Para melhorar o gerenciamento das operações, introduzimos recursos como números MAGIC únicos para cada posição, permitindo controle preciso das operações abertas e limitando o número de posições por estratégia. Além disso, foi desenvolvida uma função personalizada de travamento de lucros para proteger os ganhos, ajustando dinamicamente os níveis de stop-loss caso o mercado reverta antes de atingir o Take Profit. Essa funcionalidade garante tanto gestão de risco quanto flexibilidade, tornando o EA robusto e adaptável. Abaixo estão anexados os arquivos fonte do programa do EA. Incentivamos você a explorá-los, testar diversas configurações e compartilhar seu feedback na seção de comentários. Atenção: Estes exemplos são apenas para fins educacionais e todos os testes devem ser feitos em contas demo.

Boas programações, traders!

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16549

Arquivos anexados |
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Análise angular dos movimentos de preço: um modelo híbrido de previsão dos mercados financeiros Análise angular dos movimentos de preço: um modelo híbrido de previsão dos mercados financeiros
O que é análise angular dos mercados financeiros? Como usar os ângulos de movimento de preço e o aprendizado de máquina para prever com precisão de 67? Como combinar um modelo de regressão e um modelo de classificação com características angulares e obter um algoritmo funcional? O que Gann tem a ver com isso? Por que os ângulos de movimento do preço são bons indicadores para o aprendizado de máquina?
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Definição de sobrecompra e sobrevenda segundo a teoria do caos Definição de sobrecompra e sobrevenda segundo a teoria do caos
Determinamos as zonas de sobrecompra e sobrevenda do mercado a partir da teoria do caos: uma integração dos princípios da teoria do caos, da geometria fractal e das redes neurais para prever os mercados financeiros. O estudo demonstra o uso do expoente de Lyapunov como medida da natureza caótica do mercado e a adaptação dinâmica dos sinais de trade. A metodologia inclui um algoritmo de geração de ruído fractal, ativação tangencial hiperbólica e otimização com momento.