English Русский 中文 Español Deutsch 日本語
preview
Desenvolvimento do Toolkit de Análise de Price Action (Parte 13): Ferramenta RSI Sentinel

Desenvolvimento do Toolkit de Análise de Price Action (Parte 13): Ferramenta RSI Sentinel

MetaTrader 5Sistemas de negociação |
25 1
Christian Benjamin
Christian Benjamin

Conteúdo:


Introdução

A divergência é um conceito da análise técnica no qual o movimento de um indicador — como osciladores de momento — se desvia do movimento do preço. Em essência, quando o preço forma novos topos ou fundos que não são confirmados pelo indicador, isso pode sinalizar enfraquecimento da tendência e possível reversão ou mudança de impulso. A divergência do RSI é uma forma simples de identificar possíveis reversões de mercado. Quando o preço se move em uma direção enquanto o RSI segue outra, isso pode indicar mudança de tendência. Entretanto, analisar manualmente os gráficos em busca desses sinais pode ser lento e sujeito a erros. É nesse ponto que a automação se torna essencial.

Neste projeto, construímos um Expert Advisor em MQL5 capaz de detectar automaticamente divergências do RSI. O EA marca esses sinais com setas claras no gráfico e fornece um breve resumo, permitindo visualizar rapidamente o que está acontecendo. Seja você iniciante ou experiente, essa ferramenta ajuda a identificar oportunidades de trading que podem ser validadas antes da execução, sem a necessidade de longas análises manuais. Vamos analisar como este EA de Divergência RSI pode simplificar seu processo de negociação.


Visão Geral da Estratégia

Entendendo a Divergência do RSI

A divergência do RSI ocorre quando o Índice de Força Relativa (RSI) se move em uma direção diferente do preço do ativo, sinalizando uma possível mudança no impulso do preço. Esse contraste entre o RSI e a ação do preço é um indicador-chave que os traders usam para antecipar reversões de mercado ou continuações de tendência. Normalmente, o RSI segue o momentum do preço, confirmando assim as tendências predominantes. No entanto, quando surge uma divergência, ela revela uma discrepância que frequentemente precede um movimento significativo de preço.. Reconhecer esses sinais precocemente pode ser crucial para o timing de entradas e saídas no mercado.

No contexto da divergência do RSI, existem dois tipos principais

1. </b>Divergência Regular do RSI

A divergência regular do RSI é geralmente vista como um sinal de reversão. Ela indica que a tendência atual está perdendo força e pode estar prestes a se reverter.

  • Divergência Regular de Alta do RSI

Ocorre quando o preço forma uma mínima mais baixa enquanto o RSI forma uma mínima mais alta. Isso sugere que, embora o preço esteja caindo, o momentum está começando a mudar para cima, indicando uma possível reversão para uma tendência de alta.

Divergência de Alta

Fig 1. Divergência de Alta

  • Divergência Regular de Baixa do RSI

Acontece quando o preço forma uma máxima mais alta enquanto o RSI forma uma máxima mais baixa. Apesar do aumento do preço, o enfraquecimento do momentum (como mostrado pelo RSI) sinaliza que uma queda pode estar no horizonte.

Sinal de Venda

Fig 2. Divergência de Baixa

2. Divergência Oculta do RSI

A divergência oculta do RSI é interpretada como um sinal de continuação de tendência, em vez de uma reversão iminente. Ela confirma que a tendência atual ainda tem força, mesmo quando o RSI e o preço divergem temporariamente.

  • Divergência Oculta de Alta do RSI: Em uma tendência de alta, se o preço forma uma mínima mais alta enquanto o RSI forma uma mínima mais baixa, isso indica que a correção é apenas temporária e que a tendência de alta provavelmente continuará.

alta oculta

Fig 3. Divergência Oculta de Alta

  • Divergência Oculta de Baixa do RSI: Em uma tendência de baixa, quando o preço forma uma máxima mais baixa enquanto o RSI forma uma máxima mais alta, isso confirma a força da tendência de baixa e sugere que o movimento descendente provavelmente persistirá.

Divergência de Baixa

Fig 4. Divergência Oculta de Baixa

Abaixo está uma tabela resumo que encapsula as principais diferenças entre os tipos de divergência do RSI:

Tipo de Divergência do RSI Ação do Preço Ação do RSI Tipo de Sinal Expectativa
Alta Regular Mínima mais baixa (LL) Mínima mais alta (HL) Reversão para cima De tendência de baixa para tendência de alta
Baixa Regular Máxima mais alta (HH)  Máxima mais baixa (LH) Reversão para baixo De tendência de alta para tendência de baixa
Alta Oculta Mínima mais alta (HL) Mínima mais baixa (LL) Continuação para cima Tendência de alta continua
Baixa Oculta Máxima mais baixa (LH) Máxima mais alta (HH) Continuação para baixo Tendência de baixa continua

Em resumo, este EA escaneia continuamente tanto os dados de preço quanto do RSI ao longo de um período de lookback definido para detectar discrepâncias entre seus movimentos, o que chamamos de divergência do RSI.

Abaixo está o que ele faz:

1. Coleta e Preparação de Dados

O EA reúne os valores do RSI juntamente com os dados de preço correspondentes (mínimas, máximas, fechamentos e tempo) das barras recentes. Isso garante que a análise esteja sempre baseada nas informações mais recentes e completas.

2. Identificação de Pontos de Swing

Em seguida, ele determina os topos e fundos de swing locais tanto nos dados de preço quanto nos dados do RSI Esses pontos de swing servem como marcadores de referência para nossa análise de divergência.
3. Detecção de Divergência Regular

  • Divergência Regular de Alta: O EA procura por instâncias em que o preço faz uma mínima mais baixa enquanto o RSI forma uma mínima mais alta, sinalizando que uma tendência de baixa pode estar perdendo momentum e pode se reverter para cima.
  • Divergência Regular de Baixa: Ele também verifica situações em que o preço faz uma máxima mais alta enquanto o RSI forma uma máxima mais baixa, indicando que uma tendência de alta pode estar se aproximando do fim à medida que o momentum enfraquece.

4. Detecção de Divergência Oculta
  • Divergência Oculta de Alta: Em uma tendência de alta, se o preço forma uma mínima mais alta, mas o RSI registra uma mínima mais baixa, o EA identifica isso como um sinal de que a tendência geral de alta ainda está forte, apesar de um recuo temporário.
  • Divergência Oculta de Baixa: Por outro lado, durante uma tendência de baixa, se o preço faz uma máxima mais baixa enquanto o RSI mostra uma máxima mais alta, isso confirma que a tendência de baixa provavelmente continuará.

5. Geração Visual e Registro de Sinais

Assim que uma divergência é detectada, seja regular ou oculta, o EA marca visualmente o evento no gráfico (usando setas e rótulos) e registra os detalhes do sinal para análise posterior ou backtesting. Confira mais sobre como ele realiza os processos acima na seção Code Breakdown abaixo.


Código MQL5

//+--------------------------------------------------------------------+
//|                                                RSI Divergence.mql5 |
//|                                 Copyright 2025, Christian Benjamin |
//|                                               https://www.mql5.com |
//+--------------------------------------------------------------------+
#property copyright "2025, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---- Input parameters
input int    InpRSIPeriod             = 14;    // RSI period
input int    InpSwingLeft             = 1;     // Bars to the left for swing detection (relaxed)
input int    InpSwingRight            = 1;     // Bars to the right for swing detection (relaxed)
input int    InpLookback              = 100;   // Number of bars to scan for divergence
input int    InpEvalBars              = 5;     // Bars after which to evaluate a signal
input int    InpMinBarsBetweenSignals = 1;     // Minimum bars between same-type signals (allows frequent re-entry)
input double InpArrowOffset           = 3.0;   // Arrow offset (in points) for display
input double InpMinSwingDiffPct       = 0.05;  // Lower minimum % difference to qualify as a swing
input double InpMinRSIDiff            = 1.0;   // Lower minimum difference in RSI between swing points

// Optional RSI threshold filter for bullish divergence (disabled by default)
input bool   InpUseRSIThreshold       = false; // If true, require earlier RSI swing to be oversold for bullish divergence
input double InpRSIOversold           = 30;    // RSI oversold level
input double InpRSIOverbought         = 70;    // RSI overbought level (if needed for bearish)

//---- Global variables
int      rsiHandle;         // Handle for the RSI indicator
double   rsiBuffer[];       // Buffer for RSI values
double   lowBuffer[];       // Buffer for low prices
double   highBuffer[];      // Buffer for high prices
double   closeBuffer[];     // Buffer for close prices
datetime timeBuffer[];       // Buffer for bar times
int      g_totalBars = 0;   // Number of bars in our copied arrays
datetime lastBarTime = 0;   // Time of last closed bar

//---- Structure to hold signal information
struct SignalInfo
  {
   string            type;         // e.g. "RegBearish Divergence", "HiddenBullish Divergence"
   int               barIndex;     // Bar index where the signal was generated
   datetime          signalTime;   // Time of the signal bar
   double            signalPrice;  // Price used for the signal (swing high for bearish, swing low for bullish)
  };

