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

От начального к среднему уровню: Объекты и подокна (I)

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

Введение

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

Итак, в ходе этой серии статей мы уже говорили о графических объектах и о том, как обрабатывать простые события, связанные с ними. Кроме того, мы кратко познакомились с индикаторами и рассмотрели, как подходить к некоторым конкретным ситуациям, связанным с их использованием. Также, мы уже заложили хорошую основу для работы с подокнами. Однако мы ещё не говорили о том, как использовать объекты внутри них. Многие могут подумать: «Насколько важно уметь это делать?» Разве это не то же самое, что уже объяснялось, когда речь шла о графических объектах? В общих чертах — да, это более или менее одно и то же. Однако — и именно это я сейчас собираюсь объяснить — существуют небольшие различия и нюансы между использованием объекта в главном окне и использованием того же объекта в подокне. Самое интересное начинается, когда мы начинаем использовать элементы, которые, по идее, должны находиться в главном окне, но в итоге будут использоваться в каком-либо подокне. В таком виде вы, возможно, а точнее, почти наверняка, всё ещё не понимаете, что я пытаюсь объяснить.

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


Объекты и подокна (I)

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_ObjName "Mini Chart"
10. //+------------------------------------------------------------------+
11. int OnInit(void)
12. {
13.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 123.");
14. 
15.     ObjectCreate(0, def_ObjName, OBJ_CHART, 0, 0, 0);
16.     ObjectSetInteger(0, def_ObjName, OBJPROP_XDISTANCE, 10);
17.     ObjectSetInteger(0, def_ObjName, OBJPROP_YDISTANCE, 10);
18.     ObjectSetInteger(0, def_ObjName, OBJPROP_XSIZE, 300);
19.     ObjectSetInteger(0, def_ObjName, OBJPROP_YSIZE, 300);
20.     ObjectSetInteger(0, def_ObjName, OBJPROP_PERIOD, PERIOD_M15);
21.     ObjectSetInteger(0, def_ObjName, OBJPROP_DATE_SCALE, false);
22.     ObjectSetInteger(0, def_ObjName, OBJPROP_PRICE_SCALE, false);
23.     ObjectSetString(0, def_ObjName, OBJPROP_SYMBOL, _Symbol);
24.     ObjectSetInteger(0, def_ObjName, OBJPROP_SELECTABLE, true); 
25.     ObjectSetInteger(0, def_ObjName, OBJPROP_SELECTED, false);
26.     
27.     return INIT_SUCCEEDED;
28. };
29. //+------------------------------------------------------------------+
30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
31. {
32.     return rates_total;
33. };
34. //+------------------------------------------------------------------+
35. void OnDeinit(const int reason)
36. {
37.     ObjectDelete(0, def_ObjName);
38.     ChartRedraw();
39. };
40. //+------------------------------------------------------------------+

Код 01

Этот код очень наглядно показывает, что мы хотим сделать. Тем не менее, это не лучший способ, хотя это то, что каждый может — или должен — понять. Поскольку в этой серии статей уже были рассмотрены некоторые аспекты объектно-ориентированного программирования, мы можем улучшить этот же код 01, чтобы получить тот же результат, но с помощью кода, который гораздо проще использовать. Таким образом, даже если мы упустим какую-то деталь, само объектно-ориентированное программирование поможет нам это исправить. Поэтому этот же код 01 можно переписать в виде кода, приведённого ниже.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. class C_MiniChart
10. {
11.     private :
12. //+----------------+
13.         string m_szNameObject;
14. //+----------------+
15.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
16. //+----------------+
17.     public  :
18. //+----------------+
19.         C_MiniChart(string szName)
20.             :m_szNameObject(szName)
21.         {
22.             ObjectCreate(0, m_szNameObject, OBJ_CHART, 0, 0, 0);
23.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
24.             SetInteger(OBJPROP_XDISTANCE, 10);
25.             SetInteger(OBJPROP_YDISTANCE, 10);
26.             SetInteger(OBJPROP_XSIZE, 300);
27.             SetInteger(OBJPROP_YSIZE, 300);
28.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
29.             SetInteger(OBJPROP_DATE_SCALE, false);
30.             SetInteger(OBJPROP_PRICE_SCALE, false);
31.             SetInteger(OBJPROP_SELECTABLE, true); 
32.             SetInteger(OBJPROP_SELECTED, false);
33.         }
34. //+----------------+
35.         ~C_MiniChart()
36.         {
37.             ObjectDelete(0, m_szNameObject);
38.             ChartRedraw();
39.         }
40. //+----------------+
41. }gl_MiniChart("Mini Chart");
42. //+------------------------------------------------------------------+
43. int OnInit(void)
44. {
45.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 123.");
46.    
47.     return INIT_SUCCEEDED;
48. };
49. //+------------------------------------------------------------------+
50. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
51. {
52.     return rates_total;
53. };
54. //+------------------------------------------------------------------+

Код 02

Хорошо, теперь у нас действительно есть код, который значительно проще изменять и объяснять. Кроме того, разумеется, он гораздо лучше подходит для обучения. Теперь я хочу, чтобы вы обратили внимание на некоторые детали этого кода 02. Обратите внимание, что в шестой строке мы указываем, что индикатор НЕ БУДЕТ ИСПОЛЬЗОВАТЬ ПОДОКНО. Крайне важно, чтобы вы очень хорошо поняли, что именно мы указываем в шестой строке. Второй момент, на который мы должны обратить внимание, связан со строкой 41. Здесь мы объявляем глобальную переменную, которая, в свою очередь, инициализирует класс. Однако, взглянув на содержимое OnInit, вы сразу можете подумать: «Хорошо, но этот код не даст того же результата, что и код 01». Именно здесь легко споткнуться и сильно ошибиться в выводах. Хотя этот код 02 и выглядит иначе, чем код 01, он даёт тот же результат и ведёт себя точно так же. Это связано именно с тем, что происходит в строке 41, а также с тем, что мы используем объектно-ориентированное программирование.

Если у вас возникнут какие-либо вопросы по этому поводу, ознакомьтесь со статьёй «От начального к среднему уровню: Классы (III)», где я показываю нечто похожее на то, что мы делаем здесь. Хорошо, но этот код 02 довольно скучен и малоинтересен, поскольку всё, что он делает, — создаёт график, символ которого совпадает с символом основного графика, но с периодом в пятнадцать минут. Это можно проверить, проанализировав строку 23, где мы указываем, какой символ будет отображаться на графике, и строку 28, где определяем графический период, используемый для построения баров в создаваемом небольшом окне. Однако, прежде чем углубляться в эту тему, мне нужно, чтобы вы хорошо поняли, что здесь происходит. И, чтобы не осталось никаких сомнений относительно того, как работает предыдущий код, ниже показана анимация.


Анимация 01

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

Анимация 02

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

Поскольку я не хочу сейчас излишне усложнять ситуацию, давайте поступим следующим образом: создадим ещё один индикатор и, опираясь на то, что было объяснено в недавно опубликованных статьях, сделаем так, чтобы этот новый индикатор помещал индикатор, созданный в коде 02, в подокно. Помните, что индикатор, реализованный в коде 02, НЕ ИСПОЛЬЗУЕТ ПОДОКНО. Он использует главное окно для взаимодействия с пользователем, то есть с оператором графика. Для этого мы воспользуемся приведённым ниже кодом.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 01.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
16.     
17.     return INIT_SUCCEEDED;
18. };
19. //+------------------------------------------------------------------+
20. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
21. {
22.     return rates_total;
23. };
24. //+------------------------------------------------------------------+

Код 03

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

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

Анимация 03

Что произошло? Я этого не понимаю. Итак, уважаемый читатель, давайте внесём небольшое изменение в код 02, чтобы всё стало гораздо понятнее. Это изменение показано в приведённом ниже фрагменте.
                                   .
                                   .
                                   .
26.            SetInteger(OBJPROP_XSIZE, 300);
27.            SetInteger(OBJPROP_YSIZE, 150);
                                   .
                                   .
                                   .

Фрагмент 01

Здесь мы изменяем размеры объекта OBJ_CHART, который будет создан. Затем мы удаляем исполняемый файл кода 02 и заново компилируем код 03. Таким образом, мы снова интегрируем исполняемый файл с новыми размерами, заданными для объекта OBJ_CHART. В результате при выполнении кода 03 мы получим то, что показано на следующем изображении.

Изображение 01

"Как странно. Я думал, что объект OBJ_CHART будет добавлен в подокно, поскольку мы размещаем индикатор из кода 02 в подокне. Однако, судя по всему, код интерпретировал это иначе и в итоге поместил объект OBJ_CHART в главное окно. Да уж, как странно. Но подождите минутку. Возможно, индикатор из кода 02 на самом деле не отправляется в подокно. Возможно, именно поэтому я вижу такой результат на изображении 01. Может быть, причина именно в этом?" На самом деле нет, уважаемый читатель. Если вы попросите MetaTrader 5 показать список индикаторов, присутствующих на графике, то увидите, что индикатор из кода 02 действительно размещён в подокне, как это хорошо видно на следующем изображении.

Изображение 02

Теперь я уже совсем ничего не понимаю. Я думал, что всё будет работать определённым образом, но это становится для меня слишком сложным. Спокойно, мой друг. Веселье только начинается. Дело в том, что вы предполагали, что всё пойдёт определённым образом, но только что убедились, что на практике не всегда всё происходит так, как мы ожидаем. Нам действительно нужно учиться и разбираться, прежде чем считать, что мы уже знаем, как всё устроено. Чтобы немного упростить задачу, давайте изменим код 02 так, чтобы объект OBJ_CHART отображался в подокне, без необходимости использовать код 03. Однако не забывайте об одном: цель здесь — использовать код 02 и код 03, чтобы получить результат, который мы вскоре увидим. Но, чтобы вы могли понять, что мы будем делать, пойдём шаг за шагом. Поэтому сосредоточьтесь на том, что будет объясняться.

Итак, после изменения кода 02 мы получаем код, приведённый ниже.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_separate_window
07. #property indicator_plots           0
08. #property indicator_height          150
09. //+------------------------------------------------------------------+
10. class C_MiniChart
11. {
12.     private :
13. //+----------------+
14.         string m_szNameObject;
15. //+----------------+
16.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
17. //+----------------+
18.     public  :
19. //+----------------+
20.         C_MiniChart(string szName)
21.             :m_szNameObject(szName)
22.         {
23.             ObjectCreate(0, m_szNameObject, OBJ_CHART, 1, 0, 0);
24.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
25.             SetInteger(OBJPROP_XDISTANCE, 10);
26.             SetInteger(OBJPROP_YDISTANCE, 0);
27.             SetInteger(OBJPROP_XSIZE, 300);
28.             SetInteger(OBJPROP_YSIZE, indicator_height);
29.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
30.             SetInteger(OBJPROP_DATE_SCALE, false);
31.             SetInteger(OBJPROP_PRICE_SCALE, false);
32.             SetInteger(OBJPROP_SELECTABLE, true); 
33.             SetInteger(OBJPROP_SELECTED, false);
34.         }
35. //+----------------+
36.         ~C_MiniChart()
37.         {
38.             ObjectDelete(0, m_szNameObject);
39.             ChartRedraw();
40.         }
41. //+----------------+
42. }gl_MiniChart("Mini Chart");
43. //+------------------------------------------------------------------+
44. int OnInit(void)
45. {
46.     IndicatorSetString(INDICATOR_SHORTNAME, "Test 123.");
47.    
48.     return INIT_SUCCEEDED;
49. };
50. //+------------------------------------------------------------------+
51. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
52. {
53.     return rates_total;
54. };
55. //+------------------------------------------------------------------+

Код 04

Теперь, выполнив этот код 04, мы получим результат, похожий на тот, что показан в следующей анимации.

Анимация 04

Хм, это выглядит довольно интересно. Действительно, уважаемый читатель, подобные вещи очень интересны, и по мере нашего продвижения дальше они будут становиться всё интереснее. Однако, как бы интересно это ни казалось, у кода 04 есть одна проблема. И это проблема, которая, если вы не очень хорошо понимаете, что делает MetaTrader 5, может доставить вам немало головной боли и неприятностей. Это может даже отбить у вас желание дальше изучать MQL5. Так что будьте внимательны. Если вы сравните этот код 04 с его предшественником, то есть с кодом 02, то заметите, что в него были внесены некоторые изменения. Однако — и в этом ключевой момент — изменение, которое нас действительно интересует, находится в строке 23: это четвёртый аргумент, передаваемый функции ObjectCreate. Я не понимаю: почему этот четвёртый аргумент должен вызывать ошибку в этом коде 04? Не могли бы вы объяснить это подробнее? На самом деле проще всего понять это, если вы сами попробуете этот код. Тем не менее я постараюсь объяснить это, чтобы вы могли составить представление о проблеме, связанной с этим четвёртым аргументом.

Поскольку в шестой строке этого кода 04 мы указываем, что потребуется подокно, MetaTrader 5 создаст его при необходимости, как мы уже видели в предыдущих статьях. Однако — и именно этот момент часто вызывает у многих недоумение — при создании объекта OBJ_CHART мы даём MetaTrader 5 указание создать его и разместить в подокне. Теперь я вас спрашиваю: в каком подокне? Что ж, можно сказать, что это должно быть подокно, созданное для размещения индикатора. Верно? Неверно, уважаемый читатель. Или, точнее, возможно, так и будет, при условии, что в момент добавления этого кода 04 на график на нём НЕ БУДЕТ НИКАКИХ ДРУГИХ ПОДОКОН.

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

Чтобы сделать это немного понятнее, посмотрите следующую анимацию.

Анимация 05

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

В статье «От начального к среднему уровню: Подокна (III)» я показал, как можно увидеть, какие индикаторы присутствуют на определённом графике. Поэтому, если у вас возникнут какие-либо вопросы по поводу того, что мы здесь рассмотрим, обратитесь к этой статье за более подробной информацией. Ну что ж, а теперь перейдём к самой интересной части статьи. И для этого мы начнём с кода, приведённого ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_separate_window
07. #property indicator_plots           0
08. #property indicator_height          150
09. //+------------------------------------------------------------------+
10. #define def_ShortName "Test 123."
11. //+------------------------------------------------------------------+
12. class C_MiniChart
13. {
14.     private :
15. //+----------------+
16.         string  m_szNameObject;
17.         int     m_Sub;
18. //+----------------+
19.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
20. //+----------------+
21.         int WhatSubWin(void)
22.         {
23.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
24.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
25.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
26.                         return c0;
27.             return INVALID_HANDLE;
28.         }
29. //+----------------+
30.     public  :
31. //+----------------+
32.         C_MiniChart(string szName)
33.         {
34.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
35.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
36.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
37.             SetInteger(OBJPROP_XDISTANCE, 10);
38.             SetInteger(OBJPROP_YDISTANCE, 0);
39.             SetInteger(OBJPROP_XSIZE, 300);
40.             SetInteger(OBJPROP_YSIZE, indicator_height);
41.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
42.             SetInteger(OBJPROP_DATE_SCALE, false);
43.             SetInteger(OBJPROP_PRICE_SCALE, false);
44.             SetInteger(OBJPROP_SELECTABLE, true); 
45.             SetInteger(OBJPROP_SELECTED, false);
46.         }
47. //+----------------+
48.         ~C_MiniChart()
49.         {
50.             ObjectDelete(0, m_szNameObject);
51.             ChartRedraw();
52.         }
53. //+----------------+
54. }*gl_MiniChart;
55. //+------------------------------------------------------------------+
56. int OnInit(void)
57. {
58.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
59. 
60.     gl_MiniChart = new C_MiniChart("Mini Chart");
61.    
62.     return INIT_SUCCEEDED;
63. };
64. //+------------------------------------------------------------------+
65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
66. {
67.     return rates_total;
68. };
69. //+------------------------------------------------------------------+
70. void OnDeinit(const int reason)
71. {
72.     delete gl_MiniChart;
73. };
74. //+------------------------------------------------------------------+

Код 05

На первый взгляд этот код 05 может показаться очень сложным и чрезвычайно продуманным. Тем не менее, ничто из того, что здесь делается, не является — и не должно быть — новым для тех, кто следит за этими статьями и изучает их. Всё, что здесь делается, мы уже видели раньше. Конечно, были внесены небольшие изменения, чтобы код было немного легче понять. Но главный вопрос напрямую связан с тем, что делается в строке 54. Обратите внимание, что, в отличие от того, как мы поступали раньше, теперь у нас уже нет той же степени свободы. И причина здесь довольно проста: нам нужно знать, где будет размещён индикатор. Что значит «где»? Конечно, я знаю, где он будет размещён. Я не это имею в виду, друг мой.

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

После выполнения 58-й строки мы уже можем определить, где на самом деле будет размещён индикатор. Исходя из этого, мы можем определить, в каком подокне следует разместить объект OBJ_CHART. Это делается в строке 34. Однако, поскольку теперь мы работаем уже не так, как раньше, нам нужно убедиться, что указатель, созданный в строке 60, уничтожен. По этой причине нам нужно добавить обработчик OnDeinit, который вы можете увидеть в строке 70.

Хорошо, но действительно ли этот код 05 работает так, как нам нужно? Чтобы ответить на этот вопрос, посмотрите следующую анимацию.

Анимация 06

"Ого, вот это действительно стало гораздо интереснее. Теперь действительно похоже, что мы движемся в правильном направлении". И действительно, уважаемый читатель, всё стало гораздо интереснее. Но в любой истории всегда есть своё «но». Даже в этом коде 05 есть ошибка. "Ну же, почему бы вам просто не показать код без каких-либо ошибок? Почему вы так со мной поступаете? Сначала вы меня обнадёживаете, а сразу после этого словно бьёте молотком по голове. Поступать так очень жестоко". Ну, если посмотреть на это с такой точки зрения, возможно, это и правда может показаться жестоким. Но, мне кажется, вы забываете, что пока ещё не являетесь программистом и читаете эти статьи именно для того, чтобы узнать, как делать всё наилучшим образом.

Итак, проблема этого кода 05 заключается в том, что, если попытаться добавить два экземпляра одного и того же индикатора на один график, новые экземпляры объекта OBJ_CHART созданы не будут. Причина в том, что мы указываем MetaTrader 5 использовать для объекта OBJ_CHART одно и то же имя. Хорошо, тогда исправить это очень просто: достаточно присвоить каждому новому экземпляру создаваемого объекта своё имя. Решение такой проблемы кажется слишком простым. По правде говоря, это вообще не похоже на проблему.

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

Когда индикатор из кода 05 добавляется на график, он ищет первый экземпляр индикатора, идентифицируемого именем, которое мы объявили в строке десять. Даже если вы присвоите объекту OBJ_CHART другое имя, когда функция из строки двадцать один вернёт управление, она сообщит нам, в каком подокне находится первый экземпляр индикатора, заданного именем из строки десять. По этой причине, если вы измените только имя объекта OBJ_CHART, то не сможете разместить новый объект в правильном подокне.

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

Итак, после этого замечания и после того, как мы показали, как работать с тем, что только что создали в коде 05, мы можем снова сосредоточиться на интеграции кода 02 с кодом 03. И не забывайте: в отличие от кода 02, в котором мы не указываем, что будем использовать подокно, принцип, применённый в коде 05, поможет нам избежать создания объекта и его размещения в неправильном подокне. Итак, чтобы исправить код 02 и впоследствии интегрировать его с кодом 03, нам необходимо изменить его, как показано ниже.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_ShortName "Test 123."
10. //+------------------------------------------------------------------+
11. class C_MiniChart
12. {
13.     private :
14. //+----------------+
15.         string  m_szNameObject;
16.         int     m_Sub;
17. //+----------------+
18.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
19. //+----------------+
20.         int WhatSubWin(void)
21.         {
22.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
23.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
24.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
25.                         return c0;
26.             return INVALID_HANDLE;
27.         }
28. //+----------------+
29.     public  :
30. //+----------------+
31.         C_MiniChart(string szName)
32.         {
33.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
34.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
35.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
36.             SetInteger(OBJPROP_XDISTANCE, 10);
37.             SetInteger(OBJPROP_YDISTANCE, 0);
38.             SetInteger(OBJPROP_XSIZE, 300);
39.             SetInteger(OBJPROP_YSIZE, 150);
40.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
41.             SetInteger(OBJPROP_DATE_SCALE, false);
42.             SetInteger(OBJPROP_PRICE_SCALE, false);
43.             SetInteger(OBJPROP_SELECTABLE, true); 
44.             SetInteger(OBJPROP_SELECTED, false);
45.         }
46. //+----------------+
47.         ~C_MiniChart()
48.         {
49.             ObjectDelete(0, m_szNameObject);
50.             ChartRedraw();
51.         }
52. //+----------------+
53. }*gl_MiniChart;
54. //+------------------------------------------------------------------+
55. int OnInit(void)
56. {
57.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
58. 
59.     gl_MiniChart = new C_MiniChart("Mini Chart");
60.    
61.     return INIT_SUCCEEDED;
62. };
63. //+------------------------------------------------------------------+
64. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
65. {
66.     return rates_total;
67. };
68. //+------------------------------------------------------------------+
69. void OnDeinit(const int reason)
70. {
71.     delete gl_MiniChart;
72. };
73. //+------------------------------------------------------------------+

Код 06

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

Чтобы вы точно получили приведённые здесь исходные коды и могли постепенно их изучать и тестировать, в приложенных файлах вы найдёте, помимо кода 03, и другие коды, которые помогут вам спокойно разобраться в каждой детали. Итак, ниже приведён код, который будет интегрировать исполняемый файл кода 06.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 05.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
16.     
17.     return INIT_SUCCEEDED;
18. };
19. //+------------------------------------------------------------------+
20. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
21. {
22.     return rates_total;
23. };
24. //+------------------------------------------------------------------+

Код 07

Обратите внимание, что единственное отличие между этим кодом 07 и кодом 03 заключается именно в девятой строке; всё остальное абсолютно одинаково. Однако это небольшое отличие приведёт к тому, что код 06 будет интегрирован в код 07. После компиляции кода 07 вы сможете запустить его на любом графике MetaTrader 5 и получите результат, похожий на тот, что показан в следующей анимации.

Анимация 07

Ого, честно говоря, получилось действительно впечатляюще. Просто замечательно. Теперь я действительно чувствую себя гораздо увереннее и с ещё большим интересом изучаю каждую деталь ваших статей. Однако я заметил, что код 06 отличается от кода 05 не только в шестой строке. Почему? Разве нельзя использовать код 05, изменив только шестую строку? Конечно, можно, уважаемый читатель. Однако, поскольку этот код 06 показывает, каким должен был быть код 02, чтобы объект OBJ_CHART оказался в правильном месте, я внес эти изменения исключительно для того, чтобы показать это обновление кода 02. Но ничто не мешает вам использовать код 05, изменив только шестую строку. В результате получится то, что мы пока еще не будем использовать. Тем не менее, возможно, у вас уже появилась какая-то идея, так что давайте посмотрим, как это можно сделать. В приложенных файлах вы также найдете эти коды, чтобы можно было их опробовать. Первое изменение приведет к коду, показанному ниже.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_height          150
08. #property indicator_plots           0
09. //+------------------------------------------------------------------+
10. #define def_ShortName "Test 123."
11. //+------------------------------------------------------------------+
12. class C_MiniChart
13. {
14.     private :
15. //+----------------+
16.         string  m_szNameObject;
17.         int     m_Sub;
18. //+----------------+
19.         void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); }
20. //+----------------+
21.         int WhatSubWin(void)
22.         {
23.             for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++)
24.                 for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--)
25.                     if (ChartIndicatorName(0, c0, c1) == def_ShortName)
26.                         return c0;
27.             return INVALID_HANDLE;
28.         }
29. //+----------------+
30.     public  :
31. //+----------------+
32.         C_MiniChart(string szName)
33.         {
34.             if ((m_Sub = WhatSubWin()) == INVALID_HANDLE) return;
35.             ObjectCreate(0, m_szNameObject = szName, OBJ_CHART, m_Sub, 0, 0);
36.             ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol);
37.             SetInteger(OBJPROP_XDISTANCE, 10);
38.             SetInteger(OBJPROP_YDISTANCE, 0);
39.             SetInteger(OBJPROP_XSIZE, 300);
40.             SetInteger(OBJPROP_YSIZE, indicator_height);
41.             SetInteger(OBJPROP_PERIOD, PERIOD_M15);
42.             SetInteger(OBJPROP_DATE_SCALE, false);
43.             SetInteger(OBJPROP_PRICE_SCALE, false);
44.             SetInteger(OBJPROP_SELECTABLE, true); 
45.             SetInteger(OBJPROP_SELECTED, false);
46.         }
47. //+----------------+
48.         ~C_MiniChart()
49.         {
50.             ObjectDelete(0, m_szNameObject);
51.             ChartRedraw();
52.         }
53. //+----------------+
54. }*gl_MiniChart;
55. //+------------------------------------------------------------------+
56. int OnInit(void)
57. {
58.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
59. 
60.     gl_MiniChart = new C_MiniChart("Mini Chart");
61.    
62.     return INIT_SUCCEEDED;
63. };
64. //+------------------------------------------------------------------+
65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
66. {
67.     return rates_total;
68. };
69. //+------------------------------------------------------------------+
70. void OnDeinit(const int reason)
71. {
72.     delete gl_MiniChart;
73. };
74. //+------------------------------------------------------------------+

Код 08

В свою очередь, ниже показан код, который будет интегрировать исполняемый файл кода 08 и отвечать за вызов индикатора и создание подокна.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property description "DEMO Indicator"
05. //+------------------------------------------------------------------+
06. #property indicator_chart_window
07. #property indicator_plots           0
08. //+------------------------------------------------------------------+
09. #define def_Resource "Code 07.ex5"
10. //+------------------------------------------------------------------+
11. #resource def_Resource
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource));
16.     
17.     return INIT_SUCCEEDED;
18. };
19. //+------------------------------------------------------------------+
20. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
21. {
22.     return rates_total;
23. };
24. //+------------------------------------------------------------------+

Код 09

Код 09 очень похож на остальные коды, которые мы используем для интеграции. По сути, отличие от остальных заключается в определении в девятой строке. Однако — и это вы сможете проверить самостоятельно — в коде 08 у нас есть определение в седьмой строке. Это определение позволит нам задать высоту подокна при его создании. Однако этот код 08, в отличие от того, что мы видели в коде 05, НЕ СОЗДАСТ ПОДОКНО. Это сделает за него код 09. Таким образом, даже если вас это не особенно интересует, вы сможете добавить индикатор из кода 09 в подокно, в котором уже находится другой индикатор. И это позволяет нам создавать новые и интересные конфигурации для использования в MetaTrader 5.

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


Заключительные замечания

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

В этой статье мы рассмотрели лишь небольшую часть того, что можно изучить гораздо глубже, чтобы получить по-настоящему впечатляющие результаты. И мы сделали всё это, используя базовые знания и без каких-либо сложностей. Таким образом, разница между потрясающим результатом и просто приемлемым зависит от того, насколько сильно вы стремитесь прийти туда, куда ещё никто не дошёл. Тем не менее я уверен, что даже после всего показанного здесь многие скажут, что делать всё это не было необходимости, поскольку существуют более эффективные и простые способы получить тот же результат. Должен признать, что они правы. Тем не менее мы ещё не дошли до того момента, когда без представленных здесь знаний вы не сможете воспроизвести те же результаты, которых добивается тот, кто действительно знает, что делает. Поэтому спокойно изучите представленный здесь материал, потому что в следующих статьях мы ещё глубже рассмотрим использование объектов в подокнах.
Файл MQ5 Описание
Indicators\Code 01 Базовая демонстрация
Indicators\Code 02 Базовая демонстрация
Indicators\Code 03 Базовая демонстрация
Indicators\Code 04 Базовая демонстрация
Indicators\Code 05 Базовая демонстрация
Indicators\Code 06 Базовая демонстрация
Indicators\Code 07 Базовая демонстрация
Indicators\Code 08 Базовая демонстрация

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

Прикрепленные файлы |
Anexo.zip (6.08 KB)
Создание пользовательского тикового графика в MQL5 Создание пользовательского тикового графика в MQL5
Узнайте, как реализовать в MQL5 тиковый график, в котором каждый бар строится из фиксированного количества тиков, а не по времени. В статье рассматриваются создание и настройка пользовательского символа, получение тиков в режиме реального времени, формирование значений OHLC и передача данных с помощью функции CustomRatesUpdate. Такой подход позволяет получить свечи, формируемые по рыночной активности, которые точнее отражают интенсивность рынка и краткосрочный импульс для точного внутридневного анализа.
От базового к среднему уровню: Ресурсы От базового к среднему уровню: Ресурсы
В этой статье вы познакомитесь с концепцией, которая во многих случаях может оказаться чрезвычайно полезной и значительно упростить обмен вашими приложениями и проектами. Хотя эту концепцию непросто полностью объяснить в одной статье, того, что будет здесь изложено и объяснено, уже хватит, чтобы в будущем мы смогли сделать многое, в том числе и то, что иначе было бы невозможно. Именно поэтому эта статья ещё не была опубликована: чтобы у вас был вспомогательный материал и начальная база для изучения.
Нейронная сеть на практике: Градиент Нейронная сеть на практике: Градиент
В статье объясняется, почему и как используется градиент при обучении перцептрона, исходя из метода наименьших квадратов и правила цепочки для получения частных производных. Мы покажем реализацию вычисления градиента в классе C_Neuron на языке MQL5 и проверим её на примерах с 1 и 2 входами. Вы научитесь настраивать веса и смещение с помощью градиентного спуска, а также подготовитесь к прямому и обратному распространению.
Нейронная сеть на практике: Функции активации Нейронная сеть на практике: Функции активации
Это, без сомнения, статья на тему, которую мне до сих пор больше всего понравилось писать. В ней я показал, что на самом деле нам не нужно ничего особенного, чтобы достичь определённой цели. Мы можем достичь этого разными способами, если у нас есть необходимые знания и готовность учиться и посвящать себя этому делу. Я от всей души благодарю всех, кто помог мне с выбором функций. Приведённый здесь материал носит исключительно учебный характер. Ни в коем случае не следует рассматривать это как нечто, предназначенное для чего-либо, кроме обучения и изучения представленных концепций.