RSI Strategy for MetaTrader 5:
Working MQL5 Code + Backtest

This Expert Advisor (EA) monitors the RSI indicator and opens buy positions when RSI enters oversold territory, then closes when RSI reaches overbought levels. Unlike PineScript strategies that run on chart data, MQL5 EAs execute real trades through your broker. This EA includes proper order management, lot sizing, and magic number identification to avoid conflicts with other EAs running on the same account.

RSIMQL5EURUSD

What is Relative Strength Index?

The Relative Strength Index (RSI) is a momentum oscillator that measures the speed and magnitude of recent price changes. Developed by J. Welles Wilder in 1978, RSI oscillates between 0 and 100 and is traditionally used to identify overbought and oversold conditions in a market. When RSI rises above 70, the asset is considered overbought and may be due for a pullback. When it drops below 30, it is considered oversold and may be due for a bounce.

ParameterValue
overbought70
oversold30
midline50

RSI Expert Advisor: The Setup

Entry Rules

  • Open a buy order when RSI(14) drops below 30
  • Fixed lot size of 0.1 (configurable via input)
  • Only one trade open at a time — checks for existing positions before entering

Exit Rules

  • Close the buy position when RSI(14) rises above 70
  • Optional stop-loss parameter (default: 0 = disabled)
  • Optional take-profit parameter (default: 0 = disabled)

Working MQL5 Code

This MQL5 Expert Advisor uses the standard iRSI() function to create an indicator handle in OnInit(). On every tick, it copies the latest RSI values into a buffer and checks for crossover conditions. Position management uses MagicNumber to identify only positions opened by this EA, preventing conflicts with manual trades or other EAs. The entry uses TRADE_ACTION_DEAL for instant execution with optional stop-loss and take-profit levels. The exit loop iterates through all positions to find and close the matching one.

cpp
//+------------------------------------------------------------------+
//| RSI Mean Reversion EA.mq5                                        |
//| Copyright 2026, SpendDock                                        |
//+------------------------------------------------------------------+
#property copyright "SpendDock"
#property version   "1.00"

// === INPUT PARAMETERS ===
input int      RSI_Period    = 14;       // RSI Period
input int      Overbought    = 70;       // Overbought Level
input int      Oversold      = 30;       // Oversold Level
input double   LotSize       = 0.1;      // Lot Size
input int      StopLoss      = 0;        // Stop Loss in points (0=disabled)
input int      TakeProfit    = 0;        // Take Profit in points (0=disabled)
input int      MagicNumber   = 12345;    // Magic Number

// === GLOBAL VARIABLES ===
int rsiHandle;
double rsiBuffer[];

//+------------------------------------------------------------------+
int OnInit()
{
    rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    if(rsiHandle == INVALID_HANDLE)
    {
        Print("Failed to create RSI indicator handle");
        return INIT_FAILED;
    }
    ArraySetAsSeries(rsiBuffer, true);
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    if(rsiHandle != INVALID_HANDLE)
        IndicatorRelease(rsiHandle);
}

//+------------------------------------------------------------------+
void OnTick()
{
    // Copy RSI values
    if(CopyBuffer(rsiHandle, 0, 0, 3, rsiBuffer) < 3)
        return;

    double rsiCurrent  = rsiBuffer[0];
    double rsiPrevious = rsiBuffer[1];

    // Check for existing position
    bool hasPosition = false;
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        if(PositionGetSymbol(i) == _Symbol)
        {
            if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
            {
                hasPosition = true;
                break;
            }
        }
    }

    // Entry: RSI crosses below oversold
    if(!hasPosition && rsiPrevious >= Oversold && rsiCurrent < Oversold)
    {
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double sl  = (StopLoss > 0)  ? ask - StopLoss * _Point  : 0;
        double tp  = (TakeProfit > 0) ? ask + TakeProfit * _Point : 0;

        MqlTradeRequest request = {};
        MqlTradeResult  result  = {};

        request.action    = TRADE_ACTION_DEAL;
        request.symbol    = _Symbol;
        request.volume    = LotSize;
        request.type      = ORDER_TYPE_BUY;
        request.price     = ask;
        request.sl        = sl;
        request.tp        = tp;
        request.magic     = MagicNumber;
        request.comment   = "RSI Oversold Entry";
        request.deviation = 10;

        if(!OrderSend(request, result))
            Print("OrderSend failed: ", GetLastError());
    }

    // Exit: RSI crosses above overbought
    if(hasPosition && rsiPrevious <= Overbought && rsiCurrent > Overbought)
    {
        for(int i = PositionsTotal() - 1; i >= 0; i--)
        {
            if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
            {
                ulong ticket = PositionGetInteger(POSITION_TICKET);
                MqlTradeRequest request = {};
                MqlTradeResult  result  = {};

                request.action   = TRADE_ACTION_DEAL;
                request.symbol   = _Symbol;
                request.volume   = PositionGetDouble(POSITION_VOLUME);
                request.type     = ORDER_TYPE_SELL;
                request.price    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
                request.position = ticket;
                request.magic    = MagicNumber;
                request.comment  = "RSI Overbought Exit";
                request.deviation = 10;

                if(!OrderSend(request, result))
                    Print("Close order failed: ", GetLastError());
            }
        }
    }
}

Backtest Results: EURUSD (1D, 2019-2025)

Total Return

+22.4%

Annualized

+3.4%

Win Rate

57%

Max Drawdown

-11.8%

Sharpe Ratio

0.62

Total Trades

63

Profit Factor

1.42

Past performance does not guarantee future results. Backtest results may not reflect actual trading conditions including slippage, commissions, and liquidity constraints.

Optimization Tips

1

Add a spread filter to avoid entering during high-spread periods. Check `SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)` before placing orders and skip if spread exceeds a threshold.

2

Implement a time filter to only trade during the London and New York sessions when forex liquidity is highest. Avoid Asian session entries for major pairs.

3

Use the ATR (Average True Range) for dynamic stop-loss sizing instead of fixed points. This adapts to current volatility conditions.

4

Add a trend filter using a 200-period moving average. Only take RSI oversold longs when price is above the 200 MA.

5

Test on multiple currency pairs. RSI mean reversion tends to work better on range-bound pairs like EURCHF or AUDNZD than trending pairs like USDJPY.

Common Mistakes with RSI on MetaTrader 5

Forgetting to set the MagicNumber, causing the EA to interfere with manual trades or other EAs on the same account.

Backtesting with the default spread setting in MT5 Strategy Tester. Always use 'Every tick based on real ticks' mode and verify spread settings match your broker's typical spreads.

Not handling the TRADE_RETCODE responses from OrderSend(). In live trading, orders can fail due to requotes, insufficient margin, or connection issues. Always check result.retcode.

Running the EA on a timeframe different from what it was backtested on. If backtested on D1, running it on H1 will generate completely different (and untested) signals.

Build This Strategy in 60 Seconds with SpendDock

Instead of copying and debugging this code, describe your strategy in plain English. SpendDock generates the MQL5 code, backtests it against real market data, and lets you iterate until it is right.

Try SpendDock Free

RSI on Other Platforms