English 中文 Español Deutsch 日本語
preview
Трейдинг с экономическим календарем MQL5 (Часть 3): Добавление сортировки по валюте, важности и времени

Трейдинг с экономическим календарем MQL5 (Часть 3): Добавление сортировки по валюте, важности и времени

MetaTrader 5Трейдинг |
336 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Введение

В этой статье я опираюсь на предыдущую работу об экономическом календаре на языке MetaQuotes Language 5 (MQL5), в которой мы разработали панель новостей для отображения экономических событий. Теперь мы улучшим эту панель управления, внедрив специальные фильтры по валюте, важности и времени, что позволит трейдерам сосредоточиться только на новостях, наиболее соответствующих их стратегиям. Эти фильтры обеспечат целенаправленное представление событий, влияющих на рынок, помогая оптимизировать процесс принятия решений и повысить эффективность торговли. Мы рассмотрим следующие темы:

  1. Введение
  2. Типы фильтров в экономических календарях
  3. Реализация фильтров в MQL5
  4. Заключение

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


Типы фильтров в экономических календарях

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

Фильтр валют

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

Фильтр важности

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


Реализация фильтров в MQL5

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

//--- Define flags to enable/disable filters
bool enableCurrencyFilter = true;  // Set to 'true' to enable currency filter, 'false' to disable
bool enableImportanceFilter = true; // Set to 'true' to enable importance filter, 'false' to disable
bool enableTimeFilter = true; // Set to 'true' to enable time filter, 'false' to disable

Здесь мы определяем три булевые переменные - enableCurrencyFilter, enableImportanceFilter и enableTimeFilter, которые мы будем использовать для включения/выключения фильтров валюты, важности и времени. Каждой переменной по умолчанию присвоено значение true, что означает, что все фильтры будут активны. Изменяя эти значения на false, мы можем отключить любые фильтры, которые нам не нужны.

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

string curr_filter[] = {"AUD","CAD","CHF","EUR","GBP","JPY","NZD","USD"};
int news_filter_count = 0;

Здесь мы определяем "строковый" массив curr_filter, содержащий список валютных пар — AUD, CAD, CHF, EUR, GBP, JPY, NZD и USD, — которые мы хотим использовать для сортировки новостей на основе определенных валют. Этот массив поможет нам сузить круг новостей, отображаемых на панели управления, сосредоточившись только на тех, которые имеют отношение к выбранным валютам. Мы также определяем переменную news_filter_count для отслеживания количества отфильтрованных новостных событий, соответствующих выбранным нами критериям, гарантируя, что мы отображаем только самую релевантную информацию. Переходим к логике фильтра.

//--- Check if the event’s currency matches any in the filter array (if the filter is enabled)
bool currencyMatch = false;
if (enableCurrencyFilter) {
   for (int j = 0; j < ArraySize(curr_filter); j++) {
      if (country.currency == curr_filter[j]) {
         currencyMatch = true;
         break;
      }
   }
   
   //--- If no match found, skip to the next event
   if (!currencyMatch) {
      continue;
   }
}

Здесь мы проверяем, соответствует ли валюта события какой-либо валюте в массиве curr_filter, но только если включен фильтр валют, на что указывает флаг enableCurrencyFilter. Если фильтр включен, мы проходим по массиву curr_filter, используя цикл for. Для каждой итерации мы сравниваем валюту события с валютами в фильтре.

Если совпадение найдено, мы устанавливаем флаг currencyMatch в значение true и прерываем (break) цикл. Если совпадений не найдено (то есть currencyMatch остается равным false), мы используем оператор continue для пропуска текущего события и перехода к следующему, гарантируя, что обрабатываются только соответствующие события. Затем мы используем ту же логику для сортировки событий по степени важности.

//--- Check importance level if importance filter is enabled
bool importanceMatch = false;
if (enableImportanceFilter) {
   for (int k = 0; k < ArraySize(allowed_importance_levels); k++) {
      if (event.importance == allowed_importance_levels[k]) {
         importanceMatch = true;
         break;
      }
   }
   
   //--- If importance does not match the filter criteria, skip the event
   if (!importanceMatch) {
      continue;
   }
}

Здесь мы проверяем уровень важности события по предопределенному массиву allowed_importance_levels, но только если включен фильтр важности, на что указывает флаг enableImportanceFilter. Если фильтр включен, мы проходим массив allowed_importance_levels с помощью цикла for, сравнивая важность события с уровнями в массиве.

Если совпадение найдено, мы устанавливаем флаг importanceMatch в true и прерываем (break) цикл. Если совпадений не найдено (то есть importanceMatch остается равным false), мы используем оператор continue для пропуска текущего события, гарантируя, что будут обработаны только события с требуемым уровнем важности. Массив для определения уровней важности используется следующим образом:

// Define the levels of importance to filter (low, moderate, high)
ENUM_CALENDAR_EVENT_IMPORTANCE allowed_importance_levels[] = {CALENDAR_IMPORTANCE_LOW, CALENDAR_IMPORTANCE_MODERATE, CALENDAR_IMPORTANCE_HIGH};

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

//--- Define time range for filtering news events based on daily period
datetime timeRange = PeriodSeconds(PERIOD_D1);
datetime timeBefore = TimeTradeServer() - timeRange;
datetime timeAfter = TimeTradeServer() + timeRange;

Определяем временной диапазон для сортировки новостей на основе дневного периода. Мы используем функцию PeriodSeconds с константой PERIOD_D1, чтобы определить количество секунд в сутках, которое мы затем присвоим datetime-переменной timeRange. Переменные timeBefore и timeAfter используются для расчета временного диапазона вокруг текущего времени сервера, полученного с помощью функции TimeTradeServer, вычитая и прибавляя timeRange соответственно. Это гарантирует, что для обработки будут рассматриваться только события, попадающие в указанный временной диапазон (в течение одного дня до или после текущего времени сервера). Обязательно внесите изменения в соответствии с вашими потребностями. Вооружившись этой логикой, мы можем применить временной фильтр.

//--- Apply time filter and set timeMatch flag (if the filter is enabled)
bool timeMatch = false;
if (enableTimeFilter) {
   datetime eventTime = values[i].time;
   
   if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) {
      timeMatch = true;  //--- Event is already released
   }
   else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) {
      timeMatch = true;  //--- Event is yet to be released
   }
   
   //--- Skip if the event doesn't match the time filter
   if (!timeMatch) {
      continue;
   }
}

Здесь мы применяем фильтр времени, проверяя, попадает ли время события в указанный временной диапазон, и используем флаг timeMatch, чтобы отслеживать, соответствует ли событие критериям. Если enableTimeFilter имеет значение true, мы сначала извлекаем время события из переменной values[i].time. Затем мы проверяем, находится ли время события в прошлом (между текущим временем сервера и timeBefore) или в будущем (между текущим временем сервера и timeAfter). Если время события попадает в любой из диапазонов, флаг timeMatch устанавливается в значение true, что указывает на то, что событие соответствует фильтру времени. Если совпадений не найдено, пропускаем событие, используя оператор continue.

На этом с фильтрами все. На этом этапе мы прошли все проверки и у нас есть какие-то новостные события. Обновляем счетчик новостных фильтров на единицу.

//--- If we reach here, the currency matches the filter
news_filter_count++; //--- Increment the count of filtered events

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

//--- Set alternating colors for each data row holder
color holder_color = (news_filter_count % 2 == 0) ? C'213,227,207' : clrWhite;

//--- Create rectangle label for each data row holder
createRecLabel(DATA_HOLDERS+string(news_filter_count),62,startY-1,716,26+1,holder_color,1,clrNONE);

