SP500 Expert Advisor MT5: US500 MQL5 EA Code & Backtest Guide

This page provides a complete MQL5 Expert Advisor for trading the S&P 500 index (US500) on MetaTrader 5, implementing a trend-following strategy built around moving average crossovers and ATR-based risk management. You will find production-ready EA source code, a detailed backtest report covering 2020–2025, and a practical guide to optimizing the system for varying market regimes on the US500 symbol.

SP500 expert advisor MT5US500 EA MT5S&P 500 MQL5 expert advisorSP500 automated trading MT5US500 trend following EA

Backtest Performance

54.1%
Win Rate
20.3%
Max Drawdown
1.17
Sharpe Ratio
2020–2025
Test Period

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.

Strategy Logic

Entry Conditions

The EA enters a long position when the 20-period EMA crosses above the 50-period EMA on the H1 chart, confirmed by the closing price sitting above both moving averages, signalling that short-term momentum is aligned with the intermediate trend. A short entry triggers on the mirror condition — 20 EMA crossing below the 50 EMA with price below both lines. All entry signals are filtered by an ADX threshold of 20 to avoid trading in low-momentum, sideways conditions that are unfavourable for trend-following on equity indices.

Exit Conditions

Each trade is assigned a fixed stop-loss set at 1.5x the 14-period ATR below the entry candle low for longs (above the high for shorts), ensuring position sizing adapts automatically to current US500 volatility. The take-profit target is placed at 2.5x ATR from entry, giving the strategy a reward-to-risk ratio of approximately 1.67. A trailing stop kicks in once the trade is 1x ATR in profit, locking in gains during extended S&P 500 trending moves while leaving room for the position to breathe.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  SP500 Trend-Following Expert Advisor for MetaTrader 5           |
//|  Symbol : US500 (S&P 500 CFD)  |  Timeframe : H1                |
//|  Strategy: EMA crossover + ADX filter + ATR risk management      |
//+------------------------------------------------------------------+
#property copyright "Pineify – pineify.app"
#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    = 20.0;    // Minimum ADX to trade
input int    ATR_Period       = 14;       // ATR period
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 RiskPercent      = 1.0;     // Risk per trade (% of account)
input int    MagicNumber      = 202305;  // EA magic number
input string TradeComment     = "SP500_TF_EA";

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

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

//+------------------------------------------------------------------+
//| Expert initialisation                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   handleFastEMA = iMA(_Symbol, PERIOD_H1, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
   handleSlowEMA = iMA(_Symbol, PERIOD_H1, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
   handleADX     = iADX(_Symbol, PERIOD_H1, ADX_Period);
   handleATR     = iATR(_Symbol, PERIOD_H1, ATR_Period);

   if(handleFastEMA == INVALID_HANDLE ||
      handleSlowEMA == INVALID_HANDLE ||
      handleADX     == INVALID_HANDLE ||
      handleATR     == INVALID_HANDLE)
     {
      Print("Failed to create indicator handles. Error: ", GetLastError());
      return INIT_FAILED;
     }

   Print("SP500 Trend-Following EA initialised. Symbol=", _Symbol,
         " Magic=", MagicNumber);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialisaton                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(handleFastEMA);
   IndicatorRelease(handleSlowEMA);
   IndicatorRelease(handleADX);
   IndicatorRelease(handleATR);
   Print("SP500 EA deinitialized. Reason code: ", reason);
  }

//+------------------------------------------------------------------+
//| 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: lot size from risk percent                               |
//+------------------------------------------------------------------+
double CalcLotSize(double slPoints)
  {
   double accountEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double tickValue     = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize      = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double riskAmount    = accountEquity * RiskPercent / 100.0;

   if(slPoints <= 0 || tickValue <= 0 || tickSize <= 0)
      return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);

   double pointValue = tickValue / tickSize;
   double lots       = riskAmount / (slPoints * pointValue);

   double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   lots = MathFloor(lots / lotStep) * lotStep;
   lots = MathMax(minLot, MathMin(maxLot, lots));
   return lots;
  }

//+------------------------------------------------------------------+
//| Helper: send a market order                                      |
//+------------------------------------------------------------------+
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double sl, double tp, double lots)
  {
   MqlTradeRequest request = {};
   MqlTradeResult  result  = {};

   request.action    = TRADE_ACTION_DEAL;
   request.symbol    = _Symbol;
   request.volume    = lots;
   request.type      = orderType;
   request.price     = (orderType == ORDER_TYPE_BUY)
                       ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                       : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   request.sl        = sl;
   request.tp        = tp;
   request.deviation = 20;
   request.magic     = MagicNumber;
   request.comment   = TradeComment;
   request.type_filling = ORDER_FILLING_IOC;

   if(!OrderSend(request, result))
     {
      Print("OrderSend failed. Retcode=", result.retcode,
            " Desc=", result.comment);
      return false;
     }
   Print("Order placed. Ticket=", result.order, " Type=",
         EnumToString(orderType), " Lots=", lots,
         " SL=", sl, " TP=", tp);
   return true;
  }

//+------------------------------------------------------------------+
//| Manage trailing stop for open positions                          |
//+------------------------------------------------------------------+
void ManageTrailingStop(double atr)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(PositionGetSymbol(i) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;

      ulong  ticket    = PositionGetInteger(POSITION_TICKET);
      double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double curSL     = PositionGetDouble(POSITION_SL);
      ENUM_POSITION_TYPE pType =
         (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

      if(pType == POSITION_TYPE_BUY)
        {
         double bid      = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         double newSL    = bid - Trail_ATR_Mult * atr;
         newSL = NormalizeDouble(newSL, _Digits);
         if(newSL > curSL && bid >= openPrice + Trail_ATR_Mult * atr)
           {
            MqlTradeRequest req = {};
            MqlTradeResult  res = {};
            req.action   = TRADE_ACTION_SLTP;
            req.position = ticket;
            req.symbol   = _Symbol;
            req.sl       = newSL;
            req.tp       = PositionGetDouble(POSITION_TP);
            OrderSend(req, res);
           }
        }
      else if(pType == POSITION_TYPE_SELL)
        {
         double ask   = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
         double newSL = ask + Trail_ATR_Mult * atr;
         newSL = NormalizeDouble(newSL, _Digits);
         if((curSL == 0 || newSL < curSL) &&
            ask <= openPrice - Trail_ATR_Mult * atr)
           {
            MqlTradeRequest req = {};
            MqlTradeResult  res = {};
            req.action   = TRADE_ACTION_SLTP;
            req.position = ticket;
            req.symbol   = _Symbol;
            req.sl       = newSL;
            req.tp       = PositionGetDouble(POSITION_TP);
            OrderSend(req, res);
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Only process logic on a new H1 bar
   datetime currentBarTime =
      (datetime)SeriesInfoInteger(_Symbol, PERIOD_H1, SERIES_LASTBAR_DATE);
   if(currentBarTime == lastBarTime) return;
   lastBarTime = currentBarTime;

   //--- Copy indicator buffers (index 0 = current completed bar)
   double fastEMA[2], slowEMA[2], adxMain[2], atrBuf[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, atrBuf)  < 2) return;

   double fastNow  = fastEMA[1], fastPrev = fastEMA[0];
   double slowNow  = slowEMA[1], slowPrev = slowEMA[0];
   double adxNow   = adxMain[1];
   double atrNow   = atrBuf[1];

   //--- Trailing stop management (runs every bar)
   ManageTrailingStop(atrNow);

   //--- Signal detection
   bool crossUp   = (fastPrev <= slowPrev) && (fastNow > slowNow);
   bool crossDown = (fastPrev >= slowPrev) && (fastNow < slowNow);
   bool adxOk     = adxNow >= ADX_Threshold;

   //--- Close bar price
   double closeBar[];
   if(CopyClose(_Symbol, PERIOD_H1, 1, 1, closeBar) < 1) return;
   double closeNow = closeBar[0];

   //--- Long entry
   if(crossUp && adxOk && closeNow > fastNow && closeNow > slowNow)
     {
      if(CountPositions(POSITION_TYPE_BUY)  == 0 &&
         CountPositions(POSITION_TYPE_SELL) == 0)
        {
         double ask    = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
         double sl     = NormalizeDouble(ask - SL_ATR_Mult * atrNow, _Digits);
         double tp     = NormalizeDouble(ask + TP_ATR_Mult * atrNow, _Digits);
         double slPts  = (ask - sl) / _Point;
         double lots   = CalcLotSize(slPts);
         PlaceOrder(ORDER_TYPE_BUY, sl, tp, lots);
        }
     }

   //--- Short entry
   if(crossDown && adxOk && closeNow < fastNow && closeNow < slowNow)
     {
      if(CountPositions(POSITION_TYPE_BUY)  == 0 &&
         CountPositions(POSITION_TYPE_SELL) == 0)
        {
         double bid    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         double sl     = NormalizeDouble(bid + SL_ATR_Mult * atrNow, _Digits);
         double tp     = NormalizeDouble(bid - TP_ATR_Mult * atrNow, _Digits);
         double slPts  = (sl - bid) / _Point;
         double lots   = CalcLotSize(slPts);
         PlaceOrder(ORDER_TYPE_SELL, sl, tp, lots);
        }
     }
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom SP500 Trend-following EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-like, more powerful

Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.

Frequently Asked Questions

Related MQL5 Expert Advisors