Español Português
preview
От начального до среднего уровня: Объекты (II)

От начального до среднего уровня: Объекты (II)

MetaTrader 5Примеры |
60 0
CODE X
CODE X

Введение

В предыдущей статье От начального до среднего уровня: Объекты (I) мы впервые познакомились с графическими объектами, присутствующими в MQL5, а также продемонстрировали, как можно размещать данные объекты на графике, манипулируя ими относительно простым, хотя и в целом не очень эффективным способом, поскольку нас не интересует создание приложений, а мы стремимся сделать код максимально простым и наглядным.

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

Но поскольку мы всё ещё в начале пути, есть несколько моментов, которые необходимо решить, прежде, чем мы сможем дать волю своему воображению и превратить MetaTrader 5 во что-то действительно интересное с точки зрения программиста. С точки зрения пользователя, то, что мы собираемся сделать, может показаться бессмысленным. Итак, как обычно, давайте начнём новую тему.


Первоначальные манипуляции

То, что описывалось в предыдущей статье, не обязательно следует рассматривать как первые манипуляции с объектами. Это происходит просто потому, что мы размещаем их на графике, и всё. Для выполнения любых других действий потребуется открыть диалоговое окно, где мы получим доступ к некоторым свойствам объекта и сможем ими управлять. Однако, когда мы занимаемся программированием, мы можем пойти гораздо дальше, и теоретически нет предела нашим возможностям, есть только наше воображение. Поскольку MQL5 был разработан с целью создания приложений для использования в MetaTrader 5, а платформа MetaTrader 5 предназначена для отображения графиков с котировками, возможности MQL5 по непосредственному применению действительно ограничены.

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

Возвращаясь к нашему главному вопросу, для выполнения манипуляций с объектами нам необходимо использовать механизмы ввода и взаимодействия с пользователем. Подобные механизмы предполагают использование клавиатуры и/или мыши, что позволяет обрабатывать информацию максимально простым способом. Однако, как вы, возможно, уже догадались, использование таких механизмов предполагает перехват событий, поскольку сама операционная система, будь то Windows, Linux или любая другая, по сути, использует события для связи между пользователем и любым приложением. Это уже было показано в статье От начального до среднего уровня: События (I) . Но там мы лишь поверхностно обсуждали подобные моменты. Здесь мы немного углубимся в эту тему.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_NameObj "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0);
09.     ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple);
10. 
11.     return INIT_SUCCEEDED;
12. };
13. //+------------------------------------------------------------------+
14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
15. {
16.     static double p = 0;
17. 
18.     ObjectMove(0, def_NameObj, 0, 0, p);
19. 
20.     if (prev_calculated > 0)
21.         p = price[prev_calculated - 1];
22. 
23.     return rates_total;
24. };
25. //+------------------------------------------------------------------+
26. void OnDeinit(const int reason)
27. {
28.     ObjectDelete(0, def_NameObj);
29.     ChartRedraw();
30. };
31. //+------------------------------------------------------------------+

Код 01

Итак, если вы изучали то, что мы показали в этих статьях, вы прекрасно поймете этот код, потому что на самом деле он довольно интересный и очень простой. Он интересен тем, что ему удаётся достичь. Может быть, вы смотрите на это и думаете: "Ну вот, а я не понимаю, почему вы считаете этот код интересным. И что ещё хуже, я не понимаю, зачем вы делаете подобные вещи в функции OnCalculate, которая показана в строке 14. Не могли бы вы объяснить, что здесь происходит?" Что ж, уважаемые читатели, если у вас есть сомнения по поводу того, как работает данный код, это связано с тем, что вы не применяли на практике показанное в этих статьях.

При выполнении этого кода будет получен результат, похожий на показанный в анимации ниже. 

Анимация 01

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

Что ж, уважаемый читатель, с кодом всё в порядке, и с платформой тоже всё в порядке. На самом деле, фиолетовая линия выполняет именно ту функцию, для которой была предназначена, а именно показывает, где находилась бы последняя котировка. Однако, если после получения котировки MetaTrader запускает новое событие Calculate, не изменив при этом цену новой котировки, фиолетовая линия совпадет с линией последней котировки. Именно это мы и видим в анимации 01. И причина кроется в 18-й строке. Итак, если взять последнюю цену и новую ценой котировки, то между этими значениями может возникнуть расхождение. В данном случае фиолетовая линия всегда будет отставать от ценовой линии. Однако, если цена совпадет с предыдущей, фиолетовая линия будет совпадать с линией последней цены.

Весьма познавательно, не правда ли? Многие трейдеры и программисты не разделяют эту идею, которую вы только что узнали, и считают, что всякий раз, когда срабатывает событие Calculate, это происходит из-за изменения цены. Однако вы только что увидели, что так происходит не всегда. Поэтому очень важно хорошо разбираться в оптимизации кода, чтобы не допустить существенного снижения производительности приложения MetaTrader 5.

Итак, после того, как мы поработали с чем-то простым и, казалось бы, тривиальным, давайте поэкспериментируем с каким-нибудь событием. В данном случае мы начнём с самого простого, то есть с обработки событий клавиатуры. Я говорю, что события клавиатуры проще, потому что нам не нужно их включать или отключать. При каждом нажатии клавиши MetaTrader 5 автоматически запускает событие. Если какое-либо из наших приложений, представленных на графике, имеет обработчик события ChartEvent, мы сможем соответствующим образом обрабатывать нажатие клавиши и реагировать на него. Чтобы продемонстрировать эту первую систему, мы создадим новый код, который реагирует на события клавиатуры — простой для понимания и с довольно интересной целью для новичков. Этот код показан чуть ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.    return INIT_SUCCEEDED;
07. };
08. //+------------------------------------------------------------------+
09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
10. {
11.    return rates_total;
12. };
13. //+------------------------------------------------------------------+
14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
15. {
16.    switch(id)
17.    {
18.       case CHARTEVENT_KEYDOWN:
19.          Print(lparam);
20.          break;
21.    }
22. };
23. //+------------------------------------------------------------------+

Код 02

Вы когда-нибудь задумывались, как программист определяет, какая клавиша была нажата? Как он определяет, была ли нажата клавиша со стрелкой или клавиша типа Home или End? Это похоже на магию, если представить себе буквенно-цифровые клавиши. Но что насчет остальных клавиш? Что ж, на самом деле программист этого НЕ ЗНАЕТ. Но он точно знает, как найти эту информацию. Для этого используется код, аналогичный приведенному выше. При выполнении данного кода можно узнать, какой числовой код будет сгенерирован при нажатии определенной клавиши.

В действительности, при нажатии клавиши будет сгенерирован другой код, отличающийся от того, который мы увидим здесь. Однако, поскольку операционная система, скажем так, "переводит" значение нажатой клавиши, то при запуске приложения, подобного показанному в коде 02, MetaTrader 5 укажет нам, какое значение следует использовать. На приведенной ниже анимации можно увидеть результат.

Анимация 02

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

Хорошо, но как мы можем это использовать? Кажется, заниматься этим несколько бессмысленно. Итак, давайте посмотрим, как можно использовать информацию, полученную с помощью кода 02. Для этого мы изменим код 01, чтобы теперь сделать кое-что другое. Данное изменение можно увидеть чуть ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_NameObj     "Demo"
05. #define def_KEY_UP      38
06. #define def_KEY_DOWN    40
07. //+------------------------------------------------------------------+
08. int OnInit()
09. {
10.    ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0);
11.    ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple);
12. 
13.    return INIT_SUCCEEDED;
14. };
15. //+------------------------------------------------------------------+
16. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
17. {
18.    return rates_total;
19. };
20. //+------------------------------------------------------------------+
21. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
22. {
23.    static int p = 0;
24.    MqlRates rate[1];
25. 
26.    switch(id)
27.    {
28.       case CHARTEVENT_KEYDOWN:
29.          switch ((int)lparam)
30.          {
31.             case def_KEY_DOWN:
32.                p = (p < Bars(_Symbol, _Period) ? p + 1 : p);
33.                break;
34.             case def_KEY_UP:
35.                p = (p > 0 ? p - 1 : p);
36.                break;
37.             default:
38.                return;
39.          }
40.          Comment(StringFormat("Current bar analyzed: %d", p));
41.          CopyRates(_Symbol, _Period, p, rate.Size(), rate);
42.          ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close);
43.          break;
44.    }
45.    ChartRedraw();
46. };
47. //+------------------------------------------------------------------+
48. void OnDeinit(const int reason)
49. {
50.    Comment("");
51.    ObjectDelete(0, def_NameObj);
52.    ChartRedraw();
53. };
54. //+------------------------------------------------------------------+

Код 03

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

Итак, в строке 23 мы запускаем локальный счетчик. Это покажет нам, на сколько баров мы смещаемся относительно последнего бара на графике. Чтобы это было понятно, мы использовали строку 40, чтобы вывести определенную информацию непосредственно на график. Код между строками 29 и 39 уже служит для обеспечения того, чтобы захватывались и обрабатывались только ранее определенные клавиши. В строке 41 теперь будет выполнен поиск котировок для бара, указанного в данный момент счетчиком p. Прошу заметить, что мы ищем только один бар. Строка 42 теперь переместит объект на графике. Таким образом, при выполнении данного кода мы получаем то, что показано в анимации ниже.

Анимация 03

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

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


Более одного объекта на одном графике

Есть одна проблема, которая ставит многих новичков в тупик: они совершенно не понимают, как работает код, но при этом проявляют к нему интерес. И когда мы решаем объединить различные коды или даже использовать разные коды в одном и том же графике, цель каждого из которых состоит в создании какого-либо объекта на графике, и в итоге возникает проблема. И я ещё мягко выражаюсь, когда говорю, что пользователь не добавлял объекты на график самостоятельно. Я учитываю лишь тот факт, что он пытается использовать код, который делает это за него.

Проблема в том, что для упрощения работы как разработчиков, так и самой платформы MetaTrader 5, каждый объект на графике ДОЛЖЕН иметь уникальное имя. Если мы попытаемся создать объект с именем, которое уже существует на графике, это вызовет проблемы. Это происходит, потому что при попытке манипулировать чем-то, чего на самом деле нет на графике, мы, тем не менее, будем манипулировать чем-то другим, присутствующим на графике. Это вызывает огромную путаницу в сознании начинающего программиста или даже менее опытного пользователя.

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_NameObj     "Demo"
05. #define def_KEY_UP      38
06. #define def_KEY_DOWN    40
07. //+------------------------------------------------------------------+
08. int OnInit()
09. {
10.    ObjectCreate(0, def_NameObj, OBJ_VLINE, 0, 0, 0);
11.    ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrRoyalBlue);
12.    ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0);
13.    ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple);
14. 
15.    return INIT_SUCCEEDED;
16. };
17. //+------------------------------------------------------------------+
18. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
19. {
20.    return rates_total;
21. };
22. //+------------------------------------------------------------------+
23. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
24. {
25.    static int p = 0;
26.    MqlRates rate[1];
27. 
28.    switch(id)
29.    {
30.       case CHARTEVENT_KEYDOWN:
31.          switch ((int)lparam)
32.          {
33.             case def_KEY_DOWN:
34.                p = (p < Bars(_Symbol, _Period) ? p + 1 : p);
35.                break;
36.             case def_KEY_UP:
37.                p = (p > 0 ? p - 1 : p);
38.                break;
39.             default:
40.                return;
41.          }
42.          Comment(StringFormat("Current bar analyzed: %d", p));
43.          CopyRates(_Symbol, _Period, p, rate.Size(), rate);
44.          ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close);
45.          ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close);
46.          break;
47.    }
48.    ChartRedraw();
49. };
50. //+------------------------------------------------------------------+
51. void OnDeinit(const int reason)
52. {
53.    Comment("");
54.    ObjectDelete(0, def_NameObj);
55.    ChartRedraw();
56. };
57. //+------------------------------------------------------------------+

Код 04

Здесь мы показываем довольно грубую, но очень распространенную ошибку новичков в области программирования, поскольку их цель, по-видимому, состоит в том, чтобы создать крест на графике. Однако при выполнении данного кода результат выглядит так:

Анимация 04

Хм, странно. В обработчике события OnInit мы создаём два объекта: один представляет собой горизонтальную линию, а другой — вертикальную. Почему же тогда отображается только вертикальная линия? Что ж, дело не только в этом. Если присмотреться, то можно заметить, что вертикальная линия не была изначально предназначена для цвета, показанного в анимации 04. Так что же на самом деле здесь происходит?

Проблема в том, что, несмотря на то, что мы указываем на необходимость создания двух объектов, одного в строке 10, а другой в строке 12, но создается только объект, объявленный в строке 10. Это происходит так, потому что в момент выполнения строки 12 MetaTrader 5 сообщит, что на графике уже есть объект с тем же именем, определенный в строке 12. На самом деле, его ещё нет на графике, но он находится в очереди исполнения, готовый к размещению на графике в кратчайшие сроки.

В любом случае, создание объекта, объявленного в строке 12, будет отклонено. Поскольку большая часть кода относительно проста, многие люди в итоге не обращают внимания на подобные детали, и, что ещё хуже, из-за отсутствия интереса к изучению документации. Но главным образом это происходит из-за недостатка опыта: начинающий программист в итоге считает, что ошибка может быть где-то в другом месте. Но когда это происходит с пользователем, который пытается использовать два или более приложений одновременно на одном графике, и при этом имена объектов совпадают, ситуация становится действительно запутанной. Хотя подобные случаи крайне редки, можно столкнуться с такой ситуацией.

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_KEY_UP          38
05. #define def_KEY_DOWN        40
06. //+----------------+
07. #define macro_NameObject  "Demo" + (string)ObjectsTotal(0)
08. //+------------------------------------------------------------------+
09. string  gl_Objs[2];
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.     ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0);
14.     ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue);
15.     ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0);
16.     ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple);
17. 
18.     return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
22. {
23.     return rates_total;
24. };
25. //+------------------------------------------------------------------+
26. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
27. {
28.     static int p = 0;
29.     MqlRates rate[1];
30. 
31.     switch(id)
32.     {
33.         case CHARTEVENT_KEYDOWN:
34.             switch ((int)lparam)
35.             {
36.                 case def_KEY_DOWN:
37.                     p = (p < Bars(_Symbol, _Period) ? p + 1 : p);
38.                     break;
39.                 case def_KEY_UP:
40.                     p = (p > 0 ? p - 1 : p);
41.                     break;
42.                 default:
43.                     return;
44.             }
45.             Comment(StringFormat("Current bar analyzed: %d", p));
46.             CopyRates(_Symbol, _Period, p, rate.Size(), rate);
47.             ObjectMove(0, gl_Objs[0], 0, rate[0].time, rate[0].close);
48.             ObjectMove(0, gl_Objs[1], 0, rate[0].time, rate[0].close);
49.             break;
50.     }
51.     ChartRedraw();
52. };
53. //+------------------------------------------------------------------+
54. void OnDeinit(const int reason)
55. {
56.     Comment("");
57.     for (uint c = 0; c < gl_Objs.Size(); c++)
58.         ObjectDelete(0, gl_Objs[c]);
59.     ChartRedraw();
60. };
61. //+------------------------------------------------------------------+

Код 05

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

Анимация 05

Прошу заметить, что результат совершенно отличается от того, что было показано в анимации 04. Однако важно отметить, что, если сравнить код 05 с кодом 04, то будет заметно, что мы практически не вносили изменений в код для исправления проблемы, которую создавал код 04, причем наиболее поразительным является объявление, которое видно в строке 09 кода 05. Но поскольку этого одного было бы недостаточно для решения всех проблем, потребовалось ещё одно изменение. Его можно увидеть в строке 07 кода 05.

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

Очень простая, но очень эффективная вещь. В остальном внесенные изменения очень легко понять любому, кто изучал статьи, поэтому нет необходимости вдаваться в подробности. Несмотря на всю эту простоту, не следует забывать о следующем: поскольку макрос всегда будет создавать уникальное имя, нам необходимо где-то сохранить имя уже созданного объекта. В данном случае имя сохраняется в массиве из строка 09. Без этого мы не смогли бы найти объект, который уже присутствовал на графике.

Прежде, чем закончить данную статью, хочу объяснить ещё кое-что, что мы будем очень часто использовать в нескольких будущих статьях. Однако, чтобы должным образом разделить вопросы, перейдем к новой теме.


Краткое имя

До этого момента все показанные коды являются практически как один единственный код, и, как правило, их следует использовать один за другим. Однако на практике и в реальных условиях всё не всегда так просто и приятно. Поэтому нам, программистам, необходимо принимать определенные меры предосторожности и знать, как делать те или иные вещи. Среди них необходимость уметь получать доступ к индикатору, отображаемому на графике. А причину для этого мы увидим позже, когда начнем разрабатывать некоторые, так сказать, более сложные вещи.

Как пользователь, вы, возможно, подумаете: "но я, конечно же, знаю, как получить доступ к индикатору, достаточно нажать CTRL + I, и откроется окно, в котором мне будет предоставлен доступ к индикаторам, представленным на графике. А если я захочу разместить его на графике, мне достаточно просто зайти в браузер, найти нужный индикатор, и всё. Затем я перетаскиваю его на график, и MetaTrader 5 сделает всё остальное. Всё просто".

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

А поскольку я хочу, чтобы вы начали привыкать мыслить как программисты, нам нужно немного больше поговорить о том, каким было бы видение программиста относительно того, что можно и что нельзя делать в MetaTrader 5. Нам также необходимо понять, как MetaTrader 5 распознает то, что мы вводим в код MQL5. Дело в том, что MQL5 был разработан как способ сообщить MetaTrader 5, что мы хотим сделать.

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

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

Отлично, значит, мы можем задать имя для нашего приложения при работе с индикаторами. Хорошо, а как это сделать? Это делается с помощью функции, присутствующей в стандартной библиотеке MQL5: IndicatorSetString. Хотя это может показаться банальным и даже ненужным, присвоение короткого имени нашему индикатору значительно упростит выполнение ряда задач. Но данная же функция полезна не только для определения имени нашего индикатора. Оно также служит и другим, ещё более благородным целям. Но сначала давайте посмотрим, как мы определяем внутреннее имя для нашего индикатора. Это делается с помощью чего-то подобного этому:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.     IndicatorSetString(INDICATOR_SHORTNAME, "Version Demo");
07. 
08.     return INIT_SUCCEEDED;
09. };
10. //+------------------------------------------------------------------+
11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
12. {
13.     return rates_total;
14. };
15. //+------------------------------------------------------------------+

Код 06

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

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


Заключительные идеи

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

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

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

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

Прикрепленные файлы |
Anexo.zip (3.7 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Торговые инструменты на MQL5 (Часть 20): Построение графиков на Canvas с использованием статистической корреляции и регрессионного анализа Торговые инструменты на MQL5 (Часть 20): Построение графиков на Canvas с использованием статистической корреляции и регрессионного анализа
В этой статье мы создаем графический инструмент на основе Canvas в MQL5 для статистического корреляционного и линейного регрессионного анализа между двумя символами с возможностью перетаскивания и изменения размера. Мы включили ALGLIB для регрессионных расчетов, динамические метки тиков, точки данных и панель статистики, отображающую наклон, пересечение, корреляцию и R-квадрат. Эта интерактивная визуализация помогает лучше понять суть парной торговли, поддерживая настраиваемые темы, границы и обновление новых баров в режиме реального времени
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Нейросети в трейдинге: Унифицированное смешивание признаков для торговых решений (Окончание) Нейросети в трейдинге: Унифицированное смешивание признаков для торговых решений (Окончание)
В статье представлена завершающая часть адаптации фреймворка UniMixer средствами MQL5, включая построение SiameseNorm и объекта верхнего уровня CNeuronUniMixerBlock. Описана полная цепочка обработки рыночных данных от токенизации и контекстного выделения до сценарного моделирования и смешивания признаков. Приведены результаты тестирования на исторических данных EURUSD, демонстрирующие умеренную прибыль.