Forex Scalping EA MT5: M5 EMA, RSI and ATR Code Example
Inspect an educational M5 Expert Advisor using a completed-bar EMA crossover, RSI threshold and ATR-based stops. The source illustrates signal timing and trade-result checks; it has no verified backtest record. Broker execution, costs and account exposure require separate testing before any deployment.
Strategy logic
Entry conditions
Once per new M5 bar, compare the two completed bars of EMA 9 and EMA 21. A bullish crossover also requires the completed close above EMA 50 and RSI 14 at least 55; shorts reverse those conditions with RSI at most 45. These are illustrative filters, not proof of momentum or avoidance of sideways losses. All handles use M5 regardless of the chart timeframe.
Exit conditions
The initial requested stop and target are 1.5 and 2 times completed-bar ATR from the quoted entry price. At each new M5 bar, trailing uses the latest completed ATR: once price is at least that distance in favor, the requested stop stays that distance behind price and only tightens. It does not lock half the profit or guarantee a fill or maximum loss. Fixed lots, FOK filling, stop/tick grids and netting-account ownership need broker-specific validation; the sample has no spread, session or news filter.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| ForexScalpingEA.mq5 |
//| Forex Scalping Expert Advisor — EMA Cross + RSI + ATR |
//| Compatible with MetaTrader 5 (MQL5 5.x) |
//| For educational and research purposes only. Past performance |
//| does not guarantee future results. Trade at your own risk. |
//+------------------------------------------------------------------+
#property copyright "Pineify — pineify.app"
#property version "1.00"
#property strict
//--- Input parameters
input int FastEMA_Period = 9; // Fast EMA period
input int SlowEMA_Period = 21; // Slow EMA period
input int TrendEMA_Period = 50; // Trend filter EMA period
input int RSI_Period = 14; // RSI period
input double RSI_LongMin = 55.0; // RSI minimum for longs
input double RSI_ShortMax = 45.0; // RSI maximum for shorts
input int ATR_Period = 14; // ATR period
input double SL_ATR_Mult = 1.5; // Stop-loss ATR multiplier
input double TP_ATR_Mult = 2.0; // Take-profit ATR multiplier
input double Trail_ATR_Mult = 1.0; // Trailing stop activation (ATR mult)
input double LotSize = 0.1; // Fixed lot size
input int MagicNumber = 20260001;// EA magic number
input string TradeComment = "ScalpEA";
//--- Indicator handles
int hFastEMA=INVALID_HANDLE, hSlowEMA=INVALID_HANDLE, hTrendEMA=INVALID_HANDLE, hRSI=INVALID_HANDLE, hATR=INVALID_HANDLE;
//+------------------------------------------------------------------+
//| Expert initialisation |
//+------------------------------------------------------------------+
int OnInit()
{
if(FastEMA_Period<1 || SlowEMA_Period<1 || TrendEMA_Period<1 || RSI_Period<1 || ATR_Period<1 ||
RSI_LongMin<0 || RSI_LongMin>100 || RSI_ShortMax<0 || RSI_ShortMax>100 ||
SL_ATR_Mult<=0 || TP_ATR_Mult<=0 || Trail_ATR_Mult<=0 || LotSize<=0 || MagicNumber<0)
return INIT_PARAMETERS_INCORRECT;
hFastEMA = iMA(_Symbol, PERIOD_M5, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hSlowEMA = iMA(_Symbol, PERIOD_M5, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hTrendEMA = iMA(_Symbol, PERIOD_M5, TrendEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hRSI = iRSI(_Symbol, PERIOD_M5, RSI_Period, PRICE_CLOSE);
hATR = iATR(_Symbol, PERIOD_M5, ATR_Period);
if(hFastEMA == INVALID_HANDLE || hSlowEMA == INVALID_HANDLE ||
hTrendEMA == INVALID_HANDLE || hRSI == INVALID_HANDLE || hATR == INVALID_HANDLE)
{
Print("Error creating indicator handles: ", GetLastError());
return INIT_FAILED;
}
Print("ForexScalpingEA initialised on ", _Symbol, " M5");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(hFastEMA!=INVALID_HANDLE) IndicatorRelease(hFastEMA);
if(hSlowEMA!=INVALID_HANDLE) IndicatorRelease(hSlowEMA);
if(hTrendEMA!=INVALID_HANDLE) IndicatorRelease(hTrendEMA);
if(hRSI!=INVALID_HANDLE) IndicatorRelease(hRSI);
if(hATR!=INVALID_HANDLE) IndicatorRelease(hATR);
Print("ForexScalpingEA removed. Reason code: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Only act on a new M5 bar
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, PERIOD_M5, 0);
if(currentBar<=0 || currentBar == lastBar) return;
// Static arrays: older copied value is at index 0.
double bufFast[2], bufSlow[2], bufTrend[1], bufRSI[1], bufATR[1];
if(CopyBuffer(hFastEMA, 0, 1, 2, bufFast)!=2 ||
CopyBuffer(hSlowEMA, 0, 1, 2, bufSlow)!=2 ||
CopyBuffer(hTrendEMA, 0, 1, 1, bufTrend)!=1 ||
CopyBuffer(hRSI, 0, 1, 1, bufRSI)!=1 ||
CopyBuffer(hATR, 0, 1, 1, bufATR)!=1) return;
double fastCurr=bufFast[1], fastPrev=bufFast[0];
double slowCurr=bufSlow[1], slowPrev=bufSlow[0];
double trendEMA=bufTrend[0], rsi=bufRSI[0], atr=bufATR[0];
double closeBar=iClose(_Symbol, PERIOD_M5, 1);
if(atr<=0 || !MathIsValidNumber(atr) || closeBar<=0) return;
lastBar=currentBar;
//--- Detect EMA crossovers on the just-closed bar
bool bullCross = (fastPrev <= slowPrev) && (fastCurr > slowCurr);
bool bearCross = (fastPrev >= slowPrev) && (fastCurr < slowCurr);
//--- Manage existing positions (trailing stop)
ManageTrailingStop(atr);
//--- Skip if a position already exists for this symbol+magic
if(PositionExists()) return;
//--- Entry signals
if(bullCross && closeBar > trendEMA && rsi >= RSI_LongMin)
{
double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SL_ATR_Mult * atr,
(int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
double tp = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + TP_ATR_Mult * atr,
(int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
OpenMarketOrder(ORDER_TYPE_BUY, LotSize, sl, tp);
}
else if(bearCross && closeBar < trendEMA && rsi <= RSI_ShortMax)
{
double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) + SL_ATR_Mult * atr,
(int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
double tp = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - TP_ATR_Mult * atr,
(int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
OpenMarketOrder(ORDER_TYPE_SELL, LotSize, sl, tp);
}
}
//+------------------------------------------------------------------+
//| Send a market order |
//+------------------------------------------------------------------+
bool OpenMarketOrder(ENUM_ORDER_TYPE type, double lots, double sl, double tp)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lots;
request.type = type;
request.price = (type == ORDER_TYPE_BUY)
? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
request.sl = sl;
request.tp = tp;
request.deviation= 10;
request.magic = MagicNumber;
request.comment = TradeComment;
request.type_filling = ORDER_FILLING_FOK;
if(!OrderSend(request, result) ||
(result.retcode!=TRADE_RETCODE_DONE && result.retcode!=TRADE_RETCODE_DONE_PARTIAL))
{
Print("OrderSend failed: ", result.retcode, " — ", result.comment);
return false;
}
Print("Order opened: ticket=", result.order, " type=", EnumToString(type),
" price=", result.price, " sl=", sl, " tp=", tp);
return true;
}
//+------------------------------------------------------------------+
//| Check if a position exists for this EA |
//+------------------------------------------------------------------+
bool PositionExists()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Apply ATR-based trailing stop to open positions |
//+------------------------------------------------------------------+
void ManageTrailingStop(double atr)
{
double trailDist = Trail_ATR_Mult * atr;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket==0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
if(posType == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double newSL = NormalizeDouble(bid - trailDist, digits);
//--- Activate trail only after price moved Trail_ATR_Mult in favour
if(bid >= openPrice + trailDist && newSL > currentSL)
ModifySL(ticket, newSL);
}
else if(posType == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double newSL = NormalizeDouble(ask + trailDist, digits);
if(ask <= openPrice - trailDist && (currentSL == 0 || newSL < currentSL))
ModifySL(ticket, newSL);
}
}
}
//+------------------------------------------------------------------+
//| Modify stop-loss of an existing position |
//+------------------------------------------------------------------+
void ModifySL(ulong ticket, double newSL)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = ticket;
req.symbol = _Symbol;
req.sl = newSL;
req.tp = PositionGetDouble(POSITION_TP);
if(!OrderSend(req, res) || res.retcode!=TRADE_RETCODE_DONE)
Print("ModifySL failed: ", res.retcode);
}
//+------------------------------------------------------------------+Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom Forex scalping EA
Describe the entry, exit, position, order, and risk rules you want. Pineify generates editable MQL5 source code that you can inspect and compile 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.
Frequently Asked Questions
Related MQL5 pages
Technical references
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.