Desenvolvimento do Toolkit de Análise de Price Action (Parte 13): Ferramenta RSI Sentinel
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.

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.

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á.

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á.

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

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.

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.

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
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Caminhe em novos trilhos: Personalize indicadores no MQL5
Criando um Painel Administrador de Trading em MQL5 (Parte IX): Organização de Código (II): Modularização
Está chegando o novo MetaTrader 5 e MQL5
Construindo um Indicador Keltner Channel com Gráficos Canvas Personalizados em MQL5
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso