Blog Details

thumb
13 Jul 2026

How MetaTrader 4 Compiles EX4 Files: A Technical Deep Dive into the MQL4 Build Process

How MetaTrader 4 Compiles EX4 Files: A Technical Deep Dive into the MQL4 Build Process

Table of Contents

  • The Compilation Pipeline: An Overview
  • Lexical Analysis and Parsing
  • Semantic Analysis and Type Checking
  • Bytecode Generation
  • Optimization and Linking
  • The EX4 Binary Format in Detail
  • How Build Versions Changed the Format
  • Implications for Decompilation
  • Practical Examples: Before and After Compilation

The transformation from human-readable MQL4 source code to a compiled EX4 binary is a multi-stage process that involves several fundamental computer science concepts: lexical analysis, parsing, semantic checking, bytecode generation, optimization, and linking. Understanding this pipeline illuminates why decompilation works and why it is limited in what it can recover.

This article takes a technical approach, intended for developers and technically curious traders who want to understand what is happening under the hood when they compile an Expert Advisor in MetaEditor. This knowledge is also directly relevant to understanding what professional decompilation services do and why their results look the way they do.

The Compilation Pipeline: An Overview

When you click "Compile" in MetaEditor, the compiler executes a sequence of transformations on your MQ4 file. Each stage takes the output of the previous stage as input and produces a more-processed representation:

  1. Source Input: The raw MQ4 text file is read from disk.
  2. Lexical Analysis (Tokenization): The raw text is broken into tokens keywords, identifiers, operators, literals.
  3. Parsing: Tokens are organized into a parse tree representing the grammatical structure of the code.
  4. Semantic Analysis: The parse tree is analyzed for type correctness, scope resolution, and meaning.
  5. Intermediate Representation: An intermediate, platform-independent representation of the program is generated.
  6. Optimization: The intermediate representation is optimized for performance and size.
  7. Code Generation: Platform-specific bytecode is generated from the optimized intermediate representation.
  8. Linking: Library calls and external dependencies are resolved and linked.
  9. EX4 Output: The final binary is written with headers, metadata, string tables, and bytecode sections.

Each of these stages removes or transforms information from the original source. Some transformations are reversible; others are not. Understanding which is which explains exactly what decompilation can and cannot recover.

Lexical Analysis and Parsing

The first two stages lexical analysis and parsing are where the human-readable aspects of your code begin to disappear into more abstract representations.

During lexical analysis, the compiler scans your source text character by character, grouping characters into meaningful tokens. A line like:

double lotSize = AccountBalance() * 0.01; // 1% risk per trade

...becomes a sequence of tokens: TYPE(double)IDENTIFIER(lotSize)OPERATOR(=)BUILTIN_FUNC(AccountBalance)OPERATOR(*)LITERAL(0.01)TERMINATOR(;). The comment is immediately discarded it never makes it into the token stream.

What gets lost at the lexical stage: All comments. Every // and /* */ annotation that the developer wrote to explain the code is stripped here. This is the earliest and most complete loss of information comments are gone permanently and cannot be recovered by any decompilation technique, no matter how sophisticated.

During parsing, the token stream is organized into a parse tree a hierarchical structure representing the grammatical relationships between tokens. The parser verifies that your code follows MQL4's grammar rules and reports syntax errors if it does not. The parse tree is an abstract representation of the code's structure that is used as the basis for subsequent analysis stages.

Semantic Analysis and Type Checking

Semantic analysis is where the compiler understands what your code means, not just what it says grammatically. Key activities at this stage include:

Symbol Table Construction

Every variable, function, and parameter you declare is entered into a symbol table a data structure that maps names to their properties (type, scope, memory offset). The symbol table is how the compiler resolves references: when you write lotSize in an expression, the compiler looks it up in the symbol table to determine its type and where in memory to find it at runtime.

In some MT4 builds, the symbol table information including original variable names is retained in the compiled binary for debugging purposes. In others, variable names are discarded and replaced with numeric memory offsets. This build-version difference is one of the primary factors determining whether decompiled code has meaningful variable names or generic placeholders.

Type Checking and Coercion

The compiler verifies that operations are applied to compatible types and inserts implicit coercions where necessary. These coercions may not be visible in the original source but produce additional instructions in the compiled bytecode which decompilers must recognize and reverse to produce clean source output.

Scope Resolution

The compiler determines which declaration each identifier reference refers to, based on MQL4's scope rules. Variables declared in inner scopes shadow outer scope variables with the same name. These relationships are encoded in the bytecode's variable access patterns allowing decompilers to reconstruct approximate scope structure even when original names are unavailable.

Bytecode Generation

After semantic analysis, the compiler generates bytecode a series of numeric operation codes and operands that implement the program's logic. MetaTrader 4 uses a virtual machine architecture: the MQL4 compiler generates instructions for a proprietary MQL4 virtual machine built into the MT4 runtime, not native x86 or ARM machine code.

This virtual machine architecture has significant implications for decompilation. Virtual machine bytecode operates at a higher abstraction level than native machine code. A bytecode instruction encoding "call built-in function OrderSend" is far more semantically meaningful than the equivalent sequence of native instructions. This higher-level structure is what makes systematic decompilation feasible.

The bytecode for even simple conditional logic can expand to many virtual machine instructions. Consider:

if (iMA(NULL, 0, 20, 0, 0, 0, 0) > iMA(NULL, 0, 50, 0, 0, 0, 0))

    OpenBuyOrder();

This expands to: load symbol, load period, call iMA with params (20), load result, load symbol, load period, call iMA with params (50), load result, compare values, conditional jump if false, call OpenBuyOrder. Each of these is a separate bytecode instruction with its own opcode and operands. The decompiler must recognize this instruction sequence and reconstruct the original high-level conditional expression.

The MQL4 virtual machine instruction set is proprietary and not officially documented. Decompiler developers must reverse-engineer it through analysis of many compiled binaries comparing source code and compiled output to map opcodes to their semantic meaning. This is painstaking work that must be repeated partially for each significant MT4 build version update.

Optimization and Linking

Before writing the final binary, the compiler applies optimization passes that improve execution performance but complicate decompilation:

Constant Folding

Expressions involving only constants are evaluated at compile time and replaced with their results. The expression 1000 * 60 * 60 becomes the literal value in the bytecode. A decompiler will see the final value, not the original expression the intent behind the calculation (one hour in milliseconds, clearly) is invisible without context.

Dead Code Elimination

Code that can never execute after an unconditional return, for example is removed from the compiled output. This means decompiled code may appear to lack certain branches or error handling that the developer wrote but the compiler determined were unreachable.

Function Inlining

Small functions may be "inlined" their bytecode inserted directly at each call site rather than generating a function call instruction. Inlined functions do not appear in the bytecode as distinct named functions, making the decompiled output appear to have fewer functions than the original source. The code is correct it does the same thing but the structural organization differs from the original.

Loop Optimization

Loop invariant computations (expressions inside a loop whose value does not change between iterations) may be hoisted outside the loop by the optimizer. The decompiled code will show these computations outside the loop technically equivalent but not matching the original structure if the developer originally wrote them inside.

Linking resolves references to built-in MT4 functions and custom libraries. Built-in function calls are encoded as references to a known function table, which decompilers can map back to their MQL4 names with high accuracy. Custom library calls are similarly encoded, though the library's source code may be a separate recovery challenge.

The EX4 Binary Format in Detail

The final EX4 binary has a well-defined structure that analysis tools can systematically parse:

Magic Bytes and Signature

The first four bytes of every EX4 file contain a magic signature identifying it as an MQL4 executable. These bytes allow software to quickly identify the file type without relying on the file extension alone.

Build Version Encoding

The build version of the MT4 installation that compiled the file is encoded in the header. This is the field that professional services read to determine which decompilation approach to apply different build versions require different analysis strategies.

Timestamp and Checksums

The compilation timestamp and file checksums are stored in the header. The timestamp reveals approximately when the file was compiled, which can help establish context about which MT4 version was current at that time even if the explicit build number is ambiguous.

String Table Structure

The string table is a length-prefixed array of null-terminated strings. Each string entry has an index that the bytecode references when using that string. Reading the string table directly (without decompilation) often reveals EA parameter names, log messages, broker checks, currency pair references, and sometimes even fragments of original variable names that give strong hints about the EA's purpose and structure.

How Build Versions Changed the Format

MetaQuotes introduced significant changes to the EX4 format at several key build version milestones:

  • Pre-build 225: Early format with extensive debug information retained in compiled output. Variable names widely preserved in symbol table sections. The easiest format for decompilation.
  • Build 225: First significant format revision. Reduced debug information in default compilation settings. Variable name retention became compilation-mode-dependent.
  • Build 509: Major architecture change to the bytecode instruction set. Existing decompilers built for older formats required substantial updates to handle this build correctly.
  • Build 574–600: Progressive tightening of the format with reduced information retention. Free tools began failing consistently on files from this build range.
  • Build 600+: Current architecture. Significant changes to virtual machine instruction encoding, optimization behavior, and string table structure. Requires updated decompilation techniques that most public tools have not implemented.

Professional services like ForexMQ5 maintain updated decompilation capabilities that track these format changes as they occur. This is the fundamental technical reason why professional services succeed where automated tools fail for modern-build files.

Implications for Decompilation

Understanding the compilation pipeline makes the limitations and possibilities of decompilation much clearer:

What can be recovered with high confidence: Trading logic encoded in bytecode (the opcode-to-source mapping is known for supported builds), input parameters and their defaults (stored in the properties section), string literals including some variable names (from the string table), function call structure and control flow.

What cannot be recovered: Comments (stripped at lexical analysis), variable names when replaced with numeric identifiers by the compiler, pre-optimization expression structure (constant folding, loop hoisting), inlined function boundaries.

For a foundational understanding of the EX4 file structure, the article on what is a ex4 provides the necessary background. And to understand what you can do with recovered source code, the article on is the natural next read.

Practical Examples: Before and After Compilation

To make this concrete, consider how a simple moving average crossover entry condition changes through the compilation pipeline:

Original MQL4 source:

// Check if fast MA crossed above slow MA (bullish signal)

double fastMA = iMA(Symbol(), Period(), fastPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);

double prevFastMA = iMA(Symbol(), Period(), fastPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);

double slowMA = iMA(Symbol(), Period(), slowPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);

double prevSlowMA = iMA(Symbol(), Period(), slowPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);

bool bullishCross = (fastMA > slowMA) && (prevFastMA <= prevSlowMA);

After decompilation (typical output for modern build):

double a0 = iMA(Symbol(), Period(), input_1, 0, 1, 0, 0);

double a1 = iMA(Symbol(), Period(), input_1, 0, 1, 0, 1);

double a2 = iMA(Symbol(), Period(), input_2, 0, 1, 0, 0);

double a3 = iMA(Symbol(), Period(), input_2, 0, 1, 0, 1);

bool a4 = (a0 > a2) && (a1 <= a3);

The logic is identical and clearly recognizable as a moving average crossover to any experienced developer. The comments and meaningful variable names are gone, but the trading intent is completely recoverable through code reading. This is why professional decompilation produces functionally equivalent source code even when it cannot exactly reproduce the original's style and documentation.

The ex4decompiler.com process at ForexMQ5 takes this recovered bytecode and produces the cleanest possible MQL4 source, with the highest level of variable naming restoration that the available information supports. Starting from $50 per file, their service provides the professional analysis that modern MT4 build files require for reliable recovery.

Developer Best Practices That Emerge From Understanding Compilation

Understanding the MQL4 compilation process in detail suggests specific practices that make development more maintainable and recovery easier if source code is ever lost:

Use descriptive input parameter names and meaningful function names. Input parameter names are preserved across all MT4 build versions because they must be readable in MetaTrader's property dialog. Function names are also generally preserved in decompiled output. Using meaningful names in these locations even if internal variable names are genericized makes recovered code significantly more readable.

Keep your compilation settings consistent. MetaEditor's compiler settings include options that affect how much debug information is retained in the compiled output. Developers who need to recover source code most easily should avoid aggressive optimization and stripping settings. Standard compilation settings provide reasonable recovery prospects; heavily optimized builds with all debug information stripped produce files that are more challenging to decompile cleanly.

Document the compilation settings used for important EAs. A simple text file noting the MT4 build version and MetaEditor settings used to compile each significant EA provides invaluable context if decompilation is ever needed. This metadata can save hours of diagnostic work for any professional service attempting recovery.

Test recompilation periodically. As MT4 builds update, EX4 files compiled against older builds should be periodically recompiled against the current build to ensure compatibility. This is only possible with the MQ4 source another reason source code preservation is critical. EX4 files compiled against very old builds may eventually encounter runtime compatibility issues as MetaTrader continues to evolve.

Practical Implications for Traders and Developers

Understanding the compilation pipeline clarifies what is technically possible during decompilation and why the difficulty varies by build. Files compiled with older versions of MetaEditor tend to produce cleaner decompilation output because the optimization passes were simpler and variable references are easier to trace. Files compiled with more recent versions of MetaEditor may include additional obfuscation or optimization layers that require more sophisticated analysis.

This is why working with a specialist who has hands-on experience across many build versions produces better results than relying on automated tools alone. The compilation artifacts left in an EX4 binary tell a story about how and when the file was created, and an experienced analyst knows how to read that story. ForexMQ5's team has processed files spanning more than a decade of MetaEditor builds, giving them practical familiarity with the entire range of compilation artifacts that traders and developers bring to them.

We may use cookies or any other tracking technologies when you visit our website, including any other media form, mobile website, or mobile application related or connected to help customize the Site and improve your experience. learn more

Allow