Complete .mq5 source code

USDJPY Scalping EA MQL5: Low-Spread Intraday Momentum Strategy

A complete educational M5 Expert Advisor combining Bollinger Bands, Stochastic and EMA conditions on a broker-provided USDJPY symbol. It uses fixed lots, point-based SL/TP, tick-handler trailing and a broker-time entry window. No verified backtest, January 2026 demo diary or pair-performance ranking is supplied.

Strategy logic

Entry conditions

The last completed M5 close must be at or below the lower Bollinger band for a buy, with Stochastic main above signal and above 20, and close above EMA. A sell uses close at or above the upper band, main below signal and below 80, and close below EMA. These are level conditions, not a Stochastic threshold crossover or intrabar wick touch. Entry is evaluated once per ready M5 bar, with a spread gate and one matching-position limit.

Exit conditions

The defaults request 80-point SL and 100-point TP; on a three-digit USDJPY quote those commonly correspond to 8 and 10 pips, but broker units must be checked. Trailing activates at 60 points of favorable movement with a 40-point gap. It runs on processed ticks within the window. Outside the broker-time window, matching positions receive repeated close requests independently of the entry spread gate. Gaps, disconnection and rejected execution can defeat an intended cutoff.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//| USDJPY Scalping EA - MQL5                                       |
//| Triple-Filter: BB + Stochastic + EMA Mean Reversion             |
//| Pineify.app                                                      |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version   "1.00"
#property description "USDJPY Scalping EA with Bollinger Bands + Stochastic + EMA"
#property description "Trades mean reversion scalps on M5 inside a broker-time window"
#property description "For educational purposes only. Trade at your own risk."

#include <Trade/Trade.mqh>

CTrade trade;

//--- Input Parameters
input group "=== Strategy Parameters ==="
input int    BB_Period       = 20;           // Bollinger Bands period
input double BB_Deviation    = 2.0;           // Bollinger Bands deviation
input int    Stoch_K         = 5;             // Stochastic %K period
input int    Stoch_D         = 3;             // Stochastic %D period
input int    Stoch_Slowing   = 3;             // Stochastic slowing
input int    EMA_Period      = 50;            // Trend filter EMA

input group "=== Trade Settings ==="
input ulong  MagicNumber     = 123456;
input double LotSize         = 0.01;
input int    SL_Points       = 80;            // Stop loss in points (8 pips)
input int    TP_Points       = 100;           // Take profit in points (10 pips)
input int    TrailActivate   = 60;            // Activate trailing at points profit
input int    TrailDistance   = 40;            // Trailing stop distance in points
input int    MaxSpreadPoints = 30;            // Max allowed spread in points

input group "=== Session Filter ==="
input int    SessionStart    = 12;            // Broker-time session start hour
input int    SessionEnd      = 16;            // Broker-time session end hour

//--- Indicator handles
int hBB=INVALID_HANDLE,hStoch=INVALID_HANDLE,hEMA=INVALID_HANDLE;

//--- Indicator buffers
double bbUpper[], bbLower[];
double stochMain[], stochSignal[];
double emaLine[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(BB_Period<2 || BB_Deviation<=0 || Stoch_K<1 || Stoch_D<1 || Stoch_Slowing<1 || EMA_Period<1 || LotSize<=0 || SL_Points<=0 || TP_Points<=0 || TrailActivate<0 || TrailDistance<=0 || MaxSpreadPoints<0 || SessionStart<0 || SessionEnd>24 || SessionStart>=SessionEnd) return INIT_PARAMETERS_INCORRECT;
   trade.SetAsyncMode(false);
   trade.SetTypeFillingBySymbol(_Symbol);
   trade.SetExpertMagicNumber(MagicNumber);

   hBB = iBands(_Symbol, PERIOD_M5, BB_Period, 0, BB_Deviation, PRICE_CLOSE);
   hStoch = iStochastic(_Symbol, PERIOD_M5, Stoch_K, Stoch_D, Stoch_Slowing, MODE_SMA, STO_CLOSECLOSE);
   hEMA = iMA(_Symbol, PERIOD_M5, EMA_Period, 0, MODE_EMA, PRICE_CLOSE);

   if(hBB == INVALID_HANDLE || hStoch == INVALID_HANDLE ||
      hEMA == INVALID_HANDLE)
   {
      Print("ERROR: Failed to create indicator handles. EA will not trade.");
      return INIT_FAILED;
   }

   ArraySetAsSeries(bbUpper, true);
   ArraySetAsSeries(bbLower, true);
   ArraySetAsSeries(stochMain, true);
   ArraySetAsSeries(stochSignal, true);
   ArraySetAsSeries(emaLine, true);

   Print("USDJPY Scalping EA initialized. Magic=", MagicNumber, " Lots=", LotSize);
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(hBB!=INVALID_HANDLE) IndicatorRelease(hBB);
   if(hStoch!=INVALID_HANDLE) IndicatorRelease(hStoch);
   if(hEMA!=INVALID_HANDLE) IndicatorRelease(hEMA);
   Print("USDJPY Scalping EA deinitialized. Reason=", reason);
}

//+------------------------------------------------------------------+
//| Calculate current spread in points                               |
//+------------------------------------------------------------------+
double GetSpreadPoints()
{
   return (SymbolInfoDouble(_Symbol, SYMBOL_ASK) -
           SymbolInfoDouble(_Symbol, SYMBOL_BID)) / _Point;
}

//+------------------------------------------------------------------+
//| Detect new bar on M5 timeframe                                   |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Count open positions for this EA                                 |
//+------------------------------------------------------------------+
int CountPositions()
{
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == (long)MagicNumber)
         count++;
   }
   return count;
}

//+------------------------------------------------------------------+
//| Close all positions for this symbol and magic number             |
//+------------------------------------------------------------------+
void CloseAll()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != (long)MagicNumber) continue;
      if(!trade.PositionClose(ticket) || trade.ResultRetcode()!=TRADE_RETCODE_DONE) Print("Close failed: ",trade.ResultRetcodeDescription());
   }
}

//+------------------------------------------------------------------+
//| Manage trailing stops for open positions                         |
//+------------------------------------------------------------------+
void ManageTrailing()
{
   double trailPts = TrailDistance * _Point;
   double activatePts = TrailActivate * _Point;

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != (long)MagicNumber) continue;

      double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double currentSL = PositionGetDouble(POSITION_SL);
      double tp = PositionGetDouble(POSITION_TP);
      ENUM_POSITION_TYPE pType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

      double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double newSL = currentSL;

      if(pType == POSITION_TYPE_BUY)
      {
         double profit = bid - openPrice;
         if(profit >= activatePts)
         {
            newSL = bid - trailPts;
            if(newSL > currentSL)
               { if(!trade.PositionModify(ticket,NormalizeDouble(newSL,_Digits),tp) || trade.ResultRetcode()!=TRADE_RETCODE_DONE) Print("Trail failed: ",trade.ResultRetcodeDescription()); }
         }
      }
      else if(pType == POSITION_TYPE_SELL)
      {
         double profit = openPrice - ask;
         if(profit >= activatePts)
         {
            newSL = ask + trailPts;
            if(currentSL == 0 || newSL < currentSL)
               { if(!trade.PositionModify(ticket,NormalizeDouble(newSL,_Digits),tp) || trade.ResultRetcode()!=TRADE_RETCODE_DONE) Print("Trail failed: ",trade.ResultRetcodeDescription()); }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Main tick handler                                                |
//+------------------------------------------------------------------+
void OnTick()
{
   MqlDateTime dt; if(!TimeToStruct(TimeCurrent(),dt)) return;
   // Exit management is independent of entry spread and runs outside the window too.
   if(dt.hour<SessionStart || dt.hour>=SessionEnd) { CloseAll(); return; }
   ManageTrailing();
   if(GetSpreadPoints()>MaxSpreadPoints) return;
   static datetime lastBar=0;
   datetime currentBar=iTime(_Symbol,PERIOD_M5,0);
   if(currentBar==0 || currentBar==lastBar) return;

   //--- 5. Read indicator buffers (shift 1 = last completed bar)
   if(CopyBuffer(hBB, 1, 1, 2, bbUpper) < 2) return;
   if(CopyBuffer(hBB, 2, 1, 2, bbLower) < 2) return;
   if(CopyBuffer(hStoch, 0, 1, 2, stochMain) < 2) return;
   if(CopyBuffer(hStoch, 1, 1, 2, stochSignal) < 2) return;
   if(CopyBuffer(hEMA, 0, 1, 2, emaLine) < 2) return;

   //--- 6. Skip entry if already in a position
   if(CountPositions() > 0)
      return;

   //--- 7. Reference prices from completed bar
   double close1 = iClose(_Symbol, PERIOD_M5, 1);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(close1<=0 || ask<=0 || bid<=0) return;
   lastBar=currentBar;

   //--- 8. LONG ENTRY CONDITIONS
   // Condition A: Close at or below lower Bollinger Band
   bool bbLong   = (close1 <= bbLower[0]);
   // Condition B: Stochastic main above signal and above 20 (bullish momentum)
   bool stochLong = (stochMain[0] > stochSignal[0] && stochMain[0] > 20.0);
   // Condition C: Price above 50 EMA confirming uptrend
   bool trendLong = (close1 > emaLine[0]);

   if(bbLong && stochLong && trendLong)
   {
      double sl = NormalizeDouble(ask - SL_Points * _Point,_Digits);
      double tp = NormalizeDouble(ask + TP_Points * _Point,_Digits);
      if(trade.Buy(LotSize, _Symbol, ask, sl, tp, "USDJPY Scalper") && trade.ResultRetcode()==TRADE_RETCODE_DONE)
      {
         Print("BUY entry | Ask=", ask, " SL=", sl, " TP=", tp,
               " Spread=", GetSpreadPoints());
      }
      else Print("Entry not fully completed: ",trade.ResultRetcodeDescription());
      return;
   }

   //--- 9. SHORT ENTRY CONDITIONS
   // Condition A: Close at or above upper Bollinger Band
   bool bbShort   = (close1 >= bbUpper[0]);
   // Condition B: Stochastic main below signal and below 80 (bearish momentum)
   bool stochShort = (stochMain[0] < stochSignal[0] && stochMain[0] < 80.0);
   // Condition C: Price below 50 EMA confirming downtrend
   bool trendShort = (close1 < emaLine[0]);

   if(bbShort && stochShort && trendShort)
   {
      double sl = NormalizeDouble(bid + SL_Points * _Point,_Digits);
      double tp = NormalizeDouble(bid - TP_Points * _Point,_Digits);
      if(trade.Sell(LotSize, _Symbol, bid, sl, tp, "USDJPY Scalper") && trade.ResultRetcode()==TRADE_RETCODE_DONE)
      {
         Print("SELL entry | Bid=", bid, " SL=", sl, " TP=", tp,
               " Spread=", GetSpreadPoints());
      }
      else Print("Entry not fully completed: ",trade.ResultRetcodeDescription());
   }
}
//+------------------------------------------------------------------+

Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.

Generate a custom USDJPY 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.

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.