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

От начального к среднему уровню: Файл шаблона графика (I)

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

Введение

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

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

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


Файл шаблона (I)

Скорее всего, вы смотрите на заголовок этого раздела и думаете: «О чём вообще здесь пойдёт речь?» Что ж, мой дорогой читатель, хотя на первый взгляд тема может показаться несколько странной и совершенно не связанной с тем, что вы используете в повседневной работе, на самом деле файлы шаблонов присутствуют в вашей жизни гораздо больше, чем вы, вероятно, замечали. Особенно если вы очень активно используете MetaTrader 5. Помните, что понятие файла шаблона гораздо шире. Здесь, однако, мы ограничимся рассмотрением этого вопроса в рамках MQL5. По сути, эти файлы шаблонов — не более чем файлы, содержащие те или иные настройки графика или шаблон представления графических объектов на платформе MetaTrader 5.

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


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

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

С упомянутой статьёй можно ознакомиться здесь: Разработка торгового советника (EA) с нуля (Часть 13): Times And Trade (II). То есть то, о чём я пишу в статьях, я действительно использую. Я не пишу статьи ради того, чтобы просто писать. Я хочу, чтобы вы учились по ним или, как минимум, чтобы они послужили вам источником вдохновения для создания чего-то интересного и полезного. И поскольку многие из вас не программисты, а энтузиасты, эта серия как раз и призвана показать, как к этому прийти, воплощая свои идеи на практике и не завися от чьей-либо доброй воли.

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


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

А теперь внимание: то, что вы видите на этом изображении 02, — это элемент быстрого доступа, позволяющий использовать ещё более продвинутый механизм, доступный в MQL5. Однако, возможно, вы ещё не осознали этого именно потому, что до сих пор оставались во тьме. Настало время выйти на свет и увидеть, что мир не окутан тенями, а представляет собой разноцветный мир, полный возможностей, о которых многие даже не догадываются, потому что всё ещё отказываются видеть свет дня и предпочитают жить в неведении, с ограниченным взглядом на вещи.

Если вы всё ещё этого не поняли, я хочу, чтобы вы ещё раз посмотрели на это изображение 02 и честно ответили: знаете ли вы какой-нибудь объект в MQL5, похожий на фигуру в левом углу этого изображения 02? Да, именно так: объект OBJ_CHART — это именно то, что мы видим в левом углу этого изображения 02. "Подождите. Почему вы обращаете моё внимание на этот факт? Вы хотите сказать, что мы можем управлять объектом OBJ_CHART без необходимости его программировать? Неужели я всё это время тратил его впустую, занимаясь тем, что можно было сделать проще, быстрее и эффективнее?". Ну, я не знаю, что вам ответить на это. Но да, скорее всего, вы зря тратили время, ограничиваясь одним определённым способом, хотя могли делать это гораздо быстрее, выбрав другой путь.

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


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

Для программиста или просто более внимательного и столь же любознательного пользователя это же изображение 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. int OnInit(void)
12. {
13.     string sz0 = def_Prefix + (string)ObjectsTotal(0, -1, -1);
14. 
15.     ObjectCreate(0, sz0 , OBJ_CHART, 0, 0, 0);
16.     ObjectSetString(0, sz0, OBJPROP_SYMBOL, _Symbol);
17.     ObjectSetInteger(0, sz0, OBJPROP_XDISTANCE, 0);
18.     ObjectSetInteger(0, sz0, OBJPROP_YDISTANCE, 0);
19.     ObjectSetInteger(0, sz0, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
20.     ObjectSetInteger(0, sz0, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
21.     ObjectSetInteger(0, sz0, OBJPROP_PERIOD, PERIOD_M15);
22.     ObjectSetInteger(0, sz0, OBJPROP_DATE_SCALE, false);
23.     ObjectSetInteger(0, sz0, OBJPROP_PRICE_SCALE, false);
24.    
25.     return INIT_SUCCEEDED;
26. };
27. //+------------------------------------------------------------------+
28. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
29. {
30.     return rates_total;
31. };
32. //+------------------------------------------------------------------+
33. void OnDeinit(const int reason)
34. {
35.     ObjectsDeleteAll(0, def_Prefix);
36.     ChartRedraw();
37. };
38. //+------------------------------------------------------------------+

Код 01

Этот код 01, который, как я полагаю, не требует пояснений, даёт результат, показанный на следующем изображении.


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

Конечно, вы уже знаете, что мы можем изменить некоторые свойства этого объекта OBJ_CHART. Это позволяет нам настраивать и использовать график так, как раньше никто и представить себе не мог. Однако если размеры графика изменятся, этот код 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. #define def_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. int OnInit(void)
14. {
15.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
16.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
17.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
18.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
19.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
20.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
21.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
22.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
23.    
24.     return INIT_SUCCEEDED;
25. };
26. //+------------------------------------------------------------------+
27. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
28. {
29.     return rates_total;
30. };
31. //+------------------------------------------------------------------+
32. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
33. {
34.     switch(id)
35.     {
36.         case CHARTEVENT_CHART_CHANGE:
37.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
38.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
39.             break;
40.     }
41.     ChartRedraw();
42. };
43. //+------------------------------------------------------------------+
44. void OnDeinit(const int reason)
45. {
46.     ObjectsDeleteAll(0, def_Prefix);
47.     ChartRedraw();
48. };
49. //+------------------------------------------------------------------+

Код 02

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

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

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

Когда объект OBJ_CHART размещается на графике, он заимствует часть — и обратите на это внимание — шаблона графика. Слово, которое нас здесь интересует, — это: ЧАСТЬ. И почему это слово так важно? Потому что независимо от того, как настроен шаблон графика, для создания шаблона, который будет применён к объекту OBJ_CHART, будет использоваться только его часть. Это делается для того, чтобы объект OBJ_CHART отображался на графике.

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


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

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


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

А теперь внимание: когда мы разместим индикатор на графике, получим результат, который вы можете увидеть ниже.


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

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


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

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

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

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

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

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

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

Поэтому мы не будем начинать с изменения цветов графика, находящегося в объекте OBJ_CHART. Мы изменим цвета основного графика. Для этого мы изменим код 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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     ChartSetInteger(id, CHART_COLOR_CANDLE_BULL, clrGreen);
16.     ChartSetInteger(id, CHART_COLOR_CANDLE_BEAR, clrFireBrick);
17.     ChartSetInteger(id, CHART_COLOR_CHART_UP, clrBlack);
18.     ChartSetInteger(id, CHART_COLOR_CHART_DOWN, clrBlack);
19.     ChartSetInteger(id, CHART_COLOR_CHART_LINE, clrBlue);
20. }
21. //+------------------------------------------------------------------+
22. int OnInit(void)
23. {
24.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
25.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
26.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
27.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
28.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
29.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
30.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
31.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
32.    
33.     return INIT_SUCCEEDED;
34. };
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     return rates_total;
39. };
40. //+------------------------------------------------------------------+
41. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
42. {
43.     switch(id)
44.     {
45.         case CHARTEVENT_CHART_CHANGE:
46.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
47.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
48.             NewColors(0);
49.             break;
50.     }
51.     ChartRedraw();
52. };
53. //+------------------------------------------------------------------+
54. void OnDeinit(const int reason)
55. {
56.     ObjectsDeleteAll(0, def_Prefix);
57.     ChartRedraw();
58. };
59. //+------------------------------------------------------------------+

Код 03

Идея очень проста. Глядя на график на изображении 03, мы хотим, чтобы после добавления этого кода 03 изменились цвета баров. То, что мы здесь делаем, очень похоже на то, что мы делали в другой статье из этой же серии, От начального к среднему уровню: Индикатор (IV). Однако, в отличие от того, что было в упомянутой статье, здесь мы, так сказать, меняем цвет вручную. То есть MetaTrader 5 не вмешивается напрямую, а лишь показывает нам результат. В результате выполнения этого кода 03 мы получаем то, что показано на следующем изображении.


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

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

