Summary: Migrating EAs from MT4 to MT5 requires rewriting OrderSend with Position/Order model, replacing MarketInfo with SymbolInfo, and adapting time functions. This guide provides side-by-side code comparisons and a complete migration checklist.




MT4 to MT5 EA Migration: A Practical Guide



Why Migration Isn't Just Recompilation



Moving Expert Advisors from MT4 (MQL4) to MT5 (MQL5) is often mistaken for a simple recompilation. This is fundamentally wrong. MT5 uses a completely different trading architecture: multi-currency backtesting, built-in hedging option, and most importantly, a separation between orders (pending) and positions (market trades).

In MQL4, a market trade is an "order" from open to close. In MQL5, pending orders and market positions are distinct objects, each managed with its own set of functions.

Core Difference: Order Management Model



| Aspect | MQL4 | MQL5 |
|:---|:---|:---|
| Open market trade | OrderSend() | PositionOpen() |
| Close market trade | OrderClose() | PositionClose() |
| Modify SL/TP | OrderModify() | PositionModify() |
| Place pending order | OrderSend() | OrderSend() (different params) |
| Open price retrieval | OrderOpenPrice() | PositionGetDouble(POSITION_PRICE_OPEN) |

1. OrderSend to Position/Order Replacement



In MQL4 – Opening a Buy Order:
``cpp
int ticket = OrderSend(
Symbol(), // symbol
OP_BUY, // operation
lotSize, // volume
Ask, // price
3, // slippage
stopLoss, // stoploss
takeProfit, // takeprofit
"EA Trade", // comment
magicNumber, // magic
0, // expiration
clrGreen // arrow color
);
`

In MQL5 – Opening a Buy Position:

MQL5 replaces
OrderSend() with specialized functions. Opening a market trade requires creating a trade request structure and checking the result.

`cpp
#include
CTrade trade;

// Configure trade
trade.SetExpertMagicNumber(magicNumber);
trade.SetDeviation(3); // slippage

// Build request
double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = stopLoss;
double tp = takeProfit;

// Send order
bool success = trade.PositionOpen(
_Symbol, // symbol
ORDER_TYPE_BUY, // trade direction
lotSize, // volume
price, // open price
sl, // stop loss
tp, // take profit
"EA Trade" // comment
);

if(success)
{
ulong ticket = trade.ResultOrder(); // Get order ticket
Print("Position opened. Ticket: ", ticket);
}
else
{
Print("PositionOpen failed. Error: ", trade.ResultRetcode());
}
`

2. MarketInfo Replacement: SymbolInfo



MarketInfo() is gone in MQL5. Use SymbolInfoDouble(), SymbolInfoInteger(), and SymbolInfoDouble().

| MQL4 | MQL5 Equivalent |
|:---|:---|
|
MarketInfo(Symbol(), MODE_BID) | SymbolInfoDouble(_Symbol, SYMBOL_BID) |
|
MarketInfo(Symbol(), MODE_ASK) | SymbolInfoDouble(_Symbol, SYMBOL_ASK) |
|
MarketInfo(Symbol(), MODE_POINT) | SymbolInfoDouble(_Symbol, SYMBOL_POINT) |
|
MarketInfo(Symbol(), MODE_DIGITS) | (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS) |
|
MarketInfo(Symbol(), MODE_STOPLEVEL) | (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) |
|
MarketInfo(Symbol(), MODE_SPREAD) | (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) |
|
MarketInfo(Symbol(), MODE_MINLOT) | SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) |
|
MarketInfo(Symbol(), MODE_MAXLOT) | SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX) |

Example – Getting Ask price with validation:
`cpp
// MQL5 version
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(ask == 0)
{
Print("Failed to get ASK price. Error: ", GetLastError());
return;
}
`

3. Time Function Migration



MQL5 introduces a critical distinction between trade server time and local computer time.

| MQL4 | MQL5 | Note |
|:---|:---|:---|
|
TimeCurrent() | TimeTradeServer() or TimeCurrent() | MQL5 still supports TimeCurrent() |
|
Time[0] (array) | iTime(_Symbol, PERIOD_CURRENT, 0) | For series access |
|
CopyTime() for history | CopyTime() (similar but more features) | Both support CopyTime() |
| N/A |
TimeLocal() | Computer time (not recommended for logic) |

Best practice – Use trade server time:
`cpp
// MQL5 – Recommended for EA logic
datetime currentTime = TimeTradeServer();
`

4. Position and Order Selection



In MQL4, you loop through
OrdersTotal() for everything. In MQL5, separate loops exist:

| Purpose | MQL5 Function |
|:---|:---|
| Loop through open positions |
PositionsTotal() + PositionSelect() / PositionGetTicket() |
| Loop through pending orders |
OrdersTotal() + OrderSelect() |
| Loop through closed history |
HistorySelect() + HistoryDealsTotal() |

MQL5 – Getting current open positions:
`cpp
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
long magic = PositionGetInteger(POSITION_MAGIC);

if(magic == magicNumber)
{
// Process this position
}
}
}
`

5. Complete Migration Checklist



Before recompiling, verify these items have been addressed:

  • [ ] Replace all OrderSend() market trades with CTrade.PositionOpen() / PositionClose()

  • [ ] Replace all MarketInfo() with SymbolInfoDouble/Integer()

  • [ ] Replace Bid / Ask with SymbolInfoDouble(_Symbol, SYMBOL_BID/ASK)

  • [ ] Update pending orders: OrderSend() is still used but with different parameter structure

  • [ ] Replace OrderModify() with trade.PositionModify() or trade.OrderModify()

  • [ ] Update time functions: prefer TimeTradeServer() over TimeCurrent()

  • [ ] Add #include for CTrade class

  • [ ] Test with visual mode in Strategy Tester to see order flow

  • [ ] Verify slippage and deviation handling using trade.SetDeviation()


  • Common Pitfalls



    Pitfall 1: ECN Brokers and Stop Loss Attachment
    Some brokers reject stop loss in the initial request. The workaround is to open the trade with 0 SL/TP, then modify immediately after confirmation .

    Pitfall 2: Point vs SymbolInfoDouble
    Point still exists in MQL5, but SymbolInfoDouble(_Symbol, SYMBOL_POINT) is more reliable, especially for symbols with unusual point values.

    Pitfall 3: Lot Step Validation
    Always check
    SYMBOL_VOLUME_STEP and round lot sizes accordingly:
    `cpp
    double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
    double roundedLot = MathRound(lotSize / lotStep) * lotStep;
    `

    Summary



    | Component | MQL4 Approach | MQL5 Approach |
    |:---|:---|:---|
    | Market trade |
    OrderSend() | CTrade.PositionOpen() |
    | SL/TP modification |
    OrderModify() | CTrade.PositionModify() |
    | Quotes |
    Bid, Ask | SymbolInfoDouble() |
    | Time |
    TimeCurrent() | TimeTradeServer() |
    | Symbol info |
    MarketInfo() | SymbolInfoDouble/Integer()` |

    Migration requires rewriting core trade management logic. The CTrade class provides a cleaner abstraction, but the learning curve is steep for developers accustomed to MQL4's simpler model. Always test migrated EAs in demo mode before live deployment.

    References:
  • MQL5 Documentation – Migration from MQL4 (docs.mql5.com)

  • MQL5 Community Forum – OrderSend error 130 ECN workaround (2025)

  • MQL5 Book – Trading Operations Programming

  • MetaQuotes – MQL4 vs MQL5 Feature Comparison (2026)