//+------------------------------------------------------------------+
//| RM Sync Master - Chart Synchronization                           |
//| Copyright 2026, RM Trading Lab                                   |
//| https://www.mql5.com/en/users/rmtradinglab                       |
//+------------------------------------------------------------------+
#property copyright "© 2026 RM Trading Lab"
#property link      "https://www.mql5.com/en/users/rmtradinglab"
#property version   "1.00"

#property strict
#property indicator_chart_window
#property indicator_plots 0

//--- Synchronization line object name
string SyncLineName = "SyncTimeLine";

//+------------------------------------------------------------------+
//| Indicator calculation                                            |
//+------------------------------------------------------------------+
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[])
  {
//--- No buffer calculation is required for this indicator
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Check for a keyboard event
   if(id == CHARTEVENT_KEYDOWN)
     {
      int key = (int)lparam;
      //--- Synchronize charts when the 'V' key is pressed
      if(key == 'V' || key == 'v')
        {
         //--- Get the market time at the first visible bar
         long first_visible = ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
         datetime syncTime = iTime(_Symbol, PERIOD_CURRENT, (int)first_visible - 1);

         SyncCharts_V(syncTime);
        }
     }
  }

//+------------------------------------------------------------------+
//| Synchronize charts using a vertical line                         |
//+------------------------------------------------------------------+
void SyncCharts_V(datetime lineTime)
  {
//--- Start iterating through all open charts
   long chart_id = ChartFirst();
   while(chart_id >= 0)
     {
      //--- Process charts of the same symbol when the filter is enabled
      if(ChartSymbol(chart_id) == _Symbol)
        {
         //--- Create the synchronization line if it does not exist
         if(ObjectFind(chart_id, SyncLineName) < 0)
           {
            ObjectCreate(chart_id, SyncLineName, OBJ_VLINE, 0, lineTime, 0);
            ObjectSetInteger(chart_id, SyncLineName, OBJPROP_COLOR, clrRed);
            ObjectSetInteger(chart_id, SyncLineName, OBJPROP_STYLE, STYLE_DOT);
            ObjectSetInteger(chart_id, SyncLineName, OBJPROP_BACK, false);
            ObjectSetInteger(chart_id, SyncLineName, OBJPROP_SELECTABLE, true);
           }
         else
           {
            //--- Update the existing synchronization line
            ObjectSetInteger(chart_id, SyncLineName, OBJPROP_TIME, (long)lineTime);
           }
         SetChartOnTime(chart_id, lineTime);
        }

      //--- Move to the next open chart
      chart_id = ChartNext(chart_id);
     }
  }

//+------------------------------------------------------------------+
//| Navigate a chart to the specified market time                    |
//+------------------------------------------------------------------+
void SetChartOnTime(long chart_id, datetime syncTime)
  {
//--- Process only charts with the same symbol
   if(ChartSymbol(chart_id) == _Symbol)
     {
      string symbol = ChartSymbol(chart_id);

      //--- Disable automatic scrolling before manual navigation
      ChartSetInteger(chart_id, CHART_AUTOSCROLL, false);
      ChartNavigate(chart_id, CHART_END, 0);
      ChartRedraw(chart_id);

      //--- Get the target chart timeframe
      ENUM_TIMEFRAMES timeframe = (ENUM_TIMEFRAMES)ChartPeriod(chart_id);

      //--- Convert the synchronization time to a bar index
      long shift = iBarShift(symbol, timeframe, syncTime, false);

      //--- Calculate the navigation position using the visible bar count
      long visible_bars = ChartGetInteger(chart_id, CHART_VISIBLE_BARS);
      long desired_bar = -(shift - visible_bars + 1);

      //--- Navigate the chart to the corresponding market position
      ChartNavigate(chart_id, CHART_END, (int)desired_bar - 1);
      ChartRedraw(chart_id);
     }
  }
//+------------------------------------------------------------------+
