English Русский 中文 Español Deutsch 日本語
preview
Desenvolvimento de um sistema personalizado de detecção do regime de mercado em MQL5 (Parte 2): Expert Advisor

Desenvolvimento de um sistema personalizado de detecção do regime de mercado em MQL5 (Parte 2): Expert Advisor

MetaTrader 5Negociação |
73 1
Sahil Bagdi
Sahil Bagdi
  1. Introdução
  2. Criação de um EA adaptativo
  3. Considerações práticas e otimização
  4. Indicador: regimes para vários timeframes
  5. Avaliação do EA adaptativo: teste em dados históricos
  6. Conclusão


Introdução

Na Parte 1 desta série de artigos, estabelecemos a base para lidar com os desafios associados à dinâmica mutável do mercado. Criamos uma base estatística robusta, desenvolvemos a classe CMarketRegimeDetector, capaz de classificar objetivamente o comportamento do mercado, e criamos um indicador personalizado (MarketRegimeIndicator) para mostrar esses regimes diretamente nos nossos gráficos. Passamos do reconhecimento do problema, ou seja, a perda de desempenho de estratégias estáticas em mercados dinâmicos, ao desenvolvimento de um sistema capaz de identificar o estado predominante do mercado: tendência (de alta ou de baixa), lateralização ou volatilidade (consulte a Parte 1 como referência).

No entanto, simplesmente identificar o regime vigente é apenas metade do caminho. A verdadeira força está em adaptar nossa abordagem de negociação com base nesse conhecimento. O detector, por mais sofisticado que seja, continua sendo uma ferramenta analítica até que seus insights sejam convertidos em decisões práticas de negociação. E se o nosso sistema de negociação pudesse trocar de marcha automaticamente, acionando a lógica de seguimento de tendência quando as tendências estão fortes, passando para táticas de reversão à média em mercados laterais e ajustando parâmetros de risco durante mudanças bruscas de volatilidade?

É exatamente essa lacuna que vamos preencher na Parte 2. A partir da base estabelecida anteriormente, agora nos concentraremos na aplicação prática e no aprimoramento. Neste artigo, veremos o seguinte:

  • Criação de um EA adaptativo. Construiremos o EA completo MarketRegimeEA, que integra nosso detector CMarketRegimeDetector para selecionar e executar automaticamente diferentes estratégias de negociação (seguimento de tendência, reversão à média, rompimento), adaptadas ao regime detectado.
  • Implementação da gestão de riscos específica de cada regime. O EA mostrará como ajustar parâmetros como tamanho do lote, stop-loss e take-profit, dependendo do estado atual do mercado.
  • Considerações práticas. Veremos aspectos essenciais da implementação em condições reais, incluindo a otimização de parâmetros para diferentes instrumentos e timeframes.
  • Tratamento das transições entre regimes. Estratégias para gerenciar momentos críticos em que o mercado passa de um regime para outro, permitindo ajustes mais suaves da estratégia.
  • Métodos de integração. Discussão sobre as possibilidades de integrar o sistema de detecção de regimes de mercado (Market Regime Detection System) às suas estruturas de negociação existentes para aumentar sua adaptabilidade.

Ao final da segunda parte, você entenderá não apenas como detectar regimes de mercado, mas também como criar um sistema de negociação automatizado que adapta seu comportamento de forma inteligente, buscando um desempenho mais estável em diferentes condições dos mercados financeiros. Vamos transformar nosso sistema de detecção de regimes em uma solução de negociação verdadeiramente adaptativa.


Criação de um EA adaptativo

Nesta seção, criaremos um EA que usará nosso detector de regimes de mercado para adaptar a estratégia de negociação do próprio EA conforme as condições atuais do mercado. Isso mostra as possibilidades de integrar a detecção do regime de mercado a um sistema de negociação completo.

EA MarketRegimeEA

Nosso EA usará abordagens de negociação distintas para diferentes regimes de mercado:
  • Em mercados de tendência, ele usará estratégias de seguimento de tendência
  • Em mercados laterais, ele usará estratégias de reversão à média
  • Em mercados voláteis, ele usará estratégias de rompimento com tamanhos de posição reduzidos

Veja a implementação:

// Include the Market Regime Detector
#include <MarketRegimeEnum.mqh>
#include <MarketRegimeDetector.mqh>

// EA input parameters
input int      LookbackPeriod = 100;       // Lookback period for calculations
input int      SmoothingPeriod = 10;       // Smoothing period for regime transitions
input double   TrendThreshold = 0.2;       // Threshold for trend detection (0.1-0.5) 
input double   VolatilityThreshold = 1.5;  // Threshold for volatility detection (1.0-3.0)

// Trading parameters
input double   TrendingLotSize = 0.1;      // Lot size for trending regimes
input double   RangingLotSize = 0.05;      // Lot size for ranging regimes
input double   VolatileLotSize = 0.02;     // Lot size for volatile regimes
input int      TrendingStopLoss = 100;     // Stop loss in points for trending regimes
input int      RangingStopLoss = 50;       // Stop loss in points for ranging regimes
input int      VolatileStopLoss = 150;     // Stop loss in points for volatile regimes
input int      TrendingTakeProfit = 200;   // Take profit in points for trending regimes
input int      RangingTakeProfit = 80;     // Take profit in points for ranging regimes
input int      VolatileTakeProfit = 300;   // Take profit in points for volatile regimes

// Global variables
CMarketRegimeDetector *Detector = NULL;
int OnBarCount = 0;
datetime LastBarTime = 0;

O EA inclui parâmetros de configuração tanto para a detecção do regime de mercado quanto para a estratégia de negociação. Observe como usamos diferentes tamanhos de lote e níveis de stop-loss e take-profit para diferentes regimes de mercado. Isso permite que o EA adapte sua abordagem de gestão de riscos às condições atuais do mercado.

Inicialização do EA

A função OnInit() cria e configura o detector de regime de mercado:

