Created a Multi‑Chart Symbol Synchronization Tool for MT5 (Full Source Code Included)

 

When using multiple MT5 charts side by side, many traders find it annoying to manually match the symbol and timeframe on each chart.

This becomes especially inconvenient when charts are docked tightly together, because the timeframe toolbar at the top can be far away or even hidden.

So I created a tool that lets you switch symbols and timeframes across multiple charts with a single click. I’m also sharing the full source code.


■ What this tool can do

  • Switch symbols on all linked charts at once → Makes it easy to synchronize multiple charts to the same instrument.

  • Switch timeframes instantly on receiver charts → Especially useful in docked layouts (no need to reach for the top toolbar).

  • Assign an ID to each chart to define its role → For example: main chart, monitoring chart, secondary chart, etc.

  • Synchronize charts using global variables → Works entirely with standard MT5 features (no external DLLs).

  • Simple source code that is easy to modify


■ Source Code (full code included)

Sender Side

#property strict
#property indicator_chart_window
#property indicator_plots 0
input string byo = "_S5";
input int mado = 0;

// List of symbols
string symbols[] = {"USDJPY", "EURJPY", "AUDJPY", "EURUSD", "GBPUSD", "AUDUSD", "ETHUSD", "BTCUSD", "GOLD"};

//---------------------------------------------
// Create button
//---------------------------------------------
void CreateButton(string name, int x, int y, string text)
{
   if(ObjectFind(0, name) == -1)
   {
      ObjectCreate(0, name, OBJ_BUTTON, mado, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, name, OBJPROP_XSIZE, 50);
      ObjectSetInteger(0, name, OBJPROP_YSIZE, 18);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 7);
      ObjectSetString (0, name, OBJPROP_TEXT, text);
      
      ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clrDarkSlateGray);
      ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrSilver);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   }
}

int OnInit()
{
   for(int i=0; i<ArraySize(symbols); i++)
   {
      CreateButton("btn_sym_" + symbols[i], 5, 15 + (i * 20), symbols[i]);
   }
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) { ObjectsDeleteAll(0, "btn_sym_"); }

//---------------------------------------------
// Main logic: change this chart and sync others
//---------------------------------------------
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK && StringFind(sparam, "btn_sym_") == 0)
   {
      // 1. Get selected base symbol (e.g. USDJPY)
      string baseName = StringSubstr(sparam, 8);
      
      // 2. Switch this chart (append _S5, fixed to M1)
      string myTarget = baseName + "_S" + byo;
      if(!ChartSetSymbolPeriod(0, myTarget, PERIOD_M1))
      {
         Print("Error: ", myTarget, " not found.");
      }

      // 3. Find receiver charts and synchronize them
      long currChart = ChartFirst();
      while(currChart >= 0)
      {
         // Skip this chart
         if(currChart != ChartID())
         {
            string flagName = "Receiver_" + IntegerToString(currChart);
            if(GlobalVariableCheck(flagName))
            {
               int targetID = (int)GlobalVariableGet(flagName);
               
               // Symbol rule based on ID:
               // ID 1 → second chart (_S5)
               // Others (ID 2 etc.) → normal symbol
               string suffix = (targetID == 1) ? byo : "";
               string syncSymbol = baseName + suffix;
               
               // Keep the existing timeframe of each chart
               ChartSetSymbolPeriod(currChart, syncSymbol, ChartPeriod(currChart));
            }
         }
         currChart = ChartNext(currChart);
      }

      ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
      ChartRedraw();
   }
}

int OnCalculate(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[])
{
   return(rates_total);
}


Receiver Side

#property strict
#property indicator_chart_window
#property indicator_plots 0

//--- Marker setting for identifying this chart ---
input int MyID = 2; // Chart ID (1: second-based chart, 2: minute-based chart, etc.)

string timeFrames[] = {"M1", "M5", "M15", "M30", "H1"};

//---------------------------------------------
// Create button (coordinates based on top-right corner)
//---------------------------------------------
void CreateButton(string name, int x, int y, string text)
{
   if(ObjectFind(0, name) == -1)
   {
      ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, name, OBJPROP_XSIZE, 30);
      ObjectSetInteger(0, name, OBJPROP_YSIZE, 15);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
      ObjectSetString (0, name, OBJPROP_TEXT, text);
      
      ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clrDarkSlateGray);
      ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrSilver);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   }
}

//---------------------------------------------
// Initialization
//---------------------------------------------
int OnInit()
{
   // --- Register this chart's ID as a global variable ---
   string flagName = "Receiver_" + IntegerToString(ChartID());
   GlobalVariableSet(flagName, MyID);

   // Label to display the chart ID (optional)
   ObjectCreate(0, "ID_Label", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "ID_Label", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, "ID_Label", OBJPROP_XDISTANCE, 35);
   ObjectSetInteger(0, "ID_Label", OBJPROP_YDISTANCE, 0);
   ObjectSetInteger(0, "ID_Label", OBJPROP_FONTSIZE, 7);
   ObjectSetInteger(0, "ID_Label", OBJPROP_COLOR, clrGray);
   ObjectSetString(0, "ID_Label", OBJPROP_TEXT, "ID:" + IntegerToString(MyID));

   for(int i=0; i<ArraySize(timeFrames); i++)
   {
      CreateButton("btn_" + timeFrames[i], 35, 15 + (i * 15), timeFrames[i]);
   }
   return(INIT_SUCCEEDED);
}

//---------------------------------------------
// Deinitialization
//---------------------------------------------
void OnDeinit(const int reason)
{
   string flagName = "Receiver_" + IntegerToString(ChartID());
   if(GlobalVariableCheck(flagName)) GlobalVariableDel(flagName);
   
   ObjectsDeleteAll(0, "btn_");
   ObjectDelete(0, "ID_Label");
}

//---------------------------------------------
// Button click handler
//---------------------------------------------
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      if(StringFind(sparam, "btn_") == 0)
      {
         string tfString = StringSubstr(sparam, 4); 
         ENUM_TIMEFRAMES newTF = StringToTimeframe(tfString);
         
         if(newTF != WRONG_VALUE)
         {
            ChartSetSymbolPeriod(0, _Symbol, newTF);
         }

         ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
         ChartRedraw();
      }
   }
}

// Timeframe conversion helper
ENUM_TIMEFRAMES StringToTimeframe(string tf)
{
   if(tf == "M1")  return PERIOD_M1;
   if(tf == "M5")  return PERIOD_M5;
   if(tf == "M15") return PERIOD_M15;
   if(tf == "M30") return PERIOD_M30;
   if(tf == "H1")  return PERIOD_H1;
   if(tf == "H4")  return PERIOD_H4;
   if(tf == "D1")  return PERIOD_D1;
   return WRONG_VALUE;
}

int OnCalculate(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[])
{
   return(rates_total);
}


■ Final Notes

Many traders have ideas for “multi‑chart synchronization tools,” but there are surprisingly few examples that are actually implemented and shared publicly. I hope this helps someone who wants to build or customize their own setup.

If you have any improvement suggestions or ideas for additional features, feel free to let me know.