Migrating an Expert Advisor from MQL4 to MQL5 is not a simple find-and-replace exercise. The two platforms differ fundamentally in architecture: MQL4 uses a procedural paradigm with a simple order model, while MQL5 embraces object-oriented programming with a separate order-position system and handle-based indicators.
1. Entry Point Function Mapping
The most basic but crucial change is renaming the main functions:
```cpp
// MQL4
init() -> returns int
deinit() -> returns void
start() -> returns void
// MQL5
int OnInit() // Must return INIT_SUCCEEDED or INIT_FAILED
void OnDeinit(const int reason) // Now receives a reason code
void OnTick() // For EAs, OnCalculate() for indicators
```
Critical difference: `OnInit()` must explicitly return `INIT_SUCCEEDED` on success. Failure requires `INIT_FAILED` to prevent the EA from loading.
2. OrderSend To CTrade Conversion
This is where the biggest architectural shift happens. MQL5 separates pending orders from open positions:
```cpp
// === MQL4: Simple OrderSend ===
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, sl, tp, "EA", magic, 0, clrNONE);
if(ticket < 0) Print("Error: ", GetLastError());
// === MQL5: CTrade Class ===
#include
#include
#include
CTrade trade;
CPositionInfo position;
CSymbolInfo symbol_info;
// In OnInit()
trade.SetExpertMagicNumber(magic);
trade.SetDeviationInPoints(10);
symbol_info.Name(_Symbol);
// Opening a buy position
if(!trade.Buy(0.1, _Symbol, 0, sl, tp, "EA"))
{
Print("Error: ", trade.ResultRetcodeDescription());
}
```
Note that passing `0` as the price parameter tells `CTrade` to use the current market price automatically.
3. Position Versus Order Separation
MQL4’s `OrdersTotal()` includes both pending orders and open positions. MQL5 treats them separately:
```cpp
// === MQL4 ===
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic) {
// Process order/position
}
}
}
// === MQL5 ===
// Iterate open positions only
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(position.SelectByIndex(i)) {
if(position.Symbol() == _Symbol && position.Magic() == magic) {
double profit = position.Profit();
// Process position
}
}
}
// Iterate pending orders separately
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
// Process pending orders only
}
}
```
4. Indicator System: From Direct Calls To Handles
MQL4 calls indicators directly each time. MQL5 uses a handle system for performance:
```cpp
// === MQL4 ===
double ma = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 0);
double ma_prev = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 1);
// === MQL5 ===
// Global variables
int ma_handle;
double ma_buffer[];
// In OnInit()
ma_handle = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE) return(INIT_FAILED);
ArraySetAsSeries(ma_buffer, true); // IMPORTANT: flips index order
// In OnTick()
if(CopyBuffer(ma_handle, 0, 0, 2, ma_buffer) < 2) return;
double ma = ma_buffer[0]; // Current bar (MQL4: Close[0])
double ma_prev = ma_buffer[1]; // Previous bar (MQL4: Close[1])
// In OnDeinit()
IndicatorRelease(ma_handle);
```
The `ArraySetAsSeries(true)` call is essential because MQL5 arrays index from oldest to newest by default, while MQL4 indexes from newest to oldest.
5. Price And Market Data Access
MQL5 replaces global variables with `SymbolInfoDouble()` and similar functions:
```cpp
// === MQL4 ===
double bid = Bid;
double ask = Ask;
double point = Point;
int digits = Digits;
double close = Close[0];
double high = High[1];
// === MQL5 ===
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double close = iClose(_Symbol, _Period, 0);
double high = iHigh(_Symbol, _Period, 1);
```
6. Account Information Migration
MT5 uses unified access functions with enumeration parameters:
```cpp
// === MQL4 ===
double balance = AccountBalance();
double equity = AccountEquity();
double margin = AccountFreeMargin();
string name = AccountName();
// === MQL5 ===
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double margin = AccountInfoDouble(ACCOUNT_FREE_MARGIN);
string name = AccountInfoString(ACCOUNT_NAME);
```
7. Complete Position Closing Function
A production-ready position closer that handles both buy and sell positions:
```cpp
bool ClosePositionByTicket(ulong ticket, int slippage = 10) {
if(!PositionSelectByTicket(ticket)) return false;
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = PositionGetString(POSITION_SYMBOL);
request.volume = PositionGetDouble(POSITION_VOLUME);
request.deviation = slippage;
request.type_filling = ORDER_FILLING_FOK;
long positionType = PositionGetInteger(POSITION_TYPE);
if(positionType == POSITION_TYPE_BUY) {
request.price = SymbolInfoDouble(request.symbol, SYMBOL_BID);
request.type = ORDER_TYPE_SELL;
} else {
request.price = SymbolInfoDouble(request.symbol, SYMBOL_ASK);
request.type = ORDER_TYPE_BUY;
}
return OrderSend(request, result);
}
```
Reference: Jaume Sancho, “MQL4 to MQL5 Migration: A Practical Guide from the Trenches” (MQL5 Blogs, Feb 2026); “MT4到MT5代码迁移全攻略” (MQL5社区文章, 2025).