Синхронизация графиков для удобного технического анализа
Частая проблема при техническом анализе связана с тем, что графические разметки между разными таймфреймами не синхронизированы. Хотя большинство торговых платформ позволяют отображать различные объекты, включая трендовые линии или зоны, на разных таймфреймах при переключении между ними, параллельный анализ нескольких таймфреймов все равно не до конца полноценный. Трейдеры, например, могут открывать несколько окон графиков одного и того же инструмента, устанавливая для каждого свой таймфрейм, чтобы получить более полное представление о рынке. Однако без синхронизации каждый график работает отдельно. Это означает, что любые действия, такие как прокрутка, масштабирование или смена инструмента, приходится повторять в каждом окне. Это приводит к разрозненному анализу, увеличению нагрузки и риску упустить важные сигналы, повторяемые на разных графиках.
Синхронизация графиков решает эту проблему, связывая несколько окон одного и того же инструмента на разных таймфреймах. Такие действия, как прокрутка, масштабирование или смена инструмента, отражаются на всех синхронизированных графиках, что позволяет легко просматривать и сравнивать один и тот же контекст ценового движения на разных временных интервалах. Это позволяет одновременно видеть и сравнивать одно и то же ценовое движение в разных масштабах. В результате аналитический процесс становится более целостным, снижается объем ручной работы, а анализ по нескольким таймфреймам становится более точным и последовательным, что в конечном итоге помогает принимать более быстрые и обоснованные торговые решения.
Планирование и логика индикатора синхронизации графиков
Основная цель
Создать систему синхронизации графиков с несколькими таймфреймами, при которой объекты, нарисованные на старших таймфреймах, автоматически отображаются на младших с сохранением иерархии и стиля.
1. Ключевые требования к функциональности:
- Выбор базового таймфрейма — пользователь задает основной таймфрейм через входной параметр, остальные графики синхронизируются с тем же инструментом, но с другими таймфреймами.
- Иерархия таймфреймов:
PERIOD_H1 (Highest) ↓ PERIOD_M30 ↓ PERIOD_M15 ↓ PERIOD_M5 (Lowest)
- Правила синхронизации объектов — объекты, созданные на любом таймфрейме, отображаются на всех более младших (автоматическое распространение вниз). Например, объект с H1 будет виден на M30, M15 и M5.
- Функция сброса — очищает все синхронизированные графики (не затрагивая сам ценовой график).
2. Архитектура системы:

3. Логика работы:

4. Логика синхронизации

5. Процесс разработки:
- Инициализация графиков — основной график устанавливается на выбранный пользователем таймфрейм. Остальные графики получают таймфреймы H1/M30/M15/M5, их ID сохраняются для группы синхронизации.
- Отслеживание объектов — каждому объекту присваивается метка с указанием исходного таймфрейма, а также создаются идентификаторы копий в формате [OriginalName]_clone_[ChartID].
- Управление иерархией — поддерживается список приоритетов таймфреймов.
const ENUM_TIMEFRAMES timeframes[4] = {PERIOD_H1, PERIOD_M30, PERIOD_M15, PERIOD_M5};
- Сброс — удаляет все объекты, кроме элементов ценового графика и кнопки сброса.
Начало работы
//+------------------------------------------------------------------+ //| ChartSyncIndicator.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "GIT under MetaQuotes Ltd." #property version "1.01" #property indicator_chart_window #property indicator_plots 0 input ENUM_TIMEFRAMES BaseTimeframe = PERIOD_H1; // Base timeframe for objects input bool SyncAllTimeframes = false; input color DefaultObjColor = clrDodgerBlue; input ENUM_LINE_STYLE DefaultObjStyle = STYLE_SOLID; input int DefaultObjWidth = 2; input bool DefaultObjBack = true; input bool SyncUnlockedObjects = false; input int ResetButtonX = 10; input int ResetButtonY = 20;
Сначала зададим настраиваемые пользователем параметры индикатора синхронизации. Параметр BaseTimeFrame позволяет выбрать основной таймфрейм (например, H1), с которого графические объекты будут синхронизироваться на другие графики. Параметр SyncAllTimeFrames типа bool определяет, синхронизировать ли объекты со всеми таймфреймами или только с выбранными. Визуальные свойства объектов, такие как цвет (DefaultObjColor), стиль линии (DefaultObjStyle) и ее толщину (DefaultObjWidth), можно настроить для удобства отображения. Параметр DefaultObjBack определяет отображение объектов на заднем плане графика.
Параметр SyncUnlockedObjects определяет, будем ли синхронизировать только разблокированные объекты. Это позволяет управлять редактируемыми элементами. Наконец, параметры ResetButtonX и ResetButtonY определяют положение кнопки сброса на экране, с помощью которой можно очистить синхронизированные объекты.
#define OBJ_ORIGIN_TF "OBJ_ORIGIN_TF_" #define RESET_BUTTON "resetBtn" long baseChartID; string baseSymbol; string timeframesAssigned[]; ENUM_TIMEFRAMES availableTFs[4] = {PERIOD_H1, PERIOD_M30, PERIOD_M15, PERIOD_M5};Далее задаются базовые константы и переменные системы. С помощью #define создаем строковые константы OBJ_ORIGIN_TF_ (используется как префикс для указания исходного таймфрейма объекта) и resetBtn (кнопка сброса на графике). Переменная baseChartID хранит идентификатор основного графика, используемого как точка синхронизации. В baseSymbol указан торговый инструмент этого графика. Массив timeframesAssigned динамический, он хранит идентификаторы графиков, выбранных для синхронизации. Массив availableTFs фиксированный, в него включены четыре таймфрейма: H1, M30, M15 и M5.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { baseChartID = ChartID(); baseSymbol = Symbol(); // Set this chart to user-selected base timeframe if(Period() != BaseTimeframe) { ChartSetSymbolPeriod(baseChartID, baseSymbol, BaseTimeframe); } AssignTimeframesToCharts(); CreateResetButtons(); return INIT_SUCCEEDED; }
При загрузке индикатора сначала определяется ID текущего графика (baseChartID) и символ (baseSymbol). Затем проверяется, совпадает ли текущий таймфрейм с заданным BaseTimeFrame. Если нет, график автоматически переключается на базовый таймфрейм с помощью функции ChartSetSymbolPeriod(). После того, как установлен нужный таймфрейм, вызываем функции AssignTimeFramesToChart() (для назначения и отслеживания графиков) и CreateResetButtons() (для создания на графике кнопки сброса).
void AssignTimeframesToCharts() { // Create a list of timeframes excluding the base TF ENUM_TIMEFRAMES timeframes[3]; int index = 0; for(int i = 0; i < 4; i++) { if(availableTFs[i] != BaseTimeframe) { timeframes[index] = availableTFs[i]; index++; } } int tfIndex = 0; long currChart = ChartFirst(); ArrayResize(timeframesAssigned, 0); while(currChart != -1 && tfIndex < ArraySize(timeframes)) { if(currChart != baseChartID) { ChartSetSymbolPeriod(currChart, baseSymbol, timeframes[tfIndex]); int size = ArraySize(timeframesAssigned); ArrayResize(timeframesAssigned, size + 1); timeframesAssigned[size] = (string)currChart; tfIndex++; } currChart = ChartNext(currChart); } }
Функция назначения таймфреймов формирует список всех таймфреймов для синхронизации. Она включает в список все таймфреймы, кроме базового. Сначала функция создает временный массив timeframes для хранения всех дополнительных таймфреймов из предопределенного списка availableTFs. Она перебирает доступные значения в массиве availableTFs и добавляет в массив timeframes все таймфреймы, которые отличаются от выбранного пользователем базового BaseTimeFrame. Это гарантирует, что все таймфреймы, кроме базового (например, M30, M15, M5, если H1 является базовым) будут назначены отдельным окнам графика для синхронизации.
Затем происходит перебор всех открытых графиков через ChartFirst() и ChartNext(), чтобы найти нужные для синхронизации. Базовый график пропускается (на основе идентификатора графика baseChartID), а остальные получают новые таймфреймы с помощью функции ChartSetSymbolPeriod(). По мере того, как графикам присваивается таймфрейм, идентификатор такого графика преобразуется в строковое значение и сохраняется в массиве timeframesAssigend, который отслеживает, какие графики участвуют в синхронизации. Цикл продолжается, пока не будут назначены все таймфреймы или не закончатся доступные окна.
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { switch(id) { case CHARTEVENT_OBJECT_CREATE: case CHARTEVENT_OBJECT_DRAG: PropagateObject(sparam, (ENUM_TIMEFRAMES)Period()); break; case CHARTEVENT_OBJECT_DELETE: DeleteSyncedInstances(sparam); break; case CHARTEVENT_OBJECT_CLICK: if(sparam == RESET_BUTTON) { DeleteAllSyncedObjects(); } break; } }
Функция OnChartEvent() обрабатывает действия, которые пользователь совершает в отношении объектов графика, и реагирует соответствующим образом, чтобы сохранить синхронизацию. При создании или перемещении объекта (OBJECT_CREATE или OBJECT_DRAG) вызывается функция PropagateObject(), которая дублирует или обновляет объект на всех синхронизируемых графиках. При удалении объекта (OBJECT_DELETE) функция DeleteSyncedInstances() удаляет копии на других графиках. Когда пользователь нажимает на кнопку сброса (OBJECT_CLICK, sparam == RESET_BUTTON), срабатывает функция DeleteAllSyncedObjects(), которая очищает все синхронизированные объекты. Эта событийно-ориентированная логика гарантирует, что любые изменения, внесенные в объекты графика, мгновенно отражаются на всех нужных таймфреймах.
void PropagateObject(string objName, ENUM_TIMEFRAMES srcTF) { if(StringFind(objName, "_clone_") != -1) return; bool isLocked = ObjectGetInteger(ChartID(), objName, OBJPROP_SELECTABLE); if(!SyncUnlockedObjects && !isLocked) return; for(int i = 0; i < ArraySize(timeframesAssigned); i++) { long targetChart = (long)StringToInteger(timeframesAssigned[i]); ENUM_TIMEFRAMES targetTF = (ENUM_TIMEFRAMES)ChartPeriod(targetChart); bool shouldPropagate = false; if(SyncAllTimeframes) { shouldPropagate = true; } else { // Propagate from base timeframe to all others if(srcTF == BaseTimeframe) { shouldPropagate = true; } // Propagate from other timeframes only to lower timeframes else if(srcTF > targetTF) { shouldPropagate = true; } } if(shouldPropagate) { CloneObject(objName, ChartID(), targetChart); } } }
Функция PropagateObject() отвечает за копирование объектов (например, линий тренда и прочих фигур) на другие таймфреймы. Сначала она исключает объекты-клоны (по наличию "clone" в имени), чтобы избежать рекурсии. Затем проверяет, доступен ли объект для выделения. Если параметр SyncUnlockedObjects установлен в значение false и объект невыделяемый, функция завершает работу. Это предотвращает случайную синхронизацию неинтерактивных объектов, которые могут не предназначаться для репликации.
Основная часть функции перебирает все назначенные графики, хранящиеся в массиве timeframesAsigned. Для каждого графика она оценивает, нужно ли применять объект в соответствии с текущими правилами синхронизации. Если включен параметр SyncAllTimeframes, объект копируется на все графики независимо от источника и целевого таймфрейма. В противном случае используется логическое правило: если объект происходит из базового таймфрейма (BaseTimeframe), он распространяется на все остальные таймфреймы. Если он найден на более старшем таймфрейме (например, H1), а целевой таймфрейм младше (например, M15), он также копируется. После выполнения этих условий вызывается функция CloneObject() для копирования объекта с исходного графика на целевой график — так получается визуальная синхронизация.
void CloneObject(string objName, long srcChart, long targetChart) { int type = (int)ObjectGetInteger(srcChart, objName, OBJPROP_TYPE); string newName = objName + "_clone_" + (string)targetChart; if(!ObjectCreate(targetChart, newName, (ENUM_OBJECT)type, 0, 0, 0)) return; datetime time1 = (datetime)ObjectGetInteger(srcChart, objName, OBJPROP_TIME, 0); double price1 = ObjectGetDouble(srcChart, objName, OBJPROP_PRICE, 0); ObjectSetInteger(targetChart, newName, OBJPROP_TIME, 0, time1); ObjectSetDouble(targetChart, newName, OBJPROP_PRICE, 0, price1); datetime time2 = (datetime)ObjectGetInteger(srcChart, objName, OBJPROP_TIME, 1); double price2 = ObjectGetDouble(srcChart, objName, OBJPROP_PRICE, 1); if(time2 > 0 || price2 > 0) { ObjectSetInteger(targetChart, newName, OBJPROP_TIME, 1, time2); ObjectSetDouble(targetChart, newName, OBJPROP_PRICE, 1, price2); } color objColor = ObjectGetInteger(srcChart, objName, OBJPROP_COLOR, 0); ENUM_LINE_STYLE objStyle = (ENUM_LINE_STYLE)ObjectGetInteger(srcChart, objName, OBJPROP_STYLE, 0); int objWidth = ObjectGetInteger(srcChart, objName, OBJPROP_WIDTH, 0); bool objBack = ObjectGetInteger(srcChart, objName, OBJPROP_BACK, 0); ObjectSetInteger(targetChart, newName, OBJPROP_COLOR, objColor != clrNONE ? objColor : DefaultObjColor); ObjectSetInteger(targetChart, newName, OBJPROP_STYLE, objStyle); ObjectSetInteger(targetChart, newName, OBJPROP_WIDTH, objWidth); ObjectSetInteger(targetChart, newName, OBJPROP_BACK, objBack); ObjectSetInteger(targetChart, newName, OBJPROP_SELECTABLE, false); ObjectSetInteger(targetChart, newName, OBJPROP_HIDDEN, true); string originTag = OBJ_ORIGIN_TF + EnumToString((ENUM_TIMEFRAMES)Period()); ObjectSetString(targetChart, newName, OBJPROP_TOOLTIP, originTag); if(type == OBJ_TEXT || type == OBJ_LABEL) { string txt = ObjectGetString(srcChart, objName, OBJPROP_TEXT); ObjectSetString(targetChart, newName, OBJPROP_TEXT, txt); } if(type == OBJ_FIBO) { int levels = (int)ObjectGetInteger(srcChart, objName, OBJPROP_LEVELS); ObjectSetInteger(targetChart, newName, OBJPROP_LEVELS, levels); for(int i = 0; i < levels; i++) { double level = ObjectGetDouble(srcChart, objName, OBJPROP_LEVELVALUE, i); ObjectSetDouble(targetChart, newName, OBJPROP_LEVELVALUE, i, level); } } }
Функция CloneObject() создает копию объекта с исходного графика (srcChart) на целевом (targetChart). Сначала она определяет тип объекта (например, линия тренда, прямоугольник или уровни Фибоначчи) и генерирует новое уникальное имя, добавляя к исходному имени слова _clone_ и идентификатора целевого графика. Если не удается создать объект на целевом графике с помощью функции ObjectCreate(), функция завершает работу досрочно. Затем она копирует первую координату (время и цену) из исходного объекта и присваивает ее той же позиции на клонированном объекте на целевом графике. Если есть вторая точка (как, например, в каналах и линиях тренда), она также переносится.
Далее функция копирует визуальные свойства в свойства клонированного объекта. К ним относятся цвет, стиль (например, сплошная или пунктирная линия), толщина и фон (OBJPROP_BACK). Клонированный объект становится недоступным для выбора (OBJPROP_SELECTABLE = false) и скрывается из списка объектов (OBJPROP_HIDDEN = true), чтобы предотвратить случайные изменения и уменьшить беспорядок. Кроме того, с помощью OBJPROP_TOOLTIP добавляется всплывающая подсказка, указывающая на исходный таймфрейм. Это позволяет определить происхождение объекта, особенно при работе с несколькими синхронизированными графиками.
Наконец, функция обрабатывает специальные типы объектов. Если объект представляет собой текстовую метку или простую метку, то отображаемое текстовое содержимое переносится в клон. Если это объект Фибоначчи (OBJ_FIBO), функция копирует количество уровней и соответствующие им ценовые уровни. Это позволяет постоянно поддерживать функциональную и визуальную синхронизированность между всеми графиками.
//+------------------------------------------------------------------+ //| Required OnCalculate function | //+------------------------------------------------------------------+ 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); }
Основная логика индикатора не зависит от функции OnCalculate(), поскольку он работает исключительно за счет событий графика и объектов. Однако функция OnCalculate() по-прежнему необходима во всех пользовательских индикаторах, а ее отсутствие приводит к ошибке компиляции. Поэтому добавлен минимальный код-заглушка, который просто возвращает rates_total без выполнения каких-либо вычислений.
void DeleteSyncedInstances(string objName) { long thisChart = ChartID(); bool isClone = StringFind(objName, "_clone_") != -1; string baseName = isClone ? StringSubstr(objName, 0, StringFind(objName, "_clone_")) : objName; // Delete from all charts ObjectDelete(thisChart, objName); for(int i = 0; i < ArraySize(timeframesAssigned); i++) { long chartID = (long)StringToInteger(timeframesAssigned[i]); ObjectDelete(chartID, baseName); ObjectDelete(chartID, baseName + "_clone_" + (string)thisChart); } }
Функция DeleteSyncedInstances() при удалении графического объекта с одного графика удаляет все синхронизированные аналоги на других графиках. Сначала она определяет, является ли удаленный объект клоном (наличие в его имени слова "clone"), и соответственно извлекает исходное базовое имя. Затем функция удаляет объект с текущего графика и перебирает все графики, перечисленные в timeframesAssigned, удаляя как базовый объект, так и любой связанный с ним клон.
void DeleteAllSyncedObjects() { long chartIDs[]; ArrayResize(chartIDs, ArraySize(timeframesAssigned) + 1); chartIDs[0] = baseChartID; for(int i = 0; i < ArraySize(timeframesAssigned); i++) chartIDs[i + 1] = (long)StringToInteger(timeframesAssigned[i]); for(int j = 0; j < ArraySize(chartIDs); j++) { int total = ObjectsTotal(chartIDs[j]); for(int i = total - 1; i >= 0; i--) { string name = ObjectName(chartIDs[j], i); if(StringFind(name, RESET_BUTTON) == -1) ObjectDelete(chartIDs[j], name); } } }
Функция DeleteAllSyncedObjects() удаляет все графические объекты на всех синхронизированных графиках, за исключением самой кнопки сброса. Сначала создается массив chartIDs, содержащий базовый идентификатор графика и все идентификаторы графиков из списка timeframesAsigned. Затем для каждого из этих графиков функция перебирает все существующие объекты в обратном порядке и удаляет их, пропуская объект, который в названии содержит RESET_BUTTON. Эта функция позволяет быстро очистить рабочее пространство от всех синхронизированных построений.
void CreateResetButtons() { CreateResetButtonOnChart(baseChartID); for(int i=0; i<ArraySize(timeframesAssigned); i++) CreateResetButtonOnChart((long)StringToInteger(timeframesAssigned[i])); }Функция CreateResetButtons() добавляет кнопку сброса на базовый график и все синхронизированные графики. Она в цикле проходит по графикам и вызывает функцию CreateResetButtonOnChart(), позволяя пользователям одним щелчком мыши удалить все синхронизированные объекты с базового графика.
void CreateResetButtonOnChart(long cid) { if(ObjectFind(cid, RESET_BUTTON) >= 0) ObjectDelete(cid, RESET_BUTTON); if(!ObjectCreate(cid, RESET_BUTTON, OBJ_BUTTON, 0, 0, 0)) return; ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_XDISTANCE, ResetButtonX); ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_YDISTANCE, ResetButtonY); ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_XSIZE, 80); ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_YSIZE, 20); ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_BGCOLOR, clrGray); ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_COLOR, clrWhite); ObjectSetString(cid, RESET_BUTTON, OBJPROP_TEXT, "Reset All"); ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_SELECTABLE, false); ObjectSetInteger(cid, RESET_BUTTON, OBJPROP_HIDDEN, false); ChartRedraw(cid); }
Функция CreateResetButtonOnChart() создает кнопку для сброса на указанном графике (cid). Сначала она проверяет, существует ли уже кнопка с таким же именем. Если да, удаляет ее во избежание дублирования. Затем создается новая кнопка (OBJ_BUTTON), и с помощью функций ResetButtonX и ResetButtonY задается ее положение, а также фиксированные размеры и стиль для цвета фона, цвета текста и подписи. Кнопка сделана невыбираемой, но видимой (HIDDEN = false), чтобы пользователи могли с ней взаимодействовать.
void OnDeinit(const int reason) { DeleteAllSyncedObjects(); }Наконец, функция OnDeinit() вызывается при удалении индикатора или закрытии графика. Она очищает все синхронизированные объекты, не оставляя лишних элементов.
Демонстрация:

Заключение
Мы разработали индикатор синхронизации графиков для нескольких таймфреймов, который упрощает технический анализ за счет автоматического дублирования графических объектов — таких как трендовые линии, прямоугольники и уровни Фибоначчи — на выбранных таймфреймах. Мы начали работу с настройки параметров, позволяющих управлять стилем объектов, логикой синхронизации и элементами интерфейса, включая кнопку сброса. Были реализованы ключевые функции: распределение таймфреймов по окнам графиков, отслеживание и обработка событий объектов (создание, перемещение, удаление), а также поддержание корректной синхронизации через механизм клонирования и единообразное именование объектов. Дополнительные вспомогательные функции отвечают за создание кнопки сброса и полную очистку объектов при деинициализации или по команде пользователя.
Так мы получили удобный инструмент для анализа сразу на нескольких таймфреймах без необходимости вручную перерисовывать и поддерживать объекты на разных графиках. Он обеспечивает согласованность и наглядность при работе с разными временными масштабами одного инструмента, упрощает рабочий процесс и снижает когнитивную нагрузку. Благодаря синхронизации объектов и плавному взаимодействию между таймфреймами трейдеры могут легче находить подтверждения сигналов, отслеживать ключевые уровни и принимать более обоснованные торговые решения.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18937
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Разработка торговой стратегии: Стратегия следования за трендом на основе Индекса цветочной волатильности
Основы байесовского вывода в дискретном и непрерывном случаях: от теории к практической реализации моделей
Автоматизация греков Блэка-Шоулза: Расширенный скальпинг и микроструктурная торговля
Алгоритм оптимизации быков — Bull Optimization Algorithm (BOA)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования