Complete .mq5 source code

RSI M15 Scalping MQL5 Indicator: Fast Momentum Entries with RSI

Build an M15-only RSI indicator with a same-bar EMA filter and ATR floor. This example plots RSI and separate completed-bar signal markers; it does not execute scalps, define holding periods or supply a performance record. RSI 7, thresholds 25/75 and EMA 50 are editable examples, not proven optimal settings.

How the indicator signal works

Calculation and signal conditions

An up marker requires completed RSI to cross above 25, the same bar close above its EMA and the same bar ATR at or above the input floor. A down marker reverses the RSI and EMA conditions. The ATR input is an absolute price distance: 0.0015 is a sample value, not a portable setting for all symbols. Invalid handles fail initialization and incomplete buffer copies postpone calculation.

Interpretation and limitations

The indicator has no order requests, stop loss, take profit, trailing stop or partial close. A separate EA must define those rules and costs before signal frequency can become a trade or return statistic. The forming RSI line updates, but markers and alerts use completed bars. History changes can still alter old results.

MQL5 Custom Indicator Code

#property strict
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots 4
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_label1 "M15 RSI"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrLimeGreen
#property indicator_label2 "Up signal"
#property indicator_type3 DRAW_ARROW
#property indicator_color3 clrTomato
#property indicator_label3 "Down signal"
#property indicator_type4 DRAW_NONE
#property indicator_label4 "Signal: 1 / -1 / 0"
input int InpRSIPeriod=7;
input double InpOBLevel=75;
input double InpOSLevel=25;
input int InpEMAPeriod=50;
input int InpATRPeriod=14;
input double InpMinATR=0.0015; // Price units, not portable pips.
input bool PopupAlerts=true;
input bool PushAlerts=false;
double rsiLine[],up[],down[],signal[];
int hr=INVALID_HANDLE,he=INVALID_HANDLE,ha=INVALID_HANDLE;
datetime lastClosed=0;
int OnInit()
{
   if(_Period!=PERIOD_M15 || InpRSIPeriod<2 || InpEMAPeriod<1 || InpATRPeriod<1 ||
      InpOSLevel<0 || InpOBLevel>100 || InpOSLevel>=InpOBLevel || InpMinATR<0)
      return INIT_PARAMETERS_INCORRECT;
   SetIndexBuffer(0,rsiLine,INDICATOR_DATA); SetIndexBuffer(1,up,INDICATOR_DATA);
   SetIndexBuffer(2,down,INDICATOR_DATA); SetIndexBuffer(3,signal,INDICATOR_DATA);
   ArraySetAsSeries(rsiLine,false); ArraySetAsSeries(up,false);
   ArraySetAsSeries(down,false); ArraySetAsSeries(signal,false);
   int begin=(int)MathMax(InpRSIPeriod+1,MathMax(InpEMAPeriod,InpATRPeriod));
   for(int p=0;p<4;p++) { PlotIndexSetInteger(p,PLOT_DRAW_BEGIN,begin); PlotIndexSetDouble(p,PLOT_EMPTY_VALUE,EMPTY_VALUE); }
   PlotIndexSetInteger(1,PLOT_ARROW,233); PlotIndexSetInteger(2,PLOT_ARROW,234);
   IndicatorSetInteger(INDICATOR_LEVELS,2);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,InpOSLevel); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,InpOBLevel);
   hr=iRSI(_Symbol,PERIOD_M15,InpRSIPeriod,PRICE_CLOSE);
   he=iMA(_Symbol,PERIOD_M15,InpEMAPeriod,0,MODE_EMA,PRICE_CLOSE);
   ha=iATR(_Symbol,PERIOD_M15,InpATRPeriod);
   return hr==INVALID_HANDLE || he==INVALID_HANDLE || ha==INVALID_HANDLE?INIT_FAILED:INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
   if(hr!=INVALID_HANDLE) IndicatorRelease(hr);
   if(he!=INVALID_HANDLE) IndicatorRelease(he);
   if(ha!=INVALID_HANDLE) IndicatorRelease(ha);
}
int OnCalculate(const int rates_total,const int prev_calculated,const datetime &time[],
 const double &open[],const double &high[],const double &low[],const double &close[],
 const long &tick_volume[],const long &volume[],const int &spread[])
{
   int begin=(int)MathMax(InpRSIPeriod+1,MathMax(InpEMAPeriod,InpATRPeriod));
   if(rates_total<begin+3) return 0;
   ArraySetAsSeries(time,false); ArraySetAsSeries(close,false);
   double r[],e[],a[];
   if(CopyBuffer(hr,0,0,rates_total,r)!=rates_total || CopyBuffer(he,0,0,rates_total,e)!=rates_total ||
      CopyBuffer(ha,0,0,rates_total,a)!=rates_total) return prev_calculated;
   ArrayInitialize(up,EMPTY_VALUE); ArrayInitialize(down,EMPTY_VALUE); ArrayInitialize(signal,0);
   for(int i=0;i<rates_total;i++) rsiLine[i]=i>=InpRSIPeriod?r[i]:EMPTY_VALUE;
   for(int i=begin;i<rates_total-1;i++)
   {
      if(a[i]<InpMinATR) continue;
      if(r[i-1]<=InpOSLevel && r[i]>InpOSLevel && close[i]>e[i]) { up[i]=r[i]; signal[i]=1; }
      if(r[i-1]>=InpOBLevel && r[i]<InpOBLevel && close[i]<e[i]) { down[i]=r[i]; signal[i]=-1; }
   }
   int i=rates_total-2;
   if(prev_calculated==0 || lastClosed==0) lastClosed=time[i];
   else if(time[i]!=lastClosed)
   {
      lastClosed=time[i];
      if(signal[i]!=0)
      {
         string msg=StringFormat("%s M15 RSI %s closed-bar signal",_Symbol,signal[i]>0?"up":"down");
         if(PopupAlerts) Alert(msg);
         if(PushAlerts && !SendNotification(msg)) Print("Push failed: ",GetLastError());
      }
   }
   return rates_total;
}

Copy this code into MetaEditor, save it in the MQL5/Indicators folder, and compile with F7.

Change the settings or add your own signal filter

Describe the inputs, buffers, alerts, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.