• Skip to primary navigation
  • Skip to main content
  • Skip to footer
barmenteros FX logo

MetaTrader Programming Services | Programmers for MT4, MQL4, MT5, MQL5, Expert Advisor EA, Forex robots, Algo Trading | barmenteros FX

No matter if you need an MT4 programmer, EA programmer, Forex programmer, or MQL programmer. We are the best qualified team to develop your forex trading strategy. Highly skilled in MT4 programming, Expert Advisor EA programming, Forex programming, and MQL4 programming.

  • Home
  • Blog
  • Services
    • EA programming
    • MT4 Programming
    • MT5 Programming
    • EA Debugging and Code Review
    • TradingView Programming
    • NinjaTrader Programming
    • cTrader Programming
    • Forex Programming
    • Machine Learning For Trading
    • Deep Reinforcement Learning for Trading
  • Products
    • My Account
    • LicenseShield – MT4/MT5 License Protection
    • Latest Offers
    • MT4 Indicators
    • MT5 Indicators
  • Request Quote
  • Show Search
Hide Search
Home/Blog/MT4 to MT5 Migration: Recompiling Is the Easy Part
MT4 EA compiles successfully but fails silently in MT5 — three architectural failures that recompiling alone cannot catch

MT4 to MT5 Migration: Recompiling Is the Easy Part

An MT4 EA that takes two hours to recompile into MT5 syntax can take three weeks to start failing in ways you cannot explain. That gap — between “it compiles” and “it works the same way it did in MT4” — is the migration problem nobody talks about. The three things that actually break are not syntax. They are architecture.

Table of Contents

Toggle
  • The Recompile Assumption
  • Architecture Failure #1: Netting vs. Hedging Accounts
  • Architecture Failure #2: OrderSend Is Not a CTrade Wrapper
  • Architecture Failure #3: OnTick() Does Not Mean New Price
  • The Migration That Actually Works

The Recompile Assumption

The standard advice circulating on forums 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 the same, you are done.

This process works for simple EAs with no concurrent position logic, no execution verification, and no tick-based timing. For anything else, it produces an EA that appears to migrate successfully because it compiles, opens trades, and logs nothing unusual — while quietly failing at the architectural assumptions MT5 does not share with MT4.

A client brought me a hedging EA three weeks into a migration. The developer had recompiled it in an afternoon, tested it on demo, and considered it production-ready. The EA’s internal position counter showed 2 open positions. The account held 0. Every hedge order had been silently converted to a position close by the MT5 execution engine. The EA had been managing a ghost inventory for three weeks. The code never threw an error. It was operating on a fundamental assumption about how MT5 accounts work — an assumption MT4 never needed to make.

That is the recompile problem. The failure is silent because the EA does not crash. It continues doing work. Just the wrong work.

Architecture Failure #1: Netting vs. Hedging Accounts

MT4 did not have account types in the sense MT5 does. Every MT4 account let you hold multiple positions on the same symbol simultaneously — a buy and a sell, two buys at different levels, a grid of seven concurrent positions. This was the only mode that existed.

MT5 introduced a netting account model where the broker aggregates all positions on a single symbol into one net position. Open Buy 0.10, then open another Buy 0.10 on the same symbol: you get one position at 0.20, not two positions at 0.10 each. Open Buy 0.10, then Sell 0.10: both cancel out. You hold 0.

Any EA that relies on concurrent positions — grids, hedges, partial closes via a reverse order, martingale systems — will behave differently on a netting account. The EA receives no error when this happens.

The specific failure mode in a grid EA: price drops, the EA places grid level 2 (Buy 0.10 at 1.0950). On a MT5 hedging account, the account now holds two positions — level 1 at 1.1000, level 2 at 1.0950. The EA tracks both, calculates take-profit and trailing independently for each. On a MT5 netting account, the account holds one position at 0.20 lots at an averaged entry of approximately 1.0975. The EA’s internal grid tracker still shows two levels. It tries to close “level 1” independently — which triggers a partial close that leaves residual exposure the EA no longer tracks. The position management logic operates on a position inventory that does not match what the account holds.

Account type behavior — what MT4 assumed vs. what MT5 enforces:

ScenarioMT5 Netting AccountMT5 Hedging Account
Buy 0.10, then Buy 0.10 (same symbol)Single position at 0.20 lots, averaged entryTwo independent positions at 0.10 each
Buy 0.10, then Sell 0.10 (same symbol)Positions net to zero — both closeTwo opposing positions held simultaneously
Close “level 1” independently in a gridPartial close: reduces net position, orphaned remainderCloses the specific position by ticket
EA’s internal tracker vs. account stateMismatch — inventory diverges silentlyMatches — each ticket corresponds to one position
Diagram showing how MT5 netting accounts aggregate two buy orders into one position while hedging accounts keep them separate — the source of silent EA inventory mismatches

The fix is not code. It is deciding which account type the EA requires, then enforcing that as a hard precondition before migration begins. An `OnInit()` check against `AccountInfoInteger(ACCOUNT_MARGIN_MODE)`takes three lines and stops the EA from running on the wrong account type entirely.

Architecture Failure #2: OrderSend Is Not a CTrade Wrapper

The first thing most migration guides do is show you how to replace `OrderSend()` with `CTrade::Buy()` and `CTrade::Sell()`. The syntax is cleaner. The function signatures are more descriptive. It looks like a straightforward swap.

The problem is not the syntax. It is the execution contract.

MT4’s `OrderSend()` returns an integer: the ticket number if the order was accepted and filled, `-1` if it failed. One return value carries both pieces of information — was it accepted, and did it execute. Most MT4 EAs check `if(ticket < 0)` to detect failure. If the ticket is positive, the EA assumes the position now exists at the intended size.

MT5’s CTrade methods return `bool`. `true` when the server accepted the request. `false` only when local validation fails — invalid lot size, insufficient margin the terminal can detect before sending. A `true` return means the request left your machine. It does not mean the order was filled. It does not mean the position was opened at the size you requested.

On ECN and STP accounts in MT5, partial fills are routine. The EA calls `trade.Buy(0.10, …)` and receives `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 position tracker records 0.10. The account holds 0.07. Every subsequent lot size calculation for grid spacing, take-profit, and position management carries that error forward.

Flow diagram comparing MT4 OrderSend execution (3 steps, single return value) vs MT5 CTrade execution (4 verification steps including ResultRetcode and ResultVolume checks)

In every MT4 EA I have audited for migration, the pattern appears in some form: `if(ticket > 0)` followed immediately by state updates that assume the full intended position is now open. Each one of these becomes a silent mismatch risk in MT5. After every CTrade execution call, the correct checks are `trade.ResultRetcode() == TRADE_RETCODE_DONE` to confirm the order was fully processed, and `trade.ResultVolume()` to verify the actual filled size matches what was requested. State updates happen only after both checks pass.

Architecture Failure #3: OnTick() Does Not Mean New Price

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, measuring tick frequency as a liquidity filter — were counting real price events.

In MT5, `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 before major sessions open, these synthetic ticks can dominate the call count.

A session-filter EA I migrated for a client used a tick counter for session activation: once the counter reached 20 ticks after the session open time, the EA would begin evaluating entry signals. In MT4, 20 ticks meant 20 price movements — a reasonable proxy for market participation. After migrating to MT5 and running for two weeks on demo, the EA was opening positions in the exact low-liquidity window it was designed to avoid. The MT5 tick stream included keepalive ticks indistinguishable to the counter from real price events. By the time actual price movement began, the threshold had already been cleared on synthetic ticks.

The fix is two lines: before processing any `OnTick()` logic that depends on price change, check `if(Bid == prevBid && Ask == prevAsk) return;`. Any tick-dependent logic in a migrated EA needs this guard — and it takes a targeted search to find all the places where it belongs.

The Migration That Actually Works

Correct migration runs three audits before the first line of MT5 code is written. The audits are inventory checks on assumptions the MT4 EA makes that MT5 may not honor.

In client migration projects, these are the three checks that run before any code is touched:

Order topology audit. List every `OrderSend()` call in the MT4 EA. Classify each one: does it open a standalone position, add a grid level, or create a hedge? Any call that opens a second concurrent position on the same symbol is netting-incompatible. The account type decision — require hedging, restructure logic for netting, or both — is made here, not after the fact.

Execution confirmation audit. Find every location where the EA acts on the assumption that an order was filled. `if(ticket > 0)` and `if(ticket != EMPTY)` are the patterns to search for. Each becomes an explicit result inspection in MT5: `trade.ResultRetcode() == TRADE_RETCODE_DONE` to confirm execution, `trade.ResultVolume()` to confirm filled size. State updates are moved to after both checks pass.

Tick dependency audit. Search for tick counters, tick-based timers, and any `static` variable that increments in `OnTick()` and gates behavior through a conditional. Each one gets a price-change 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 actual MT5 migration work — rewriting order calls, restructuring CTrade logic, adding guards — is then targeted and verifiable. The failures that look like broker problems during live trading are development problems. Catching them in audit is the difference between a migration that works and three weeks of unexplained account behavior.

At barmenteros FX, these three audits are the first step of every MT4 to MT5 migration project we take on. If your EA has been migrated and is behaving unexpectedly — or you want to know exactly what needs to change before the migration begins — request a free assessment.

Written by:
barmenteros FX
Published on:
March 17, 2026
Last Updated:
March 20, 2026
Thoughts:
No comments yet

Categories: Blog, Myth-BustingTags: Expert Advisors, metatrader, mql4, MQL5, production code

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Explore more

Get a Free Quote Get Inspiration Get Connected

Footer

barmenteros FX

Avenida Principe Salman, 6, 5th
29603 Marbella (Malaga) — Spain

Copyright © 2026

Footer

COMPANY

  • Home
  • About barmenteros FX
  • Contact
  • Request Quote

SERVICES

  • EA Programming
  • MT4 Programming
  • MT5 Programming
  • MQL4 Programming
  • MQL5 Programming
  • EA Debugging and Code Review
  • TradingView Programming
  • NinjaTrader Programming
  • cTrader Programming
  • Forex Programming
  • Machine Learning For Trading
  • Python to MetaTrader Integration
  • Deep Reinforcement Learning for Trading
  • MetaTrader 4/5 License Management
  • All Services

PRODUCTS

  • My account
  • LicenseShield – MT4/MT5 License Protection
  • Latest Offers
  • MT4 Indicators
  • MT5 Indicators

LEGAL

  • Terms and Conditions
  • Privacy Policy
  • Cookies Policy
  • Risk Disclosure
  • Payments & Refunds Policy
  • Warranty & Support Policy
  • Intellectual Property Notice
  • General Disclaimer