Здесь мы устанавливаем чередующиеся цвета для каждого держателя строки данных, чтобы улучшить визуальное различие между строками. holder_color определяется с помощью тернарного оператора, в котором, если news_filter_count имеет четное значение (то есть "news_filter_count % 2 == 0"), используется светло-зеленый цвет (C'213,227,207'), а если нечетное, цвет устанавливается на белый. Это гарантирует, что каждая строка имеет чередующийся цвет, что облегчает чтение данных.

Затем мы создаем прямоугольную метку для каждого держателя строки данных, используя функцию createRecLabel, которая размещает цветной прямоугольник в указанных координатах. Метка однозначно идентифицируется путем объединения DATA_HOLDERS со строковым представлением news_filter_count, чтобы гарантировать, что каждая строка имеет уникальное имя, а размеры прямоугольника задаются в соответствии с содержимым. Граница прямоугольника имеет толщину 1, при этом мы устанавливаем цвет заливки на чередующийся holder_color, а цвет границы - на clrNONE (без цвета).

Однако обратите внимание, что мы добавили 1 пиксель к смещению держателей по оси Y (выделено желтым цветом), чтобы избавиться от границ. Вот результат сравнения.

До добавления 1 пикселя:

До изменения

После добавления 1 пикселя:

После изменения

Всё работает как надо. Следующее, что нам нужно сделать, — это обновить новости, отображаемые на панели управления после применения фильтров.

updateLabel(TIME_LABEL,"Server Time: "+TimeToString(TimeCurrent(),
           TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
           IntegerToString(news_filter_count)+"/"+IntegerToString(allValues));

Здесь мы используем функцию updateLabel для обновления метки, которая отображает текущее время сервера и общее количество отсортированных новостных событий. Мы обновляем метку, идентифицированную как TIME_LABEL, новой строкой, которая объединяет текущее время сервера и количество новостей. Чтобы получить текущее время сервера, мы используем функцию TimeCurrent и форматируем ее с помощью функции TimeToString с флагами TIME_DATE | TIME_SECONDS.

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

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

//+------------------------------------------------------------------+
//|     Function to create text label                                |
//+------------------------------------------------------------------+

bool updateLabel(string objName,string txt) {
    // Reset any previous errors
    ResetLastError();

    if (!ObjectSetString(0,objName,OBJPROP_TEXT,txt)) {
        Print(__FUNCTION__, ": failed to update the label! Error code = ", _LastError);
        return (false);
    }

    ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the label

    // Redraw the chart to display the label
    ChartRedraw(0);

    return (true); // Label creation successful
}

Здесь мы определяем функцию updateLabel, которую используем для обновления существующей метки на графике. Функция принимает два параметра: objName (имя объекта метки) и txt (текст, который будет отображаться на метке). Начнем со сброса всех предыдущих ошибок с помощью функции ResetLastError, чтобы получить "чистый лист". Далее пытаемся обновить текст метки с помощью предоставленной строки txt с использованием функции ObjectSetString. Если обновление не удалось, выводим сообщение об ошибке с помощью функции Print вместе с кодом ошибки, полученным из _LastError, и вернем false.

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

Обновленные новости

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

string curr_filter[] = {"AUD","CAD","CHF","EUR","GBP","JPY","NZD","USD"};
int news_filter_count = 0;

// Define the levels of importance to filter (low, moderate, high)
ENUM_CALENDAR_EVENT_IMPORTANCE allowed_importance_levels[] = {CALENDAR_IMPORTANCE_LOW, CALENDAR_IMPORTANCE_MODERATE, CALENDAR_IMPORTANCE_HIGH};

//--- Loop through each calendar value up to the maximum defined total
for (int i = 0; i < valuesTotal; i++){

   MqlCalendarEvent event; //--- Declare event structure
   CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID

   MqlCalendarCountry country; //--- Declare country structure
   CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID

   MqlCalendarValue value; //--- Declare calendar value structure
   CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values
   
   
   //--- Check if the event’s currency matches any in the filter array (if the filter is enabled)
   bool currencyMatch = false;
   if (enableCurrencyFilter) {
      for (int j = 0; j < ArraySize(curr_filter); j++) {
         if (country.currency == curr_filter[j]) {
            currencyMatch = true;
            break;
         }
      }
      
      //--- If no match found, skip to the next event
      if (!currencyMatch) {
         continue;
      }
   }
   
   //--- Check importance level if importance filter is enabled
   bool importanceMatch = false;
   if (enableImportanceFilter) {
      for (int k = 0; k < ArraySize(allowed_importance_levels); k++) {
         if (event.importance == allowed_importance_levels[k]) {
            importanceMatch = true;
            break;
         }
      }
      
      //--- If importance does not match the filter criteria, skip the event
      if (!importanceMatch) {
         continue;
      }
   }
   
   //--- Apply time filter and set timeMatch flag (if the filter is enabled)
   bool timeMatch = false;
   if (enableTimeFilter) {
      datetime eventTime = values[i].time;
      
      if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) {
         timeMatch = true;  //--- Event is already released
      }
      else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) {
         timeMatch = true;  //--- Event is yet to be released
      }
      
      //--- Skip if the event doesn't match the time filter
      if (!timeMatch) {
         continue;
      }
   }
   
   //--- If we reach here, the currency matches the filter
   news_filter_count++; //--- Increment the count of filtered events
   
   //--- Set alternating colors for each data row holder
   color holder_color = (news_filter_count % 2 == 0) ? C'213,227,207' : clrWhite;

   //--- Create rectangle label for each data row holder
   createRecLabel(DATA_HOLDERS+string(news_filter_count),62,startY-1,716,26+1,holder_color,1,clrNONE);

   //--- Initialize starting x-coordinate for each data entry
   int startX = 65;
   
   //--- Loop through calendar data columns
   for (int k=0; k<ArraySize(array_calendar); k++){
         
      //--- Print event details for debugging
      //Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time);
   
      //--- Skip event if currency does not match the selected country code
      // if (StringFind(_Symbol,country.currency) < 0) continue;
   
      //--- Prepare news data array with time, country, and other event details
      string news_data[ArraySize(array_calendar)];
      news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date
      news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time
      news_data[2] = country.currency; //--- Event country currency
   
      //--- Determine importance color based on event impact
      color importance_color = clrBlack;
      if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;}
      else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;}
      else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;}
   
      //--- Set importance symbol for the event
      news_data[3] = ShortToString(0x25CF);
   
      //--- Set event name in the data array
      news_data[4] = event.name;
         
      //--- Populate actual, forecast, and previous values in the news data array
      news_data[5] = DoubleToString(value.GetActualValue(),3);
      news_data[6] = DoubleToString(value.GetForecastValue(),3);
      news_data[7] = DoubleToString(value.GetPreviousValue(),3);         
      
      //--- Create label for each news data item
      if (k == 3){
         createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(22-12),news_data[k],importance_color,22,"Calibri");
      }
      else {
         createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,12,"Calibri");
      }
   
      //--- Increment x-coordinate for the next column
      startX += buttons[k]+3;
   }
   //--- Increment y-coordinate for the next row of data
   startY += 25;
   //Print(startY); //--- Print current y-coordinate for debugging
}

//Print("Final News = ",news_filter_count);
updateLabel(TIME_LABEL,"Server Time: "+TimeToString(TimeCurrent(),
           TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
           IntegerToString(news_filter_count)+"/"+IntegerToString(allValues));

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

//+------------------------------------------------------------------+
//|      Function to destroy the Dashboard panel                     |
//+------------------------------------------------------------------+

void destroy_Dashboard(){

   //--- Delete main rectangle panel
   ObjectDelete(0, MAIN_REC);
   
   //--- Delete first sub-rectangle in the dashboard
   ObjectDelete(0, SUB_REC1);
   
   //--- Delete second sub-rectangle in the dashboard
   ObjectDelete(0, SUB_REC2);
   
   //--- Delete header label text
   ObjectDelete(0, HEADER_LABEL);
   
   //--- Delete server time label text
   ObjectDelete(0, TIME_LABEL);
   
   //--- Delete label for impact/importance
   ObjectDelete(0, IMPACT_LABEL);

   //--- Delete all objects related to the calendar array
   ObjectsDeleteAll(0, ARRAY_CALENDAR);
   
   //--- Delete all objects related to the news array
   ObjectsDeleteAll(0, ARRAY_NEWS);
   
   //--- Delete all data holder objects created in the dashboard
   ObjectsDeleteAll(0, DATA_HOLDERS);
   
   //--- Delete all impact label objects
   ObjectsDeleteAll(0, IMPACT_LABEL);
   
   //--- Redraw the chart to update any visual changes
   ChartRedraw(0);
}

Здесь мы определяем пользовательскую функцию destroy_Dashboard, которую будем использовать для полного удаления всех элементов, созданных для нашей панели на графике, возвращая его в исходное состояние. Это подразумевает удаление каждого объекта, метки и держателя, используемых на панели управления. Сначала мы удаляем прямоугольник основной панели, вызывая функцию ObjectDelete на MAIN_REC, которая представляет собой основной контейнер нашей панели управления. Затем мы приступаем к удалению остальных прямоугольников, таких как SUB_REC1 и SUB_REC2, которые мы использовали для организации различных разделов панели управления.

После этого мы удаляем метки, отображающие такую информацию, как заголовок панели мониторинга (HEADER_LABEL), время сервера (TIME_LABEL) и уровень важности (IMPACT_LABEL). Каждая из этих меток удаляется, чтобы гарантировать удаление любой текстовой информации, отображаемой на графике. Далее удаляем все объекты в ARRAY_CALENDAR и ARRAY_NEWS, в которых хранится информация о календаре и новостных данных соответственно. Мы выполняем это действие с помощью функции ObjectsDeleteAll, которая позволяет нам очистить любые динамически созданные объекты, связанные с этими массивами.

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

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

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
//---

   destroy_Dashboard(); 
}

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


Заключение

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

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

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

Прикрепленные файлы |
Создаем индикатор канал Кельтнера с помощью пользовательской графики Canvas на MQL5 Создаем индикатор канал Кельтнера с помощью пользовательской графики Canvas на MQL5
В настоящей статье мы создаем индикатор канал Кельтнера с помощью пользовательской графики Canvas на MQL5. Мы подробно описываем интеграцию скользящих средних, расчеты ATR, а также улучшенную визуализацию графиков. Мы также расскажем о тестировании на истории, чтобы оценить эффективность индикатора и получить практическую информацию о трейдинге.
Пользовательские символы MQL5: Создаем символ 3D-баров Пользовательские символы MQL5: Создаем символ 3D-баров
В данной статье представлено детальное руководство по созданию инновационного индикатора 3DBarCustomSymbol.mq5, который генерирует пользовательские символы в MetaTrader 5, объединяющие цену, время, объем и волатильность в единое трехмерное представление. Рассматриваются математические основы, архитектура системы, практические аспекты реализации и применения в торговых стратегиях.
Возможности Мастера MQL5, которые вам нужно знать (Часть 50): Осциллятор Awesome Возможности Мастера MQL5, которые вам нужно знать (Часть 50): Осциллятор Awesome
Осциллятор Awesome — еще один индикатор Билла Вильямса, используемый для измерения импульса. Он может генерировать несколько сигналов. Как и в предыдущих статьях, мы рассмотрим его на основе паттернов, используя классы и сборку Мастера MQL5.
Автоматизация торговых стратегий на MQL5 (Часть 6): Поиск ордер-блоков для торговли по концепции Smart Money Автоматизация торговых стратегий на MQL5 (Часть 6): Поиск ордер-блоков для торговли по концепции Smart Money
В настоящей статье мы автоматизируем обнаружение ордер-блоков на MQL5, используя чистый анализ движения цены. Мы определяем ордер-блоки , реализуем их обнаружение и интегрируем автоматическое исполнение сделок. Наконец, для оценки эффективности стратегии, мы проведём её бэк-тестирование.