GBPUSD Expert Advisor MT5: M15 EMA/RSI Code and Test Plan
Inspect a GBPUSD M15 EA using EMA crossovers, an RSI level, a live quote/SMA filter and ATR-based stops. The source is an educational example with illustrative parameters. No verified 2021–2025 backtest, optimization study or live win-rate benchmark is supplied.
Strategy logic
Entry conditions
Once per new M15 bar, compare the two completed EMA 9/21 values. A buy also requires completed RSI above 50 and current ask above completed SMA 50; shorts use RSI below 50 and bid below SMA. RSI is a level test, not a required crossing. The spread filter uses broker points, independently of ATR. These combined conditions define a signal; their improvement over an unfiltered strategy is not established.
Exit conditions
Initial requested SL and TP are 1.5 and 2 times completed ATR from the entry quote, not the entry candle low/high. At each new M15 bar, trailing is evaluated before the spread entry filter and only tightens after a move of one current ATR in favor. It uses that ATR as the gap and cannot guarantee partial profit or a maximum loss. There is no news or session filter. Risk sizing is an estimate using balance and tick properties; costs, fills, broker limits and netting-account ownership need separate validation.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| GBPUSD Scalping Expert Advisor for MetaTrader 5 |
//| Strategy: EMA Crossover + RSI Filter + ATR Risk Management |
//| Timeframe: M15 | Symbol: GBPUSD |
//| DISCLAIMER: For educational purposes only. Past backtest |
//| results do not guarantee future performance. Trade at your |
//| own risk. Always test thoroughly before live deployment. |
//+------------------------------------------------------------------+
#property copyright "Pineify MQL5 Example"
#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 TrendSMA_Period = 50; // Trend SMA period
input int RSI_Period = 14; // RSI period
input int ATR_Period = 14; // ATR period
input double RiskPercent = 1.0; // Risk per trade (% of balance)
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 ATR multiplier
input double MaxSpreadPoints = 20.0; // Max allowed spread (points)
input int MagicNumber = 202201; // EA magic number
input string TradeComment = "GBPUSD_Scalp"; // Trade comment
//--- Global handles
int hFastEMA = INVALID_HANDLE;
int hSlowEMA = INVALID_HANDLE;
int hTrendSMA = INVALID_HANDLE;
int hRSI = INVALID_HANDLE;
int hATR = INVALID_HANDLE;
//--- Trade object
#include <Trade/Trade.mqh>
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(FastEMA_Period<1 || SlowEMA_Period<1 || TrendSMA_Period<1 || RSI_Period<1 || ATR_Period<1 ||
RiskPercent<=0 || SL_ATR_Mult<=0 || TP_ATR_Mult<=0 || Trail_ATR_Mult<=0 || MaxSpreadPoints<0 || MagicNumber<0)
return INIT_PARAMETERS_INCORRECT;
//--- Validate symbol
if(Symbol() != "GBPUSD" && Symbol() != "GBPUSDm")
Print("Example labeled GBPUSD; inspect contract for ", Symbol());
//--- Create indicator handles
hFastEMA = iMA(_Symbol, PERIOD_M15, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hSlowEMA = iMA(_Symbol, PERIOD_M15, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hTrendSMA = iMA(_Symbol, PERIOD_M15, TrendSMA_Period, 0, MODE_SMA, PRICE_CLOSE);
hRSI = iRSI(_Symbol, PERIOD_M15, RSI_Period, PRICE_CLOSE);
hATR = iATR(_Symbol, PERIOD_M15, ATR_Period);
if(hFastEMA == INVALID_HANDLE || hSlowEMA == INVALID_HANDLE ||
hTrendSMA == INVALID_HANDLE || hRSI == INVALID_HANDLE || hATR == INVALID_HANDLE)
{
Print("Error: Failed to create indicator handles. Error code: ", GetLastError());
return(INIT_FAILED);
}
//--- Configure trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(10);
if(!trade.SetTypeFillingBySymbol(_Symbol)) return INIT_FAILED;
Print("GBPUSD Scalping EA initialized successfully.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Release indicator handles
if(hFastEMA != INVALID_HANDLE) IndicatorRelease(hFastEMA);
if(hSlowEMA != INVALID_HANDLE) IndicatorRelease(hSlowEMA);
if(hTrendSMA != INVALID_HANDLE) IndicatorRelease(hTrendSMA);
if(hRSI != INVALID_HANDLE) IndicatorRelease(hRSI);
if(hATR != INVALID_HANDLE) IndicatorRelease(hATR);
Print("GBPUSD Scalping EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Only process on new M15 bar
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, PERIOD_M15, 0);
if(currentBarTime<=0 || currentBarTime == lastBarTime) return;
//--- Fetch indicator buffers (index 1 = previous closed bar)
double fastEMA_curr[2], slowEMA_curr[2], trendSMA[2], rsi[2], atr[2];
if(CopyBuffer(hFastEMA, 0, 1, 2, fastEMA_curr) < 2) return;
if(CopyBuffer(hSlowEMA, 0, 1, 2, slowEMA_curr) < 2) return;
if(CopyBuffer(hTrendSMA, 0, 1, 2, trendSMA) < 2) return;
if(CopyBuffer(hRSI, 0, 1, 2, rsi) < 2) return;
if(CopyBuffer(hATR, 0, 1, 2, atr) < 2) return;
double fastPrev = fastEMA_curr[0];
double slowPrev = slowEMA_curr[0];
double fastCurr = fastEMA_curr[1];
double slowCurr = slowEMA_curr[1];
double smaCurr = trendSMA[1];
double rsiCurr = rsi[1];
double atrCurr = atr[1];
if(atrCurr<=0 || !MathIsValidNumber(atrCurr)) return;
lastBarTime=currentBarTime;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
//--- Manage trailing stops on open positions
ManageTrailingStop(atrCurr);
//--- Skip if position already open for this EA
if(PositionExistsForMagic()) return;
double spreadPoints=(double)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
if(spreadPoints<0 || spreadPoints>MaxSpreadPoints) return;
//--- Detect EMA crossover signals
bool bullishCross = (fastPrev <= slowPrev) && (fastCurr > slowCurr);
bool bearishCross = (fastPrev >= slowPrev) && (fastCurr < slowCurr);
//--- Long entry conditions
if(bullishCross && rsiCurr > 50 && ask > smaCurr)
{
double sl = NormalizeDouble(ask - SL_ATR_Mult * atrCurr, _Digits);
double tp = NormalizeDouble(ask + TP_ATR_Mult * atrCurr, _Digits);
double lots = CalculateLotSize(ask - sl);
if(lots > 0)
{
if(!trade.Buy(lots,_Symbol,ask,sl,tp,TradeComment) ||
(trade.ResultRetcode()!=TRADE_RETCODE_DONE && trade.ResultRetcode()!=TRADE_RETCODE_DONE_PARTIAL))
Print("Buy failed: ",trade.ResultRetcode()," ",trade.ResultRetcodeDescription());
}
}
//--- Short entry conditions
if(bearishCross && rsiCurr < 50 && bid < smaCurr)
{
double sl = NormalizeDouble(bid + SL_ATR_Mult * atrCurr, _Digits);
double tp = NormalizeDouble(bid - TP_ATR_Mult * atrCurr, _Digits);
double lots = CalculateLotSize(sl - bid);
if(lots > 0)
{
if(!trade.Sell(lots,_Symbol,bid,sl,tp,TradeComment) ||
(trade.ResultRetcode()!=TRADE_RETCODE_DONE && trade.ResultRetcode()!=TRADE_RETCODE_DONE_PARTIAL))
Print("Sell failed: ",trade.ResultRetcode()," ",trade.ResultRetcodeDescription());
}
}
}
//+------------------------------------------------------------------+
//| Calculate lot size based on risk percentage |
//+------------------------------------------------------------------+
double CalculateLotSize(double slDistance)
{
if(slDistance <= 0) return 0;
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * RiskPercent / 100.0;
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(tickSize<=0 || tickValue<=0 || lotStep<=0 || minLot<=0 || maxLot<minLot || riskAmount<=0) return 0;
double slTicks = slDistance / tickSize;
double lots = riskAmount / (slTicks * tickValue);
lots = NormalizeDouble(MathFloor(MathMin(lots,maxLot) / lotStep) * lotStep,8);
return lots>=minLot ? lots : 0;
}
//+------------------------------------------------------------------+
//| Manage trailing stop for open positions |
//+------------------------------------------------------------------+
void ManageTrailingStop(double atrValue)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!PositionSelectByTicket(PositionGetTicket(i))) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
double trailDist = Trail_ATR_Mult * atrValue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
long posType = PositionGetInteger(POSITION_TYPE);
if(posType == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double newSL = NormalizeDouble(bid - trailDist, _Digits);
double minProfit = NormalizeDouble(openPrice + trailDist, _Digits);
if(bid >= minProfit && newSL > currentSL)
if(!trade.PositionModify(PositionGetTicket(i),newSL,PositionGetDouble(POSITION_TP)) ||
trade.ResultRetcode()!=TRADE_RETCODE_DONE)
Print("Trailing failed: ",trade.ResultRetcode()," ",trade.ResultRetcodeDescription());
}
else if(posType == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double newSL = NormalizeDouble(ask + trailDist, _Digits);
double minProfit = NormalizeDouble(openPrice - trailDist, _Digits);
if(ask <= minProfit && (currentSL == 0 || newSL < currentSL))
if(!trade.PositionModify(PositionGetTicket(i),newSL,PositionGetDouble(POSITION_TP)) ||
trade.ResultRetcode()!=TRADE_RETCODE_DONE)
Print("Trailing failed: ",trade.ResultRetcode()," ",trade.ResultRetcodeDescription());
}
}
}
//+------------------------------------------------------------------+
//| Check if a position exists for this EA's magic number |
//+------------------------------------------------------------------+
bool PositionExistsForMagic()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!PositionSelectByTicket(PositionGetTicket(i))) continue;
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
PositionGetString(POSITION_SYMBOL) == _Symbol)
return true;
}
return false;
}
//+------------------------------------------------------------------+Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom GBPUSD 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.