Хорошо, поскольку цель здесь учебная, я не буду беспокоиться о восстановлении или сохранении исходной цветовой схемы. Итак, можем перейти к следующему шагу. Суть в том, чтобы функция NewColors из строки 13 этого кода 03 применялась к объекту OBJ_CHART. Таким образом, цветовая схема будет применена к элементам, отображаемым в объекте OBJ_CHART. Идея проста, но как мы реализуем это на практике? Итак, вы должны помнить, что объект OBJ_CHART — это, по сути, график и, как таковой, имеет собственный идентификатор. Обратите внимание на следующее: если в строке 48 кода 03 указать идентификатор графика, который должен получить значения из процедуры в строке 13, MetaTrader 5 будет знать, к какому графику нужно применить эти же значения.

Вы со мной согласны? Хорошо, а как мы можем узнать, каков идентификатор графика в объекте OBJ_CHART? В некотором смысле это самая простая часть, поскольку в библиотеке MQL5 у нас уже есть все необходимые для этого механизмы. Итак, единственное, что нам нужно изменить в коде 03, — это строка 48. Её нужно заменить строкой, показанной ниже.

NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));

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


Анимация 01

Но что здесь произошло? Что ж, это было довольно необычно. Должен признаться, я совсем не ожидал такого результата. Я ожидал, что всё будет иначе или, по крайней мере, что цвета элементов, отображаемых на графике объекта OBJ_CHART, как-то изменятся. Но, судя по всему, всё сработало не так, как мы ожидали.

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

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

Обратите внимание: после того как мы изменили строку 48, добавив приведённое выше содержимое,в функции NewColors (строка 13) у нас появляется идентификатор графика, который нужно перерисовать. Чтобы выполнить эту перерисовку или, точнее, заставить MetaTrader 5 перерисовать график, находящийся в объекте OBJ_CHART, мы должны указать функции ChartRedraw из библиотеки MQL5, какой идентификатор графика нужно немедленно перерисовать. Зная это, мы можем изменить код 03 и получить код 04, полное содержание которого показано ниже.

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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     ChartSetInteger(id, CHART_COLOR_CANDLE_BULL, clrGreen);
16.     ChartSetInteger(id, CHART_COLOR_CANDLE_BEAR, clrFireBrick);
17.     ChartSetInteger(id, CHART_COLOR_CHART_UP, clrBlack);
18.     ChartSetInteger(id, CHART_COLOR_CHART_DOWN, clrBlack);
19.     ChartSetInteger(id, CHART_COLOR_CHART_LINE, clrBlue);
20.     ChartRedraw(id);
21. }
22. //+------------------------------------------------------------------+
23. int OnInit(void)
24. {
25.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
26.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
27.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
28.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
29.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
30.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
31.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
32.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
33.    
34.     return INIT_SUCCEEDED;
35. };
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     return rates_total;
40. };
41. //+------------------------------------------------------------------+
42. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
43. {
44.     switch(id)
45.     {
46.         case CHARTEVENT_CHART_CHANGE:
47.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
48.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
49.             NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));
50.             break;
51.     }
52.     ChartRedraw();
53. };
54. //+------------------------------------------------------------------+
55. void OnDeinit(const int reason)
56. {
57.     ObjectsDeleteAll(0, def_Prefix);
58.     ChartRedraw();
59. };
60. //+------------------------------------------------------------------+

Код 04

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


Анимация 02

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

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

