MQL 4: Связь пользовательских программ с внутренними массивами терминала

FXWizard

Гуру форума
MQL 4: Связь пользовательских программ с внутренними массивами терминала

В предыдущей статье мы коснулись вопроса общего взаимодействия исполняющей среды MetaTrader’а с пользовательской программой. Сегодня подробно будут рассмотрены механизмы связывания переменных пользовательских программ со специальными участками памяти - внутренним представлением данных в памяти терминала.
В процессе работы терминал обрабатывает большое количество данных, среди которых хранится вся история по тому или иному инструменту на том или ином временном промежутке. История загружается в терминал из специальных файлов с диска вашего компьютера, а затем в процессе он-лайн работы с MetaTrader’ом подгружается через интернет с сервера брокера. Исторические данные о каждом инструменте хранятся в динамической памяти выполняющегося MetaTrader’а в шести массивах, каждый из которых доступен пользователю из MQL-программы. Не трудно догадаться, что этими шестью массивами являются следующие: массив Open, массив Low, массив High, массив Close, массив Time и массив Volume. Пользовательским программам элементы каждого из перечисленных выше массивов доступны по названию массива и индексу. Индекс - это номер бара для которого запрашиваются данные. Например, если требуется узнать значение цены открытия бара, сформированного три периода назад, то в программе мы напишем Open[3].
 

Вложения

  • mql4_50_1.jpg
    mql4_50_1.jpg
    28,5 КБ · Просмотры: 34

FXWizard

Гуру форума
Часто бывает удобно в программе вместо полных названий массивов использовать их сокращённые синонимы. Так приведённый пример можно записать короче: O[3]. Остальные пять массивов тоже имеют свои синонимы:

использование L эквивалентно Low,
H ~ High,
C ~ Close,
T ~ Time,
V ~ Volume.

На приведённом рисунке наглядно проиллюстрированно то, что каждому бару соответствует набор из шести элементов массивов исторических данных. Индекс ячейки массива соответствует номеру бара. Количество элементов в каждом из массивов данных обусловлено количеством исторических данных, сохранённых в истории MetaTrader’а. Программно узнать его можно, обратившись к встроенной в MQL переменной "Bars". На рисунке, также, видно, что нумерация баров осуществляется с нуля. Для удобства восприятия можно сравнить номер бара с его возрастом во временных периодах текущего графика. Чем старше возраст, тем больше номер бара; текущий же бар ещё не сформировался и имеет нулевой номер, его можно считать "новорожденным". Если бы мы смотрели на годовые графики, то получалась бы полная аналогия номера бара с возрастом человека.

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

Если требуется получить данные из истории другого временного периода или другого инструмента, то следует использовать специально для этого предназначенные функции из арсенала MQL 4.
 

Вложения

  • mql4_50_2.jpg
    mql4_50_2.jpg
    31,7 КБ · Просмотры: 34

FXWizard

Гуру форума
После рассмотрения встроенных в MetaTrader массивов для хранения и работы с историческими данными, перейдём к вопросу взаимодействия пользовательской программы с исполняющей средой MetaTrader’а, а именно - к рассмотрению массивов для отрисовки индикаторов.

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

По аналогии с рассмотренными выше массивами исторических данных, данные для отрисовки пользовательских индикаторов тоже хранятся во внутренних массивах, выделенных MetaTrader’ом. Как мы уже знаем из прошлой статьи, таких массивов может быть выделено до восьми. Но заранее распределять память в MetaTrader’е под эти массивы было бы не слишком экономно, ведь не каждый индикатор будет отрисовывать все восемь графиков. Это означает, что при запуске индикатора, MetaTrader должен быть явно проинформирован, какое количество массивов он должен выделить под данные индикатора и, что не менее важно, какое количество массивов MetaTrader будет учитывать, отрисовывая индикатор на графике.

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

Но само по себе объявление количества доступных индикатору массивов ничего не даст. Даже объявление трёх переменных, которые должны содержать данные индикатора, ничем не поможет. Объявленные массивы необходимо связать с выделенной под них памятью для того, чтобы сделать их действительно актуальными при отрисовке индикатора испольняющей средой MetaTradder’а.

Связывание пользовательской программы с этими массивами происходит с помощью вызова функции SetIndexBuffer. Первым параметром передаётся номер выделенного массива, вторым - имя переменной, объявленной как массив без элементов.

На рисунке показана ситуация, которая случится после единственного вызова этой функции, как показано ниже:

SetIndexBuffer(0,ExtMapBuffer1);

Тем самым осуществляется связывание переменной ExtMapBuffer1 с первым (с номером "0") массивом, выделенным исполняющей средой. Заметим, что в соответствии со свойством индикатора indicator_buffers равным трём, два выделенных массива останутся не связанными.

Если мы попробуем обратиться к элементам переменной массивов ExtMapBuffer2 и ExtMapBuffer3, не связанных с распределённой MetatRader’ом памятью, ничего не получится. Все наши усилия не будут видны на экране терминала. Как вы уже, наверное, догадались, эти массивы тоже надо связать с распределённой MetaTrader’ом памятью, используя следующие вызовы:

SetIndexBuffer(1,ExtMapBuffer2);
SetIndexBuffer(2,ExtMapBuffer3);

Теперь давайте разберёмся, как правильно заполнять массивы, доступные нам посредством связанных с ними
переменных нашей программы. Обратимся к остову индикатора, который был подготовлен для нас мастером создания советников, и рассмотрим подробнее функцию start().

int start()
{
int counted_bars=IndicatorCounted();
//---- TODO: add your code here
//----
return(0);
}

Видно, что мастер добавил строку, в которой создаётся и инициализируется целочисленная переменная counted_bars. После вызова функции IndicatorCounted(), переменная будет содержать количество баров, для которых уже подсчитаны значения создаваемого индикатора.

Можно было бы обойтись и без неё, но если мы видим, что мастер так настойчиво предлагает нам пользоваться таким способом работы с данными, то мы не видем пока особого смысла пренебрегать этими рекомендациями.
Если бы мы решили не использовать IndicatorCounted() для выяснения того, для каких баров уже подсчитан индикатор, то нам пришлось бы изобрести свой собственный способ делать это, либо можно было бы каждый раз, когда исполняющей средой MetaTrader’а вызывается функция start(), пересчитывать значения индикатора на всех барах.

Последний способ может сильно нагрузить компьютер вычислениями, и в случае, когда к ценовым графикам прикреплено большое количество индикаторов, игнорирующих вызов функции IndicatorCounted(), мы ощутили бы некоторый дискомфорт из-за замедленной реакции терминала на наши действия.
Для того, чтобы избежать проблем с чрезмерной вычислительной нагрузкой, предлагается при каждом вызове исполняющей средой выяснять, какое количество баров уже было учтено при вычислении значений индикатора на предыдущих вызовах функции start(). Это и делается в первой же строке кода нашей функции start().
После этого, обычно проверяется, не произошло ли при вызове MQL-функции IndicatorCounted() какой-либо ошибки. Если counted_bars меньше, чем ноль, то ошибка произошла, и не имеет смысла дальше продолжать данную итерацию вычисления индикатора.
В случае ошибки мы возвращаемся из функции start() при помощи директивы return. Указывая в качестве параметра директивы return отрицательное значение мы, тем самым, возвращаем это число в исполняющую среду терминала, давая возможность MetaTrader’у понять, что вычисления не произведены или произведены неверно ввиду какой-либо ошибки. Если ошибки не произошло, то можно продолжать вычисления и дальнейший алгоритм работы большинства индикаторов совпадает:

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

int start()
{
int limit = 0;
// получаем количество учтённых
// на предыдущих итерациях баров
int counted_bars=IndicatorCounted();
// проверяем, возникла ли ошибка
if(counted_bars<0) return(-1);
// последний учтённый бар на
// всякий случай будем пересчитывать
if(counted_bars>0) counted_bars--;
// сколько баров нужно просчитать
// на этот раз?
limit=Bars-counted_bars;
// выполняем основной цикл
// вычисления значений индикатора
for(int i=0; i<limit; i++) {
// вычисляем значения индикатора
// на i-том баре и заносим его
// в связанный со специальной
// памятью MetaTrader.а массив.
// Например, ExtMapBuffer1
}
// возвращаем 0, как код удачного
// завершения вычислений
return(0);
}

На этом мы заканчиваем очередной этап изучения MQL 4, а читателям, до выпуска следующего номера журнала, рекомендуем разобрать исходный код поставляемых с MetaTrader’ом примеров индикаторов с учётом изложенного материала.

Александр Иванов
 
Верх