Complete MQL5 guide & code

Best MT5 Expert Advisor 2026: How to Choose & What to Avoid

Choosing the best MT5 expert advisor requires understanding key evaluation criteria such as verified backtest methodology, risk management architecture, and live forward-test performance. This guide breaks down the most important factors to assess before purchasing or deploying any MT5 EA, and highlights common red flags that signal curve-fitted or unreliable robots.

Implementation details & mechanics

Core implementation pattern

The educational EA requests a trade after a completed-bar 20/50 EMA crossover when ADX exceeds the illustrative threshold and spread is below a fraction of ATR. These filters do not prove a trend will persist. The sample uses fixed lots and one matching position; broker filling modes, volume steps, stop distances and account mode need separate checks.

Error handling & safety rules

The initial stop and target are measured from the requested entry price, not the candle high or low. The example checks an ATR-based trailing stop once per bar after the spread filter, so it may skip a trailing update during a wide spread. Stop orders do not cap losses under all conditions: gaps, slippage and rejected modifications can change execution.

MQL5 Code Implementation

//+------------------------------------------------------------------+
//|  BuyerGuide_DualEMA_EA.mq5                                      |
//|  Example EA for "Best MT5 Expert Advisor" buyer guide page       |
//|  Demonstrates key EA architecture: handles, ATR sizing, RRIM     |
//+------------------------------------------------------------------+
#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    ADX_Period       = 14;       // ADX period
input double ADX_Threshold    = 25.0;    // Minimum ADX to confirm trend
input int    ATR_Period       = 14;       // ATR period for SL/TP sizing
input double SL_ATR_Mult      = 1.5;     // Stop-loss ATR multiplier
input double TP_ATR_Mult      = 2.5;     // Take-profit ATR multiplier
input double Trail_ATR_Mult   = 1.0;     // Trailing stop activation (ATR)
input double LotSize          = 0.1;     // Fixed lot size
input int    MagicNumber      = 20260001; // Unique EA magic number
input string TradeComment     = "BuyerGuide_EA";

//--- Indicator handles
int handleFastEMA;
int handleSlowEMA;
int handleADX;
int handleATR;

//--- Global state
datetime lastBarTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Create indicator handles
   handleFastEMA = iMA(_Symbol, PERIOD_CURRENT, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
   handleSlowEMA = iMA(_Symbol, PERIOD_CURRENT, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
   handleADX     = iADX(_Symbol, PERIOD_CURRENT, ADX_Period);
   handleATR     = iATR(_Symbol, PERIOD_CURRENT, ATR_Period);

   if(handleFastEMA == INVALID_HANDLE || handleSlowEMA == INVALID_HANDLE ||
      handleADX == INVALID_HANDLE     || handleATR == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create indicator handles. EA will not trade.");
      return INIT_FAILED;
     }

   Print("BuyerGuide_DualEMA_EA initialized on ", _Symbol,
         " | FastEMA=", FastEMA_Period, " SlowEMA=", SlowEMA_Period,
         " ADX_Threshold=", ADX_Threshold);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Release indicator handles to free memory
   if(handleFastEMA != INVALID_HANDLE) IndicatorRelease(handleFastEMA);
   if(handleSlowEMA != INVALID_HANDLE) IndicatorRelease(handleSlowEMA);
   if(handleADX     != INVALID_HANDLE) IndicatorRelease(handleADX);
   if(handleATR     != INVALID_HANDLE) IndicatorRelease(handleATR);
   Print("BuyerGuide_DualEMA_EA deinitialized. Reason code: ", reason);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                              |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Only process logic on new bar open (bar-close signals)
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;

   //--- Copy completed bars 2 and 1; static arrays store the older value first
   double fastEMA[2], slowEMA[2], adxMain[2], atrVal[2];

   if(CopyBuffer(handleFastEMA, 0, 1, 2, fastEMA) < 2) return;
   if(CopyBuffer(handleSlowEMA, 0, 1, 2, slowEMA) < 2) return;
   if(CopyBuffer(handleADX,     0, 1, 2, adxMain) < 2) return;
   if(CopyBuffer(handleATR,     0, 1, 2, atrVal)  < 2) return;

   double prevFast = fastEMA[0];
   double prevSlow = slowEMA[0];
   double currFast = fastEMA[1];
   double currSlow = slowEMA[1];
   double adx      = adxMain[1];
   double atr      = atrVal[1];

   //--- Check spread filter (skip if spread is too wide relative to ATR)
   double spreadPoints = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
   if(spreadPoints > atr * 0.15)
     {
      Print("Spread too wide (", spreadPoints, "), skipping bar.");
      return;
     }

   //--- Detect crossovers on last closed bar
   bool bullCross = (prevFast <= prevSlow) && (currFast > currSlow);
   bool bearCross = (prevFast >= prevSlow) && (currFast < currSlow);

   //--- ADX trend filter
   bool trendStrong = (adx >= ADX_Threshold);

   //--- Manage trailing stops on existing positions
   ManageTrailingStops(atr);

   //--- Count open positions for this EA
   int openPositions = CountOpenPositions();

   if(openPositions == 0)
     {
      if(bullCross && trendStrong)
         OpenTrade(ORDER_TYPE_BUY, atr);
      else if(bearCross && trendStrong)
         OpenTrade(ORDER_TYPE_SELL, atr);
     }
  }

//+------------------------------------------------------------------+
//| Open a market order with ATR-based SL and TP                     |
//+------------------------------------------------------------------+
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 - SL_ATR_Mult * atr, _Digits);
      tp    = NormalizeDouble(price + TP_ATR_Mult * atr, _Digits);
     }
   else
     {
      price = bid;
      sl    = NormalizeDouble(price + SL_ATR_Mult * atr, _Digits);
      tp    = NormalizeDouble(price - TP_ATR_Mult * atr, _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.magic     = MagicNumber;
   request.comment   = TradeComment;
   request.deviation = 10;

   if(!OrderSend(request, result) ||
      (result.retcode != TRADE_RETCODE_DONE && result.retcode != TRADE_RETCODE_DONE_PARTIAL))
      Print("OrderSend failed: ", result.retcode, " | ", result.comment);
   else
      Print("Trade opened: ", EnumToString(orderType), " @ ", price,
            " SL=", sl, " TP=", tp, " Ticket=", result.order);
  }

//+------------------------------------------------------------------+
//| Manage trailing stops for open positions                          |
//+------------------------------------------------------------------+
void ManageTrailingStops(double atr)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL)  != _Symbol)  continue;
      if(PositionGetInteger(POSITION_MAGIC)  != MagicNumber) continue;

      double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double currentSL = PositionGetDouble(POSITION_SL);
      double trailDist  = Trail_ATR_Mult * atr;

      ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

      if(posType == POSITION_TYPE_BUY)
        {
         double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         double profitPoints = bid - openPrice;
         if(profitPoints >= trailDist)
           {
            double newSL = NormalizeDouble(bid - trailDist, _Digits);
            if(newSL > currentSL)
               ModifyPositionSL(ticket, newSL);
           }
        }
      else if(posType == POSITION_TYPE_SELL)
        {
         double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
         double profitPoints = openPrice - ask;
         if(profitPoints >= trailDist)
           {
            double newSL = NormalizeDouble(ask + trailDist, _Digits);
            if(newSL < currentSL || currentSL == 0)
               ModifyPositionSL(ticket, newSL);
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Modify position stop-loss                                         |
//+------------------------------------------------------------------+
void ModifyPositionSL(ulong ticket, double newSL)
  {
   MqlTradeRequest request = {};
   MqlTradeResult  result  = {};

   request.action   = TRADE_ACTION_SLTP;
   request.position = ticket;
   request.sl       = newSL;
   request.tp       = PositionGetDouble(POSITION_TP);

   if(!OrderSend(request, result) ||
      (result.retcode != TRADE_RETCODE_DONE && result.retcode != TRADE_RETCODE_DONE_PARTIAL))
      Print("ModifyPositionSL failed: ticket=", ticket, " retcode=", result.retcode);
  }

//+------------------------------------------------------------------+
//| Count open positions managed by this EA                           |
//+------------------------------------------------------------------+
int CountOpenPositions()
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == MagicNumber)
         count++;
     }
   return count;
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor, save it in your MQL5 folder (Experts, Scripts, or Include), and compile with F7.

Generate custom MQL5 code with Pineify AI

Describe the trade execution, indicator calculations, risk management, or data processing logic you need. Pineify generates editable MQL5 source code. Review its logic, request a syntax check, and compile and test the final file 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.