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.

forex scalping EA MT5forex scalping expert advisor MT5MQL5 scalping EA freebest scalping EA MetaTrader 5M1 M5 scalping EA MQL5

Backtest Performance

60.8%
Win Rate
12.1%
Max Drawdown
1.47
Sharpe Ratio
2022–2025
Test Period

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 (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom Forex Scalping EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-like, more powerful

Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.

Frequently Asked Questions

Related MQL5 Expert Advisors