SignalInfo signals[];  // Global array to store signals

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
     {
      Print("Error creating RSI handle");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(rsiHandle != INVALID_HANDLE)
      IndicatorRelease(rsiHandle);
   EvaluateSignalsAndPrint();
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Process once per new closed candle (using bar1's time)
   datetime currentBarTime = iTime(_Symbol, _Period, 1);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;

//--- Copy RSI data
   ArrayResize(rsiBuffer, InpLookback);
   ArraySetAsSeries(rsiBuffer, true);
   if(CopyBuffer(rsiHandle, 0, 0, InpLookback, rsiBuffer) <= 0)
     {
      Print("Error copying RSI data");
      return;
     }

//--- Copy price and time data
   ArrayResize(lowBuffer, InpLookback);
   ArrayResize(highBuffer, InpLookback);
   ArrayResize(closeBuffer, InpLookback);
   ArraySetAsSeries(lowBuffer, true);
   ArraySetAsSeries(highBuffer, true);
   ArraySetAsSeries(closeBuffer, true);
   if(CopyLow(_Symbol, _Period, 0, InpLookback, lowBuffer) <= 0 ||
      CopyHigh(_Symbol, _Period, 0, InpLookback, highBuffer) <= 0 ||
      CopyClose(_Symbol, _Period, 0, InpLookback, closeBuffer) <= 0)
     {
      Print("Error copying price data");
      return;
     }

   ArrayResize(timeBuffer, InpLookback);
   ArraySetAsSeries(timeBuffer, true);
   if(CopyTime(_Symbol, _Period, 0, InpLookback, timeBuffer) <= 0)
     {
      Print("Error copying time data");
      return;
     }

   g_totalBars = InpLookback;

//--- Identify swing lows and swing highs
   int swingLows[];
   int swingHighs[];
   int startIndex = InpSwingLeft;
   int endIndex   = g_totalBars - InpSwingRight;
   for(int i = startIndex; i < endIndex; i++)
     {
      if(IsSignificantSwingLow(i, InpSwingLeft, InpSwingRight))
        {
         ArrayResize(swingLows, ArraySize(swingLows) + 1);
         swingLows[ArraySize(swingLows) - 1] = i;
        }
      if(IsSignificantSwingHigh(i, InpSwingLeft, InpSwingRight))
        {
         ArrayResize(swingHighs, ArraySize(swingHighs) + 1);
         swingHighs[ArraySize(swingHighs) - 1] = i;
        }
     }

//--- Bearish Divergence (using swing highs)
   if(ArraySize(swingHighs) >= 2)
     {
      ArraySort(swingHighs); // ascending order: index 0 is most recent
      int recent   = swingHighs[0];
      int previous = swingHighs[1];

      // Regular Bearish Divergence: Price makes a higher high while RSI makes a lower high
      if(highBuffer[recent] > highBuffer[previous] &&
         rsiBuffer[recent] < rsiBuffer[previous] &&
         (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
        {
         Print("Regular Bearish Divergence detected at bar ", recent);
         DisplaySignal("RegBearish Divergence", recent);
        }
      // Hidden Bearish Divergence: Price makes a lower high while RSI makes a higher high
      else
         if(highBuffer[recent] < highBuffer[previous] &&
            rsiBuffer[recent] > rsiBuffer[previous] &&
            (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
           {
            Print("Hidden Bearish Divergence detected at bar ", recent);
            DisplaySignal("HiddenBearish Divergence", recent);
           }
     }

//--- Bullish Divergence (using swing lows)
   if(ArraySize(swingLows) >= 2)
     {
      ArraySort(swingLows); // ascending order: index 0 is most recent
      int recent   = swingLows[0];
      int previous = swingLows[1];

      // Regular Bullish Divergence: Price makes a lower low while RSI makes a higher low
      if(lowBuffer[recent] < lowBuffer[previous] &&
         rsiBuffer[recent] > rsiBuffer[previous] &&
         (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
        {
         // Optionally require the earlier swing's RSI be oversold
         if(!InpUseRSIThreshold || rsiBuffer[previous] <= InpRSIOversold)
           {
            Print("Regular Bullish Divergence detected at bar ", recent);
            DisplaySignal("RegBullish Divergence", recent);
           }
        }
      // Hidden Bullish Divergence: Price makes a higher low while RSI makes a lower low
      else
         if(lowBuffer[recent] > lowBuffer[previous] &&
            rsiBuffer[recent] < rsiBuffer[previous] &&
            (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
           {
            Print("Hidden Bullish Divergence detected at bar ", recent);
            DisplaySignal("HiddenBullish Divergence", recent);
           }
     }
  }

//+------------------------------------------------------------------------+
//| IsSignificantSwingLow: Determines if the bar at 'index' is a swing low |
//+------------------------------------------------------------------------+
bool IsSignificantSwingLow(int index, int left, int right)
  {
   double currentLow = lowBuffer[index];
// Check left side for a local minimum condition
   for(int i = index - left; i < index; i++)
     {
      if(i < 0)
         continue;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
     }
// Check right side for a local minimum condition
   for(int i = index + 1; i <= index + right; i++)
     {
      if(i >= g_totalBars)
         break;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
     }
   return true;
  }

//+--------------------------------------------------------------------------+
//| IsSignificantSwingHigh: Determines if the bar at 'index' is a swing high |
//+--------------------------------------------------------------------------+
bool IsSignificantSwingHigh(int index, int left, int right)
  {
   double currentHigh = highBuffer[index];
// Check left side for a local maximum condition
   for(int i = index - left; i < index; i++)
     {
      if(i < 0)
         continue;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
     }
// Check right side for a local maximum condition
   for(int i = index + 1; i <= index + right; i++)
     {
      if(i >= g_totalBars)
         break;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| DisplaySignal: Draws an arrow on the chart and records the signal|
//+------------------------------------------------------------------+
void DisplaySignal(string signalText, int barIndex)
  {
// Prevent duplicate signals on the same bar (or too close)
   for(int i = 0; i < ArraySize(signals); i++)
     {
      if(StringFind(signals[i].type, signalText) != -1)
         if(MathAbs(signals[i].barIndex - barIndex) < InpMinBarsBetweenSignals)
            return;
     }

// Update a "LatestSignal" label for regular signals.
   if(StringFind(signalText, "Reg") != -1)
     {
      string labelName = "LatestSignal";
      if(ObjectFind(0, labelName) == -1)
        {
         if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
           {
            Print("Failed to create LatestSignal label");
            return;
           }
         ObjectSetInteger(0, labelName, OBJPROP_CORNER, 0);
         ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
         ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20);
         ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
        }
      ObjectSetString(0, labelName, OBJPROP_TEXT, signalText);
     }

// Create an arrow object for the signal.
   string arrowName = "Arrow_" + signalText + "_" + IntegerToString(barIndex);
   if(ObjectFind(0, arrowName) < 0)
     {
      int arrowCode = 0;
      double arrowPrice = 0.0;
      color arrowColor = clrWhite;
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

      if(StringFind(signalText, "Bullish") != -1)
        {
         arrowCode  = 233; // Wingdings up arrow
         arrowColor = clrLime;
         arrowPrice = lowBuffer[barIndex] - (InpArrowOffset * point);
        }
      else
         if(StringFind(signalText, "Bearish") != -1)
           {
            arrowCode  = 234; // Wingdings down arrow
            arrowColor = clrRed;
            arrowPrice = highBuffer[barIndex] + (InpArrowOffset * point);
           }

      if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeBuffer[barIndex], arrowPrice))
        {
         Print("Failed to create arrow object ", arrowName);
         return;
        }
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
     }

// Record the signal for evaluation.
   SignalInfo sig;
   sig.type = signalText;
   sig.barIndex = barIndex;
   sig.signalTime = timeBuffer[barIndex];
   if(StringFind(signalText, "Bullish") != -1)
      sig.signalPrice = lowBuffer[barIndex];
   else
      sig.signalPrice = highBuffer[barIndex];
   ArrayResize(signals, ArraySize(signals) + 1);
   signals[ArraySize(signals) - 1] = sig;

   UpdateSignalCountLabel();
  }

//+------------------------------------------------------------------+
//| UpdateSignalCountLabel: Updates a label showing signal counts    |
//+------------------------------------------------------------------+
void UpdateSignalCountLabel()
  {
   int regCount = 0, hidCount = 0;
   for(int i = 0; i < ArraySize(signals); i++)
     {
      if(StringFind(signals[i].type, "Reg") != -1)
         regCount++;
      else
         if(StringFind(signals[i].type, "Hidden") != -1)
            hidCount++;
     }
   string countText = "Regular Signals: " + IntegerToString(regCount) +
                      "\nHidden Signals: " + IntegerToString(hidCount);
   string countLabel = "SignalCount";
   if(ObjectFind(0, countLabel) == -1)
     {
      if(!ObjectCreate(0, countLabel, OBJ_LABEL, 0, 0, 0))
        {
         Print("Failed to create SignalCount label");
         return;
        }
      ObjectSetInteger(0, countLabel, OBJPROP_CORNER, 0);
      ObjectSetInteger(0, countLabel, OBJPROP_XDISTANCE, 10);
      ObjectSetInteger(0, countLabel, OBJPROP_YDISTANCE, 40);
      ObjectSetInteger(0, countLabel, OBJPROP_COLOR, clrYellow);
     }
   ObjectSetString(0, countLabel, OBJPROP_TEXT, countText);
  }

//+--------------------------------------------------------------------+
//| EvaluateSignalsAndPrint: After backtesting, prints signal accuracy |
//+--------------------------------------------------------------------+
void EvaluateSignalsAndPrint()
  {
   double closeAll[];
   int totalBars = CopyClose(_Symbol, _Period, 0, WHOLE_ARRAY, closeAll);
   if(totalBars <= 0)
     {
      Print("Error copying complete close data for evaluation");
      return;
     }
   ArraySetAsSeries(closeAll, true);

   int totalEvaluated = 0, regTotal = 0, hidTotal = 0;
   int regEval = 0, hidEval = 0;
   int regCorrect = 0, hidCorrect = 0;

   for(int i = 0; i < ArraySize(signals); i++)
     {
      int evalIndex = signals[i].barIndex - InpEvalBars;
      if(evalIndex < 0)
         continue;
      double evalClose = closeAll[evalIndex];

      if(StringFind(signals[i].type, "Bullish") != -1)
        {
         if(StringFind(signals[i].type, "Reg") != -1)
           {
            regTotal++;
            regEval++;
            if(evalClose > signals[i].signalPrice)
               regCorrect++;
           }
         else
            if(StringFind(signals[i].type, "Hidden") != -1)
              {
               hidTotal++;
               hidEval++;
               if(evalClose > signals[i].signalPrice)
                  hidCorrect++;
              }
         totalEvaluated++;
        }
      else
         if(StringFind(signals[i].type, "Bearish") != -1)
           {
            if(StringFind(signals[i].type, "Reg") != -1)
              {
               regTotal++;
               regEval++;
               if(evalClose < signals[i].signalPrice)
                  regCorrect++;
              }
            else
               if(StringFind(signals[i].type, "Hidden") != -1)
                 {
                  hidTotal++;
                  hidEval++;
                  if(evalClose < signals[i].signalPrice)
                     hidCorrect++;
                 }
            totalEvaluated++;
           }
     }

   double overallAccuracy = (totalEvaluated > 0) ? (double)(regCorrect + hidCorrect) / totalEvaluated * 100.0 : 0.0;
   double regAccuracy = (regEval > 0) ? (double)regCorrect / regEval * 100.0 : 0.0;
   double hidAccuracy = (hidEval > 0) ? (double)hidCorrect / hidEval * 100.0 : 0.0;

   Print("----- Backtest Signal Evaluation -----");
   Print("Total Signals Generated: ", ArraySize(signals));
   Print("Signals Evaluated: ", totalEvaluated);
   Print("Overall Accuracy: ", DoubleToString(overallAccuracy, 2), "%");
   Print("Regular Signals: ", regTotal, " | Evaluated: ", regEval, " | Accuracy: ", DoubleToString(regAccuracy, 2), "%");
   Print("Hidden Signals:  ", hidTotal, " | Evaluated: ", hidEval, " | Accuracy: ", DoubleToString(hidAccuracy, 2), "%");
  }
//+------------------------------------------------------------------+


Análise do código

1. Informações de Cabeçalho e Parâmetros de Entrada

No topo do nosso script, temos um cabeçalho bem definido que fornece informações importantes sobre o código.

Informações do Arquivo e do Autor


O cabeçalho especifica o nome do arquivo (RSI Divergence.mql5), o aviso de direitos autorais e um link para o perfil do autor. Isso garante a devida atribuição e fornece aos usuários um ponto de referência caso precisem verificar atualizações ou documentação adicional.

Controle de Versão e Diretivas de Compilação

As diretivas #property definem propriedades importantes, como o número da versão e o uso de regras estritas de compilação (#property strict). Isso ajuda a manter a consistência e reduzir possíveis erros durante o desenvolvimento e a implantação. Seguindo adiante, a seção de parâmetros de entrada é essencial para personalização. Esses parâmetros permitem que você, ou qualquer usuário, ajuste o comportamento da lógica de detecção de divergência sem modificar o código principal. Abaixo estão alguns destaques:

Parâmetros de RSI e Detecção de Swing

  • InpRSIPeriod: Define o período do indicador RSI.
  • InpSwingLeft e InpSwingRight: Definem quantas barras em cada lado são consideradas ao detectar pontos de swing. Ajustar esses valores torna a detecção de swing mais flexível ou mais rigorosa.

Configurações de Divergência e Avaliação de Sinal

  • InpLookback: Determina quantas barras no passado o script irá escanear em busca de divergências.
  • InpEvalBars: Especifica o número de barras a aguardar antes de avaliar se um sinal foi bem-sucedido.
  • InpMinBarsBetweenSignals: Ajuda a evitar sinais duplicados ao impor uma separação mínima de barras entre sinais semelhantes.

Personalizações de Exibição

  • InpArrowOffset: Define a distância (em pontos) em que as setas são deslocadas do ponto de swing, melhorando a clareza visual no gráfico.

Filtro Opcional de Limite do RSI

  • InpUseRSIThreshold, juntamente com InpRSIOversold e InpRSIOverbought, fornece uma camada extra de filtragem. Isso garante que, para divergência de alta, o swing anterior do RSI esteja na região de sobrevenda — caso o usuário escolha habilitar esse filtro.

//+------------------------------------------------------------------+
//|                                           RSI Divergence.mql5    |
//|                               Copyright 2025, Christian Benjamin |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2025, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---- Input parameters
input int    InpRSIPeriod             = 14;    // RSI period
input int    InpSwingLeft             = 1;     // Bars to the left for swing detection (relaxed)
input int    InpSwingRight            = 1;     // Bars to the right for swing detection (relaxed)
input int    InpLookback              = 100;   // Number of bars to scan for divergence
input int    InpEvalBars              = 5;     // Bars after which to evaluate a signal
input int    InpMinBarsBetweenSignals = 1;     // Minimum bars between same-type signals (allows frequent re-entry)
input double InpArrowOffset           = 3.0;   // Arrow offset (in points) for display
input double InpMinSwingDiffPct       = 0.05;  // Lower minimum % difference to qualify as a swing
input double InpMinRSIDiff            = 1.0;   // Lower minimum difference in RSI between swing points

// Optional RSI threshold filter for bullish divergence (disabled by default)
input bool   InpUseRSIThreshold       = false; // If true, require earlier RSI swing to be oversold for bullish divergence
input double InpRSIOversold           = 30;    // RSI oversold level
input double InpRSIOverbought         = 70;    // RSI overbought level (if needed for bearish)

2. Inicialização do Indicador

Nesta parte, inicializamos nosso indicador RSI. A função OnInit() cria um handle para o indicador RSI usando parâmetros como símbolo, timeframe e o período do RSI especificado pelo usuário. Essa etapa é crucial porque cada operação subsequente depende de ter um handle de RSI válido para recuperar os dados do indicador.

  • A função iRSI é chamada com os parâmetros necessários.
  • O tratamento de erros é implementado para capturar qualquer falha na criação do handle.
  • A inicialização garante que nosso indicador esteja pronto para aquisição e análise de dados.
int OnInit()
{
   // Create the RSI indicator handle with the specified period
   rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
   {
      Print("Error creating RSI handle");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

3. Aquisição de Dados a Cada Novo Candle

Na função OnTick(), verificamos se um novo candle foi fechado antes de processar. Isso garante que nossa análise esteja sempre trabalhando com dados concluídos. Em seguida, copiamos arrays de valores do RSI, mínimas, máximas, fechamentos e dados de tempo ao longo de um período de lookback configurável. Definir os arrays como série garante que os dados estejam ordenados com a barra mais recente no índice 0.

  • O código aguarda o próximo candle fechado para evitar o processamento de dados incompletos.
  • Os dados de RSI e preço são recuperados usando funções como CopyBuffer e CopyLow/High/Close/Time.
  • O uso de ArraySetAsSeries preserva a ordem correta para análise de séries temporais.

void OnTick()
{
   // Process only once per new closed candle by comparing bar times
   datetime currentBarTime = iTime(_Symbol, _Period, 1);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;
   
   // Copy RSI data for a given lookback period
   ArrayResize(rsiBuffer, InpLookback);
   ArraySetAsSeries(rsiBuffer, true);
   if(CopyBuffer(rsiHandle, 0, 0, InpLookback, rsiBuffer) <= 0)
   {
      Print("Error copying RSI data");
      return;
   }
   
   // Copy price data (lows, highs, closes) and time data for analysis
   ArrayResize(lowBuffer, InpLookback);
   ArrayResize(highBuffer, InpLookback);
   ArrayResize(closeBuffer, InpLookback);
   ArraySetAsSeries(lowBuffer, true);
   ArraySetAsSeries(highBuffer, true);
   ArraySetAsSeries(closeBuffer, true);
   if(CopyLow(_Symbol, _Period, 0, InpLookback, lowBuffer) <= 0 ||
      CopyHigh(_Symbol, _Period, 0, InpLookback, highBuffer) <= 0 ||
      CopyClose(_Symbol, _Period, 0, InpLookback, closeBuffer) <= 0)
   {
      Print("Error copying price data");
      return;
   }
   
   ArrayResize(timeBuffer, InpLookback);
   ArraySetAsSeries(timeBuffer, true);
   if(CopyTime(_Symbol, _Period, 0, InpLookback, timeBuffer) <= 0)
   {
      Print("Error copying time data");
      return;
   }
   
   g_totalBars = InpLookback;
   
   // (Further processing follows here...)
}

4. Detecção de Swing (Identificação de Fundos de Swing e Topos de Swing)

Antes de podermos detectar divergências, primeiro precisamos identificar pontos de swing significativos. Duas funções auxiliares, IsSignificantSwingLow e IsSignificantSwingHigh, são usadas para identificar mínimos e máximos locais. Elas fazem isso comparando a mínima ou máxima de uma barra com suas barras vizinhas dentro de uma determinada janela e verificando se a diferença percentual atende a um limite definido.
  • As funções verificam tanto à esquerda quanto à direita da barra atual.
  • Elas calculam a diferença percentual para garantir que apenas swings significativos sejam marcados.
  • Essa filtragem reduz o ruído, garantindo que nossa análise de divergência foque em movimentos de mercado relevantes.
bool IsSignificantSwingLow(int index, int left, int right)
{
   double currentLow = lowBuffer[index];
   // Check left side for local minimum condition
   for(int i = index - left; i < index; i++)
   {
      if(i < 0) continue;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   // Check right side for local minimum condition
   for(int i = index + 1; i <= index + right; i++)
   {
      if(i >= g_totalBars) break;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   return true;
}

bool IsSignificantSwingHigh(int index, int left, int right)
{
   double currentHigh = highBuffer[index];
   // Check left side for local maximum condition
   for(int i = index - left; i < index; i++)
   {
      if(i < 0) continue;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   // Check right side for local maximum condition
   for(int i = index + 1; i <= index + right; i++)
   {
      if(i >= g_totalBars) break;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   return true;
}

5. Detecção de Divergência: Divergências de Baixa e de Alta

Uma vez identificados os pontos de swing, o algoritmo compara swings recentes para detectar divergências. Para divergência de baixa, o código observa dois topos de swing e verifica se o preço está fazendo uma máxima mais alta enquanto o RSI mostra uma máxima mais baixa (ou o inverso, no caso de divergência de baixa oculta). Para divergência de alta, ele compara de forma semelhante dois fundos de swing. Um limite opcional de RSI pode validar ainda mais os sinais de alta, garantindo que a leitura anterior do RSI esteja na região de sobrevenda.

  • Dois pontos de swing recentes (sejam topos ou fundos) são usados para a análise de divergência.
  • As condições para divergências regulares e ocultas são claramente separadas.
  • Parâmetros opcionais (como a condição de sobrevenda do RSI) fornecem filtragem adicional para a força do sinal.

// --- Bearish Divergence (using swing highs)
if(ArraySize(swingHighs) >= 2)
{
   ArraySort(swingHighs); // Ensure ascending order: index 0 is most recent
   int recent   = swingHighs[0];
   int previous = swingHighs[1];
   
   // Regular Bearish Divergence: Price makes a higher high while RSI makes a lower high
   if(highBuffer[recent] > highBuffer[previous] &&
      rsiBuffer[recent] < rsiBuffer[previous] &&
      (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
   {
      Print("Regular Bearish Divergence detected at bar ", recent);
      DisplaySignal("RegBearish Divergence", recent);
   }
   // Hidden Bearish Divergence: Price makes a lower high while RSI makes a higher high
   else if(highBuffer[recent] < highBuffer[previous] &&
           rsiBuffer[recent] > rsiBuffer[previous] &&
           (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
   {
      Print("Hidden Bearish Divergence detected at bar ", recent);
      DisplaySignal("HiddenBearish Divergence", recent);
   }
}

// --- Bullish Divergence (using swing lows)
if(ArraySize(swingLows) >= 2)
{
   ArraySort(swingLows); // Ensure ascending order: index 0 is most recent
   int recent   = swingLows[0];
   int previous = swingLows[1];
   
   // Regular Bullish Divergence: Price makes a lower low while RSI makes a higher low
   if(lowBuffer[recent] < lowBuffer[previous] &&
      rsiBuffer[recent] > rsiBuffer[previous] &&
      (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
   {
      // Optionally require the earlier RSI swing to be oversold
      if(!InpUseRSIThreshold || rsiBuffer[previous] <= InpRSIOversold)
      {
         Print("Regular Bullish Divergence detected at bar ", recent);
         DisplaySignal("RegBullish Divergence", recent);
      }
   }
   // Hidden Bullish Divergence: Price makes a higher low while RSI makes a lower low
   else if(lowBuffer[recent] > lowBuffer[previous] &&
           rsiBuffer[recent] < rsiBuffer[previous] &&
           (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
   {
      Print("Hidden Bullish Divergence detected at bar ", recent);
      DisplaySignal("HiddenBullish Divergence", recent);
   }
}

6. Exibição e Registro de Sinais

Quando uma divergência é detectada, é importante marcar o sinal visualmente e registrar seus detalhes para avaliação posterior. A função DisplaySignal() não apenas cria uma seta no gráfico (usando diferentes códigos de seta e cores para sinais de alta e de baixa), mas também atualiza um rótulo para o sinal mais recente e armazena os metadados do sinal em um array global. Esse registro sistemático permite o backtesting posterior da estratégia.

  • Sinais duplicados são evitados verificando se já existe um sinal para uma barra semelhante.
  • Indicações visuais como setas e rótulos aumentam a legibilidade do gráfico.
  • Cada sinal é armazenado com detalhes como tipo, índice da barra, tempo e preço, facilitando a avaliação posterior de desempenho.

void DisplaySignal(string signalText, int barIndex)
{
   // Prevent duplicate signals on the same or nearby bars
   for(int i = 0; i < ArraySize(signals); i++)
   {
      if(StringFind(signals[i].type, signalText) != -1)
         if(MathAbs(signals[i].barIndex - barIndex) < InpMinBarsBetweenSignals)
            return;
   }
     
   // Update a label for the latest regular signal
   if(StringFind(signalText, "Reg") != -1)
   {
      string labelName = "LatestSignal";
      if(ObjectFind(0, labelName) == -1)
      {
         if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
         {
            Print("Failed to create LatestSignal label");
            return;
         }
         ObjectSetInteger(0, labelName, OBJPROP_CORNER, 0);
         ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
         ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20);
         ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
      }
      ObjectSetString(0, labelName, OBJPROP_TEXT, signalText);
   }
     
   // Create an arrow object to mark the signal on the chart
   string arrowName = "Arrow_" + signalText + "_" + IntegerToString(barIndex);
   if(ObjectFind(0, arrowName) < 0)
   {
      int arrowCode = 0;
      double arrowPrice = 0.0;
      color arrowColor = clrWhite;
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
          
      if(StringFind(signalText, "Bullish") != -1)
      {
         arrowCode  = 233; // Wingdings up arrow
         arrowColor = clrLime;
         arrowPrice = lowBuffer[barIndex] - (InpArrowOffset * point);
      }
      else if(StringFind(signalText, "Bearish") != -1)
      {
         arrowCode  = 234; // Wingdings down arrow
         arrowColor = clrRed;
         arrowPrice = highBuffer[barIndex] + (InpArrowOffset * point);
      }
          
      if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeBuffer[barIndex], arrowPrice))
      {
         Print("Failed to create arrow object ", arrowName);
         return;
      }
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
   }
     
   // Record the signal details for later backtesting evaluation
   SignalInfo sig;
   sig.type = signalText;
   sig.barIndex = barIndex;
   sig.signalTime = timeBuffer[barIndex];
   if(StringFind(signalText, "Bullish") != -1)
      sig.signalPrice = lowBuffer[barIndex];
   else
      sig.signalPrice = highBuffer[barIndex];
   ArrayResize(signals, ArraySize(signals) + 1);
   signals[ArraySize(signals) - 1] = sig;
     
   UpdateSignalCountLabel();
}

Avaliação de Backtest na Desinicialização

Por fim, a função EvaluateSignalsAndPrint() é chamada quando o expert é desinicializado. Essa função avalia retrospectivamente todos os sinais registrados comparando o movimento do preço algumas barras após o sinal com o preço registrado do sinal. Ela calcula a precisão tanto para sinais regulares quanto ocultos, fornecendo feedback valioso sobre o desempenho da nossa estratégia de divergência.

  • A função recupera dados históricos completos de fechamento.
  • Cada sinal é avaliado após um número fixo de barras (conforme definido por InpEvalBars).
  • Métricas de precisão são calculadas para o total de sinais, bem como separadamente para sinais regulares e ocultos, auxiliando na validação de desempenho.

void EvaluateSignalsAndPrint()
{
   double closeAll[];
   int totalBars = CopyClose(_Symbol, _Period, 0, WHOLE_ARRAY, closeAll);
   if(totalBars <= 0)
   {
      Print("Error copying complete close data for evaluation");
      return;
   }
   ArraySetAsSeries(closeAll, true);
   
   int totalEvaluated = 0, regTotal = 0, hidTotal = 0;
   int regEval = 0, hidEval = 0;
   int regCorrect = 0, hidCorrect = 0;
   
   for(int i = 0; i < ArraySize(signals); i++)
   {
      int evalIndex = signals[i].barIndex - InpEvalBars;
      if(evalIndex < 0)
         continue;
      double evalClose = closeAll[evalIndex];
      
      if(StringFind(signals[i].type, "Bullish") != -1)
      {
         if(StringFind(signals[i].type, "Reg") != -1)
         {
            regTotal++;
            regEval++;
            if(evalClose > signals[i].signalPrice)
               regCorrect++;
         }
         else if(StringFind(signals[i].type, "Hidden") != -1)
         {
            hidTotal++;
            hidEval++;
            if(evalClose > signals[i].signalPrice)
               hidCorrect++;
         }
         totalEvaluated++;
      }
      else if(StringFind(signals[i].type, "Bearish") != -1)
      {
         if(StringFind(signals[i].type, "Reg") != -1)
         {
            regTotal++;
            regEval++;
            if(evalClose < signals[i].signalPrice)
               regCorrect++;
         }
         else if(StringFind(signals[i].type, "Hidden") != -1)
         {
            hidTotal++;
            hidEval++;
            if(evalClose < signals[i].signalPrice)
               hidCorrect++;
         }
         totalEvaluated++;
      }
   }
     
   double overallAccuracy = (totalEvaluated > 0) ? (double)(regCorrect + hidCorrect) / totalEvaluated * 100.0 : 0.0;
   double regAccuracy = (regEval > 0) ? (double)regCorrect / regEval * 100.0 : 0.0;
   double hidAccuracy = (hidEval > 0) ? (double)hidCorrect / hidEval * 100.0 : 0.0;
     
   Print("----- Backtest Signal Evaluation -----");
   Print("Total Signals Generated: ", ArraySize(signals));
   Print("Signals Evaluated: ", totalEvaluated);
   Print("Overall Accuracy: ", DoubleToString(overallAccuracy, 2), "%");
   Print("Regular Signals: ", regTotal, " | Evaluated: ", regEval, " | Accuracy: ", DoubleToString(regAccuracy, 2), "%");
   Print("Hidden Signals:  ", hidTotal, " | Evaluated: ", hidEval, " | Accuracy: ", DoubleToString(hidAccuracy, 2), "%");
}


Testes e Resultados

Após compilar com sucesso seu EA usando o MetaEditor, arraste seu EA para o gráfico para teste. Certifique-se de estar usando uma conta demo para evitar arriscar dinheiro real. Você também pode adicionar o indicador RSI ao seu gráfico para facilitar a confirmação dos sinais durante o teste do EA. Para fazer isso, navegue até a aba Indicators, selecione o indicador RSI na pasta Panels e defina seus parâmetros preferidos, garantindo que correspondam aos do seu EA. Confira o GIF abaixo, que ilustra como adicionar a janela do indicador RSI em um gráfico do MetaTrader 5. Você também pode ver o sinal confirmado, uma divergência regular de alta em um timeframe de um minuto.

Convergência de Alta

Fig 5. Configuração do Indicador e Resultado do Teste 1

Abaixo está outro teste que conduzimos no Boom 500, confirmado tanto pela ação do preço quanto pelo indicador RSI, mostrando um sinal de venda.

Teste

Fig 6. Resultado do Teste 2

Outro teste foi conduzido usando backtesting no GIF abaixo, que mostra várias mudanças positivas. Se você observar com atenção, notará tanto sinais ocultos de continuação quanto sinais regulares. No entanto, alguns sinais precisam ser filtrados devido à falta de confirmação, apesar de seu impacto positivo.

Backtesting

Fig 7. Resultado do Teste 3


Conclusão

Esta ferramenta provou estar extremamente alinhada com a ação do preço, que é o objetivo central da nossa série de criar o maior número possível de ferramentas de análise de price action. Eu realmente apreciei a eficácia com que o indicador RSI interage com a ação do preço ao extrair sinais positivos das divergências. Os testes que conduzimos mostraram resultados promissores e uma tendência positiva.

No entanto, acredito que seja o momento de introduzir outra melhoria que utilize bibliotecas externas para identificação precisa e exata de swings, melhorando assim a precisão dos sinais. Meu conselho é testar a ferramenta cuidadosamente e ajustar seus parâmetros para se adequar ao seu estilo de negociação. Lembre-se de que cada sinal gerado deve ser verificado antes da entrada, pois a ferramenta foi projetada para ajudar você a monitorar o mercado e confirmar sua estratégia geral.

Data Nome da Ferramenta  Descrição Versão  Atualizações  Notas
01/10/24 Projetor de Gráficos Script para sobrepor a ação do preço do dia anterior com efeito fantasma. 1.0 Lançamento Inicial Primeira ferramenta no Lynnchris Tool Chest
18/11/24 Comentário Analítico Ele fornece informações do dia anterior em formato tabular, além de antecipar a direção futura do mercado. 1.0 Lançamento Inicial Segunda ferramenta no Lynnchris Tool Chest
27/11/24 Mestre em Análise Atualização regular das métricas de mercado a cada duas horas  1.01 Segundo Lançamento Terceira ferramenta no Lynnchris Tool Chest
02/12/24 Previsor Analítico  Atualização regular das métricas de mercado a cada duas horas com integração ao Telegram 1.1 Terceira Edição Ferramenta número 4
09/12/24 Navegador de Volatilidade O EA analisa as condições de mercado usando os indicadores Bandas de Bollinger, RSI e ATR 1.0 Lançamento Inicial Ferramenta número 5
19/12/24 Reversão à Média Ceifador de Sinal  Analisa o mercado usando a estratégia de reversão à média e fornece sinais  1.0  Lançamento Inicial  Ferramenta número 6 
9/01/25  Pulso de sinal  Analisador de múltiplos períodos de tempo 1.0  Lançamento Inicial  Ferramenta número 7 
17/01/25  Quadro de Métricas  Painel com botão para análise  1.0  Lançamento Inicial Ferramenta número 8 
21/01/25 Fluxo externo Análises por meio de bibliotecas externas 1.0  Lançamento Inicial Ferramenta número 9 
27/01/25 VWAP Preço médio ponderado por volume   1.3  Lançamento Inicial  Ferramenta número 10 
02/02/25  Heikin Ashi  Suavização de Tendência e identificação de sinais de reversão  1.0  Lançamento Inicial  Ferramenta número 11
04/02/25  FibVWAP  Geração de sinais por meio de análise em Python  1.0  Lançamento Inicial  Ferramenta número 12
14/02/25  DIVERGÊNCIA RSI  Ação do preço versus divergências do RSI  1.0  Lançamento Inicial  Ferramenta número 13 

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

Arquivos anexados |
RSI_DIVERGENCE.mq5 (33.61 KB)
Últimos Comentários | Ir para discussão (1)
linfo2
linfo2 | 18 fev. 2025 em 20:05
Mais uma vez, obrigado, Chris, por seus artigos bem pensados e úteis. Muito apreciado como um modelo para eu modificar. Também aprecio seu pensamento erudito e suas implementações práticas. Para mim, tive problemas com EvaluateSignalsAndPrint, está escrito que a função retorna Error Copying Compete close data for evaluation. O último erro é '4003'. Não sou inteligente o suficiente para descobrir por que, ao usar a variável WHOLE_ARRAY, a função falha. Para mim, se eu substituir 'WHOLE_ARRAY no copyclose por uma contagem das barras fechadas, posso obter um retorno 'Backtest Signal Evaluation'. A propósito, estou em um fuso horário estranho, GMT +13, e às vezes tenho problemas com datas e horários locais e do servidor, mas talvez isso possa ajudar alguém.
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.
Criando um Painel Administrador de Trading em MQL5 (Parte IX): Organização de Código (II): Modularização Criando um Painel Administrador de Trading em MQL5 (Parte IX): Organização de Código (II): Modularização
Nesta discussão, damos um passo adiante ao dividir nosso programa MQL5 em módulos menores e mais gerenciáveis. Esses componentes modulares serão então integrados ao programa principal, melhorando sua organização e capacidade de manutenção. Essa abordagem simplifica a estrutura do programa principal e torna os componentes individuais reutilizáveis em outros Expert Advisors (EAs) e no desenvolvimento de indicadores. Ao adotar esse design modular, criamos uma base sólida para melhorias futuras, beneficiando tanto nosso projeto quanto a comunidade mais ampla de desenvolvedores.
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.
Construindo um Indicador Keltner Channel com Gráficos Canvas Personalizados em MQL5 Construindo um Indicador Keltner Channel com Gráficos Canvas Personalizados em MQL5
Neste artigo, construímos um indicador Keltner Channel com gráficos canvas personalizados em MQL5. Detalhamos a integração de médias móveis, cálculos de ATR e visualização aprimorada do gráfico. Também abordamos o backtesting para avaliar o desempenho do indicador e obter insights práticos de trading.