MQL4: How to Programmatically Change the Chart Symbol?

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/deinit or OnDeinit are 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 using ChartID(). 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 is CHART_SYMBOL.
  • value: The string value to set the property to. For CHART_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:

  1. Creating a graphical button object using ObjectCreate.
  2. Setting button properties like position, size, and text using ObjectSetInteger and ObjectSetString.
  3. Making the button clickable by setting OBJPROP_EXPERT to true (in MQL4; MQL5 is similar but might involve ON_CLICK property or using standard library controls).
  4. Implementing the OnChartEvent function to capture click events.
  5. Checking if the clicked object’s name (sparam) matches our button’s name.
  6. Using ChartSetString(currentChartID, CHART_SYMBOL, newSym) to perform the symbol change.
  7. Printing success or failure messages, including GetLastError().
  8. 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_id is invalid.
  • 4206 (ERRCHARTWRONG_PROPERTY): The prop_id is incorrect (not likely for CHART_SYMBOL).
  • 4006 (ERRINVALIDSYMBOL): The provided value string 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 GlobalVariableSet or 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() and GlobalVariableGet() (or their TerminalInfo equivalents 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, FileClose to read from and write to files (e.g., .txt, .csv) in the terminal’s Files directory. 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.


Leave a Reply