Новая статья: Рецепты MQL5 - обработка пользовательских событий графика

 
На сайте mql5.com опубликована статья Рецепты MQL5 - обработка пользовательских событий графика:

В данной статье рассматриваются аспекты проектирования и разработки системы пользовательских событий графика в среде MQL5. Предлагается пример подхода для классификации событий. Приводится программный код событийного класса и класса-обработчика пользовательских событий.

Введение

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

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


1. Пользовательское событие графика

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

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

Разработчик предлагает для обработки всех событий графика 1 перечисление - ENUM_CHART_EVENT.

Согласно Документации, существует 65536 идентификаторов пользовательских событий. Первый и последний идентификаторы пользовательских событий задаются явными значениями CHARTEVENT_CUSTOM и CHARTEVENT_CUSTOM_LAST, что в численном выражении равно 1000 и 66534 соответственно (рис.1).

Рис.1 Первый и последний идентификаторы пользовательских событий

Рис.1 Первый и последний идентификаторы пользовательских событий

Автор: Dennis Kirichenko

 
Спасибо за статью!

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

Есть ли возможность запихнуть OnChartEvent в сам класс? Хотел бы создавать различные классы. И прописывать в каждом из них свой OnChartEvent. Как только событие происходит, так сразу вызывается во всех объявленных классах свой OnChartEvent.

Сейчас же (в статье так) приходится при создании нового класса запихивать его обработку в глобальную OnChartEvent. Какое-то костыльное решение. Я ни разу не программер, но неужели событийная модель и на других ООП-языках выглядит так же криво. Ведь логично OnChartEvent прописывать внутри класса, как виртуальный метод.
 
kbw74614:
...
Сейчас же (в статье так) приходится при создании нового класса запихивать его обработку в глобальную OnChartEvent. Какое-то костыльное решение. Я ни разу не программер, но неужели событийная модель и на других ООП-языках выглядит так же криво. Ведь логично OnChartEvent прописывать внутри класса, как виртуальный метод.
Посмотрите пример в директории Metatrader 4\MQL4\Indicators\Examples\SimplePanel.
 
tol64:
Посмотрите пример в директории Metatrader 4\MQL4\Indicators\Examples\SimplePanel.
Вот кусок кода оттуда:
//+------------------------------------------------------------------+
//| Global Variables |
//+------------------------------------------------------------------+
CPanelDialog ExtDialog;
// ............
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
ExtDialog.ChartEvent(id,lparam,dparam,sparam);
}

Вы хотели показать тот самый костыль, о котором написал выше? Нет никакого внутриклассового ChartEvent, который бы вызывался при наступлении события без прописывания его вызова в глобальном OnChartEvent.

Стоит мне в этом коде создать еще один объект типа CPanelDialog, так для корректной работы потребуется писать аналогичную строчку вызова ChartEvent в OnChartEvent. И так с каждым классом. В других языках тоже так через задницу события реализуются? Я просто не в курсе еще пока (не гуглил).
 

Пока только на уровне идеи, не проверял, получится или не:

Делаем самый базовый класс с виртуальным методом ChartEvent, и абсолютно все классы делаем его потомками. Создавая новый класс, добавляем автоматом его в какой-нибудь массив типа базового класса. И в OnChartEvent в цикле вызываем ChartEvent всех созданных классов в проге. Надо проверить, будет ли работать. В ООП сам - ноль без палочки. Программеры, стукните меня в нужное место, чтобы фигню не городил!

 
kbw74614:

Делаем самый базовый класс с виртуальным методом ChartEvent, и абсолютно все классы делаем его потомками. Создавая новый класс, добавляем автоматом его в какой-нибудь массив типа базового класса. И в OnChartEvent в цикле вызываем ChartEvent всех созданных классов в проге. Надо проверить, будет ли работать. В ООП сам - ноль без палочки. Программеры, стукните меня в нужное место, чтобы фигню не городил!

По-быстрому проверил, работает:

// Скрипт проверки идеи универсального обработчика событий: https://forum.mql4.com/ru/65134#993001
#property strict

// Базовый класс всех классов для событий
class EVENTBASE
{
private:
  int GetAllEventClassesSize( void )
  {
    const int Size = ArraySize(AllEventClasses);
    int Pos = 0;

    for (int i = 0; i < Size; i++)
    {
      AllEventClasses[Pos] = AllEventClasses[i];

      if ((CheckPointer(AllEventClasses[i]) != POINTER_INVALID) && (AllEventClasses[i] != GetPointer(this)))
        Pos++;
    }

    return(Pos);
  }

public:
  EVENTBASE( void )
  {
    const int Pos = this.GetAllEventClassesSize();

    ArrayResize(AllEventClasses, Pos + 1);

    AllEventClasses[Pos] = GetPointer(this);
  }

  ~EVENTBASE( void )
  {
    ArrayResize(AllEventClasses, this.GetAllEventClassesSize());
  }

// Метод-обработчик события (входные параметры можно сделать под свою событийную модель)
// Например, сработал TakeProfit - событие. Или спред стал отрицательным - событие. И т.д.
// В примере входные параметры взяты для штатной событийной модели OnChartEvent
  virtual void Event( const int id, const long &lparam, const double &dparam, const string &sparam )
  {
    return;
  }
};

// Глобальный массив указателей на все классы
EVENTBASE* AllEventClasses[];

// Вызов обработчиков событий всех классов
void MyEvent( const int id, const long &lparam, const double &dparam, const string &sparam )
{
  const int Size = ArraySize(AllEventClasses);

  for (int i = 0; i < Size; i++)
    if (CheckPointer(AllEventClasses[i]) != POINTER_INVALID)
      AllEventClasses[i].Event(id, lparam, dparam, sparam);

  return;
}

// А теперь плодим классы-потомки для проверки работоспособности универсального обработчика событий

class CLASSTYPE1: EVENTBASE
{
public:
  virtual void Event( const int id, const long &lparam, const double &dparam, const string &sparam )
  {
    Print("CLASSTYPE1: Hello World!");

    return;
  }
};

class CLASSTYPE2: EVENTBASE
{
public:
  virtual void Event( const int id, const long &lparam, const double &dparam, const string &sparam )
  {
    Print("CLASSTYPE2: Hello World!");

    return;
  }
};

// Сама проверка работоспособности
void OnStart( void )
{
  const long lparam = 0;
  const double dparam = 0;
  const string sparam = "";

  CLASSTYPE1 ClassType1;
  CLASSTYPE2* ClassType2 = new CLASSTYPE2;

  MyEvent(0, lparam, dparam, sparam);

  if (CheckPointer(ClassType2) == POINTER_DYNAMIC)
    delete ClassType2;

  return;
}
Либо я способный ООП-новичек, либо для всех это итак очевидно... Научите красиво использовать мощь ООП. Как идиот - изобретаю велосипеды.