Создание торговой панели администратора на MQL5 (Часть IX): Организация кода (V). Класс AnalyticsPanel
Содержание
Введение
В предыдущих разделах я ввел концепцию аналитической панели, которая на тот момент состояла в основном из статических элементов интерфейса, таких как круговая диаграмма, отображающая соотношение прибылей и убытков. Однако этим компонентам не хватало возможности обновления данных в реальном времени, что ограничивало их полезность в динамичных торговых средах. В рамках этой темы мы делаем значительный шаг вперед, улучшая как дизайн, так и функциональность интерфейса, уделяя особое внимание тому, как получать и отображать торговые данные в реальном времени.
Наш подход к разработке использует стандартную библиотеку MQL5, в частности, классы из каталога \Include\Controls\. Мы расширим и настроим существующие классы, такие как CLabel, CEdit и CDialog, для создания адаптивных, управляемых данными компонентов пользовательского интерфейса. Они составляют основу нашей постоянно развивающейся торговой панели администратора, которая разработана как модульный инструмент с множеством интерфейсов, предлагающий комплексное управление счетом и стратегиями, а также интерфейс связи через Telegram для удаленного управления и уведомлений в реальном времени.
Хотя основной целью остается разработка этой усовершенствованной панели, описанные здесь методы — особенно те, которые включают сбор данных в реальном времени и динамическое обновление пользовательского интерфейса — могут быть применены к различным проектам на языке MQL5, выходящим за рамки административных интерфейсов, включая советников, пользовательские индикаторы и интерактивные инструменты обучения.
Обзор класса AnalyticsPanel
В рамках модульного подхода к разработке, подходящего для крупномасштабных программ на языке MQL5, а также для повышения повторного использования и удобства сопровождения кода, мы создаем отдельный заголовочный файл класса AnalyticsPanel. Этот класс предназначен для объединения визуального оформления аналитической панели и получения и отображения рыночных данных в реальном времени.
Помимо предоставления стандартных показателей учетной записи, на панели будут отображаться различные значения технических индикаторов, которые используются в пользовательской стратегии, которую я назвал Confluence. Эта стратегия основана на принципе слияния, при котором сигналы от нескольких индикаторов сравниваются для генерации единого торгового сигнала. Если ни один из показателей не совпадает с другим, панель отображает сообщение No Consensus (нет согласия), тем самым избегая ложных или слабых сигналов.
Класс AnalyticsPanel будет включать методы для инициализации и обновления макета панели, обновления значений меток в реальном времени, а также управления визуальной обратной связью на основе логики стратегии. Ниже представлен визуальный макет панели, а в дальнейшем обсуждении мы подробно рассмотрим детали реализации, благодаря которым она была создана.

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

AnalyticsPanel Live
Интеграция AnalyticsPanel в New_Admin_Panel открывает ряд практических преимуществ.
- Централизованный мониторинг: теперь аналитика в реальном времени, такая как соотношение прибылей и убытков, изменения баланса и сводки сделок, доступна из основного интерфейса без необходимости использования отдельных инструментов.
- Улучшенный пользовательский опыт: сочетание аналитики с основными функциями, такими как выполнение стратегии и обмен сообщениями в Telegram, обеспечивает более унифицированный и интуитивно понятный рабочий процесс для пользователя.
- Масштабируемость и поддержка кода: Разделение компонентов интерфейса на классы, такие как AnalyticsPanel, повышает читаемость кода, упрощает тестирование и поддерживает будущие обновления с минимальными сбоями.
- Возможность повторного использования в разных проектах: модульная конструкция позволяет повторно использовать или адаптировать панель аналитики (и другие панели) в других советниках или торговых инструментах.
- Торговый сигнал: представленная здесь версия дает полезные сигналы, отражающие совпадение торговых событий.
Реализация в MQL5
При разработке нашей аналитической панели мы придерживаемся принципов объектно-ориентированного программирования, разделяя основные компоненты на различные уровни доступа для повышения ясности, безопасности и удобства сопровождения. Структура класса такова, что определенные функции доступны другим частям программы — это "публичные" (public) элементы, которые служат интерфейсом для взаимодействия с панелью. Другие внутренние детали, касающиеся внутренней работы и управления данными, скрыты от внешних компонентов; это "приватные" (private) элементы. Этот метод инкапсуляции позволяет нам представить четкую и структурированную схему нашей панели, что облегчает будущим разработчикам понимание роли и назначения каждого раздела.
Перед определением класса CAnalyticsPanel необходимо включить два важных заголовочных файла:
Dialog.mqh – заголовок предоставляет доступ к классам, связанным с диалоговыми окнами, включая CAppDialog, который служит базовым классом для создания пользовательской панели.
Label.mqh – определение класса CLabel, который используется для создания и управления всеми текстовыми метками, отображаемыми на панели.
Эти компоненты необходимы для того, чтобы пользовательская панель имела доступ к стандартным структурам элементов управления пользовательского интерфейса MQL5. Без них класс панели не смог бы наследовать от CAppDialog или создавать элементы управления метками.
Нам также необходимо определить макросы для отступов и компоновки (AP_GAP, AP_DEFAULT_LABEL_HEIGHT), чтобы его визуальная структура оставалась отзывчивой и чистой.#include <Controls\Dialog.mqh> #include <Controls\Label.mqh> // Use unique macro names for layout in this class: #define AP_DEFAULT_LABEL_HEIGHT 20 #define AP_GAP 10
Приведенная ниже схема разделяет основные компоненты нашего класса на пять ключевых областей:
1. Структура и цель класса
Класс CAnalyticsPanel служит наглядной и информативной панелью мониторинга, предназначенной для отображения обновлений в реальном времени о торговых и рыночных условиях непосредственно на графике MetaTrader 5. Полученный из класса CAppDialog стандартной библиотеки MQL5, он использует объектно-ориентированное программирование для отображения структурированных данных в аккуратно расположенных разделах. Панель включает в себя метки для отображения сведений об учетной записи, открытых сделок, рыночных котировок, индикаторов и сводной информации о сигналах, что позволяет трейдерам отслеживать все в одном компактном окне.
//+------------------------------------------------------------------+ //| Analytics Panel Class | //+------------------------------------------------------------------+ class CAnalyticsPanel : public CAppDialog { protected: // Helper: Create a text label. // The label's text color is set as provided and its background is forced to black. bool CreateLabelEx(CLabel &label, int x, int y, int width, int height, string label_name, string text, color clr) { // Construct a unique control name by combining the dialog name and label_name. string unique_name = m_name + "_" + label_name; if(!label.Create(m_chart_id, unique_name, m_subwin, x, y, x+width, y+height)) { Print("Failed to create label: ", unique_name, " Err=", GetLastError()); return false; } if(!Add(label)) { Print("Failed to add label: ", unique_name, " Err=", GetLastError()); return false; } if(!label.Text(text)) { Print("Failed to set text for label: ", unique_name); return false; } label.Color(clr); // Set text color label.Color(clrBlack); // Force background to black return true; } // Account and Trade Data Labels: CLabel m_lblBalance; CLabel m_lblEquity; CLabel m_lblMargin; CLabel m_lblOpenTrades; // PnL is split into two labels: CLabel m_lblPnLName; CLabel m_lblPnLValue; // Market Data Labels (split Bid/Ask): CLabel m_lblBidName; CLabel m_lblBidValue; CLabel m_lblAskName; CLabel m_lblAskValue; CLabel m_lblSpread; // Indicator Section Separator: CLabel m_lblIndicatorSeparator; // New header for indicator section: CLabel m_lblIndicatorHeader; // Indicator Labels (static name and dynamic value labels): CLabel m_lblRSIName; CLabel m_lblRSIValue; CLabel m_lblStochName; CLabel m_lblStochK; // %K value CLabel m_lblStochD; // %D value CLabel m_lblCCIName; CLabel m_lblCCIValue; CLabel m_lblWilliamsName; CLabel m_lblWilliamsValue; // New Summary Labels (for consensus across indicators): CLabel m_lblSignalHeader; // Header label for summary column ("SIGNAL") CLabel m_lblExpectation; // Text summary (e.g., "Buy" or "Sell") CLabel m_lblConfirmation; // Confirmation symbol (e.g., up or down arrow) // Previous values for dynamic color changes (market data): double m_prevBid; double m_prevAsk; public: CAnalyticsPanel(); virtual ~CAnalyticsPanel(); // Create the panel and initialize UI controls. virtual bool CreatePanel(const long chart, const string name, const int subwin, int x1, int y1, int x2, int y2); // Update the panel with the latest account, trade, market and indicator data. void UpdatePanel(); void Toggle(); private: void CreateControls(); void UpdateAccountInfo(); void UpdateTradeStats(); void UpdateMarketData(); void UpdateIndicators(); };
Ниже я привел подробное описание и объяснение данной переменной.
Переменные-члены
Метки пользовательского интерфейса для данных учетной записи и сделок:
Метки учетных записей:
- m_lblBalance, m_lblEquity и m_lblMargin отображают, соответственно, баланс счета, эквити и маржу.
- Параметр m_lblOpenTrades отображает количество открытых в данный момент сделок.
- m_lblPnLName - статическая метка, которая просто отображает текст "PnL:" (остается черной).
- m_lblPnLValue - динамическая метка, отображающая числовое значение прибыли и убытков и меняющая цвет (зеленый - прибыль, красный - убыток).
Метки рыночных данных:
Эти метки отображают значения цены покупки/продажи и информацию о спреде:
- m_lblBidName и m_lblBidValue — цены bid.
- m_lblAskName и m_lblAskValue - цены ask.
- m_lblSpread displays - текущий спред в пунктах.
- Цвета значений bid и ask динамически обновляются в соответствии с изменениями рынка (например, синий, когда значение увеличивается, и красный, когда оно уменьшается).
Раздел индикаторов:
- Разделитель (m_lblIndicatorSeparator) визуально отличает раздел индикатора от остальной части панели.
- В метке заголовка (m_lblIndicatorHeader) отображается INDICATORS, четко указывающее на раздел.
В этот раздел включено несколько показателей:
- RSI - две метки, m_lblRSIName и m_lblRSIValue, отображают название ("RSI:") и его текущее значение.
- Stochastic Oscillator - индикатор состоит из двух частей:
- m_lblStochName - статическая часть ("Stoch:").
- m_lblStochK и m_lblStochD - динамические значения для %K и %D.
- CCI - m_lblCCIName и m_lblCCIValue отображают индекс товарного канала (CCI).
- Williams %R - m_lblWilliamsName и m_lblWilliamsValue отображают значения Williams %R.
Раздел Summary (Consensus):
- Метка заголовка (m_lblSignalHeader) отображает слово SIGNAL над столбцом итогов.
- Для отображения консенсусного сигнала по всем индикаторам используются две метки: m_lblExpectation и m_lblConfirmation.
- m_lblExpectation предоставляет итог в виде тексте, например Buy (купить), Sell (продать) или No Consensus (нет согласия).
- m_lblConfirmation отображает символическое подтверждение (стрелка вверх "↑" для покупки, стрелка вниз "↓" для продажи или тире "-", если согласия не достигнуто).
Внутреннее состояние рыночных данных:
Две переменные, m_prevBid и m_prevAsk, хранят предыдущие значения цены покупки и продажи, помогая определить, растут или падают эти значения, — таким образом, это влияет на цветовую кодировку, используемую для этих меток.
2. Вспомогательный метод для создания меток
Ключевой вспомогательной функцией в этом классе является CreateLabelEx — многократно используемый метод для создания и настройки текстовых меток на панели. Этот метод включает в себя все шаги, необходимые для создания метки, присвоения ей уникального имени, настройки размера и местоположения, применения желаемого текста и цветов, а также добавления её в список элементов управления панели. Если какой-либо шаг в этом процессе завершится неудачей, в журнал будут записаны соответствующие сообщения об ошибках для отладки.
bool CAnalyticsPanel::CreateLabelEx(CLabel &label, int x, int y, int width, int height, string label_name, string text, color clr) { string unique_name = m_name + "_" + label_name; if(!label.Create(m_chart_id, unique_name, m_subwin, x, y, x+width, y+height)) { Print("Failed to create label: ", unique_name, " Err=", GetLastError()); return false; } if(!Add(label)) { Print("Failed to add label: ", unique_name, " Err=", GetLastError()); return false; } if(!label.Text(text)) { Print("Failed to set text for label: ", unique_name); return false; } label.Color(clr); // Set text color label.BackColor(clrBlack); // Force background to black return true; }
3. Организация макета пользовательского интерфейса
Метод CreateControls организует все элементы пользовательского интерфейса на панели. Здесь мы расположили различные разделы: информация о счете (баланс, эквити, маржа), статистика сделок (открытые сделки и прибыль/убыток), рыночные данные (цена покупки, цена продажи и спред) и раздел индикаторов. В разделе индикаторов мы создаем заголовки типа INDICATORS и SIGNAL, чтобы четко обозначить группы информации. В коде используются предопределенные константы для высоты и отступов меток, чтобы обеспечить равномерное расположение и визуальную организацию всех элементов.
//+------------------------------------------------------------------+ //| CreateControls: Instantiate and add UI controls | //+------------------------------------------------------------------+ void CAnalyticsPanel::CreateControls() { int curX = AP_GAP; int curY = AP_GAP; int labelWidth = 150; // Account Information Labels: CreateLabelEx(m_lblBalance, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Balance", "Balance: $0.00", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblEquity, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Equity", "Equity: $0.00", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblMargin, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Margin", "Margin: 0", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Trade Statistics Labels: CreateLabelEx(m_lblOpenTrades, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "OpenTrades", "Open Trades: 0", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // PnL labels: CreateLabelEx(m_lblPnLName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "PnLName", "PnL:", clrBlack); CreateLabelEx(m_lblPnLValue, curX+50, curY, labelWidth-50, AP_DEFAULT_LABEL_HEIGHT, "PnLValue", "$0.00", clrLime); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Market Data Labels: CreateLabelEx(m_lblBidName, curX, curY, 40, AP_DEFAULT_LABEL_HEIGHT, "BidName", "Bid:", clrDodgerBlue); CreateLabelEx(m_lblBidValue, curX+40, curY, 100, AP_DEFAULT_LABEL_HEIGHT, "BidValue", "0.00000", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblAskName, curX, curY, 40, AP_DEFAULT_LABEL_HEIGHT, "AskName", "Ask:", clrDodgerBlue); CreateLabelEx(m_lblAskValue, curX+40, curY, 100, AP_DEFAULT_LABEL_HEIGHT, "AskValue", "0.00000", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblSpread, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Spread", "Spread: 0.0 pips", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Indicator Section Separator: CreateLabelEx(m_lblIndicatorSeparator, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Separator", "------------------------------------------", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Indicator Section Header: CreateLabelEx(m_lblIndicatorHeader, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "IndicatorHeader", "INDICATORS", clrDodgerBlue); // Summary Column Header for Signal: CreateLabelEx(m_lblSignalHeader, curX+250, curY , 100, AP_DEFAULT_LABEL_HEIGHT, "SignalHeader", "SIGNAL", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Indicator Labels: // RSI: CreateLabelEx(m_lblRSIName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "RSIName", "RSI:", clrDodgerBlue); CreateLabelEx(m_lblRSIValue, curX+50, curY, 90, AP_DEFAULT_LABEL_HEIGHT, "RSIValue", "N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Stochastic: CreateLabelEx(m_lblStochName, curX, curY, 60, AP_DEFAULT_LABEL_HEIGHT, "StochName", "Stoch:", clrDodgerBlue); CreateLabelEx(m_lblStochK, curX+60, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "StochK", "K: N/A", clrDodgerBlue); CreateLabelEx(m_lblStochD, curX+150, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "StochD", "D: N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // CCI: CreateLabelEx(m_lblCCIName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "CCIName", "CCI:", clrDodgerBlue); CreateLabelEx(m_lblCCIValue, curX+50, curY, 90, AP_DEFAULT_LABEL_HEIGHT, "CCIValue", "N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Williams %R: CreateLabelEx(m_lblWilliamsName, curX, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "WilliamsName", "Williams:", clrDodgerBlue); CreateLabelEx(m_lblWilliamsValue, curX+70, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "WilliamsValue", "N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Summary Column: Expectation text and Confirmation symbol. CreateLabelEx(m_lblExpectation, curX+250, curY - (AP_DEFAULT_LABEL_HEIGHT+AP_GAP)*4, 100, AP_DEFAULT_LABEL_HEIGHT, "Expectation", "Expect: N/A", clrDodgerBlue); CreateLabelEx(m_lblConfirmation, curX+300, curY - (AP_DEFAULT_LABEL_HEIGHT+AP_GAP)*4, 50, AP_DEFAULT_LABEL_HEIGHT, "Confirmation", "N/A", clrDodgerBlue); ChartRedraw(m_chart_id); }
4. Обновление данных в реальном времени для информации о счетах и рынке.
Используя встроенные функции MQL5, панель получает информацию о счете — такую как баланс, эквити и исходное значение маржи, — а также рыночные данные, такие как цены покупки и продажи и спред. Методы UpdateAccountInfo и UpdateTradeStats напрямую запрашивают информацию об учетной записи с помощью таких функций, как AccountInfoDouble(). Тем временем, UpdateMarketData получает данные о ценах в реальном времени с помощью функции с помощью SymbolInfoTick(), сравнивает текущие значения с предыдущими и обновляет цветовую кодировку меток bid и ask в зависимости от изменений. Это позволяет трейдерам видеть актуальное состояние счета и рынка в реальном времени.
//+------------------------------------------------------------------+ //| UpdateAccountInfo: Refresh account-related data | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateAccountInfo() { double balance = AccountInfoDouble(ACCOUNT_BALANCE); double equity = AccountInfoDouble(ACCOUNT_EQUITY); double margin = AccountInfoDouble(ACCOUNT_MARGIN); m_lblBalance.Text("Balance: $" + DoubleToString(balance, 2)); m_lblEquity.Text("Equity: $" + DoubleToString(equity, 2)); // Directly display the raw margin value. m_lblMargin.Text("Margin: " + DoubleToString(margin, 2)); } //+------------------------------------------------------------------+ //| UpdateTradeStats: Refresh trade statistics | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateTradeStats() { int total = PositionsTotal(); double pnl = 0; for(int i = 0; i < total; i++) { ulong ticket = PositionGetTicket(i); if(ticket != 0) pnl += PositionGetDouble(POSITION_PROFIT); } // Update PnL labels: m_lblPnLName.Text("PnL:"); // static remains black m_lblPnLValue.Text("$" + DoubleToString(pnl, 2)); if(pnl < 0) m_lblPnLValue.Color(clrRed); else m_lblPnLValue.Color(clrLime); m_lblOpenTrades.Text("Open Trades: " + IntegerToString(total)); } //+------------------------------------------------------------------+ //| UpdateMarketData: Refresh market data display | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateMarketData() { MqlTick last_tick; if(SymbolInfoTick(Symbol(), last_tick)) { // Update Bid value and color based on change: color bidColor = clrBlue; if(m_prevBid != 0) bidColor = (last_tick.bid >= m_prevBid) ? clrBlue : clrRed; m_lblBidValue.Color(bidColor); m_lblBidValue.Text(DoubleToString(last_tick.bid, 5)); // Update Ask value and color based on change: color askColor = clrBlue; if(m_prevAsk != 0) askColor = (last_tick.ask >= m_prevAsk) ? clrBlue : clrRed; m_lblAskValue.Color(askColor); m_lblAskValue.Text(DoubleToString(last_tick.ask, 5)); m_prevBid = last_tick.bid; m_prevAsk = last_tick.ask; // Update Spread: double spread_pips = (last_tick.ask - last_tick.bid) * 1e4; m_lblSpread.Text("Spread: " + DoubleToString(spread_pips, 1) + " pips"); } }
5. Расчет технических индикаторов и формирование консенсусного сигнала
Метод UpdateIndicators извлекает значения для нескольких технических индикаторов — RSI, Stochastic, CCI и Williams %R — путем вызова функций индикаторов и считывания их самых последних данных. Значение каждого индикатора сравнивается с заранее определенными пороговыми значениями, чтобы определить, указывает ли он на необходимость покупки или продажи. Например, значение RSI ниже 30 интерпретируется как сигнал к покупке, а значение выше 70 — как сигнал к продаже. Затем панель объединяет эти отдельные сигналы в общее заключение. Если все индикаторы совпадают, в сводной таблице появляется четкий сигнал на покупку или продажу в виде стрелки. Это позволяет трейдерам быстро визуально оценить рыночную ситуацию.
//+------------------------------------------------------------------+ //| UpdateIndicators: Calculate and display various indicators | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateIndicators() { // Local suggestion variables (1 = Buy, -1 = Sell, 0 = Neutral) int suggestionRSI = 0, suggestionStoch = 0, suggestionCCI = 0, suggestionWilliams = 0; // --- RSI --- int handleRSI = iRSI(Symbol(), PERIOD_CURRENT, 14, PRICE_CLOSE); double rsi[1]; color rsiValueColor = clrGreen; // default if(CopyBuffer(handleRSI, 0, 0, 1, rsi) > 0) { if(rsi[0] < 30) { rsiValueColor = clrBlue; // buy condition suggestionRSI = 1; } else if(rsi[0] > 70) { rsiValueColor = clrRed; // sell condition suggestionRSI = -1; } m_lblRSIValue.Color(rsiValueColor); m_lblRSIValue.Text(DoubleToString(rsi[0], 2)); } IndicatorRelease(handleRSI); // --- Stochastic --- int handleStoch = iStochastic(Symbol(), PERIOD_CURRENT, 5, 3, 3, MODE_SMA, STO_LOWHIGH); double stochK[1], stochD[1]; if(CopyBuffer(handleStoch, 0, 0, 1, stochK) > 0 && CopyBuffer(handleStoch, 1, 0, 1, stochD) > 0) { // Initialize color variables. color colorK = clrGreen; color colorD = clrGreen; if(stochK[0] > stochD[0]) { colorK = clrBlue; colorD = clrRed; suggestionStoch = 1; } else if(stochK[0] < stochD[0]) { colorK = clrRed; colorD = clrBlue; suggestionStoch = -1; } else suggestionStoch = 0; m_lblStochK.Color(colorK); m_lblStochD.Color(colorD); m_lblStochK.Text("K: " + DoubleToString(stochK[0], 4)); m_lblStochD.Text("D: " + DoubleToString(stochD[0], 4)); } IndicatorRelease(handleStoch); // --- CCI --- int handleCCI = iCCI(Symbol(), PERIOD_CURRENT, 14, PRICE_TYPICAL); double cci[1]; color cciValueColor = clrGreen; if(CopyBuffer(handleCCI, 0, 0, 1, cci) > 0) { if(cci[0] < -100) { cciValueColor = clrBlue; // buy condition suggestionCCI = 1; } else if(cci[0] > 100) { cciValueColor = clrRed; // sell condition suggestionCCI = -1; } m_lblCCIValue.Color(cciValueColor); m_lblCCIValue.Text(DoubleToString(cci[0], 2)); } IndicatorRelease(handleCCI); // --- Williams %R --- int handleWPR = iWPR(Symbol(), PERIOD_CURRENT, 14); double williams[1]; color williamsValueColor = clrGreen; if(CopyBuffer(handleWPR, 0, 0, 1, williams) > 0) { if(williams[0] > -80) { williamsValueColor = clrBlue; // buy condition suggestionWilliams = 1; } else if(williams[0] < -20) { williamsValueColor = clrRed; // sell condition suggestionWilliams = -1; } m_lblWilliamsValue.Color(williamsValueColor); m_lblWilliamsValue.Text(DoubleToString(williams[0], 2)); } IndicatorRelease(handleWPR); // --- Consensus Summary --- int consensus = 0; if(suggestionRSI != 0 && suggestionRSI == suggestionStoch && suggestionRSI == suggestionCCI && suggestionRSI == suggestionWilliams) consensus = suggestionRSI; if(consensus == 1) { m_lblExpectation.Text("Buy"); m_lblExpectation.Color(clrBlue); m_lblConfirmation.Text("↑"); // Up arrow for Buy m_lblConfirmation.Color(clrBlue); } else if(consensus == -1) { m_lblExpectation.Text("Sell"); m_lblExpectation.Color(clrRed); m_lblConfirmation.Text("↓"); // Down arrow for Sell m_lblConfirmation.Color(clrRed); } else { m_lblExpectation.Text("No Consensus"); m_lblExpectation.Color(clrOrangeRed); m_lblConfirmation.Text("-"); m_lblConfirmation.Color(clrOrangeRed); } }
Интеграция CAnalyticsPanel в главную панель администратора
1. В верхней части файла советника включен заголовочный файл AnalyticsPanel.mqh, который ПРЕДОСТАВЛЯЕТ доступ к классу CAnalyticsPanel:
#include <AnalyticsPanel.mqh> Это гарантирует, что компилятор распознает класс и его члены во время компиляции.
2. Объявим глобальный указатель
Объявлен глобальный указатель типа CAnalyticsPanel, который будет использоваться для динамического управления экземпляром панели:
CAnalyticsPanel *g_analyticsPanel = NULL; Этот указатель помогает отслеживать, существует ли панель, и позволяет динамически создавать, удалять ее и переключать ее видимость.
3. Создадим кнопку для запуска панели.
В функции CreateAdminPanel() создается кнопка Analytics Panel (аналитическая панель) и добавляется в главную панель администратора. Эта кнопка служит для запуска панели пользователем.
bool CreateAdminPanel() { // Analytics Panel Button if(!CreateButton(g_analyticsButton, ANALYTICS_BTN_NAME, INDENT_LEFT, y, INDENT_LEFT+btnWidth, y+BUTTON_HEIGHT, "Analytics Panel")) return false; return true; }
4. Обработка событий нажатия кнопки
Функция HandleAnalytics() отвечает за управление жизненным циклом панели. Проверяет, была ли панель уже создана. Если нет, создает экземпляр CAnalyticsPanel, вызывает его метод CreatePanel() с координатами позиционирования и переключает его видимость. В противном случае функция просто включает или выключает панель.
//------------------------------------------------------------------ // Handle Analytics Panel button click void HandleAnalytics() { if(g_analyticsPanel == NULL) { g_analyticsPanel = new CAnalyticsPanel(); // Coordinates for Analytics panel (adjust as needed) if(!g_analyticsPanel.CreatePanel(g_chart_id, "AnalyticsPanel", g_subwin, 900, 20, 900+500, 20+460)) { delete g_analyticsPanel; g_analyticsPanel = NULL; Print("Failed to create Analytics Panel"); return; } } g_analyticsPanel.Toggle(); ChartRedraw(); }
5. Отправка событий
В функции AdminPanelOnEvent() события, такие как клики или взаимодействия пользователя, передаются в g_analyticsPanel, если она видима. Это гарантирует, что панель сможет независимо реагировать на ввод пользователя, не вступая в конфликт с основной панелью или другими подпанелями.
if(g_analyticsPanel != NULL && g_analyticsPanel.IsVisible()) return g_analyticsPanel.OnEvent(id, lparam, dparam, sparam);
6. Динамическое обновление панели
Внутри функции OnTick(), если панель аналитики существует, регулярно вызывается ее метод UpdatePanel(). Это позволяет панели динамически обновлять отображаемую аналитику или содержимое пользовательского интерфейса во время работы советника.
void OnTick() { if(g_analyticsPanel != NULL) { g_analyticsPanel.UpdatePanel(); } }
7. Удаление
В функции OnDeinit() панель корректно уничтожается, а память освобождается путем вызова функции Destroy() и удаления указателя. Это крайне важно для предотвращения утечек памяти при удалении или перекомпиляции советника.
Тестирование
Это был невероятный опыт тестирования. Создавалось ощущение, что всё происходит практически в реальном времени: значения на панели обновлялись с каждым щелчком. Мне удалось совершить сделку, используя стратегию слияния сигналов, но, к несчастью, к моменту начала записи сигнал уже изменился на No Consensus. Тем не менее, я рад поделиться результатами — смотрите изображение ниже!

Демонстрация работы панели аналитики
На изображении выше значение Open Trades (открытые сделки) обновлялось при каждом закрытии позиции. Мы начали с 21 позиции и закончили с 18.

Доступ ко всем панелям осуществляется с главной панели.
Заключение
Мы успешно расширили нашу программу, интегрировав в нее панель аналитики. Это дополнение демонстрирует, что с помощью того же модульного подхода, который применяется к другим панелям, можно включить гораздо больше подпанелей. Данное обсуждение завершает часть IX серии. Однако это не означает, что работа завершена — есть еще несколько областей, требующих доработки. Тем не менее, основная концепция теперь хорошо отработана и четко изложена.
Этот цикл статей будет полезен как начинающим, так и опытным разработчикам разного уровня подготовки. Здесь можно почерпнуть множество ценных выводов. Изложенные здесь идеи закладывают прочную основу для еще более передовых разработок в будущем.
В представленных результатах я нарисовал на графике каналы, и по совпадению аналитическая панель сгенерировала сигнал на покупку. Этот сигнал возник в результате совпадения всех перечисленных индикаторов, идеально совпавшего с зоной поддержки канала. Я уверенно совершил сделку, основываясь на этой стратегии, учитывающей сложившиеся обстоятельства. Тестирование проводилось на демо-счете — как всегда, я настоятельно рекомендую всем трейдерам тщательно протестировать свои стратегии на демо-счете, прежде чем рисковать реальными деньгами.
Преимущество этой системы заключается в том, что трейдеры могут мгновенно получать доступ к важной рыночной и учетной информации через аналитическую панель, а также пользоваться аналитическими возможностями сигналов, основанных на анализе сводной информации. Одной из областей, требующих улучшения, является отсутствие оповещений о стратегии; добавление уведомлений сделало бы систему еще более удобной для пользователя и гарантировало бы, что ни одна возможность не будет упущена.
Ниже прилагаются как заголовочный файл, так и основная программа. Обязательно поделитесь своим мнением в комментариях. Удачной разработки и успешной торговли!
| Имя файла | Назначение |
|---|---|
| AnalyticsPanel.mqh | Заголовочный файл, определяющий структуру и поведение аналитической панели, которая объединяет множество индикаторов для генерации торговых сигналов на основе анализа сводной информации. |
| New_Admin_Panel.mqh | Основная программа MQL5-советника, которая инициализирует и управляет всем интерфейсом административной панели, включая навигацию, аутентификацию и интеграцию различных подпанелей, таких как Communications, Analytics и Trade Management. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17397
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Улучшенная оптимизация сталкивающихся тел — Enhanced Colliding Bodies Optimization (ECBO)
Инженерия признаков с Python и MQL5 (Часть IV): Распознавание свечных паттернов с помощью UMAP-регрессии
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования