Торговые инструменты на MQL5 (Часть 3): Создание панели сканера по нескольким таймфреймам для стратегической торговли
Введение
В своей предыдущей статье, Часть 2, мы дополнили инструмент Trade Assistant на MetaQuotes Language 5 (MQL5) динамической визуальной обратной связью для улучшения интерактивности. Теперь мы сосредоточимся на создании панели сканера по нескольким таймфреймам, которая будет предоставлять торговые сигналы в режиме реального времени для принятия стратегических решений. Мы представляем интерфейс на основе сетки с сигналами, управляемыми индикаторами, и кнопкой закрытия, описывая эти достижения в следующих подразделах:
Эти разделы помогут нам создать интуитивно понятную и мощную информационную панель управления торговлей.
Схема информационной панели сканера
Мы стремимся создать панель сканера по нескольким таймфреймам, предоставляющую четкие торговые сигналы в режиме реального времени для улучшения процесса принятия стратегических решений. Панель будет представлена в виде сетки, отображающей сигналы на покупку и продажу на нескольких таймфреймах, что позволит нам быстро оценивать рыночные условия, не переключая графики. Будет включена кнопка закрытия, позволяющая легко закрывать панель, обеспечивая понятный и гибкий пользовательский интерфейс, адаптируемый к нашим торговым потребностям.
Мы будем использовать сигналы от ключевых индикаторов, включая Индекс относительной силы (RSI), Стохастический осциллятор (STOCH), Индекс товарного канала (CCI), Индекс среднего направленного движения (ADX), а также Awesome Oscillator (Чудесный осциллятор) (AO), которые предназначены для определения потенциальных торговых возможностей с настраиваемыми пороговыми значениями силы. Тем не менее, выбор используемых индикаторов или данных о ценовом движении остается за вами. Такая настройка поможет определять тренды и развороты на разных таймфреймах, поддерживая как краткосрочные, так и долгосрочные стратегии. Наша цель - создать оптимизированный и интуитивно понятный инструмент, предоставляющий полезную информацию, оставаясь при этом удобным для пользователя, и проложит путь для будущих улучшений, таких как автоматические оповещения или дополнительные индикаторы. Ниже приведена визуализация того, к чему мы стремимся.

