Summary: Step-by-step technical guide for converting MQL4 EAs to MQL5. Covers OrderSend to PositionOpen mapping, tick handling, time series reversal, and backtest compatibility adjustments.




Migrating an Expert Advisor from MQL4 to MQL5 is not a simple syntax replacement. The two languages have fundamental differences in trade execution, history access, and event handling. Direct copy-paste will fail because MQL5 uses a position-centric model while MQL4 uses an order-centric model.

Critical Difference: Trade Execution Model

MQL4 uses individual orders where each partial close creates a new order. MQL5 uses positions (netting or hedging) with separate trade history records. The function mapping:

| MQL4 | MQL5 |
|------|------|
| `OrderSend()` | `PositionOpen()` / `OrderSend()` (CTrade) |
| `OrderClose()` | `PositionClose()` |
| `OrderModify()` | `PositionModify()` |
| `OrdersTotal()` | `PositionsTotal()` |
| `OrderSelect()` | `PositionSelect()` |

Code: Converting OrderSend to MQL5 Position Open
```cpp
// MQL4 version
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, stopLoss, takeProfit, "EA", magic, 0, clrNONE);

// MQL5 equivalent using CTrade
#include
CTrade trade;
double sl = 0, tp = 0;
if(stopLoss > 0) sl = Ask - stopLoss * _Point;
if(takeProfit > 0) tp = Ask + takeProfit * _Point;
bool success = trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, 0.1, Ask, sl, tp, "EA");
```

Time Series Index Reversal

MQL4 uses time series where index 0 is the current bar. MQL5 uses array-like series where index 0 is the oldest bar unless `ArraySetAsSeries()` is called.

```cpp
// MQL4 - current bar close
double currentClose = Close[0];

// MQL5 - must set series or use copying
double closeArray[];
ArraySetAsSeries(closeArray, true);
CopyClose(Symbol(), Period(), 0, 10, closeArray);
double currentClose = closeArray[0];
```

Event Handler Mapping

| MQL4 | MQL5 |
|------|------|
| `init()` | `OnInit()` |
| `deinit()` | `OnDeinit()` |
| `start()` | `OnTick()` |
| `OnTimer()` | `OnTimer()` (same but requires `EventSetTimer()`) |

Backtest Compatibility Fix

MQL5 backtest does NOT guarantee `_LastError` reset on every tick. Always check return values explicitly:

```cpp
// MQL4 style (unsafe in MQL5)
OrderSend(...);
if(GetLastError() == 0) { ... }

// MQL5 safe pattern
bool result = trade.PositionOpen(...);
if(result && trade.ResultRetcode() == TRADE_RETCODE_DONE) {
Print("Position opened successfully");
} else {
Print("Error: ", trade.ResultRetcode());
}
```

Tick Structure Differences

MQL4 `bid`/`ask` are global variables. MQL5 uses `SymbolInfoTick()` to fetch both bid and ask simultaneously, preventing cross-tick misalignment:

```cpp
// MQL5 - atomic tick fetch
MqlTick currentTick;
SymbolInfoTick(Symbol(), currentTick);
double currentBid = currentTick.bid;
double currentAsk = currentTick.ask;
```

Order of Operations for Migration
1. Replace all trade functions with CTrade class methods
2. Convert time series indexing (add `ArraySetAsSeries` where needed)
3. Update event handlers to MQL5 naming
4. Replace `_Point` with `SymbolInfoDouble(Symbol(), SYMBOL_POINT)`
5. Add explicit error handling after every trade operation

Reference: MQL5 Documentation - Migration from MQL4 (https://www.mql5.com/en/docs/migration), "MQL5 Programming for Traders" by Andrew R. Young, 2021.