int OnInit()
{
    // Create and initialize the Market Regime Detector
    Detector = new CMarketRegimeDetector(LookbackPeriod, SmoothingPeriod);
    if(Detector == NULL)
    {
        Print("Failed to create Market Regime Detector");
        return INIT_FAILED;
    }
    
    // Configure the detector
    Detector.SetTrendThreshold(TrendThreshold);
    Detector.SetVolatilityThreshold(VolatilityThreshold);
    Detector.Initialize();
    
    // Initialize variables
    OnBarCount = 0;
    LastBarTime = 0;
    
    return INIT_SUCCEEDED;
}

Essa função cria e configura o detector de regime de mercado com os parâmetros definidos pelo usuário. Além disso, ela inicializa as variáveis de contagem de barras, que usaremos para acompanhar novas barras.

Processamento de ticks do EA

A função OnTick() processa novos dados de preço e implementa a estratégia de negociação com base no regime detectado:

void OnTick()
{
    // Check for new bar
    datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 0);
    if(currentBarTime == LastBarTime)
        return; // No new bar
        
    LastBarTime = currentBarTime;
    OnBarCount++;
    
    // Wait for enough bars to accumulate
    if(OnBarCount < LookbackPeriod)
    {
        Comment("Accumulating data: ", OnBarCount, " of ", LookbackPeriod, " bars");
        return;
    }
    
    // Get price data
    double close[];
    ArraySetAsSeries(close, true);
    int copied = CopyClose(Symbol(), PERIOD_CURRENT, 0, LookbackPeriod, close);
    
    if(copied != LookbackPeriod)
    {
        Print("Failed to copy price data: copied = ", copied, " of ", LookbackPeriod);
        return;
    }
    
    // Process data with the detector
    if(!Detector.ProcessData(close, LookbackPeriod))
    {
        Print("Failed to process data with Market Regime Detector");
        return;
    }
    
    // Get current market regime
    ENUM_MARKET_REGIME currentRegime = Detector.GetCurrentRegime();
    
    // Display current regime information
    string regimeText = "Current Market Regime: " + Detector.GetRegimeDescription();
    string trendText = "Trend Strength: " + DoubleToString(Detector.GetTrendStrength(), 4);
    string volatilityText = "Volatility: " + DoubleToString(Detector.GetVolatility(), 4);
    
    Comment(regimeText + "\n" + trendText + "\n" + volatilityText);
    
    // Execute trading strategy based on market regime
    ExecuteRegimeBasedStrategy(currentRegime);
}
Essa função:
  1. Verifica se há uma nova barra para evitar cálculos desnecessários
  2. Aguarda até que barras suficientes se acumulem para uma detecção confiável do regime
  3. Obtém os dados de preço mais recentes
  4. Processa os dados usando o detector de regimes de mercado
  5. Obtém o regime de mercado atual
  6. Exibe informações sobre o regime
  7. Implementa a estratégia de negociação conforme o regime

O uso de ArraySetAsSeries(close, true) é importante, pois configura o array de preços como série temporal, com o preço mais recente no índice 0. Essa é a convenção padrão de indexação em MQL5 para trabalhar com dados de séries temporais.

Estratégia de negociação conforme o regime

A função ExecuteRegimeBasedStrategy() implementa diferentes abordagens de trading para diferentes regimes de mercado:

void ExecuteRegimeBasedStrategy(ENUM_MARKET_REGIME regime)
{
    // Check if we already have open positions
    if(PositionsTotal() > 0)
        return; // Don't open new positions if we already have one
    
    // Get current market information
    double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
    double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
    
    // Determine trading parameters based on regime
    double lotSize = 0.0;
    int stopLoss = 0;
    int takeProfit = 0;
    ENUM_ORDER_TYPE orderType = ORDER_TYPE_BUY;
    
    switch(regime)
    {
        case REGIME_TRENDING_UP: {
            lotSize = TrendingLotSize;
            stopLoss = TrendingStopLoss;
            takeProfit = TrendingTakeProfit;
            orderType = ORDER_TYPE_BUY; // Buy in uptrend
            break;
        }
        case REGIME_TRENDING_DOWN: {
            lotSize = TrendingLotSize;
            stopLoss = TrendingStopLoss;
            takeProfit = TrendingTakeProfit;
            orderType = ORDER_TYPE_SELL; // Sell in downtrend
            break;
	}
            
        case REGIME_RANGING: {
            // In ranging markets, we can use mean-reversion strategies
            // For simplicity, we'll use RSI to determine overbought/oversold
            double rsi[];
            ArraySetAsSeries(rsi, true);
            int rsiCopied = CopyBuffer(iRSI(Symbol(), PERIOD_CURRENT, 14, PRICE_CLOSE), 0, 0, 2, rsi);
            
            if(rsiCopied != 2)
                return;
                
            lotSize = RangingLotSize;
            stopLoss = RangingStopLoss;
            takeProfit = RangingTakeProfit;
            
            if(rsi[0] < 30) // Oversold
                orderType = ORDER_TYPE_BUY;
            else if(rsi[0] > 70) // Overbought
                orderType = ORDER_TYPE_SELL;
            else
                return; // No signal
                
            break;
        }

        case REGIME_VOLATILE: {
            // In volatile markets, we can use breakout strategies
            // For simplicity, we'll use Bollinger Bands
            double upper[], lower[];
            ArraySetAsSeries(upper, true);
            ArraySetAsSeries(lower, true);
            
            int bbCopied1 = CopyBuffer(iBands(Symbol(), PERIOD_CURRENT, 20, 2, 0, PRICE_CLOSE), 1, 0, 2, upper);
            int bbCopied2 = CopyBuffer(iBands(Symbol(), PERIOD_CURRENT, 20, 2, 0, PRICE_CLOSE), 2, 0, 2, lower);
            
            if(bbCopied1 != 2 || bbCopied2 != 2)
                return;
                
            lotSize = VolatileLotSize;
            stopLoss = VolatileStopLoss;
            takeProfit = VolatileTakeProfit;
            
            double close[];
            ArraySetAsSeries(close, true);
            int copied = CopyClose(Symbol(), PERIOD_CURRENT, 0, 2, close);
            
            if(copied != 2)
                return;
                
            if(close[1] < upper[1] && close[0] > upper[0]) // Breakout above upper band
                orderType = ORDER_TYPE_BUY;
            else if(close[1] > lower[1] && close[0] < lower[0]) // Breakout below lower band
                orderType = ORDER_TYPE_SELL;
            else
                return; // No signal
                
            break;
        }

        default:
            return; // No trading in undefined regime
    }
    
    // Calculate stop loss and take profit levels
    double slLevel = 0.0;
    double tpLevel = 0.0;
    
    if(orderType == ORDER_TYPE_BUY)
    {
        slLevel = ask - stopLoss * point;
        tpLevel = ask + takeProfit * point;
    }
    else if(orderType == ORDER_TYPE_SELL)
    {
        slLevel = bid + stopLoss * point;
        tpLevel = bid - takeProfit * point;
    }
    
    // Execute trade
    MqlTradeRequest request;
    MqlTradeResult result;
    
    ZeroMemory(request);
    ZeroMemory(result);
    
    request.action = TRADE_ACTION_DEAL;
    request.symbol = Symbol();
    request.volume = lotSize;
    request.type = orderType;
    request.price = (orderType == ORDER_TYPE_BUY) ? ask : bid;
    request.sl = slLevel;
    request.tp = tpLevel;
    request.deviation = 10;
    request.magic = 123456; // Magic number for this EA
    request.comment = "Market Regime: " + Detector.GetRegimeDescription();
    request.type_filling = ORDER_FILLING_FOK;
    
    bool success = OrderSend(request, result);
    
    if(success)
    {
        Print("Trade executed successfully: ", result.retcode, " ", result.comment);
    }
    else
    {
        Print("Trade execution failed: ", result.retcode, " ", result.comment);
    }
}
Essa função implementa uma estratégia abrangente de negociação conforme o regime:
  1. Regimes de tendência. Usa estratégias de seguimento de tendência, comprando em tendências de alta e vendendo em tendências de baixa.
  2. Regimes laterais. Usa estratégias de reversão à média com base no RSI, comprando em condições de sobrevenda e vendendo em condições de sobrecompra.
  3. Regimes voláteis. Usa estratégias de rompimento com base nas Bandas de Bollinger, com tamanhos de posição reduzidos para a gestão de riscos.
Para cada regime, a função define os tamanhos de lote e os níveis de stop-loss e take-profit correspondentes, de acordo com os parâmetros configurados pelo usuário. Em seguida, ela calcula os níveis específicos de stop-loss e take-profit e executa a operação por meio da função OrderSend().

O uso de estratégias distintas para cada regime é a principal inovação deste EA. Ao adaptar sua abordagem às condições atuais do mercado, o EA pode obter um desempenho mais estável em uma ampla variedade de condições de mercado.

Limpeza do EA

A função OnDeinit() garante a limpeza correta quando o EA é removido:

void OnDeinit(const int reason)
{
    // Clean up
    if(Detector != NULL)
    {
        delete Detector;
        Detector = NULL;
    }
    
    // Clear the comment
    Comment("");
}

Essa função exclui o objeto Market Regime Detector para evitar vazamentos de memória e remove todos os comentários do gráfico.

Vantagens da negociação baseada em regimes de mercado

Este EA demonstra várias vantagens principais da negociação baseada em regimes de mercado:
  1. Adaptabilidade. O EA adapta automaticamente sua estratégia de negociação às condições atuais do mercado, usando abordagens diferentes para regimes diferentes.
  2. Gestão de riscos. O EA ajusta os tamanhos das posições de acordo com a volatilidade do mercado, usando posições menores em regimes mais voláteis para gerenciar o risco.
  3. Seleção da estratégia. O EA seleciona a estratégia mais adequada para cada regime, usando seguimento de tendência em mercados de tendência e reversão à média em mercados laterais.
  4. Transparência. O EA exibe de forma clara o regime de mercado atual e suas principais características, fornecendo aos traders um contexto valioso para a tomada de decisões de trading.

Ao implementar a detecção do regime de mercado em seus sistemas de negociação, você pode alcançar vantagens semelhantes, criando estratégias mais robustas e adaptativas, que têm bom desempenho em uma ampla variedade de condições de mercado.

Na próxima seção, discutiremos considerações práticas sobre a implementação e a otimização do sistema de detecção do regime de mercado em condições reais de negociação.


Considerações práticas e otimização

Nesta seção final, discutiremos os aspectos práticos da implementação e da otimização do sistema de detecção do regime de mercado em condições reais de negociação. Veremos a otimização de parâmetros, o tratamento das transições entre regimes e a integração com sistemas de negociação existentes.

Otimização de parâmetros

A eficácia do nosso sistema de detecção de regimes de mercado depende em grande parte da escolha dos parâmetros. Estes são os principais parâmetros que os traders devem otimizar para seus instrumentos de negociação e timeframes específicos:

  1. Período de lookback. O período de lookback determina a quantidade de dados históricos usada para detectar o regime. Períodos mais longos proporcionam uma classificação de regimes mais estável, mas podem ser menos sensíveis a mudanças recentes no mercado. Períodos mais curtos são mais sensíveis, mas podem gerar mais sinais falsos.
    // Example of testing different lookback periods
    for(int lookback = 50; lookback <= 200; lookback += 25)
    {
        CMarketRegimeDetector detector(lookback, SmoothingPeriod);
        detector.SetTrendThreshold(TrendThreshold);
        detector.SetVolatilityThreshold(VolatilityThreshold);
        
        // Process historical data and evaluate performance
        // ...
    }
    
    Em geral, períodos de lookback de 50 a 200 barras costumam ser adequados para a maioria dos instrumentos e timeframes. O valor ideal depende da duração típica dos regimes de mercado para o instrumento específico negociado.

  2. Limiar de tendência. O limiar de tendência determina quão forte a tendência precisa ser para que o mercado possa ser classificado como de tendência. Limiares mais altos resultam em menos classificações de tendência, mas com maior confiança. Limiares mais baixos permitem identificar mais tendências, mas também podem incluir tendências mais fracas.
    // Example of testing different trend thresholds
    for(double threshold = 0.1; threshold <= 0.5; threshold += 0.05)
    {
        CMarketRegimeDetector detector(LookbackPeriod, SmoothingPeriod);
        detector.SetTrendThreshold(threshold);
        detector.SetVolatilityThreshold(VolatilityThreshold);
        
        // Process historical data and evaluate performance
        // ...
    }
    Limiares de tendência entre 0.1 e 0.3 são pontos de partida comuns. O valor ideal depende do comportamento típico de tendência do instrumento.

  3. Limiar de volatilidade. O limiar de volatilidade determina qual nível de volatilidade é necessário para classificar o mercado como volátil. Limiares mais altos resultam em menos classificações como volátil, enquanto limiares mais baixos identificam mais períodos voláteis.
    // Example of testing different volatility thresholds
    for(double threshold = 1.0; threshold <= 3.0; threshold += 0.25)
    {
        CMarketRegimeDetector detector(LookbackPeriod, SmoothingPeriod);
        detector.SetTrendThreshold(TrendThreshold);
        detector.SetVolatilityThreshold(threshold);
        
        // Process historical data and evaluate performance
        // ...
    }
    Limiares de volatilidade entre 1.5 e 2.5 são pontos de partida comuns. O valor ideal depende das características típicas de volatilidade do instrumento.

Tratamento das transições entre regimes

Observação. Os 5 blocos de código a seguir não são exatamente uma implementação da ideia, mas apenas ideias e pseudocódigo de como essa implementação poderia ser.

As transições entre regimes são momentos críticos que exigem atenção especial. Mudanças bruscas na estratégia de negociação durante transições podem levar à má execução das operações e ao aumento do slippage. Veja estratégias para gerenciar as transições entre regimes de forma mais eficaz.

  1. Suavização das transições. O parâmetro do período de suavização ajuda a reduzir o nível de ruído na classificação dos regimes, exigindo que o regime se mantenha durante uma quantidade mínima de barras antes de ser reconhecido:
    // Example of implementing smoothed regime transitions
    ENUM_MARKET_REGIME SmoothRegimeTransition(ENUM_MARKET_REGIME newRegime)
    {
        static ENUM_MARKET_REGIME regimeHistory[20];
        static int historyCount = 0;
        
        // Add new regime to history
        for(int i = 19; i > 0; i--)
            regimeHistory[i] = regimeHistory[i-1];
            
        regimeHistory[0] = newRegime;
        
        if(historyCount < 20)
            historyCount++;
            
        // Count occurrences of each regime
        int regimeCounts[5] = {0};
        
        for(int i = 0; i < historyCount; i++)
            regimeCounts[regimeHistory[i]]++;
            
        // Find most common regime
        int maxCount = 0;
        ENUM_MARKET_REGIME dominantRegime = REGIME_UNDEFINED;
        
        for(int i = 0; i < 5; i++)
        {
            if(regimeCounts[i] > maxCount)
            {
                maxCount = regimeCounts[i];
                dominantRegime = (ENUM_MARKET_REGIME)i;
            }
        }
        
        return dominantRegime;
    }
    Essa função mantém o histórico das classificações mais recentes dos regimes de mercado e retorna o regime mais frequente, reduzindo o impacto de oscilações temporárias.

  2. Alteração gradual do tamanho da posição. Durante transições entre regimes, muitas vezes é mais adequado ajustar gradualmente o tamanho das posições, em vez de fazer mudanças bruscas:
    // Example of gradual position sizing during transitions
    double CalculateTransitionLotSize(ENUM_MARKET_REGIME previousRegime, 
                                     ENUM_MARKET_REGIME currentRegime,
                                     int transitionBars,
                                     int maxTransitionBars)
    {
        // Base lot sizes for each regime
        double regimeLotSizes[5] = {
            TrendingLotSize,    // REGIME_TRENDING_UP
            TrendingLotSize,    // REGIME_TRENDING_DOWN
            RangingLotSize,     // REGIME_RANGING
            VolatileLotSize,    // REGIME_VOLATILE
            0.0                 // REGIME_UNDEFINED
        };
        
        // If not in transition, use current regime's lot size
        if(previousRegime == currentRegime || transitionBars >= maxTransitionBars)
            return regimeLotSizes[currentRegime];
            
        // Calculate weighted average during transition
        double previousWeight = (double)(maxTransitionBars - transitionBars) / maxTransitionBars;
        double currentWeight = (double)transitionBars / maxTransitionBars;
        
        return regimeLotSizes[previousRegime] * previousWeight + 
               regimeLotSizes[currentRegime] * currentWeight;
    }
    Essa função calcula a média ponderada dos tamanhos das posições durante mudanças de regime, permitindo um ajuste mais suave à medida que o mercado muda de regime.

Integração com sistemas de negociação existentes

O sistema de detecção do regime de mercado pode ser integrado a sistemas de negociação existentes para melhorar o desempenho desses sistemas. Veja estratégias para uma integração eficaz:

  1. Seleção da estratégia. Use o regime de mercado detectado para escolher a estratégia de negociação mais adequada:
    // Example of strategy selection based on market regime
    bool ExecuteTradeSignal(ENUM_MARKET_REGIME regime, int strategySignal)
    {
        // Strategy signal: 1 = buy, -1 = sell, 0 = no signal
        
        switch(regime)
        {
            case REGIME_TRENDING_UP:
            case REGIME_TRENDING_DOWN:
                // In trending regimes, only take signals in the direction of the trend
                if((regime == REGIME_TRENDING_UP && strategySignal == 1) ||
                   (regime == REGIME_TRENDING_DOWN && strategySignal == -1))
                    return true;
                break;
                
            case REGIME_RANGING:
                // In ranging regimes, take all signals
                if(strategySignal != 0)
                    return true;
                break;
                
            case REGIME_VOLATILE:
                // In volatile regimes, be more selective
                // Only take strong signals (implementation depends on strategy)
                if(IsStrongSignal(strategySignal))
                    return true;
                break;
                
            default:
                // In undefined regimes, don't trade
                break;
        }
        
        return false;
    }
    Essa função filtra os sinais de negociação conforme o regime de mercado atual, executando apenas as operações compatíveis com as características do regime.

  2. Adaptação de parâmetros. Adapte os parâmetros da estratégia conforme o regime de mercado detectado:
    // Example of parameter adaptation based on market regime
    void AdaptStrategyParameters(ENUM_MARKET_REGIME regime)
    {
        switch(regime)
        {
            case REGIME_TRENDING_UP:
            case REGIME_TRENDING_DOWN:
                // In trending regimes, use longer moving averages
                FastPeriod = 20;
                SlowPeriod = 50;
                // Use wider stop losses
                StopLoss = TrendingStopLoss;
                // Use larger take profits
                TakeProfit = TrendingTakeProfit;
                break;
                
            case REGIME_RANGING:
                // In ranging regimes, use shorter moving averages
                FastPeriod = 10;
                SlowPeriod = 25;
                // Use tighter stop losses
                StopLoss = RangingStopLoss;
                // Use smaller take profits
                TakeProfit = RangingTakeProfit;
                break;
                
            case REGIME_VOLATILE:
                // In volatile regimes, use very short moving averages
                FastPeriod = 5;
                SlowPeriod = 15;
                // Use wider stop losses
                StopLoss = VolatileStopLoss;
                // Use larger take profits
                TakeProfit = VolatileTakeProfit;
                break;
                
            default:
                // In undefined regimes, use default parameters
                FastPeriod = 14;
                SlowPeriod = 28;
                StopLoss = 100;
                TakeProfit = 200;
                break;
        }
    }
    Essa função ajusta os parâmetros da estratégia com base no regime de mercado atual, ajustando a estratégia a condições de mercado específicas.

Monitoramento do funcionamento

Monitore regularmente o funcionamento do seu sistema de detecção de regimes de mercado para confirmar que ele classifica os regimes de mercado com precisão:

// Example of performance monitoring for regime detection
void MonitorRegimeDetectionPerformance()
{
    static int regimeTransitions = 0;
    static int correctPredictions = 0;
    static ENUM_MARKET_REGIME lastRegime = REGIME_UNDEFINED;
    
    // Get current regime
    ENUM_MARKET_REGIME currentRegime = Detector.GetCurrentRegime();
    
    // If regime has changed, evaluate the previous regime's prediction
    if(currentRegime != lastRegime && lastRegime != REGIME_UNDEFINED)
    {
        regimeTransitions++;
        
        // Evaluate if the previous regime's prediction was correct
        // Implementation depends on your specific evaluation criteria
        if(EvaluateRegimePrediction(lastRegime))
            correctPredictions++;
            
        // Log performance metrics
        double accuracy = (double)correctPredictions / regimeTransitions * 100.0;
        Print("Regime Detection Accuracy: ", DoubleToString(accuracy, 2), "% (", 
              correctPredictions, "/", regimeTransitions, ")");
    }
    
    lastRegime = currentRegime;
}

Essa função acompanha as transições de regime e avalia a precisão das previsões dos regimes de mercado, fornecendo um feedback valioso para a otimização do sistema.


Indicador: regimes para vários timeframes

Código completo:

#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

// Include the Market Regime Detector
#include <MarketRegimeEnum.mqh>
#include <MarketRegimeDetector.mqh>

// Input parameters
input int      LookbackPeriod = 100;       // Lookback period for calculations
input double   TrendThreshold = 0.2;       // Threshold for trend detection (0.1-0.5)
input double   VolatilityThreshold = 1.5;  // Threshold for volatility detection (1.0-3.0)

// Timeframes to analyze
input bool     UseM1 = false;              // Use 1-minute timeframe
input bool     UseM5 = false;              // Use 5-minute timeframe
input bool     UseM15 = true;              // Use 15-minute timeframe
input bool     UseM30 = true;              // Use 30-minute timeframe
input bool     UseH1 = true;               // Use 1-hour timeframe
input bool     UseH4 = true;               // Use 4-hour timeframe
input bool     UseD1 = true;               // Use Daily timeframe
input bool     UseW1 = false;              // Use Weekly timeframe
input bool     UseMN1 = false;             // Use Monthly timeframe

// Global variables
CMarketRegimeDetector *Detectors[];
ENUM_TIMEFRAMES Timeframes[];
int TimeframeCount = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    // Initialize timeframes array
    InitializeTimeframes();
    
    // Create detectors for each timeframe
    ArrayResize(Detectors, TimeframeCount);
    
    for(int i = 0; i < TimeframeCount; i++)
    {
        Detectors[i] = new CMarketRegimeDetector(LookbackPeriod);
        if(Detectors[i] == NULL)
        {
            Print("Failed to create Market Regime Detector for timeframe ", EnumToString(Timeframes[i]));
            return INIT_FAILED;
        }
        
        // Configure the detector
        Detectors[i].SetTrendThreshold(TrendThreshold);
        Detectors[i].SetVolatilityThreshold(VolatilityThreshold);
        Detectors[i].Initialize();
    }
    
    // Set indicator name
    IndicatorSetString(INDICATOR_SHORTNAME, "Multi-Timeframe Regime Analysis");
    
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Initialize timeframes array based on user inputs                 |
//+------------------------------------------------------------------+
void InitializeTimeframes()
{
    // Count selected timeframes
    TimeframeCount = 0;
    if(UseM1)  TimeframeCount++;
    if(UseM5)  TimeframeCount++;
    if(UseM15) TimeframeCount++;
    if(UseM30) TimeframeCount++;
    if(UseH1)  TimeframeCount++;
    if(UseH4)  TimeframeCount++;
    if(UseD1)  TimeframeCount++;
    if(UseW1)  TimeframeCount++;
    if(UseMN1) TimeframeCount++;
    
    // Resize and fill timeframes array
    ArrayResize(Timeframes, TimeframeCount);
    
    int index = 0;
    if(UseM1)  Timeframes[index++] = PERIOD_M1;
    if(UseM5)  Timeframes[index++] = PERIOD_M5;
    if(UseM15) Timeframes[index++] = PERIOD_M15;
    if(UseM30) Timeframes[index++] = PERIOD_M30;
    if(UseH1)  Timeframes[index++] = PERIOD_H1;
    if(UseH4)  Timeframes[index++] = PERIOD_H4;
    if(UseD1)  Timeframes[index++] = PERIOD_D1;
    if(UseW1)  Timeframes[index++] = PERIOD_W1;
    if(UseMN1) Timeframes[index++] = PERIOD_MN1;
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    // Check if there's enough data
    if(rates_total < LookbackPeriod)
        return 0;
    
    // Process data for each timeframe
    string commentText = "Multi-Timeframe Regime Analysis\n\n";
    
    for(int i = 0; i < TimeframeCount; i++)
    {
        // Get price data for this timeframe
        double tfClose[];
        ArraySetAsSeries(tfClose, true);
        int copied = CopyClose(Symbol(), Timeframes[i], 0, LookbackPeriod, tfClose);
        
        if(copied != LookbackPeriod)
        {
            Print("Failed to copy price data for timeframe ", EnumToString(Timeframes[i]));
            continue;
        }
        
        // Process data with the detector
        if(!Detectors[i].ProcessData(tfClose, LookbackPeriod))
        {
            Print("Failed to process data for timeframe ", EnumToString(Timeframes[i]));
            continue;
        }
        
        // Add timeframe information to comment
        commentText += TimeframeToString(Timeframes[i]) + ": ";
        commentText += Detectors[i].GetRegimeDescription();
        commentText += " (Trend: " + DoubleToString(Detectors[i].GetTrendStrength(), 2);
        commentText += ", Vol: " + DoubleToString(Detectors[i].GetVolatility(), 2) + ")";
        commentText += "\n";
    }
    
    // Display the multi-timeframe analysis
    Comment(commentText);
    
    // Return the number of calculated bars
    return rates_total;
}

//+------------------------------------------------------------------+
//| Convert timeframe enum to readable string                        |
//+------------------------------------------------------------------+
string TimeframeToString(ENUM_TIMEFRAMES timeframe)
{
    switch(timeframe)
    {
        case PERIOD_M1:  return "M1";
        case PERIOD_M5:  return "M5";
        case PERIOD_M15: return "M15";
        case PERIOD_M30: return "M30";
        case PERIOD_H1:  return "H1";
        case PERIOD_H4:  return "H4";
        case PERIOD_D1:  return "D1";
        case PERIOD_W1:  return "W1";
        case PERIOD_MN1: return "MN1";
        default:         return "Unknown";
    }
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Clean up
    for(int i = 0; i < TimeframeCount; i++)
    {
        if(Detectors[i] != NULL)
        {
            delete Detectors[i];
            Detectors[i] = NULL;
        }
    }
    
    // Clear the comment
    Comment("");
}

Esse indicador não plota linhas no gráfico. Em vez disso, ele analisa os regimes de mercado (mercado em tendência de alta ou de baixa, mercado lateral, mercado volátil) em vários timeframes selecionados pelo usuário usando a classe CMarketRegimeDetector (desenvolvida na Parte 1) e exibe os resultados como texto no canto superior esquerdo do gráfico.

Explicação do código:

  1. Propriedades e elementos incluídos:

    • #property indicator_chart_window faz com que o indicador seja executado na janela principal do gráfico.
    • #property indicator_buffers 0 , #property indicator_plots 0 indica que esse indicador, por si só, não usa buffers de dados nem linhas de gráfico ou histogramas.
    • #include <MarketRegimeEnum.mqh> , #include <MarketRegimeDetector.mqh> incluem as definições necessárias e a classe principal do detector desenvolvida anteriormente.

  2. Parâmetros de entrada:

    • LookbackPeriod , TrendThreshold , VolatilityThreshold são as principais configurações da lógica de detecção do regime de mercado, aplicadas de forma uniforme a todos os timeframes analisados.
    • De UseM1 a UseMN1 há uma série de entradas lógicas que permite ao usuário alternar (true/false) para indicar quais timeframes padrão (do período de 1 minuto ao mensal) devem ser incluídos na análise.

  3. Variáveis globais:

    • Detectors[]. Array destinado a armazenar instâncias separadas do detector CMarketRegimeDetector. Para cada timeframe selecionado, será mantido aqui um objeto detector próprio e dedicado.
    • Timeframes[]. Array para armazenar os identificadores de timeframes em MQL5 (como PERIOD_H1 , PERIOD_D1 ), correspondentes aos timeframes que o usuário seleciona por meio das entradas.
    • TimeframeCount. Variável inteira usada para acompanhar a quantidade de timeframes efetivamente selecionados.

  4. OnInit() (função de inicialização):

    • É executada uma vez quando o indicador é iniciado.
    • Chama InitializeTimeframes() para descobrir quais timeframes foram escolhidos pelo usuário e preencher o array Timeframes.
    • Redimensiona o array Detectors para que corresponda a TimeframeCount.
    • Executa um loop por TimeframeCount iterações:
      • Para cada timeframe selecionado, a função cria um novo objeto CMarketRegimeDetector usando o LookbackPeriod comum.
      • A função configura essa instância do detector com os valores comuns de TrendThreshold e VolatilityThreshold.
      • É importante observar que cada instância do detector mantém seu próprio estado interno com base nos dados do timeframe atribuído a ela.
    • Define o nome exibido do indicador.

  5. InitializeTimeframes() (função auxiliar):

    • Conta quantas entradas Use... estão definidas como true.
    • Redimensiona o array global Timeframes de acordo.
    • Preenche o array Timeframes com as constantes PERIOD_... correspondentes aos timeframes selecionados.

  6. OnCalculate() (função principal de cálculo):

    • É executada a cada novo tick ou barra.
    • Verifica se há barras históricas suficientes (rates_total) disponíveis no timeframe atual do gráfico para atender ao requisito LookbackPeriod.
    • Inicializa uma string commentText vazia.
    • Percorre cada timeframe selecionado (controlado por i):
      • Usa CopyClose() para extrair os preços de fechamento dos últimos LookbackPeriod períodos especificamente para Symbol() e Timeframes[i] (por exemplo, extrai os preços de fechamento de H4, mesmo que o indicador esteja em um gráfico M15).
      • Se a obtenção dos dados for bem-sucedida, o método ProcessData() do objeto detector correspondente (Detectors[i]) é chamado com os dados de preço extraídos (tfClose).
      • Adiciona os resultados (nome do timeframe, descrição do regime, força da tendência, volatilidade) de Detectors[i] à string commentText.
    • Por fim, usa Comment(commentText) para exibir no canto do gráfico um resumo da análise de todos os timeframes selecionados.

  7. TimeframeToString() (função auxiliar):

    • Um utilitário simples para converter constantes MQL5 PERIOD_... em strings legíveis como "M1", "H4", "D1".

  8. OnDeinit() (função de deinicialização):

    • É executada uma vez quando o indicador é removido do gráfico ou quando o terminal é fechado.
    • Percorre o array Detectors e usa delete para liberar a memória alocada para cada objeto CMarketRegimeDetector criado em OnInit(), evitando vazamentos de memória.
    • Remove o comentário de texto do canto do gráfico.

Em essência, esse código configura de forma eficiente detectores independentes de regimes de mercado para vários timeframes, extrai os dados necessários de cada um deles, executa a análise e apresenta uma visão geral consolidada dos regimes de mercado em vários timeframes diretamente no gráfico do usuário.

Usando o indicador acima para trabalhar com vários timeframes ao mesmo tempo, é possível criar outro EA que analise vários intervalos de tempo antes de enviar uma operação. No entanto, para manter a concisão, não criaremos outro EA; em vez disso, vamos primeiro testar este.


Avaliação do EA adaptativo: teste em dados históricos

Depois de criar o EA MarketRegimeEA, o próximo passo lógico é avaliar seu desempenho por meio de teste em dados históricos. Isso nos permite observar como a lógica adaptativa ao regime de mercado se comporta em dados históricos e avaliar o impacto das configurações dos seus parâmetros.

Configuração inicial do teste

Para esta demonstração, escolhemos o ouro (XAUUSD) como instrumento de teste, usando dados do timeframe M1. Os parâmetros iniciais aplicados ao EA foram escolhidos arbitrariamente da seguinte forma:

  • LookbackPeriod (período de lookback): 100
  • SmoothingPeriod (período de suavização): 10
  • TrendThreshold (limiar de tendência): 0.2
  • VolatilityThreshold (limiar de volatilidade): 1.5
  • Tamanhos de lote: Trending (mercado de tendência) = 0.1, Ranging (mercado lateral) = 0.1, Volatile (mercado volátil) = 0.1
  • SL: Trending (tendência) = 1000, Ranging (lateral) = 1600, Volatile (volátil) = 2000
  • TP: Trending (tendência) = 1400, Ranging (lateral) = 1000, Volatile (volátil) = 1800

A execução do EA com esses parâmetros padrão produziu os seguintes resultados:

Como podemos ver pela curva de capital e pelas métricas de desempenho, o teste inicial em dados históricos com essas configurações gerou resultados subótimos. Embora tenha havido períodos em que essa estratégia gerou lucro (chegando, em determinado momento, a um crescimento do patrimônio de aproximadamente 20%), o desempenho geral indica ausência de lucratividade estável e rebaixamentos significativos. Esse resultado destaca a importância de ajustar os parâmetros ao instrumento e ao timeframe específicos usados na negociação. Esses parâmetros servem como ponto de partida, mas não representam uma configuração ideal.

Para investigar o potencial de melhora no desempenho, usamos os recursos de otimização de parâmetros no Testador de Estratégias do MetaTrader 5, especialmente a otimização genética. O objetivo era identificar um conjunto de parâmetros (dentro do intervalo testado) que alinhe melhor a detecção do regime e a lógica de negociação ao comportamento dos dados históricos de preços do ouro durante o período analisado. Os parâmetros escolhidos para otimização incluíam os valores de Stop Loss e Take Profit específicos de cada regime de mercado, enquanto os demais parâmetros foram mantidos nos valores padrão.

Após a otimização, o Testador de Estratégias identificou o seguinte conjunto de parâmetros, proporcionando uma melhora significativa no desempenho em dados históricos:

Repetir o teste em dados históricos usando esses parâmetros otimizados produziu um perfil de desempenho visivelmente diferente: 

O teste em dados históricos com os parâmetros otimizados mostra uma melhora perceptível, apresentando lucro líquido positivo e uma curva de capital mais favorável em comparação com a execução inicial.

No entanto, é extremamente importante interpretar esses resultados com cautela. A otimização de parâmetros, especialmente com o uso de algoritmos genéticos sobre dados históricos e a realização do teste em dados históricos no mesmo período, traz consigo o risco de sobreajuste. Isso significa que os parâmetros podem se ajustar excepcionalmente bem aos dados históricos específicos usados, mas talvez sejam difíceis de generalizar de forma eficaz para dados de mercado futuros e não vistos.

Assim, embora os resultados otimizados demonstrem o potencial da abordagem de estratégia adaptativa quando os parâmetros são ajustados, eles não devem ser interpretados como prova definitiva de lucratividade futura. O principal objetivo deste exercício é demonstrar:

  1. A funcionalidade do EA adaptativo com base no regime de mercado.
  2. O impacto significativo dos parâmetros no desempenho.
  3. Ou seja, demonstrar que o conceito básico, que adapta a estratégia ao regime de mercado, pode gerar resultados positivos em dados históricos quando configurado corretamente.

O fato de que até mesmo a versão não otimizada apresentou períodos de lucratividade indica que a lógica principal não está fundamentalmente errada. No entanto, para transformar essa solução em uma estratégia robusta e pronta para uso, serão necessárias etapas adicionais que vão além da simples otimização, incluindo:

  • Teste out-of-sample rigoroso para confirmar a robustez dos parâmetros.
  • Implementação de uma gestão de riscos mais avançada.
  • Possível incorporação dos métodos discutidos anteriormente, como suavização das transições e ajuste gradual do tamanho das posições, para reagir de forma mais eficiente às mudanças de regime.


Conclusão

Ao longo desta série de dois artigos, percorremos o caminho da identificação de um problema fundamental do trading algorítmico, o impacto prejudicial das mudanças nas condições de mercado sobre estratégias estáticas, ao desenvolvimento e à implementação de uma solução adaptativa abrangente. Na Parte 1, criamos um mecanismo para entender o estado do mercado, desenvolvendo um sistema de detecção do regime de mercado fundamentado estatisticamente e representando visualmente seus resultados.

Nesta segunda e última parte, demos o passo decisivo da detecção para a ação. Demonstramos como usar os recursos do nosso detector de regimes de mercado (CMarketRegimeDetector), criando o MarketRegimeEA, um EA capaz de alternar automaticamente entre estratégias de seguimento de tendência, reversão à média e rompimento conforme o regime de mercado detectado. Vimos como a adaptação não apenas da lógica de entrada no mercado, mas também dos parâmetros de risco, como tamanho do lote e níveis de stop, pode criar uma abordagem de negociação mais robusta.

Além disso, analisamos os aspectos práticos da implantação desse tipo de sistema. Estudamos a importância da otimização de parâmetros (período de lookback, limiar de tendência, limiar de volatilidade) para ajustar o detector às características específicas do mercado e discutimos métodos para alternar suavemente entre regimes de mercado, minimizando possíveis movimentos erráticos de preço ou mudanças abruptas na estratégia. Por fim, abordamos como essa estrutura de detecção de regimes pode ser integrada a sistemas de negociação existentes, atuando como um filtro inteligente ou como um ajustador de parâmetros.

O objetivo era ambicioso: criar sistemas de negociação que reconheçam a não estacionariedade inerente ao mercado e se adaptem a ela. Ao combinar os recursos de detecção desenvolvidos na Parte 1 com a estrutura adaptativa de execução da estratégia e as considerações práticas discutidas na Parte 2, agora você tem um roteiro para criar estratégias significativamente mais robustas e alinhadas à natureza dinâmica dos mercados financeiros. A transição de uma abordagem estática para uma abordagem baseada na adaptação aos regimes de mercado está concluída. Essa transição permite navegar melhor por condições complexas de mercado e aplicar níveis mais avançados de análise inteligente e flexibilidade.


Visão geral dos arquivos

Veja um resumo de todos os arquivos criados neste artigo:
Nome do arquivo Descrição
MarketRegimeEnum.mqh
Define a enumeração dos tipos de regime de mercado usados em todo o sistema
CStatistics.mqh Classe de cálculos estatísticos para detecção de regimes de mercado
MarketRegimeDetector.mqh Implementação principal da detecção do regime de mercado
MarketRegimeEA.mq5 EA que se adapta a diferentes regimes de mercado
MultiTimeframeRegimes.mq5  Exemplo de análise de regimes em vários timeframes

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

Últimos Comentários | Ir para discussão (1)
abhishek maderana
abhishek maderana | 12 jul. 2025 em 21:53
Acho que, por engano, você carregou a mesma imagem de parâmetro para os parâmetros refinados. Você pode compartilhar quais foram os parâmetros refinados após a otimização do MT5?
Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): Estratégia de trading pós-notícia Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): Estratégia de trading pós-notícia
Durante o primeiro minuto após a divulgação de notícias econômicas importantes, o risco de erro de avaliação é extremamente alto. Nesse curto intervalo, o movimento do preço pode ser errático e volátil, frequentemente levando ao acionamento de ordens pendentes dos dois lados do mercado. Pouco depois da publicação, geralmente dentro de um minuto, o mercado tende a se estabilizar, retomando ou corrigindo a tendência predominante em patamares mais normais de volatilidade. Nesta seção, examinaremos uma abordagem alternativa para o trading baseado em notícias, a fim de avaliar sua eficácia como um complemento valioso ao conjunto de ferramentas do trader. Continue lendo para acompanhar mais detalhes desta discussão.
Do básico ao intermediário: Objetos e sub janelas (III) Do básico ao intermediário: Objetos e sub janelas (III)
Este artigo descreve um fluxo seguro para lidar com a remoção de objetos gráficos: interceptar eventos, recriar o objeto e recuperar suas propriedades mínimas. Mostramos como evitar armadilhas com ponteiros, prevenir recriações indevidas durante o encerramento e salvar posição/dimensões com atualização via DRAG/CHANGE. Assim, o indicador permanece íntegro e o usuário mantém parte das personalizações.
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.
Otimização por Comunidade de Cientistas - Community of Scientist Optimization (CoSO): Prática Otimização por Comunidade de Cientistas - Community of Scientist Optimization (CoSO): Prática
Continuação do tema de otimização por comunidade científica. O CoSO não deve ser tratado como uma solução pronta, mas como uma plataforma de pesquisa promissora. Com o refinamento adequado, o CoSO pode encontrar seu nicho em tarefas em que a adaptabilidade e a robustez a mudanças sejam importantes, e quando o tempo de processamento não for crítico.