От новичка до эксперта: Периоды на рынке Форекс
Содержание
Введение
Однажды в своем городе я нашел время понаблюдать за тем, как ежедневно работает местный овощной рынок, и то, что я обнаружил, было весьма поучительным. Каждое утро фермеры рано прибывают со своими свежими продуктами, а розничные торговцы приходят так же рано, чтобы заключить выгодные оптовые сделки. В течение первых нескольких часов после открытия рынка активность достигает своего пика — сделки совершаются быстро, а цены часто динамично меняются. По мере того как день приближается к полудню и обеду, темп постепенно замедляется; появляется все меньше розничных продавцов и многие фермеры начинают собираться. Тем не менее, с приближением вечера и времени закрытия рынка активность вновь возрастает, и припозднившиеся покупатели и продавцы делают свои последние шаги перед заходом солнца.
Это простое рыночное поведение поразительно отражает то, как функционирует рынок Форекс. Точно так же, как фермеры и розничные торговцы, трейдеры по всему миру работают в рамках отдельных сессий — периодов высокой и низкой активности, которые определяют ритм рынка. Бывают пиковые часы, когда совершаются крупные сделки, определяющие характер и динамику торгового дня.
Сегодняшняя задача состоит в том, чтобы использовать MQL5 для визуализации этих периодов рыночных сессий, органично совмещая их со структурами старших и младших таймфреймов. Синхронизируя открытие и закрытие этих сессий, мы стремимся лучше понять, как каждая из них влияет на следующую и как время действительно влияет на движение цены. Еще одна захватывающая тайна, которую предстоит раскрыть, заключается в осознании того, что сами финансовые свечи представляют собой временные бары, обычно обозначающие часовые, дневные или недельные периоды. Применяя ту же концепцию к рыночным сессиям, мы можем создавать свечи на основе сессий, каждая из которых отражает уникальные характеристики данного торгового окна. Как и в случае с традиционными таймфреймами, каждая сессия имеет свою собственную структуру открытия, максимума, минимума и закрытия (OHLC), рассказывающую об активности, настроениях и волатильности в течение этого периода.
Однако наиболее заметным отличием является то, что сессионные бары часто перекрываются, особенно для глобальных рынков, которые работают в одно и то же время. Например, Европейская и Американская сессии (рис.1) пересекаются в течение нескольких часов, создавая периоды повышенной волатильности и ликвидности. В отличие от стандартных баров таймфрейма, которые последовательно следуют один за другим, сессионные бары сосуществуют во времени, отражая одновременное участие нескольких регионов. Именно это наложение приводит к динамичным изменениям настроений на рынке, что делает анализ на основе сессий важным дополнением к традиционным основанным на времени структурам.

