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
| 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 | Strategy orders are simulated; live automation needs a separate 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.
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.