Содержание
Всем привет!
В этом уроке мы рассмотрим, что такое массивы, зачем они вообще нужны, какие бывают и как правильно их использовать. Научимся работать со свечами, определять значения цены Open, High, Low, Close свечей на графике, попробуем определить цену последней свечи. Поехали.
Массивы — Учебник по MQL4
Массив это сгруппированный набор данных одного типа под общим названием. Проще говоря, это переменная определенного типа, будь то int, double, string и т.д, которая может содержать в себе от одного до 2147483647 значений. Они называются элементами массива. Объявляться массив может на локальном, либо глобальном уровне.
0 |
int MyArray[3]; |
Выше объявлен одномерный массив. Тип данных у него целый, integer, т.е. он может содержать любые не дробные числа. MyArray это его наименование, оно, как вы понимаете, может быть любым. Далее в квадратных скобках указывается количество элементов этого массива, в нашем случае их три. Значение в квадратных скобках также называют индексом.
Массивы могут быть как одномерные, так и многомерные: двумерные, трехмерные или четырехмерные. Лично мне трехмерный и четырехмерный массив ни разу в работе не понадобились, поэтому их рассматривать сейчас не будем.
Посмотрим на изображение из справочника на сайте MQL:
Значением элемента Array[4] у одномерного массива является число 34 (рис. a), для двумерного массива значением элемента Array[3,7] число равно 28 (рис. b). А для трёхмерного массива значением элемента Array[5,4,1] является целое число 77.
Если у вам возник вопрос «Зачем мне эти массивы?», то тут все просто — в них вы будете хранить большое количество информации, которое просто неудобно держать в отдельно созданных переменных. Например, вам нужно для анализа хранить номера Тикетов всех закрытых ордеров на покупку. Это можно сделать через отдельные переменные типа int, но тогда у вас будет целый список с именами MyTicket1, MyTicket2, MyTicket3 и т.д. Это неудобно, особенно, когда у вас больше сотни ордеров, плюс количество ордеров в истории будет постоянно увеличиваться. Другой пример: Вам нужно сохранить данные индикатора Moving Average за последние 20 свечей для дальнейшего расчета, либо построения графических фигур. Согласитесь, опрометчиво создать 20 разных переменных, когда можно сделать всего один массив с уникальным именем, который содержит в себе эти 20 значений.
Продолжим. Созданный ранее массив MyArray не содержит данных, изменим это, сразу задав ему значения:
0 |
int MyArray[3] = { 5, 8, 3}; |
Значения инициализируются через запятую по порядку, начало и конец данных обрамляется фигурными скобками. В данном случае есть одномерный массив, объявлено, что он содержит в себе три элемента, мы по порядку задаем им значения. Получается, что первый элемент массива будет равен 5, второй 8, а третий 3. Нумерация массивов всегда считается от нуля. Соответственно, чтобы проверить, что значения элементов присвоены корректно — сделаем принт в журнал:
0 1 2 |
Print("Первый элемент: ", MyArray[0]); //равно 5 Print("Второй элемент: ", MyArray[1]); //равно 8 Print("Третий элемент: ", MyArray[2]); //равно 3 |
Повторюсь, размер массива задается целым числом, в нашем случае это [3], т.е. в нем содержится три элемента. Но! При работе со значениями этого массива отсчет начинается с нуля, значит три элемента будут иметь порядковые номера 0, 1 и 2. Это важно запомнить, чтобы не путаться в будущем.
Значения массиву можно присваивать в любой момент работы с кодом, не обязательно делать это сразу. Для статического массива необходимо объявить только количество элементов.
0 1 2 3 4 5 6 7 8 9 |
int MyArray2[3]; //в нужном месте кода задаем значения элементам массива MyArray2[0] = 123; MyArray2[1] = 44; MyArray2[2] = 68; Print("Первый элемент: ", MyArray2[0]); //равно 123 Print("Второй элемент: ", MyArray2[1]); //равно 44 Print("Третий элемент: ", MyArray2[2]); //равно 68 |
Теперь, давайте рассмотрим двумерный массив. Он содержит в себе два индекса в квадратных скобках:
0 |
string Team[2][3]; |
Объявлен двумерный массив типа string, первый индекс которого равен двум, второй — трем, значит в сумме у этого массива 6 значений. Зададим ему текст:
0 1 2 3 4 5 6 7 8 9 10 11 12 |
string Team[2][3]; Team[0][0] = "Роман"; Team[0][1] = "Программист"; Team[0][2] = "Торгует Night Hawk EA"; Team[1][0] = "Виталий"; Team[1][1] = "Тестер"; Team[1][2] = "Торгует Zodiaq EA"; Print("Участники команды:"); Print(Team[0][0],",",Team[0][1],",",Team[0][2]); Print(Team[1][0],",",Team[1][1],",",Team[1][2]); |
Рекомендую вывести эту информацию в принт в терминале, чтобы понять, как это работает. Мы разбили первый индекс массива для данных об участниках команды — меня и коллеги, у них номера 0 и 1, а второй индекс содержит информацию о нас: имя, род деятельности, советник.
Двумерные массивы удобны для хранения информации об ордерах — первый индекс будет как внутренняя нумерация ордера, а второй уже хранить в себе цену открытия, закрытия, цели, прибыль и т.д. Также я использовал их при импорте новостных данных для нашего индикатора Truly News Indicator из интернета. Там все данные о новостях (наименование, время, важность, прогноз) хранятся в типе данных sting. Далее записанную информацию в массив я разбивал на отдельные графические объекты, которые и отображали всю информацию на графике.
Значения двумерному массиву можно задавать также при его объявлении в самом начале, если вы знаете данные, которые хотите ему передать. Задаются они также через запятую для всех элементов последовательно.
0 1 2 3 4 5 6 7 8 9 |
double DoubleArray[3][3] = { 0.4, 2.5, 6.1, 5.6, 8.9, 1.1, 7.4, 5.8, 0.9 }; Print(DoubleArray[0][0]); //результат 0.4 Print(DoubleArray[0][1]); //результат 2.5 Print(DoubleArray[0][2]); //результат 6.1 Print(DoubleArray[1][0]); //результат 5.6 Print(DoubleArray[1][1]); //результат 8.9 Print(DoubleArray[1][2]); //результат 1.1 Print(DoubleArray[2][0]); //результат 7.4 Print(DoubleArray[2][1]); //результат 5.8 Print(DoubleArray[2][2]); //результат 0.9 |
Стоит заметить, что не обязательно при инициализации задавать значения всем константам, можно пропустить нужное количество. Пропуск идет у последних элементов:
0 1 2 3 4 |
double DoubleArray2[2][2] = { 2.9, 6.5 }; Print(DoubleArray2[0][0]); //результат 2.9 Print(DoubleArray2[0][1]); //результат 6.5 Print(DoubleArray2[1][0]); //результат 0.0 (пропуск) Print(DoubleArray2[1][1]); //результат 0.0 (пропуск) |
Динамические массивы
Выше мы рассмотрели статические массивы. Но что делать, если мы не знаем, какое количество данных нужно нам хранить в массиве в момент его объявления? Нужно использовать динамический массив, размер которого можно изменить в любое время.
Они имеют такой же вид, как и статические, с единственной разницей — у них нет индекса внутри квадратных скобок. Объявим массив типа int:
0 |
int DinamicArray[]; |
Прежде, чем его начать использовать, мы должны задать ему размер с помощью функции ArrayResize:
0 |
ArrayResize(DinamicArray,5); //новый размер массива будет равен 5 |
Функция имеет три параметра: первый это имя объявленного массива, оно указывается без фигурных скобок в конце. Вторая — новый размер, который будет задан. Третий параметр это размер дополнительного резерва для массива, его не обязательно указывать и нужен он только когда вы много раз меняете размер вашего массива, скажем каждый тик. Если использовать резерв, то новый размер массива будет браться из этой памяти, что ускорит работу выполнения кода. В моем примере резерв не нужен, поэтому он не указывается.
Если в дальнейшем вам будет нужно изменить размер массива — нужно еще раз вызывать эту функцию. Естественно, если размер будет уменьшен, то все данные прошлого массива с бОльшим количеством элементов будут удалены. Т.е. если размер массива был 5, стал 3, то в памяти останется только первые три значения, два же сотрутся. Динамический массив может быть только для первого измерения, значит второй и последующие индексы должны быть определены программистом.
Чтобы не создавать отдельную статью, рассмотрим сразу другие основные функции для работы с массивами обоих типов:
- ArrayInitialize — функция инициализирует числовой массив заданным значением. Передача данных идет сразу для всех элементов.
0 1 2 3 |
int array[5]; ArrayInitialize(array,123); Print(array[3]); //результат 123 Print(array[1]); //результат 123 |
- ArrayFill — выполняет по сути тоже, что и предыдущая функция — задает значения элементам массива с разницей в том, что можно указать с какого элемента начинать и сколько значений заполнить. Первый параметр это имя массива, второй — индекс с которого нужно начать заполнение (0 — обозначает самый первый). Третий параметр это количество элементов, которые нужно заполнить, четвертый — значение, которым нужно заполнить массив. На примере ниже мы заполним 0 и 1 элементы массива значением «1111», а 2 и 3 — «2222»:
0 1 2 3 4 5 6 7 |
int array1[]; ArrayResize(array1,4); ArrayFill(array1,0,2,1111); ArrayFill(array1,2,2,2222); Print(array1[0]); //результат 1111 Print(array1[1]); //результат 1111 Print(array1[2]); //результат 2222 Print(array1[3]); //результат 2222 |
- ArraySize — функция считает количество элементов заданного массива. В основном нужна для динамических массивов, когда их размер периодически меняется и нужно знать актуальное его значение.
0 1 2 |
int array2[]; ArrayResize(array2,10); Print("Размер массива: ",ArraySize(array2)); //результат 10 |
- ArrayRange — возвращает количество элементов в заданном измерении массива. Нужен для работы с многомерными массивами:
0 1 2 3 |
double d_array[5][2][4]; Print("Размер первого измерения: ", ArrayRange(d_array,0)); //результат 5 Print("Размер второго измерения: ", ArrayRange(d_array,1)); //результат 2 Print("Размер третьего измерения: ", ArrayRange(d_array,2)); //результат 4 |
- ArraySort — сортировка массива по первому измерению. С помощью этой функции можно поменять порядок всех или части элементов задом наперед. Функция имеет четыре параметра: наименование массива, сколько элементов нужно сортировать (WHOLE_ARRAY — обозначает весь массив), с какого индекса начать сортировку (0 значит первый), направление сортировки: MODE_ASCEND — в порядке возрастания, MODE_DESCEND — в порядке убывания.
0 1 2 3 4 |
double sort_array[6] = { 1,2,3,4,5,6 }; ArraySort(sort_array,WHOLE_ARRAY,0,MODE_DESCEND); //результат 6,5,4,3,2,1; Print("Значение последнего элемента: ", sort_array[5]); ArraySort(sort_array,WHOLE_ARRAY,0,MODE_ASCEND); //результат 1,2,3,4,5,6; Print("Значение последнего элемента: ", sort_array[5]); |
- ArrayIsDynamic — функция проверяет, является ли данный массив динамическим:
0 1 2 |
int array3[]; bool Dinamic = ArrayIsDynamic(array3); Print("Является ли массив динамическим: ",Dinamic); |
- ArrayMaximum, ArrayMinimum — поиск в числовом массиве максимального или минимального номера элемента. Данные функции имеют три настройки: наименование массива, количество элементов для анализа, номер начального индекса, с которого начинать подсчет. Возвращает номер элемента, а не само значение. Чтобы его узнать, полученный номер нужно указать в фигурных скобках массива.
0 1 2 3 4 |
int array4[10] = { 4,4,11,3,19,25,2,1,7,9 }; int max = ArrayMaximum(array4,WHOLE_ARRAY,0); int min = ArrayMinimum(array4,WHOLE_ARRAY,0); Print("Максимальное значение: ",array4[max]); Print("Минимальное значение: ",array4[min]); |
С массивами, как и с обычными переменными можно совершать арифметические функции, а также присоединять текст одного элемента массива к другому.
0 1 2 3 4 5 |
int array5[3] = { 2, 5, 3 }; int sum = array5[0] + array5[1] + array5[2]; Print("Сумма элементов массива: ",sum); string array_str[2] = { "World","Hello" }; Print(array_str[1]+" "+array_str[0]); |
Массивы-таймсерии
Массивы-таймсерии — одномерные массивы, элементы которых содержат характеристики свечей на графике. Их существует шесть видов, названия у них предопределены. Все значения баров на графике хранятся в этих массивах.
- Open — цена открытия бара.
- Close — цена закрытия бара
- High — максимальная цена бара
- Low — минимальная цена бара
- Time — время открытия бара
- Volume — объем бара.
Массивы-таймсерии всегда являются динамическими, их размер нельзя менять, он всегда равен последнему известному значению подгруженных баров истории.
Нумерация идет справа налево и начинается с нулевого бара, т.е. текущего, который еще открыт (не сформировался). Соответственно первый бар это предыдущая (первая закрытая) свеча, второй — свеча слева от первой и т.д. При наведении мышью на любую свечу вы получите все ее характеристики. Также их можно посмотреть в ‘Окне Данных‘, открыв его в меню «Вид», либо нажав «Ctrl+D’
После закрытия свечи новый открытый бар принимает значение 0. Все остальные номера баров смещаются на одну единицу (тот, что был 1, становится равным 2 и т.д.). После закрытия свечи ее данные цены и времени больше не изменяются.
Узнаем значение цены первой закрывшейся свечи:
0 1 2 3 4 |
Print("Цена открытия: ",Open[1]); Print("Цена закрытия: ",Close[1]); Print("Хай свечи: ",High[1]); Print("Лоу свечи: ",Low[1]); Print("Время открытия: ",Time[1]); |
У нулевой свечи еще нет цены закрытия, поэтому при определении Close[0] результат будет равен текущей цене Бид до того момента, пока бар не закроется.
Попробуем с помощью изученной ранее математической функции MathMax узнать более высокую цену открытия между двумя свечами:
0 1 |
double max_bar = MathMax(Open[0],Open[1]); Print("Более высокая цена открытия: ",max_bar); |
Далее узнаем самую низкую цену закрытия 1,2 и 3 свечи:
0 1 |
double min_bar = MathMin(Close[1],MathMin(Close[2],Close[3])); Print("Самая низкая цена закрытия: ",min_bar); |
Теперь определим самый высокий High и самый низкий Low последних 20 свечей:
0 1 2 3 4 |
int max_high = ArrayMaximum(High,20,0); Print("Самый высокий High: ",High[max_high]," на ",max_high," свече."); int max_min = ArrayMinimum(Low,20,0); Print("Самый низкий Low: ",Low[max_min]," на ",max_min," свече."); |
Значение Массивов-таймсерии также можно вычитать и прибавлять, допустим, чтобы узнать размер свечи от цены открытия до закрытия:
0 1 2 |
double candle_size = MathAbs(Close[1]-Open[1]); //возводим в модуль результат candle_size /= Point; //делим полученный результат на размер пункта инструмента Print("Размер [1] свечи: ",DoubleToString(candle_size,1)); |
С помощью цикла в дальнейшем мы изучим как находить разнообразные свечные паттерны и делать более сложные расчеты.
Стоит заметить, что данный тип отображения предопределённых названий работает только на текущей инструменте и таймфрейме графика. Если вам нужно знать цену на другом символе, либо ином периоде, то нужно воспользоваться следующими функциями: iOpen, iClose, iHigh, iLow, iTime. Они имеют три параметра: первый это символ, который нужно использовать (если указать NULL, то будет учитываться текущий), второй timeframe — период графика (при 0 — текущий). Третий это сдвиг, т.е. то значение, которое указывается в квадратных скобках.
0 1 2 3 4 |
Print("Цена открытия: ",iOpen(NULL,0,1)); Print("Цена закрытия: ",iClose(NULL,0,1)); Print("Хай свечи: ",iHigh(NULL,0,1)); Print("Лоу свечи: ",iLow(NULL,0,1)); Print("Время открытия: ",iTime(NULL,0,1)); |
Заключение
На этом все. Мы изучили основные способы задавать и работать с массивами, необходимые функции для них, уже появилось начальное представление как происходит работа с графиками. Материала получилось много, на написание этой статьи ушло несколько дней, так что думаю освоить его с первого раза будет не легко.
Все вопросы по статье можете задавать в комментариях, возможно у вас будут идеи, как ее можно дополнить.
В приложенных mql4 файлах скрипта специально закомментированы Print’ы, чтобы при установке на график на вас не вылилась гора информации. При проверке кода нужно просто по очереди снять знак комментария // с нужного раздела.
[download url=»http://www.davinci-fx.com/wp-content/uploads/2021/01/1.4-Arrays.rar» title=»Скачать пример урока»]
Блог для успешных трейдеров на рынке Форекс. Полезный сайт о Forex — DaVinci FX Group.