Gold Trend Following EA MT5: MQL5 XAUUSD Trend Strategy & Code
Study an H1 dual-EMA Expert Advisor with completed-bar signals, an ATR entry threshold and requested initial/trailing stops. The full MQL5 example is intended for code inspection and independent testing. It provides no verified gold performance record or evidence that these defaults are optimal.
Strategy logic
Entry conditions
Evaluate the completed H1 EMA 20/50 crossover once per new H1 bar. A buy or sell request also requires completed ATR 14 at least ATR_Min_Filter, measured in the symbol price units. The default 0.5 is a threshold choice, not an account-currency risk budget or proof that whipsaws are avoided. The source checks matching symbol-and-magic positions before entry and uses fixed lots.
Exit conditions
At each new H1 bar, request closure of positions opposite the completed crossover, then evaluate trailing stops before the ATR entry filter. Do not open the new direction while an opposite position remains after a failed or partial close. Initial requested SL and TP are 1.5 and 2.5 times ATR from the entry quote. Trailing uses the latest completed ATR for activation and distance and only tightens; it is not managed every tick or guaranteed to preserve profit. IOC can partially fill; reconcile results and test broker and account-mode constraints.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| GoldTrendFollowingEA.mq5 |
//| XAUUSD Dual EMA Trend Following Expert Advisor |
//| For MetaTrader 5 — MQL5 5.x compatible |
//| DISCLAIMER: For educational purposes only. Past performance |
//| does not guarantee future results. Trade at your own risk. |
//+------------------------------------------------------------------+
#property copyright "Pineify Example"
#property version "1.00"
#property strict
//--- Input parameters
input int FastEMA_Period = 20; // Fast EMA period
input int SlowEMA_Period = 50; // Slow EMA period
input int ATR_Period = 14; // ATR period
input double ATR_SL_Mult = 1.5; // Stop-loss ATR multiplier
input double ATR_TP_Mult = 2.5; // Take-profit ATR multiplier
input double ATR_Trail_Mult = 1.0; // Trailing stop ATR multiplier
input double ATR_Min_Filter = 0.5; // Min ATR in symbol price units for entry
input double LotSize = 0.10; // Fixed lot size
input ulong MagicNumber = 20240001; // EA magic number
//--- Indicator handles
int handleFastEMA=INVALID_HANDLE;
int handleSlowEMA=INVALID_HANDLE;
int handleATR=INVALID_HANDLE;
//--- Buffers
double fastEMABuf[];
double slowEMABuf[];
double atrBuf[];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(FastEMA_Period<1 || SlowEMA_Period<1 || ATR_Period<1 || ATR_SL_Mult<=0 || ATR_TP_Mult<=0 ||
ATR_Trail_Mult<=0 || ATR_Min_Filter<0 || LotSize<=0) return INIT_PARAMETERS_INCORRECT;
// Create indicator handles
handleFastEMA = iMA(_Symbol, PERIOD_H1, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
handleSlowEMA = iMA(_Symbol, PERIOD_H1, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
handleATR = iATR(_Symbol, PERIOD_H1, ATR_Period);
if(handleFastEMA == INVALID_HANDLE || handleSlowEMA == INVALID_HANDLE || handleATR == INVALID_HANDLE)
{
Print("ERROR: Failed to create indicator handles.");
return INIT_FAILED;
}
// Set buffer as series (newest index = 0)
ArraySetAsSeries(fastEMABuf, true);
ArraySetAsSeries(slowEMABuf, true);
ArraySetAsSeries(atrBuf, true);
Print("GoldTrendFollowingEA initialized on ", _Symbol);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(handleFastEMA!=INVALID_HANDLE) IndicatorRelease(handleFastEMA);
if(handleSlowEMA!=INVALID_HANDLE) IndicatorRelease(handleSlowEMA);
if(handleATR!=INVALID_HANDLE) IndicatorRelease(handleATR);
Print("GoldTrendFollowingEA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Only act on a new H1 bar
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, PERIOD_H1, 0);
if(currentBarTime<=0 || currentBarTime == lastBarTime) return;
// Copy indicator data (3 values: index 0 = current, 1 = prev)
if(CopyBuffer(handleFastEMA, 0, 0, 3, fastEMABuf) < 3) return;
if(CopyBuffer(handleSlowEMA, 0, 0, 3, slowEMABuf) < 3) return;
if(CopyBuffer(handleATR, 0, 0, 2, atrBuf) < 2) return;
double fastNow = fastEMABuf[1]; // Previous closed bar
double fastPrev = fastEMABuf[2];
double slowNow = slowEMABuf[1];
double slowPrev = slowEMABuf[2];
double atr = atrBuf[1];
if(atr<=0 || !MathIsValidNumber(atr)) return;
lastBarTime=currentBarTime;
bool bullCross = (fastPrev <= slowPrev) && (fastNow > slowNow);
bool bearCross = (fastPrev >= slowPrev) && (fastNow < slowNow);
int openLongs = CountPositions(POSITION_TYPE_BUY);
int openShorts = CountPositions(POSITION_TYPE_SELL);
// Close counter-direction positions on crossover
if(bullCross && openShorts > 0) CloseAllPositions(POSITION_TYPE_SELL);
if(bearCross && openLongs > 0) CloseAllPositions(POSITION_TYPE_BUY);
// Management is independent of the minimum-ATR entry filter.
ManageTrailingStop(atr);
if(atr<ATR_Min_Filter) return;
// Open new positions only after opposing positions are confirmed absent
if(bullCross && CountPositions(POSITION_TYPE_BUY)==0 && CountPositions(POSITION_TYPE_SELL)==0)
OpenTrade(ORDER_TYPE_BUY, atr);
if(bearCross && CountPositions(POSITION_TYPE_SELL)==0 && CountPositions(POSITION_TYPE_BUY)==0)
OpenTrade(ORDER_TYPE_SELL, atr);
}
//+------------------------------------------------------------------+
//| Open a market order |
//+------------------------------------------------------------------+
void OpenTrade(ENUM_ORDER_TYPE orderType, double atr)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double price, sl, tp;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(orderType == ORDER_TYPE_BUY)
{
price = ask;
sl = NormalizeDouble(price - atr * ATR_SL_Mult, _Digits);
tp = NormalizeDouble(price + atr * ATR_TP_Mult, _Digits);
}
else
{
price = bid;
sl = NormalizeDouble(price + atr * ATR_SL_Mult, _Digits);
tp = NormalizeDouble(price - atr * ATR_TP_Mult, _Digits);
}
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = orderType;
request.price = price;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.magic = MagicNumber;
request.comment = "GoldTrendEA";
request.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(request,result) || (result.retcode!=TRADE_RETCODE_DONE && result.retcode!=TRADE_RETCODE_DONE_PARTIAL))
Print("OrderSend failed: ", result.retcode, " ", result.comment);
else
Print("Order opened: ", EnumToString(orderType), " price=", price, " sl=", sl, " tp=", tp);
}
//+------------------------------------------------------------------+
//| Count open positions by type |
//+------------------------------------------------------------------+
int CountPositions(ENUM_POSITION_TYPE posType)
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == (long)MagicNumber &&
PositionGetInteger(POSITION_TYPE) == posType)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Close all positions of a given type |
//+------------------------------------------------------------------+
void CloseAllPositions(ENUM_POSITION_TYPE posType)
{
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) != (long)MagicNumber) continue;
if(PositionGetInteger(POSITION_TYPE) != posType) continue;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.position = ticket;
req.volume = PositionGetDouble(POSITION_VOLUME);
req.type = (posType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
req.price = (posType == POSITION_TYPE_BUY)
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
req.deviation = 10;
req.magic = MagicNumber;
req.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(req,res) || (res.retcode!=TRADE_RETCODE_DONE && res.retcode!=TRADE_RETCODE_DONE_PARTIAL))
Print("Close failed ticket=", ticket, " retcode=", res.retcode);
}
}
//+------------------------------------------------------------------+
//| Manage ATR-based trailing stop |
//+------------------------------------------------------------------+
void ManageTrailingStop(double atr)
{
double trailDist = atr * ATR_Trail_Mult;
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) != (long)MagicNumber) continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE pType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double newSL = currentSL;
if(pType == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double trail = NormalizeDouble(bid - trailDist, _Digits);
// Only trail if in profit by at least 1x ATR
if(bid >= openPrice + trailDist && trail > currentSL)
newSL = trail;
}
else if(pType == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double trail = NormalizeDouble(ask + trailDist, _Digits);
if(ask <= openPrice - trailDist && (trail < currentSL || currentSL == 0))
newSL = trail;
}
if(newSL != currentSL)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.symbol = _Symbol;
req.position = ticket;
req.sl = newSL;
req.tp = currentTP;
if(!OrderSend(req,res) || res.retcode!=TRADE_RETCODE_DONE)
PrintFormat("Trailing modification failed: %u %s",res.retcode,res.comment);
}
}
}
//+------------------------------------------------------------------+Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom XAUUSD trend-following 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.