Complete .mq5 source code

Indices Trading Robot MT5: Multi-Symbol MQL5 EA Example

Inspect a three-symbol M15 EA example for broker-specific index CFDs. It applies the same EMA/RSI/ADX rules independently to each configured symbol and estimates per-trade size from ATR stop distance. This is one code example, not an EA ranking or a verified performance comparison. It has no session or portfolio-risk cap.

Strategy logic

Entry conditions

For each configured symbol, compare EMA 10/50 on completed M15 bars 2 and 1; buys also require completed RSI above 50 and ADX above 20, with the inverse for sells. The loop runs when the host chart receives a tick and checks each symbol for a new bar. It can hold three positions concurrently but is not driven by every tick of each remote symbol. Replace the example .cash names with broker symbols and verify data availability.

Exit conditions

Request closure on an opposite completed EMA crossover, and avoid a new entry while a matching position remains. Initial requested SL/TP are 1.5 and 2.5 ATR from entry. Once a move reaches 1 ATR in favor, the trailing gap is 1.5 ATR, so its first adjustment may still be below break-even. Management runs once per symbol bar on a host-chart tick. Stops do not cap gap losses; IOC fills, margin, costs and correlated account-wide exposure need testing.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  IndicesTradingRobot.mq5                                         |
//|  Multi-Index EA for US30, NAS100 & SP500 on MetaTrader 5         |
//|  Strategy: Dual EMA Crossover + RSI + ADX with ATR risk sizing   |
//|  For educational purposes only. Past results ≠ future returns.   |
//+------------------------------------------------------------------+
#property copyright "Pineify Educational Example"
#property version   "1.00"
#property strict

//--- Input parameters
input double   RiskPercent      = 1.0;    // Risk per trade (%)
input int      FastEMA_Period   = 10;     // Fast EMA period
input int      SlowEMA_Period   = 50;     // Slow EMA period
input int      RSI_Period       = 14;     // RSI period
input int      ADX_Period       = 14;     // ADX period
input int      ATR_Period       = 14;     // ATR period
input double   ATR_SL_Mult      = 1.5;   // ATR multiplier for stop loss
input double   ATR_TP_Mult      = 2.5;   // ATR multiplier for take profit
input double   ATR_Trail_Mult   = 1.0;   // ATR multiplier to activate trailing
input int      MagicNumber      = 202601; // Magic number
input string   TradeComment     = "IndicesRobot";

//--- Symbol list
string Symbols[] = {"US30.cash", "NAS100.cash", "SP500.cash"};
int    SymCount  = 3;

//--- Indicator handles per symbol
int hFastEMA[3], hSlowEMA[3], hRSI[3], hADX[3], hATR[3];
datetime lastBarTime[3]={0,0,0};

//+------------------------------------------------------------------+
//| Expert initialization                                             |
//+------------------------------------------------------------------+
int OnInit()
  {
   ArrayInitialize(hFastEMA,INVALID_HANDLE); ArrayInitialize(hSlowEMA,INVALID_HANDLE);
   ArrayInitialize(hRSI,INVALID_HANDLE); ArrayInitialize(hADX,INVALID_HANDLE); ArrayInitialize(hATR,INVALID_HANDLE);
   if(RiskPercent<=0 || FastEMA_Period<1 || SlowEMA_Period<1 || RSI_Period<1 || ADX_Period<1 || ATR_Period<1 ||
      ATR_SL_Mult<=0 || ATR_TP_Mult<=0 || ATR_Trail_Mult<=0 || MagicNumber<0) return INIT_PARAMETERS_INCORRECT;

   for(int i = 0; i < SymCount; i++)
     {
      string sym = Symbols[i];
      if(!SymbolSelect(sym,true)) { Print("Symbol unavailable: ",sym); return INIT_FAILED; }
      hFastEMA[i] = iMA(sym, PERIOD_M15, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
      hSlowEMA[i] = iMA(sym, PERIOD_M15, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
      hRSI[i]     = iRSI(sym, PERIOD_M15, RSI_Period, PRICE_CLOSE);
      hADX[i]     = iADX(sym, PERIOD_M15, ADX_Period);
      hATR[i]     = iATR(sym, PERIOD_M15, ATR_Period);

      if(hFastEMA[i] == INVALID_HANDLE || hSlowEMA[i] == INVALID_HANDLE ||
         hRSI[i] == INVALID_HANDLE     || hADX[i] == INVALID_HANDLE     ||
         hATR[i] == INVALID_HANDLE)
        {
         Print("Failed to create indicator handle for ", sym);
         return INIT_FAILED;
        }
     }
   Print("IndicesTradingRobot initialized for ", SymCount, " symbols.");
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   for(int i = 0; i < SymCount; i++)
     {
      if(hFastEMA[i]!=INVALID_HANDLE) IndicatorRelease(hFastEMA[i]);
      if(hSlowEMA[i]!=INVALID_HANDLE) IndicatorRelease(hSlowEMA[i]);
      if(hRSI[i]!=INVALID_HANDLE) IndicatorRelease(hRSI[i]);
      if(hADX[i]!=INVALID_HANDLE) IndicatorRelease(hADX[i]);
      if(hATR[i]!=INVALID_HANDLE) IndicatorRelease(hATR[i]);
     }
   Print("IndicesTradingRobot deinitialized. Reason: ", reason);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                              |
//+------------------------------------------------------------------+
void OnTick()
  {
   for(int i = 0; i < SymCount; i++)
     {
      string sym = Symbols[i];

      //--- Only run on new M15 bar to avoid duplicate signals
      datetime curBar = iTime(sym, PERIOD_M15, 0);
      if(curBar<=0 || curBar == lastBarTime[i]) continue;

      //--- Read indicator buffers (index 1 = last closed bar)
      double fastEma[2], slowEma[2], rsiVal[2], adxVal[2], atrVal[2];
      if(CopyBuffer(hFastEMA[i], 0, 1, 2, fastEma) < 2) continue;
      if(CopyBuffer(hSlowEMA[i], 0, 1, 2, slowEma) < 2) continue;
      if(CopyBuffer(hRSI[i],     0, 1, 2, rsiVal)  < 2) continue;
      if(CopyBuffer(hADX[i],     0, 1, 2, adxVal)  < 2) continue;
      if(CopyBuffer(hATR[i],     0, 1, 2, atrVal)  < 2) continue;

      double atr   = atrVal[1];
      double rsi   = rsiVal[1];
      double adx   = adxVal[1];
      if(atr<=0 || !MathIsValidNumber(atr)) continue;
      lastBarTime[i]=curBar;
      // Static arrays: bar 2 at index 0, completed bar 1 at index 1.
      bool crossUp=(fastEma[1]>slowEma[1]) && (fastEma[0]<=slowEma[0]);
      bool crossDown=(fastEma[1]<slowEma[1]) && (fastEma[0]>=slowEma[0]);

      //--- Manage trailing stop on open positions
      ManageTrailing(sym, atr);

      //--- Close opposite position if crossover occurs
      if(crossUp)   ClosePositions(sym, POSITION_TYPE_SELL);
      if(crossDown) ClosePositions(sym, POSITION_TYPE_BUY);

      //--- Check if already in a position for this symbol
      if(HasOpenPosition(sym)) continue;

      //--- Entry conditions
      double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
      double bid = SymbolInfoDouble(sym, SYMBOL_BID);

      if(crossUp && rsi > 50.0 && adx > 20.0)
        {
         double sl = ask - ATR_SL_Mult * atr;
         double tp = ask + ATR_TP_Mult * atr;
         double lot = CalcLotSize(sym, ask - sl);
         OpenOrder(sym, ORDER_TYPE_BUY, lot, ask, sl, tp);
        }
      else if(crossDown && rsi < 50.0 && adx > 20.0)
        {
         double sl = bid + ATR_SL_Mult * atr;
         double tp = bid - ATR_TP_Mult * atr;
         double lot = CalcLotSize(sym, sl - bid);
         OpenOrder(sym, ORDER_TYPE_SELL, lot, bid, sl, tp);
        }
     }
  }

//+------------------------------------------------------------------+
//| Open a market order                                               |
//+------------------------------------------------------------------+
void OpenOrder(string sym, ENUM_ORDER_TYPE type, double lot, double price, double sl, double tp)
  {
   if(lot<=0 || price<=0) return;
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};

   req.action    = TRADE_ACTION_DEAL;
   req.symbol    = sym;
   req.volume    = lot;
   req.type      = type;
   req.price     = price;
   req.sl        = NormalizeDouble(sl, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS));
   req.tp        = NormalizeDouble(tp, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS));
   req.magic     = MagicNumber;
   req.comment   = TradeComment;
   req.type_filling = ORDER_FILLING_IOC;

   if(!OrderSend(req,res) || (res.retcode!=TRADE_RETCODE_DONE && res.retcode!=TRADE_RETCODE_DONE_PARTIAL))
      Print("OrderSend failed for ", sym, " retcode: ", res.retcode, " ",res.comment);
   else
      Print("Order opened: ", sym, " type=", EnumToString(type), " lot=", lot, " sl=", req.sl, " tp=", req.tp);
  }

//+------------------------------------------------------------------+
//| Calculate lot size based on account risk                          |
//+------------------------------------------------------------------+
double CalcLotSize(string sym, double slPoints)
  {
   if(slPoints <= 0) return 0;
   double tickVal  = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_VALUE);
   double tickSize = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_SIZE);
   double riskAmt  = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0;
   double minLot   = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
   double maxLot   = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
   double stepLot  = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
   if(tickSize<=0 || tickVal<=0 || stepLot<=0 || minLot<=0 || maxLot<minLot || riskAmt<=0) return 0;
   double lot=riskAmt/(slPoints/tickSize*tickVal);
   lot=NormalizeDouble(MathFloor(MathMin(lot,maxLot)/stepLot)*stepLot,8);
   return lot>=minLot ? lot : 0;
  }

//+------------------------------------------------------------------+
//| Manage trailing stop for open positions                           |
//+------------------------------------------------------------------+
void ManageTrailing(string sym, double atr)
  {
   for(int p = PositionsTotal() - 1; p >= 0; p--)
     {
      ulong ticket = PositionGetTicket(p);
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) != sym) continue;
      if(PositionGetInteger(POSITION_MAGIC) != 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 bid = SymbolInfoDouble(sym, SYMBOL_BID);
      double ask = SymbolInfoDouble(sym, SYMBOL_ASK);

      if(pType == POSITION_TYPE_BUY)
        {
         double profit = bid - openPrice;
         if(profit >= ATR_Trail_Mult * atr)
           {
            double newSL = bid - ATR_SL_Mult * atr;
            if(newSL > currentSL)
              {
               MqlTradeRequest req = {};
               MqlTradeResult  res = {};
               req.action   = TRADE_ACTION_SLTP;
               req.symbol   = sym;
               req.position = ticket;
               req.sl       = NormalizeDouble(newSL, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS));
               req.tp       = currentTP;
               if(!OrderSend(req,res) || res.retcode!=TRADE_RETCODE_DONE)
                  Print("Trailing failed for ",sym," ",res.retcode," ",res.comment);
              }
           }
        }
      else if(pType == POSITION_TYPE_SELL)
        {
         double profit = openPrice - ask;
         if(profit >= ATR_Trail_Mult * atr)
           {
            double newSL = ask + ATR_SL_Mult * atr;
            if(newSL < currentSL || currentSL == 0)
              {
               MqlTradeRequest req = {};
               MqlTradeResult  res = {};
               req.action   = TRADE_ACTION_SLTP;
               req.symbol   = sym;
               req.position = ticket;
               req.sl       = NormalizeDouble(newSL, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS));
               req.tp       = currentTP;
               if(!OrderSend(req,res) || res.retcode!=TRADE_RETCODE_DONE)
                  Print("Trailing failed for ",sym," ",res.retcode," ",res.comment);
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Close all positions of a given type for a symbol                  |
//+------------------------------------------------------------------+
void ClosePositions(string sym, ENUM_POSITION_TYPE closeType)
  {
   for(int p = PositionsTotal() - 1; p >= 0; p--)
     {
      ulong ticket = PositionGetTicket(p);
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) != sym) continue;
      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
      if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) != closeType) continue;

      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action   = TRADE_ACTION_DEAL;
      req.symbol   = sym;
      req.position = ticket;
      req.volume   = PositionGetDouble(POSITION_VOLUME);
      req.type     = (closeType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
      req.price    = (closeType == POSITION_TYPE_BUY)
                     ? SymbolInfoDouble(sym, SYMBOL_BID)
                     : SymbolInfoDouble(sym, SYMBOL_ASK);
      req.type_filling = ORDER_FILLING_IOC;
      req.magic    = MagicNumber;
      req.comment  = TradeComment + "_close";

      if(!OrderSend(req,res) || (res.retcode!=TRADE_RETCODE_DONE && res.retcode!=TRADE_RETCODE_DONE_PARTIAL))
         Print("ClosePositions failed for ", sym, " retcode: ", res.retcode, " ",res.comment);
     }
  }

//+------------------------------------------------------------------+
//| Check if symbol has an open position managed by this EA           |
//+------------------------------------------------------------------+
bool HasOpenPosition(string sym)
  {
   for(int p = 0; p < PositionsTotal(); p++)
     {
      ulong ticket = PositionGetTicket(p);
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) == sym &&
         PositionGetInteger(POSITION_MAGIC)  == MagicNumber)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

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

Generate a custom US30/NAS100 multi-index 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

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.