English Deutsch 日本語
preview
Изучение Стандартной библиотеки MQL5 (Часть 1): Интеграция и связывание компонентов

Изучение Стандартной библиотеки MQL5 (Часть 1): Интеграция и связывание компонентов

MetaTrader 5Примеры |
71 0
Clemence Benjamin
Clemence Benjamin

Содержание

  1. Введение
  2. Реализация
  3. Тестирование
  4. Заключение


Введение

Многие разработчики, входящие в экосистему MQL5 — от увлеченных новичков до опытных программистов, перешедших из других языков, — сталкиваются с распространенной и неприятной проблемой: они умеют писать код, но им трудно создавать надежные советники профессионального уровня. Стандартная библиотека MQL5 — это мощный набор предварительно созданных, протестированных и оптимизированных классов, разработанных для устранения этой проблемы. Однако само её богатство может показаться чрезмерным. Настоящая проблема заключается не в недостатке компонентов, а в отсутствии четкой дорожной карты — практического руководства по интеграции этих сложных строительных блоков в целостную, функционирующую торговую систему.

Разработчики часто оказываются в ситуации, когда у них есть целый набор высокоточных инструментов, но нет схемы, как собрать из них готовое решение. Чтобы упростить эту сложность, мы воспользуемся аналогией из реальной жизни: подобно тому, как человеческий организм полагается на специализированные органы, работающие под координацией головного мозга, советник полагается на специализированные модули, координируемые его основной программой.

В этой аналогии советник выступает в роли мозга — центрального органа, принимающего решения. Вспомогательные классы библиотеки играют роль его органов, выполняя специализированные функции:

  • Класс CTrade совершает сделки — подобно рукам, выполняющим действия.
  • Класс CIndicators анализирует рыночные данные — подобно глазам, воспринимающим окружающую среду.
  • Класс CAccountInfo отслеживает состояние счета — подобно нервной системе, собирающей и передающей обратную связь.

Рис. 1. Концептуальная программная система

Рис. 2. Концептуальная биологическая система

Эта аналогия носит исключительно иллюстративный характер, призванный сделать абстрактную структуру объектно-ориентированного программирования (ООП) более интуитивно понятной, помогая читателям визуализировать, как каждая часть библиотеки взаимодействует, формируя скоординированную и эффективную торговую систему.

Все это — преимущества объектно-ориентированного программирования: модульность, возможность повторного использования и абстракция, — но в этой серии статей мы намеренно пропускаем подробное теоретическое объяснение, чтобы сначала сосредоточиться на практическом применении. Многие учащиеся лучше понимают концепции на практике, и как только вы начинаете создавать и интегрировать классы на практике, основные принципы ООП начинают обретать естественный смысл.

Для читателей, которые предпочитают изучить теорию ООП перед продолжением, существует множество отличных источников на форуме сообщества MQL5 и за его пределами, которые подробно объясняют инкапсуляцию, наследование и полиморфизм. Однако наш подход в этой статье основан на практике: на реальных примерах, диаграммах и аналогиях, которые сделают ваше знакомство со стандартной библиотекой MQL5 приятным и продуктивным.

К концу этого обсуждения мы выйдем за рамки отдельных примеров и представим генеральный план — исчерпывающее руководство по вплетению мощных элементов стандартной библиотеки MQL5 в прочную структуру профессионального советника, а также откроем способы расширения самой библиотеки. 

Рис. 3: Советник как мозг, а классы как органы

Хотя советник справедливо рассматривается как мозг или основная программа, мы также должны понимать, что сами модули могут быть построены путем включения других модулей — то, что мы называем зависимостями. Точно так же, как мы учимся интегрировать целые библиотеки в программы советников, нам необходимы те же знания при связывании модулей или встраивании одного модуля в другой.

Приведенная выше аналогия показывает, что взаимосвязи между модулями существуют двумя ключевыми способами: как правило, через координацию основной программы, но также и через прямые зависимости. Например, класс CPanel не существует сам по себе — он напрямую включает в себя и зависит от WndObj.mqh и ChartObjectsTxtControls.mqh.

Рис. 4. Взаимодействие между классами

Хорошая новость в том, что эта сеть взаимоотношений не так уж сложна, если ее понять. При использовании любого модуля в советнике, будь то простой класс или сложный зависимый модуль, существуют последовательные, повторяемые шаги, которые гарантируют успешную интеграцию каждый раз. Смотрите фрагмент кода ниже — он выделяет ключевые зависимости, используемые Panel.mqh, и места их включения.

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>

В следующем разделе я расскажу о шагах, которые обычно выполняю при интеграции библиотечных классов в свои проекты. Это поможет вам сформировать прочное понимание, прежде чем мы перейдем к этапу реализации, где мы более подробно рассмотрим практические примеры.

Интеграция какого-либо класса в советник

В большинстве случаев выбор классов для использования происходит после определения общей стратегии или цели вашего советника — нет необходимости перегружать проект ненужными компонентами. Однако в этом разделе мы сосредоточимся не на разработке стратегии, а на изучении самого процесса интеграции.

Наша цель — поделиться необходимыми знаниями и практическими шагами, которые позволят вам уверенно интегрировать любой библиотечный модуль в свои будущие проекты, когда возникнет такая необходимость. В следующем разделе описаны эти шаги, сопровождаемые структурной схемой для иллюстрации процесса.

Последовательность операций:

  1. Анализ – изучение цели класса и цепочки наследования
  2. Включение - добавление директивы #include с указанием корректного пути
  3. Объявление — создание экземпляра объекта в советнике
  4. Инициализация — вызов Create() в OnInit() с параметрами
  5. Во время выполнения — обновление в OnTick(), обработка событий
  6. Очистка - освобождение ресурсов в OnDeinit()

Обработка ошибок:

  • Ошибки компиляции → Проверка зависимостей
  • Сбои создания → Проверка параметров
  • Проблемы во время работы → Мониторинг состояния объекта

