Forex Scalping EA MT5: Best MQL5 Strategies, Code & 2026 Guide
This page covers how to build and deploy a high-frequency forex scalping Expert Advisor in MQL5 for MetaTrader 5, targeting short-term momentum bursts on M1 and M5 timeframes. You will find complete, runnable MQL5 code, backtested performance stats across 2022–2025, strategy logic breakdowns, and answers to the most common questions traders ask when automating a scalping EA.
Historical test results
Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.
Strategy logic
Entry conditions
A long trade is triggered when the fast EMA (9-period) crosses above the slow EMA (21-period) on the M5 chart and the current candle close is above the 50-period EMA, confirming the short-term trend. An additional RSI filter (14-period, threshold 55) ensures the EA only enters longs in momentum conditions, avoiding choppy sideways markets. Short entries mirror these conditions in reverse, requiring the fast EMA to cross below the slow EMA with RSI below 45.
Exit conditions
Each trade is protected by a fixed stop-loss set at 1.5x the 14-period ATR value measured at entry, dynamically sized to current volatility rather than a static pip count. The take-profit target is placed at 2x ATR from entry, yielding an approximate 1:1.33 reward-to-risk ratio per trade. If price moves 1x ATR in favour before the TP is reached, a trailing stop activates and locks in half the open profit, letting winners run during extended moves.
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, hSlowEMA, hTrendEMA, hRSI, hATR;
//--- Previous bar values for cross detection
double prevFast, prevSlow;
//+------------------------------------------------------------------+
//| Expert initialisation |
//+------------------------------------------------------------------+
int OnInit()
{
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)
{
IndicatorRelease(hFastEMA);
IndicatorRelease(hSlowEMA);
IndicatorRelease(hTrendEMA);
IndicatorRelease(hRSI);
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 == lastBar) return;
lastBar = currentBar;
//--- Read indicator buffers (index 1 = previous closed bar, 2 = bar before that)
double bufFast[3], bufSlow[3], bufTrend[2], bufRSI[2], bufATR[2];
if(CopyBuffer(hFastEMA, 0, 0, 3, bufFast) < 3) return;
if(CopyBuffer(hSlowEMA, 0, 0, 3, bufSlow) < 3) return;
if(CopyBuffer(hTrendEMA, 0, 0, 2, bufTrend) < 2) return;
if(CopyBuffer(hRSI, 0, 0, 2, bufRSI) < 2) return;
if(CopyBuffer(hATR, 0, 0, 2, bufATR) < 2) return;
double fastCurr = bufFast[1], fastPrev = bufFast[2];
double slowCurr = bufSlow[1], slowPrev = bufSlow[2];
double trendEMA = bufTrend[1];
double rsi = bufRSI[1];
double atr = bufATR[1];
double closeBar = iClose(_Symbol, PERIOD_M5, 1);
//--- 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_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_BUY, LotSize, sl, tp);
}
else if(bearCross && closeBar < trendEMA && rsi <= RSI_ShortMax)
{
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_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))
{
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(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))
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 ATR inputs, alert rules, visual style, or confirmation filter 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 | Via TradingView broker 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 Supertrend reference before porting logic between them.
Frequently Asked Questions
Related MQL5 pages
Risk and testing note
Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.