Blog

MQL4 vs MQL5: what actually changes when you switch platform

TradeSmithy Team4 min read

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:

IndicatorMT4 line numberingMT5 line numbering
Alligatorfrom 1from 0
Gator Oscillatorfrom 1from 0
Fractalsfrom 1from 0
Envelopesfrom 1from 0
Ichimoku Kinko Hyofrom 1from 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

← More from the TradeSmithy blog