Converting code between programming languages is a common task, and for algorithmic traders, migrating from MQL4 to MQL5 is often a necessary step to leverage the advanced features and improved performance of the MetaTrader 5 platform. While both languages share a common ancestor and syntax roots, MQL5 introduces significant changes that require more than a simple find-and-replace operation. This guide provides a structured approach for experienced MQL4 developers looking to make the transition.
Introduction to MQL4 and MQL5
Understanding the core philosophies and architectural differences between MQL4 and MQL5 is crucial before embarking on a conversion project.
Overview of MQL4: Strengths and Limitations
MQL4, the language for MetaTrader 4, was designed primarily around the concept of handling orders on an account level and using a simplified event model. Its strengths lie in its maturity, extensive codebase built over many years, and a large community providing countless EAs, indicators, and scripts. The MQL4 trading context often felt more direct, with functions like OrderSend immediately submitting a request.
However, MQL4 also has limitations. It lacks true object-oriented programming (OOP) features, which can make complex codebases harder to manage and maintain. The event model is simpler, primarily revolving around start(), init(), and deinit(). Memory management is less explicit, and while generally robust, it can lead to issues if not handled carefully. The backtesting engine in MT4 is less sophisticated than its MT5 counterpart.
Overview of MQL5: Advantages and New Features
MQL5 is a more modern, robust, and flexible language. It introduces full support for OOP, including classes, structures, and inheritance, significantly improving code structure, reusability, and maintainability. The event-driven model is greatly expanded with functions like OnTick(), OnTrade(), OnTimer(), OnChartEvent(), and OnCalculate(), providing finer control over program execution.
The trading system in MQL5 is based on positions, orders, and deals, aligning more closely with standard financial market terminology and offering more transparency in trade lifecycle management. Functions operate on a symbols-specific basis by default, requiring explicit symbol handling. MQL5 also natively supports multi-threading, which is essential for optimizing performance, especially in indicators and complex EAs, and includes a more advanced backtesting engine capable of multi-currency testing.
Key Differences Between MQL4 and MQL5
The fundamental differences can be summarized in several key areas:
- Trading System: MQL4 is order-centric; MQL5 is position-centric with explicit concepts of orders and deals.
- Execution Model: MQL4
start()vs. MQL5OnTick(),OnCalculate(), and other event handlers. - Language Features: MQL5 supports OOP, namespaces, enumerations, and pointers more explicitly.
- Built-in Functions: Many MQL4 functions are obsolete or replaced with new MQL5 equivalents, particularly for trading operations and indicator access.
- Symbol and Timeframe Handling: MQL5 functions often require explicit symbol and timeframe parameters.
- Indicator Buffers: Different mechanisms for accessing indicator data (
iMA,iRSIin MQL4 vs.IndicatorCreate,CopyBufferin MQL5). - Compilation: MQL5 uses a more stringent compiler, often catching errors that MQL4 might overlook until runtime.
Preparing for the Conversion
Migrating a codebase requires careful preparation to minimize issues and ensure a smooth transition.
Analyzing Your MQL4 Codebase
Before writing any MQL5 code, thoroughly analyze the MQL4 project you intend to convert. Identify:
- The purpose and core logic of the program (EA, indicator, script).
- Dependencies on other files (include files). MQL5 uses a similar include mechanism (
#include), but paths might need adjustment. - Custom functions and variables.
- How trading operations are handled (
OrderSend,OrderSelect, loops throughOrdersTotal()). - How indicator data is accessed (
iMA,iRSI, etc.). - Any reliance on global variables or static variables within functions.
- The event handlers being used implicitly (
start,init,deinit).
Documenting these elements will provide a clear roadmap for the conversion process.
Identifying Incompatible Functions and Syntax
This is a critical step. Go through your analyzed code and list all MQL4 functions that are known to be obsolete or behave differently in MQL5. Pay close attention to:
- Trading functions:
OrderSend,OrderClose,OrderModify,OrderSelect,OrdersTotal,OrderTicket,OrderMagicNumber,OrderSymbol,OrderLots,OrderOpenPrice,OrderType,OrderOpenTime,OrderExpiration,OrderCommission,OrderSwap,OrderProfit,OrderComment,OrderClosePrice,OrderCloseTime,OrderPeakProfit,OrderReason,OrderState. - Account information functions: Many like
AccountBalance,AccountEquity,AccountFreeMargin,AccountInfoInteger,AccountInfoDouble,AccountInfoStringhave direct or slightly different MQL5 equivalents often accessed viaAccountInfoXXXorAccountInfostructures/functions. - Market information functions:
SymbolInfoInteger,SymbolInfoDouble,SymbolInfoString,MarketInfoneed review. - Time Series access: Functions like
iClose,iOpen,iHigh,iLow,iVolume,iTimeare replaced byCopyClose,CopyOpen, etc., or accessing data directly from indicators created viaIndicatorCreate. - Indicator functions:
iMA,iRSI,iBands, etc. are replaced byIndicatorCreateand then accessing data buffers.
Also note syntax differences, such as the requirement for explicit type casting or stricter variable scope rules.
Setting Up Your MQL5 Development Environment
Ensure you have the MetaTrader 5 terminal installed. MetaEditor, the integrated development environment, is included. Familiarize yourself with its MQL5 specific features:
- Project Structure: MQL5 projects can be more structured, especially when using classes and multiple files.
- Debugger: The MQL5 debugger has improved capabilities.
- Profiler: Use the built-in profiler to identify performance bottlenecks.
- Backtester: Understand the options and capabilities of the MT5 Strategy Tester.
- Documentation: Keep the MQL5 Reference open; it’s your most valuable resource during conversion.
Create a new MQL5 project or file and prepare to copy and adapt your MQL4 code incrementally.
Step-by-Step Conversion Process
This section outlines the practical steps involved in rewriting MQL4 code for the MQL5 environment.
Adapting Data Types and Structures
MQL5 has stricter rules regarding data types. While core types (int, double, string, bool, datetime, color) exist in both, implicit conversions are less forgiving in MQL5. Pay attention to:
- Boolean: MQL5
boolis a true boolean type, unlike MQL4 whereintwas often used for boolean values (0 = false, non-zero = true). - Enumerations: MQL5 supports explicit
enumtypes, improving code readability and type safety (e.g.,ENUM_ORDER_TYPE,ENUM_POSITION_TYPE). Replace integer constants with enumerations where appropriate. - Structures: MQL5 supports user-defined
structtypes. The new trading system heavily uses structures likeMqlTradeRequestandMqlTradeResult.
Review variable declarations and function parameters, ensuring types match and performing explicit casting ((int), (double), etc.) where necessary to avoid compiler warnings or runtime errors.
Replacing Obsolete Functions
This is often the most time-consuming part. Replace deprecated MQL4 functions with their MQL5 counterparts. For example:
Instead of MQL4’s iClose(Symbol(), Period(), index):
double close_price = iClose(Symbol(), Period(), 0);
In MQL5, you would typically use CopyClose after getting the symbol handle and creating an indicator or accessing market data:
long symbol_handle = SymbolHandle(_Symbol);
// Need to get data into a buffer first, e.g., using CopyClose
double close_prices[];
if (CopyClose(_Symbol, _Period, 0, 1, close_prices) > 0)
{
double close_price = close_prices[0];
}
Indicator access also changes significantly:
Instead of MQL4’s iMA(Symbol(), Period(), period, shift, method, price_type, index):
double ma_value = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0);
In MQL5, you create an indicator handle and copy data from its buffer:
int ma_handle = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE);
double ma_buffer[];
if (CopyBuffer(ma_handle, 0, 0, 1, ma_buffer) > 0)
{
double ma_value = ma_buffer[0];
}
// Remember to delete the handle when done (e.g., in OnDeinit)
// IndicatorRelease(ma_handle);
Adjusting Order Management Functions
The trading system rewrite is perhaps the largest change. MQL5 uses OrderSend with an MqlTradeRequest structure. Iterating through orders and positions also changes.
In MQL4, you might loop through orders:
for (int i = 0; i < OrdersTotal(); i++)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if (OrderMagicNumber() == my_magic && OrderSymbol() == Symbol())
{
// Process order
}
}
}
In MQL5, you typically work with positions and use functions like PositionSelect, PositionGetTicket, PositionGetDouble, etc. You might loop through positions or select by ticket:
long pos_ticket = -1;
// Assuming you know the position ticket
// Or loop through positions:
/*
for(int i=0; i<PositionsTotal(); i++)
{
if(PositionSelectByIndex(i))
{
if(PositionGetInteger(POSITION_MAGIC) == my_magic && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
pos_ticket = PositionGetInteger(POSITION_TICKET);
break; // Found the position
}
}
}
*/
// If position_ticket is found and selected
if (PositionSelectByTicket(pos_ticket))
{
double volume = PositionGetDouble(POSITION_VOLUME);
// Process position
}
Sending an order is also fundamentally different. Instead of simple parameters:
double price = Ask;
bool success = OrderSend(Symbol(), OP_BUY, 0.1, price, 3, 0, 0, "My Order", my_magic, 0, Green);
In MQL5, you populate an MqlTradeRequest structure and call OrderSend (or PositionOpen, etc.) with it:
// MQL5 OrderSend requires MqlTradeRequest and MqlTradeResult
MqlTradeRequest request = {};
MqlTradeResult result = {};
double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.action = TRADE_ACTION_DEAL; // Or TRADE_ACTION_ORDER
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY; // Or ORDER_TYPE_BUY_LIMIT, etc.
request.price = price;
request.deviation = 3; // In points
request.magic = my_magic;
request.comment = "My Order";
// Example for a Limit order:
/*
request.action = TRADE_ACTION_PENDING;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY_LIMIT;
request.price = price - 20 * _Point; // Example limit price
request.magic = my_magic;
request.comment = "My Limit Order";
request.expiration = TimeCurrent() + 60*60; // Example expiration
*/
if (OrderSend(request, result))
{
if (result.retcode == TRADE_RETCODE_SUCCEEDED)
{
long new_order_ticket = result.order;
long new_position_ticket = result.deal; // For TRADE_ACTION_DEAL
// Success handling
} else {
// Error handling: result.retcode, result.comment
}
} else {
// OrderSend failed (communication error etc.)
}
Handling Event Handling Differences
MQL5 uses a more explicit event model. The MQL4 start() function is typically replaced by OnTick() for EAs or OnCalculate() for indicators.
init()->OnInit()(ReturnINIT_SUCCEEDEDorINIT_FAILED).deinit()->OnDeinit(const int reason)(The reason parameter provides context for deinitialization).start()->OnTick()(for EAs reacting to new quotes) orOnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[])(for indicators processing price data).- Add other handlers like
OnTimer(),OnChartEvent(),OnTrade(),OnTradeTransaction()as needed based on the program’s logic.
Convert your start() logic into the appropriate MQL5 event handler, adapting function calls and variable usage.
Advanced Conversion Techniques
Beyond direct function replacement, consider leveraging MQL5’s advanced features.
Using Include Files and Libraries
MQL5 supports includes similar to MQL4, but also introduces the concept of MQL5 Libraries (.mqh files specifically designed for library use, though regular includes work) and modules. For complex conversions, consider refactoring common logic into include files or classes within include files to promote reusability across different parts of your converted code or for future projects. MQL5 also supports namespaces, which can help organize code in larger projects.
Optimizing Code for MQL5
MQL5 offers opportunities for optimization. The stricter compiler often leads to more efficient bytecode. Leverage built-in functions optimized for performance. Avoid unnecessary calculations within OnTick() or OnCalculate(). For indicators, utilize the prev_calculated parameter in OnCalculate() to avoid recalculating the entire buffer on every new tick; only process new bars or data points. Use the profiler to identify performance bottlenecks.
Exploiting MQL5’s Multi-Threading Capabilities
MQL5 supports multi-threading, particularly beneficial in the Strategy Tester for optimizing parameters or when running complex indicators. While writing explicitly multi-threaded code can be advanced, the MQL5 Strategy Tester automatically utilizes multiple cores. Ensure your code is thread-safe if you use shared resources, although for typical EA/indicator logic within a single instance, this is less of a concern than when designing components intended for parallel execution within the tester or complex libraries.
For indicators, the OnCalculate function is designed to be called by the terminal’s calculation engine, which can itself be multi-threaded during backtesting or on multi-core systems. Efficient use of prev_calculated and proper buffer handling is key here.
Testing and Debugging the Converted Code
Thorough testing is non-negotiable after conversion. The differences between MQL4 and MQL5 mean that logic that worked perfectly before might fail or behave differently.
Unit Testing Strategies for MQL5
MQL5 has improved capabilities for testing. While not a formal unit testing framework like NUnit or JUnit, you can create MQL5 scripts or special EAs designed to test specific functions or logic blocks in isolation. For example, create a script that calls your order management functions with predefined parameters and verifies the output or return codes. Test data access functions (CopyBuffer, etc.) with known price data patterns. Use Print() or Comment() extensively during testing to log intermediate results and verify calculations.
Debugging Techniques in MetaEditor
The MetaEditor debugger for MQL5 is a powerful tool. Use breakpoints (F9) to pause execution at specific lines. Step through the code line by line (F10, F11). Inspect the values of variables in the Watch window. Examine the call stack to understand how functions were called. Use the ‘Terminal’ tab within the debugger for output messages (Print, Alert, Comment). Debugging converted code is crucial for identifying issues arising from syntax changes, logic adaptation, or unexpected behavior of new MQL5 functions.
Backtesting and Optimization in MQL5
Once the code compiles and passes basic debugging, extensive backtesting is required. The MQL5 Strategy Tester offers more options (real ticks, multi-currency, genetic optimization). Run backtests on historical data to ensure the EA behaves as expected. Compare results against the original MQL4 version if possible (though exact numerical results may differ due to tick data handling and calculation engine differences).
Use the optimization features to find the best parameters for your strategy in the MQL5 environment. Be mindful that optimization in MT5 is more resource-intensive due to the granular tick data, but provides more accurate results. Ensure your conversion handles the tester’s requirements correctly, particularly within the OnTester() and OnTesterInit() functions if your EA uses them.
Converting MQL4 to MQL5 is a significant undertaking that requires a deep understanding of both languages. By following a structured approach, analyzing your existing code, understanding the differences, and thoroughly testing your conversion, you can successfully migrate your trading systems to the more powerful and modern MQL5 platform.