MQL4 vs MQL5: what actually changes when you switch platform
The most expensive assumption in MetaTrader development is that MQL5 is MQL4 with a bigger version number. It is not. They are different languages with different trading models, and the differences that hurt most are the ones that compile without complaint and then trade on wrong numbers.
Here is what actually changes.
1. The trading model: orders vs positions
MQL4 thinks in orders. You send one with OrderSend, it comes back as a
ticket, and you manipulate it by selecting it:
int ticket = OrderSend(_Symbol, OP_BUY, 0.1, Ask, 3, sl, tp);
if (OrderSelect(ticket, SELECT_BY_TICKET)) {
OrderModify(ticket, OrderOpenPrice(), newSl, newTp, 0);
}
MQL5 thinks in positions, and the netting/hedging distinction is real.
The idiomatic route is the CTrade class:
#include <Trade/Trade.mqh>
CTrade trade;
trade.Buy(0.1, _Symbol, 0, sl, tp);
if (PositionSelect(_Symbol)) {
trade.PositionModify(_Symbol, newSl, newTp);
}
OP_BUY, SELECT_BY_TICKET, OrderClose, OrderModify and MODE_TRADES
do not exist in MQL5. This is the single most common source of "why won't
this compile" when porting by hand.
One thing that trips people the other way: OrderSend is not MQL4-only.
MQL5 has its own OrderSend(MqlTradeRequest&, MqlTradeResult&) — it is
exactly what CTrade wraps. Seeing the name in MQL5 source is not evidence
of a porting mistake.
2. Indicators: a value vs a handle
In MQL4 an indicator call returns the number you wanted:
double rsi = iRSI(_Symbol, 0, 14, PRICE_CLOSE, 0);
In MQL5 the same call returns a handle — a reference to a running indicator — and you copy values out of its buffer:
int handle = iRSI(_Symbol, _Period, 14, PRICE_CLOSE);
double buf[];
CopyBuffer(handle, 0, 0, 1, buf);
double rsi = buf[0];
Two practical consequences. First, MQL5 handles should be created once in
OnInit, not on every tick — creating them per tick is a real performance
problem, not a style preference. Second, the bar index moved: in MQL4 it is
the last argument to the indicator call, in MQL5 it is an argument to
CopyBuffer.
3. Buffer numbering is not the same — and this one compiles
MQL5 numbers every indicator buffer from 0. MQL4 mostly does too, but
for five indicators it uses 1-based MODE_* constants instead:
| Indicator | MT4 line numbering | MT5 line numbering |
|---|---|---|
| Alligator | from 1 | from 0 |
| Gator Oscillator | from 1 | from 0 |
| Fractals | from 1 | from 0 |
| Envelopes | from 1 | from 0 |
| Ichimoku Kinko Hyo | from 1 | from 0 |
Read the wrong buffer and you do not get an error. You get the Alligator's teeth where you asked for its jaw, on an EA that runs perfectly happily. This is the failure mode to fear, and it is why TradeSmithy encodes the numbering in its indicator registry rather than leaving it to be remembered.
4. Argument order differs, silently
iBands is the clearest example. Both platforms take a period, a deviation,
a band shift and an applied price — in different orders:
// MQL4: deviation BEFORE the shift
iBands(_Symbol, 0, period, deviation, shift, PRICE_CLOSE, MODE_UPPER, bar);
// MQL5: shift BEFORE the deviation
iBands(_Symbol, _Period, period, shift, deviation, PRICE_CLOSE);
Swap them and you get bands of the wrong width, from code that compiles. A deviation of 2 and a shift of 0 becomes a deviation of 0 and a shift of 2 — still valid arguments, completely different bands.
5. Nine indicators exist only on MT5
If you are targeting MetaTrader 4, these have no built-in equivalent:
- Double Exponential Moving Average (DEMA)
- Triple Exponential Moving Average (TEMA)
- Fractal Adaptive Moving Average (FrAMA)
- Adaptive Moving Average (AMA)
- Variable Index Dynamic Average (VIDyA)
- ADX by Welles Wilder
- Triple Exponential Average (TriX)
- Volumes
- Chaikin Oscillator
That leaves 29 of the 38 built-ins available on both. There are also
smaller mismatches inside shared indicators — MT4's iOBV takes an applied
price where MT5's takes an applied volume, for instance.
6. #property strict is MQL4-only
MQL4 wants it. MQL5 has no such property — but an unknown #property is a
compiler warning, not an error, so a file carrying it still compiles and
runs. It is the one leftover from the other language that is genuinely
harmless, which is worth knowing before you go hunting for a bug it did not
cause.
What this means for building
A strategy is not portable by copying source between the two. Every layer differs: the order calls, the indicator calls, the buffer indices, the argument order, and which indicators exist at all.
In TradeSmithy a project targets a platform, and that choice flows through everything downstream — which indicators the library offers, which extension an uploaded custom indicator must have, which language the graph exports to, and what a marketplace buyer is told before they pay. Switching a project's platform deletes and rewrites nothing; it surfaces the conflicts instead, marking the nodes that cannot work on the new target and refusing the export until they are resolved.
That refusal happens before anything is spent on generating code. Finding out in MetaEditor that your EA references an indicator the terminal does not have is a slower and more annoying way to learn the same fact.
Keep reading
How to build a MetaTrader Expert Advisor without writing MQL
A node graph is a program. Here is what each node type does, how the connections become code, and what you get when you press export.
4 min readHow to use your own custom indicator in an Expert Advisor
Wire a compiled .ex4 or .ex5 into a strategy through iCustom — what a definition needs, why the upload is required, and what a buyer receives.
6 min readEvery indicator parameter can be a wire, not a typed-in number
An RSI period does not have to be 14 forever. Wire it to a global parameter and it becomes tunable in the terminal — and optimisable later — without touching the graph.
3 min read