Working with enumerated types (enums) and strings is fundamental in MQL5 programming. While enums provide a structured way to represent a fixed set of values, often corresponding to MQL5 constants, input from external sources, user interfaces, or configuration files frequently comes in string format. Converting these strings back into their corresponding enum values is a common requirement in developing robust Expert Advisors, indicators, and scripts.
This article delves into practical methods for performing this conversion, exploring different approaches, their implementation details, and considerations for error handling in MQL5.
Introduction to Enums and Strings in MQL5
Understanding Enumerations (Enums) in MQL5
Enumerations in MQL5 are a distinct data type consisting of a set of named integer constants. They enhance code readability and maintainability by allowing you to use descriptive names instead of raw integer values. MQL5 provides numerous built-in enums for various purposes, such as ENUM_ORDER_TYPE, ENUM_TIMEFRAMES, ENUM_POSITION_TYPE, ENUM_ACCOUNT_PROPERTY_INTEGER, etc. You can also define custom enums.
For example, ENUM_ORDER_TYPE includes members like ORDER_TYPE_BUY, ORDER_TYPE_SELL, ORDER_TYPE_BUY_LIMIT, etc., representing specific trading order types with underlying integer values.
Working with Strings in MQL5
Strings in MQL5 (of type string) are sequences of characters. They are frequently used for:
- Input parameters (
inputkeyword). - Reading from or writing to files.
- Interacting with user interface controls.
- Parsing data from external sources (like web requests).
- Logging and messaging.
Strings are flexible but require careful handling, especially when their content is expected to represent a value of another data type, such as an enum.
The Need for Conversion: String to Enum
The necessity for string-to-enum conversion arises when you receive data as a string that conceptually represents an enum value. Common scenarios include:
- Parsing input parameters: An EA might accept a string input like “BUY_LIMIT” or “H1” for order type or timeframe configuration.
- Processing file data: Reading trading signals or settings stored as text.
- Handling user interface input: Getting a selected item text from a dropdown menu (e.g., representing a symbol or indicator type).
Direct assignment from a string to an enum is not possible. You must implement logic to interpret the string and map it to the correct enum member.
Methods for Converting a String to an Enum
Several techniques can be employed to convert a string representation to an enum value in MQL5. The choice of method often depends on the number of possible enum values and performance considerations.
Using a Switch Statement for String-to-Enum Conversion
A switch statement is a straightforward method, especially when dealing with a relatively small number of enum values. You compare the input string against literals corresponding to the string representations of the enum members.
- Advantages: Simple, easy to read for a small number of cases.
- Disadvantages: Can become lengthy and harder to manage with many enum values. Less efficient than other methods for large enums as it requires multiple string comparisons in the worst case.
ENUM_ORDER_TYPE StringToOrderType(const string sType)
{
if (StringLen(sType) == 0) return WRONG_VALUE; // Handle empty string
if (StringCompare(sType, "BUY", false) == 0) return ORDER_TYPE_BUY;
if (StringCompare(sType, "SELL", false) == 0) return ORDER_TYPE_SELL;
if (StringCompare(sType, "BUY_LIMIT", false) == 0) return ORDER_TYPE_BUY_LIMIT;
if (StringCompare(sType, "SELL_LIMIT", false) == 0) return ORDER_TYPE_SELL_LIMIT;
if (StringCompare(sType, "BUY_STOP", false) == 0) return ORDER_TYPE_BUY_STOP;
if (StringCompare(sType, "SELL_STOP", false) == 0) return ORDER_TYPE_SELL_STOP;
if (StringCompare(sType, "CLOSE_BY", false) == 0) return ORDER_TYPE_CLOSE_BY;
// Return a sentinel value for failure
return WRONG_VALUE;
}
Note: While the example above uses if-else if, a switch statement cannot directly switch on strings. The standard approach mimics a switch using if-else if chains with string comparison functions like StringCompare or StringFind (though StringCompare is generally preferred for exact matches). MQL5 StringCompare is case-sensitive by default; the example uses false for case-insensitivity.
Employing a Lookup Table (Array or Map) for Conversion
For enums with a moderate to large number of members, using a lookup table offers better organization and potentially better performance than a long if-else if chain. An array of structures or a map (CMap) can store pairs of string names and their corresponding enum values.
- Advantages: Scalable, easy to add new enum members. Can be more efficient depending on the lookup mechanism.
- Disadvantages: Requires pre-populating the lookup table.
CMapintroduces object overhead.
Using a dynamic array of structures is a common approach:
struct EnumStringPair
{
string name;
int value; // Or the specific enum type
};
// Pre-populate this array (e.g., in OnInit)
EnumStringPair OrderTypeMap[];
void InitOrderTypeMap()
{
ArrayResize(OrderTypeMap, 7);
OrderTypeMap[0] = {"BUY", ORDER_TYPE_BUY};
OrderTypeMap[1] = {"SELL", ORDER_TYPE_SELL};
OrderTypeMap[2] = {"BUY_LIMIT", ORDER_TYPE_BUY_LIMIT};
OrderTypeMap[3] = {"SELL_LIMIT", ORDER_TYPE_SELL_LIMIT};
OrderTypeMap[4] = {"BUY_STOP", ORDER_TYPE_BUY_STOP};
OrderTypeMap[5] = {"SELL_STOP", ORDER_TYPE_SELL_STOP};
OrderTypeMap[6] = {"CLOSE_BY", ORDER_TYPE_CLOSE_BY};
// Sort the array for faster lookup (e.g., binary search)
// ArraySort(OrderTypeMap, WHOLE_ARRAY, 0, SORT_BY_NAME);
}
ENUM_ORDER_TYPE StringToOrderTypeLookup(const string sType)
{
if (StringLen(sType) == 0) return WRONG_VALUE;
// Assuming OrderTypeMap is initialized and sorted
// Use ArrayBsearch for efficient lookup if sorted
// Example without ArrayBsearch:
for (int i = 0; i < ArraySize(OrderTypeMap); i++)
{
if (StringCompare(sType, OrderTypeMap[i].name, false) == 0)
{
return (ENUM_ORDER_TYPE)OrderTypeMap[i].value;
}
}
return WRONG_VALUE;
}
If using a sorted array and ArrayBsearch, the lookup becomes O(log N), significantly faster than the O(N) linear scan or if-else if chain for large N.
Leveraging String Comparison Functions
As seen in the examples, string comparison functions like StringCompare(string str1, string str2, bool case_sensitive) are essential for matching the input string with known enum names. While StringCompare checks for exact equality (or inequality based on return value), functions like StringFind could potentially be used if partial matching were desired, though this is less common and riskier for direct enum conversion.
It is crucial to decide whether the comparison should be case-sensitive. MQL5 enum names are conventionally uppercase (e.g., ORDER_TYPE_BUY). If the input string might be mixed case, use StringCompare with the false flag for case-insensitive comparison.
Implementation Examples
Let’s walk through practical examples for common MQL5 enums.
Example 1: Converting Order Types (ENUMORDERTYPE)
We already demonstrated the if-else if and lookup table approaches for ENUM_ORDER_TYPE above. Here’s a consolidated function using the if-else if style, which is often sufficient and clear for this enum’s size:
ENUM_ORDER_TYPE ConvertStringToOrderType(const string sOrderType)
{
if (StringLen(sOrderType) == 0) return WRONG_VALUE;
if (StringCompare(sOrderType, "BUY", false) == 0) return ORDER_TYPE_BUY;
else if (StringCompare(sOrderType, "SELL", false) == 0) return ORDER_TYPE_SELL;
else if (StringCompare(sOrderType, "BUY_LIMIT", false) == 0) return ORDER_TYPE_BUY_LIMIT;
else if (StringCompare(sOrderType, "SELL_LIMIT", false) == 0) return ORDER_TYPE_SELL_LIMIT;
else if (StringCompare(sOrderType, "BUY_STOP", false) == 0) return ORDER_TYPE_BUY_STOP;
else if (StringCompare(sOrderType, "SELL_STOP", false) == 0) return ORDER_TYPE_SELL_STOP;
else if (StringCompare(sOrderType, "CLOSE_BY", false) == 0) return ORDER_TYPE_CLOSE_BY;
return WRONG_VALUE; // Indicate failure or unrecognized string
}
Example 2: Converting Timeframes (ENUM_TIMEFRAMES)
ENUM_TIMEFRAMES has more members than ENUM_ORDER_TYPE. While if-else if is still viable, a lookup table becomes more attractive. Here’s an example using a simple array lookup (assuming the map is initialized):
// Assuming TimeframeMap is initialized similarly to OrderTypeMap
// e.g. {{"M1", PERIOD_M1}, {"M5", PERIOD_M5}, ... , {"MN1", PERIOD_MN1}}
ENUM_TIMEFRAMES ConvertStringToTimeframe(const string sTimeframe)
{
if (StringLen(sTimeframe) == 0) return PERIOD_CURRENT; // Or WRONG_VALUE, depending on convention
// If TimeframeMap is sorted, use ArrayBsearch for efficiency
// int index = ArrayBsearch(TimeframeMap, sTimeframe, WHOLE_ARRAY, 0, SORT_BY_NAME);
// Simple linear search example:
for (int i = 0; i < ArraySize(TimeframeMap); i++)
{
if (StringCompare(sTimeframe, TimeframeMap[i].name, false) == 0)
{
return (ENUM_TIMEFRAMES)TimeframeMap[i].value;
}
}
// Return a default or error value
return PERIOD_CURRENT; // Or WRONG_VALUE
}
Note: PERIOD_CURRENT is a valid ENUM_TIMEFRAMES value but often not the intended result of a string conversion unless explicitly requested. WRONG_VALUE (which is -1) or a custom INVALID_TIMEFRAME constant defined within the enum or elsewhere is a better choice for indicating conversion failure.
Code Snippets and Explanation
The code snippets provided illustrate the core logic. Key points:
- Input Validation: Always check if the input string is empty or null (
StringLen(s) == 0orCheckPointer(s) == POINTER_INVALIDif dealing with object pointers, though strings are value types unless passed by reference). An empty string cannot map to a valid enum. - Comparison: Use
StringComparefor matching. The third parameter controls case sensitivity. - Return Value: Define a clear strategy for handling strings that do not match any known enum name. Returning a specific invalid value (like
WRONG_VALUE) or a default value is crucial for error handling in the calling code.
Error Handling and Validation
Robust code requires careful consideration of invalid inputs and potential failures during conversion.
Handling Invalid String Inputs
An input string might not correspond to any valid enum member’s name. Your conversion function must detect this and signal the failure. Common ways to do this include:
- Returning a predefined ‘invalid’ enum value (e.g.,
WRONG_VALUEor a custom value). This is simple but requires the caller to check the returned value. - Returning a boolean indicating success/failure and passing the result via a reference parameter.
bool TryConvertStringToOrderType(const string sOrderType, ENUM_ORDER_TYPE& result)
{
if (StringLen(sOrderType) == 0) return false;
if (StringCompare(sOrderType, "BUY", false) == 0) { result = ORDER_TYPE_BUY; return true; }
else if (StringCompare(sOrderType, "SELL", false) == 0) { result = ORDER_TYPE_SELL; return true; }
// ... other comparisons
return false; // No match found
}
This Try... pattern is common in other languages and provides explicit success/failure status.
Ensuring Enum Value Existence
When defining custom enums or working with built-in ones, ensure that your conversion logic covers all relevant or expected string representations. Missing a case in your if-else if chain or lookup table means that string will never be converted correctly.
For custom enums, consider adding a special INVALID_ or UNKNOWN_ member to the enum itself to serve as a distinct error indicator.
enum CustomSignalType
{
SIGNALNONE = 0,
SIGNALBUY,
SIGNALSELL,
SIGNALUNKNOWN = -1 // Explicit invalid value
};
CustomSignalType StringToCustomSignalType(const string sSignal)
{
if (StringCompare(sSignal, “BUY”, false) == 0) return SIGNALBUY;
if (StringCompare(sSignal, “SELL”, false) == 0) return SIGNALSELL;
return SIGNAL_UNKNOWN;
}
Best Practices for Robust Conversion
- Centralize Conversion Logic: Encapsulate conversion logic within dedicated functions (e.g.,
StringToOrderType,StringToTimeframe). This improves modularity and maintainability. - Handle Case Sensitivity: Be explicit about whether the conversion is case-sensitive or not using
StringCompare‘s third parameter. - Clear Failure Indication: Use a consistent method for indicating conversion failure (return value, reference parameter, logging).
- Log Errors: If a conversion fails, log a warning or error message indicating the problematic input string. This aids debugging.
- Performance vs. Readability: Choose the method (if-else if, lookup table) that best balances performance needs with code readability for the specific enum and expected frequency of conversion.
Conclusion
Converting a string to an enum is a necessary task in MQL5 when receiving configuration or data in text format. While MQL5 doesn’t offer a built-in direct cast, implementing this conversion through logic is straightforward.
Summary of String-to-Enum Conversion Techniques
We discussed two primary approaches:
If-else ifchain: Simple and readable for enums with few members.- Lookup Table (Array/Map): More scalable and potentially faster (especially with sorted arrays and
ArrayBsearch) for enums with many members.
Advantages and Disadvantages of Each Method
| Method | Advantages | Disadvantages | Best For |
| :————– | :——————————— | :—————————————- | :—————————————- |
| If-else if | Simple, easy to implement | Becomes verbose with many cases, O(N) perf | Small enums |
| Lookup Table | Scalable, maintainable, O(log N) possible | Requires initialization, potentially more code | Moderate to large enums |
Further Resources and Learning
To deepen your understanding, explore the MQL5 documentation on:
- Enumerations (enums)
- Strings
- String Functions
- Arrays and Array Functions
- Objects and Classes (for CMap)
Implementing reliable string-to-enum conversion is a step towards building more flexible and user-configurable MQL5 applications.