Complete .mq5 source code

MQL5 Trend Following EA: H1 EMA and ADX Code

Inspect a single-symbol H1 trend-following EA using two completed-bar EMA values and the ADX main line. The example uses fixed lots, initial ATR-based SL and TP, and reverse-cross exits. It provides source for testing rather than an independently verified performance record.

Strategy logic

Entry conditions

A fast EMA crossover above the slow EMA with ADX at or above the chosen threshold permits a buy; the reverse crossover permits a sell. ADX is a numerical filter, not proof that a trend will persist. Inputs use completed H1 bars. An entry requires no remaining matching position in either direction, including after a failed reverse close.

Exit conditions

The requested initial SL is 1.5 ATR and TP is 2 ATR from the quote by default. These levels are fixed after entry; the code has no trailing-stop function. A completed opposite EMA cross requests closure regardless of ADX, and unresolved closure is retried on later chart ticks. Execution and costs can change realized outcomes; SL is not a guaranteed cash-loss cap.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  TrendFollowingEA.mq5                                            |
//|  Pineify – MQL5 Trend Following Expert Advisor                   |
//|  Strategy : EMA crossover + ADX trend filter                     |
//|  DISCLAIMER: For educational purposes only. Past performance     |
//|  does not guarantee future results. Trade at your own risk.      |
//+------------------------------------------------------------------+
#property copyright "Pineify"
#property version   "1.00"
#property strict

//--- Input parameters
input int    FastEmaPeriod  = 20;      // Fast EMA period
input int    SlowEmaPeriod  = 50;      // Slow EMA period
input int    AdxPeriod      = 14;      // ADX period
input double AdxThreshold   = 25.0;   // Minimum ADX for trend confirmation
input int    AtrPeriod      = 14;      // ATR period for SL/TP sizing
input double AtrSlMult      = 1.5;    // ATR multiplier for stop loss
input double AtrTpMult      = 2.0;    // ATR multiplier for initial take profit
input double LotSize        = 0.1;    // Fixed lot size
input int    MagicNumber    = 202401; // EA magic number
input int    Slippage       = 10;     // Max slippage in points

//--- Global indicator handles
int g_hFastEma  = INVALID_HANDLE;
int g_hSlowEma  = INVALID_HANDLE;
int g_hAdx      = INVALID_HANDLE;
int g_hAtr      = INVALID_HANDLE;
bool closeBuy=false, closeSell=false;

//+------------------------------------------------------------------+
//| Expert initialisation                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(FastEmaPeriod<=0 || SlowEmaPeriod<=0 || AdxPeriod<=0 || AtrPeriod<=0
      || AdxThreshold<0 || AdxThreshold>100 || AtrSlMult<=0 || AtrTpMult<=0
      || LotSize<=0 || MagicNumber<=0 || Slippage<0) return INIT_PARAMETERS_INCORRECT;
   g_hFastEma = iMA(_Symbol, PERIOD_H1, FastEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
   g_hSlowEma = iMA(_Symbol, PERIOD_H1, SlowEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
   g_hAdx     = iADX(_Symbol, PERIOD_H1, AdxPeriod);
   g_hAtr     = iATR(_Symbol, PERIOD_H1, AtrPeriod);

   if(g_hFastEma == INVALID_HANDLE || g_hSlowEma == INVALID_HANDLE ||
      g_hAdx     == INVALID_HANDLE || g_hAtr     == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create indicator handles.");
      return INIT_FAILED;
     }

   Print("TrendFollowingEA initialised on ", _Symbol);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(g_hFastEma != INVALID_HANDLE) IndicatorRelease(g_hFastEma);
   if(g_hSlowEma != INVALID_HANDLE) IndicatorRelease(g_hSlowEma);
   if(g_hAdx     != INVALID_HANDLE) IndicatorRelease(g_hAdx);
   if(g_hAtr     != INVALID_HANDLE) IndicatorRelease(g_hAtr);
   Print("TrendFollowingEA deinitialised. Reason: ", reason);
  }

//+------------------------------------------------------------------+
//| Helper: copy a single buffer value (bar index 1 = last closed)  |
//+------------------------------------------------------------------+
double GetBuffer(int handle, int bufferIndex, int shift = 1)
  {
   double buf[];
   ArraySetAsSeries(buf, true);
   if(CopyBuffer(handle, bufferIndex, shift, 1, buf) < 1)
      return EMPTY_VALUE;
   return buf[0];
  }

//+------------------------------------------------------------------+
//| Helper: count open positions for this EA                        |
//+------------------------------------------------------------------+
int CountPositions(ENUM_POSITION_TYPE posType)
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(PositionGetSymbol(i) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
         PositionGetInteger(POSITION_TYPE)  == posType)
         count++;
     }
   return count;
  }

//+------------------------------------------------------------------+
//| Helper: close all positions of a given type                     |
//+------------------------------------------------------------------+
void ClosePositions(ENUM_POSITION_TYPE posType)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetSymbol(i)                    != _Symbol)        continue;
      if(PositionGetInteger(POSITION_MAGIC)       != MagicNumber)   continue;
      if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) != posType) continue;

      MqlTradeRequest  req  = {};
      MqlTradeResult   res  = {};
      req.action    = TRADE_ACTION_DEAL;
      req.symbol    = _Symbol;
      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 = Slippage;
      req.magic     = MagicNumber;
      req.position  = ticket;
      req.comment   = "TrendEA close";

      if(!OrderSend(req, res) || res.retcode!=TRADE_RETCODE_DONE)
         Print("ClosePositions ERROR: ", res.retcode, " ticket=", ticket);
     }
  }

//+------------------------------------------------------------------+
//| Helper: open a new market order                                 |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE orderType, double sl, double tp)
  {
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   req.action    = TRADE_ACTION_DEAL;
   req.symbol    = _Symbol;
   req.volume    = LotSize;
   req.type      = orderType;
   req.price     = (orderType == ORDER_TYPE_BUY)
                     ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                     : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   req.sl        = NormalizeDouble(sl,_Digits);
   req.tp        = NormalizeDouble(tp,_Digits);
   req.deviation = Slippage;
   req.magic     = MagicNumber;
   req.comment   = "TrendEA open";

   if(!OrderSend(req, res) || res.retcode!=TRADE_RETCODE_DONE)
      Print("OpenPosition ERROR: ", res.retcode);
   else
      Print("Position opened: type=", EnumToString(orderType),
            " price=", req.price, " SL=", sl, " TP=", tp);
  }

//+------------------------------------------------------------------+
//| Expert tick                                                      |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(closeBuy) { ClosePositions(POSITION_TYPE_BUY); closeBuy=CountPositions(POSITION_TYPE_BUY)>0; }
   if(closeSell) { ClosePositions(POSITION_TYPE_SELL); closeSell=CountPositions(POSITION_TYPE_SELL)>0; }
   if(closeBuy || closeSell) return;
   //--- Only act on a new bar close (H1)
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, PERIOD_H1, 0);
   if(currentBar == lastBar) return;
   if(currentBar<=0) return;

   //--- Read indicator values from the last closed bar (shift = 1)
   double fastEma  = GetBuffer(g_hFastEma, 0, 1);
   double slowEma  = GetBuffer(g_hSlowEma, 0, 1);
   double fastEmaP = GetBuffer(g_hFastEma, 0, 2); // previous bar
   double slowEmaP = GetBuffer(g_hSlowEma, 0, 2);
   double adxVal   = GetBuffer(g_hAdx,     0, 1); // ADX main line
   double atrVal   = GetBuffer(g_hAtr,     0, 1);

   if(fastEma == EMPTY_VALUE || fastEmaP == EMPTY_VALUE || slowEmaP == EMPTY_VALUE || slowEma == EMPTY_VALUE ||
      adxVal  == EMPTY_VALUE || atrVal  == EMPTY_VALUE || atrVal<=0)
      return;

   lastBar=currentBar;
   //--- Trend signals
   bool bullCross = (fastEmaP <= slowEmaP) && (fastEma > slowEma);
   bool bearCross = (fastEmaP >= slowEmaP) && (fastEma < slowEma);
   bool trendStrong = adxVal >= AdxThreshold;

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

   //--- Exit logic: reverse crossover closes existing positions
   if(bearCross && CountPositions(POSITION_TYPE_BUY)>0) { ClosePositions(POSITION_TYPE_BUY); closeBuy=CountPositions(POSITION_TYPE_BUY)>0; }
   if(bullCross && CountPositions(POSITION_TYPE_SELL)>0) { ClosePositions(POSITION_TYPE_SELL); closeSell=CountPositions(POSITION_TYPE_SELL)>0; }
   if(CountPositions(POSITION_TYPE_BUY)>0 || CountPositions(POSITION_TYPE_SELL)>0) return;

   //--- Entry logic
   if(bullCross && trendStrong && CountPositions(POSITION_TYPE_BUY) == 0)
     {
      double sl = ask - AtrSlMult * atrVal;
      double tp = ask + AtrTpMult * atrVal;
      OpenPosition(ORDER_TYPE_BUY, sl, tp);
     }

   if(bearCross && trendStrong && CountPositions(POSITION_TYPE_SELL) == 0)
     {
      double sl = bid + AtrSlMult * atrVal;
      double tp = bid - AtrTpMult * atrVal;
      OpenPosition(ORDER_TYPE_SELL, sl, tp);
     }
  }
//+------------------------------------------------------------------+

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

Generate a custom Multi-pair 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

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

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.