От базового к среднему уровню: Ресурсы
Введение
В предыдущей статье «От базового к среднему уровню: Подокна (IV)» мы увидели, что не всё так, как мы обычно себе представляем, пока не увидим, как всё работает на практике. Нередко кто-то считает, что уже что-то знает, просто потому что владеет основами, но это большая ошибка.
Прежде чем продолжить разговор о подокнах, нам нужно сделать небольшую паузу и затронуть другую, не менее важную тему. Учитывая характер того, что мы рассмотрим в этой статье, прошу вас уделить максимум внимания тому, что будет рассмотрено далее, поскольку неправильное понимание этого материала наверняка приведёт к серьёзным проблемам в будущем.
То, что мы рассмотрим в этой статье, позволяет создавать приложения, которые в противном случае вам пришлось бы распространять в виде набора из нескольких файлов — со всеми неизбежными неудобствами. Вместо этого мы сможем распространять их в виде одного исполняемого файла, что значительно упростит сопровождение.
Ресурсы
Одно из самых трудных для понимания понятий для новичка — это так называемые ресурсы. Я имею в виду ресурсы в программном смысле, а не аппаратные ресурсы. Дело в том, что понятие ресурса не имеет особого смысла, когда о нём вдруг начинают говорить без подготовки. И ещё меньше смысла в нём становится, когда его объясняют поверхностно и наспех, как это обычно и бывает.
Чтобы правильно разобраться в этой теме, нам сначала нужно понять несколько понятий, представленных в предыдущих статьях. Поэтому, если вам трудно понять какую-то конкретную деталь, прочитайте предыдущие статьи.
Теоретически ресурс — это довольно простое понятие: ресурсом считается всё, что вы используете в программе, а также всё, что необходимо для запуска самой программы. Именно в этом и заключается базовая идея. Однако как программист вы должны лучше понимать некоторые детали, чтобы иметь возможность добавлять новые элементы в уже готовую программу.
Для начала следует отметить, что в программном обеспечении существует два основных типа ресурсов. Первый относится к внутренним ресурсам кода, а второй — к внешним ресурсам того же кода. Внутренний ресурс — это любой элемент, включённый в исполняемый файл. Напротив, внешний ресурс — это то, что программе придётся искать где-то вне области памяти, где находится сам исполняемый файл. Именно правильное понимание этого различия поначалу и может вызывать затруднения. Это связано с тем, что многие новички оказываются в полном замешательстве, когда видят, что один исполняемый файл способен выполнять определённые действия, по-видимому, не требуя никаких внешних файлов или данных, тогда как другой требует большого объёма данных, хранящихся в разных местах и во всевозможных внешних файлах.
По этой причине многие новички склонны думать, что код, дающий тот же результат, что и другой, обязательно лучше или хуже. На самом деле разница между этими двумя подходами зависит от того, как спроектирована система, которую предстоит запускать. В зависимости от ситуации целесообразнее использовать тот или иной подход. Кроме того, есть ещё один вопрос, который, возможно, и станет решающим фактором при окончательном выборе модели. И вот здесь всё начинает становиться интереснее, поскольку этот вопрос связан с будущими обновлениями самого приложения, которое вы создаёте.
Возможно, вам покажется, что эта тема немного скучна и не имеет особого значения. Тем не менее это становится чрезвычайно важным, когда мы сталкиваемся с определёнными типами проблем. Предположим, вы хотите создать советник, который будет выполнять автоматические операции с использованием какого-либо индикатора. Многие могут подумать, что единственный способ создать нечто подобное — использовать два или более исполняемых файла: один для самого советника и один или несколько исполняемых файлов для индикаторов, которые будут использоваться. В принципе, это был бы правильный и наиболее подходящий способ. Однако, когда вы решите распространять свою систему, возникнет небольшая проблема: как гарантировать, что пользователь не удалит индикатор, который использует советник?
Один из способов избежать этого — объединить всю систему в один файл, которым в данном случае стал бы сам файл советника. "Но подождите минуту. Разве вы только что не сказали, что советнику понадобятся индикаторы?" Да, ему понадобятся индикаторы, которые вы создадите. "Тогда как это вообще может работать, если мы не распространяем эти индикаторы?" Что ж, именно здесь всё начнёт обретать смысл, уважаемый читатель. Существует способ встроить индикаторы или любые другие элементы в исполняемый файл советника. Таким образом, ни пользователь, ни оператор не смогут повлиять на анализ, для которого на самом деле был разработан советник, поскольку, в принципе, ни пользователь, ни оператор даже не будут знать о существовании этих индикаторов и о том, что они используются.
Я говорю «в принципе», потому что, возможно, вы захотите, чтобы они отображались на графике. В таком случае пользователь увидит их, хотя и не поймёт, почему при размещении этого советника на графике начинают появляться эти линии. И когда советник удаляется, эти же линии исчезают с графика вместе с ним.
Даже если индикаторы не будут отображаться на графике, как мы уже видели в предыдущих статьях, они всё равно будут появляться в списке индикаторов. Однако ни пользователь, ни оператор не смогут узнать, откуда взялся этот индикатор или эти индикаторы, поскольку они просто появляются и исчезают каждый раз, когда советник добавляется на график или удаляется с него.
Хм, вот это мне действительно кажется довольно интересным. Значит, вместо того чтобы отправлять коллеге кучу файлов, мне нужно отправить ему только один. И в этом файле будет содержаться полная, заранее запрограммированная конфигурация того, что нужно сделать. Мне нравится эта идея.
На самом деле именно в этом и заключается основной принцип. Однако этим дело не ограничивается. Мы можем включить как данные, которые нельзя будет изменять, так и данные, которые можно будет изменять. И всё это с учётом ряда критериев, которые вы, как программист, заранее определите, чтобы установить, что каждый пользователь может изменять, а что — нет. И если оператор или пользователь столкнётся с какой-либо проблемой в настройках и обратится к вам за помощью в её устранении, вам нужно будет просто сказать: удалите программу, удалите все файлы настроек, а затем установите её заново. Сделайте это, и всё вернётся в норму. Хотя поначалу пользователю эта идея может не понравиться, это послужит ему уроком, и в следующий раз он будет осторожнее при изменении настроек. Или, по крайней мере, чтобы он был менее любопытным и не трогал то, что не следует. :-)
Итак, теперь, когда сама концепция уже как следует представлена, можно подумать о том, как начать работать с тем, что мы называем ресурсами. Для начала забудьте о том, что будете работать с одним или несколькими файлами. Работа с ресурсами требует терпения и дисциплины. Без них весь ваш код превратится в настоящий хаос. В самой документации MQL5 вы можете получить представление о том, как можно работать с ресурсами. Однако то, что там объясняется, довольно поверхностно. И даже этой статьи не хватит, чтобы показать всё, что на самом деле можно сделать. По мере публикации новых статей вы увидите, что эта тема действительно очень интересна и с ней довольно увлекательно работать.
Итак, давайте начнём с очень простого примера, чтобы вы поняли, как эта концепция реализуется на практике. В этом примере используются два файла. Первый — текстовый файл, и не беспокойтесь о его содержимом. Сосредоточьтесь на том, чтобы понять, как здесь применяется концепция ресурса. Текстовый файл показан ниже. 
Изображение 01
На изображении 01 зелёным выделены имя и расположение текстового файла. Жёлтым показано точное содержимое этого файла. Итак, что мы будем с этим делать? Спокойно, сосредоточьтесь и будьте внимательны. Теперь переходим к той части, которую нужно выполнить в MQL5. Она показана ниже.01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #resource "MyFile.txt" as string Antoine; 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. Print(Antoine); 09. } 10. //+------------------------------------------------------------------+
Код 01
Когда вы запустите этот код в терминале MetaTrader 5, в результате увидите то, что показано в следующей анимации.