Рис.1. Концептуальное наложение между Европейской и Американской торговыми сессиями
Благодаря этой разработке мы стремимся визуализировать сессионные бары на том же аналитическом языке, что и стандартные свечи, давая трейдерам возможность распознавать характерное для конкретной сессии поведение, определять, где наблюдаются пики ликвидности, и сравнивать, как настроение одной сессии перетекает в следующую. В конечном счете, эта функция обеспечивает взаимопонимание между традиционным анализом таймфреймов и скрытым ритмом рыночных сессий, способствуя более глубокой связи между временем, структурой и динамикой цен.
Чтобы воплотить эту идею в жизнь, расскажем о полной реализации ниже — подробно объясняя лежащую в основе концепцию, а затем представим полный код на MQL5 в следующем разделе настоящего обсуждения.
Реализация
Наше исследование рыночных периодов продолжает развиваться, поскольку мы внедряем еще одну мощную функцию, которая объединяет традиционный анализ таймфреймов с динамикой глобальных торговых сессий на рынке Форекс. Это усовершенствование устраняет разрыв между структурированными временными периодами и поведением на реальных сессиях, предлагая более реалистичное представление о том, как рынки дышат в течение дня. Для реализации этой новой функции, мы сначала разработаем и протестируем ее изолированно, чтобы убедиться в ее точности и производительности, прежде чем интегрировать в существующий индикатор Market Periods Synchronizer. Результатом станет более продвинутая и содержательная версия инструмента, которая идеально согласует как временную структуру, так и зависящее от сессии поведение рынка.
Еще одна причина изолированной реализации этой функции заключается в том, чтобы каждый читатель мог полностью понять концепцию до начала интеграции. Это также служит хорошей практикой разработки, особенно по мере того, как программа усложняется с каждой новой функцией. Изолируя компоненты, мы упрощаем тестирование, отладку и понимание системы — как для обучающихся, так и для разработчиков, расширяющих инструмент в будущих версиях.
Чтобы воплотить эту концепцию в жизнь, выполним двухэтапный рабочий процесс: сначала разработаем изолированную функцию, а затем интегрируем проверенный компонент в советник Market Periods Synchronizer. Такой подход гарантирует, что логика остается модульной, простой в тестировании и адаптируемой для будущих усовершенствований.
- Шаг 1 — Разработка в изоляции: Начинаем с создания отдельного класса CSessionVisualizer, отвечающего за определение торговых сессий, вычисление их значений OHLC и отображение их в виде заполненных прямоугольников, имитирующих свечи (опционально включая тени и метки). Эту автономную версию следует протестировать независимо — например, путем построения графиков сессий на графике EURUSD H1 — для проверки точности границ сессий и визуализации данных.
- Шаг 2 — Этап интеграции: После проверки визуализатор сессии может быть интегрирован в основной советник. Это включает в себя добавление новых элементов управления пользовательским интерфейсом, таких как переключатель “Показывать сессии: ВКЛ. / ВЫКЛ.”, средство выбора цвета сессии и параметр просмотра, позволяющий определить, сколько исторических сессий следует отображать. Функция RefreshLines() будет расширена для вызова визуализатора сеанса после рисования основных и второстепенных линий, обеспечивая синхронизацию всех элементов. Общие глобальные переменные (например, g_Lookback) будут поддерживать согласованность, в то время как визуальные перекрытия между сессиями будут управляться с использованием полупрозрачных уровней для наглядности.
Шаг 1 — Разработка в изоляции
В этом разделе мы создадим модульный класс под названием CSessionVisualizer, предназначенный для обнаружения, расчета и визуализации сессий рынка Форекс на графике — в комплекте с элементами в виде свечей, дополнительными тенями и метками. Этот подход подчеркивает модульность, удобочитаемость и возможность повторного использования, что позволяет впоследствии интегрировать эту функцию в более крупные платформы, такие как советник Market Periods Synchronizer.
1. Установка и Назначение Заголовка
Каждый хороший модуль на MQL5 начинается с соответствующего заголовка, который четко определяет, что это за скрипт, кто его написал и для чего он предназначен. Это помогает другим пользователям понять ваш код и обеспечивает соблюдение правил разработки для контроля версий.
Также определяем небольшой, но мощный макрос ARGB, позволяющий легко создавать цвета с прозрачностью, что важно для смешивания нескольких визуальных слоев (например, наложения сессий на графике).
//+------------------------------------------------------------------+ //| SessionVisualizerTest.mqh | //| Modular class for Forex session OHLC visualization | //| Author: Clemence Benjamin | //| Version: 1.00 | //+------------------------------------------------------------------+ #property strict // ARGB Macro: Creates a color with alpha (transparency) #define ARGB(a,r,g,b) ((color)(((uchar)(a))<<24)|(((uchar)(r))<<16)|(((uchar)(g))<<8)|((uchar)(b))) #include <Object.mqh> // Provides access to chart object manipulation
Здесь мы используем Object.mqh, потому что визуализатор создает несколько объектов графика (прямоугольники, надписи, линии) и управляет ими. Макрос ARGB() позволяет создавать полупрозрачные цвета, такие как ARGB(120,255,0,0), для прозрачной красной заливки, что очень важно для визуального наложения слоев. Но позже мы удалим ARGB, чтобы использовать макрос из CCanvas.
2. Определения Сессий и Настройка Структуры
Прежде чем что-либо рисовать, необходимо определить, что означает “сессия” в нашем контексте. Каждая валютная сессия — в Сиднее, Токио, Лондоне и Нью—Йорке - имеет свои уникальные часы открытия и закрытия. Мы используем перечисление enum для их символического определения и структуру для хранения свойств каждой сессии, таких как её название, время, цвет и то, активна ли она в данный момент.
enum SESSION_TYPE { SESSION_SYDNEY = 0, // 22:00-07:00 GMT SESSION_TOKYO = 1, // 00:00-09:00 GMT SESSION_LONDON = 2, // 08:00-17:00 GMT SESSION_NEWYORK = 3 // 13:00-22:00 GMT }; struct SessionInfo { SESSION_TYPE type; string name; // Short code e.g., "LON" int open_hour; // GMT open hour int close_hour; // GMT close hour color sess_color; // Visual color on chart bool enabled; // Whether to render this session };
Использование перечислений гарантирует, что наш код будет легко читаться и расширяться в дальнейшем. Например, для добавления “пользовательской сессии” потребуется только одна дополнительная запись и инициализация структуры.
3. Входные Параметры и Инициализация Класса
Далее определяем входные параметры и подготавливаем конструктор класса. Эта настройка позволяет трейдерам или разработчикам настраивать периоды ретроспективы, цвета и поведение индикатора непосредственно в диалоговом окне ввода советника.
input int InpSessionLookback = 10; // Days of historical sessions input bool InpShowSessionWicks = false; input int InpWickAlpha = 120; input color InpFillBull = clrLime; input color InpFillBear = clrPink; class CSessionVisualizer : public CObject { private: SessionInfo m_sessions[4]; int m_gmt_offset; string m_prefix; public: CSessionVisualizer(string prefix = "SESS_") : m_prefix(prefix) { // Initialize all session parameters (GMT-based) m_sessions[SESSION_SYDNEY] = {SESSION_SYDNEY, "SYD", 22, 7, clrAqua, true}; m_sessions[SESSION_TOKYO] = {SESSION_TOKYO, "TOK", 0, 9, clrYellow, true}; m_sessions[SESSION_LONDON] = {SESSION_LONDON, "LON", 8, 17, clrRed, true}; m_sessions[SESSION_NEWYORK]= {SESSION_NEWYORK,"NY", 13, 22, clrBlue, true}; // Basic GMT offset estimation MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); m_gmt_offset = -dt.hour % 24; } ~CSessionVisualizer() { DeleteAllSessionObjects(); }
Здесь m_prefix помогает предотвратить конфликты имен объектов на графике, особенно при одновременном использовании нескольких инструментов. Например, каждый объект может быть помечен следующим образом: "SESS_LON_O_1730764800."
4. Сессии Обновления и Рисования
Метод RefreshSessions() является сердцем визуализатора. Он стирает все рисунки предыдущей сессии и восстанавливает их в течение заданного пользователем периода времени. Такая модульная конструкция позволяет обновлять визуальные эффекты на каждом новом баре или тике таймера, не загромождая график.
void RefreshSessions(int lookback_days = 10) { DeleteAllSessionObjects(); // Clear previously drawn sessions datetime end_time = TimeCurrent(); datetime start_time = end_time - (lookback_days * 86400); for (int day = 0; day < lookback_days; day++) { datetime day_start = start_time + (day * 86400); for (int s = 0; s < 4; s++) { if (!m_sessions[s].enabled) continue; DrawSessionForDay(m_sessions[s], day_start); } } ChartRedraw(); }
Эта структура основана на цикле для повышения эффективности и модульности. Каждый день и каждая сессия обрабатываются индивидуально — этот метод позволяет избежать накладывающиеся друг на друга артефакты сессии или пропуск диапазонов данных.
5. Рисование индивидуальных сессий
Функция DrawSessionForDay() заботится о фактическом рендеринге каждой сессии. Определяет время открытия и закрытия (с поправкой на смещение по Гринвичу), извлекает ценовые данные для этого окна и визуализирует сессию в виде прямоугольника в форме свечи с дополнительными тенями.
void DrawSessionForDay(const SessionInfo &sess, datetime day_start) { MqlDateTime dt_open, dt_close; TimeToStruct(day_start, dt_open); dt_open.hour = sess.open_hour + m_gmt_offset; datetime t_open = StructToTime(dt_open); TimeToStruct(day_start, dt_close); dt_close.hour = sess.close_hour + m_gmt_offset; datetime t_close = StructToTime(dt_close); if (sess.close_hour < sess.open_hour) t_close += 86400; // Overnight fix double o,h,l,c; if (!GetOHLCInTimeRange(t_open, t_close, o,h,l,c)) return; color body_col = (c > o) ? InpFillBull : InpFillBear; string rect = m_prefix + sess.name + "_B_" + IntegerToString((int)t_open); ObjectCreate(0, rect, OBJ_RECTANGLE, 0, t_open, MathMin(o,c), t_close, MathMax(o,c)); ObjectSetInteger(0, rect, OBJPROP_BGCOLOR, body_col); ObjectSetInteger(0, rect, OBJPROP_FILL, true); ObjectSetInteger(0, rect, OBJPROP_BACK, true); }
Такой подход дает нам сессионные бары, которые ведут себя как свечи — новый способ анализировать сессии как их собственные микротаймфреймы.
6. Получение данных OHLC сессии
Функция GetOHLCInTimeRange() функция извлекает точные данные об открытии, максимуме, минимуме и закрытии за выбранный таймфрейм в течение каждой сессии. Использование функции iBarShift() обеспечивает точное выравнивание по существующим барам графика.
bool GetOHLCInTimeRange(datetime start, datetime end, double &open, double &high, double &low, double &close) { int shift_start = iBarShift(_Symbol, _Period, start, false); int shift_end = iBarShift(_Symbol, _Period, end, false); if (shift_start < 0 || shift_end < 0) return false; int count = shift_start - shift_end + 1; if (count <= 0) return false; double o[],h[],l[],c[]; ArraySetAsSeries(o,true); ArraySetAsSeries(h,true); ArraySetAsSeries(l,true); ArraySetAsSeries(c,true); CopyOpen(_Symbol,_Period,shift_end,count,o); CopyHigh(_Symbol,_Period,shift_end,count,h); CopyLow(_Symbol,_Period,shift_end,count,l); CopyClose(_Symbol,_Period,shift_end,count,c); open = o[count-1]; high = h[ArrayMaximum(h,0,count)]; low = l[ArrayMinimum(l,0,count)]; close = c[0]; return true; }
Это позволяет получать данные OHLC “сессионных свечей” из стандартных ценовых рядов — без повторного запроса к внешним источникам.
7. Очистка и Управление Объектами
Хороший код всегда убирает за собой. Функция DeleteAllSessionObjects() гарантирует, что старые объекты будут удалены при обновлении или деинициализации. Без этого после долгих сессий тестирования вы бы получили тысячи оставшихся прямоугольников.
void DeleteAllSessionObjects() { int total = ObjectsTotal(0); for (int i = total - 1; i >= 0; i--) { string name = ObjectName(0, i); if (StringFind(name, m_prefix) == 0) ObjectDelete(0, name); } }
Разработка тестового советника:
Мы начинаем с компактного заголовка и включаемого файла. Этот советник намеренно сделан маленьким: он загружает только визуализатор, запускает таймер для периодического обновления (чтобы вы могли видеть обновление сессий во время работы рынка) и очищает его при выходе. Небольшие размеры тестовых систем помогают выявлять проблемы и проверять поведение класса перед интеграцией в более крупные системы.
//+------------------------------------------------------------------+ //| SessionTest.mq5 - Standalone test for CSessionVisualizer | //| Purpose: lightweight EA to exercise the SessionVisualizer class | //+------------------------------------------------------------------+ #property strict #include <SessionVisualizerTest.mqh>
Далее объявляем экземпляр среды выполнения. Использование указателя и new дает явный контроль над временем жизни: мы создаем визуализатор в OnInit() и удаляем его в OnDeinit(). Поэтому деструктор запускается немедленно и удаляет созданные классом объекты. Этот паттерн удобен для тестирования и отражает то, как вы управляли бы объектами в более крупных советниках, которые динамически создают/уничтожают подсистемы.
// runtime pointer to the session visualizer CSessionVisualizer *g_session_vis = NULL;
OnInit() - это то, где мы создаем визуализатор, устанавливаем таймер обновления и выполняем начальную отрисовку. В примере используется 30-секундный демонстрационный таймер, достаточно короткий, чтобы часто видеть обновления, но достаточно продолжительный, чтобы избежать чрезмерной загрузки процессора. Мы передаем визуализатору уникальный префикс, чтобы его объекты были четко разделены по именам (удобно при тестировании нескольких инструментов на одном графике). Немедленный вызов RefreshSessions(5) приведет к отображению пятидневных сессий, так что вы сможете сразу же проверить хронологию построения графика.
int OnInit() { // allocate the session visualizer with a test-specific prefix to avoid name clashes g_session_vis = new CSessionVisualizer("TEST_SESS_"); // Refresh every 30 seconds during testing (demo). Adjust for production usage. EventSetTimer(30); // initial rendering of the last 5 days (quick sanity check) if(g_session_vis != NULL) g_session_vis.RefreshSessions(5); return(INIT_SUCCEEDED); }
Примечание: перед вызовом метода мы выполняем нулевую проверку. Если new по какой-либо причине выходит из строя (что очень редко встречается на обычных настольных компьютерах), это предотвращает сбой и облегчает обнаружение сбоя в логах.
Функция OnTimer() запускает периодические обновления. Для визуализации сессии это полезно, поскольку значения OHLC сессии могут незначительно изменяться на динамических барах (в зависимости от таймфрейма, используемого для расчета OHLC), а частые перерисовки позволяют наблюдать за изменением теней и тел сессии. В рабочем советнике можно предпочесть обновлять данные только на новых барах или с более длительным интервалом, но для тестирования достаточно 30 секунд.
void OnTimer() { // periodic refresh of the last 5 days if(g_session_vis != NULL) g_session_vis.RefreshSessions(5); }
OnDeinit() выполняет очистку: отключаем таймер и удаляем объект визуализатора. Удаление объекта вызывает его деструктор, который (после реализации) удаляет все объекты сессии, созданные классом на графике. Тем самым гарантируя, что после тестирования график останется чистым. Явная очистка необходима в средах с повторяющейся загрузкой (загрузка/выгрузка множества советников), чтобы избежать раздувания графика и задерживающихся объектов.
void OnDeinit(const int reason) { EventKillTimer(); if(g_session_vis != NULL) { // destructor will call DeleteAllSessionObjects() delete g_session_vis; g_session_vis = NULL; } }
Первоначальный результат теста:
Наш начальный этап тестирования был простым — после успешной компиляции сессионного тестового советника (Session Test EA) мы просто прикрепили его к живому графику MetaTrader 5. Результатом стала мгновенная и четкая визуализация форекс-сессий. На изображении ниже четко видно, как сессии накладываются друг на друга. Для удобства идентификации каждая из них отличается своим цветом. Заполненные прямоугольники сессии также передают настроение: лимонно-зеленый цвет указывает на бычье закрытие сессии, а розовый - на медвежье. Эта визуализация с цветовой кодировкой позволяет легко интерпретировать доминирующее направление каждой торговой сессии с первого взгляда.

Рис.2. Сессионный тест
Шаг 2 - Этап Интеграции
Каждый отличный торговый инструмент проходит этап трансформации — от экспериментального скрипта до полностью интегрированного производственного модуля. Наш Session Visualizer не является исключением. Первоначально заголовок теста (SessionVisualizerTest.mqh) был направлен на создание основы: определение торговых сессий, получение данных OHLC и рисование цветных прямоугольников, представляющих каждый период рынка Форекс. Хотя во время тестирования он хорошо выполнял свою задачу, производственная интеграция в наш советник Market Periods Synchronizer EA потребовала доработки для повышения наглядности, расширяемости и совместимости.
Почему Мы Отказались От Заполненных Сессионных Тел
В тестовой версии сессии были представлены сплошными цветными прямоугольниками, в которых использовались цвета сессий для выделения каждого активного торгового блока. Такой подход выглядел динамично, но быстро стал визуально перегруженным — особенно когда перекрывающиеся сессии или исторические слои отображались вместе. Стиль заливки также скрывал основные детали графика, что противоречило одной из наших целей: обеспечить контекст без беспорядка.
Следовательно, в новой версии мы вводим незаполненные прямоугольники тел с пунктирными границами. Они действуют как полые свечи, отмечая диапазон открытия–закрытия каждой сессии, но не закрывая ценовой график. Выбранный дизайн делает график "дышащим" и гарантирует, что трейдер по-прежнему сможет четко видеть движение цены под сессионными маркерами.
Для нас, разработчиков, это важный урок: иногда меньше цвета - больше информации. Удалив непрозрачные заливки, мы повысили наглядность и профессионализм в представлении.
Интеграция CCanvas и ARGB: Единый Визуальный Движок
Небольшим, но важным техническим изменением в новом заголовке является использование #include <Canvas/Canvas.mqh> для визуальных элементов на основе ARGB. Это обеспечивает совместимость с существующей в нашем советнике функцией CCanvas, которая также управляет фоновым рендерингом и прозрачностью для других модулей. Вместо того, чтобы переопределять ARGB отдельно, новая версия делегирует это библиотеке Canvas, обеспечивая согласованность во всей графике, используемой советником.
Это решение является как архитектурным, так и практичным. При создании модульных советников, которые повторно используют компоненты, дублирование макросов ARGB может привести к конфликтам или непредсказуемому поведению, особенно если один компонент определяет прозрачность по-разному. Использование единого общего движка — CCanvas — помогает поддерживать единую логику рендеринга и сокращает затраты на обслуживание.
Проще говоря, мы объявляем о включении Canvas глобально, чтобы каждый визуальный компонент — будь то сессионные блоки, тени или оверлеи — говорил на одном и том же графическом “языке”.
Переопределение Сессий: От Цветных Блоков До Рыночных Рамок
Структура представления сеанса остается прежней: мы по-прежнему используем структуру SessionInfo, содержащую имя, тип, время открытия/закрытия и цвет. Однако мы переосмыслили способ визуализации этих атрибутов.
Теперь каждая сессия визуализирует:
- Пунктирный прямоугольник, обозначающий ценовой диапазон открытия–закрытия
- Опционные заполненные серым цветом прямоугольники “тени”, показывающие общее время прохождения сессии от максимума до минимума
- Текстовые метки и вертикальные линии, обозначающие время открытия и закрытия
- Лейеринг переднего плана/фона таким образом, чтобы прямоугольники оставались за ценовыми свечами, в то время как строки и метки оставались видимыми пользователю
Это создает аналитический, но элегантный вид — как прозрачная модель рыночного ритма. Трейдеры могут видеть, какие сессии расширяются или сокращаются, не теряя при этом детализации графика. Новая концепция “незаполненности” действует скорее как рамка для рынка, чем как покрытие.
Настройка среды выполнения: Создание динамического визуализатора
Чтобы обеспечить пользователям и советникам больше гибкости, мы превратили ранее статические настройки в переменные среды выполнения. Например, m_show_wicks, m_wick_alpha и m_gmt_offset теперь настраиваются глобально. Трейдер или разработчик может переключать видимость тени, изменять прозрачность или корректировать смещение по времени без повторной компиляции.
Вы заметите такие функции, как
void SetShowWicks(bool show) { m_show_wicks = show; } void SetWickAlpha(int alpha) { m_wick_alpha = MathMax(0, MathMin(255, alpha)); } void SetGMTOffset(int offset){ m_gmt_offset = offset; }
Эти небольшие, но значимые дополнения обеспечивают взаимодействие между интерфейсом советника и уровнем визуализации в режиме реального времени - фундаментальный аспект современного модульного проектирования на MQL5.
Контекстная Аналитика: Отображение Текущей и Предыдущей Сессий
В отличие от тестовой версии, в которой отображались только статические исторические сеансы, новый заголовок позволяет отслеживать текущие и недавние сессии. Во время реальной торговли система определяет, какая сессия активна, и накладывает на нее метку “Live”. Она также сохраняет недавно закрытую сессию видимой в течение одного часа после закрытия, предоставляя быструю визуальную информацию о том, как прошла предыдущая сессия.
Теперь, когда мы разобрались с изменениями, внесенными для обеспечения эффективной интеграции, перейдём к подробному описанию нового заголовка SessionVisualizer. После этого обсудим его интеграцию в советник (EA) и расскажем о новых функциях советника, призванных помочь трейдерам более эффективно использовать периоды рыночных сессий для принятия более эффективных торговых решений.
1) Строгость описания модуля и компиляции
Начинаем с четкого описания, поскольку опубликованный код тоже является документацией. Управление версиями сообщает читателям о том, что нового появилось (например, “незаполненные прямоугольники, пунктирные границы и серые тени”), а #property strict обеспечивает современную защиту типа MQL5 от скрытых ошибок, которые часто проскальзывают в экспериментальных заголовках.
//+------------------------------------------------------------------+ //| SessionVisualizer.mqh | //| Modular class for Forex session OHLC visualization | //| Copyright 2025: Clemence Benjamin | //| Version: 1.02 | //+------------------------------------------------------------------+ #property strict
2) Общие зависимости (Object API + унифицированный ARGB через Canvas)
На этапе тестирования обычно используется наш собственный макрос ARGB; в процессе разработки во избежание конфликтов мы используем единый визуальный движок советника. Включение файла Canvas.mqh предоставляет нам каноническую ARGB() и плавную совместимость с другими функциями рисования в советнике Market Periods Synchronizer.
#include <Object.mqh> #include <Canvas/Canvas.mqh> // for ARGB()
3) Модель предметной области (enum + struct)
Формализуем проблемное пространство: четыре основных сессии, понятные пользователю названия и простой контейнер SessionInfo. Даже если свойство используется не везде (например, sess_color больше не окрашивает заливку тела), мы сохраняем его — цвета по-прежнему определяют метки / линии и поддерживают обратную совместимость с предыдущими инструментами и привычками пользователей.
//---------------------------------------------- // Session enum and info //---------------------------------------------- enum SESSION_TYPE { SESSION_SYDNEY = 0, // 22:00-07:00 GMT SESSION_TOKYO = 1, // 00:00-09:00 GMT SESSION_LONDON = 2, // 08:00-17:00 GMT SESSION_NEWYORK= 3 // 13:00-22:00 GMT }; struct SessionInfo { SESSION_TYPE type; string name; // "SYD","TOK","LON","NY" int open_hour; // GMT open hour int close_hour; // GMT close hour color sess_color; // base tint (not used for border now) bool enabled; };
4) Параметры среды выполнения (“кнопки”, управляемые советником)
Объявляем настраиваемые советником глобальные переменные таким образом, чтобы функции можно было переключать без перекомпиляции. Функция lookback регулирует плотность истории, m_show_wicks/m_wick_alpha управляет видимостью и прозрачностью диапазонов теней, а m_gmt_offset позволяет советнику согласовывать серверное время с GMT — классической проблемой в реальном мире.
//---------------------------------------------- // Runtime params (configured by EA) //---------------------------------------------- int m_session_lookback = 10; bool m_show_wicks = false; int m_wick_alpha = 120; int m_gmt_offset = 0;
5) Оболочка класса и настройки конструктора по умолчанию
Сохраняем публичный API компактным, а частное состояние - явным. В конструкторе инициализируем канонические расписания по Гринвичу и удобочитаемые трехбуквенные теги. Вы заметите, что мы сохраняем m_prefix: в этом секрет безопасного именования объектов и последующего их удаления — без случайных столкновений с другими объектами графика.
class CSessionVisualizer : public CObject { private: SessionInfo m_sessions[4]; string m_prefix; public: CSessionVisualizer(string prefix="SESS_") : m_prefix(prefix) { // Defaults (GMT schedule) m_sessions[SESSION_SYDNEY] .type=SESSION_SYDNEY; m_sessions[SESSION_SYDNEY] .name="SYD"; m_sessions[SESSION_SYDNEY] .open_hour=22; m_sessions[SESSION_SYDNEY] .close_hour=7; m_sessions[SESSION_SYDNEY] .sess_color=clrAqua; m_sessions[SESSION_SYDNEY] .enabled=true; m_sessions[SESSION_TOKYO] .type=SESSION_TOKYO; m_sessions[SESSION_TOKYO] .name="TOK"; m_sessions[SESSION_TOKYO] .open_hour=0; m_sessions[SESSION_TOKYO] .close_hour=9; m_sessions[SESSION_TOKYO] .sess_color=clrYellow; m_sessions[SESSION_TOKYO] .enabled=true; m_sessions[SESSION_LONDON] .type=SESSION_LONDON; m_sessions[SESSION_LONDON] .name="LON"; m_sessions[SESSION_LONDON] .open_hour=8; m_sessions[SESSION_LONDON] .close_hour=17; m_sessions[SESSION_LONDON] .sess_color=clrRed; m_sessions[SESSION_LONDON] .enabled=true; m_sessions[SESSION_NEWYORK].type=SESSION_NEWYORK; m_sessions[SESSION_NEWYORK].name="NY"; m_sessions[SESSION_NEWYORK].open_hour=13; m_sessions[SESSION_NEWYORK].close_hour=22; m_sessions[SESSION_NEWYORK].sess_color=clrBlue; m_sessions[SESSION_NEWYORK].enabled=true; } ~CSessionVisualizer() { DeleteAllSessionObjects(); }
6) Общедоступный API: обновление + переключатели (предназначены для работы с живыми графиками)
RefreshSessions() - это перестройка с помощью одной кнопки: очистите, перерисуйте историю, затем нарисуйте живое окно. Предоставляем крошечные, выразительные методы-установщики, чтобы панель советника могла переключать сессии, перекрашивать темы или синхронизировать время по Гринвичу на лету. Одно заметное изменение по сравнению с заголовком теста: нет заливки тела; функция SetFillColors() теперь является холостой командой, сохраняемой исключительно для обеспечения обратной совместимости.
//--------------------------------------------------------------- // PUBLIC API //--------------------------------------------------------------- void RefreshSessions(int lookback_days=10) { m_session_lookback = lookback_days; DeleteAllSessionObjects(); const datetime now = TimeCurrent(); const datetime start = now - (m_session_lookback*86400); // Historical completed sessions for(int d=0; d<m_session_lookback; ++d) { datetime day_start = start + d*86400; for(int s=0; s<4; ++s) { if(!m_sessions[s].enabled) continue; DrawHistoricalSessionForDay(m_sessions[s], day_start); } } // Active session windows DrawCurrentSessions(); ChartRedraw(); } void SetSessionEnabled(SESSION_TYPE type, bool enabled) { for(int i=0;i<4;i++) if(m_sessions[i].type==type){ m_sessions[i].enabled=enabled; break; } } void SetSessionColor(SESSION_TYPE type, color col) { for(int i=0;i<4;i++) if(m_sessions[i].type==type){ m_sessions[i].sess_color=col; break; } } // Kept for compatibility with EA; ignored (we no longer fill bodies). void SetFillColors(color /*bull*/, color /*bear*/) { /* no-op */ } void SetShowWicks(bool show) { m_show_wicks = show; } void SetWickAlpha(int alpha) { m_wick_alpha = MathMax(0, MathMin(255, alpha)); } void SetGMTOffset(int offset){ m_gmt_offset = offset; } // public cleanup void ClearAll() { DeleteAllSessionObjects(); }
7) Историческое рисование (только завершенные сессии)
Распространенным приемом использования сессионных инструментов является внесение частичных данных в “историю”. Здесь мы вычисляем открытие / закрытие с помощью m_gmt_offset, обрабатываем случай “перенос после полуночи”, а затем пропускаем все, что не полностью в прошлом. Только после подтверждения заполнения окна, вычислим OHLC и выполним рендеринг.
private: //--------------------------------------------------------------- // CORE DRAWING //--------------------------------------------------------------- void DrawHistoricalSessionForDay(const SessionInfo &sess, datetime day_start) { // build opru/close datetimes adjusted by GMT offset MqlDateTime dto, dtc; TimeToStruct(day_start, dto); dtc = dto; dto.hour = sess.open_hour + m_gmt_offset; dto.min=0; dto.sec=0; dtc.hour = sess.close_hour + m_gmt_offset; dtc.min=0; dtc.sec=0; datetime t_open = StructToTime(dto); datetime t_close = StructToTime(dtc); if(sess.close_hour < sess.open_hour) t_close += 86400; // wraps midnight // Only draw if fully in the past if(t_close >= TimeCurrent()) return; if(t_close < TimeCurrent() - (m_session_lookback*86400)) return; double o,h,l,c; if(!GetOHLCInTimeRange(t_open, t_close, o,h,l,c)) return; DrawSessionVisuals(sess, t_open, t_close, o,h,l,c, /*is_current=*/false); }
8) Обработка сессии в режиме реального времени (и 1-часовая отсрочка для последней сессии)
Определяем активную сессию в режиме реального времени, создавая сегодняшнее окно и настраивая его на ночные сессии. Если мы находимся внутри, транслируем OHLC до настоящего момента, добавляем в h/l текущую ставку, чтобы поле отображало прямую трансляцию, и помечаем ее как “(Прямая трансляция)”. Мы также сохраняем только что завершившуюся сессию на экране в течение дополнительного часа - это очень удобно во время ролловеров, когда трейдерам нужен контекст.
void DrawCurrentSessions() { const datetime now = TimeCurrent(); MqlDateTime cur; TimeToStruct(now, cur); for(int i=0;i<4;i++) { if(!m_sessions[i].enabled) continue; const SessionInfo sess = m_sessions[i]; // compute session window for "today" (adjusted for wrap) MqlDateTime ds, de; TimeToStruct(now, ds); de = ds; ds.hour = sess.open_hour + m_gmt_offset; ds.min=0; ds.sec=0; de.hour = sess.close_hour + m_gmt_offset; de.min=0; de.sec=0; datetime session_start = StructToTime(ds); datetime session_end = StructToTime(de); if(sess.close_hour < sess.open_hour) { if(cur.hour >= sess.open_hour + m_gmt_offset) session_end += 86400; // ends tomorrow else session_start -= 86400; // started yesterday } if(now >= session_start && now <= session_end) { // pull OHLC until now (close = current) double o,h,l,c; if(!GetOHLCInTimeRange(session_start, now, o,h,l,c)) o = h = l = c = SymbolInfoDouble(_Symbol, SYMBOL_BID); const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); h = MathMax(h, bid); l = MathMin(l, bid); c = bid; DrawSessionVisuals(sess, session_start, session_end, o,h,l,c, /*is_current=*/true); } else if(now > session_end && now <= session_end + 3600) { // Keep just-closed session visible for 1 hour double o,h,l,c; if(!GetOHLCInTimeRange(session_start, session_end, o,h,l,c)) continue; DrawSessionVisuals(sess, session_start, session_end, o,h,l,c, /*is_current=*/false); } } }
9) Визуальная грамматика: незаполненные тела, пунктирные границы, опциональная серая тень
Это изменение сигнатуры в заголовке теста. Тела не заполнены и расположены за ценой (OBJPROP_BACK=true), поэтому ничто не заслоняет свечи. Рамки обозначают настроения (темно-зеленый - бычий, красный - медвежий; в противном случае - цвет сессии). Когда тени включены, они представляют собой мягкие серые прямоугольники ARGB, нарисованные на заднем плане, чтобы показать полный диапазон максимумов и минимумов без помех. Метки и временные шкалы остаются на переднем плане для большей наглядности.
// Renders: // - unfilled body rectangle (Open..Close) with dashed border // * border = clrDarkGreen if bullish, clrRed if bearish (Neutral uses session color) // - optional GREY filled wick rectangles in background for visibility // - opru/close vlines + labels in foreground (thin) void DrawSessionVisuals(const SessionInfo &sess, datetime t_open, datetime t_close, double o,double h,double l,double c, bool is_current) { const bool bullish = (c > o); const bool bearish = (c < o); // ---------- vertical lines ---------- string vopen = m_prefix + sess.name + "_O" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, vopen) == -1) ObjectCreate(0, vopen, OBJ_VLINE, 0, t_open, 0); ObjectSetInteger(0, vopen, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, vopen, OBJPROP_WIDTH, is_current?2:1); ObjectSetInteger(0, vopen, OBJPROP_STYLE, is_current?STYLE_SOLID:STYLE_DASH); ObjectSetInteger(0, vopen, OBJPROP_BACK, false); ObjectSetInteger(0, vopen, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, vopen, OBJPROP_HIDDEN, false); string vclose = m_prefix + sess.name + "_C" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_close); if(ObjectFind(0, vclose) == -1) ObjectCreate(0, vclose, OBJ_VLINE, 0, t_close, 0); ObjectSetInteger(0, vclose, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, vclose, OBJPROP_WIDTH, is_current?2:1); ObjectSetInteger(0, vclose, OBJPROP_STYLE, is_current?STYLE_SOLID:STYLE_DASH); ObjectSetInteger(0, vclose, OBJPROP_BACK, false); ObjectSetInteger(0, vclose, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, vclose, OBJPROP_HIDDEN, false); // ---------- body rectangle (UNFILLED, dashed) ---------- const double body_top = MathMax(o,c); const double body_bot = MathMin(o,c); color border_col = sess.sess_color; // neutral fallback if(bullish) border_col = clrDarkGreen; else if(bearish) border_col = clrRed; string rect = m_prefix + sess.name + "_R" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, rect) == -1) { if(!ObjectCreate(0, rect, OBJ_RECTANGLE, 0, t_open, body_bot, t_close, body_top)) PrintFormat("Failed to create session rectangle %s err=%d", rect, GetLastError()); } // unfilled rectangle with dashed edge, kept in background ObjectSetInteger(0, rect, OBJPROP_FILL, false); ObjectSetInteger(0, rect, OBJPROP_COLOR, border_col); ObjectSetInteger(0, rect, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, rect, OBJPROP_WIDTH, 1); ObjectSetInteger(0, rect, OBJPROP_BACK, true); ObjectSetInteger(0, rect, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, rect, OBJPROP_HIDDEN, false); // keep coordinates fresh ObjectMove(0, rect, 0, t_open, body_bot); ObjectMove(0, rect, 1, t_close, body_top); // ---------- WICKS (optional) as GREY filled rectangles in background ---------- if(m_show_wicks) { uint wick_col = ARGB(m_wick_alpha, 128,128,128); // semi-transparent grey // Upper wick: [body_top .. high] string wu = m_prefix + sess.name + "_WU" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(h > body_top) { if(ObjectFind(0, wu) == -1) { if(!ObjectCreate(0, wu, OBJ_RECTANGLE, 0, t_open, body_top, t_close, h)) PrintFormat("Failed to create upper wick %s err=%d", wu, GetLastError()); } ObjectSetInteger(0, wu, OBJPROP_BGCOLOR, wick_col); ObjectSetInteger(0, wu, OBJPROP_COLOR, wick_col); ObjectSetInteger(0, wu, OBJPROP_FILL, true); ObjectSetInteger(0, wu, OBJPROP_BACK, true); ObjectSetInteger(0, wu, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, wu, OBJPROP_HIDDEN, false); ObjectMove(0, wu, 0, t_open, body_top); ObjectMove(0, wu, 1, t_close, h); } else ObjectDelete(0, wu); // Lower wick: [low .. body_bot] string wl = m_prefix + sess.name + "_WL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(l < body_bot) { if(ObjectFind(0, wl) == -1) { if(!ObjectCreate(0, wl, OBJ_RECTANGLE, 0, t_open, l, t_close, body_bot)) PrintFormat("Failed to create lower wick %s err=%d", wl, GetLastError()); } ObjectSetInteger(0, wl, OBJPROP_BGCOLOR, wick_col); ObjectSetInteger(0, wl, OBJPROP_COLOR, wick_col); ObjectSetInteger(0, wl, OBJPROP_FILL, true); ObjectSetInteger(0, wl, OBJPROP_BACK, true); ObjectSetInteger(0, wl, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, wl, OBJPROP_HIDDEN, false); ObjectMove(0, wl, 0, t_open, l); ObjectMove(0, wl, 1, t_close, body_bot); } else ObjectDelete(0, wl); } else { // Ensure wicks are removed if disabled ObjectDelete(0, m_prefix + sess.name + "_WU" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open)); ObjectDelete(0, m_prefix + sess.name + "_WL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open)); } // ---------- Labels in foreground ---------- string session_tag = sess.name + (is_current ? " (Live)" : ""); string lbl_o = m_prefix + sess.name + "_OL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, lbl_o) == -1) ObjectCreate(0, lbl_o, OBJ_TEXT, 0, t_open, o); ObjectSetString (0, lbl_o, OBJPROP_TEXT, session_tag + " Open"); ObjectSetInteger(0, lbl_o, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, lbl_o, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, lbl_o, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, lbl_o, OBJPROP_HIDDEN, false); ObjectSetInteger(0, lbl_o, OBJPROP_BACK, false); string lbl_c = m_prefix + sess.name + "_CL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_close); if(ObjectFind(0, lbl_c) == -1) ObjectCreate(0, lbl_c, OBJ_TEXT, 0, t_close, c); ObjectSetString (0, lbl_c, OBJPROP_TEXT, session_tag + " Close"); ObjectSetInteger(0, lbl_c, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, lbl_c, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, lbl_c, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, lbl_c, OBJPROP_HIDDEN, false); ObjectSetInteger(0, lbl_c, OBJPROP_BACK, false); }
10) Надежная агрегация OHLC (на основе сдвига баров)
Для обеспечения корректности на разных таймфреймах вычисляем OHLC сессии, используя границы iBarShift, и извлекаем компактный фрагмент массивов, а затем агрегируем его с помощью ArrayMaximum/Minimum. Это позволяет избежать случайных ошибок и работает, даже если степень детализации таймфреймов не совсем совпадает с границами сессии.
//--------------------------------------------------------------- // Data helpers //--------------------------------------------------------------- bool GetOHLCInTimeRange(datetime start, datetime end, double &open, double &high, double &low, double &close) { int shift_start = iBarShift(_Symbol, _Period, start, false); int shift_end = iBarShift(_Symbol, _Period, end, false); if(shift_start < 0 || shift_end < 0) return false; int bars = shift_start - shift_end + 1; if(bars <= 0) return false; double opens[], highs[], lows[], closes[]; ArraySetAsSeries(opens, true); ArraySetAsSeries(highs, true); ArraySetAsSeries(lows, true); ArraySetAsSeries(closes, true); if(CopyOpen (_Symbol,_Period, shift_end, bars, opens) != bars) return false; if(CopyHigh (_Symbol,_Period, shift_end, bars, highs) != bars) return false; if(CopyLow (_Symbol,_Period, shift_end, bars, lows) != bars) return false; if(CopyClose(_Symbol,_Period, shift_end, bars, closes) != bars) return false; open = opens[bars-1]; high = highs[ArrayMaximum(highs,0,bars)]; low = lows [ArrayMinimum(lows ,0,bars)]; close = closes[0]; return true; }
11) Детерминированная очистка (GC с префиксной областью действия)
Профессиональные графические инструменты должны обеспечивать чистоту рабочей поверхности. При обновлении или удалении советника выполняем итерацию объектов в обратном порядке и удаляем только те, которые содержат наш m_prefix. Это обеспечивает вежливое сосуществование с другими индикаторами/советниками и предотвращает появление “призрачных рисунков”, раздражающих пользователей.
//--------------------------------------------------------------- // Cleanup //--------------------------------------------------------------- void DeleteAllSessionObjects() { int total = ObjectsTotal(0); for(int i = total-1; i >= 0; --i) { string name = ObjectName(0, i); if(StringFind(name, m_prefix) == 0) ObjectDelete(0, name); } } };
Интеграция CSessionVisualizer и Session Info в MarketPeriodsSynchronizerEA
До этого обновления наша предыдущая версия советника была сосредоточена на структуре основных и второстепенных периодов, заливках тела, тенях и элементах управления пользовательским интерфейсом — без представления о рыночных сессиях или сводках сессий. В этой версии мы внедрили новый класс CSessionVisualizer и добавили компактную панель “Session Information” (“Информация о сессии”), а также удобные переключатели управления, чтобы сессии и их основные тени можно было настраивать независимо от визуальных элементов основных периодов. Ниже я расскажу вам об интеграции, как если бы мы шаг за шагом разрабатывали более раннюю версию советника, объясняя причины каждого изменения и вставляя реализующий его точный фрагмент. (Для справки, прежняя базовая версия советника - версия 1.01 без сессий и информационной панели.
1) Подключите визуализатор сессии к советнику
Включаем заголовок для нашего нового средства визуализации сеанса вместе с помощником Canvas. В тестовом заголовке уже используется CSessionVisualizer с незаполненными пунктирными прямоугольниками и дополнительными серыми теневыми заливками на заднем плане, поэтому визуально он не будет отличаться от наших оверлеев HTF. Мы сохраняем включенные файлы (includes) минимальными и явными, чтобы избежать коллизий между ODR и именем.
#include <Canvas/Canvas.mqh> // Canvas helper library (expects Canvas.mqh to be present) #include <SessionVisualizer.mqh> // Integrated Session Visualizer class
2) Новые входные данные для сессий и информационная панель
Входные данные - это контракт с пользователями. Мы добавили специальные переключатели для отображения сессий, окна ретроспективного анализа, смещение брокера по Гринвичу (чтобы сессии выстраивались в линию), видимость теней/alpha только для сессий и флажок для панели “Информация о сессии”. Обратите внимание, что мы намеренно разделяем настройки теней для основных событий и сессий — трейдеры часто хотят иметь тени для одного, но не для другого.
// Session inputs input bool InpShowSessions = true; // Show Forex sessions input int InpSessionLookback = 10; // Session lookback (days) input int InpGMTOffset = 0; // GMT offset (broker hours) // Wick visualization (separated for majors and sessions) input bool InpShowSessionWicks = false; // Show wick regions for sessions input int InpSessionWickAlpha = 120; // Alpha transparency for session wicks 0..255 // Session information panel input bool InpShowSessionInfo = true; // Show session info panel
В более ранних версиях не было входных данных, привязанных к конкретной сессии; все вращалось вокруг основных / второстепенных функций и одного переключателя теней. Теперь мы устраняем эти проблемы, поэтому раскладка остается чистой, даже когда обе функции активны.
3) Глобальные параметры среды выполнения отражают новые входные данные
Информационная панель советника изменяет состояние во время выполнения, поэтому мы сохраняем изменяемые копии входных данных. Здесь добавляем g_ShowSessions, среду выполнения session-wick (g_ShowSessionWicks, g_SessionWickAlpha) и флаг для информационной панели. Сохранение их рядом с другими глобальными параметрами сохраняет ментальную модель: входные данные → среда выполнения → пользовательский интерфейс.
// Wick runtime (separated for majors and sessions) bool g_ShowWicks; int g_WickAlpha; bool g_ShowSessionWicks; int g_SessionWickAlpha; // Session runtime bool g_ShowSessions; // Session information panel bool g_ShowSessionInfo;
4) Единый общий экземпляр CSessionVisualizer
Все сессионные чертежи должны иметь общий стабильный префикс именования и жизненный цикл. Инстанцируем экземпляр одного визуализатора с коротким префиксом, чтобы имена объектов оставались уникальными и их было легко просматривать / очищать, не затрагивая другие объекты графика.
// Session visualizer instance CSessionVisualizer g_sessions("SESS_");
5) Информация о сессии — компактное состояние, которое мы можем обновить по тику
Трейдеры любят быстрый доступ к контексту: «В какой сессии я нахожусь? Как была закрыта предыдущая сессия?» Мы храним минимальные данные, необходимые для создания панели: имена, операционную систему, характер (бычий/ медвежий/ нейтральный) и время последнего обновления, чтобы избежать избыточной работы.
// Session data storage string g_CurrentSessionName = ""; double g_CurrentSessionOpen = 0; double g_CurrentSessionCurrent = 0; string g_CurrentSessionNature = ""; datetime g_CurrentSessionStart = 0; string g_PrevSessionName = ""; double g_PrevSessionOpen = 0; double g_PrevSessionClose = 0; string g_PrevSessionNature = ""; datetime g_LastSessionUpdate = 0;
6) Более широкий фон холста для размещения новой панели
Информационный блок о сессии содержит три краткие строки для краткого описания текущей и предыдущей сессий. Корректируем ширину/высоту панели, чтобы раскладка выглядела более «дышащей», а текст не был неуклюже вставлен.
// Canvas background - INCREASED WIDTH CCanvas g_bgCanvas; string g_bg_name = ""; int g_bg_x = 6; int g_bg_y = 44; // uses Y_OFFSET in OnInit int g_bg_w = 450; // increased from 430 int g_bg_h = 340; // increased from 300 for session info
7) Инициализируем новые кнопки в OnInit()
OnInit() - это когда входные данные переходят в текущее состояние. Мы также настраиваем визуализатор, чтобы он знал, какие настройки побочных каналов применяются к сессиям (не основным): Смещение по Гринвичу, переключатель теней и альфа теней. Несмотря на то, что класс session предоставляет функцию SetFillColors(), в наших визуальных элементах текущей сессии используются незаполненные прямоугольники, но мы сохраняем этот вызов для обеспечения совместимости в дальнейшем.
// Initialize session visualizer with separate session wick params g_sessions.SetGMTOffset(InpGMTOffset); g_sessions.SetFillColors(g_FillBull, g_FillBear); // kept for compatibility (no body fill in visualizer) g_sessions.SetShowWicks(g_ShowSessionWicks); // session-specific wick setting g_sessions.SetWickAlpha(g_SessionWickAlpha); // session-specific wick alpha // copy panel flag g_ShowSessionInfo = InpShowSessionInfo;
8) Добавим пользовательский интерфейс “Session Information”
Рисуем крошечные метки внутри панели управления, чтобы показать текущую сессию (название, цена открытия, характер) и предыдущую сессию (название, цена O/З, характер). Цвета мягко подчеркивают бычьи/медвежьи тренды. Панель создается один раз, содержимое обновляется каждый тик.
void UpdateSessionInfo() { if(!g_ShowSessionInfo) return; datetime now = TimeCurrent(); // Always refresh current session nature UpdateCurrentSessionInfo(now); // Update previous session only at boundaries if(g_LastSessionUpdate == 0 || IsNewSessionStart(now)) { UpdatePreviousSessionInfo(now); g_LastSessionUpdate = now; } UpdateSessionDisplay(); }
9) Умное ведение бухгалтерского учета сессии (текущей + предыдущей)
Разделяем задачи на три вспомогательные функции:
- UpdateCurrentSessionInfo: узнаем, в какой сессии находимся (по Гринвичу), кэшируем цену открытия сессии и классифицируем ее, используя текущее предложение цены по сравнению с открытием сессии.
- UpdatePreviousSessionInfo: когда начинается новая сессия, сделаем снимок открытия/закрытия и характера предыдущей сессии.
- UpdateSessionDisplay: вставим последние строки/ цвета в объекты меток.
Он намеренно выполнен легким — без тяжелых циклов — поэтому прекрасно сочетается с обычным циклом обновления HTF советника. В отличие от этого, в более раннем советнике такой концепции не было, поэтому логика панели и границ в этой версии совершенно новая.
void UpdateSessionInfo() { if(!g_ShowSessionInfo) return; datetime now = TimeCurrent(); // Always refresh current session nature UpdateCurrentSessionInfo(now); // Update previous session only at boundaries if(g_LastSessionUpdate == 0 || IsNewSessionStart(now)) { UpdatePreviousSessionInfo(now); g_LastSessionUpdate = now; } UpdateSessionDisplay(); }
10) Элементы управления сессией на информационной панели
Сила должна быть у трейдера под рукой. Мы добавили две новые кнопки: одну для включения / выключения сесий (Sessions on/off), другую для переключения теней сессии (Session Wicks). Мы сохраняем элементы управления основными периодами независимыми. Кнопки находятся рядом с существующими переключателями, чтобы сохранить мышечную память.
// NEW ROW: Session wicks, Sessions, Major VLines (swapped positions for clarity) CreateButton(btn_toggle_session_wicks, 12, 86 + Y_OFFSET, 130, 22, g_ShowSessionWicks ? "Sess Wicks: ON" : "Sess Wicks: OFF"); CreateButton(btn_toggle_sessions, 152, 86 + Y_OFFSET, 130, 22, g_ShowSessions ? "Sessions: ON" : "Sessions: OFF"); CreateButton(btn_toggle_maj_vlines, 292, 86 + Y_OFFSET, 130, 22, g_ShowMajorVLines ? "Maj VLines: ON" : "Maj VLines: OFF");
А обработчики кликов просто переворачивают бит и обновляют его:
if(obj == btn_toggle_session_wicks) { g_ShowSessionWicks = !g_ShowSessionWicks; ObjectSetString(main_chart_id, btn_toggle_session_wicks, OBJPROP_TEXT, g_ShowSessionWicks ? "Sess Wicks: ON" : "Sess Wicks: OFF"); RefreshLines(); return; } if(obj == btn_toggle_sessions) { g_ShowSessions = !g_ShowSessions; ObjectSetString(main_chart_id, btn_toggle_sessions, OBJPROP_TEXT, g_ShowSessions ? "Sessions: ON" : "Sessions: OFF"); RefreshLines(); return; }
11) Рисуем сессии внутри RefreshLines() (одно место для управления ими всеми)
Центральным элементом перерисовки советника является функция RefreshLines(). После управления HTF-объектами, мы либо попросим визуализатор отрисовать сессии (с последними настройками для каждой сессии), либо очистим их. Сохранение этого параметра обеспечивает однократное обновление и один проход очистки.
// Draw sessions if toggled - NOW WITH SEPARATE WICK SETTINGS if(g_ShowSessions) { g_sessions.SetFillColors(g_FillBull, g_FillBear); // no-op for bodies today; future-proof g_sessions.SetShowWicks(g_ShowSessionWicks); g_sessions.SetWickAlpha(g_SessionWickAlpha); g_sessions.RefreshSessions(InpSessionLookback); } else { g_sessions.ClearAll(); }
В более старом советнике функция RefreshLines() обрабатывала только HTF/мелкие визуальные эффекты Благодаря централизованному управлению сессиями, все визуальные эффекты управляются одним таймером / тиком, что позволяет избежать мерцания и условий гонки.
12) Обновление и очистка на тике и deinit
На каждом тике мы пересчитываем состояние высокого уровня: новые бары, а теперь и информационный блок сессии. Во время удаления мы также удаляем сессионные рисунки, чтобы при повторном подключении все начиналось сначала.
void OnTick() { bool need_refresh = false; // ... (bar-change checks for majors/minors) // Always update session info on tick for real-time current session nature UpdateSessionInfo(); if(need_refresh) RefreshLines(); } void DeleteAllHTFLines() { // remove HTF objects // ... // Also clear sessions g_sessions.ClearAll(); }
13) Более высокая панель после восстановления, подходящая для нового раздела
При сворачивании/восстанавливании, высота фона должна соответствовать содержимому. Мы увеличиваем восстановленную высоту, чтобы информация о сессии была полностью видна, не перекрывая другие элементы графика.
void RestoreUI() { UpdateBackgroundHeight(340); // was 250; space for the Session Info CreateAllOtherUIObjects(); ObjectSetString(main_chart_id, btn_minimize, OBJPROP_TEXT, "Minimize"); }
Тестирование
Для этого теста мы подключаем советник непосредственно к живому графику (а не к тестеру стратегий), поскольку наше внимание сосредоточено исключительно на визуальных эффектах на графике. Это позволяет нам убедиться, что визуализация и элементы управления соответствуют нашим целям. Ниже приведено изображение, демонстрирующее обобщенную функцию в действии.

Рис. 3. Конечный результат, включающий в себя Форекс-сессии и отображение информации
Заключение
Сессии Форекс и фондового рынка могут дать трейдерам и финансовым аналитикам более подробную информацию при надлежащем понимании и визуализации. Реализованная здесь идея помогает нам представлять торговые сессии в виде свечных периодов, раскрывая ценную информацию о максимумах, минимумах, ценах открытия и закрытия каждой сессии. Этот подход позволяет нам применять наши традиционные знания о свечах для анализа на уровне сессии, помогая прогнозировать потенциальные движения на следующей сессии, основываясь на характере предыдущих.
Благодаря этой разработке мы внедрили визуальные маркеры и утилиту динамического управления (Market Periods Synchronizer), позволяющие нам переключать, настраивать и синхронизировать отображение сессий непосредственно на графике. Кроме того, на панели информации о сессии отображается информация о ценах открытия, закрытия и направленности (бычьей или медвежьей) как текущей, так и предыдущих сессий, что дает трейдерам быстрое и интуитивно понятное представление основных рыночных сил.
Поскольку бычий или медвежий характер сессии измеряется в течение длительных периодов времени, он часто отражает более широкую предвзятость рынка и тенденции тренда. Понимание этого поведения не только обостряет техническую перспективу, но и укрепляет психологию трейдинга благодаря прямому визуальному наблюдению за динамикой рынка.
Вы можете поэкспериментировать, внести изменения и поделиться своими идеями в комментариях ниже. Вспомогательные исходные файлы прилагаются для ознакомления и дальнейшего изучения.
Основные уроки
| Основной урок: | Описание: |
|---|---|
| Интеграция Классов и Модульная Конструкция | Создание многоразовых модулей, таких как CSessionVisualizer, способствует более чистой архитектуре и упрощению обслуживания. Выделив логику визуализации в отдельный класс, советник приобретает гибкость — разработчики могут изменять визуальные эффекты сессии или повторно использовать компонент в других проектах без переписывания основного кода. |
| Переменные Среды Выполнения и Синхронизация Пользовательского Интерфейса | Зеркальное отображение входных параметров в глобальных параметрах среды выполнения гарантирует, что элементы управления пользовательским интерфейсом в режиме реального времени (такие как ползунки, кнопки и переключатели) могут динамически обновлять визуальное поведение без повторной инициализации советника. Этот подход учит, как создавать адаптивные утилиты для построения графиков с использованием пользовательских интерфейсов на основе объектов. |
| Управление Сессиями и Отображение Данных | Использование структурированных сессионных данных (например, часы начала и окончания торгов по Гринвичу, расчеты цен открытия и закрытия) демонстрирует, как объединить данные графиков в реальном времени с основанной на времени торговой логикой. Это помогает разработчикам сочетать аналитику и визуальные эффекты, чтобы сделать рыночный контекст более интуитивно понятным. |
Вложения
| Название исходного файла | Версия | Описание |
|---|---|---|
| SessionVisualizerTest.mqh | 1.0 | Отдельный тестовый скрипт, предназначенный для проверки рендеринга и поведения класса CSessionVisualizer. Позволяет разработчикам изолировать визуальную логику, обеспечивая корректное отображение прямоугольников сессии, цветов и картирование временных периодов перед интеграцией в более крупные системы. |
| SessionTest.mq5 | 1.0 | Упрощенный советник, разработанный специально для тестирования и демонстрации функциональности заголовка SessionVisualizerTest. |
| SessionVisualizer.mqh | 1.0 | Основная реализация класса, управляющая визуализацией Форекс-сессий. Управляет настройками часовых поясов, цветовым отображением и графической отрисовкой границ сеанса на графике, являясь многоразовым визуальным модулем для любого советника или индикатора, которому требуется основанный на сессии контекст. |
| MarketPeriodsSychronizer_EA.mq5 | 1.02 | Обновленный советник, объединяющий SessionVisualizer и новую информационную панель сессий. Синхронизирует несколько маркеров таймфреймов, заполнение сессий и аналитику в режиме реального времени в единую панель управления, предоставляя трейдерам интерактивный и обучающий инструмент визуализации рыночного периода. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/20005
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Нейросети в трейдинге: разностное моделирование рыночной микроструктуры (Блок разностей)
От новичка до эксперта: Раскрытие секретов теней свечей
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования