Complete .mq5 source code

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

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionSeries evaluated on chart updatesEvent handlers with explicit buffers
DeploymentRuns in TradingView with alertsRuns in the MT5 terminal or on a VPS
Broker accessStrategy orders are simulated; live automation needs a separate integrationDirect broker connectivity
BacktestingStrategy Tester for strategy scriptsStrategy Tester for Expert Advisors
Code complexitySimpler, functional syntaxC++-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.