Algorithmic trading in MetaTrader 4 (MT4) often requires dynamic interaction with the charting environment. While most Expert Advisors (EAs) or indicators operate on a single, predefined chart symbol and timeframe, there are scenarios where programmatic modification of the chart’s symbol becomes a necessity. This capability allows for building more flexible tools, such as market scanners, custom trading panels, or utility scripts that need to visualize data for various instruments without manual chart reconfiguration.
This article delves into the methods available in MQL4 (and touching upon MQL5 where relevant) to programmatically change the symbol displayed on a chart. We will explore the core functions, practical implementation examples, and important considerations for integrating this functionality into your trading applications.
Why Programmatically Change Chart Symbols?
The ability to change the chart symbol programmatically offers several advantages for sophisticated MQL development:
- Building Market Scanners: EAs or scripts can iterate through a list of symbols and display charts sequentially or on demand for analysis.
- Creating Custom Trading Panels: Users can select a symbol from a dropdown or input field within a panel, and the linked chart updates instantly.
- Developing Utility Scripts: Tools for data visualization, backtesting preparation, or symbol-specific tasks can automatically configure the target chart.
- Strategy Visualization: An EA might switch symbols to show related markets or perform cross-instrument analysis visualization.
Essentially, it empowers developers to create more interactive and dynamic interfaces beyond the standard chart configuration.
Understanding the Limitations and Considerations
While powerful, programmatic chart manipulation comes with nuances:
- Client Terminal Performance: Rapidly changing symbols on multiple charts can impact terminal performance, especially with complex indicators attached.
- Indicator and EA Re-initialization: Changing a chart’s symbol causes all attached indicators and EAs to re-initialize (
OnInit/deinitorOnDeinitare called). This is a crucial event to handle correctly in your code. - Context: The functions for changing symbols typically operate on a specific chart ID, not globally across the terminal unless explicitly iterated.
- User Experience: Ensure changes are clear to the user, perhaps with visual feedback, to avoid confusion.
- MQL4 vs. MQL5: The core mechanism is similar, relying on chart property manipulation functions, though specific function names or parameters might differ slightly.
The Symbol() Function in MQL4
The Symbol() function is one of the most basic and frequently used functions in MQL4 and MQL5. Its purpose is straightforward:
How to Retrieve the Current Chart Symbol
The Symbol() function returns the string name of the financial instrument currently associated with the chart where the MQL program (EA, indicator, or script) is running.
string currentSymbol = Symbol();
Print("The current chart symbol is: ", currentSymbol);
This function is read-only in the context of the chart it is called upon. It tells you what symbol the chart is currently displaying.
Limitations of Symbol() for Changing Symbols
Crucially, the Symbol() function cannot be used to change the symbol of the chart. It serves purely as a getter for the current symbol’s name. There is no overload or alternative syntax for Symbol() that allows setting a new symbol. Attempting to use it for modification is fundamentally incorrect and will result in compilation errors or unexpected behavior.
To change the symbol, we must interact with the chart’s properties using dedicated chart manipulation functions.
Using ChartSetString() Function to Change Chart Symbol
The primary method for programmatically changing the symbol of a chart in both MQL4 and MQL5 is using the ChartSetString() function.
Syntax and Parameters of ChartSetString()
The ChartSetString() function allows setting various string properties of a chart. Its syntax is as follows:
bool ChartSetString(
long chart_id, // chart ID
int prop_id, // property ID
string value // property value
);
chart_id: The ID of the chart you want to modify. You can get the current chart’s ID usingChartID(). To modify another chart, you would need its specific ID, typically obtained when creating it or iterating through existing charts.prop_id: The identifier of the chart property to set. For changing the symbol, the relevant property isCHART_SYMBOL.value: The string value to set the property to. ForCHART_SYMBOL, this is the string name of the desired financial instrument (e.g., “EURUSD”, “GBPUSD”, “XAUUSD”).
The function returns true if the property was successfully set, false otherwise. A common reason for failure is an invalid chart_id or an invalid value (i.e., a non-existent symbol name).
Practical Example: Changing Symbol on Button Press
Let’s create a simple MQL4 script or EA that adds a button to the chart. Clicking this button will change the chart’s symbol from EURUSD to GBPUSD (or vice versa).
//+------------------------------------------------------------------+
//| SymbolChangeExample.mq4 |
//| Your Name |
//| Link |
//+------------------------------------------------------------------+
#property copyright "Your Name"
#property link "Link"
#property version "1.00"
#property strict
// Button properties
#define BUTTON_NAME "ChangeSymbolButton"
#define BUTTON_X 20
#define BUTTON_Y 50
#define BUTTON_SIZE_X 100
#define BUTTON_SIZE_Y 25
#define BUTTON_TEXT "Change Symbol"
//--- Global variables
long currentChartID;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Get the current chart ID
currentChartID = ChartID();
//--- Create the button object
if(!ObjectCreate(0, BUTTON_NAME, OBJ_BUTTON, 0, 0, 0))
{
Print("Error creating button: ", GetLastError());
return(INIT_FAILED);
}
//--- Set button properties
ObjectSetInteger(0, BUTTON_NAME, OBJPROP_XDISTANCE, BUTTON_X);
ObjectSetInteger(0, BUTTON_NAME, OBJPROP_YDISTANCE, BUTTON_Y);
ObjectSetInteger(0, BUTTON_NAME, OBJPROP_XSIZE, BUTTON_SIZE_X);
ObjectSetInteger(0, BUTTON_NAME, OBJPROP_YSIZE, BUTTON_SIZE_Y);
ObjectSetString(0, BUTTON_NAME, OBJPROP_TEXT, BUTTON_TEXT);
ObjectSetInteger(0, BUTTON_NAME, OBJPROP_FONTSIZE, 8);
ObjectSetInteger(0, BUTTON_NAME, OBJPROP_SELECTABLE, 0); // Not selectable by mouse
ObjectSetInteger(0, BUTTON_NAME, OBJPROP_EXPERT, 1); // Button receives clicks
ChartRedraw(0);
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Delete the button object on removal
ObjectDelete(0, BUTTON_NAME);
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, // Event identifier
const long &lparam, // lparam event parameter
const double &dparam, // dparam event parameter
const string &sparam) // sparam event parameter
{
// Handle object click event
if(id == CHARTEVENT_OBJECT_CLICK)
{
// Check if the clicked object is our button
if(sparam == BUTTON_NAME)
{
Print("Button clicked!");
string currentSym = Symbol();
string newSym = "";
if(currentSym == "EURUSD")
{
newSym = "GBPUSD";
}
else
{
newSym = "EURUSD";
}
// Change the chart symbol
if(ChartSetString(currentChartID, CHART_SYMBOL, newSym))
{
Print("Successfully changed symbol to ", newSym);
}
else
{
Print("Failed to change symbol to ", newSym, ". Error: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
This example demonstrates:
- Creating a graphical button object using
ObjectCreate. - Setting button properties like position, size, and text using
ObjectSetIntegerandObjectSetString. - Making the button clickable by setting
OBJPROP_EXPERTtotrue(in MQL4; MQL5 is similar but might involveON_CLICKproperty or using standard library controls). - Implementing the
OnChartEventfunction to capture click events. - Checking if the clicked object’s name (
sparam) matches our button’s name. - Using
ChartSetString(currentChartID, CHART_SYMBOL, newSym)to perform the symbol change. - Printing success or failure messages, including
GetLastError(). - Cleaning up the object in
OnDeinit.
Handling Errors and Invalid Symbols
As shown in the example, it’s vital to check the return value of ChartSetString(). If it returns false, call GetLastError() to understand the reason for failure. Common errors include:
- 4201 (ERRCHARTWRONG_ID): The
chart_idis invalid. - 4206 (ERRCHARTWRONG_PROPERTY): The
prop_idis incorrect (not likely forCHART_SYMBOL). - 4006 (ERRINVALIDSYMBOL): The provided
valuestring is not a valid symbol name recognized by the terminal.
Before attempting to change the symbol, especially when dealing with user input, it is highly recommended to validate that the target symbol string is valid. You can use MarketInfo(symbol_name, MODE_LOTSIZE) in MQL4 or SymbolInfoString(symbol_name, SYMBOL_BASE) (or other properties) in MQL5. If these functions return non-zero values or true, the symbol likely exists and is recognized.
// Example validation before changing symbol (MQL4)
string targetSymbol = "NONEXISTENTSYMBOL";
if(MarketInfo(targetSymbol, MODE_LOTSIZE) > 0)
{
// Symbol exists, proceed with changing
if(ChartSetString(currentChartID, CHART_SYMBOL, targetSymbol))
{
Print("Successfully changed symbol to ", targetSymbol);
}
else
{
Print("Failed to change symbol to ", targetSymbol, ". Error: ", GetLastError());
}
}
else
{
Print("Error: Symbol '", targetSymbol, "' is invalid or not available.");
}
Using MarketInfo with MODE_LOTSIZE is a common MQL4 trick to check symbol validity without requesting detailed quote information, as MODE_LOTSIZE should be greater than 0 for any tradable symbol.
Implementing Symbol Change with User Input
Changing the symbol based on hardcoded strings is simple, but allowing users to specify the symbol makes the tool much more flexible. This involves adding input mechanisms to your MQL program.
Creating an Input Field for Symbol Selection
For a script or EA that is attached to a chart and intended for one-off use or persistent presence, creating graphical input fields is the most common approach. This involves drawing edit box objects (OBJ_EDIT) on the chart.
An edit box allows the user to type text. You would typically place a button next to it (like the one in the previous example) to trigger the symbol change action after the user has entered the desired symbol name.
// Assuming button creation as before
// ... (button code from previous example) ...
// Edit box properties
#define EDIT_NAME "SymbolEditBox"
#define EDIT_X BUTTON_X + BUTTON_SIZE_X + 10 // Position next to button
#define EDIT_Y BUTTON_Y
#define EDIT_SIZE_X 80
#define EDIT_SIZE_Y BUTTON_SIZE_Y
#define DEFAULT_SYMBOL "GBPUSD"
// In OnInit()
// Create the edit box object
if(!ObjectCreate(0, EDIT_NAME, OBJ_EDIT, 0, 0, 0))
{
Print("Error creating edit box: ", GetLastError());
return(INIT_FAILED);
}
// Set edit box properties
ObjectSetInteger(0, EDIT_NAME, OBJPROP_XDISTANCE, EDIT_X);
ObjectSetInteger(0, EDIT_NAME, OBJPROP_YDISTANCE, EDIT_Y);
ObjectSetInteger(0, EDIT_NAME, OBJPROP_XSIZE, EDIT_SIZE_X);
ObjectSetInteger(0, EDIT_NAME, OBJPROP_YSIZE, EDIT_SIZE_Y);
ObjectSetString(0, EDIT_NAME, OBJPROP_TEXT, DEFAULT_SYMBOL); // Default value
ObjectSetInteger(0, EDIT_NAME, OBJPROP_SELECTABLE, 1); // Make it selectable
ObjectSetInteger(0, EDIT_NAME, OBJPROP_READONLY, 0); // Make it editable
ObjectSetInteger(0, EDIT_NAME, OBJPROP_EXPERT, 1); // Can receive text input/events
ChartRedraw(0);
// In OnDeinit()
// Delete the edit box object
ObjectDelete(0, EDIT_NAME);
// ... (delete button) ...
ChartRedraw(0);
// In OnChartEvent() - Modify the button click handling
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == BUTTON_NAME)
{
// Get text from the edit box
string targetSymbol = ObjectGetString(0, EDIT_NAME, OBJPROP_TEXT);
// Now validate and change symbol...
}
}
// In OnChartEvent() - Optional: Handle text change in edit box (CHARTEVENT_OBJECT_DRAG, CHARTEVENT_OBJECT_ENDEDIT)
// This is more complex and often handled implicitly when the button is clicked.
This adds an input field where the user can type the symbol name before clicking the button.
Validating User Input before Applying Changes
Before passing the user-provided string from the edit box to ChartSetString(), you must validate it. Using the MarketInfo() (MQL4) or SymbolInfoString() (MQL5) check as described earlier is essential to prevent runtime errors caused by attempting to load a non-existent or invalid symbol.
Place the validation logic inside the button click handler, right after retrieving the text from the edit box:
// Inside OnChartEvent() - Button click block
if(id == CHARTEVENT_OBJECT_CLICK && sparam == BUTTON_NAME)
{
string targetSymbol = ObjectGetString(0, EDIT_NAME, OBJPROP_TEXT);
// --- Validation Step ---
if(MarketInfo(targetSymbol, MODE_LOTSIZE) > 0)
{
// Symbol is valid, attempt to change chart symbol
if(ChartSetString(currentChartID, CHART_SYMBOL, targetSymbol))
{
Print("Successfully changed symbol to ", targetSymbol);
}
else
{
// ChartSetString failed for other reasons (e.g., internal error)
Print("Failed to change symbol to ", targetSymbol, ". ChartSetString Error: ", GetLastError());
}
}
else
{
// Symbol is invalid or not available
Print("Error: Invalid or unavailable symbol entered: ", targetSymbol);
// Optionally provide user feedback on the chart itself
}
}
Example: Symbol Changer Panel
Combining the input field and button logic, you can create a simple panel. For a more complete panel, you would typically use more sophisticated object management, perhaps grouping objects or using a dedicated GUI library (though MQL4’s native object capabilities are sufficient for simple panels). The structure remains the same: input field, button, event handler, validation, and ChartSetString.
A more advanced panel might include:
- A dropdown list (
OBJ_COMBO_BOX– MQL5 only, simulate with buttons/labels in MQL4) populated with available symbols (SymbolSelect). - Error messages displayed directly on the chart using text labels (
OBJ_LABEL). - Saving the last used symbol preference using
GlobalVariableSetor file operations.
Implementing a full-featured panel requires careful object management (creation, positioning, event handling for multiple objects), but the core symbol-changing mechanism remains the same: validate the symbol string and call ChartSetString(ChartID(), CHART_SYMBOL, validatedSymbolString);.
Advanced Techniques and Considerations
Beyond changing the symbol on a single chart, you might encounter more complex requirements.
Changing Symbol Across Multiple Charts
To change the symbol on charts other than the one your code is attached to, you need their chart IDs. If you created those charts programmatically using ChartOpen(), this function returns the new chart’s ID. You can store these IDs and later use them with ChartSetString().
// Example: Open a new chart and change its symbol
long newChart = ChartOpen("AUDUSD", PERIOD_H1);
if(newChart != 0 && newChart != INVALID_HANDLE)
{
Print("New chart opened with ID: ", newChart);
// Wait a moment for the chart to fully initialize (optional, but can help)
Sleep(100);
// Now change its symbol
if(ChartSetString(newChart, CHART_SYMBOL, "NZDUSD"))
{
Print("Successfully changed new chart symbol to NZDUSD");
}
else
{
Print("Failed to change new chart symbol. Error: ", GetLastError());
}
}
else
{
Print("Failed to open new chart. Error: ", GetLastError());
}
Iterating through all existing charts in the terminal to change their symbols is possible but less common. You can use functions like ChartFirst() and ChartNext() to traverse chart IDs, but be cautious as this affects the entire terminal state.
Synchronization of Symbol Changes with Other Indicators/Scripts
When you change a chart’s symbol using ChartSetString(), the terminal sends events that cause attached EAs and indicators to re-initialize. Your code in OnInit() (MQL4) or OnChartEvent with CHARTEVENT_INIT (MQL5) will be executed again.
If you have multiple MQL programs running on the same chart and one changes the symbol, all of them will re-initialize. If they need to coordinate or maintain state across symbol changes, you’ll need a synchronization mechanism:
- Global Variables: Use
GlobalVariableSet()andGlobalVariableGet()(or theirTerminalInfoequivalents in MQL5) to share simple state information accessible across different MQL programs and charts. - File I/O: Save and load more complex state data to files.
- Object Properties: Store state within chart objects if the state relates to a specific graphical element.
Consider a scenario where you have an indicator and an EA on the same chart. If the EA changes the symbol, the indicator’s OnInit will run. The indicator might need to check the new symbol name (Symbol()) during its re-initialization and adjust its behavior accordingly (e.g., load symbol-specific settings).
Saving and Restoring Symbol Preferences
For tools like custom panels, users might expect the tool to remember the last symbol they were viewing or a list of preferred symbols. This state can be saved and loaded:
- Input Parameters: For a script or EA, you can use
input string PreferredSymbol = "EURUSD";but this requires manually changing parameters in the properties dialog. - Global Variables: Simple, persistent storage across terminal sessions. Useful for storing one or a few string values.
- File I/O: Use functions like
FileOpen,FileWriteString,FileReadString,FileCloseto read from and write to files (e.g.,.txt,.csv) in the terminal’sFilesdirectory. This is suitable for saving lists of symbols or more complex configuration data.
When the MQL program initializes (OnInit), it can check for saved preferences (e.g., read from a file). If a preference is found, it can use ChartSetString() to set the chart to the user’s preferred symbol automatically.
// Example: Load preferred symbol from a file on initialization
// In OnInit()
string preferredSymbol = "";
int fileHandle = FileOpen("symbol_preference.txt", FILE_READ);
if(fileHandle != INVALID_HANDLE)
{
preferredSymbol = FileReadString(fileHandle);
FileClose(fileHandle);
// Validate and apply loaded preference
if(MarketInfo(preferredSymbol, MODE_LOTSIZE) > 0)
{
if(ChartSetString(ChartID(), CHART_SYMBOL, preferredSymbol))
{
Print("Loaded and set preferred symbol: ", preferredSymbol);
}
else
{
Print("Failed to set preferred symbol: ", preferredSymbol, ". Error: ", GetLastError());
}
}
else
{
Print("Loaded symbol '", preferredSymbol, "' is invalid.");
}
}
else
{
Print("No symbol preference file found or error opening file.");
}
// ... rest of OnInit ...
// Example: Save current symbol to file on deinitialization
// In OnDeinit()
string currentSym = Symbol();
int fileHandle = FileOpen("symbol_preference.txt", FILE_WRITE);
if(fileHandle != INVALID_HANDLE)
{
FileWriteString(fileHandle, currentSym);
FileClose(fileHandle);
Print("Saved current symbol '", currentSym, "' to file.");
}
else
{
Print("Failed to save symbol preference file. Error: ", GetLastError());
}
// ... rest of OnDeinit ...
This approach allows your tools to pick up where the user left off, enhancing usability.
Mastering programmatic chart manipulation using ChartSetString() is a valuable skill for developing advanced MQL applications. By understanding its parameters, handling errors, validating inputs, and considering the broader implications like re-initialization and synchronization, you can build powerful and flexible trading tools.