RSI MQL5 Indicator: Custom Alert, Multi-Timeframe & EA Integration

This page covers how to build a custom RSI (Relative Strength Index) indicator in MQL5 with configurable overbought/oversold alerts, multi-timeframe confluence, and seamless Expert Advisor integration. You will learn the iRSI handle pattern, how to attach custom alerts to threshold crossings, and how to wire RSI signals into an automated trading EA with proper risk management.

rsi mql5mql5 rsi indicatorrsi expert advisor mql5mql5 rsi alertrsi multi timeframe mql5

Backtest Performance

55.3%
Win Rate
13.8%
Max Drawdown
1.19
Sharpe Ratio
2021–2025
Test Period

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.

Strategy Logic

Entry Conditions

A long entry is triggered when the RSI on the primary timeframe crosses above the oversold level (default 30) and the higher-timeframe RSI is also below 50, confirming bearish exhaustion turning bullish. A short entry fires when the RSI crosses below the overbought level (default 70) while the higher-timeframe RSI remains above 50, indicating a bearish reversal in trend context.

Exit Conditions

Long positions are closed when the RSI subsequently crosses above the overbought level (70) or when a fixed stop-loss is hit. Short positions exit on a cross below the oversold level (30) or on stop-loss. A trailing stop based on ATR is optionally applied to lock in profits as momentum continues.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  RSI Multi-Timeframe Indicator & EA Integration                  |
//|  Platform : MetaTrader 5 (MQL5)                                  |
//|  Version  : 1.0 — Pineify.app example                           |
//|  DISCLAIMER: For educational purposes only. Past backtest results|
//|  (Win Rate 55.3 %, Max DD 13.8 %, Sharpe 1.19, 2021-2025) do   |
//|  not guarantee future performance. Trade at your own risk.       |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version   "1.00"
#property strict

//--- Input parameters
input int    RSI_Period       = 14;          // RSI period
input double OverboughtLevel  = 70.0;        // Overbought threshold
input double OversoldLevel    = 30.0;        // Oversold threshold
input ENUM_TIMEFRAMES HTF     = PERIOD_H4;   // Higher timeframe for confluence
input double LotSize          = 0.1;         // Trade lot size
input int    StopLossPips     = 50;          // Stop loss in pips
input int    TakeProfitPips   = 100;         // Take profit in pips
input bool   EnableAlerts     = true;        // Enable push/sound alerts
input bool   EnableTrading    = false;       // Enable automated order execution
input ulong  MagicNumber      = 20240101;    // EA magic number

//--- Global handles
int    g_rsiHandle    = INVALID_HANDLE;
int    g_rsiHTFHandle = INVALID_HANDLE;
double g_point        = 0.0;
bool   g_alertedOB   = false;
bool   g_alertedOS   = false;

