Introduction to MQL4 and MQL5
MQL4 and MQL5 are proprietary languages developed by MetaQuotes Software for algorithmic trading platforms, MetaTrader 4 and MetaTrader 5, respectively. MQL4, released with MetaTrader 4, became the de facto standard for retail forex trading automation. MQL5, introduced with MetaTrader 5, represents a significant evolution, incorporating elements from C++ and offering enhanced capabilities.
While both languages are designed for developing Expert Advisors, custom indicators, scripts, and libraries, MQL5 was built from the ground up to be more efficient, flexible, and powerful, particularly for multi-asset trading environments and sophisticated strategy testing.
Key Differences Between MQL4 and MQL5
The transition from MQL4 to MQL5 involves understanding fundamental differences that impact code structure, execution, and functionality. Some of the most critical distinctions include:
- Execution Model: MQL4 is primarily event-driven (New Tick, Timer, etc.), while MQL5 expands this with more comprehensive event handling and incorporates
OnCalculatespecifically for indicators, designed for efficiency. - Language Syntax and Features: MQL5 is closer to C++, offering true object-oriented programming (OOP) with classes and inheritance. It also includes structures, pointers, references, and namespaces, which are absent or limited in MQL4.
- Standard Library: MQL5 boasts a significantly richer standard library (
MQL5 Standard Library) with classes for various tasks, including trading operations, technical indicators, graphical objects, and database access. - Trading Functions: The trading functions in MQL5 operate differently. Instead of simple functions like
OrderSend,OrderModify,OrderDelete,OrderClose, andOrderSelect, MQL5 uses request structures (MqlTradeRequest) and result structures (MqlTradeResult) for all trade operations viaOrderSend, allowing for more complex orders and better error handling. - Indicator Buffers: MQL4 uses
SetIndexBufferwith indices starting from 0. MQL5 also usesSetIndexBuffer, but buffer types are more strictly defined (INDICATOR_DATA,INDICATOR_COLOR_INDEX, etc.), and buffer indexing and access methods are different, often requiring explicit data copying. - Timeseries Access: MQL5 provides dedicated functions like
CopyBuffer,CopyClose,CopyHigh, etc., for explicit data retrieval from indicator buffers and standard timeseries data. Direct array-like access to timeseries data (High[0],Close[i]) common in MQL4 is handled differently and often less efficiently in MQL5. - Global Variables: MQL5 uses
GlobalVariablefunctions similarly to MQL4, but the lifecycle and intended use might require reconsideration, especially in the context of MQL5’s more structured environment.
Understanding the Core Architectural Changes
The architectural shift from MQL4 to MQL5 is profound. MQL4’s model is largely procedural and event-driven, suitable for simpler, single-instrument logic. MQL5, conversely, is designed for parallelism and multi-instrument operations, aligning with MetaTrader 5’s capabilities as a multi-asset platform.
Indicators in MQL5 are optimized to work with the OnCalculate event, which is triggered when new data arrives or data history changes. This event receives price data arrays and previous indicator calculated values directly, allowing for efficient iterative calculations without constantly requesting data history within the loop, a common pattern in MQL4 OnCalculate loops (or loops simulating OnCalculate within start/OnTick).
This design facilitates faster backtesting, especially with tick data, and better performance in live trading by decoupling data processing from graphical output.
Why Convert from MQL4 to MQL5?
Converting MQL4 indicators to MQL5 offers several compelling advantages:
- MetaTrader 5 Platform: MT5 is MetaQuotes’ flagship platform, receiving ongoing updates and support. MT4’s future, while still widely used, is less certain in the long term.
- Enhanced Functionality: MQL5 provides access to a wider range of functions, including database operations, sophisticated graphical objects, and interaction with external programs.
- Improved Performance: MQL5 code, especially when written to leverage its architecture (e.g., using the
OnCalculateevent correctly), can execute faster, particularly for complex calculations and backtesting. - Access to MT5 Features: MT5 supports multi-currency backtesting, tick-based testing, and has a built-in economic calendar, all features accessible or leverageable by MQL5 programs.
- Maintainability: The OOP features and stricter syntax of MQL5 can lead to more modular, readable, and maintainable codebases.
Preparing for the Conversion Process
Successful conversion requires thorough preparation to understand the scope of work and potential challenges.
Analyzing Your MQL4 Indicator’s Code
Begin by conducting a detailed analysis of your MQL4 indicator. Identify:
- Indicator Buffers: How many buffers are used? What data do they store? What are their types (
DRAW_LINE,DRAW_HISTOGRAM, etc.)? How are they indexed? - Input Parameters: List all
externvariables. Their types and intended uses will need to be mapped to MQL5inputparameters. - Calculations: Breakdown the core calculation logic. How does it iterate through bars? How does it access price data (
Close[i],High[i], etc.)? How does it access previous indicator buffer values? - Helper Functions: Identify any custom functions used internally within the indicator. These will need to be reviewed and potentially rewritten or adapted.
- Global Variables: Check for usage of
GlobalVariablefunctions. Consider if their use is still appropriate in MQL5 or if class members or alternative mechanisms might be better. - Object Creation: If the indicator draws objects (lines, arrows, text), note the object types and how their properties are set. MQL5 uses different object creation functions and properties.
Identifying Incompatible Functions and Syntax
Create a list of all MQL4 functions and keywords used that are known to be incompatible or have different behavior in MQL5. This is a critical step.
Common examples include:
- Trading Functions: All MQL4 trading functions (
OrderSend,OrderSelect,OrdersTotal, etc.) must be replaced with MQL5 equivalents usingOrderSendwithMqlTradeRequest. - Timeseries Access: Direct
Close[i],High[i],Open[i],Low[i],Volume[i],Time[i]array access needs to be replaced withCopy...functions (e.g.,CopyClose,CopyTime) or accessing the data arrays passed intoOnCalculate. - Indicator Functions: Some built-in indicator functions have different parameters or names (
iMAvsiMA,iCustomvsiCustom). The way handles are obtained and used withCopyBufferis also different. - Global Variables: While
GlobalVariable...functions exist, their usage within indicators might need review. - String Handling: MQL5 has enhanced string handling, including UTF-16 support, which might affect how strings are processed.
- Resource Handling: MQL5 provides better resource management capabilities.
Plan how each incompatible element will be replaced or refactored in the MQL5 code.
Setting Up Your MQL5 Development Environment
Ensure you have MetaTrader 5 installed and the MetaEditor 5 IDE ready. Familiarize yourself with the MetaEditor 5 interface, debugger, and the MQL5 Reference documentation. MetaEditor 5 includes integrated documentation for MQL5 functions and classes, which is essential during conversion.
Create a new MQL5 Custom Indicator project in MetaEditor 5. This will generate a basic template code structure with the necessary event functions (OnInit, OnDeinit, OnCalculate).
Step-by-Step Conversion Guide
This section outlines the practical steps involved in rewriting your MQL4 indicator code in MQL5.
Replacing MQL4 Functions with MQL5 Equivalents
Go through your analyzed MQL4 code line by line and replace incompatible functions and syntax with their MQL5 counterparts. This is the most labor-intensive part.
Example: Accessing Close Price
- MQL4:
mql4
double price = Close[i];
- MQL5 (within OnCalculate):
mql5
// 'close' is one of the input arrays to OnCalculate
double price = close[i];
- MQL5 (outside OnCalculate or for historical data):
mql5
double price_array[];
int count = CopyClose(Symbol(), Period(), start_index, num_bars, price_array);
if(count > 0)
{
double price = price_array[0]; // Example: access the first copied bar
}
Example: Using a Moving Average
- MQL4:
mql4
double ma_val = iMA(Symbol(), Period(), ma_period, ma_shift, MODE_SMA, PRICE_CLOSE, i);
- MQL5:
mql5
int ma_handle = iMA(Symbol(), Period(), ma_period, ma_shift, MODE_SMA, PRICE_CLOSE);
if(ma_handle != INVALID_HANDLE)
{
double ma_buffer[];
// Copy data from the MA indicator buffer
int count = CopyBuffer(ma_handle, 0, i, 1, ma_buffer);
if(count > 0)
{
double ma_val = ma_buffer[0];
}
// Don't forget to release the handle when done (e.g., in OnDeinit)
// IndicatorRelease(ma_handle);
}
Handling Global Variables and Data Structures
Review your usage of MQL4 global variables (GlobalVariable...). If they are only used within the indicator instance, consider replacing them with class members (if using OOP) or simply global variables within the MQL5 indicator’s file scope. If they need to persist across restarts or communicate between different programs, GlobalVariable... functions are still an option in MQL5, but evaluate if alternatives like shared files or external databases are more suitable for complex inter-process communication.
MQL5 offers stronger support for dynamic arrays, structs, and classes. Array handling, including sorting and searching, is generally more robust. Be mindful of array indexing direction (AS_SERIES) if your MQL4 code relied heavily on time-series indexing (0 being the current bar).
Adjusting Event Handling and Indicator Buffers
indicators utilize the OnCalculate event, which has a specific signature:
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double& price[],
const int& volume[],
const int& spread[])
Or, more commonly for standard price data:
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double& close[]) // Example for close price
Your core calculation logic from MQL4’s start or OnTick (within loops) will go inside OnCalculate. The rates_total parameter gives the total number of bars available, prev_calculated indicates how many bars were processed in the previous OnCalculate call (useful for optimizing calculations by processing only new bars), and begin is the index of the first bar where calculation is needed. Input arrays like close[] provide direct access to price data for the relevant bars.
Indicator buffers in MQL5 are set up using SetIndexBuffer in OnInit. You must specify the buffer type (INDICATOR_DATA, INDICATOR_COLOR_INDEX, etc.) and associate a double array with it.
Example: Setting up a buffer
// Global or class member double array
double my_buffer[];
// In OnInit()
SetIndexBuffer(0, my_buffer, INDICATOR_DATA);
// Optional: Set drawing style, colors, etc. in OnInit
PlotIndexSetInteger(0, PLOT_TYPE, PLOT_LINE);
PlotIndexSetInteger(0, PLOT_COLOR, clBlue);
Inside OnCalculate, you will write the calculated values into my_buffer[i] for the relevant bars i.
Compiling and Debugging Your MQL5 Indicator
Once you have rewritten the code, compile it in MetaEditor 5. The compiler will report errors and warnings, which you must address. Pay close attention to type mismatches, function signature errors, and undeclared variables.
Use the MetaEditor 5 debugger extensively. Set breakpoints, step through your code, inspect variable values, and examine arrays to understand the execution flow and identify logic errors. The debugger is your most powerful tool for finding why your converted indicator isn’t behaving as expected.
Advanced Conversion Techniques and Considerations
Beyond a direct translation, consider leveraging MQL5’s advanced features.
Optimizing Performance in MQL5
- Efficient Loops: Use
prev_calculatedinOnCalculateto process only new bars whenever possible. - Array Operations: MQL5 provides functions for efficient array copying and manipulation (
CopyBuffer,ArrayCopy). Avoid calculating values bar by bar if block operations are possible. - Memory Management: While MQL5 handles much of the memory management, be mindful of large dynamic arrays. Release indicator handles using
IndicatorReleaseinOnDeinitif you create them usingiMA,iCustom, etc.
Utilizing MQL5’s Object-Oriented Features
Even for indicators, OOP can improve code organization. You can define classes for complex calculations, data structures, or graphical object management. This makes the code more modular, reusable, and easier to maintain, especially for large indicators or libraries.
Backtesting and Forward Testing Your Converted Indicator
Test your converted indicator thoroughly in the Strategy Tester in MetaTrader 5. Use high-quality tick data for the most accurate results. Compare the output (lines, histograms, arrows) with the original MQL4 indicator on the same historical data to ensure the calculations are identical. Use the visual mode in the tester to watch the indicator draw on the chart bar by bar.
After backtesting, deploy the indicator on a demo account for forward testing. Monitor its behavior in a live market environment to catch any issues that might not appear in historical testing.
Common Issues and Troubleshooting
Conversion is rarely seamless. Be prepared to troubleshoot common problems.
Dealing with Compilation Errors
- Function Not Found: You’ve used an MQL4 function that doesn’t exist or has a different name in MQL5. Consult the MQL5 Reference.
- Wrong Parameters: You’ve called an MQL5 function with the incorrect number or types of arguments. Check the function signature in the MQL5 Reference.
- Type Mismatch: You’re trying to assign a value of one type to a variable of an incompatible type. Use explicit type casting if necessary, but first ensure you’re using the correct variable types.
- Undeclared Identifier: You’ve used a variable or function name that hasn’t been declared. Check for typos or missing declarations.
Resolving Runtime Errors
- Array Out of Range: Accessing an array index that is outside its bounds. This often happens with loops or when accessing indicator buffers. Carefully check loop conditions and array sizes.
- Division by Zero: A calculation attempts to divide by zero. Implement checks before division.
- Invalid Indicator Handle: You’re trying to use an indicator handle obtained with
iMA,iCustom, etc., that isINVALID_HANDLE. Always check the handle value after creating it. - Incorrect Calculation Output: The indicator draws but the values are wrong. Use the debugger to step through the
OnCalculatefunction and inspect intermediate values. Verify the logic against the original MQL4 code.
Ensuring Accurate Indicator Output
Visual comparison is key. Overlay your MQL5 indicator on the same chart and data as the original MQL4 version. Lines should match exactly, histograms should have the same bar heights and positions, and objects should appear at the correct locations. If there are discrepancies, systematically compare calculated values bar by bar, potentially printing values to the Experts log or using the debugger, to pinpoint where the calculation diverges.
Pay special attention to edge cases, like the first few bars on the chart, or periods with missing data, as these can sometimes reveal subtle logic errors in the conversion. Converting MQL4 to MQL5 is a process of careful adaptation, testing, and refinement, yielding indicators capable of leveraging the full power of the MetaTrader 5 platform.