Рис. 5. План интеграции библиотеки MQL5

Процесс интеграции модулей

Анализ зависимостей

  • Связи родитель-потомок: например, CPanel → WndObj → ChartObjectsTxtControls
  • Контракты интерфейсов: какие методы должны быть реализованы
  • Поток данных: как информация проходит между модулями

Module Inheritance Hierarchy

Рис. 6. Иерархия наследования модулей

Этапы реализации

  1. Цепочка включений: каждый модуль включает свои прямые зависимости
  2. Настройка наследования: иерархии классов устанавливаются через наследование
  3. Реализация методов: переопределение виртуальных методов по мере необходимости
  4. Изолированное тестирование: тестирование модулей независимо друг от друга перед интеграцией советника

Общие паттерны

  • Многоуровневая архитектура: модули верхнего уровня зависят от модулей нижнего уровня
  • Разделение интерфейсов: модули предоставляют чёткие и специализированные API
  • Внедрение зависимостей: передавайте необходимые объекты вместо жесткого кодирования

Рис. 7. Процесс интеграции модулей

После того как архитектурная основа была прочно заложена, а план интеграции четко определен, мы переходим к этапу реализации, где теория воплощается в практику. Именно здесь мы применяем структурированный процесс интеграции к работающему проекту, превращая концептуальное понимание в работающий советник.

Наш план реализации заключается в создании советника InfoPanel EA — информационной панели, демонстрирующей, как интегрировать класс CPanel и его зависимости в торговую среду. Этот пример покажет, как именно применять каждый этап нашей методологии интеграции для создания чистой, профессиональной и расширяемой системы.


Реализация

При работе с любым библиотечным классом в MQL5 интеграция начинается с понимания того, что представляет собой этот класс на самом деле — его назначение, его происхождение и то, как он вписывается в экосистему элементов управления на графике. CPanel — это больше, чем просто прямоугольная область; это самодостаточный интерфейсный слой, который наследует от CWndObj и управляет собственным жизненным циклом на графике. Он служит архитектурным каркасом для сложных компонентов пользовательского интерфейса. Прежде чем какой-либо код появится в советнике, это понимание формирует концептуальную основу для бесшовной интеграции.

Анализ класса CPanel — в Panel.mqh

Первое знакомство с классом всегда должно носить исследовательский характер. Изучение заголовка Panel.mqh позволяет выявить ключевые взаимосвязи и точки расширения. Класс наследует от CWndObj, что означает, что он наследует способность обрабатывать события и управлять оконными объектами на графике. Внутри он инкапсулирует CChartObjectRectLabel, графический прямоугольник, который задает видимую рамку панели.

Определив его общедоступные точки входа — в частности, Create()  и BorderType() — вы определите ключевые точки управления, с которыми будет взаимодействовать ваш советник. Такой подход заменяет метод проб и ошибок обоснованным проектированием.

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CPanel                                                     |
//| Usage: control that is displayed by                              |
//|             the CChartObjectRectLabel object                     |
//+------------------------------------------------------------------+
class CPanel : public CWndObj
  {
private:
   CChartObjectRectLabel m_rectangle;       // chart object
   //--- parameters of the chart object
   ENUM_BORDER_TYPE  m_border;              // border type

public:
                     CPanel(void);
                    ~CPanel(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- parameters of the chart object
   ENUM_BORDER_TYPE  BorderType(void)       const { return(m_border);                                  }
   bool              BorderType(const ENUM_BORDER_TYPE type);

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_rectangle.Description(m_text));           }
   virtual bool      OnSetColorBackground(void)   { return(m_rectangle.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_rectangle.Color(m_color_border));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPanel::CPanel(void) : m_border(BORDER_FLAT)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPanel::~CPanel(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_rectangle.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set border type                                                  |
//+------------------------------------------------------------------+
bool CPanel::BorderType(const ENUM_BORDER_TYPE type)
  {
//--- save new value of parameter
   m_border=type;
//--- set up the chart object
   return(m_rectangle.BorderType(type));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CPanel::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_rectangle.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CPanel::OnShow(void)
  {
   return(m_rectangle.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CPanel::OnHide(void)
  {
   return(m_rectangle.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CPanel::OnMove(void)
  {
//--- position the chart object
   return(m_rectangle.X_Distance(m_rect.left) && m_rectangle.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CPanel::OnResize(void)
  {
//--- resize the chart object
   return(m_rectangle.X_Size(m_rect.Width()) && m_rectangle.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CPanel::OnChange(void)
  {
//--- set up the chart object
   return(CWndObj::OnChange() && m_rectangle.BorderType(m_border));
  }
//+------------------------------------------------------------------+
   

Позже каждая из этих строк напрямую отразится в соответствующем шаге инициализации советника.

Этапы интеграции

Этап 1. Включение заголовка и зависимостей

Как только цель становится ясна, следующим шагом является включение. Советник должен получить доступ к CPanel и её базовым классам с помощью соответствующих директив #include. Структура файлов MQL5 строго относится к путям включения, поэтому всегда проверяйте, что ваш файл Panel.mqh и поддерживающие его заголовочные файлы (WndObj.mqh, ChartObjectsTxtControls.mqh и т. д.) доступны в каталоге Include проекта или глобальной директории.

#include <Controls\Panel.mqh>  // gives access to CPanel and its internals

На этом этапе советник “знает”, что класс панели существует — он готов к созданию экземпляра.

Этап 2. Объявление объекта

Объявление определяет область видимости и время жизни панели в советнике. Большинство разработчиков предпочитают объявлять CPanel как указатель на глобальном уровне, чтобы объект сохранялся между вызовами обработчиков событий и мог быть корректно удалён на этапе очистки.

CPanel *mainPanel = NULL;

Это делает объект доступным во всех обработчиках событий (OnInit, OnTick, OnDeinit и т. д.), обеспечивая согласованный жизненный цикл.

Этап 3. Инициализация — ввод панели в работу

Создание происходит внутри OnInit(), и именно здесь дизайн заголовка встречается с выполнением. Create() требует шесть аргументов: идентификатор графика, уникальное имя, целевое подокно и четыре координаты, определяющие прямоугольник панели. Всегда проверяйте, что вызов возвращает true; результат false часто указывает на проблему с параметром или зависимостью.

int OnInit()
{
   mainPanel = new CPanel();
   if(!mainPanel.Create(ChartID(), "InfoPanel", 0, 10, 10, 400, 120))
   {
      Print("Panel creation failed.");
      delete mainPanel;
      mainPanel = NULL;
      return(INIT_FAILED);
   }

   mainPanel.BorderType(BORDER_RAISED);
   mainPanel.Text("Information Panel");
   return(INIT_SUCCEEDED);
}

Этот фрагмент демонстрирует минимальную, но полную инициализацию. Теперь панель визуально существует на графике, занимая указанный прямоугольник.

Этап 4. Управление во время выполнения

Сама панель может не нуждаться в постоянных обновлениях, если она статична, но именно здесь расширяется ваша логика интеграции. Возможно, вы захотите встроить дочерние элементы управления, такие как метки или кнопки, или периодически обновлять содержимое панели.

Для динамических обновлений событие OnTimer() обеспечивает стабильный механизм обновления, не перегружая цикл OnTick().

void OnTimer()
{
   // Placeholder for runtime UI updates
}

Каждое обновление может обновлять данные, отображаемые внутри панели, без перерисовки объекта.

Этап 5. Очистка и обработка ошибок

Для предотвращения утечек памяти необходимо уничтожать каждый динамически созданный объект. В OnDeinit() вызовите оператор delete и установите указатель в NULL. Это обеспечит корректное освобождение памяти и предотвратит появление висячих объектов графика.

void OnDeinit(const int reason)
{
   if(mainPanel)
   {
      mainPanel.OnHide();
      delete mainPanel;
      mainPanel = NULL;
   }
}

Если компиляция завершится неудачей, проверьте отсутствующие файлы типа include или неопределенные ссылки. Ошибки создания обычно связаны с некорректными координатами или конфликтами имен, в то время как проблемы с мерцанием во время работы или событиями часто возникают из-за перекрывающихся объектов графика.

Анализ CChartObjectText в ChartObjectsTxtControls.mqh

В этом разделе мы сосредоточимся на интерфейсном слое заголовка ChartObjectsTxtControls.mqh. Для ясности и краткости мы выделили только публичную и верхнеуровневую часть класса — части, определяющие способ взаимодействия с ним. Полный исходный код намного длиннее, но для нашего анализа нам нужен только публичный интерфейс, который представляет собой “входную дверь” класса.

Такой выборочный подход является преднамеренным. Суть объектно-ориентированного программирования (ООП) — это абстракция: нам не нужно знать, как работает внутреннее устройство, если мы понимаем интерфейс и как правильно его использовать. Интерфейс определяет доступные методы, их параметры и ожидаемое поведение — этого достаточно для эффективного построения интеграций.

Наша цель здесь — проанализировать заголовок, чтобы понять его назначение и определить ключевые точки взаимодействия — методы, которые мы будем вызывать из нашего основного советника для описания или манипулирования текстом на графике. В данном случае мы включили этот заголовок не для построения панели (как когда он служил зависимостью CPanel), а исключительно для текстового описания в нашем советнике.

Когда он ранее использовался в качестве зависимости, нам не нужно было особо сосредотачиваться на его внутренней логике — это делала родительская панель CPanel. Однако в этом образовательном контексте мы целенаправленно изучаем его, чтобы понять, как построен каждый класс и как использовать его независимо.

Приведённый ниже фрагмент показывает интерфейс CChartObjectText, который предоставляет все необходимые методы для работы с текстовыми объектами непосредственно на графике. Среди них — Create(), FontSize() и Anchor() — наши ключевые точки взаимодействия при интеграции текстовых элементов в панель или советник. Это методы, позволяющие задавать положение пикселей, устанавливать стили шрифтов, управлять выравниванием и видимостью — всё, что нужно для формирования текстовой части нашей панели InfoPanel.

//+------------------------------------------------------------------+
//|                                      ChartObjectsTxtControls.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//| All text objects.                                                |
//+------------------------------------------------------------------+
#include "ChartObject.mqh"
//+------------------------------------------------------------------+
//| Class CChartObjectText.                                          |
//| Purpose: Class of the "Text" object of chart.                    |
//|          Derives from class CChartObject.                        |
//+------------------------------------------------------------------+
class CChartObjectText : public CChartObject
  {
public:
                     CChartObjectText(void);
                    ~CChartObjectText(void);
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const datetime time,const double price);
   //--- method of identifying the object
   virtual int       Type(void) const override { return(OBJ_TEXT); }
   //--- methods of access to properties of the object
   double            Angle(void) const;
   bool              Angle(const double angle) const;
   string            Font(void) const;
   bool              Font(const string font) const;
   int               FontSize(void) const;
   bool              FontSize(const int size) const;
   ENUM_ANCHOR_POINT Anchor(void) const;
   bool              Anchor(const ENUM_ANCHOR_POINT anchor) const;
   //--- methods for working with files
   virtual bool      Save(const int file_handle) override;
   virtual bool      Load(const int file_handle) override;
  };

Этапы интеграции CChartObjectText в советник

Этап 1. Включение

Мы размещаем файл ChartObjectsTxtControls.mqh в верхней части советника, чтобы определения классов были видны. Мы используем тот же путь, что и библиотека.

Если вы уже включили Panel.mqh, этот заголовочный файл часто подтягивается, но явное его включение в советник делает намерения и зависимости понятными.

#include <ChartObjects\ChartObjectsTxtControls.mqh>  // provides CChartObjectLabel et al.

Этап 2. Объявление

Мы объявляем объекты меток там, где функции жизненного цикла советника могут получить к ним доступ — в глобальной области видимости. Используйте объекты стека для классов текста графика (они управляют прикреплением внутри) или указатели, если вы предпочитаете семантику создания/удаления. Тщательно называйте метки (уникальные для каждого графика). Если вы ожидаете несколько меток, используйте понятные паттерны именования: "InfoPanel_123_txt1".

// global declarations
CChartObjectLabel txtDesc;    // pixel-based label (good for panel body)

Этап 3. Инициализация (OnInit)

Далее мы создаем метку внутри OnInit(), используя сигнатуру, показанную в заголовочном файле ChartObjectsTxtControls.mqh: Create(long chart_id, const string name, const int window, const int X, const int Y). Размещаем её относительно области панели, чтобы обеспечить согласованность компоновки.

Если создание метки не удается, запишите в лог полезную информацию (идентификатор графика, название, координаты) и прервите инициализацию.

int OnInit()
{
  string txt_name = panel_name + "_txtDesc";
  int left = PANEL_X1 + PADDING;
  int top  = PANEL_Y1 + PADDING + 18;

  if(!txtDesc.Create(ChartID(), txt_name, 0, left, top))
  {
    Print("txtDesc.Create failed");
    return(INIT_FAILED);
  }

  txtDesc.Description("Hello trader! I am InfoPanel EA");
  txtDesc.FontSize(11);
  // txtDesc.Anchor(ANCHOR_LEFT_UPPER); // if API available
  return(INIT_SUCCEEDED);
}

Этап 4. Во время выполнения (OnTick/OnTimer/OnChartEvent)

Сведите к минимуму объём работы во время выполнения для статического текста. Если вам нужны динамические поля, обновляйте Description() из OnTimer() (устанавливайте EventSetTimer() в OnInit()), а не каждый тик. Если вы добавляете интерактивность (кнопки или редактирование из того же заголовочного файла), перенаправьте OnChartEvent() и проверьте sparam на наличие кликов по объектам.

Для нескольких строк используйте либо несколько экземпляров CChartObjectLabel, расположенных вертикально друг над другом, либо сгенерируйте \n и проверьте, принимает ли Description() переносы строк; если нет, предпочтительнее использовать отдельные метки.

void OnTimer()
{
  // e.g., update price line inside the panel
  txtDesc.Description("Bid: " + DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits));
}

Этап 5. Очистка (OnDeinit)

Удалите объект графика при завершении работы советника. Используйте метод класса Delete() (или ObjectDelete() в качестве резервного варианта). Не вызывайте внутренние защищенные методы других классов — просто удалите свою метку и освободите любой указатель панели.

void OnDeinit(const int reason)
{
  txtDesc.Delete();        // removes the chart object
  // delete panel pointer here if allocated
  EventKillTimer();        // if used
}

Соединение всех частей в советник

Окончательная интеграция класса CPanel в наш советник InfoPanel завершает базовый этап нашего исследования стандартной библиотеки. Эта панель не является интерактивным дашбордом или контроллером торговых операций — это статическое отображение информации, простое, но профессионально оформленное информационное отображение на графике, которое наглядно показывает, какой продукт используется. Многие разработчики могли бы выбрать аналогичный дизайн, чтобы отображать идентификацию и подробности своего советника непосредственно на графике, избегая ограничений, связанных с именем графика по умолчанию, или необходимости копаться в свойствах. Это ненавязчивая, но практичная визуальная подпись — визуальный тег, который делает рабочее пространство графика более понятным.

Хотя концепция может показаться скромной, ее значимость заключается в методе, а не в масштабе. Здесь мы отработали полный цикл интеграции классов: от включения заголовка стандартной библиотеки до создания и настройки экземпляра CPanel и дополнения её текстовыми элементами графика для осмысленного отображения. Этот процесс отражает саму суть того, как структурируются и управляются профессиональные компоненты MQL5. Речь идет о понимании архитектурного ритма — включение, объявление, создание и управление — который вы впоследствии будете использовать для гораздо более сложных систем.

Этот первый проект намеренно прост, потому что он закладывает основу для дальнейшего расширения. Как только вы освоите CPanel, добавление других элементов, таких как CChartObjectLabel для более насыщенного текстового вывода или даже CPicture для брендинга, станет естественным. Вместе эти статические элементы образуют небольшую, но полноценную экосистему визуализации информации, готовую к дальнейшему развитию по мере продвижения нашего исследования.

Более широкая цель состоит в том, чтобы приучить себя мыслить модульно, рассматривать стандартную библиотеку не как жесткую структуру, а как гибкую основу, которую можно расширять бесконечно. Каждый класс — CPanel, CChartObjectLabel, CPicture и другие — является строительным блоком. То, что сегодня начинается как простая панель отображения, впоследствии может превратиться в комплексное рабочее пространство для визуализации данных, настраиваемых элементов управления и адаптивных компоновок.

Поэтому наш путь с CPanel — это не просто создание статической информационной панели; это начало освоения логики и мастерства, необходимых для построения целых систем, начиная со стандартной библиотеки — шаг за шагом, слой за слоем.

//+------------------------------------------------------------------+
//| InfoPanel_EA.mq5                                                 |
//| Minimal example: creating a CPanel and a label text showing      |
//| a short EA description with panel background & border. We will   |
//| also add a logo to explore CPicture.                             |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property version   "1.0"

#include <Controls/Panel.mqh>                        // Panel header (correct path)
#include <ChartObjects\ChartObjectsTxtControls.mqh>  // for CChartObjectLabel

//---- Globals --------------------------------------------------------
CPanel             *infoPanel = NULL;   // pointer to panel (allocate/free)
CChartObjectLabel   txtDesc;            // label text inside the panel

// layout (pixels)
int PANEL_X1 = 10;
int PANEL_Y1 = 30;
int PANEL_X2 = 300;   // widened to give text room
int PANEL_Y2 = 100;
int PADDING  = 10;

// short description that fits comfortably on one line
string EA_DESCRIPTION = "Hello trader! I am InfoPanel EA";

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
{
   // allocate the panel object
   infoPanel = new CPanel();
   if(infoPanel == NULL)
   {
      Print("InfoPanel: allocation failed");
      return(INIT_FAILED);
   }

   // unique panel name
   string panel_name = "InfoPanel_" + IntegerToString((int)ChartID());

   // create the panel on the current chart (subwindow 0 = main)
   if(!infoPanel.Create(ChartID(), panel_name, 0, PANEL_X1, PANEL_Y1, PANEL_X2, PANEL_Y2))
   {
      Print("InfoPanel: Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }

   // appearance: border type, header text, background and border colors
   infoPanel.BorderType(BORDER_RAISED);
   infoPanel.Text("InfoPanel");

   // Set a visible background and border color.
   // Use standard color constants; change them if you prefer another palette.
   // If your library expects ARGB or different color encoding, adapt accordingly.
   infoPanel.ColorBackground(clrSilver); // panel background
   infoPanel.ColorBorder(clrBlack);      // panel border

   // compute child label position inside panel (some padding below header)
   int left = PANEL_X1 + PADDING;
   int top  = PANEL_Y1 + PADDING + 18; // leave header area

   // create a CChartObjectLabel as panel body (unique name)
   string txt_name = panel_name + "_txtDesc";
   if(!txtDesc.Create(ChartID(), txt_name, 0, left, top))
   {
      Print("InfoPanel: txtDesc.Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }

   // set the short description and font size using the correct API
   txtDesc.Description(EA_DESCRIPTION);
   txtDesc.FontSize(11);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // remove the label object
   txtDesc.Delete();

   // free the panel (do NOT call protected methods)
   if(infoPanel != NULL)
   {
      delete infoPanel;
      infoPanel = NULL;
   }

   // safe kill of timer if later added
   EventKillTimer();
}

//+------------------------------------------------------------------+
//| Expert tick function (kept intentionally light)                  |
//+------------------------------------------------------------------+
void OnTick()
{
   // static info panel — no per-tick UI updates
}

//+------------------------------------------------------------------+
//| Chart event handler (optional extension point)                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{        
// placeholder for future interactive controls
}

Рис. 8. Первоначальное тестирование

Теперь, сделав еще один шаг, мы добавим на панель логотип BMP, чтобы придать ей узнаваемый вид. Я всегда предпочитал использовать инструменты с открытым исходным кодом для создания изображений. GIMP — отличный выбор, поскольку он позволяет быстро и легко экспортировать изображения в формате BMP. Конечно, вы можете использовать любое другое программное обеспечение, с которым вам удобно работать. После того, как ваш логотип создан и сохранен в папке "Изображения" вашего терминала для быстрого доступа, мы можем перейти к изучению класса CPicture и спланировать, как интегрировать его в нашу панель.

Ниже показаны два изображения. На рис. 9 показано, где будет расположен логотип на нашей панели, а на рис. 10 — логотип, который я создал с помощью GIMP. При экспорте изображения в формате BMP убедитесь, что используете 24-битную глубину цвета, один слой и отсутствие альфа-канала. Эти настройки отлично работали во время тестирования — другие конфигурации экспорта приводили к изображениям, которые некорректно отображались на панели. Показанное здесь изображение логотипа не в корректном формате BMP, поэтому оно не будет работать должным образом. Я использовал версию PNG, которая поддерживается для целей публикации. Логотип, прикрепленный к статье, вы найдете внизу.

LOGO

Рис. 9. Пространство логотипа советника

BMP logo

Рис. 10. Логотип размером 56x59 пикселей

Разбор и подготовка класса CPicture к интеграции

Класс CPicture расширяет основу, заложенную нами ранее с помощью CPanel и CChartObjectLabel. Подобно тому, как панель предоставляла нам контейнер, а метка — текст, этот класс предлагает простой способ отображения растровых изображений на графике — идеально подходит для нашего следующего усовершенствования, где мы хотим добавить миниатюру логотипа на информационную панель.

По своей сути, CPicture представляет собой небольшую обертку над объектом графика CChartObjectBmpLabel. Он наследует от CWndObj, то есть следует тем же методам жизненного цикла (Create, OnShow, OnHide и т. д.), которые мы видели ранее. Такая конструкция гарантирует, что мы сможем интегрировать его в наш советник, используя те же знакомые этапы, что и с предыдущими классами — выделение памяти, создание, настройка и очистка.

При более внимательном рассмотрении выделяются несколько ключевых моментов. Основной метод создания, Create(chart, name, subwin, x1, y1, x2, y2), определяет область объекта на графике в пиксельных координатах, как и в CPanel. После создания мы можем назначить фактическое изображение с помощью BmpName(name), которое указывает на файл .bmp, расположенный в папке Images терминала. Метод Border(value) регулирует видимую ширину или толщину границы области изображения, хотя для корректного отображения логотипа мы, скорее всего, установим это значение равным нулю. Внутри эти два метода обновляют базовый объект графика через обработчик OnChange().

Наши ключевые точки взаимодействия при интеграции

Для достижения своих целей нам нужно сосредоточиться лишь на нескольких ключевых точках взаимодействия:

  • Create() – инициализирует объект на графике, используя уникальное имя и пиксельные координаты.
  • BmpName() – назначает растровый файл для отображения, например, логотип нашего советника.
  • Border() – при необходимости определяет значения границы или ширины (необязательно для логотипов).
  • Деструктор или оператор delete — для безопасного освобождения памяти при удалении советника.
//+------------------------------------------------------------------+
//|                                                      Picture.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsBmpControls.mqh>
//+------------------------------------------------------------------+
//| Class CPicture                                                   |
//| Note: image displayed by                                         |
//|             the CChartObjectBmpLabel object                      |
//+------------------------------------------------------------------+
class CPicture : public CWndObj
  {
private:
   CChartObjectBmpLabel m_picture;          // chart object
   //--- parameters of the chart object
   int               m_border;              // border width
   string            m_bmp_name;            // filename

public:
                     CPicture(void);
                    ~CPicture(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- parameters of the chart object
   int               Border(void) const { return(m_border); }
   bool              Border(const int value);
   string            BmpName(void) const { return(m_bmp_name); }
   bool              BmpName(const string name);

protected:
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnChange(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPicture::CPicture(void) : m_border(0),
                           m_bmp_name(NULL)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPicture::~CPicture(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CPicture::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_picture.Create(chart,name,subwin,x1,y1))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set border width                                                 |
//+------------------------------------------------------------------+
bool CPicture::Border(const int value)
  {
//--- save new value of parameter
   m_border=value;
//--- set up the chart object
   return(m_picture.Width(value));
  }
//+------------------------------------------------------------------+
//| Set image                                                        |
//+------------------------------------------------------------------+
bool CPicture::BmpName(const string name)
  {
//--- save new value of parameter
   m_bmp_name=name;
//--- set up the chart object
   return(m_picture.BmpFileOn(name));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CPicture::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_picture.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CPicture::OnShow(void)
  {
   return(m_picture.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CPicture::OnHide(void)
  {
   return(m_picture.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CPicture::OnMove(void)
  {
//--- position the chart object
   return(m_picture.X_Distance(m_rect.left) && m_picture.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CPicture::OnChange(void)
  {
//--- set up the chart object
   return(m_picture.Width(m_border) && m_picture.BmpFileOn(m_bmp_name));
  }
//+------------------------------------------------------------------+

С помощью этих простых хэндлов у нас есть все необходимое для интеграции графического компонента в нашу панель.

Планирование интеграции

Теперь, когда мы понимаем, как работает CPicture, можно спланировать, как добавить его в наш InfoPanel_EA. Файл изображения — наш логотип — сначала должен быть подготовлен в формате BMP. Как упоминалось ранее, такие инструменты, как GIMP, упрощают экспорт в BMP. После того, как у нас есть файл, мы помещаем его в папку Images терминала (MQL5/Images/), чтобы советник мог легко получить к нему доступ по имени.

В своем коде мы:

  1. Включим заголовочный файл Picture.mqh.
  2. Объявим глобальный указатель для нашего экземпляра CPicture, как мы это делали для панели.
  3. Создадим объект в OnInit(), аккуратно расположив его внутри или непосредственно над панелью.
  4. Укажем имя изображения с помощью BmpName("logo.bmp"), чтобы загрузить выбранный файл.
  5. Освободим ресурсы в OnDeinit(), чтобы обеспечить корректное завершение работы.

Таким образом, наша панель превратится из простого текстового отображения в область с фирменной информацией — тонкий, но значимый шаг, который делает презентацию советника более профессиональной и узнаваемой.

Обоснование этого этапа

Хотя это дополнение может показаться незначительным, оно углубляет наше понимание того, как любой визуальный класс из стандартной библиотеки может быть объединен на практике. Каждая интеграция подкрепляет паттерн: безопасное создание, настройка и управление жизненным циклом. К моменту завершения интеграции логотипа мы объединим три различных стандартных класса — CPanel, CChartObjectLabel и CPicture — в один целостный интерфейс.

Это подготовит нас к следующему разделу, где мы запустим полную интеграцию и разместим логотип нашего советника на самой панели. Это завершит создание нашей статической информационной панели и послужит визуальным подтверждением всего, что мы изучили до сих пор. В следующем разделе будет продемонстрировано, как эти классы могут гармонично сосуществовать, делая наш советник одновременно информативным и визуально привлекательным.

//+------------------------------------------------------------------+
//| InfoPanel_EA.mq5                                                 |
//| Info panel with logo placed inside the panel and on foreground   |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property version   "1.0"

#include <Controls\Panel.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
#include <Controls\Picture.mqh>

//---- Globals --------------------------------------------------------
CPanel             *infoPanel = NULL;
CChartObjectLabel   txtDesc;
CPicture           *logo = NULL;

// layout (pixels)
int PANEL_X1 = 10;
int PANEL_Y1 = 30;
int PANEL_X2 = 300;
int PANEL_Y2 = 100;
int PADDING  = 10;

// logo size and filename
int LOGO_SIZE = 56;
string LOGO_FILE = "Logo.bmp";

// short description
string EA_DESCRIPTION = "Hello trader! I am InfoPanel EA";

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
{
   // 1. Create panel
   infoPanel = new CPanel();
   if(infoPanel == NULL)
   {
      Print("InfoPanel: allocation failed");
      return(INIT_FAILED);
   }

   string panel_name = "InfoPanel_" + IntegerToString((int)ChartID());

   if(!infoPanel.Create(ChartID(), panel_name, 0, PANEL_X1, PANEL_Y1, PANEL_X2, PANEL_Y2))
   {
      Print("InfoPanel: Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }

   infoPanel.BorderType(BORDER_RAISED);
   infoPanel.Text("InfoPanel");
   infoPanel.ColorBackground(clrSilver);
   infoPanel.ColorBorder(clrBlack);

   // 2. Create label inside panel
   int label_left = PANEL_X1 + PADDING;
   int label_top = PANEL_Y1 + PADDING + 18;
   string txt_name = panel_name + "_txtDesc";
   if(!txtDesc.Create(ChartID(), txt_name, 0, label_left, label_top))
   {
      Print("InfoPanel: txtDesc.Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }
   txtDesc.Description(EA_DESCRIPTION);
   txtDesc.FontSize(11);

   // 3. Set panel as background
   if(!ObjectSetInteger(ChartID(), panel_name, OBJPROP_BACK, true))
      PrintFormat("InfoPanel: unable to set OBJPROP_BACK for '%s' (non-fatal).", panel_name);

   // 4. Compute logo coordinates
   int logo_right = PANEL_X2 - PADDING;
   int logo_left = logo_right - LOGO_SIZE;
   int logo_top = PANEL_Y1 + PADDING;
   int logo_bottom = logo_top + LOGO_SIZE;
   string logo_name = panel_name + "_logo";

   // 5. Create logo
   logo = new CPicture();
   if(logo == NULL)
   {
      Print("InfoPanel: logo allocation failed (continuing without logo)");
   }
   else
   {
      if(!logo.Create(ChartID(), logo_name, 0, logo_left, logo_top, logo_right, logo_bottom))
      {
         Print("InfoPanel: logo.Create() failed (continuing without logo)");
         delete logo; logo = NULL;
      }
      else
      {
         // Ensure logo is in foreground
         if(!ObjectSetInteger(ChartID(), logo_name, OBJPROP_BACK, false))
            Print("InfoPanel: Failed to set OBJPROP_BACK=false for logo (non-fatal).");

         // Log coordinates
         PrintFormat("InfoPanel: Logo coords: left=%d, top=%d, right=%d, bottom=%d", logo_left, logo_top, logo_right, logo_bottom);

         // Load built-in logo.bmp with correct relative path (no fullpath needed).You can use custom logo names as long you update the code.
         string bmp_path = "\\Images\\Logo.bmp";
         bool ok = logo.BmpName(bmp_path);
         PrintFormat("InfoPanel: logo.BmpName('%s') returned %s", bmp_path, ok ? "true" : "false");
         if(!ok)
         {
            Print("InfoPanel: LOGO load failed. Verify MT5 installation has MQL5\\Images\\dollar.bmp.");
         }
         else
         {
            Print("InfoPanel: Logo loaded successfully!");
         }

         logo.Border(LOGO_SIZE);
         ChartRedraw(ChartID()); // Force redraw
      }
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(logo != NULL) { delete logo; logo = NULL; }
   txtDesc.Delete();
   if(infoPanel != NULL) { delete infoPanel; infoPanel = NULL; }
   EventKillTimer();
}

//+------------------------------------------------------------------+
//| OnTick                                                          |
//+------------------------------------------------------------------+
void OnTick()
{
}

//+------------------------------------------------------------------+
//| Chart events                                                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
}

Разместив логотип CPicture внутри прямоугольника CPanel и выведя его на передний план, мы провели небольшую, но эффективную демонстрацию: объединили три отдельных класса стандартной библиотеки — CPanel, CChartObjectLabel и CPicture — в один целостный элемент пользовательского интерфейса. Сначала мы создали панель, затем добавили описательную метку и, наконец, вычислили точные пиксельные координаты, чтобы вставить логотип так, чтобы он аккуратно располагался в правой части панели. Вызов ObjectSetInteger(..., OBJPROP_BACK, true) для панели и OBJPROP_BACK = false для логотипа позволили нам контролировать порядок отрисовки, так что изображение отображается над фоном, но позади других потенциальных интерактивных объектов. Мы также использовали ChartRedraw(), чтобы гарантировать, что терминал перерисовывается сразу после загрузки растрового изображения. Все эти шаги следовали одному и тому же ритму интеграции: включение, объявление, создание, настройка, проверка возвращаемых значений и очистка.

Из этого упражнения вышло несколько конкретных уроков об интеграции классов. Во-первых, важны расчёт координат и дисциплина именования — вычисляйте позиции относительно прямоугольника панели и используйте уникальные, предсказуемые имена объектов (мы использовали panel_name + "_logo"), чтобы избежать конфликтов и упростить отладку. Во-вторых, внешние ресурсы требуют осторожной обработки: мы сохранили растровое изображение в MQL5\Images, передали его относительный путь ("\Images\Logo.bmp") в функцию BmpName() и логировали результат, чтобы советник оставался устойчивым, если файл отсутствует. В-третьих, соблюдайте контракт жизненного цикла каждого класса: мы выделяли объекты с помощью new, проверяли каждый вызов Create() и BmpName() и всегда удаляли объекты в OnDeinit(), чтобы избежать появления лишних объектов графика или утечки памяти. В-четвертых, управление слоями и перерисовкой — это практические навыки: OBJPROP_BACK и ChartRedraw() позволяют нам управлять видимостью и обновлением предсказуемым образом.

Наконец, эта работа подтверждает повторяемый паттерн, применимый к любому классу стандартной библиотеки: определить общедоступные ключевые точки взаимодействия (создать, настроить методы-установщики, такие как Border()/BmpName() или Text()), интегрировать их в жизненный цикл советника (OnInit → runtime → OnDeinit), проверять каждый шаг с помощью четкого логирования и вычислять геометрию относительно существующих элементов, чтобы компоновка оставалась поддерживаемой. То, что выглядит как крошечная функция — логотип внутри панели — на самом деле является компактной площадкой для отработки более широкого набора навыков, которые мы развиваем: компоновка небольших, хорошо работающих модулей в более крупные, профессиональные интерфейсы. Теперь мы можем уверенно расширять панель (адаптивная компоновка, дополнительные статические строки метаданных или необязательные неинтерактивные значки), зная, что любой класс из стандартной библиотеки может быть интегрирован с использованием того же самого дисциплинированного подхода.


Тестирование

После компиляции кода в MetaEditor 5 мы разместили его на графике, чтобы наблюдать за нашей небольшой информационной панелью с описанием и логотипом — как показано на изображении ниже. Эта панель и ее компоненты могут рассматриваться как визуальный образ советника. Ту же идею можно применить для придания вашим будущим продуктам уникального и узнаваемого присутствия на графике. На данный момент мы можем гордиться тем, что успешно продемонстрировали концепцию и углубили наше понимание процесса интеграции.

Testing the InfoPanel_EA

Рис. 11. Тестирование InfoPanel_EA на графике


Заключение

В ходе этого упражнения мы успешно разработали повторяемую процедуру интеграции классов стандартной библиотеки в советник. Начиная с создания панели CPanel и заканчивая добавлением описательной метки и логотипа с помощью CChartObjectLabel и CPicture, мы изучили, как можно комбинировать и контролировать различные компоненты пользовательского интерфейса для получения качественного и функционального результата. Эта небольшая, но полная демонстрация отражает ту же архитектурную дисциплину, которая требуется для более крупных и сложных систем.

Цель здесь состояла не только в создании статической информационной панели, но и в усвоении рабочего паттерна — включение, объявление, инициализация, настройка и очистка — который универсально применяется во всей экосистеме классов MQL5. По мере дальнейшего продвижения в серии материалов по изучению стандартной библиотеки MQL5 это базовое упражнение упростит нашу будущую работу с более сложными элементами управления и составными модулями.

Хотя возможности для уточнений и творческих вариаций еще остаются, основная идея была достигнута: понимание того, как взаимодействуют модульные классы и как небольшие многократно используемые части могут превратиться в полноценные приложения. Повторение всячески поощряется — воссоздайте этот пример, поэкспериментируйте с пользовательскими цветами, альтернативными компоновками или новыми классами, такими как CButton, CDialog или CCheckBox. Каждая итерация углубляет ваше понимание того, как работает объектная модель MQL5.

Помните, модульность — ключ к прогрессу. Многие мощные строительные блоки уже существуют в стандартной библиотеке; наша задача как разработчиков — собрать их в интеллектуальные системы. Если вы стремитесь овладеть этим ремеслом, посвятите время изучению объектно-ориентированного программирования (ООП) — это даст вам понимание того, как проектировать, расширять и поддерживать собственные масштабируемые фреймворки.

Это упражнение может показаться простым на первый взгляд, но это важный шаг на вашем пути к профессиональной разработке на MQL5. Мы затронули многое, но впереди еще много интересного. Вложенный файл содержит полную реализацию — не стесняйтесь тестировать, дорабатывать и делиться своими мыслями. Давайте сделаем это обсуждение еще более содержательным с помощью ваших комментариев и креативных дополнений под этой статьей.


Название файла Описание
InfoPanel_EA.mq5 Демонстрирует, как интегрировать несколько классов стандартной библиотеки в рамках советника. Советник создает информационную панель на графике с помощью CPanel, добавляет текстовую метку с описанием с помощью CChartObjectLabel и встраивает изображение логотипа с помощью класса CPicture. Этот пример служит основой для изучения модульного построения пользовательского интерфейса и взаимодействия классов в MQL5.
Logo.zip  Содержит логотип в поддерживаемом формате BMP, который можно извлечь в каталог Images терминала. Однако рекомендуем создать собственную пользовательскую версию, используя инструменты с открытым исходным кодом, такие как GIMP, для более персонализированного процесса разработки.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19834

Прикрепленные файлы |
Logo.zip (1.55 KB)
Автоматизация торговых стратегий на MQL5 (Часть 38): Торговля по скрытым дивергенциям RSI с фильтрами угла наклона Автоматизация торговых стратегий на MQL5 (Часть 38): Торговля по скрытым дивергенциям RSI с фильтрами угла наклона
В этой статье мы создадим советник на MQL5, который определяет скрытые дивергенции RSI по свинговым точкам с заданным параметром силы, ограничением по количеству баров между свинговыми точками, допуском и фильтрами угла наклона ценовой линии и линии RSI. Советник открывает позиции на покупку и продажу по подтверждённым сигналам с фиксированным размером лота, уровнями Stop Loss и Take Profit, заданными в пипсах (в исходной статье — pips; в коде расчёт выполняется через _Point), а также опциональным трейлинг-стопом для управления рисками.
Возможности Мастера MQL5, которые вам нужно знать (Часть 83): Использование паттернов стохастического осциллятора и FrAMA — поведенческие архетипы Возможности Мастера MQL5, которые вам нужно знать (Часть 83): Использование паттернов стохастического осциллятора и FrAMA — поведенческие архетипы
Стохастический осциллятор и фрактальная адаптивная скользящая средняя образуют еще одну пару взаимодополняющих индикаторов, которую можно использовать в советнике MQL5. Стохастический осциллятор позволяет точно выявлять изменения моментума, а FrAMA подтверждает преобладающие тренды. Как и прежде, для изучения этой пары индикаторов используем Мастер MQL5, чтобы создать советник и проверить ее потенциал.
От начального до среднего уровня: Очереди, списки и деревья (III) От начального до среднего уровня: Очереди, списки и деревья (III)
В этой статье мы сделаем следующий шаг в понимании того, что такое связный список и как он работает. Хотя содержание данной статьи может показаться довольно сложным и запутанным для новичков, я постараюсь объяснить его максимально доступным языком. Это поможет вам понять, почему и когда следует использовать связные списки.
Автоматизация торговых стратегий на MQL5 (Часть 37): Поиск дивергенций и конвергенций RSI с визуализацией на графике Автоматизация торговых стратегий на MQL5 (Часть 37): Поиск дивергенций и конвергенций RSI с визуализацией на графике
В этой статье мы создадим советник на MQL5, который определяет обычные дивергенции RSI по свинговым точкам с заданным параметром силы, ограничениями по числу баров и допуском. Он открывает позиции по бычьим и медвежьим сигналам с фиксированным объёмом позиции, уровнями Stop Loss и Take Profit в пунктах (pips) и опциональным трейлинг-стопом. На графике отображаются цветные линии и подписи свинговых точек, чтобы лучше видеть логику работы стратегии.