Three Silent Failures in MT4 to MT5 Migration (And the Audit That Catches Them)
The standard migration advice goes like this: rename the file, resolve the compile errors — most of which are renamed functions — and run the backtester. If the equity curve looks similar, you are done.
I have worked through enough MT4 to MT5 migrations to say this with confidence: the advice is right for simple EAs and dangerous for anything else. Syntax errors surface immediately — the compiler catches them. The failures that cost real money are architectural: EAs that migrate cleanly, backtest cleanly, and then fail quietly for days or weeks in live trading.
Three specific failure modes appear in the majority of non-trivial migrations. Here is what they are, why they happen, and the audit that surfaces all of them before a single line of MT5 code is written.
Failure 1: The Netting Account Assumption
MT4 had one account model. You could hold as many concurrent positions on a symbol as you wanted — a buy and a sell on EURUSD, a grid of seven levels, an explicit hedge. MT4 never needed to handle this as a configurable concept because every account worked the same way.
MT5 introduced netting accounts. On a netting account, the broker aggregates all positions on a symbol into one net position. Open Buy 0.10, then open another Buy 0.10: you get one position at 0.20 lots, not two at 0.10 each. Open Buy 0.10, then Sell 0.10: the positions cancel. Both close.
Any EA that relies on concurrent positions — grids, explicit hedges, martingale sequences with individual level management — is making an assumption MT5 netting accounts silently override. The EA receives no error. It continues running, tracking a position inventory that diverges from what the account actually holds.
The fix is a hard precondition in OnInit() :
Three lines stop the EA from running on the wrong account type entirely. The alternative — detecting the problem after three weeks in live trading — is more expensive.
The decision about whether to require hedging, restructure the logic for netting, or support both is a strategic one that belongs in the pre-migration audit, not discovered after deployment.
Failure 2: CTrade's Bool Return Is Not a Fill Confirmation
MT4's OrderSend() returned an integer: the ticket number if the order was accepted and filled, -1 on failure. One return value carried both pieces of information — was it sent, and did it execute. The standard MT4 pattern was if(ticket > 0) followed immediately by state updates that assume the full intended position is now open.
MT5's CTrade methods return bool . true when the server accepted the request. Not when the position was filled. Not at the size you requested.
On ECN and STP accounts in MT5, partial fills are routine. trade.Buy(0.10, ...) returns true . The server fills 0.07 lots. The remaining 0.03 is rejected or queued depending on the broker's fill policy. The EA's internal state records 0.10. Every subsequent calculation — grid spacing, take-profit sizing, position management — carries that error forward.
The correct pattern after any CTrade execution call:
ResultRetcode() == TRADE_RETCODE_DONE confirms the order was fully processed. ResultVolume() confirms the actual filled size. State updates happen only after both checks pass.
In every MT4 EA I have audited for migration, the if(ticket > 0) → immediate state update pattern appears in some form. Each instance is a silent mismatch risk in MT5 that does not surface until live trading on an ECN account.
Failure 3: OnTick Does Not Mean Price Changed
In MT4, OnTick() fired when price changed. Every call brought a new Bid and Ask. Developers who used tick count as a proxy for market activity — counting ticks since the session opened, using tick frequency as a liquidity filter — were counting real price events.
MT5's OnTick() fires on any market data event, including broker-generated keepalive ticks where Bid and Ask are identical to the previous call. During low-liquidity periods and in the minutes surrounding major session opens, these synthetic ticks can dominate the call count.
A session-filter EA used tick count for activation: after the session open time, once the counter reached 20 ticks, the EA began evaluating entry signals. In MT4, 20 ticks meant 20 price movements — a reasonable proxy for market participation. After migrating to MT5 and running on demo for two weeks, the EA was opening positions in the exact low-liquidity window it was designed to avoid. The threshold had already been cleared on keepalive ticks before real price movement began.
The guard is two lines placed at the top of OnTick() :
Any static variable that increments in OnTick() and gates behavior through a conditional needs this guard placed before the increment. In a complex EA, finding all such variables is the audit — not obvious from a casual read.
The Pre-Migration Audit
These three failures do not require extensive code changes to fix. They require knowing where to look before migration begins.
The audit runs three checks against the existing MT4 source:
Order topology audit. List every OrderSend() call. Classify each one: standalone position, grid level, or hedge. Any call that opens a second concurrent position on the same symbol is netting-incompatible. The account type decision is made here.
Execution confirmation audit. Search for every if(ticket > 0) or if(ticket != EMPTY) pattern followed by state updates. These become explicit ResultRetcode() + ResultVolume() checks in MT5. State updates move to after both pass.
Tick dependency audit. Search for static variables that increment in OnTick() and appear in conditionals that gate trading behavior. Each gets a prevBid/prevAsk guard before the increment.
These three audits take two to four hours on a typical client EA. They surface every silent-failure risk before any code changes begin. The subsequent rewrite is then targeted — every change is traceable to a specific audit finding.
Running these before migration begins is the difference between a transition that works from day one and three weeks of unexplained behavior that looks like a broker problem.