Анимация 01
Теперь обратите внимание, что область, выделенная жёлтым цветом на изображении 01, выводится в терминале. Однако здесь происходит нечто, чего вы, возможно, не понимаете. Или, возможно, вы думаете, что происходит одно, тогда как на самом деле происходит совсем другое. Поэтому внимательно следите за тем, что я сейчас объясню, потому что позже, когда мы будем использовать такой подход, я больше не стану это повторять.
При компиляции этого кода 01 четвёртая строка берёт всё содержимое файла, показанного на изображении 01, и встраивает его в исполняемый файл. А теперь подумайте вот о чём: для исполняемого файла содержимое этой четвёртой строки — это огромный массив со всем тем, что раньше находилось в файле MyFile.txt. С этого момента всё это содержимое будет находиться в этом массиве. Чтобы упростить всё, мы используем тип string для представления данных, хранящихся в этой огромной константе с именем Antoine. Возвращаясь к тому, что вы наверняка уже знаете и умеете интерпретировать, то, что мы делаем в коде 01, эквивалентно тому, что показано в приведённом ниже коде 02.01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. const string Antoine = 05. "E foi então que apareceu a raposa:\n\r" \ 06. "- Boa dia, disse a raposa.\n\r" \ 07. "- Bom dia, respondeu polidamente o principezinho, que se voltou, mas não viu nada.\n\r"\ 08. "- Eu estou aqui, disse a voz, debaixo da macieira…\n\r" \ 09. "- Quem és tu? perguntou o principezinho. Tu és bem bonita…\n\r" \ 10. "- Sou uma raposa, disse a raposa.\n\r" \ 11. "- Vem brincar comigo, propôs o principezinho. Estou tão triste…\n\r" \ 12. "- Eu não posso brincar contigo, disse a raposa.\n\r"; 13. //+------------------------------------------------------------------+ 14. void OnStart(void) 15. { 16. Print(Antoine); 17. } 18. //+------------------------------------------------------------------+
Код 02
"Хм, я не понимаю. Как это так? На мой взгляд, код 01 гораздо проще и создаёт гораздо меньше сложностей, чем код 02. И всё же вы говорите мне, что код 01 эквивалентен этому коду 02. По-моему, в этом нет ни малейшего смысла".
Именно по этой причине я так долго ждал, прежде чем объяснить эту тему. Поначалу подобные вещи кажутся совершенно бессмысленными. И это при том, что мы только начинаем видеть, что на самом деле можем сделать. Тем не менее очень важно правильно понять эту начальную часть. Также важно понять всё, что было объяснено в предыдущих статьях, потому что по мере того, как мы начнём добавлять сюда новые данные, всё будет становиться всё более запутанным. Как бы странно это ни казалось, на самом деле происходит следующее: четвёртая строка кода 01 фактически создаёт эквивалент четвёртой строки кода 02. И вот в этом-то и заключается самое интересное: код 01 гораздо легче понять, чем код 02, особенно если задача состоит в том, чтобы включить в исполняемый файл какие-нибудь более сложные данные.
Так что будьте внимательны, потому что это важно понять. Мы можем включить в исполняемый файл данные любого типа. Любого типа — от исполняемого содержимого MetaTrader 5 до изображений, звуков или любых других элементов, которые вам нужны или которые вы хотите туда включить. Именно такие элементы и называются программными ресурсами.
Подождите минутку. Вы хотите сказать, что с помощью этой системы, которую мы только что рассмотрели, мы можем встроить один исполняемый файл в другой? Но это не сработает. По крайней мере, я не понимаю, как это могло бы сработать. Ведь, в принципе, включение чего-либо в индикатор, скрипт или советник не обязательно делает это исполняемым объектом. По крайней мере, именно так я себе представляю, как это работает.
Что ж, поскольку вы всё ещё невинны в этом мире и не знаете некоторых уловок, которые мы, программисты, можем использовать, пришло время осознать, что мир — это не усыпанная розами дорога, где всё красиво и чудесно и все желают другим только самого лучшего. Потому что на самом деле всё работает не совсем так.
Чтобы понять, что мы собираемся начать делать, нам придётся вернуться к предыдущей статье. Там мы рассмотрели, как можно сделать так, чтобы один индикатор загружал другой на график. Таким образом, мы могли бы изменять или контролировать определённые элементы графика. А теперь обратите внимание на следующее: чтобы система не давала сбоев, было необходимо, чтобы оба кода были скомпилированы и находились в соответствующих местах. Это факт, и если вы изучили то, что там было показано, то прекрасно понимаете, о чём я говорю. Однако не всегда мы хотим, чтобы всё работало именно так, поскольку в этом случае система стала бы гораздо менее стабильной, так как её работа зависела бы от наличия этих исполняемых файлов, что могло бы сделать работу с ней для конечного пользователя довольно неприятной. Но что было бы, если бы мы применили концепцию, которую рассматриваем в этой статье? Как в действительности выглядела бы система? Чтобы ответить на этот вопрос, вернёмся к тому, что мы видели ранее.
Итак, ниже приведены исходные коды, использованные в той статье.01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_separate_window 07. #property indicator_height 50 08. #property indicator_plots 0 09. //+------------------------------------------------------------------+ 10. int OnInit(void) 11. { 12. IndicatorSetString(INDICATOR_SHORTNAME, "Test 123."); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Код 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. int OnInit(void) 10. { 11. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 12. ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01")); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Код 04
Хорошо, идея в том, чтобы этот код 04 загружал и выполнял индикатор, показанный в коде 03. Думаю, к этому моменту все уже поняли, что происходит. Обратите внимание, что именно в двенадцатой строке этого кода 04 выполняется загрузка. Там мы указываем имя и путь к файлу, который будет использоваться. Теперь обратите самое пристальное внимание на детали, потому что дьявол кроется именно в них.
Первое, на что следует обратить внимание, — это расположение самого исполняемого файла, который мы собираемся встроить в другой файл, который создаём. Обратите особое внимание на этот момент, потому что, если вы его не поймёте, в итоге у вас возникнет полная путаница. В двенадцатой строке кода 04 мы указываем путь к исполняемому файлу. Однако, когда мы собираемся использовать его как ресурс, нам нужно указывать путь в относительном, а не в абсолютном виде. Хм, а что такое относительный и абсолютный пути? Я никогда об этом не слышал. Итак, дорогой читатель, MetaTrader 5 использует абсолютный путь для доступа к определённым файлам в песочнице.В статье «От начального к среднему уровню: Навигация по песочнице» мы уже говорили об этой песочнице; за подробностями обратитесь к той статье. Когда вы используете абсолютный путь, его нужно указывать относительно самой песочницы. Однако компилятор использует песочницу не так, как MetaTrader 5. Чтобы упростить объяснение, представьте себе следующее: MetaTrader 5 находит исполняемый файл, исходя из положения внутри корня песочницы. А компилятор, напротив, использует путь относительно исходного файла кода, независимо от того, где именно он находится внутри песочницы.
Да уж, вся эта история с песочницей и относительными и абсолютными путями кажется очень запутанной. Разве нельзя как-нибудь подытожить всё это и объяснить попроще? К сожалению, нет. Вам нужно понять этот вопрос, прежде чем пытаться понять, как будет изменён код 04. Итак, вернёмся к объяснению. Мы знаем, что код 03 НАХОДИТСЯ В ТОМ ЖЕ КАТАЛОГЕ, что и код 04. Эта информация очень важна во время компиляции и интеграции. Если бы код 03 находился в другом каталоге, нам пришлось бы указать это компилятору, задав полный путь, по которому он смог бы его найти. Без этой информации компилятор НЕ СМОЖЕТ преобразовать код 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_Resource "Code 01.ex5" 10. //+------------------------------------------------------------------+ 11. int OnInit(void) 12. { 13. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 14. ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01")); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. return rates_total; 22. }; 23. //+------------------------------------------------------------------+
Код 05
Единственное изменение, которое мы здесь внесли, — это добавление девятой строки в код 05. Так что не забывайте:
Индикатор, показанный в коде 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 01.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. int OnInit(void) 14. { 15. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 16. ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01")); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 22. { 23. return rates_total; 24. }; 25. //+------------------------------------------------------------------+
Код 06
Теперь у нас есть нечто действительно интересное. И всё это лишь потому, что мы добавили в предыдущий код эту одиннадцатую строку. Эта одиннадцатая строка интегрирует в код 06 код, заданный в девятой строке, тем самым превращая его в ресурс самого кода 06. И это ещё не всё. На самом деле всё гораздо, гораздо интереснее. Так что давайте разберём всё по порядку, потому что здесь есть одна практическая сложность, и позже я объясню, как её решить; именно она выводит многих из себя, когда один исполняемый файл встраивают в другой.
В предыдущей статье я упомянул, что, чтобы поместить на график индикатор, показанный в коде 03, нужно убедиться, что он скомпилирован и доступен для запуска. Это факт. Так вот, когда вы скомпилируете этот код 06, произойдёт нечто, похожее на магию. Дело в том, что, ЕСЛИ исполняемый файл НЕ СУЩЕСТВУЕТ, компилятор создаст исполняемый файл, соответствующий коду, заданному в строке 09. "А? Как? Что это ещё за безумие? Вы хотите сказать, что, если у нас просто есть код и мы пытаемся скомпилировать другой код, которому нужен этот исполняемый файл, компилятор сам создаст его для нас?" Да, именно это и произойдёт. Сообщение, которое выводит компилятор, довольно длинное. И поскольку ширина изображений ограничена, полностью оно не отобразится. Однако нужный нам фрагмент выделен ниже. 
Изображение 02
В этом сообщении компилятор сообщает нам, что не нашёл исполняемый файл и пытается его скомпилировать. Поскольку ему это удалось, мы получаем сообщение, показанное во второй строке изображения 02. Если бы произошла какая-либо ошибка, сообщение было бы другим, как показано ниже. 
Изображение 03
Обратите внимание, что теперь компилятор сообщает о наличии ошибки компиляции. Однако ошибка связана именно с тем, что компилятор НЕ СМОГ скомпилировать код, необходимый для разрешения зависимости в строке одиннадцать. То есть именно строку, в которой должна была выполняться интеграция. Это может быть связано с тем, что код, который нужно интегрировать, содержит какую-то ошибку, либо с проблемой относительного пути к самому интегрируемому коду. В любом случае окончательный исполняемый файл не будет создан, пока вы не устраните проблему, препятствующую интеграции.
Думаю, я уже начинаю это понимать. Но что произойдёт, если исполняемый файл, который нужно интегрировать, уже существует? Компилятор снова его скомпилирует? В таком случае повторная компиляция выполняться не будет. Даже если код был изменён, компилятор по-прежнему будет использовать ранее скомпилированный исполняемый файл. По этой причине стоит внимательно следить за тем, что вы делаете; в противном случае вы можете изменить код, который должен быть интегрирован, а при запуске обнаружить, что выполняется его предыдущая версия.
В любом случае стоит внимательно следить за сообщениями, которые выводит компилятор. Если исполняемый файл, который нужно интегрировать, уже существует, мы получим результат, подобный показанному на следующем изображении. 
Изображение 04
Обратите внимание, что это изображение 04 очень похоже на то, что вы увидели бы, если бы скомпилировали код 04. Однако конечный результат может сильно отличаться от того, что получается при выполнении. Подробности оставим на потом, поскольку эта тема требует времени, а в этой статье я всё ещё хочу показать, как будет решаться интеграция во время выполнения. Пока что мы лишь создали эти коды и встроили один в другой в виде ресурсов. Итак, пришло время посмотреть, как превратить всё это в нечто действительно исполняемое.
А теперь переходим ко второй части, которая может оказаться довольно запутанной. Чтобы код заработал, нам нужно проделать один манёвр. Этот манёвр основан на использовании специального оператора, который нужно добавить в код вполне определённым образом. Этот оператор используется для разрешения области видимости непосредственно в самом коде. Да уж, всё это становится всё более запутанным и сложным. Что это ещё за оператор разрешения области видимости? Что ж, подробнее об этом вы можете прочитать в самой документации MQL5, в этом разделе. Простите, что я не использую более подходящее название.
Однако в самой документации показаны лишь основные аспекты этого оператора разрешения области видимости. Что нас действительно интересует здесь и далее, так это то, что нам нужно каким-то образом указать нашему новому коду, как выполнять встроенный в него код. То есть нам нужно сделать так, чтобы шестнадцатая строка кода 06 выполняла код, который теперь является внутренним ресурсом только что созданного нами исполняемого файла, вместо того чтобы по-прежнему пытаться выполнить тот внешний код, как это было раньше и как это происходит до сих пор именно из-за того, как всё ещё написана шестнадцатая строка.
Ладно, думаю, я понимаю, к чему вы клоните. Но теперь я спрошу вас: как мы собираемся это сделать? Потому что, с моей точки зрения, у нас нет никаких средств доступа к этому ресурсу, который встраивается в исполняемый файл с помощью одиннадцатой строки этого кода 06. И есть ещё кое-что. Я ознакомился с документацией о том, как использовать код в качестве ресурса, и там показано нечто совершенно иное, чем то, что вы делаете в этом коде 06. Поэтому, на мой взгляд, даже если эта интеграция и выполняется, на деле это не сделает встроенный код работоспособным. Если, конечно, у вас нет туза в рукаве, который позволит вам это сделать. Но в таком случае я понятия не имею, как мог бы сработать подобный трюк.
Что ж, пока я не буду вдаваться в подробности о том, почему то, что мы собираемся сделать, работает. Но уверяю вас: всё, что нам нужно будет сделать, — это изменить шестнадцатую строку кода 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 01.ex5" 10. //+------------------------------------------------------------------+ 11. #resource def_Resource 12. //+------------------------------------------------------------------+ 13. int OnInit(void) 14. { 15. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 16. ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "::" + def_Resource)); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 22. { 23. return rates_total; 24. }; 25. //+------------------------------------------------------------------+
Код 07
Благодаря этому небольшому изменению нам наконец удастся создать нечто действительно интересное. И хотя вам может показаться, что нам по-прежнему понадобится исполняемый файл, созданный на основе кода 03, показанного в этой статье, этот файл можно будет удалить сразу после компиляции кода 07. Более того, я даже советую вам удалить его сразу после этого; причину я объясню в другой статье. В любом случае этот код 07 сделает индикатор, встроенный в конечный исполняемый файл, полностью работоспособным. Это связано с тем, что сам индикатор, показанный в коде 03, теперь является частью кода 07. Таким образом, достаточно будет передать этот исполняемый файл, соответствующий коду 07, и поделиться им с вашими друзьями и коллегами, не беспокоясь о наличии или отсутствии исполняемого файла, показанного в коде 03.
Итак, теперь, запустив этот код 07, вы увидите то, что показано в следующей анимации. 
Анимация 02
Хм, не вижу, чтобы происходило что-то особенное. Такого поведения уже удавалось добиться ранее. И всё же я не уверен, не пытаетесь ли вы обмануть меня этой историей о том, что можете встраивать и запускать код напрямую только потому, что изменили шестнадцатую строку. По-моему, вы шутите. Не может быть, чтобы эта шестнадцатая строка кода 07 действительно запускала индикатор, встроенный в исполняемый файл.
Что ж, среди нас есть скептик. Что ж, давайте проверим, действительно ли код 07 может делать то, о чём здесь говорится. Для этого достаточно открыть список индикаторов, присутствующих на графике, и проверить информацию, которая там отображается. Сделав это, мы увидим нечто похожее на изображение ниже. 
Изображение 05
Обратите внимание, что на этом изображении я кое-что выделил. Итак, теперь вы убедились, что код 07 действительно может запускать встроенный в него индикатор? В любом случае лучше всего, чтобы вы увидели это собственными глазами и убедились в том, что здесь было показано.
Заключительные замечания
В этой статье вы познакомились с концепцией, которая во многих случаях может оказаться чрезвычайно полезной и значительно упростить обмен вашими приложениями и проектами. Хотя эту концепцию непросто полностью объяснить в одной статье, уже изложенного здесь нам хватит, чтобы в ближайшем будущем сделать многое другое, включая и то, что иначе было бы невозможно. Именно для этого и публикуется эта статья: чтобы у вас был вспомогательный материал и начальная база для изучения.
Поэтому моя рекомендация такова: спокойно изучите концепцию, изложенную здесь. По мере публикации новых статей вы заметите, что понимание того, что было изложено здесь, очень поможет вам лучше понять и усвоить следующий материал. Так что максимально используйте коды, включённые во вложение, и до встречи в следующей статье.| Файл MQ5 | Описание |
|---|---|
| Indicators\Code 01 | Базовая демонстрация |
| Indicators\Code 02 | Базовая демонстрация |
| Indicators\Code 03 | Базовая демонстрация |
| Scripts\Code 01 | Базовая демонстрация |
| Scripts\Code 02 | Базовая демонстрация |
| Scripts\MyFile.txt | Простой пример файла |
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/17253
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
От начального к среднему уровню: Объекты и подокна (I)
Нейронная сеть на практике: Функции активации
Создание пользовательского тикового графика в MQL5
Нейросети в трейдинге: Диффузионная генерация торговых планов (DPCC)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования