От начального к среднему уровню: Файл шаблона графика (II)
Введение
В предыдущей статье «От начального к среднему уровню: Файл шаблона графика (I)» мы начали подробнее разбирать тему, которая в целом была мало исследована значительной частью программистов MQL5. Многие люди не имеют чёткого представления о том, насколько MQL5 способен сделать MetaTrader 5 гораздо более интерактивным, затратив намного меньше усилий. Значительная часть пользователей видит в MetaTrader 5 только то, что видно после установки платформы. По этой причине многие даже не представляют, насколько гибко можно настраивать MetaTrader 5 — если, конечно, вы готовы приложить усилия и уделить время изучению некоторых деталей самой платформы.
Я никого не виню за то, что им не удаётся использовать платформу более эффективно, ведь многие видят только то, что очевидно и привычно. То есть большинство людей полагает, что мы можем программировать только индикаторы и советники. Даже эти программы не могут создавать определённые конфигурации, поскольку платформа не позволяет нам делать некоторые вещи. Такой ограниченный и жёсткий тип мышления не свойственен настоящему программисту или, по крайней мере, человеку с более широким взглядом на то, что его окружает. Чтобы MetaTrader 5 стал гораздо большим, чем кажется сразу после установки, вам нужно понять, как использовать MQL5. Ключ, открывающий двери в рай, находится именно здесь.
Хорошо, поскольку материал, который мы будем рассматривать, может показаться несколько запутанным, я прошу вас сейчас отложить в сторону всё, что может вас отвлекать. Так вы сможете полностью сосредоточиться на том, что будет объясняться в этой статье. Здесь мы увидим, как использовать шаблоны для доступа к определённым «возможностям», затратив гораздо меньше усилий на программирование.Файл шаблона (II)
В предыдущей статье я показал, как можно скопировать часть настроек основного графика на график, отображаемый в объекте OBJ_CHART. Думаю, вы уже изучили это и провели несколько экспериментов с тем, что мы рассмотрели. Однако это лишь скучная и не слишком интересная часть. Но по-настоящему интересно становится, когда мы начинаем глубже изучать файлы настроек.
Прежде чем перейти к этому, мне нужно немного пояснить контекст, поскольку опыт у каждого может существенно различаться. Если многие могут хорошо разбираться в файлах настроек, то другие не имеют ни малейшего представления о том, как интерпретировать содержащуюся в них информацию. Итак, прежде чем начать, нам нужно понять, как настраивать эти элементы непосредственно в MQL5. Это связано с тем, что некоторые вещи можно сделать напрямую с помощью файлов настроек, тогда как другие — только средствами программирования на MQL5. Только со временем, с опытом и большой практикой вы сможете определить, когда, где и почему лучше использовать тот или иной подход. Я здесь не для того, чтобы говорить вам: делайте это, а того не делайте. Я хочу показать вам, что это можно сделать и так, и по-другому. Решение о том, какую методологию использовать, зависит исключительно от вас; это личный выбор.
В предыдущих статьях мы рассматривали, как интегрировать индикаторы в другой код, превращая их тем самым во внутренние ресурсы этого кода. До сих пор это делалось только в коде индикаторов. Тем не менее у нас уже есть механизм, который мы можем использовать. Мы также увидели, что можем управлять самим объектом 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. class C_Chart 10. { 11. private : 12. //+----------------+ 13. string m_szObject; 14. //+----------------+ 15. public : 16. //+----------------+ 17. C_Chart() 18. { 19. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 20. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 21. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 22. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 23. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 24. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 25. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 26. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 27. } 28. //+----------------+ 29. ~C_Chart() 30. { 31. ObjectDelete(0, m_szObject); 32. ChartRedraw(); 33. } 34. //+----------------+ 35. void Resize(void) 36. { 37. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 38. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 39. ChartRedraw(); 40. } 41. //+----------------+ 42. }*gl_Chart = NULL; 43. //+------------------------------------------------------------------+ 44. int OnInit(void) 45. { 46. gl_Chart = new C_Chart(); 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. //+------------------------------------------------------------------+ 56. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 57. { 58. switch(id) 59. { 60. case CHARTEVENT_CHART_CHANGE: 61. if (gl_Chart) (*gl_Chart).Resize(); 62. break; 63. } 64. }; 65. //+------------------------------------------------------------------+ 66. void OnDeinit(const int reason) 67. { 68. delete gl_Chart; 69. }; 70. //+------------------------------------------------------------------+
Код 01
При выполнении этого кода 01 мы получаем результат, показанный на следующем изображении.

Изображение 01
Думаю, на этом этапе все уже понимают, что происходит, ведь материал довольно свежий и, следовательно, должен ещё оставаться в памяти у этих жаждущих знаний людей. По крайней мере, я на это надеюсь. Теперь оставим этот индикатор, показанный в коде 01, на графике и создадим новый код. Он показан ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_applied_price PRICE_CLOSE 08. #property indicator_buffers 1 09. #property indicator_plots 1 10. #property indicator_type1 DRAW_LINE 11. #property indicator_color1 clrRed 12. #property indicator_style1 STYLE_SOLID 13. #property indicator_width1 3 14. #property indicator_label1 "EMA" 15. //+------------------------------------------------------------------+ 16. input uint def_nPeriods = 9; //Период: 17. input ENUM_MA_METHOD def_MA_Method = MODE_EMA; //Метод: 18. //+------------------------------------------------------------------+ 19. double Buff_iMA[]; 20. int Handle; 21. //+------------------------------------------------------------------+ 22. int OnInit(void) 23. { 24. SetIndexBuffer(0, Buff_iMA, INDICATOR_DATA); 25. IndicatorSetString(INDICATOR_SHORTNAME, EnumToString(def_MA_Method) + "_" + (string)def_nPeriods + "_" + EnumToString((ENUM_APPLIED_PRICE)_AppliedTo)); 26. if ((Handle = iMA(NULL, 0, def_nPeriods, 0, def_MA_Method, _AppliedTo)) == INVALID_HANDLE) 27. { 28. Print("Could not start the indicator..."); 29. return INIT_FAILED; 30. }; 31. 32. return INIT_SUCCEEDED; 33. }; 34. //+------------------------------------------------------------------+ 35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 36. { 37. ArrayInitialize(Buff_iMA, EMPTY_VALUE); 38. CopyBuffer(Handle, 0, 0, def_nPeriods, Buff_iMA); 39. 40. return rates_total; 41. }; 42. //+------------------------------------------------------------------+ 43. void OnDeinit(const int reason) 44. { 45. if (Handle != INVALID_HANDLE) 46. IndicatorRelease(Handle); 47. }; 48. //+------------------------------------------------------------------+
Код 02
Этот код 02 тоже не должен вызвать трудностей в понимании, хотя единственное новое здесь — то, как в строке 25 формируется имя индикатора. Но это не должно представлять никакой проблемы, по крайней мере для тех, кто изучал содержание статей. Поэтому я не вижу причин вдаваться в подробности и мы можем сразу перейти к практической части. Теперь, если мы попросим MetaTrader 5 добавить на график также и этот индикатор (код 02), показанный на изображении 01, мы получим результат, который можно увидеть на следующем изображении.

Изображение 02
Всё ожидаемо и без каких-либо сюрпризов. Мы все уже знаем, что можно объединить два кода, сделав один из них внутренним ресурсом другого, более сложного кода. Таким образом, эти коды 01 и 02 можно скомпилировать по отдельности, а затем интегрировать в третий код, после чего они станут его внутренними ресурсами. И чтобы не усложнять ситуацию и оставаться в рамках уровня, который можно считать подходящим для обучения, давайте поступим немного иначе: превратим код 02 во внутренний ресурс кода 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_Chart 10. { 11. private : 12. //+----------------+ 13. string m_szObject; 14. //+----------------+ 15. public : 16. //+----------------+ 17. C_Chart() 18. { 19. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 20. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 21. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 22. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 23. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 24. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 25. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 26. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 27. } 28. //+----------------+ 29. ~C_Chart() 30. { 31. ObjectDelete(0, m_szObject); 32. ChartRedraw(); 33. } 34. //+----------------+ 35. void Resize(void) 36. { 37. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 38. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 39. ChartRedraw(); 40. } 41. //+----------------+ 42. }*gl_Chart = NULL; 43. //+------------------------------------------------------------------+ 44. #define def_Resource "Code 02.ex5" 45. //+------------------------------------------------------------------+ 46. #resource def_Resource 47. //+------------------------------------------------------------------+ 48. int OnInit(void) 49. { 50. gl_Chart = new C_Chart(); 51. ChartIndicatorAdd(0, 0, iCustom(NULL, NULL, "::" + def_Resource)); 52. 53. return INIT_SUCCEEDED; 54. }; 55. //+------------------------------------------------------------------+ 56. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 57. { 58. return rates_total; 59. }; 60. //+------------------------------------------------------------------+ 61. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 62. { 63. switch(id) 64. { 65. case CHARTEVENT_CHART_CHANGE: 66. if (gl_Chart) (*gl_Chart).Resize(); 67. break; 68. } 69. }; 70. //+------------------------------------------------------------------+ 71. void OnDeinit(const int reason) 72. { 73. delete gl_Chart; 74. }; 75. //+------------------------------------------------------------------+
Код 03
Этот код 03 несколько грубоват. Его можно и нужно доработать, чтобы он выглядел немного более изящно. Тем не менее, для того, что я хочу объяснить, этого достаточно. А теперь внимание: если вы не можете понять, что делает этот код 03, просто взглянув на него, я прошу вас прочитать и изучить предыдущие статьи. Здесь очень важно понять, что делает этот код 03, просто взглянув на него. Не думайте, что вы уже это знаете, только потому, что вам кажется, будто вы понимаете, как всё устроено. Чтобы понять остальную часть статьи, действительно необходимо разобраться в том, как работает этот код 03.
Тем, кто знает, что делает этот код 03, нет необходимости смотреть результат его выполнения, так как он будет таким же, как на изображении 02. Поэтому я не буду снова показывать то же изображение. Именно здесь всё начинает становиться интересным.
Внимательно посмотрите на строку 51 кода 03. В предыдущей статье мы увидели, что можем скопировать данные настроек с основного графика на график, созданный объектом OBJ_CHART. Мы увидели, что это возможно именно благодаря одной детали, которую нужно реализовать в нашем коде. Мы могли запросить у MetaTrader 5 идентификатор графика объекта OBJ_CHART. При этом мы могли использовать те же вызовы, что и для настройки основного графика, чтобы настроить и график объекта OBJ_CHART. На этом этапе, думаю, всё ясно.
А теперь самое интересное: первым аргументом вызова в строке 51 как раз является идентификатор графика, к которому будет добавлен индикатор. Если вам это непонятно, обратитесь к документации за более подробной информацией. Теперь возникает вопрос: что произойдёт, если мы получим идентификатор графика, созданного и поддерживаемого объектом OBJ_CHART, и передадим его в качестве первого аргумента в строке 51? Помните, что первый аргумент — это идентификатор графика, на котором должен быть размещён индикатор. Можно сказать так: «Хорошо, я думаю, что MetaTrader 5 разместит указанный в строке пятьдесят один индикатор на графике, созданном объектом OBJ_CHART». И, на самом деле, вы были бы не совсем неправы, ведь такой ответ показал бы, что вы поняли, как всё работает к этому моменту. Однако давайте посмотрим, происходит ли это на практике. Для этого изменим код 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_Resource "Code 02.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. class C_Chart 14. { 15. private : 16. //+----------------+ 17. string m_szObject; 18. //+----------------+ 19. void Proc(long id) 20. { 21. Print("ID informed: ", id); 22. Print("Result: ", ChartIndicatorAdd(id, 0, iCustom(NULL, NULL, "::" + def_Resource))); 23. ChartRedraw(id); 24. } 25. //+----------------+ 26. public : 27. //+----------------+ 28. C_Chart() 29. { 30. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 31. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 32. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 33. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 34. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 35. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 36. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 37. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 38. } 39. //+----------------+ 40. ~C_Chart() 41. { 42. ObjectDelete(0, m_szObject); 43. ChartRedraw(); 44. } 45. //+----------------+ 46. void Resize(void) 47. { 48. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 49. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 50. ChartRedraw(); 51. } 52. //+----------------+ 53. void AddIndicator() 54. { 55. Proc(ObjectGetInteger(0, m_szObject, OBJPROP_CHART_ID)); 56. Proc(ChartID()); 57. } 58. //+----------------+ 59. }*gl_Chart = NULL; 60. //+------------------------------------------------------------------+ 61. int OnInit(void) 62. { 63. gl_Chart = new C_Chart(); 64. (*gl_Chart).AddIndicator(); 65. 66. return INIT_SUCCEEDED; 67. }; 68. //+------------------------------------------------------------------+ 69. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 70. { 71. return rates_total; 72. }; 73. //+------------------------------------------------------------------+ 74. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 75. { 76. switch(id) 77. { 78. case CHARTEVENT_CHART_CHANGE: 79. if (gl_Chart) (*gl_Chart).Resize(); 80. break; 81. } 82. }; 83. //+------------------------------------------------------------------+ 84. void OnDeinit(const int reason) 85. { 86. delete gl_Chart; 87. }; 88. //+------------------------------------------------------------------+
Код 04
А теперь будьте внимательны, потому что понимание этого объяснения поможет вам понять, почему нам придётся сделать всё иначе, чтобы добиться задуманного результата. Но прежде чем сделать это, давайте посмотрим на результат выполнения кода 04.

Изображение 03
На этом изображении я выделяю две области. Каждая из них с помощью стрелки показывает источник выделенных данных. Чтобы правильно понять эти данные, нам нужно вернуться к коду 04. В строке 64 у нас есть вызов внутренней процедуры класса. Мы делаем это так, чтобы не извлекать — точнее, не искать — какие-либо данные внутри класса. Мы просто будем использовать содержащиеся в ней данные. Внутри вызываемой процедуры есть две строки, которые выполнят логику, реализованную в процедуре из строки девятнадцать. Это важная часть кода. Обратите внимание, что единственное различие между строками 55 и 56 заключается именно в типе передаваемого аргумента.
Но какой тип данных мы здесь передаём? Итак, если вы поняли предыдущую статью, то знаете, что в строке 55 мы получаем идентификатор графика объекта OBJ_CHART. В строке 56, напротив, мы получаем идентификатор основного графика. Иными словами, неважно, что делает процедура в строке 19. В обоих случаях мы передаём в процедуру правильные и необходимые данные. Обратите внимание, что в строке 21 мы выводим значение предоставленного идентификатора. Однако именно строка 22 действительно имеет важное значение.
В статьях, где мы говорили об интеграции индикатора в другой код, мы рассмотрели один из способов сделать это. Один из них — как раз тот, который мы используем здесь: библиотечная функция ChartIndicatorAdd. Обратите внимание: если вызов функции в MetaTrader 5 выполнится успешно, в терминале будет выведено это подтверждение. Если функция по какой-либо причине завершится неудачно, в терминале также будет выведено сообщение. Однако хочу, чтобы было ясно: единственное различие во время выполнения заключается в том, что в одном случае мы обращаемся к графику объекта OBJ_CHART, а в другом — к основному графику. И в результате мы получаем то, что показано на изображении 03. Там видно, что, когда мы пытаемся добавить индикатор к графику объекта OBJ_CHART, функция завершается неудачно. Но если мы попытаемся сделать то же самое, указав на основной график, функция выполнится успешно.
Это означает, что мы не можем добавить индикатор к графику, созданному и поддерживаемому объектом OBJ_CHART. Но подождите, это не совсем так. Если вы ознакомитесь с другими моими статьями, то увидите, что мне так или иначе удавалось добавить индикатор к графику, созданному и поддерживаемому объектом OBJ_CHART. Как это возможно? На самом деле к графику, созданному и поддерживаемому объектом OBJ_CHART, можно добавить индикатор или даже несколько, если вы этого хотите.
Однако используемые механизмы — не те, которые мы рассматриваем в данный момент. То, что вы не можете найти указаний о том, как это сделать, объясняется именно тем, что вы только что увидели в этом коде 04. Многие программисты полагают, что могут использовать механизм, показанный в коде, для добавления одного или нескольких индикаторов к графику объекта OBJ_CHART. Однако любая попытка сделать это так, как показано здесь, с треском провалится. Именно на этом этапе мы и начнём работать с файлами настроек.
Теперь мы пойдём совершенно другим путём. Тем не менее, результат будет весьма интересным. Кроме того, это, конечно же, позволит нам создавать практически всё, что мы только можем себе представить.
А теперь на минутку забудьте о программировании. Но сначала давайте разберёмся ещё с одной вещью. Многие трейдеры и пользователи торговых платформ, таких как MetaTrader 5, имеют в своём распоряжении различные настройки для разных сетапов, что позволяет им быстро изменять настройки графика при необходимости. В эти файлы настроек можно включать различные элементы. Но пока что мы ограничимся только самым необходимым.
Среди этих элементов есть списки индикаторов как для основного окна, так и для окон индикаторов. Думаю, даже вы уже создавали подобные настройки графика. Однако, как правило, они не ограничиваются применением только к основному графику. Если мы примем определённые меры предосторожности, то сможем применить их и к объекту OBJ_CHART.
"Да ладно, что это за безумие вы мне рассказываете? Если я правильно понял, вы говорите, что мы можем создать одну из таких конфигураций, используя несколько индикаторов, а затем добавить её к объекту OBJ_CHART. Именно это вы имеете в виду?". Да, уважаемый читатель. Да, вы всё поняли правильно. Эти файлы настроек, если соблюдать определённые меры предосторожности, можно применять к объекту OBJ_CHART. И чтобы показать, что это действительно так, давайте шаг за шагом разберём, как это сделать. Одно уточнение: здесь я буду исходить из того, что вы не имеете ни малейшего представления о том, как создать файл шаблона и получить к нему доступ. Итак, давайте посмотрим, как это сделать.
Прежде всего, мы будем использовать максимально чистый график. То есть на нём нет ни одного индикатора. Результат будет таким, как показано ниже.

Изображение 04
Отлично. Теперь добавим на этот график только один индикатор. В данном случае мы добавляем индикатор, созданный в коде 02, поскольку цель состоит в том, чтобы показать, как это делается на практике. Таким образом, мы получаем результат, показанный ниже.

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

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

Изображение 06
Теперь у нас есть новый шаблон, который отображается в области, выделенной зелёным цветом на изображении 06. Каждый раз, когда вы создаёте новый шаблон, он будет появляться здесь, и при необходимости его можно будет применить к любому графику. Однако важно понимать следующее: шаблон будет полностью применён только в том случае, если в момент применения к графику будут доступны все коды, включённые в него. Но, поскольку здесь мы только практикуемся, нет никаких побочных эффектов, о которых нам стоило бы беспокоиться. После этого мы готовы перейти ко второй части. И на этот раз мы сосредоточимся на программировании на MQL5.
Чтобы это было проще понять, давайте проведём небольшой эксперимент. Мы уже знаем, что код 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. int OnInit(void) 10. { 11. ChartApplyTemplate(0, "Testing.tpl"); 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. //+------------------------------------------------------------------+
Код 05
Этот код 05 отлично подойдёт для того, чтобы вы поняли одну важную вещь о шаблонах. Обратите внимание, что код действительно очень прост. Однако эта одиннадцатая строка оказывает большое влияние на результат, который мы получим, применив код к графику. В этой строке мы указываем MetaTrader 5 применить к графику только что сохранённый шаблон. А теперь обратите особое внимание, потому что это действительно очень важно. Когда MetaTrader 5 применит шаблон к графику, это приведёт к сбросу всех настроек, которые были у графика до выполнения одиннадцатой строки. Следовательно, даже индикатор, созданный с помощью этого кода, будет удалён. То есть, как только шаблон станет активным, этот код перестанет присутствовать на графике. Чтобы это понять, посмотрите следующую анимацию.

Анимация 02
Обратите внимание, что индикатор называется CODE 05. Однако из-за одиннадцатой строки, когда мы проверим, какие индикаторы присутствуют на графике, мы увидим, что это не CODE 05, а CODE 02. Почему? Именно потому, что при применении шаблона в строке 11 кода 05 этот код будет удалён с графика. И все настройки графика, включая присутствующие индикаторы или советник, будут удалены, а вместо них будут применены настройки, содержащиеся в файле шаблона.
Это та концептуальная часть, которую вам нужно понять. При использовании этой концепции есть некоторые небольшие ограничения. Однако эти ограничения не помешают нам сделать то, что мы хотим сейчас. А теперь начинается самое интересное. Функции ChartApplyTemplate нужны два параметра. Второй параметр, очевидно, указывает на шаблон, который будет применён к графику. Но что происходит с первым параметром? К чему он относится? Итак, этот первый параметр указывает идентификатор графика, к которому будет применён шаблон. И почему именно эту часть можно считать самой интересной?
Причина проста. Помните доказательство того, что код 04 НЕ МОЖЕТ добавить индикатор на график, созданный и поддерживаемый объектом OBJ_CHART? Итак, если мы изменим код 04 так, чтобы можно было использовать то, что показано в строке 11 этого кода 05, то сможем, хотя и с некоторыми ограничениями, управлять определёнными аспектами графика объекта OBJ_CHART. И поскольку код 05 сработал, а нам нужно лишь перенести ту же идею в код 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_Resource "Code 02.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. class C_Chart 14. { 15. private : 16. //+----------------+ 17. string m_szObject; 18. //+----------------+ 19. void Proc(long id) 20. { 21. Print("ID informed: ", id); 22. Print("Result: ", ChartIndicatorAdd(id, 0, iCustom(NULL, NULL, "::" + def_Resource))); 23. ChartApplyTemplate(id, "Testing.tpl"); 24. ChartRedraw(id); 25. } 26. //+----------------+ 27. public : 28. //+----------------+ 29. C_Chart() 30. { 31. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 32. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 33. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 34. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 35. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 36. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 37. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 38. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 39. } 40. //+----------------+ 41. ~C_Chart() 42. { 43. ObjectDelete(0, m_szObject); 44. ChartRedraw(); 45. } 46. //+----------------+ 47. void Resize(void) 48. { 49. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 50. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 51. ChartRedraw(); 52. } 53. //+----------------+ 54. void AddIndicator() 55. { 56. Proc(ObjectGetInteger(0, m_szObject, OBJPROP_CHART_ID)); 57. Proc(ChartID()); 58. } 59. //+----------------+ 60. }*gl_Chart = NULL; 61. //+------------------------------------------------------------------+ 62. int OnInit(void) 63. { 64. gl_Chart = new C_Chart(); 65. (*gl_Chart).AddIndicator(); 66. 67. return INIT_SUCCEEDED; 68. }; 69. //+------------------------------------------------------------------+ 70. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 71. { 72. return rates_total; 73. }; 74. //+------------------------------------------------------------------+ 75. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 76. { 77. switch(id) 78. { 79. case CHARTEVENT_CHART_CHANGE: 80. if (gl_Chart) (*gl_Chart).Resize(); 81. break; 82. } 83. }; 84. //+------------------------------------------------------------------+ 85. void OnDeinit(const int reason) 86. { 87. delete gl_Chart; 88. }; 89. //+------------------------------------------------------------------+
Код 06
А теперь будьте внимательны, потому что этот момент часто ставит многих в тупик. Единственное изменение, внесённое в код 04 для получения этого кода 06, — это добавление строки 23. Однако вы должны помнить, что произошло при выполнении кода 05. Понять это чрезвычайно важно. Хотя в коде 06 и делается попытка использовать подход, который действительно можно применить, выполнение действий так, как показано здесь, не приведёт к результату, которого вы ожидаете. То есть даже если всё кажется идеальным и полностью работоспособным.
В конечном итоге этот код 06, при всей своей сложности, даст тот же результат, что и значительно более простой код. Это связано с тем, что при его выполнении результат будет таким же, как и при выполнении кода 05. Итак, как же нам сделать так, чтобы всё работало, как нужно? Как можно добавить индикатор на график, созданный и поддерживаемый объектом OBJ_CHART? Итак, вот та запутанная часть, которую многие не понимают. И ответ таков:
Не пытайтесь создать что-то универсальное. Чтобы получить конкретные результаты, вам придётся использовать конкретные механизмы.
И что же означает эта фраза? Она говорит нам, что для каждого типа проблемы нам нужно решение, способное решить именно эту проблему — и только её. Многие новички привыкли копировать и вставлять фрагменты кода в свои программы, и это усугубилось с использованием искусственного интеллекта для программирования. Хотя это и может сработать, зачастую такой подход с треском проваливается, когда нам нужно, чтобы всё работало очень специфическим образом.
По этой причине, несмотря на энтузиазм многих людей по поводу так называемых искусственных интеллектов, помогающих в программировании, такие системы мало пригодны. Это связано с тем, что они, как правило, создают универсальные решения. А когда нам нужно что-то конкретное, такие системы оказываются не более чем пустой тратой времени. Если вы не понимаете, какую концепцию нужно использовать, вы окажетесь в тупике, надеясь, что эти модели GPT сгенерируют подходящий ответ и решат вашу проблему. Но этого не произойдёт.
Итак, как же нам решить вопрос, поставленный в этой статье? Чтобы добиться этого, нам придётся отказаться от надежды использовать какой-либо GPT для создания кода и посвятить себя изучению и практике программирования, как это делалось раньше. Я знаю, что это может показаться утомительной и скучной задачей. Но в конце концов вы сможете найти решение, пока остальные будут по-прежнему пытаться понять, почему оно работает или почему не работает.
Проблема кода 06 не в использовании самой двадцать третьей строки. Проблема заключается во взаимодействии кода с самим собой. Но как такое возможно? Теперь я и правда запутался. Итак, уважаемый читатель, почему выполняется строка 57 этого кода 06? Она выполняется именно для того, чтобы в строке 22 встроенный индикатор был добавлен на график. Однако, если в этом и состоит цель, то почему, даже зная, что встроенный индикатор будет добавлен на график, нам нужно также выполнить строку 23? В этом нет особого смысла. Строка 22 выполняется корректно именно потому, что мы добавляем в код встроенный ресурс, в данном случае индикатор CODE 02, как вы можете увидеть в строке 11. Ещё раз исхожу из того, что вы прочитали и поняли предыдущие статьи, чтобы понимать, о чём я сейчас говорю.
Зная, что строка 22 будет корректно выполняться при применении этой логики к основному графику, нет смысла использовать строку 23 для применения шаблона, так как она удалит код с графика и не позволит графику, созданному объектом OBJ_CHART, получить шаблон, который мы хотим использовать, не так ли? Итак, чтобы просто показать, что этот код 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 02.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. class C_Chart 14. { 15. private : 16. //+----------------+ 17. string m_szObject; 18. //+----------------+ 19. void Proc(long id) 20. { 21. if (!ChartIndicatorAdd(id, 0, iCustom(NULL, NULL, "::" + def_Resource))) 22. ChartApplyTemplate(id, "Testing.tpl"); 23. ChartRedraw(id); 24. } 25. //+----------------+ 26. public : 27. //+----------------+ 28. C_Chart() 29. { 30. m_szObject = "Demo_" + (string)ObjectsTotal(0, -1, -1); 31. ObjectCreate(0, m_szObject , OBJ_CHART, 0, 0, 0); 32. ObjectSetString(0, m_szObject, OBJPROP_SYMBOL, _Symbol); 33. ObjectSetInteger(0, m_szObject, OBJPROP_XDISTANCE, 0); 34. ObjectSetInteger(0, m_szObject, OBJPROP_YDISTANCE, 0); 35. ObjectSetInteger(0, m_szObject, OBJPROP_PERIOD, PERIOD_M15); 36. ObjectSetInteger(0, m_szObject, OBJPROP_DATE_SCALE, false); 37. ObjectSetInteger(0, m_szObject, OBJPROP_PRICE_SCALE, false); 38. } 39. //+----------------+ 40. ~C_Chart() 41. { 42. ObjectDelete(0, m_szObject); 43. ChartRedraw(); 44. } 45. //+----------------+ 46. void Resize(void) 47. { 48. ObjectSetInteger(0, m_szObject, OBJPROP_XSIZE, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2); 49. ObjectSetInteger(0, m_szObject, OBJPROP_YSIZE, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS)); 50. ChartRedraw(); 51. } 52. //+----------------+ 53. void AddIndicator() 54. { 55. Proc(ObjectGetInteger(0, m_szObject, OBJPROP_CHART_ID)); 56. Proc(ChartID()); 57. } 58. //+----------------+ 59. }*gl_Chart = NULL; 60. //+------------------------------------------------------------------+ 61. int OnInit(void) 62. { 63. gl_Chart = new C_Chart(); 64. (*gl_Chart).AddIndicator(); 65. 66. return INIT_SUCCEEDED; 67. }; 68. //+------------------------------------------------------------------+ 69. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 70. { 71. return rates_total; 72. }; 73. //+------------------------------------------------------------------+ 74. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 75. { 76. switch(id) 77. { 78. case CHARTEVENT_CHART_CHANGE: 79. if (gl_Chart) (*gl_Chart).Resize(); 80. break; 81. } 82. }; 83. //+------------------------------------------------------------------+ 84. void OnDeinit(const int reason) 85. { 86. delete gl_Chart; 87. }; 88. //+------------------------------------------------------------------+
Код 07
Посмотрите, что было сделано в коде 07. Раньше функция ChartApplyTemplate выполнялась независимо от того, успешно ли отработала функция ChartIndicatorAdd. Теперь вызов ChartApplyTemplate будет выполняться только в том случае, если ChartIndicatorAdd завершится с ошибкой. Помните, что цель здесь — дидактическая. Поэтому не пытайтесь применять показанное к чему-то более сложному, потому что это не сработает. И я снова подчёркиваю, что каждый случай индивидуален. Однако, несмотря на скептицизм многих относительно того, какой результат мы можем получить, результат оказывается довольно удивительным, как видно из следующей анимации.

Анимация 03
Обратите внимание, что нам действительно удалось добавить индикатор из кода 02 на график объекта OBJCHART. И хотя многие могут подумать, что этот индикатор неработоспособен, он будет отлично работать. Но есть одна деталь:Индикатор, присутствующий на графике, созданном и поддерживаемом объектом OBJ_CHART, НЕ БУДЕТ ОТОБРАЖАТЬСЯ В СПИСКЕ вместе с остальными индикаторами.
В этом можно убедиться, запросив отображение списка индикаторов. В случае анимации 03 список показан на следующем изображении.

Изображение 07
Заключительные замечания
В этой статье мы увидели, что не всё так, как кажется на первый взгляд. Хотя показанное здесь поначалу может показаться весьма сложным, я полагаю, что мне удалось достичь поставленной цели — показать и объяснить, что понимание MQL5 имеет решающее значение для более эффективного использования MetaTrader 5.
Хотя мы можем размещать индикаторы на графике объекта OBJ_CHART, выбранное решение подходит не для всех ситуаций.
В следующей статье мы ещё глубже рассмотрим показанное здесь, что позволит нам создавать ещё более эффективные решения с гораздо меньшими затратами труда.| Файл MQ5 | Описание |
|---|---|
| Indicators\Code 01 | Базовая демонстрация |
| Indicators\Code 02 | Базовая демонстрация |
| Indicators\Code 03 | Базовая демонстрация |
| Indicators\Code 04 | Базовая демонстрация |
| Indicators\Code 05 | Базовая демонстрация |
| Indicators\Code 06 | Базовая демонстрация |
| Indicators\Code 07 | Базовая демонстрация |
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/17470
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Инжиниринг признаков для машинного обучения (Часть 4): Реализация временных признаков в MQL5
Альтернатива временным барам (Часть 3): Индикатор в подокне для отображения альтернативных баров в MQL5
Индикатор средней цены входа маркет-мейкера
Нейронная сеть на практике: Ручное обратное распространение
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования