How to Code an RSI EA in MQL5 (Full Example + Real Backtest)

Published 2026-09-22 · Ranar Algo

The Relative Strength Index is one of the first indicators most traders reach for, and "how do I turn my RSI rule into an EA?" is a question that comes up constantly. The idea is simple — buy when RSI comes back up out of oversold, sell when it drops out of overbought — but the MQL5 details trip people up: RSI in MQL5 is a *handle*, not a value, you read it with CopyBuffer, and you only want to act once per bar. Below is a complete, compiling Expert Advisor that does it correctly, followed by a real backtest of the exact code.

The strategy

A classic RSI(14) mean-reversion rule on the H1 chart:

The key word is *crosses*. You compare the last two closed bars — you do not fire every time RSI happens to be below 30, or you'll open a trade on every tick while it sits there.

The MQL5 details that matter

Three things separate MQL5 from old MQL4 code and from most AI-generated snippets:

The full EA

#property version   "1.00"
#property strict
#include <Trade/Trade.mqh>

input int    InpRsiPeriod   = 14;
input double InpOversold    = 30.0;
input double InpOverbought  = 70.0;
input double InpExitLevel   = 50.0;
input double InpLots        = 0.10;
input int    InpStopLossPts = 500;   // stop loss in points
input int    InpTakeProfPts = 900;   // take profit in points
input int    InpMagic       = 528491;

CTrade   c_Trade;
int      h_RSI = INVALID_HANDLE;
double   g_rsi[];
datetime g_lastBar = 0;

int OnInit()
{
   h_RSI = iRSI(_Symbol, _Period, InpRsiPeriod, PRICE_CLOSE);
   if(h_RSI == INVALID_HANDLE) { Print("iRSI failed, err=", GetLastError()); return(INIT_FAILED); }
   ArraySetAsSeries(g_rsi, true);
   c_Trade.SetExpertMagicNumber(InpMagic);
   c_Trade.SetTypeFillingBySymbol(_Symbol);
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   if(h_RSI != INVALID_HANDLE) IndicatorRelease(h_RSI);
}

bool IsNewBar()
{
   datetime t = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
   if(t == g_lastBar) return(false);
   g_lastBar = t;
   return(true);
}

bool HasPosition()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(PositionGetTicket(i) == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == InpMagic) return(true);
   }
   return(false);
}

void OnTick()
{
   if(!IsNewBar()) return;
   if(CopyBuffer(h_RSI, 0, 0, 3, g_rsi) < 3) return;
   double rsiPrev = g_rsi[2];   // bar before last closed
   double rsiNow  = g_rsi[1];   // last closed bar

   if(HasPosition())
   {
      long type = PositionGetInteger(POSITION_TYPE);
      if(type == POSITION_TYPE_BUY  && rsiNow >= InpExitLevel) c_Trade.PositionClose(_Symbol);
      if(type == POSITION_TYPE_SELL && rsiNow <= InpExitLevel) c_Trade.PositionClose(_Symbol);
      return;
   }

   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double pt  = _Point;

   if(rsiPrev <= InpOversold && rsiNow > InpOversold)       // cross up out of oversold -> long
   {
      double sl = InpStopLossPts > 0 ? NormalizeDouble(ask - InpStopLossPts * pt, _Digits) : 0;
      double tp = InpTakeProfPts > 0 ? NormalizeDouble(ask + InpTakeProfPts * pt, _Digits) : 0;
      c_Trade.Buy(InpLots, _Symbol, ask, sl, tp);
   }
   else if(rsiPrev >= InpOverbought && rsiNow < InpOverbought) // cross down out of overbought -> short
   {
      double sl = InpStopLossPts > 0 ? NormalizeDouble(bid + InpStopLossPts * pt, _Digits) : 0;
      double tp = InpTakeProfPts > 0 ? NormalizeDouble(bid - InpTakeProfPts * pt, _Digits) : 0;
      c_Trade.Sell(InpLots, _Symbol, bid, sl, tp);
   }
}

double OnTester() { return TesterStatistics(STAT_PROFIT); }

A few things worth calling out: every position loop is filtered by both symbol and magic number, so this EA never touches trades it didn't open; stops and targets are normalized to the symbol's digit count; and the handle is released in OnDeinit(). Those are the details that keep an EA correct on a real 5-digit broker.

A real backtest of this exact code

Paste that into MetaEditor, compile (it builds with 0 errors, 0 warnings), and run it in the Strategy Tester. Here is the result of running this exact source on EURUSD, H1, full-year 2024 with the default inputs above and a $10,000 starting balance:

That's a modest, low-drawdown result on unoptimized defaults — exactly what you'd expect from a plain RSI rule with no filters. It's a starting point, not a finished system. Past performance does not guarantee future results, and you should always test across multiple symbols and market regimes before trusting any EA.

If you'd rather describe it than code it

Writing, compiling, and backtesting an EA by hand is the reliable way to learn, but it's slow to iterate. Ranar Algo lets you type the strategy in plain English — "RSI 14 on H1, buy when it crosses up through 30, sell when it crosses down through 70, exit at 50, 50-pip stop" — and it generates the MQL5, compiles it, and runs a real MetaTrader 5 backtest, returning the same kind of numbers you see above. You keep full ownership of the generated code — it's your IP to take into MetaEditor and modify. It's still improving and feedback is genuinely welcome; the goal is to shorten the loop between an idea and seeing whether it holds up.

Wrapping up

An RSI EA is a great first Expert Advisor because the logic is easy to reason about, which makes the MQL5 mechanics — handles, CopyBuffer, series indexing, new-bar timing — easy to see in isolation. Get those right and you can swap in any indicator. The code above compiles as-is and the backtest numbers are from that exact source, so you have a clean, honest baseline to build on.

Build & backtest your own EA free →

Ranar Algo turns a plain-English strategy into a compiled MQL5 Expert Advisor with a real MetaTrader 5 backtest. You keep your IP. Past performance does not guarantee future results.