All posts · MQL4 · MQL5 · Expert Advisors
Converting MQL4 to MQL5: 7 Things to Remember
Porting an EA or indicator from MT4 to MT5? The seven MQL4 to MQL5 changes that matter most, from trade requests and positions to indicator handles.
· 4 min read · Updated
If you've been building in MQL4 and are thinking of porting your scripts, EAs or indicators to MQL5, you're not alone. MetaQuotes has been steering everyone toward MetaTrader 5, and there are good reasons to follow.
Why move to MT5 at all
The biggest one is the MT5 Strategy Tester. Compared with MT4, it gives you:
- Optimization spread across all your CPU cores (and optionally the MQL5 Cloud Network), where MT4 optimizes on a single core
- Multi-currency testing, so one EA can trade and be tested on several symbols at once
- An "every tick based on real ticks" mode that replays your broker's actual tick history
- Richer reports, plus custom optimization criteria through
OnTester()
If you want to validate strategies properly rather than throwing darts at EURUSD, that alone makes MQL5 worth learning.
The other reason: MT5 is the platform MetaQuotes is pushing. Its own site only offers MetaTrader 5, and MT4 is now mainly available through brokers that still support it. If you've built useful tools in MQL4, now is a good time to migrate.
Converting isn't plug-and-play, though. MQL5 changes the structures, the events and, above all, the trading logic. Here are the seven differences that matter most, fast. (For the bigger picture first, see MQL4 vs MQL5: the differences that matter.)
1. Trading logic: requests and results
MQL4: one call
OrderSend(Symbol(), OP_BUY, 1.0, Ask, 3, 0, 0, "Buy Order", 0, 0, clrGreen);MQL5: a request structure
MqlTradeRequest request;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 1.0;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation = 10; // MQL4's "slippage", in points
request.magic = 123456;
request.comment = "Buy Order";
if(!OrderSend(request, result))
PrintFormat("OrderSend failed: retcode %u (%s)", result.retcode, result.comment);If this feels like overkill, you're not alone. In return you get precise control and detailed error reporting through result.retcode and result.comment.
Two things to watch. First, the request also has a filling mode (type_filling), and brokers support different ones. A mode the symbol doesn't allow is rejected with retcode 10030, "Unsupported filling mode". Second, the standard library's CTrade class (#include <Trade\Trade.mqh>) wraps all of this, and trade.SetTypeFillingBySymbol(_Symbol) picks a filling mode the symbol supports. For most EAs, CTrade is the simpler route.
2. Order management: MT5 tracks positions, not just orders
MQL4 treats every trade as an order. MT5 splits that into three things:
- Orders: your request ("I want to buy")
- Deals: what actually got executed
- Positions: your current exposure ("I'm long 1 lot of EURUSD")
So the way you read open trades has to change.
MQL4
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS))
{
if(OrderType() == OP_BUY)
{
// Your logic
}
}
}MQL5
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i); // also selects the position
if(ticket == 0) continue;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
// Your logic
}
}Watch out: OrdersTotal() still exists in MQL5, but there it only counts pending orders. An MQL4 loop ported as-is never finds your open trades.
MT5 accounts can also be netting (one position per symbol, where an opposite order reduces or closes it) or hedging (several positions per symbol, like MT4). Check ACCOUNT_MARGIN_MODE if your logic depends on it.
3. Indicator buffers: where most conversions break
If you only read one section, read this one.
- MQL5 indicators don't return values directly.
- You create a handle and copy buffers.
- You have to understand
ArraySetAsSeries(), or you'll read the wrong candle.
MQL4
double value = iRSI(NULL, 0, 14, PRICE_CLOSE, 1);Simple. Done.
MQL5
int rsiHandle = INVALID_HANDLE;
int OnInit()
{
rsiHandle = iRSI(_Symbol, _Period, 14, PRICE_CLOSE); // create once
if(rsiHandle == INVALID_HANDLE)
return INIT_FAILED;
return INIT_SUCCEEDED;
}
void OnTick()
{
double rsi[];
ArraySetAsSeries(rsi, true); // index 0 = newest, like MQL4
if(CopyBuffer(rsiHandle, 0, 0, 3, rsi) < 3)
return; // data not ready yet
double current = rsi[0]; // current (forming) candle
double previous = rsi[1]; // last closed candle
}Without ArraySetAsSeries(rsi, true), the three copied values run oldest-first:
rsi[0]is the oldest of the three (two candles ago)rsi[2]is the current candle
With it, indexing works like MQL4:
rsi[0]= current candlersi[1]= previous candlersi[2]= two candles ago
This one line saves hours of head-scratching. Call it on the array before reading any values from it.
4. Custom indicator buffers
For custom indicators, buffer setup changes slightly too.
MQL4
double buffer[];
SetIndexBuffer(0, buffer);MQL5
double buffer[];
SetIndexBuffer(0, buffer, INDICATOR_DATA);And in MQL5 you style plots with the PlotIndexSet*() functions instead of SetIndexStyle():
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrBlue);More boilerplate, more control. Our guide to coding custom indicators in MQL5 walks through a complete one.
5. Price and time series
The predefined Close[], Time[] and friends don't exist in MQL5.
MQL4
double price = Close[1];
datetime time = Time[1];MQL5
double close[];
datetime time[];
ArraySetAsSeries(close, true);
ArraySetAsSeries(time, true);
CopyClose(_Symbol, _Period, 0, 5, close);
CopyTime(_Symbol, _Period, 0, 5, time);
// close[1] and time[1] are now the last closed bar, as in MQL4For a single value, iClose(_Symbol, _Period, 1) and iTime(_Symbol, _Period, 1) also work in MQL5. If you copy arrays, though, forgetting ArraySetAsSeries() gives you backwards arrays, and your logic fails silently.
6. Timeframes and symbols
MQL4
string symbol = Symbol();
int tf = Period();MQL5
string symbol = _Symbol;
ENUM_TIMEFRAMES tf = _Period;MQL5 types timeframes as ENUM_TIMEFRAMES and adds ones MT4 doesn't have (M2, M3, H2, H6, H12 and more). The values differ too: in MQL4 PERIOD_H1 is 60 (minutes), in MQL5 it's 16385. Never do arithmetic on timeframe constants. Use PeriodSeconds(tf) when you need a bar's length.
7. Testing and optimization
Once the code compiles, use what the MT5 tester gives you:
- Test multi-symbol logic in a single run.
- Write a custom optimization criterion in
OnTester()instead of optimizing for raw profit. - Optimize in parallel across all cores.
Then compare the converted bot's trades with the MQL4 version's in the Strategy Tester. Small differences are normal because the two testers use different price data, but missing or doubled trades usually point at one of the changes above.
Conversion cheat sheet
| MQL4 | MQL5 |
|---|---|
OrderSend(...) |
OrderSend(request, result) or CTrade |
OrdersTotal() + OrderSelect() for open trades |
PositionsTotal() + PositionGetTicket() |
OrderSelect() on history |
HistorySelect() + HistoryDealGetTicket() |
OrderOpenPrice() |
PositionGetDouble(POSITION_PRICE_OPEN) |
Ask, Bid |
SymbolInfoDouble(_Symbol, SYMBOL_ASK) / SYMBOL_BID |
iRSI(..., shift) |
iRSI() handle + CopyBuffer() + ArraySetAsSeries() |
Close[i], Time[i] |
CopyClose() / CopyTime(), or iClose() / iTime() |
Point, Digits |
_Point, _Digits |
Rather not port it by hand? Our MQL4 to MQL5 converter converts an EA or indicator with AI and reports every part it had to change or couldn't convert. Compile and test its output like any port.
Converted bots need the same care as new ones: run them on a demo account before trusting them with real money.
Build a bot from your idea
Describe your trading idea and the AI writes MT4 or MT5 code for you to review and test, with a plain-English explanation and setup steps. Your first bot is free.