Реализация средствами MQL5
Для создания программы на MQL5 нам нужно будет задать метаданные программы, а затем определить некоторые константы имен объектов, которые помогут ссылаться на объекты информационной панели и легко управлять ими.
//+------------------------------------------------------------------+ //| TimeframeScanner Dashboard EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" // Define identifiers and properties for UI elements #define MAIN_PANEL "PANEL_MAIN" //--- Main panel rectangle identifier #define HEADER_PANEL "PANEL_HEADER" //--- Header panel rectangle identifier #define HEADER_PANEL_ICON "PANEL_HEADER_ICON" //--- Header icon label identifier #define HEADER_PANEL_TEXT "PANEL_HEADER_TEXT" //--- Header title label identifier #define CLOSE_BUTTON "BUTTON_CLOSE" //--- Close button identifier #define SYMBOL_RECTANGLE "SYMBOL_HEADER" //--- Symbol rectangle identifier #define SYMBOL_TEXT "SYMBOL_TEXT" //--- Symbol text label identifier #define TIMEFRAME_RECTANGLE "TIMEFRAME_" //--- Timeframe rectangle prefix #define TIMEFRAME_TEXT "TIMEFRAME_TEXT_" //--- Timeframe text label prefix #define HEADER_RECTANGLE "HEADER_" //--- Header rectangle prefix #define HEADER_TEXT "HEADER_TEXT_" //--- Header text label prefix #define RSI_RECTANGLE "RSI_" //--- RSI rectangle prefix #define RSI_TEXT "RSI_TEXT_" //--- RSI text label prefix #define STOCH_RECTANGLE "STOCH_" //--- Stochastic rectangle prefix #define STOCH_TEXT "STOCH_TEXT_" //--- Stochastic text label prefix #define CCI_RECTANGLE "CCI_" //--- CCI rectangle prefix #define CCI_TEXT "CCI_TEXT_" //--- CCI text label prefix #define ADX_RECTANGLE "ADX_" //--- ADX rectangle prefix #define ADX_TEXT "ADX_TEXT_" //--- ADX text label prefix #define AO_RECTANGLE "AO_" //--- AO rectangle prefix #define AO_TEXT "AO_TEXT_" //--- AO text label prefix #define BUY_RECTANGLE "BUY_" //--- Buy rectangle prefix #define BUY_TEXT "BUY_TEXT_" //--- Buy text label prefix #define SELL_RECTANGLE "SELL_" //--- Sell rectangle prefix #define SELL_TEXT "SELL_TEXT_" //--- Sell text label prefix #define WIDTH_TIMEFRAME 90 //--- Width of timeframe and symbol rectangles #define WIDTH_INDICATOR 70 //--- Width of indicator rectangles #define WIDTH_SIGNAL 90 //--- Width of BUY/SELL signal rectangles #define HEIGHT_RECTANGLE 25 //--- Height of all rectangles #define COLOR_WHITE clrWhite //--- White color for text and backgrounds #define COLOR_BLACK clrBlack //--- Black color for borders and text #define COLOR_LIGHT_GRAY C'230,230,230' //--- Light gray color for signal backgrounds #define COLOR_DARK_GRAY C'105,105,105' //--- Dark gray color for indicator backgrounds
Начинаем с создания структуры пользовательского интерфейса для нашей панели сканера по нескольким таймфреймам, используя директиву #define для создания таких констант, как "MAIN_PANEL" и "HEADER_PANEL" для прямоугольников главной панели и панели заголовка, а также "HEADER_PANEL_ICON", "HEADER_PANEL_TEXT" и "CLOSE_BUTTON" для значка, наименования заголовка и элементов кнопок закрытия.
Определяем идентификаторы для сеточной структуры информационной панели. Для инструмента задаем "SYMBOL_RECTANGLE" и "SYMBOL_TEXT", в то время как префиксы "TIMEFRAME_RECTANGLE" и "TIMEFRAME_TEXT" обрабатывают ряды таймфреймов. Используем префиксы "HEADER_RECTANGLE" и "HEADER_TEXT" для заголовков столбцов, а также префиксы типа "RSI_RECTANGLE", "STOCH_RECTANGLE", "BUY_RECTANGLE" с соответствующими "RSI_TEXT", "STOCH_TEXT" и "BUY_TEXT" для индикаторных и сигнальных ячеек.
Настраиваем размеры с помощью "WIDTH_TIMEFRAME" (90 пикселей), "WIDTH_INDICATOR" (70 пикселей), "WIDTH_SIGNAL" (90 пикселей) и "HEIGHT_RECTANGLE" (25 пикселей). Определяем цвета, используя "COLOR_WHITE" и "COLOR_BLACK" для текста и границ, "COLOR_LIGHT_GRAY" ("C'230,230,230'") для фона сигналов и "COLOR_DARK_GRAY" ("C'105,105,105'") для индикаторов, обеспечивая единообразное и понятное расположение. Затем нужно определить еще несколько глобальных переменных, которые мы будем использовать на протяжении всей программы.
bool panel_is_visible = true; //--- Flag to control panel visibility // Define the timeframes to be used ENUM_TIMEFRAMES timeframes_array[] = {PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M20, PERIOD_M30, PERIOD_H1, PERIOD_H2, PERIOD_H3, PERIOD_H4, PERIOD_H8, PERIOD_H12, PERIOD_D1, PERIOD_W1}; //--- Array of timeframes for scanning // Global variables for indicator values double rsi_values[]; //--- Array to store RSI values double stochastic_values[]; //--- Array to store Stochastic signal line values double cci_values[]; //--- Array to store CCI values double adx_values[]; //--- Array to store ADX values double ao_values[]; //--- Array to store AO values
Здесь мы объявляем логическую переменную "panel_is_visible" и присваиваем ей значение true, определяющее, будет ли информационная панель отображаться на графике. Этот флаг позволяет изменять видимость информационной панели по мере необходимости, особенно когда нам не нужны обновления данных. Затем определяем массив "timeframes_array", используя тип ENUM_TIMEFRAMES, в котором перечисляются периоды от "PERIOD_M1" (1 минута) до "PERIOD_W1" (неделя). Этот массив определяет таймфреймы, которые будет анализировать информационная панель, что позволяет нам структурированно сканировать рыночные сигналы на нескольких временных горизонтах. Если какие-то таймфреймы вам не нужны или вы хотите изменить набор, просто отредактируйте перечисления.
Для хранения данных индикатора создаем двойные массивы "rsi_values", "stochastic_values", "cci_values", "adx_values" и "ao_values". Эти массивы содержат рассчитанные значения для Индекса относительной силы, Стохастического осциллятора, Индекса товарного канала, Индекса среднего направленного движения и Awesome Oscillator соответственно, что позволяет нам эффективно обрабатывать и отображать торговые сигналы для каждого таймфрейма. Теперь мы можем определить некоторые вспомогательные функции, которые будем использовать для определения направления сигнала и сокращённого отображения имени таймфрейма.
//+------------------------------------------------------------------+ //| Truncate timeframe enum to display string | //+------------------------------------------------------------------+ string truncate_timeframe_name(int timeframe_index) //--- Function to format timeframe name { string timeframe_string = StringSubstr(EnumToString(timeframes_array[timeframe_index]), 7); //--- Extract timeframe name return timeframe_string; //--- Return formatted name } //+------------------------------------------------------------------+ //| Calculate signal strength for buy/sell | //+------------------------------------------------------------------+ string calculate_signal_strength(double rsi, double stochastic, double cci, double adx, double ao, bool is_buy) //--- Function to compute signal strength { int signal_strength = 0; //--- Initialize signal strength counter if(is_buy && rsi < 40) signal_strength++; //--- Increment for buy if RSI is oversold else if(!is_buy && rsi > 60) signal_strength++; //--- Increment for sell if RSI is overbought if(is_buy && stochastic < 40) signal_strength++; //--- Increment for buy if Stochastic is oversold else if(!is_buy && stochastic > 60) signal_strength++; //--- Increment for sell if Stochastic is overbought if(is_buy && cci < -70) signal_strength++; //--- Increment for buy if CCI is oversold else if(!is_buy && cci > 70) signal_strength++; //--- Increment for sell if CCI is overbought if(adx > 40) signal_strength++; //--- Increment if ADX indicates strong trend if(is_buy && ao > 0) signal_strength++; //--- Increment for buy if AO is positive else if(!is_buy && ao < 0) signal_strength++; //--- Increment for sell if AO is negative if(signal_strength >= 3) return is_buy ? "Strong Buy" : "Strong Sell"; //--- Return strong signal if 3+ conditions met if(signal_strength >= 2) return is_buy ? "Buy" : "Sell"; //--- Return regular signal if 2 conditions met return "Neutral"; //--- Return neutral if insufficient conditions }
Здесь определяем функцию "truncate_timeframe_name", которая принимает целочисленный параметр "timeframe_index" для форматирования названий таймфреймов для отображения. Внутри мы используем StringSubstr для извлечения подстроки из результата функции EnumToString, примененной к "timeframes_array[timeframe_index]", начиная с позиции 7, и сохранить ее в "timeframe_string". Затем возвращаем "timeframe_string", предоставляя чистое, понятное пользователю название таймфрейма.
Создаем функцию "calculate_signal_strength" для определения сигналов на покупку или продажу на основе значений индикатора. Инициализируем целое число "signal_strength" равным нулю, чтобы подсчитать совпадающие условия. Для Индекса относительной силы увеличиваем значение "signal_strength", если значение "is_buy" равно true, а "rsi" ниже 40 (перепроданность), или если значение "is_buy" равно false, а "rsi" превышает 60 (перекупленность). Аналогично, проверяем "stochastic" (ниже 40 или выше 60), "CCI" (ниже -70 или выше 70) и "ao" (положительный для покупки, отрицательный для продажи), увеличивая "signal_strength" для каждого выполненного условия.
Мы также оцениваем Индекс среднего направленного движения, увеличивая значение "signal_strength", если значение "adx" превышает 40, что указывает на сильный тренд как для сценариев покупки, так и продажи. Если значение "signal_strength" достигает 3 или более, возвращаем значение “Strong Buy” для "is_buy" true или “Strong Sell” в противном случае. Если значение равно 2, возвращаем “Buy” или “Sell”, а при меньшем количестве - “Neutral”, что позволяет четко классифицировать сигналы информационной панели. Теперь мы можем определить функции, которые позволят нам создавать объекты.
//+------------------------------------------------------------------+ //| Create a rectangle for the UI | //+------------------------------------------------------------------+ bool create_rectangle(string object_name, int x_distance, int y_distance, int x_size, int y_size, color background_color, color border_color = COLOR_BLACK) //--- Function to create a rectangle { ResetLastError(); //--- Reset error code if(!ObjectCreate(0, object_name, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Create rectangle object Print(__FUNCTION__, ": failed to create Rectangle: ERR Code: ", GetLastError()); //--- Log creation failure return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_XDISTANCE, x_distance); //--- Set x position ObjectSetInteger(0, object_name, OBJPROP_YDISTANCE, y_distance); //--- Set y position ObjectSetInteger(0, object_name, OBJPROP_XSIZE, x_size); //--- Set width ObjectSetInteger(0, object_name, OBJPROP_YSIZE, y_size); //--- Set height ObjectSetInteger(0, object_name, OBJPROP_CORNER, CORNER_RIGHT_UPPER); //--- Set corner to top-right ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color); //--- Set background color ObjectSetInteger(0, object_name, OBJPROP_BORDER_COLOR, border_color); //--- Set border color ObjectSetInteger(0, object_name, OBJPROP_BORDER_TYPE, BORDER_FLAT); //--- Set flat border style ObjectSetInteger(0, object_name, OBJPROP_BACK, false); //--- Set to foreground ChartRedraw(0); //--- Redraw chart return(true); //--- Return success } //+------------------------------------------------------------------+ //| Create a text label for the UI | //+------------------------------------------------------------------+ bool create_label(string object_name, string text, int x_distance, int y_distance, int font_size = 12, color text_color = COLOR_BLACK, string font = "Arial Rounded MT Bold") //--- Function to create a label { ResetLastError(); //--- Reset error code if(!ObjectCreate(0, object_name, OBJ_LABEL, 0, 0, 0)) { //--- Create label object Print(__FUNCTION__, ": failed to create Label: ERR Code: ", GetLastError()); //--- Log creation failure return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_XDISTANCE, x_distance); //--- Set x position ObjectSetInteger(0, object_name, OBJPROP_YDISTANCE, y_distance); //--- Set y position ObjectSetInteger(0, object_name, OBJPROP_CORNER, CORNER_RIGHT_UPPER); //--- Set corner to top-right ObjectSetString(0, object_name, OBJPROP_TEXT, text); //--- Set label text ObjectSetString(0, object_name, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, object_name, OBJPROP_FONTSIZE, font_size); //--- Set font size ObjectSetInteger(0, object_name, OBJPROP_COLOR, text_color); //--- Set text color ObjectSetInteger(0, object_name, OBJPROP_ANCHOR, ANCHOR_CENTER); //--- Center text ChartRedraw(0); //--- Redraw chart return(true); //--- Return success }
Для возможности создания объектов определяем функцию "create_rectangle" с параметрами "object_name", "x_distance", "y_distance", "x_size", "y_size", "background_color", и "border_color". Используем ResetLastError, создаем OBJ_RECTANGLE_LABEL с помощью функции ObjectCreate и регистрируем ошибки с помощью функции "Print", если это не удается, возвращая значение false.
Задаем свойства прямоугольника с помощью функции ObjectSetInteger для положения, размера, "CORNER_RIGHT_UPPER", "background_color", "border_color" и "BORDER_FLAT", обеспечивая отображение на переднем плане. Используем ChartRedraw и возвращаем значение true. Для текста определяем функцию "create_label" с параметрами "object_name", "text", "x_distance", "y_distance", "font_size", "text_color" и "font".
Используем "ResetLastError", создаем "OBJ_LABEL" с помощью функции "ObjectCreate" и регистрируем ошибки, если это не удается. Используем "ObjectSetInteger" для определения положения, размера, цвета и "ANCHOR_CENTER", а функцию ObjectSetString - для "text" и "font". Используем "ChartRedraw" и возвращаем значение true. Вооружившись этими функциями, теперь мы можем создать начальные объекты панели, которые послужат нам начальной точкой в обработчике "OnInit".
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() //--- Initialize EA { create_rectangle(MAIN_PANEL, 632, 40, 617, 374, C'30,30,30', BORDER_FLAT); //--- Create main panel background create_rectangle(HEADER_PANEL, 632, 40, 617, 27, C'60,60,60', BORDER_FLAT); //--- Create header panel background create_label(HEADER_PANEL_ICON, CharToString(91), 620, 54, 18, clrAqua, "Wingdings"); //--- Create header icon create_label(HEADER_PANEL_TEXT, "TimeframeScanner", 527, 52, 13, COLOR_WHITE); //--- Create header title create_label(CLOSE_BUTTON, CharToString('r'), 32, 54, 18, clrYellow, "Webdings"); //--- Create close button // Create header rectangle and label create_rectangle(SYMBOL_RECTANGLE, 630, 75, WIDTH_TIMEFRAME, HEIGHT_RECTANGLE, clrGray); //--- Create symbol rectangle create_label(SYMBOL_TEXT, _Symbol, 585, 85, 11, COLOR_WHITE); //--- Create symbol label // Create summary and indicator headers (rectangles and labels) string header_names[] = {"BUY", "SELL", "RSI", "STOCH", "CCI", "ADX", "AO"}; //--- Define header titles for(int header_index = 0; header_index < ArraySize(header_names); header_index++) { //--- Loop through headers int x_offset = (630 - WIDTH_TIMEFRAME) - (header_index < 2 ? header_index * WIDTH_SIGNAL : 2 * WIDTH_SIGNAL + (header_index - 2) * WIDTH_INDICATOR) + (1 + header_index); //--- Calculate x position int width = (header_index < 2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); //--- Set width based on header type create_rectangle(HEADER_RECTANGLE + IntegerToString(header_index), x_offset, 75, width, HEIGHT_RECTANGLE, clrGray); //--- Create header rectangle create_label(HEADER_TEXT + IntegerToString(header_index), header_names[header_index], x_offset - width/2, 85, 11, COLOR_WHITE); //--- Create header label } // Create timeframe rectangles and labels, and summary/indicator cells for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { //--- Loop through timeframes // Highlight current timeframe color timeframe_background = (timeframes_array[timeframe_index] == _Period) ? clrLimeGreen : clrGray; //--- Set background color for current timeframe color timeframe_text_color = (timeframes_array[timeframe_index] == _Period) ? COLOR_BLACK : COLOR_WHITE; //--- Set text color for current timeframe create_rectangle(TIMEFRAME_RECTANGLE + IntegerToString(timeframe_index), 630, (75 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), WIDTH_TIMEFRAME, HEIGHT_RECTANGLE, timeframe_background); //--- Create timeframe rectangle create_label(TIMEFRAME_TEXT + IntegerToString(timeframe_index), truncate_timeframe_name(timeframe_index), 585, (85 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), 11, timeframe_text_color); //--- Create timeframe label // Create summary and indicator cells for(int header_index = 0; header_index < ArraySize(header_names); header_index++) { //--- Loop through headers for cells string cell_rectangle_name, cell_text_name; //--- Declare cell name and label variables color cell_background = (header_index < 2) ? COLOR_LIGHT_GRAY : COLOR_BLACK; //--- Set cell background color switch(header_index) { //--- Select cell type case 0: cell_rectangle_name = BUY_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = BUY_TEXT + IntegerToString(timeframe_index); break; //--- Buy cell case 1: cell_rectangle_name = SELL_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = SELL_TEXT + IntegerToString(timeframe_index); break; //--- Sell cell case 2: cell_rectangle_name = RSI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = RSI_TEXT + IntegerToString(timeframe_index); break; //--- RSI cell case 3: cell_rectangle_name = STOCH_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = STOCH_TEXT + IntegerToString(timeframe_index); break; //--- Stochastic cell case 4: cell_rectangle_name = CCI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = CCI_TEXT + IntegerToString(timeframe_index); break; //--- CCI cell case 5: cell_rectangle_name = ADX_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = ADX_TEXT + IntegerToString(timeframe_index); break; //--- ADX cell case 6: cell_rectangle_name = AO_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = AO_TEXT + IntegerToString(timeframe_index); break; //--- AO cell } int x_offset = (630 - WIDTH_TIMEFRAME) - (header_index < 2 ? header_index * WIDTH_SIGNAL : 2 * WIDTH_SIGNAL + (header_index - 2) * WIDTH_INDICATOR) + (1 + header_index); //--- Calculate x position int width = (header_index < 2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); //--- Set width based on cell type create_rectangle(cell_rectangle_name, x_offset, (75 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), width, HEIGHT_RECTANGLE, cell_background); //--- Create cell rectangle create_label(cell_text_name, "-/-", x_offset - width/2, (85 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), 10, COLOR_WHITE); //--- Create cell label } } // Initialize indicator arrays ArraySetAsSeries(rsi_values, true); //--- Set RSI array as timeseries ArraySetAsSeries(stochastic_values, true); //--- Set Stochastic array as timeseries ArraySetAsSeries(cci_values, true); //--- Set CCI array as timeseries ArraySetAsSeries(adx_values, true); //--- Set ADX array as timeseries ArraySetAsSeries(ao_values, true); //--- Set AO array as timeseries return(INIT_SUCCEEDED); //--- Return initialization success }
В обработчике OnInit инициализируем пользовательский интерфейс панели сканера по нескольким таймфреймам. Используем "create_rectangle", чтобы нарисовать "MAIN_PANEL" в точке (632, 40) размером 617x374 пикселя в “C’30,30,30’” и "HEADER_PANEL" в той же позиции с высотой 27 пикселей в “C’60,60,60’”. Используем "create_label", чтобы добавить "HEADER_PANEL_ICON" с символом Wingdings в точку (620, 54). Используем символы по умолчанию на MQL5 и используем функцию CharToString для преобразования кода символа в строку. Вот код символа, который мы использовали - 91, но вы можете использовать любой, какой вам больше нравится.

Затем создаем "HEADER_PANEL_TEXT" с “TimeframeScanner” в (527, 52) и "CLOSE_BUTTON" в (32, 54), но на этот раз используем другой шрифт и сопоставляем букву "r" со строкой. Вот визуализация различных символов шрифта, которые можно использовать.

Настраиваем отображение символа, используя функцию "create_rectangle" для "SYMBOL_RECTANGLE" в (630, 75), размером "WIDTH_TIMEFRAME" на "HEIGHT_RECTANGLE", серым цветом. Используем "create_label", чтобы поместить "SYMBOL_TEXT" в координату (585, 85) с текущим символом. Для заголовков определяем массив "header_names" с заголовками типа “BUY” и “RSI”, в цикле создаем "HEADER_RECTANGLE" при y=75 с x-смещениями на основе "WIDTH_SIGNAL" и "WIDTH_INDICATOR" и меток "HEADER_TEXT" при y=85, с помощью функции "create_label".
Создаём сетку таймфреймов, перебирая "timeframes_array". Используем "create_rectangle" для "TIMEFRAME_RECTANGLE" при x=630, y-смещениях от (75 + "HEIGHT_RECTANGLE"), скорректированную на -(1 + "timeframe_index"), окрашенную с помощью "timeframe_background". Используем "create_label" для "TIMEFRAME_TEXT" с именами из функции "truncate_timeframe_name". Для ячеек создаем цикл "BUY_RECTANGLE", "RSI_RECTANGLE" и т.д. с помощью функции "create_rectangle", используя "cell_background", и добавляем метки “-/-” с помощью функции "create_label". Инициализируем массивы индикаторов типа "rsi_values" с помощью функции ArraySetAsSeries, задавая их как временные ряды для обработки данных. Возвращаем INIT_SUCCEEDED для подтверждения успешной инициализации, формирования структуры информационной панели и структуры данных. После компиляции получаем следующий результат.

На изображении видно, что информационная панель у нас готова. Все, что нам сейчас нужно сделать, это добавить значения индикаторов и использовать их для анализа. У нас уже есть функция для анализа, нам просто нужно получить данные. Чтобы легко добиться этого, создаем функцию для обработки всей логики динамического обновления.
//+------------------------------------------------------------------+ //| Update indicator values | //+------------------------------------------------------------------+ void updateIndicators() //--- Update dashboard indicators { for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { //--- Loop through timeframes // Initialize indicator handles int rsi_indicator_handle = iRSI(_Symbol, timeframes_array[timeframe_index], 14, PRICE_CLOSE); //--- Create RSI handle int stochastic_indicator_handle = iStochastic(_Symbol, timeframes_array[timeframe_index], 14, 3, 3, MODE_SMA, STO_LOWHIGH); //--- Create Stochastic handle int cci_indicator_handle = iCCI(_Symbol, timeframes_array[timeframe_index], 20, PRICE_TYPICAL); //--- Create CCI handle int adx_indicator_handle = iADX(_Symbol, timeframes_array[timeframe_index], 14); //--- Create ADX handle int ao_indicator_handle = iAO(_Symbol, timeframes_array[timeframe_index]); //--- Create AO handle // Check for valid handles if(rsi_indicator_handle == INVALID_HANDLE || stochastic_indicator_handle == INVALID_HANDLE || cci_indicator_handle == INVALID_HANDLE || adx_indicator_handle == INVALID_HANDLE || ao_indicator_handle == INVALID_HANDLE) { //--- Check if any handle is invalid Print("Failed to create indicator handle for timeframe ", truncate_timeframe_name(timeframe_index)); //--- Log failure continue; //--- Skip to next timeframe } // Copy indicator values if(CopyBuffer(rsi_indicator_handle, 0, 0, 1, rsi_values) <= 0 || //--- Copy RSI value CopyBuffer(stochastic_indicator_handle, 1, 0, 1, stochastic_values) <= 0 || //--- Copy Stochastic signal line value CopyBuffer(cci_indicator_handle, 0, 0, 1, cci_values) <= 0 || //--- Copy CCI value CopyBuffer(adx_indicator_handle, 0, 0, 1, adx_values) <= 0 || //--- Copy ADX value CopyBuffer(ao_indicator_handle, 0, 0, 1, ao_values) <= 0) { //--- Copy AO value Print("Failed to copy buffer for timeframe ", truncate_timeframe_name(timeframe_index)); //--- Log copy failure continue; //--- Skip to next timeframe } // Update RSI color rsi_text_color = (rsi_values[0] < 30) ? clrBlue : (rsi_values[0] > 70) ? clrRed : COLOR_WHITE; //--- Set RSI text color update_label(RSI_TEXT + IntegerToString(timeframe_index), DoubleToString(rsi_values[0], 2), rsi_text_color); //--- Update RSI label // Update Stochastic (Signal Line only) color stochastic_text_color = (stochastic_values[0] < 20) ? clrBlue : (stochastic_values[0] > 80) ? clrRed : COLOR_WHITE; //--- Set Stochastic text color update_label(STOCH_TEXT + IntegerToString(timeframe_index), DoubleToString(stochastic_values[0], 2), stochastic_text_color); //--- Update Stochastic label // Update CCI color cci_text_color = (cci_values[0] < -100) ? clrBlue : (cci_values[0] > 100) ? clrRed : COLOR_WHITE; //--- Set CCI text color update_label(CCI_TEXT + IntegerToString(timeframe_index), DoubleToString(cci_values[0], 2), cci_text_color); //--- Update CCI label // Update ADX color adx_text_color = (adx_values[0] > 25) ? clrBlue : COLOR_WHITE; //--- Set ADX text color update_label(ADX_TEXT + IntegerToString(timeframe_index), DoubleToString(adx_values[0], 2), adx_text_color); //--- Update ADX label // Update AO color ao_text_color = (ao_values[0] > 0) ? clrGreen : (ao_values[0] < 0) ? clrRed : COLOR_WHITE; //--- Set AO text color update_label(AO_TEXT + IntegerToString(timeframe_index), DoubleToString(ao_values[0], 2), ao_text_color); //--- Update AO label // Update Buy/Sell signals string buy_signal = calculate_signal_strength(rsi_values[0], stochastic_values[0], cci_values[0], adx_values[0], ao_values[0], true); //--- Calculate buy signal string sell_signal = calculate_signal_strength(rsi_values[0], stochastic_values[0], cci_values[0], adx_values[0], ao_values[0], false); //--- Calculate sell signal color buy_text_color = (buy_signal == "Strong Buy") ? COLOR_WHITE : COLOR_WHITE; //--- Set buy text color color buy_background = (buy_signal == "Strong Buy") ? clrGreen : (buy_signal == "Buy") ? clrSeaGreen : COLOR_DARK_GRAY; //--- Set buy background color update_rectangle(BUY_RECTANGLE + IntegerToString(timeframe_index), buy_background); //--- Update buy rectangle update_label(BUY_TEXT + IntegerToString(timeframe_index), buy_signal, buy_text_color); //--- Update buy label color sell_text_color = (sell_signal == "Strong Sell") ? COLOR_WHITE : COLOR_WHITE; //--- Set sell text color color sell_background = (sell_signal == "Strong Sell") ? clrRed : (sell_signal == "Sell") ? clrSalmon : COLOR_DARK_GRAY; //--- Set sell background color update_rectangle(SELL_RECTANGLE + IntegerToString(timeframe_index), sell_background); //--- Update sell rectangle update_label(SELL_TEXT + IntegerToString(timeframe_index), sell_signal, sell_text_color); //--- Update sell label // Release indicator handles IndicatorRelease(rsi_indicator_handle); //--- Release RSI handle IndicatorRelease(stochastic_indicator_handle); //--- Release Stochastic handle IndicatorRelease(cci_indicator_handle); //--- Release CCI handle IndicatorRelease(adx_indicator_handle); //--- Release ADX handle IndicatorRelease(ao_indicator_handle); //--- Release AO handle } }
Чтобы упростить управление обновлением значений на информационной панели, реализуем функцию "updateIndicators" для обновления значений индикаторов и сигналов. В цикле перебираем "timeframes_array", используя "timeframe_index", обрабатывая каждый таймфрейм. Используем функции iRSI, "iStochastic", "iCCI", iADX и "iAO" для создания хэндлов индикаторов типа "rsi_indicator_handle" для текущего инструмента и таймфрейма, настраивая такие параметры, как 14-периодный RSI и 20-периодный CCI. Все настройки индикаторов настраиваются в соответствии с вашими потребностями, поэтому не ограничивайте себя значениями по умолчанию.
Затем проверяем, равен ли какой-либо хэндл, такой как "rsi_indicator_handle", значению INVALID_HANDLE, что указывает на ошибку при создании. Если это так, используем "Print", чтобы вывести сообщение об ошибке с выводом функции "truncate_timeframe_name" и перейти к следующему таймфрейму. Используем CopyBuffer для извлечения последних значений в массивы типа "rsi_values", и в случае сбоя выводим сообщение об ошибке и продолжаем. Обновляем отображение индикаторов с помощью функции "update_label". Например, устанавливаем "rsi_text_color" на основе "rsi_values[0]" (синий, если <30, красный, если >70, иначе "COLOR_WHITE") и обновляем "RSI_TEXT" форматированным значением функции "DoubleToString". Повторяем это для "stochastic_values", "cci_values", "adx_values" и "ao_values", применяя цветовую логику (например, зеленый для положительных "ao_values").
Вычисляем сигналы, используя функцию "calculate_signal_strength", передавая "rsi_values[0]" и другие, чтобы получить "buy_signal" и "sell_signal". Производим настройку "buy_background" (например, зелёный для “Strong Buy”) и используем "update_rectangle" для "BUY_RECTANGLE", обновляя "BUY_TEXT" с помощью "update_label". Точно также делаем для "sell_background" и "SELL_TEXT". Наконец, используем IndicatorRelease для освобождения хэндлов, таких как "rsi_indicator_handle", обеспечивая эффективное управление ресурсами. Вспомогательные функции, которые мы использовали, определены следующим образом.
//+------------------------------------------------------------------+ //| Update rectangle background color | //+------------------------------------------------------------------+ bool update_rectangle(string object_name, color background_color)//--- Function to update rectangle color { int found = ObjectFind(0, object_name); //--- Find rectangle object if(found < 0) { //--- Check if object not found ResetLastError(); //--- Reset error code Print("UNABLE TO FIND THE RECTANGLE: ", object_name, ". ERR Code: ", GetLastError()); //--- Log error return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color); //--- Set background color ChartRedraw(0); //--- Redraw chart return(true); //--- Return success } //+------------------------------------------------------------------+ //| Update label text and color | //+------------------------------------------------------------------+ bool update_label(string object_name, string text, color text_color) //--- Function to update label { int found = ObjectFind(0, object_name); //--- Find label object if(found < 0) { //--- Check if object not found ResetLastError(); //--- Reset error code Print("UNABLE TO FIND THE LABEL: ", object_name, ". ERR Code: ", GetLastError()); //--- Log error return(false); //--- Return failure } ObjectSetString(0, object_name, OBJPROP_TEXT, text); //--- Set label text ObjectSetInteger(0, object_name, OBJPROP_COLOR, text_color); //--- Set text color ChartRedraw(0); //--- Redraw chart return(true); //--- Return success }
Определяем функцию "update_rectangle", принимая "object_name" и "background_color" в качестве параметров для изменения внешнего вида прямоугольников. Используем ObjectFind, чтобы найти прямоугольник, сохраняя результат в поле "found". Если значение "found" меньше 0, что указывает на отсутствие объекта, используем ResetLastError, выводим сообщение об ошибке с помощью функций "Print" и "GetLastError" и возвращаем значение false. Обновляем фон прямоугольника, используя функцию "ObjectSetInteger", чтобы установить для "OBJPROP_BGCOLOR" значение "background_color". Используем ChartRedraw, чтобы обновить график и возвращаем значение true в случае успеха. Для обновления текста определяем функцию "update_label" с помощью параметров "object_name", "text" и "text_color".
Используем "ObjectFind" для проверки существования метки, и если значение "found" отрицательное, используем "ResetLastError", выводим сообщение об ошибке с помощью функции "Print" и возвращаем значение false. Используем ObjectSetString, чтобы присвоить "OBJPROP_TEXT" значение "text", а функцию ObjectSetInteger - чтобы присвоить "OBJPROP_COLOR" значение "text_color". Используем "ChartRedraw" для обновления графика и возврата значения true, что позволяет динамически обновлять метки. Теперь мы можем вызвать функцию обновления на тике, чтобы внести изменения в информационную панель.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() //--- Handle tick events { if (panel_is_visible) { //--- Check if panel is visible updateIndicators(); //--- Update indicators } }
Здесь, в обработчике OnTick, просто вызываем функцию "updateIndicators", если панель видна, чтобы применить обновления. В конце концов, нам надо удалить созданные нами объекты так, чтобы они удалялись с этого графика тогда, когда они нам больше не нужны.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) //--- Deinitialize EA { ObjectDelete(0, MAIN_PANEL); //--- Delete main panel ObjectDelete(0, HEADER_PANEL); //--- Delete header panel ObjectDelete(0, HEADER_PANEL_ICON); //--- Delete header icon ObjectDelete(0, HEADER_PANEL_TEXT); //--- Delete header title ObjectDelete(0, CLOSE_BUTTON); //--- Delete close button ObjectsDeleteAll(0, SYMBOL_RECTANGLE); //--- Delete all symbol rectangles ObjectsDeleteAll(0, SYMBOL_TEXT); //--- Delete all symbol labels ObjectsDeleteAll(0, TIMEFRAME_RECTANGLE); //--- Delete all timeframe rectangles ObjectsDeleteAll(0, TIMEFRAME_TEXT); //--- Delete all timeframe labels ObjectsDeleteAll(0, HEADER_RECTANGLE); //--- Delete all header rectangles ObjectsDeleteAll(0, HEADER_TEXT); //--- Delete all header labels ObjectsDeleteAll(0, RSI_RECTANGLE); //--- Delete all RSI rectangles ObjectsDeleteAll(0, RSI_TEXT); //--- Delete all RSI labels ObjectsDeleteAll(0, STOCH_RECTANGLE); //--- Delete all Stochastic rectangles ObjectsDeleteAll(0, STOCH_TEXT); //--- Delete all Stochastic labels ObjectsDeleteAll(0, CCI_RECTANGLE); //--- Delete all CCI rectangles ObjectsDeleteAll(0, CCI_TEXT); //--- Delete all CCI labels ObjectsDeleteAll(0, ADX_RECTANGLE); //--- Delete all ADX rectangles ObjectsDeleteAll(0, ADX_TEXT); //--- Delete all ADX labels ObjectsDeleteAll(0, AO_RECTANGLE); //--- Delete all AO rectangles ObjectsDeleteAll(0, AO_TEXT); //--- Delete all AO labels ObjectsDeleteAll(0, BUY_RECTANGLE); //--- Delete all buy rectangles ObjectsDeleteAll(0, BUY_TEXT); //--- Delete all buy labels ObjectsDeleteAll(0, SELL_RECTANGLE); //--- Delete all sell rectangles ObjectsDeleteAll(0, SELL_TEXT); //--- Delete all sell labels ChartRedraw(0); //--- Redraw chart }
Наконец, мы реализуем процесс очистки с помощью функции OnDeinit, которая запускается при удалении советника. Используем ObjectDelete для удаления отдельных элементов пользовательского интерфейса, начиная с прямоугольника "MAIN_PANEL", за которым следуют "HEADER_PANEL", "HEADER_PANEL_ICON", "HEADER_PANEL_TEXT" и "CLOSE_BUTTON", гарантируя, что компоненты главной панели и заголовка будут удалены с графика.
Мы систематически удаляем все объекты информационной панели, используя функцию ObjectsDeleteAll для каждого типа элементов. Удаляем все прямоугольники и метки, связанные с "SYMBOL_RECTANGLE" и "SYMBOL_TEXT", "TIMEFRAME_RECTANGLE" и "TIMEFRAME_TEXT", а также "HEADER_RECTANGLE" и "HEADER_TEXT", очищая отображения символа, таймфрейма и заголовка. Также удаляем объекты, связанные с индикаторами, включая "RSI_RECTANGLE", "STOCH_RECTANGLE", "CCI_RECTANGLE", "ADX_RECTANGLE" и "AO_RECTANGLE", вместе с соответствующими текстовыми метками, такими как "RSI_TEXT".
Завершаем очистку, используя функцию "ObjectsDeleteAll", чтобы удалить все объекты "BUY_RECTANGLE" и "SELL_RECTANGLE" вместе с их метками "BUY_TEXT" и "SELL_TEXT", удалив все элементы, связанные с сигналом. Наконец, используем ChartRedraw для обновления графика, обеспечивая чистый вид после деинициализации. В итоге, нужно позаботиться о кнопке отмены, чтобы при нажатии на нее мы закрывали информационную панель и отключали дальнейшие обновления.
//+------------------------------------------------------------------+ //| Expert chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int event_id, //--- Event ID const long& long_param, //--- Long parameter const double& double_param, //--- Double parameter const string& string_param) //--- String parameter { if (event_id == CHARTEVENT_OBJECT_CLICK) { //--- Check for object click event if (string_param == CLOSE_BUTTON) { //--- Check if close button clicked Print("Closing the panel now"); //--- Log panel closure PlaySound("alert.wav"); //--- Play alert sound panel_is_visible = false; //--- Hide panel ObjectDelete(0, MAIN_PANEL); //--- Delete main panel ObjectDelete(0, HEADER_PANEL); //--- Delete header panel ObjectDelete(0, HEADER_PANEL_ICON); //--- Delete header icon ObjectDelete(0, HEADER_PANEL_TEXT); //--- Delete header title ObjectDelete(0, CLOSE_BUTTON); //--- Delete close button ObjectsDeleteAll(0, SYMBOL_RECTANGLE); //--- Delete all symbol rectangles ObjectsDeleteAll(0, SYMBOL_TEXT); //--- Delete all symbol labels ObjectsDeleteAll(0, TIMEFRAME_RECTANGLE); //--- Delete all timeframe rectangles ObjectsDeleteAll(0, TIMEFRAME_TEXT); //--- Delete all timeframe labels ObjectsDeleteAll(0, HEADER_RECTANGLE); //--- Delete all header rectangles ObjectsDeleteAll(0, HEADER_TEXT); //--- Delete all header labels ObjectsDeleteAll(0, RSI_RECTANGLE); //--- Delete all RSI rectangles ObjectsDeleteAll(0, RSI_TEXT); //--- Delete all RSI labels ObjectsDeleteAll(0, STOCH_RECTANGLE); //--- Delete all Stochastic rectangles ObjectsDeleteAll(0, STOCH_TEXT); //--- Delete all Stochastic labels ObjectsDeleteAll(0, CCI_RECTANGLE); //--- Delete all CCI rectangles ObjectsDeleteAll(0, CCI_TEXT); //--- Delete all CCI labels ObjectsDeleteAll(0, ADX_RECTANGLE); //--- Delete all ADX rectangles ObjectsDeleteAll(0, ADX_TEXT); //--- Delete all ADX labels ObjectsDeleteAll(0, AO_RECTANGLE); //--- Delete all AO rectangles ObjectsDeleteAll(0, AO_TEXT); //--- Delete all AO labels ObjectsDeleteAll(0, BUY_RECTANGLE); //--- Delete all buy rectangles ObjectsDeleteAll(0, BUY_TEXT); //--- Delete all buy labels ObjectsDeleteAll(0, SELL_RECTANGLE); //--- Delete all sell rectangles ObjectsDeleteAll(0, SELL_TEXT); //--- Delete all sell labels ChartRedraw(0); //--- Redraw chart } } }
В обработчике OnChartEvent слушаем щелчки по объекту, когда идентификатором события является CHARTEVENT_OBJECT_CLICK, а нажатым объектом является кнопка отмены, и воспроизводим звуковой сигнал оповещения, используя PlaySound, чтобы оповестить пользователя о том, что панель отключается. Затем отключаем видимость панели и используем ту же логику, которую использовали для очистки графика в OnDeinit, чтобы очистить информационную панель. После компиляции получаем следующий результат.

На изображении мы видим, что информационная панель обновлена с указанием данных индикатора и направлений торговли. Теперь остается только протестировать работоспособность проекта, и это рассматривается в следующем разделе.
Тестирование на истории
Мы провели тестирование, а ниже представлена скомпилированная визуализация в едином формате растрового изображения Graphics Interchange Format (GIF).

Заключение
В заключение, мы разработали панель сканера по нескольким таймфреймам на MQL5, объединяющую структурированную сеточную компоновку, сигналы индикаторов в режиме реального времени и интерактивную кнопку закрытия для улучшения стратегических торговых решений. Мы продемонстрировали дизайн и реализацию этих функций, гарантируя их эффективность благодаря надежной инициализации и динамическим обновлениям, адаптированным к нашим торговым требованиям. Можно адаптировать эту информационную панель в соответствии со своими предпочтениями, что значительно улучшит вашу способность отслеживать рыночные сигналы и действовать в соответствии с ними на нескольких таймфреймах.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18319
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Моделирование рынка (Часть 17): Сокеты (XI)
Нейросети в трейдинге: Адаптивная факторная токенизация (MTmixAtt)
Индикатор CandleCode: Формализация свечных моделей в MQL5
Возможности Мастера MQL5, которые вам нужно знать (Часть 64): Использование паттернов каналов Демарка и конвертов с ядром белого шума
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования