RSI MQL5 Indicator: Custom Alert, Multi-Timeframe & EA Integration
Use the MQL5 iRSI handle inside a complete Expert Advisor with current-chart and higher-timeframe readings. The example defaults to alerts only; optional order execution uses fixed lots and initial stops. Save it under MQL5/Experts, not Indicators: it has OnTick and no custom plot buffers.
Implementation details & mechanics
Core implementation pattern
Once per current-chart bar, read RSI from the two completed chart bars and the latest completed higher-timeframe bar. The sample up condition is an oversold rebound while higher-timeframe RSI is below 50; the down condition is an overbought reversal while higher-timeframe RSI is above 50. These are explicitly contrarian level filters, not proof of trend confirmation. HTF must be longer than the chart period.
Error handling & safety rules
When enabled, trading exits matching longs at a completed RSI reading at or above the overbought level, and shorts at or below the oversold level. These exit tests are levels, not crossing events. Failed closes are retried on subsequent ticks while attached; new entries are blocked until matching positions are gone. Initial SL/TP are broker requests; there is no ATR trailing stop. Fixed lots, FX-style pip conversion, FOK filling and nonpersistent retry state require broker-specific testing.
MQL5 Code Implementation
#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;
datetime lastBarTime=0;
bool closeLongPending=false,closeShortPending=false;
//+------------------------------------------------------------------+
//| Expert initialisation |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Validate inputs
if(RSI_Period < 2 || OversoldLevel<0 || OverboughtLevel>100 || OversoldLevel>=OverboughtLevel || LotSize<=0 || StopLossPips<=0 || TakeProfitPips<=0 || PeriodSeconds(HTF)<=PeriodSeconds(_Period))
{
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()
{
if(EnableTrading)
{
if(closeLongPending) { ClosePositions(POSITION_TYPE_BUY); closeLongPending=HasType(POSITION_TYPE_BUY); }
if(closeShortPending) { ClosePositions(POSITION_TYPE_SELL); closeShortPending=HasType(POSITION_TYPE_SELL); }
}
datetime currentBarTime=iTime(_Symbol,_Period,0);
if(currentBarTime==0 || currentBarTime==lastBarTime) return;
double rsiCurrent[2],rsiHTF[1];
if(CopyBuffer(g_rsiHandle,0,1,2,rsiCurrent)!=2 || CopyBuffer(g_rsiHTFHandle,0,1,1,rsiHTF)!=1) return;
if(currentBarTime!=iTime(_Symbol,_Period,0)) return;
lastBarTime=currentBarTime;
// Static arrays store oldest copied value first: shift 2 then shift 1.
double rsiPrev=rsiCurrent[0],rsiNow=rsiCurrent[1],htfNow=rsiHTF[0];
bool buy=rsiPrev<=OversoldLevel && rsiNow>OversoldLevel && htfNow<50;
bool sell=rsiPrev>=OverboughtLevel && rsiNow<OverboughtLevel && htfNow>50;
if(EnableAlerts && (buy || sell))
{
string msg=StringFormat("%s completed RSI %s, RSI=%.2f HTF=%.2f",_Symbol,buy?"up signal":"down signal",rsiNow,htfNow);
Alert(msg); if(!SendNotification(msg)) Print("Push failed: ",GetLastError());
}
if(!EnableTrading) return;
if(rsiNow>=OverboughtLevel && HasType(POSITION_TYPE_BUY))
{ ClosePositions(POSITION_TYPE_BUY); closeLongPending=HasType(POSITION_TYPE_BUY); }
if(rsiNow<=OversoldLevel && HasType(POSITION_TYPE_SELL))
{ ClosePositions(POSITION_TYPE_SELL); closeShortPending=HasType(POSITION_TYPE_SELL); }
if(closeLongPending || closeShortPending || HasType(POSITION_TYPE_BUY) || HasType(POSITION_TYPE_SELL)) return;
if(buy) OpenOrder(ORDER_TYPE_BUY);
if(sell) OpenOrder(ORDER_TYPE_SELL);
}
bool HasType(const ENUM_POSITION_TYPE type)
{
for(int i=PositionsTotal()-1;i>=0;i--)
if(PositionGetTicket(i)>0 && PositionGetString(POSITION_SYMBOL)==_Symbol &&
PositionGetInteger(POSITION_MAGIC)==(long)MagicNumber && PositionGetInteger(POSITION_TYPE)==type) return true;
return false;
}
//+------------------------------------------------------------------+
//| 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 = NormalizeDouble(sl,_Digits);
request.tp = NormalizeDouble(tp,_Digits);
request.magic = MagicNumber;
request.comment = "RSI_MTF";
request.type_filling = ORDER_FILLING_FOK;
if(!OrderSend(request, result) || result.retcode!=TRADE_RETCODE_DONE)
{
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) || result.retcode!=TRADE_RETCODE_DONE)
Print("Close failed: ticket=", ticket, " retcode=", result.retcode);
else
Print("Position closed: ticket=", ticket);
}
}
//+------------------------------------------------------------------+Copy this code into MetaEditor, save it in your MQL5 folder (Experts, Scripts, or Include), and compile with F7.
Generate custom MQL5 code with Pineify AI
Describe the trade execution, indicator calculations, risk management, or data processing logic you need. Pineify generates editable MQL5 source code. Review its logic, request a syntax check, and compile and test the final file in MetaEditor.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Strategy orders are simulated; live automation needs a separate integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
The calculation can be implemented on either platform, but the runtime model and buffer APIs differ. Read the Pine Script reference guide before porting logic between them.
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.