Indices Trading Robot MT5: US30, NAS100 & SP500 EA Comparison
This page covers automated indices trading robots for MetaTrader 5 that trade US30 (Dow Jones), NAS100 (Nasdaq 100), and SP500 simultaneously using a unified multi-index trend-following strategy. It compares EA configurations, risk parameters, and session filters purpose-built for the unique volatility profiles of US equity index CFDs.
Historical test results
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 opens a long position when the fast EMA (10-period) crosses above the slow EMA (50-period) on the M15 timeframe, confirmed by RSI above 50 and ADX above 20 signaling trend strength. For short entries, the fast EMA must cross below the slow EMA with RSI below 50 and ADX above 20. Each index symbol is evaluated independently, allowing simultaneous positions across US30, NAS100, and SP500 within the same session.
Exit conditions
Trades are closed when the opposing EMA crossover signal appears or when price hits a dynamically calculated ATR-based take-profit level set at 2.5x the 14-period ATR. A hard stop-loss is placed at 1.5x ATR from entry to cap downside on gap opens that are common during US pre-market sessions. Trailing stops activate once floating profit exceeds 1x ATR, locking in gains during sustained trending moves.
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[], hSlowEMA[], hRSI[], hADX[], hATR[];
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
ArrayResize(hFastEMA, SymCount);
ArrayResize(hSlowEMA, SymCount);
ArrayResize(hRSI, SymCount);
ArrayResize(hADX, SymCount);
ArrayResize(hATR, SymCount);
for(int i = 0; i < SymCount; i++)
{
string sym = Symbols[i];
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++)
{
IndicatorRelease(hFastEMA[i]);
IndicatorRelease(hSlowEMA[i]);
IndicatorRelease(hRSI[i]);
IndicatorRelease(hADX[i]);
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
static datetime lastBarTime[];
if(i == 0) ArrayResize(lastBarTime, SymCount);
datetime curBar = iTime(sym, PERIOD_M15, 0);
if(curBar == lastBarTime[i]) continue;
lastBarTime[i] = curBar;
//--- Read indicator buffers (index 1 = last closed bar)
double fastEma[2], slowEma[2], rsiVal[2], adxVal[2], atrVal[2];
if(CopyBuffer(hFastEMA[i], 0, 0, 2, fastEma) < 2) continue;
if(CopyBuffer(hSlowEMA[i], 0, 0, 2, slowEma) < 2) continue;
if(CopyBuffer(hRSI[i], 0, 0, 2, rsiVal) < 2) continue;
if(CopyBuffer(hADX[i], 0, 0, 2, adxVal) < 2) continue;
if(CopyBuffer(hATR[i], 0, 0, 2, atrVal) < 2) continue;
double atr = atrVal[1];
double rsi = rsiVal[1];
double adx = adxVal[1];
bool bullCross = fastEma[1] > slowEma[1] && fastEma[0] <= slowEma[0]; // previous bar crossed
// Use bar index 0 vs 1 for actual crossover detection
bool crossUp = (fastEma[0] > slowEma[0]) && (fastEma[1] <= slowEma[1]);
bool crossDown = (fastEma[0] < slowEma[0]) && (fastEma[1] >= slowEma[1]);
//--- 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)
{
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))
Print("OrderSend failed for ", sym, " error: ", GetLastError());
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.01;
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 lot = riskAmt / (slPoints / tickSize * tickVal);
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double stepLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
lot = MathFloor(lot / stepLot) * stepLot;
lot = MathMax(minLot, MathMin(maxLot, lot));
return lot;
}
//+------------------------------------------------------------------+
//| 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;
OrderSend(req, res);
}
}
}
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;
OrderSend(req, res);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 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))
Print("ClosePositions failed for ", sym, " error: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| 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 ATR inputs, alert rules, visual style, or confirmation filter 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 | Via TradingView broker 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. Read the Pine Script Supertrend reference before porting logic between them.
Frequently Asked Questions
Related MQL5 pages
Risk and testing note
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.