English 中文 Español Deutsch 日本語
preview
Разработка инструментария для анализа движения цен (Часть 17): Советник TrendLoom

Разработка инструментария для анализа движения цен (Часть 17): Советник TrendLoom

MetaTrader 5Торговые системы |
708 3
Christian Benjamin
Christian Benjamin

Содержание



Введение

Методы анализа рынка и подтверждения входа в сделку различаются у разных аналитиков трендов. Многие трейдеры анализируют несколько таймфреймов, таких как M1, M5 и M15 или H1, H4 и W1, чтобы подтвердить свои точки входа и повысить надежность сигналов. Вместо того чтобы переключать таймфреймы для оценки общего тренда, вы просто нажимаете кнопку и получаете своевременное обновление либо оно приходит автоматически. Вы когда-нибудь видели, как на более коротком таймфрейме начиналась продажа, вы открывали сделку, а затем, открыв график на более крупном таймфрейме, обнаруживали тренд на покупку?

Советник TrendLoom позволяет избежать такой ошибки. Он оснащен панелью с семью кнопками, представляющими различные торговые стили. Каждая кнопка отображает три таймфрейма, которые анализируются вместе с использованием скользящих средних для генерации сигналов, таких как BUY (покупка), SELL (продажа) или NEUTRAL (нейтральное состояние). Этот мощный инструмент обеспечивает быстрое обновление подтверждений и непрерывно обновляется по мере обнаружения соответствующих сигналов.


Обзор стратегии

Советник TrendLoom EA имеет графический интерфейс (панель). Панель содержит семь кнопок, каждая из которых соответствует определенной торговой стратегии.
  • Short-Term Focus (M1, M5, M15) - краткосрочная торговля
  • Scalping/Intraday (M5, M15, H1) - скальпинг/внутридневная торговля
  • Swing Trading (M15, H1, H4) - свинговая торговля
  • Trend Trading (H1, H4, D1) - торговля по тренду
  • MTF Trend Confirmation (H1, H4, W1) - подтверждение тренда на нескольких таймфреймах
  • Short Scalper/Mid-Trend (M5, H1, D1) - короткий скальпинг
  • Long-Term Trend (H1, D1, W1) - долгосрочный тренд

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

  • Сбор данных - для каждого из трех временных интервалов (например, M1, M5 и M15) советник получает цену закрытия последней полностью сформированной свечи.
  • Расчет SMA - для каждого таймфрейма советник рассчитывает простую скользящую среднюю (SMA) за 50 периодов. Она служит ориентиром для текущей цены.
Генерация индивидуальных сигналов - советник сравнивает цену закрытия с соответствующей простой скользящей средней

  • Если цена находится выше скользящей средней, это считается бычьим сигналом и ей присваивается значение +1.
  • Если цена находится ниже скользящей средней, это расценивается как медвежий сигнал, и ей присваивается значение -1.
  • Когда цена равна скользящей средней, сигнал нейтральный (0).

Объединение сигналов

  • Три отдельных сигнала (по одному из каждого таймфрейма) суммируются.
  • Определение окончательного сигнала:
  • Если сумма равна 2 или больше, это указывает на сильный бычий импульс. Советник возвращает сигнал на покупку.
  • Если сумма равна -2 или меньше, это указывает на сильный медвежий импульс. Советник возвращает сигнал на продажу.
  • В противном случае сигналы являются смешанными или нейтральными.

Давайте рассмотрим следующую схему, чтобы лучше понять этот процесс.

Блок-схема

Рис 1. Блок-схема


Реализация в MQL5

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

//+------------------------------------------------------------------+
//|                                                 TrendLoom EA.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.00"
#property strict

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

Сюда входят директивы и библиотеки ссылок в папке Include в MetaEditor's. Файлы в подпапке Controls содержат встроенные классы для диалоговых окон и кнопок. Кроме того, они содержат классы для меток и панелей. Такая конструкция упрощает создание интерактивного интерфейса без переписывания кода. Файл в подпапке ChartObjects (ChartObjectsTxtControls.mqh) позволяет отображать динамический текст на графике.

#include <Controls/Dialog.mqh>
#include <Controls/Button.mqh>
#include <Controls/Label.mqh>
#include <Controls/Panel.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>  // Adjusted include path (singular folder)

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

#ifndef ALIGN_LEFT
  #define ALIGN_LEFT   0
#endif
#ifndef ALIGN_CENTER
  #define ALIGN_CENTER 1
#endif
#ifndef ALIGN_RIGHT
  #define ALIGN_RIGHT  2
#endif

#define clrSilver 0xC0C0C0

Советник задает входные параметры, которые регулируют внешний вид и положение панели и ее кнопок. Параметры PanelX, PanelY и PanelWidth задают геометрию панели, а параметры цвета определяют визуальную тему. Размеры кнопок регулируются параметрами btnWidth, btnHeight и btnSpacing, а советник позволяет настраивать как расположение кнопок, так и их цвета. Данная конфигурация обеспечивает гибкость в настройке пользовательского интерфейса под ваши потребности.

//---- Input parameters -----------------------------------------------
input int    PanelX               = 10;
input int    PanelY               = 10;
input int    PanelWidth           = 250;
input int    btnWidth             = 220;
input int    btnHeight            = 30;
input int    btnSpacing           = 5;

input color  PanelBackgroundColor = clrDimGray;
input color  PanelHeaderColor     = clrBlueViolet;
input color  ButtonBgColor        = clrBlack;
input color  ButtonTextColor      = clrBlueViolet;
input color  AnalysisTextColor    = clrLime;

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

//---- Button Names and Texts (7 analysis options) --------------------
string buttonNames[7] =
  {
   "btnShortTerm",
   "btnScalping",
   "btnSwing",
   "btnTrend",
   "btnMTFTrend",
   "btnShortScalper",
   "btnLongTerm"
  };

string buttonTexts[7] =
  {
   "Short Term Focus\n(M1, M5, M15)",
   "Scalping/Intraday\n(M5, M15, H1)",
   "Swing Trading\n(M15, H1, H4)",
   "Trend Trading\n(H1, H4, D1)",
   "MTF Trend Confirm\n(H1, H4, W1)",
   "Short Scalper/Mid Trend\n(M5, H1, D1)",
   "Long Term Trend\n(H1, D1, W1)"
  };

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

// Global object names for panel header and analysis label
#define PANEL_BG "PanelBG"
#define PANEL_HEADER "PanelHeader"
#define ANALYSIS_LABEL "AnalysisResult"

Затем в коде объявляются две вспомогательные функции: GetSMA вычисляет простую скользящую среднюю, а AnalyzeTimeframes анализирует рынок на разных таймфреймах. Эти функции составляют основу логики анализа рынка.

//--- Helper function declarations
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift);
string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3);

Пользовательский класс CTrendLoomPanel наследуется от CAppDialog. Он объединяет все элементы пользовательского интерфейса, такие как заголовок, главная панель, кнопки и метка результата. Такая конструкция создает модульный интерфейс, который проще в управлении и расширении.

Создание панели

Метод CreateTrendPanel сначала создает диалоговое окно. Затем создается заголовок с пользовательским текстом, цветом, размером и стилем шрифта. Выравнивание устанавливается с помощью функции ObjectSetInteger.

bool CreateTrendPanel(const long chart, const string name, const int x1, const int y1, const int x2, const int y2)
{
   if(!CAppDialog::Create(chart, name, 0, x1, y1, x2, y2))
   {
      Print("Failed to create TrendLoom dialog.");
      return false;
   }
   if(!m_lblHeader.Create(0, "TrendLoomHeader", 0, 10, 10, x2 - x1 - 20, 30))
   {
      Print("Failed to create header label.");
      return false;
   }
   m_lblHeader.Text("TrendLoom EA");
   m_lblHeader.Color(PanelHeaderColor);
   m_lblHeader.FontSize(14);
   m_lblHeader.Font("Segoe UI");
   Add(m_lblHeader);
   if(!ObjectSetInteger(0L, m_lblHeader.Name(), OBJPROP_ALIGN, (long)ALIGN_CENTER))
      Print("Failed to set header alignment");

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

Обработка событий

Метод OnEvent обрабатывает взаимодействие с пользователем. При нажатии кнопки происходит вызов функции AnalyzeTimeframes с соответствующими параметрами таймфреймов. Результаты анализа обновляются на панели, и отображается предупреждение.

bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(sparam == "btnShortTerm")
   {
      string res = AnalyzeTimeframes(PERIOD_M1, PERIOD_M5, PERIOD_M15);
      string out = "Short Term Focus: " + res;
      UpdateResults(out);
      Alert(out);
      return true;
   }
   else if(sparam == "btnScalping")
   {
      string res = AnalyzeTimeframes(PERIOD_M5, PERIOD_M15, PERIOD_H1);
      string out = "Scalping/Intraday: " + res;
      UpdateResults(out);
      Alert(out);
      return true;
   }
   // Additional conditions for other buttons
   return false;
}

Обновление пользовательского интерфейса

Метод UpdateResults обновляет метку результатов, добавляя новые данные анализа. Затем вызывается функция ChartRedraw, благодаря чему обновленная информация отображается немедленно.

void UpdateResults(const string &result)
{
   m_lblResults.Text("Analysis Result: " + result);
   ChartRedraw();
}

Основные аналитические функции

Расчет простой скользящей средней (SMA)

Функция GetSMA вычисляет скользящую среднюю, создавая хэндл индикатора с помощью функции. Она копирует значения SMA из буфера индикатора, а затем освобождает хэндл для освобождения ресурсов.
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
   int handle = iMA(symbol, timeframe, period, 0, MODE_SMA, PRICE_CLOSE);
   if(handle == INVALID_HANDLE)
   {
      Print("Failed to create iMA handle for timeframe ", timeframe);
      return 0.0;
   }
   double sma[];
   if(CopyBuffer(handle, 0, shift, 1, sma) <= 0)
   {
      Print("Failed to copy buffer for timeframe ", timeframe);
      IndicatorRelease(handle);
      return 0.0;
   }
   double result = sma[0];
   IndicatorRelease(handle);
   return result;
}

Анализ нескольких таймфреймов

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

string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3)
{
   int period = 50;
   int shift  = 1; // last completed candle

   double price1 = iClose(_Symbol, tf1, shift);
   double sma1   = GetSMA(_Symbol, tf1, period, shift);
   int signal1   = (price1 > sma1) ? 1 : (price1 < sma1 ? -1 : 0);

   double price2 = iClose(_Symbol, tf2, shift);
   double sma2   = GetSMA(_Symbol, tf2, period, shift);
   int signal2   = (price2 > sma2) ? 1 : (price2 < sma2 ? -1 : 0);

   double price3 = iClose(_Symbol, tf3, shift);
   double sma3   = GetSMA(_Symbol, tf3, period, shift);
   int signal3   = (price3 > sma3) ? 1 : (price3 < sma3 ? -1 : 0);

   int sum = signal1 + signal2 + signal3;
   if(sum >= 2)
      return "BUY";
   else if(sum <= -2)
      return "SELL";
   else
      return "NEUTRAL";
}
Функции жизненного цикла советника отвечают за инициализацию, очистку и обработку событий. Функция OnInit создает панель TrendLoom, используя входные параметры. Если создать панель не удалось, советник возвращает ошибку инициализации.
int OnInit()
{
   if(!TrendPanel.CreateTrendPanel(0, "TrendLoom Panel", PanelX, PanelY, PanelX + PanelWidth + 20, PanelY + 400))
   {
      Print("Failed to create TrendLoom Panel.");
      return INIT_FAILED;
   }
   return INIT_SUCCEEDED;
}

Функция OnDeinit выполняет очистку, уничтожая панель при удалении советника или закрытии графика.

void OnDeinit(const int reason)
{
   TrendPanel.Destroy(reason);
}

Наконец, функция OnChartEvent перенаправляет события графика обработчику событий панели, чтобы интерфейс оставался отзывчивым.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   TrendPanel.ChartEvent(id, lparam, dparam, sparam);
}

В MetaEditor необходимые файлы хранятся в папке include. Для доступа к указанным файлам, как показано в фрагменте кода, обратитесь к подпапкам, изображенным на скриншотах ниже. Такая организация гарантирует, что компилятор найдет файлы элементов управления диалогом, кнопкой, меткой и панелью в папке include/Controls, а элементы управления объектами графика — в папке include/ChartObjects.

#include <Controls/Dialog.mqh>
#include <Controls/Button.mqh>
#include <Controls/Label.mqh>
#include <Controls/Panel.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>  // Adjusted include path (singular folder)

Шаг 1

Рис 2. Шаг 1

Шаг 2

Рис. 3. Шаг 2


Код MQL5

//+------------------------------------------------------------------+
//|                                                 TrendLoom EA.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.00"
#property strict

#include <Controls/Dialog.mqh>
#include <Controls/Button.mqh>
#include <Controls/Label.mqh>
#include <Controls/Panel.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>  // Adjusted include path (singular folder)

// Define alignment constants if not already defined
#ifndef ALIGN_LEFT
#define ALIGN_LEFT   0
#endif
#ifndef ALIGN_CENTER
#define ALIGN_CENTER 1
#endif
#ifndef ALIGN_RIGHT
#define ALIGN_RIGHT  2
#endif

#define clrSilver 0xC0C0C0

//---- Input parameters -----------------------------------------------
input int    PanelX               = 10;       // Top-left X coordinate of panel
input int    PanelY               = 10;       // Top-left Y coordinate of panel
input int    PanelWidth           = 250;      // Panel width (for longer text)
input int    btnWidth             = 220;      // Button width
input int    btnHeight            = 30;       // Button height
input int    btnSpacing           = 5;        // Spacing between buttons

input color  PanelBackgroundColor = clrDimGray;     // Panel background color
input color  PanelHeaderColor     = clrBlueViolet;  // Panel header text color

input color  ButtonBgColor        = clrBlack;       // Button background color
input color  ButtonTextColor      = clrBlueViolet;  // Button text color

input color  AnalysisTextColor    = clrLime;        // Analysis result text color

//---- Button Names and Texts (7 analysis options) --------------------
string buttonNames[7] =
  {
   "btnShortTerm",
   "btnScalping",
   "btnSwing",
   "btnTrend",
   "btnMTFTrend",
   "btnShortScalper",
   "btnLongTerm"
  };

string buttonTexts[7] =
  {
   "Short Term Focus\n(M1, M5, M15)",
   "Scalping/Intraday\n(M5, M15, H1)",
   "Swing Trading\n(M15, H1, H4)",
   "Trend Trading\n(H1, H4, D1)",
   "MTF Trend Confirm\n(H1, H4, W1)",
   "Short Scalper/Mid Trend\n(M5, H1, D1)",
   "Long Term Trend\n(H1, D1, W1)"
  };

// Global object names for panel header and analysis label
#define PANEL_BG "PanelBG"
#define PANEL_HEADER "PanelHeader"
#define ANALYSIS_LABEL "AnalysisResult"

//--- Helper function declarations
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift);
string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3);

//------------------------------------------------------------------------------
// CTrendLoomPanel class - A modern, modular panel for TrendLoom EA
//------------------------------------------------------------------------------
class CTrendLoomPanel : public CAppDialog
  {
private:
   CLabel            m_lblHeader;
   CPanel            m_panelMain;
   CButton           m_btnShortTerm;
   CButton           m_btnScalping;
   CButton           m_btnSwing;
   CButton           m_btnTrend;
   CButton           m_btnMTFTrend;
   CButton           m_btnShortScalper;
   CButton           m_btnLongTerm;
   CLabel            m_lblResults;

public:
   // Create the TrendLoom Panel dialog
   bool              CreateTrendPanel(const long chart, const string name, const int x1, const int y1, const int x2, const int y2)
     {
      if(!CAppDialog::Create(chart, name, 0, x1, y1, x2, y2))
        {
         Print("Failed to create TrendLoom dialog.");
         return false;
        }
      // Create header label
      if(!m_lblHeader.Create(0, "TrendLoomHeader", 0, 10, 10, x2 - x1 - 20, 30))
        {
         Print("Failed to create header label.");
         return false;
        }
      m_lblHeader.Text("TrendLoom EA");
      m_lblHeader.Color(PanelHeaderColor);
      m_lblHeader.FontSize(14);
      m_lblHeader.Font("Segoe UI");
      Add(m_lblHeader);
      // Set header text alignment to center using ObjectSetInteger
      if(!ObjectSetInteger(0L, m_lblHeader.Name(), OBJPROP_ALIGN, (long)ALIGN_CENTER))
         Print("Failed to set header alignment");

      // Create main panel background
      int panelBottom = 50 + (btnHeight + btnSpacing) * 7 + btnSpacing;
      if(!m_panelMain.Create(0, "TrendLoomPanel", 0, 10, 50, x2 - x1 - 10, panelBottom))
        {
         Print("Failed to create main panel.");
         return false;
        }
      m_panelMain.Color(PanelBackgroundColor);
      m_panelMain.BorderType(BORDER_RAISED);
      m_panelMain.ColorBorder(clrSilver);
      Add(m_panelMain);

      // Starting coordinates for buttons
      int btnX = 20; // relative to dialog
      int btnY = 60;
      int buttonWidth = btnWidth;
      int buttonHeight = btnHeight;

      // Create each button with a modern look
      if(!m_btnShortTerm.Create(0, buttonNames[0], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnShortTerm.Text(buttonTexts[0]);
      m_btnShortTerm.Font("Segoe UI");
      m_btnShortTerm.FontSize(12);
      m_btnShortTerm.Color(ButtonBgColor);
      Add(m_btnShortTerm);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnScalping.Create(0, buttonNames[1], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnScalping.Text(buttonTexts[1]);
      m_btnScalping.Font("Segoe UI");
      m_btnScalping.FontSize(12);
      m_btnScalping.Color(ButtonBgColor);
      Add(m_btnScalping);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnSwing.Create(0, buttonNames[2], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnSwing.Text(buttonTexts[2]);
      m_btnSwing.Font("Segoe UI");
      m_btnSwing.FontSize(12);
      m_btnSwing.Color(ButtonBgColor);
      Add(m_btnSwing);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnTrend.Create(0, buttonNames[3], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnTrend.Text(buttonTexts[3]);
      m_btnTrend.Font("Segoe UI");
      m_btnTrend.FontSize(12);
      m_btnTrend.Color(ButtonBgColor);
      Add(m_btnTrend);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnMTFTrend.Create(0, buttonNames[4], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnMTFTrend.Text(buttonTexts[4]);
      m_btnMTFTrend.Font("Segoe UI");
      m_btnMTFTrend.FontSize(12);
      m_btnMTFTrend.Color(ButtonBgColor);
      Add(m_btnMTFTrend);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnShortScalper.Create(0, buttonNames[5], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnShortScalper.Text(buttonTexts[5]);
      m_btnShortScalper.Font("Segoe UI");
      m_btnShortScalper.FontSize(12);
      m_btnShortScalper.Color(ButtonBgColor);
      Add(m_btnShortScalper);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnLongTerm.Create(0, buttonNames[6], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnLongTerm.Text(buttonTexts[6]);
      m_btnLongTerm.Font("Segoe UI");
      m_btnLongTerm.FontSize(12);
      m_btnLongTerm.Color(ButtonBgColor);
      Add(m_btnLongTerm);
      btnY += buttonHeight + btnSpacing;

      // Create results label below the buttons
      if(!m_lblResults.Create(0, "TrendResults", 0, btnX, btnY, btnX + buttonWidth, btnY + 30))
         return false;
      m_lblResults.Text("Analysis Result: [Waiting for Input]");
      m_lblResults.Font("Segoe UI");
      m_lblResults.FontSize(12);
      m_lblResults.Color(AnalysisTextColor);
      Add(m_lblResults);
      // Set results text alignment to left using ObjectSetInteger
      if(!ObjectSetInteger(0L, m_lblResults.Name(), OBJPROP_ALIGN, (long)ALIGN_LEFT))
         Print("Failed to set results alignment");

      Show();
      return true;
     }

   // Process events (button clicks)
   bool              OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
     {
      if(sparam == "btnShortTerm")
        {
         string res = AnalyzeTimeframes(PERIOD_M1, PERIOD_M5, PERIOD_M15);
         string out = "Short Term Focus: " + res;
         UpdateResults(out);
         Alert(out);
         return true;
        }
      else
         if(sparam == "btnScalping")
           {
            string res = AnalyzeTimeframes(PERIOD_M5, PERIOD_M15, PERIOD_H1);
            string out = "Scalping/Intraday: " + res;
            UpdateResults(out);
            Alert(out);
            return true;
           }
         else
            if(sparam == "btnSwing")
              {
               string res = AnalyzeTimeframes(PERIOD_M15, PERIOD_H1, PERIOD_H4);
               string out = "Swing Trading: " + res;
               UpdateResults(out);
               Alert(out);
               return true;
              }
            else
               if(sparam == "btnTrend")
                 {
                  string res = AnalyzeTimeframes(PERIOD_H1, PERIOD_H4, PERIOD_D1);
                  string out = "Trend Trading: " + res;
                  UpdateResults(out);
                  Alert(out);
                  return true;
                 }
               else
                  if(sparam == "btnMTFTrend")
                    {
                     string res = AnalyzeTimeframes(PERIOD_H1, PERIOD_H4, PERIOD_W1);
                     string out = "MTF Trend Confirm: " + res;
                     UpdateResults(out);
                     Alert(out);
                     return true;
                    }
                  else
                     if(sparam == "btnShortScalper")
                       {
                        string res = AnalyzeTimeframes(PERIOD_M5, PERIOD_H1, PERIOD_D1);
                        string out = "Short Scalper/Mid Trend: " + res;
                        UpdateResults(out);
                        Alert(out);
                        return true;
                       }
                     else
                        if(sparam == "btnLongTerm")
                          {
                           string res = AnalyzeTimeframes(PERIOD_H1, PERIOD_D1, PERIOD_W1);
                           string out = "Long Term Trend: " + res;
                           UpdateResults(out);
                           Alert(out);
                           return true;
                          }
      return false;
     }

   bool              ChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
     {
      return OnEvent(id, lparam, dparam, sparam);
     }

   // Update the results label and refresh the chart
   void              UpdateResults(const string &result)
     {
      m_lblResults.Text("Analysis Result: " + result);
      ChartRedraw();
     }
  };

// Global instance of the TrendLoom Panel
CTrendLoomPanel TrendPanel;

//------------------------------------------------------------------------------
// Helper functions (core analysis logic)
//------------------------------------------------------------------------------
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
  {
   int handle = iMA(symbol, timeframe, period, 0, MODE_SMA, PRICE_CLOSE);
   if(handle == INVALID_HANDLE)
     {
      Print("Failed to create iMA handle for timeframe ", timeframe);
      return 0.0;
     }
   double sma[];  // dynamic array to store SMA values
   if(CopyBuffer(handle, 0, shift, 1, sma) <= 0)
     {
      Print("Failed to copy buffer for timeframe ", timeframe);
      IndicatorRelease(handle);
      return 0.0;
     }
   double result = sma[0];
   IndicatorRelease(handle);
   return result;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3)
  {
   int period = 50;
   int shift  = 1; // last completed candle

   double price1 = iClose(_Symbol, tf1, shift);
   double sma1   = GetSMA(_Symbol, tf1, period, shift);
   int signal1   = (price1 > sma1) ? 1 : (price1 < sma1 ? -1 : 0);

   double price2 = iClose(_Symbol, tf2, shift);
   double sma2   = GetSMA(_Symbol, tf2, period, shift);
   int signal2   = (price2 > sma2) ? 1 : (price2 < sma2 ? -1 : 0);

   double price3 = iClose(_Symbol, tf3, shift);
   double sma3   = GetSMA(_Symbol, tf3, period, shift);
   int signal3   = (price3 > sma3) ? 1 : (price3 < sma3 ? -1 : 0);

   int sum = signal1 + signal2 + signal3;
   if(sum >= 2)
      return "BUY";
   else
      if(sum <= -2)
         return "SELL";
      else
         return "NEUTRAL";
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(!TrendPanel.CreateTrendPanel(0, "TrendLoom Panel", PanelX, PanelY, PanelX + PanelWidth + 20, PanelY + 400))
     {
      Print("Failed to create TrendLoom Panel.");
      return INIT_FAILED;
     }
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   TrendPanel.Destroy(reason);
  }

//+------------------------------------------------------------------+
//| Chart Event Handler                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   TrendPanel.ChartEvent(id, lparam, dparam, sparam);
  }
//+------------------------------------------------------------------+


Результаты

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

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

Рис 4. Тестирование при Volatility 75 (1 сек)

Ниже представлена диаграмма, иллюстрирующая поведение рынка после совершения сделки на основе полученного сигнала. Здесь показано продолжение сделки, показанной на GIF-анимации выше. Я использовал таймфрейм M1, чтобы представить более широкий обзор сделок.

Рис. 5. Тестирование на V 75 (1 сек)


Заключение

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

Дата Название инструмента  Описание Версия  Обновления  Примечания
01/10/24 Chart Projector Скрипт для наложения эффекта призрака на движение цены за предыдущий день. 1.0 Первоначальная версия Инструмент номер 1
18/11/24 Analytical Comment Предоставляет информацию за предыдущий день в табличном формате, а также прогнозирует будущее направление рынка. 1.0 Первоначальная версия Инструмент номер 2
27/11/24 Analytics Master Регулярное обновление рыночных показателей каждые два часа  1.01 Вторая версия Инструмент номер 3
02/12/24 Analytics Forecaster  Регулярное обновление рыночных показателей каждые два часа с интеграцией с Telegram 1.1 Третья версия Инструмент номер 4
09/12/24 Volatility Navigator Советник анализирует рыночные условия с помощью полос Боллинджера, RSI и ATR. 1.0 Первоначальная версия Инструмент номер 5
19/12/24 Mean Reversion Signal Reaper  Анализирует рынок и генерирует сигналы, используя стратегию возврата к среднему  1.0  Первоначальная версия  Инструмент номер 6 
9/01/25  Signal Pulse  Анализирует несколько таймфреймов 1.0  Первоначальная версия  Инструмент номер 7 
17/01/25  Metrics Board  Панель с кнопками для анализа  1.0  Первоначальная версия Инструмент номер 8 
21/01/25 External Flow Аналитика с помощью внешних библиотек 1.0  Первоначальная версия Инструмент номер 9 
27/01/25 VWAP Взвешенная по объему средняя цена   1.3  Первоначальная версия  Инструмент номер 10 
02/02/25  Heikin Ashi  Сглаживание тренда и идентификация сигналов разворота  1.0  Первоначальная версия  Инструмент номер 11
04/02/25  FibVWAP  Генерация сигнала с помощью анализа Python  1.0  Первоначальная версия  Инструмент номер 12
14/02/25  RSI DIVERGENCE  Дивергенция цены и RSI  1.0  Первоначальная версия  Инструмент номер 13 
17/02/25  Parabolic Stop and Reverse (PSAR)  Автоматизация стратегии PSAR 1.0 Первоначальная версия  Инструмент номер 14
20/02/25  Скрипт Quarters Drawer  Нанесение уровней четвертей на график  1.0  Первоначальная версия  Инструмент номер 15 
27/02/25  Intrusion Detector Обнаружение и оповещение о достижении ценой уровней четвертей 1.0   Первоначальная версия Инструмент номер 16 
27/02/25  TrendLoom Tool  Панель мультитаймфреймового анализа 1.0 Первоначальная версия Инструмент номер 17

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

Прикрепленные файлы |
TrendLoom_EA.mq5 (26.63 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
linfo2
linfo2 | 21 мар. 2025 в 03:01
Ух ты, фантастика, я вижу, что в это вложено много сил, спасибо, что поделились своим подходом и кодом.
Christian Benjamin
Christian Benjamin | 21 мар. 2025 в 11:21
linfo2 #:
Вау, фантастика, я вижу, что в это вложено много усилий, спасибо, что поделились своим подходом и кодом.
Не за что. Спасибо и вам за то, что обратились к нам👏.
gardee005
gardee005 | 25 мар. 2025 в 16:49
очень нравится ваша работа, слежу за ней. хороший спектр идей, с реализацией. спасибо!
Алгоритм оптимизации одуванчика — Dandelion Optimizer (DO) Алгоритм оптимизации одуванчика — Dandelion Optimizer (DO)
Алгоритм оптимизации одуванчика DO превращает простой полёт семени по ветру в стратегию математического поиска. Три фазы - вихревой подъём, дрейф к центру популяции и приземление по траектории Леви - формируют изящную метафору, которая на практике показывает интересные результаты.
Разработка инструментария для анализа движения цен (Часть 15): Введение в теорию четвертей (II) — советник Intrusion Detector Разработка инструментария для анализа движения цен (Часть 15): Введение в теорию четвертей (II) — советник Intrusion Detector
В нашей предыдущей статье мы представили простой скрипт Quarters Drawer. Продолжая тему, создадим советник для отслеживания четвертей и предоставления информации о потенциальной реакции рынка на этих уровнях. В статье описана разработка инструмента для обнаружения необходимых зон.
Реализация механизма безубыточности в MQL5 (Часть 1): Базовый класс и режим безубытка по фиксированным пунктам Реализация механизма безубыточности в MQL5 (Часть 1): Базовый класс и режим безубытка по фиксированным пунктам
В данной статье рассматривается применение механизма безубыточности (breakeven) в автоматизированных стратегиях на языке MQL5. Начнем с простого объяснения, что такое режим безубытка, как он реализуется и каковы его возможные вариации. Далее эта функциональность интегрируется в советника Order Blocks, созданного нами в последней статье об управлении рисками. Для оценки эффективности проведем два бэктеста при определенных условиях: один с применением механизма безубыточности и другой — без.
Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (EEMFlow) Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (EEMFlow)
Статья знакомит с архитектурой фреймворка EEMFlow, ориентированного на работу с событийными потоками данных. Особое внимание уделяется адаптивным и многоуровневым модулям, которые обеспечивают гибкую обработку как глобальных, так и локальных изменений. Архитектура фреймворка позволяет сохранять ключевую информацию, минимизировать влияние шума и эффективно формировать признаки для дальнейшего анализа, делая EEMFlow перспективным инструментом для прогнозирования динамики финансовых рынков.