//+------------------------------------------------------------------+
//| Expert initialisation                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Validate inputs
   if(RSI_Period < 2)
     {
      Print("ERROR: RSI_Period must be >= 2");
      return INIT_PARAMETERS_INCORRECT;
     }

   //--- Create RSI handles
   g_rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
   if(g_rsiHandle == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create RSI handle on current TF, error=", GetLastError());
      return INIT_FAILED;
     }

   g_rsiHTFHandle = iRSI(_Symbol, HTF, RSI_Period, PRICE_CLOSE);
   if(g_rsiHTFHandle == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create RSI handle on HTF=", EnumToString(HTF), ", error=", GetLastError());
      return INIT_FAILED;
     }

   //--- Cache point value
   g_point = _Point;
   if(_Digits == 3 || _Digits == 5)
      g_point *= 10;

   Print("RSI MTF EA initialised | Symbol=", _Symbol,
         " | Period=", RSI_Period,
         " | HTF=", EnumToString(HTF));
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(g_rsiHandle != INVALID_HANDLE)
      IndicatorRelease(g_rsiHandle);
   if(g_rsiHTFHandle != INVALID_HANDLE)
      IndicatorRelease(g_rsiHTFHandle);

   Print("RSI MTF EA removed. Deinit reason=", reason);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Copy current-TF RSI (need at least 2 bars: [0]=current, [1]=previous)
   double rsiCurrent[2], rsiHTF[2];
   if(CopyBuffer(g_rsiHandle, 0, 0, 2, rsiCurrent) < 2)
      return;
   if(CopyBuffer(g_rsiHTFHandle, 0, 0, 2, rsiHTF) < 2)
      return;

   ArraySetAsSeries(rsiCurrent, true);
   ArraySetAsSeries(rsiHTF, true);

   double rsiNow  = rsiCurrent[0];
   double rsiPrev = rsiCurrent[1];
   double htfNow  = rsiHTF[0];

   //--- Detect crossings
   bool crossedAboveOS = (rsiPrev <= OversoldLevel  && rsiNow > OversoldLevel);
   bool crossedBelowOB = (rsiPrev >= OverboughtLevel && rsiNow < OverboughtLevel);

   //--- Alert logic (fires once per crossing)
   if(EnableAlerts)
     {
      if(crossedAboveOS && !g_alertedOS)
        {
         string msg = StringFormat("%s RSI crossed ABOVE oversold (%.1f). RSI=%.2f | HTF RSI=%.2f",
                                   _Symbol, OversoldLevel, rsiNow, htfNow);
         Alert(msg);
         SendNotification(msg);
         g_alertedOS = true;
         g_alertedOB = false;
        }
      if(crossedBelowOB && !g_alertedOB)
        {
         string msg = StringFormat("%s RSI crossed BELOW overbought (%.1f). RSI=%.2f | HTF RSI=%.2f",
                                   _Symbol, OverboughtLevel, rsiNow, htfNow);
         Alert(msg);
         SendNotification(msg);
         g_alertedOB = true;
         g_alertedOS = false;
        }
      //--- Reset alert flags when RSI returns to neutral zone
      if(rsiNow > OversoldLevel + 5.0 && rsiNow < OverboughtLevel - 5.0)
        {
         g_alertedOS = false;
         g_alertedOB = false;
        }
     }

   //--- Trading logic (only when EnableTrading = true)
   if(!EnableTrading)
      return;

   //--- Only trade on new bar to avoid re-entry on same bar
   static datetime lastBarTime = 0;
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;

   //--- Count existing positions for this EA
   int longCount  = 0;
   int shortCount = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket))
        {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
            PositionGetInteger(POSITION_MAGIC) == (long)MagicNumber)
           {
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)  longCount++;
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) shortCount++;
           }
        }
     }

   //--- Entry: RSI cross above oversold AND HTF RSI < 50 (bearish exhaustion)
   if(crossedAboveOS && htfNow < 50.0 && longCount == 0)
      OpenOrder(ORDER_TYPE_BUY);

   //--- Entry: RSI cross below overbought AND HTF RSI > 50 (bullish exhaustion)
   if(crossedBelowOB && htfNow > 50.0 && shortCount == 0)
      OpenOrder(ORDER_TYPE_SELL);

   //--- Exit: close longs when RSI crosses above overbought
   if(rsiNow >= OverboughtLevel && longCount > 0)
      ClosePositions(POSITION_TYPE_BUY);

   //--- Exit: close shorts when RSI crosses below oversold
   if(rsiNow <= OversoldLevel && shortCount > 0)
      ClosePositions(POSITION_TYPE_SELL);
  }

//+------------------------------------------------------------------+
//| Open a market order                                              |
//+------------------------------------------------------------------+
bool OpenOrder(ENUM_ORDER_TYPE orderType)
  {
   MqlTradeRequest request = {};
   MqlTradeResult  result  = {};

   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double price = (orderType == ORDER_TYPE_BUY) ? ask : bid;
   double sl, tp;

   if(orderType == ORDER_TYPE_BUY)
     {
      sl = price - StopLossPips  * g_point;
      tp = price + TakeProfitPips * g_point;
     }
   else
     {
      sl = price + StopLossPips  * g_point;
      tp = price - TakeProfitPips * g_point;
     }

   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = _Symbol;
   request.volume   = LotSize;
   request.type     = orderType;
   request.price    = price;
   request.sl       = sl;
   request.tp       = tp;
   request.magic    = MagicNumber;
   request.comment  = "RSI_MTF";
   request.type_filling = ORDER_FILLING_FOK;

   if(!OrderSend(request, result))
     {
      Print("OrderSend failed: retcode=", result.retcode, " | ", result.comment);
      return false;
     }
   Print("Order opened: type=", EnumToString(orderType),
         " price=", price, " SL=", sl, " TP=", tp);
   return true;
  }

//+------------------------------------------------------------------+
//| Close all positions of a given type                              |
//+------------------------------------------------------------------+
void ClosePositions(ENUM_POSITION_TYPE posType)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(!PositionSelectByTicket(ticket))
         continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol)
         continue;
      if(PositionGetInteger(POSITION_MAGIC) != (long)MagicNumber)
         continue;
      if(PositionGetInteger(POSITION_TYPE) != posType)
         continue;

      MqlTradeRequest request = {};
      MqlTradeResult  result  = {};
      request.action = TRADE_ACTION_DEAL;
      request.symbol = _Symbol;
      request.volume = PositionGetDouble(POSITION_VOLUME);
      request.type   = (posType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
      request.price  = (posType == POSITION_TYPE_BUY)
                       ? SymbolInfoDouble(_Symbol, SYMBOL_BID)
                       : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      request.position      = ticket;
      request.magic         = MagicNumber;
      request.comment       = "RSI_MTF_Close";
      request.type_filling  = ORDER_FILLING_FOK;

      if(!OrderSend(request, result))
         Print("Close failed: ticket=", ticket, " retcode=", result.retcode);
      else
         Print("Position closed: ticket=", ticket);
     }
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom Multi-pair Oscillator EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-like, more powerful

Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.

Frequently Asked Questions

Related MQL5 Expert Advisors