Виды переменных
Прикладная программа на языке MQL4 может содержать десятки и сотни переменных. Очень
важным свойством любой переменной является возможность использовать в программе её
значение. Ограничение такой возможности непосредственно связано с областью видимости
переменных.
Область видимости переменной -
место в программе, в котором доступно значение
переменной. Каждая переменная имеет свою область видимости.
По области видимости в
языке MQL4 различают два вида переменных: локальные и глобальные.
Локальные и глобальные переменные
Локальная переменная - переменная, объявленная внутри какой-либо функции. Областью видимости локальных
переменных является тело функции, в которой эта переменная объявлена. Локальная
переменная может быть проинициализирована константой или выражением, соответствующими
ее типу.
Глобальная переменная - переменная, объявленная за пределами всех функций. Областью видимости глобальных
переменных является вся программа. Глобальная переменная не локализована ни на
каком уровне. Глобальная переменная может быть проинициализирована только соответствующей
ее типу константой (но не выражением). Инициализация глобальных переменных производится
однократно перед началом исполнения специальных функций.
Если управление в программе находится внутри какой-либо функции, то значения локальных
переменных, объявленных в другой функции не доступны. Значение любой глобальной
переменной доступно из любой специальной и пользовательской функции. Рассмотрим
простой пример.
|
Задача 22. Составить программу, считающую тики. |
Алгоритм решения Задачи 22, использующий глобальную переменную (countticks.mq4):
int Tick;
int start()
{
Tick++;
Comment("Поступил тик № ",Tick);
return;
}
В этой программе используется всего одна глобальная переменная Tick. Она является
глобальной, т.к. объявлена за пределами описания функции start(). Это значит, что
от тика к тику значение этой переменной будет сохраняться. Рассмотрим подробности
исполнения программы.
В разделе Специальные функции мы рассматривали критерии, при которых запускаются на исполнение специальные функции.
Кратко напомним, что специальная функция start() в экспертах запускается на выполнение
клиентским терминалом в момент поступления очередного тика. В момент присоединения эксперта
к окну финансового инструмента произойдут следующие события:
1. Объявление глобальной переменной Tick. Эта переменная не проинициализирована
константой, поэтому её значение на этом этапе равно нулю.
2. Управление удерживается в клиентском терминале до поступления тика.
3. Поступил тик. Управление передаётся специальной функции start().
3.1. В рамках исполнения специальной функции start() управление передаётся оператору:
Tick++;
В результате исполнения этого оператора значение переменной Tick увеличится на 1
(целую единицу).
3.2. Управление передаётся оператору:
Comment("Поступил тик № ",Tick);
Исполнение стандартной функции Comment() приведёт к появлению сообщения:
3.3. Управление передаётся оператору:
return;
В результате его исполнения специальная функция start() заканчивает свою работу,
управление передаётся клиентскому терминалу. При этом глобальная переменная продолжает
своё существование, её значение сохраняется равным 1.
Далее события будут повторяться, начиная с пункта 2. Переменная Tick снова будет
участвовать в вычислениях, однако на втором тике, в момент запуска на исполнение
функции start(), её значение равно 1, поэтому результатом исполнения оператора:
Tick++;
будет новое значение переменной Tick - оно увеличится на 1 и теперь будет равно
2, поэтому исполнение функции Comment() приведёт к появлению сообщения:
Таким образом, значение переменной Tick будет увеличиваться на 1 при каждом запуске
специальной функции start(), т.е. на каждом тике. Решение подобных задач становится
возможным только в случае использования переменных, сохраняющих своё значение после
выхода из функции (в данном случае использована глобальная переменная). Использовать
для той же цели локальные переменные бессмысленно: локальная переменная теряет
своё значение по окончании исполнения функции, в которой она объявлена.
В этом очень легко убедиться, запустив на выполнение эксперт, в котором
переменная Tick открыта как локальная переменная (т.е. программа содержит
алгоритмическую ошибку):
int start() // Специальная функция start()
{
int Tick; // Локальная переменная
Tick++; // Счётчик тиков
Comment("Поступил тик № ",Tick); // Сообщение, содержащее номер
return; // Оператор выхода из start()
}
С точки зрения синтаксиса в представленном коде ошибок нет. Эта программа
может быть успешно скомпилирована и запущена на исполнение. И она будет работать,
однако всё время будет сообщать один и тот же результат:
И это вполне естественно, потому что переменная Tick будет инициализирована нолём
в начале исполнения специальной функции start() при каждом её запуске. Последующее
увеличение этого значения на единицу будет приводить лишь к тому, что к моменту
сообщения значение Tick всякий раз будет равно 1.
Статические переменные
На физическом уровне локальные переменные представлены во временной области памяти
соответствующей функции. Существует способ расположить переменную, объявленную
внутри функции, в постоянной памяти программы. Для этого при объявлении переменной
перед типом переменной необходимо указать модификатор static:
static int Number; // Статическая переменная целого типа
Ниже представлено решение задачи 22 с использованием статической переменной
(эксперт
staticvar.mq4):
int start()
{
static int Tick;
Tick++;
Comment("Поступил тик № ",Tick);
return;
}
Статические переменные инициализируются однократно. Каждая статическая переменная
может быть проинициализирована соответствующей ее типу константой (в отличие от
простой локальной переменной, которая может быть проинициализирована любым выражением).
Если явной инициализации нет, то статическая переменная инициализируется нулем.
Статические переменные хранятся в постоянной области памяти программы, их значения
не теряются при выходе из функции. Вместе с тем, статические переменные имеют ограничение,
свойственное локальным переменным - областью видимости статической переменной остаётся
функция, внутри которой эта переменная объявлена, в отличие от глобальных переменных,
значение которых доступно из любого места программы.
Легко увидеть, что программы
countticks.mq4 и staticvar.mq4 дают одинаковый результат.
Все массивы являются статическими изначально, т.е. имеют вид static, даже если
при инициализации это явно не указано (см. Массивы).
Внешние переменные
Внешняя переменная - это переменная, значение которой доступно из окна свойств программы. Внешняя
переменная объявляется за пределами всех функций и является глобальной, область
её видимости - вся программа. При объявлении внешней переменной перед типом её
значения необходимо указать модификатор extern:
extern int Number; // Внешняя переменная целого типа
Внешние переменные указываются в головной части программы, а именно перед
любой функцией, в которой имеется обращение к внешней переменной. Использование внешних переменных очень удобно, если время от времени возникает необходимость
запустить программу на выполнение с иными значениями переменных.
|
Задача 23. Составить программу, в которой реализуются следующие условия: если
цена достигла некоторого уровня Level и опустилась ниже этого уровня на n пунктов,
то один раз сообщить об этом факте трейдеру. |
Понятно, что поставленная задача предполагает необходимость изменения настроек,
ведь сегодня цены уже не такие, как вчера, а завтра будут не такими, как сегодня.
Чтобы обеспечить возможность изменения настроек в эксперте
externvar.mq4
использованы внешние переменные:
extern double Level = 1.2500;
extern int n = 5;
bool Fact_1 = false;
bool Fact_2 = false;
int start()
{
double Price = Bid;
if (Fact_2==true)
return;
if (NormalizeDouble(Price,Digits) >= NormalizeDouble(Level,Digits))
Fact_1 = true;
if (Fact_1 == true && NormalizeDouble(Price,Digits)<=
NormalizeDouble(Level-n*Point,Digits))
My_Alert();
return;
}
void My_Alert()
{
Alert("Условия достигнуты");
Fact_2 = true;
return;
}
В этой программе внешние переменные заданы в строках:
extern double Level = 1.2500;
extern int n = 1;
Значения внешних переменных доступны из окна свойств программы. Ценность этих переменных
состоит в том, что их можно изменить в любой момент - и на этапе присоединения
программы в окно финансового инструмента и в процессе работы программы.
Рис. 54. Окно свойств программы; здесь можно изменить значения переменных.
В момент присоединения программы к окну финансового инструмента в окне настроек
будут указаны те значения переменных, которые указаны в коде программы. Пользователь
может по своему усмотрению изменить эти значения. С момента, когда пользователь
нажал кнопку ОК на панели настроек, программа будет запущена клиентским терминалом
на исполнение. При этом начальные значения внешних переменных будут такими, какими
они были указаны пользователем. В процессе работы программы значения этих переменных
могут быть изменены выполняющейся программой.
Если у пользователя возникла необходимость поменять значения внешних переменных
в процессе работы программы, то необходимо открыть окно настроек и внести изменения.
Очень важно помнить, что панель свойств программы может быть вызвана на экран только
в период, когда программа (эксперт или индикатор) находится в состоянии ожидания
нового тика, т.е. ни одна из специальных функций не исполняется. В течение времени исполнения программы панель её свойств вызвана быть
не может. Поэтому, если программа составлена таким образом, что выполняется долго
(несколько секунд или десятков секунд), то у пользователя могут возникнуть затруднения
с доступом к окну свойств программы. Значения внешних переменных скрипта доступны только
в момент его присоединения к окну финансового инструмента, но в процессе работы
не могут быть изменены. Если окно свойств программы открыто, то эксперт не работает - управление удерживается
клиентским терминалом и не передаётся программе для запуска специальных функций.
|
Обратите внимание, в период, когда открыто окно свойств эксперта и пользователь
принимает решение о значениях внешних переменных, эксперт (или индикатор) не работает.
Установив значения внешних переменных и нажав ОК на панели настроек программы, пользователь
заново запускает её в работу. |
При этом клиентский терминал последовательно запускает на исполнение специальную
функцию deinit(), потом специальную функцию init(), а после этого, в момент поступления
нового тика, запускает на исполнение специальную функцию start(). При исполнении
функции deinit(), завершающей программу, внешние переменные будут иметь значения,
полученные в предыдущем сеансе, т.е. те, которые были у них до открытия панели
настроек эксперта. Перед исполнением специальной функции init() внешние переменные
получат значения, установленные пользователем на панели настроек эксперта, и при
исполнении функции init() внешние переменные будут иметь уже новые, установленные
пользователем значения. Таким образом, новые значения внешних переменных вступают
в силу с момента нового сеанса (init - start - deinit) работы эксперта, начинающегося
с исполнения init().
Факт открытия окна настроек эксперта на значения глобальных переменных не влияет.
В течение всего времени, пока открыто окно настроек, и после его закрытия, глобальные
переменные сохраняют свои значения, имеющиеся на момент, предшествующий открытию
панели настроек эксперта.
В программе externvar.mq4 используются также две глобальные и одна локальная переменные.
bool Fact_1 = false;
bool Fact_2 = false;
double Price = Bid;
Алгоритмически решение задачи выглядит так. Идентифицируются два события: первое
- факт достижения ценой уровня Level, второе - факт того, что сообщение (о снижении
ниже уровня Level на n пунктов) уже выдано. Эти события находят своё отражение
в значениях переменных Fact_1 и Fact_2: если событие ещё не свершилось, то значение
соответствующей переменной равно false, а если свершилось - true. В строках:
if (NormalizeDouble(Price,Digits) >= NormalizeDouble(Level,Digits))
Fact_1 = true;
определяется факт свершения первого события. Стандартная функция NormalizeDouble()
позволяет производить вычисления со значениями действительных переменных с заданной
точностью (соответствующей точности цены финансового инструмента). Если цена оказалась
равной или выше заданного уровня, то факт первого события считается свершившимся,
поэтому глобальной переменной Fact_1 присваивается значение истина. Программа составлена
так, что переменная Fact_1, получив однажды значение истина, никогда не изменит
его на ложь - для этого в программе нет соответствующего кода.
В строках:
if (Fact_1 == true && NormalizeDouble(Price,Digits)<=
NormalizeDouble(Level-n*Point,Digits))
My_Alert();
определяется необходимость выдачи сообщения. Если уже свершилось первое событие
и цена опустилась на n пунктов ниже (меньше или равно) заданного уровня, то необходимо
выдать сообщение - осуществляется вызов на исполнение пользовательской функции My_Alert(). В пользовательской функции, после собственно сообщения, отмечается
факт того, что сообщение уже выдано, путём присвоения глобальной переменной Fact_2
значения true. После этого заканчивает работу пользовательская функция и вслед
за ней и специальная функция start().
После того, как переменная Fact_2 получает значение истина, программа всякий раз
будет завешать свою работу, поэтому сообщение, однажды сделанное, больше никогда
в течение этого сеанса работы программы не повторится:
if (Fact_2==true)
return;
Показательным в этой программе является то, что значения глобальных переменных могут
быть изменены в любом месте (и в специальной функции и в пользовательской) и сохраняются
в течение всего периода работы программы - в период между тиками, после изменения
значений внешних переменных или в результате переключения таймфрейма.
В общем случае значения глобальных переменных могут быть изменены в любой специальной
функции. Поэтому нужно быть особенно внимательным, указывая операторы, изменяющие
значения глобальных переменных в функциях init() и deinit(). Например, если в функции
init() обнулить значение глобальной переменной, то при первом исполнении функции
start() значение этой переменной будет нулевым, т.е. значение этой переменной,
достигнутое в период предыдущего исполнения start(), будет потеряно (т.е. после
изменения внешних настроек программы).