Moving from MT4 to MT5 requires a complete rewrite because MQL5 handles indicators and trade execution very differently.
In MT5, you cannot simply call an indicator function like iMA() inside OnTick(). Instead, you have to create an "indicator handle" when the EA loads, and then pull data into an array (buffer) on every tick. Additionally, MT5 uses a "Position" system rather than an "Order" system for open trades.
To make execution clean and reliable, we use the built-in <Trade\Trade.mqh> standard library.
Here is the fully converted, MT5-ready version of the WTI Scalping strategy:
Code: Select all
//+------------------------------------------------------------------+
//| Oil_Scalper_EA.mq5 |
//| forex-scalping.com |
//+------------------------------------------------------------------+
#property copyright "forex-scalping.com"
#property version "1.00"
#include <Trade\Trade.mqh> // Include standard trade library
CTrade trade; // Initialize the CTrade object
//--- Input Parameters
input double LotSize = 0.5;
input int StopLoss = 200;
input int TakeProfit = 400;
input ulong MagicNumber = 888999; // Note: MagicNumber is ulong in MT5
input int FastEMA = 20;
input int SlowEMA = 50;
input int RSIPeriod = 14;
//--- Session Time Filter
input int TradeStartHour = 14;
input int TradeEndHour = 17;
//--- Trailing Stop Settings
input int TrailingStop = 150;
input int TrailingStep = 50;
//--- Spread Filter
input int MaxSpread = 40;
//--- Indicator Handles
int handleFastEMA;
int handleSlowEMA;
int handleRSI;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set the magic number for the CTrade object
trade.SetExpertMagicNumber(MagicNumber);
// Initialize indicator handles
handleFastEMA = iMA(_Symbol, PERIOD_CURRENT, FastEMA, 0, MODE_EMA, PRICE_CLOSE);
handleSlowEMA = iMA(_Symbol, PERIOD_CURRENT, SlowEMA, 0, MODE_EMA, PRICE_CLOSE);
handleRSI = iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE);
// Check if handles were created successfully
if(handleFastEMA == INVALID_HANDLE || handleSlowEMA == INVALID_HANDLE || handleRSI == INVALID_HANDLE)
{
Print("Error: Failed to initialize indicators.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Free up memory by releasing indicator handles
IndicatorRelease(handleFastEMA);
IndicatorRelease(handleSlowEMA);
IndicatorRelease(handleRSI);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Manage open positions (Trailing Stop)
ManageTrailingStop();
// 2. Check time filter
MqlDateTime timeStruct;
TimeCurrent(timeStruct);
bool isTradingTime = (timeStruct.hour >= TradeStartHour && timeStruct.hour < TradeEndHour);
if(!isTradingTime) return;
// 3. Prevent multiple positions (Count only this EA's positions)
if(PositionsTotalThisEA() > 0) return;
// 4. Check Spread Filter
long currentSpread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
if(currentSpread > MaxSpread) return;
// 5. Retrieve Indicator Values via CopyBuffer
double fastEmaArray[], slowEmaArray[], rsiArray[];
// Make arrays behave like MT4 (index 0 is current unclosed candle)
ArraySetAsSeries(fastEmaArray, true);
ArraySetAsSeries(slowEmaArray, true);
ArraySetAsSeries(rsiArray, true);
// Copy the last 2 candles for EMA, and 1 for RSI
if(CopyBuffer(handleFastEMA, 0, 0, 2, fastEmaArray) <= 0) return;
if(CopyBuffer(handleSlowEMA, 0, 0, 2, slowEmaArray) <= 0) return;
if(CopyBuffer(handleRSI, 0, 0, 1, rsiArray) <= 0) return;
double fastEma0 = fastEmaArray[0];
double fastEma1 = fastEmaArray[1];
double slowEma0 = slowEmaArray[0];
double slowEma1 = slowEmaArray[1];
double currentRsi = rsiArray[0];
// Fetch current Bid/Ask and Point size for MT5
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// Buy Condition
if(fastEma1 <= slowEma1 && fastEma0 > slowEma0 && currentRsi > 50)
{
double sl = ask - StopLoss * point;
double tp = ask + TakeProfit * point;
trade.Buy(LotSize, _Symbol, ask, sl, tp, "Oil Scalp Buy");
}
// Sell Condition
if(fastEma1 >= slowEma1 && fastEma0 < slowEma0 && currentRsi < 50)
{
double sl = bid + StopLoss * point;
double tp = bid - TakeProfit * point;
trade.Sell(LotSize, _Symbol, bid, sl, tp, "Oil Scalp Sell");
}
}
//+------------------------------------------------------------------+
//| Custom Function: Count Positions for this EA |
//+------------------------------------------------------------------+
int PositionsTotalThisEA()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Manage Trailing Stop Function |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i); // Selects position and gets ticket
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
long posType = PositionGetInteger(POSITION_TYPE);
// Buy Position Trailing Logic
if(posType == POSITION_TYPE_BUY)
{
if(bid - openPrice > TrailingStop * point)
{
double newSL = bid - TrailingStop * point;
if(currentSL < bid - (TrailingStop + TrailingStep) * point || currentSL == 0)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
// Sell Position Trailing Logic
else if(posType == POSITION_TYPE_SELL)
{
if(openPrice - ask > TrailingStop * point)
{
double newSL = ask + TrailingStop * point;
if(currentSL > ask + (TrailingStop + TrailingStep) * point || currentSL == 0)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
}
//+------------------------------------------------------------------+
Important MT5 Architecture Changes:
CTrade Class: Replaces the clunky OrderSend() function with trade.Buy(), trade.Sell(), and trade.PositionModify(). It handles the complex trade requests in the background.
Handles & Arrays: Indicators now generate a handle in OnInit(). During OnTick(), we copy the exact number of candles we need into an array (fastEmaArray), using ArraySetAsSeries() to make sure index 0 is always the current live candle—mirroring how MT4 works.
Positions, Not Orders: We built a custom PositionsTotalThisEA() function. MT5 distinguishes between active trades ("Positions") and pending limits/stops ("Orders").