Теперь мы внесём, так сказать, последнее изменение в этот код 04. Цель этого изменения как раз в том, чтобы скопировать значения с основного графика на график, который будет отображаться внутри объекта OBJ_CHART. Код, который это делает, приведён ниже.

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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     ChartSetInteger(id, CHART_COLOR_CANDLE_BULL, ChartGetInteger(0, CHART_COLOR_CANDLE_BULL));
16.     ChartSetInteger(id, CHART_COLOR_CANDLE_BEAR, ChartGetInteger(0, CHART_COLOR_CANDLE_BEAR));
17.     ChartSetInteger(id, CHART_COLOR_CHART_UP, ChartGetInteger(0, CHART_COLOR_CHART_UP));
18.     ChartSetInteger(id, CHART_COLOR_CHART_DOWN, ChartGetInteger(0, CHART_COLOR_CHART_DOWN));
19.     ChartSetInteger(id, CHART_COLOR_CHART_LINE, ChartGetInteger(0, CHART_COLOR_CHART_LINE));
20.     ChartRedraw(id);
21. }
22. //+------------------------------------------------------------------+
23. int OnInit(void)
24. {
25.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
26.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
27.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
28.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
29.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
30.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
31.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
32.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
33.    
34.     return INIT_SUCCEEDED;
35. };
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     return rates_total;
40. };
41. //+------------------------------------------------------------------+
42. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
43. {
44.     switch(id)
45.     {
46.         case CHARTEVENT_CHART_CHANGE:
47.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
48.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
49.             NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));
50.             break;
51.     }
52.     ChartRedraw();
53. };
54. //+------------------------------------------------------------------+
55. void OnDeinit(const int reason)
56. {
57.     ObjectsDeleteAll(0, def_Prefix);
58.     ChartRedraw();
59. };
60. //+------------------------------------------------------------------+

Код 05

Хотя этот код 05 и работает, он просто ужасен. Мне не нравится делать что-либо таким образом, потому что создаётся впечатление, будто мы сами не очень понимаем, что делаем. Кроме того, по мере появления новых требований и добавления новых элементов всё становится гораздо более трудоёмким и утомительным. И причина в том, что в процедуре из строки 13 мы дублируем элементы очень рискованным способом. В результате со временем код становится очень подверженным опечаткам и ошибкам при редактировании.

По моему скромному мнению, код 05 был бы написан гораздо лучше, если бы его сделали так, как показано ниже.

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_Prefix "DEMO_"
10. //+------------------------------------------------------------------+
11. string szObject;
12. //+------------------------------------------------------------------+
13. void NewColors(long id)
14. {
15.     const ENUM_CHART_PROPERTY_INTEGER list[] = {
16.         CHART_COLOR_CANDLE_BULL,
17.         CHART_COLOR_CANDLE_BEAR,
18.         CHART_COLOR_CHART_UP,
19.         CHART_COLOR_CHART_DOWN,
20.         CHART_COLOR_CHART_LINE
21.     };
22.     
23.     for (uint c = 0; c < list.Size(); c++)
24.         ChartSetInteger(id, list[c], ChartGetInteger(0, list[c]));
25.     ChartRedraw(id);
26. }
27. //+------------------------------------------------------------------+
28. int OnInit(void)
29. {
30.     szObject = def_Prefix + (string)ObjectsTotal(0, -1, -1);
31.     ObjectCreate(0, szObject , OBJ_CHART, 0, 0, 0);
32.     ObjectSetString(0, szObject, OBJPROP_SYMBOL, _Symbol);
33.     ObjectSetInteger(0, szObject, OBJPROP_XDISTANCE, 0);
34.     ObjectSetInteger(0, szObject, OBJPROP_YDISTANCE, 0);
35.     ObjectSetInteger(0, szObject, OBJPROP_PERIOD, PERIOD_M15);
36.     ObjectSetInteger(0, szObject, OBJPROP_DATE_SCALE, false);
37.     ObjectSetInteger(0, szObject, OBJPROP_PRICE_SCALE, false);
38.    
39.     return INIT_SUCCEEDED;
40. };
41. //+------------------------------------------------------------------+
42. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
43. {
44.     return rates_total;
45. };
46. //+------------------------------------------------------------------+
47. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
48. {
49.     switch(id)
50.     {
51.         case CHARTEVENT_CHART_CHANGE:
52.             ObjectSetInteger(0, szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
53.             ObjectSetInteger(0, szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS));
54.             NewColors(ObjectGetInteger(0, szObject, OBJPROP_CHART_ID));
55.             break;
56.     }
57.     ChartRedraw();
58. };
59. //+------------------------------------------------------------------+
60. void OnDeinit(const int reason)
61. {
62.     ObjectsDeleteAll(0, def_Prefix);
63.     ChartRedraw();
64. };
65. //+------------------------------------------------------------------+

Код 06

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

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

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


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

В этой статье мы рассмотрели основы работы с шаблоном и то, как его можно использовать для переноса настроек основного графика на график, созданный и поддерживаемый объектом OBJ_CHART. Хотя вам может казаться, что вы прекрасно поняли, что здесь произошло, я бы хотел, чтобы вы провели небольшой эксперимент. Попробуйте написать тот код, который мы рассмотрели, и тестируйте его по мере добавления каждого вызова функций библиотеки MQL5. Это позволит вам на практике увидеть, как следует строить и организовывать свой код. Хотя в приложении у вас есть полные версии кода, если вы сами увидите, какие проблемы вызывает наличие или отсутствие того или иного вызова, — вам будет гораздо проще понять, как строить код на MQL5. Это позволит вам приобрести гораздо больше опыта как программисту.
Файл MQ5 Описание
Indicators\Code 01 Базовая демонстрация
Indicators\Code 02 Базовая демонстрация
Indicators\Code 03 Базовая демонстрация
Indicators\Code 04 Базовая демонстрация

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

Прикрепленные файлы |
Альтернатива временным барам (Часть 2): Построение баров серий в MQL5 Альтернатива временным барам (Часть 2): Построение баров серий в MQL5
Мы реализуем тиковые, объемные и долларовые бары серий в Python и MQL5 и встраиваем их в существующий фреймворк построения баров. В статье подробно описаны обновление двух накопителей, офлайн-калибровка с начальными значениями для каждого направления, сохранение состояния в советниках и проверка совпадения результатов Python и MQL5. Бары серий (runs bar) выявляют односторонние всплески активности, которые может скрывать метрика чистого дисбаланса, улучшая тем самым охват данных в спокойные сессии и для моделей возврата к среднему.
Нейронная сеть на практике: Вопрос шкалы Нейронная сеть на практике: Вопрос шкалы
Среди широкой публики бытует ложное представление о том, что нейронная сеть или искусственный интеллект могут в некотором смысле постигать мир, в котором мы живём. В некоторых случаях это даже может быть правдой. Но вам, если вы хотите добиться более быстрой сходимости нейронной сети или даже отдельного нейрона, нужно понимать, что шкала используемых значений часто влияет на скорость сходимости. Кроме того, в этой статье я покажу кое-что странное, что происходит в нейронных сетях. Это сведёт вас с ума и заставит задуматься о многом в этой теме.
Создание объектно-ориентированного движка инференса ONNX на языке MQL5 Создание объектно-ориентированного движка инференса ONNX на языке MQL5
В статье рассказывается, как запускать модели, обученные на Python, непосредственно в MetaTrader 5 с помощью функций ONNX терминала. Мы создаем класс MQL5, который инкапсулирует создание сеанса, фиксирует формы входных/выходных тензоров, применяет min-max-нормализацию признаков, чтобы повторять обработку, использованную при обучении, и запускает OnnxRun один раз на каждый бар, чтобы снизить нагрузку на ЦП. В результате получается надежный и удобный в сопровождении канал инференса для графиков в реальном времени и Тестера стратегий без использования сокетов или DLL-библиотек.
Построение системы обнаружения дивергенций: Создание пользовательского индикатора MPO4 Построение системы обнаружения дивергенций: Создание пользовательского индикатора MPO4
Представляем MPO4 — осциллятор на основе рыночного давления, который учитывает тело и направление свечей в контексте текущей волатильности. В статье подробно рассматриваются математические основы индикатора, нормирование значений в ограниченном диапазоне и сглаживание с помощью EMA. Затем создается модуль дивергенций на основе опорных точек, предназначенный для работы без перерисовки. Вы получите полную реализацию на MQL5 и практические рекомендации по интерпретации сигналов, включая сравнение с RSI как альтернативным источником.