English 中文 Español Deutsch 日本語 Português
Язык MQL4 для "чайников". Сложные вопросы простым языком

Язык MQL4 для "чайников". Сложные вопросы простым языком

MetaTrader 4Примеры | 27 июня 2007, 11:11
13 086 19
Antoniuk Oleg
Antoniuk Oleg

Введение

Это вторая статья из цикла "Язык MQL4 для 'чайников'". В первой статье "Язык MQL4 для 'чайников'. Первое знакомство" рассказывалось о том, что можно сделать с помощью языка MQL4, мы научились писать обычные скрипты, поняли, что такое переменная, научились работе с переменными, разобрались, что такое функция, массивы, встроенные или предопределённые массивы и переменные, циклы for и простые и сложные условия. Сейчас мы будем разбираться в более сложных и продвинутых конструкциях языка, изучим новые возможности и посмотрим, как их можно применять в ежедневной практике. Сегодня вы узнаете про новый вид циклов while, новый вид условий switch, операторы break и continue. Кроме того вы научитесь писать собственные функции и работать с многомерными массивами. На десерт я подготовил для вас разъяснения о препроцессоре.


Совет

Не вздумайте читать эту статью, полностью не разобравшись с первой. Вы только наломаете дров и все равно ничего не поймете. Эта статья основывается на старом материале, так что не нужно спешить! Также хочу успокоить вас. Сложности, с которыми вы сталкиваетесь сейчас при изучении материала, обманчивы. Придет время, когда вы даже не будете задумываться о том, как пишутся циклы, где какие условия поставить, - все будет происходить автоматически. Чем дольше вы будете работать с языком MQL4, тем легче вам будет его использовать.


Новый вид циклов while

Хочу отметить, что цикл for, описанный в предыдущей статье, является универсальным и может заменить другой вид циклов, с которым мы сейчас познакомимся. Но это не всегда удобно и оправданно. Иногда намного эффективнее использовать while. Скоро вы сами поймете, где какой вид цикла использовать более рационально. Давайте выполним одну задачу двумя способами: найдем суммарный объем всех баров, используя оба цикла и посмотрим в чем разница:

// используем цикл for
double sum = 0.0;
 
for(int a = 0; a < Bars; a++)
    sum += Volume[a];
 
// здесь используем while, результат будет одинаковый
double sum = 0.0;
int a = 0;
 
while(a < Bars)
  {
    sum += Volume[a];
    a++;
  }
Как видно, теперь счетчик объявляется и используется отдельно. Вообще while переводится как "пока". То есть не "до свидания", а в том смысле, что пока это условие истинно, то цикл продолжает выполняться. Посмотрите на общую форму:


while(условие выполнения цикла)
  {
    код
  }

Вот ещё более понятный пример:

На самом деле цикл while отличается от for лишь отсутствием счетчика. Если он вам не нужен используйте while, хотя это не обязательно. Например, я часто использую while со счетчиком, это уже больше дело вкуса. Как и в случае с for, если тело цикла включает лишь одну инструкцию, то можно упустить фигурные скобки. Также, для общего развития вам нужно запомнить значение слова итерация. Это один из многочисленных проходов (повторений), которые выполняет цикл. То есть выполнив тело цикла один раз, тем самым была выполнена одна итерация.

Новый вид условий switch или переключатель

Как и в случае с циклами, нужно отметить, что switch можно заменить на комбинацию привычных вам условий if и else. Конструкция switch используется в том случае, когда вам нужно выполнить определенные действия в зависимости от значения какой-то переменной. Это похоже на обычный переключатель режимов в микроволновке. Например, представьте, что вы пишите советника и он изменяет свое поведение в зависимости от состояния рынка. Пусть за это отвечает переменная int marketState. Она может принимать следующие значения:

  • 1 - восходящий тренд
  • 2 - нисходящий тренд
  • 3 - флэт

Неважно как определяется это состояние, наша задача реализовать какой-то механизм, чтобы советник в зависимости от состояния рынка выполнял соответствующие действия. Что ж, вы умеете это делать. Самый очевидный вариант выглядит следующим образом:

if(marketState == 1)
  {
    // стратегия торговли для восходящего тренда
  }
else 
    if(marketState == 2)
      {
        // стратегия для нисходящего тренда
      }
    else 
        if(marketState == 3)
          {
            // стратегия для флета
          }
        else
          {
            // ошибка: такого состояния не предусмотрено!
          }

Тут нужно отметить несколько особенностей:

  • все условия проводятся с одной и той же переменной;
  • все условия сводятся к сравнению этой переменной с одним из значений, которые она способна принимать.

Так вот, все это относится и к структуре switch. Посмотрите на аналогичный по результату код, который использует switch:

switch(marketState)
  {
    case 1:  // стратегия торговли для восходящего тренда
             break;
    case 2:  // стратегия для нисходящего тренда
             break;
    case 3:  // стратегия для флета
             break;
    default: // ошибка: такого состояния не предусмотрено!
             break;
  }

Обратите внимание, сначала мы указываем какую переменную будем сравнивать:

// switch - ключевое слово, marketState - 
// переменная для сравнения
switch(marketState)

а потом указываем что делать в конкретных случаях:

 
case 1:                        // case - ключевое слово; 
   // стратегия торговли       // если marketState равно 1, то 
   // для восходящего тренда   // выполняем этот код
   break;                      // ключевое слово, которое указывает  
                               // на конец действий в этом случае
 
case 2:                        // если marketState равно 2, то
    // стратегия для           // выполняем это
    // нисходящего тренда          
    break;                     // конец
 
case 3:                        // аналогично
    // стратегия для флета
    break;
 
default:                       // иначе, выполняем это
    // ошибка: такого   
    // состояния не 
    // предусмотрено!
    break;

В общем виде switch имеет такую форму:

switch(переменная для сравнения)
  {
    case [значение переменной]:
    // код для этого случая
    break;
   
    case [другое значение переменной]
    // код для этого случая
    break;
   
    default:
    // код для всех остальных случаев
    break;
  }

Используйте switch, если сравниваете одну переменную с несколькими значениями и каждому значению соответствует определенный блок кода. В любом другом случае используйте обычные комбинации условий if и else. Иногда нужно выполнить какой-то код при нескольких значениях переменной. То есть, например, если marketState == 1 или 2, то выполнить такой-то код. Вот как это можно сделать используя switch:

switch(marketState)
  {
    case 1:  // если marketState равно 1
    case 2:  // или если marketState равно 2, то
             // выполнить это
             break;
 
    default: // в любом другом случае выполнить 
             // этот код
             break;
  }

Операторы continue и break

С оператором break мы только что познакомились. Он предназначен для выхода из тела switch. Кроме того вы можете его использовать для выхода из цикла. Например, если при каких-то условиях цикл больше выполнять не требуется. Допустим нам нужно узнать сколько первых баров потребуется, что бы вместить объем в 1000 пунктов. Для этого можно написать такой код:

int a = 0;
double volume = 0.0;
 
while(volume < 1000.0)
  {
    volume += Volume[a]; // равносильно volume = volume + Volume[a]; 
    a++;
  }
 
// теперь переменная "a" содержит количество баров, которые 
// в сумме имеют объем не меньше 1000 пунктов

А теперь напишем аналогичный код, но будем использовать оператор break:

int a = 0;
double volume = 0.0;
 
while(a < Bars)
  {
    // если объем превышает 1000 пунктов, то
    if(volume > 1000.0) // выходим из цикла   
        break;            
 
    volume += Volume[a];
    a++;
  }

Как видно оператор break очень прост в использовании и позволяет избавиться от лишних итераций цикла. Еще один полезный оператор continue предназначен для "пропускания" ненужных итераций. Пусть нам нужно посчитать суммарный объем, но мы не должны учитывать объемы баров в момент важных новостей. Как известно, важные новости влекут за собой огромные объемы пунктов. Притворимся наивными детьми и будем считать, что объем бара в 50 пунктов и больше - это новость. Для решения этой задачки воспользуемся оператором continue:

int a = -1;
double volume = 0.0;
 
while(a < Bars)
  {
    a++;
    // если объем превышает 50 пунктов, то это должно 
    // быть новость, пропустим ее 
    if(Volume[a] > 50.0)          
        continue;          
    volume += Volume[a];
  }

Как видите, использование оператора continue довольно тривиально, но иногда это может очень помочь вам. Понятно, что этот скрипт предназначен для мелких таймфреймов.


Пишем собственные функции

Да, но зачем они нужны? Дело в том, что частенько в вашем коде вы будете находить повторы. То есть один и тот же набор инструкций вы будете использовать то здесь, то там. Что бы сэкономить ваше время и силы, вы можете вынести этот повторяющийся код в отдельную функцию. А когда нужно, просто вписать название этой функции и она сделает все за вас. Давайте посмотрим как это работает. Представьте, что вам нужно узнать цвет свечи. Всем известно, что "белой" считается свеча, которая закрылась выше открытия, "черной" - наоборот. Напишем код для определения цвета свечи:

bool color; // так как имеется всего 2 варианта 
            // (белая или черная свеча), 
            // то будем считать, что значению 
            // false соответствует черная 
            // свеча, а true - белая
 
if(Close[0] > Open[0])
    color = true;      // белая свеча
    
if(Open[0] > Close[0])
    color = false;     // черная свеча

Вот и все, теперь в переменной color хранится цвет последней свечи. Что бы узнать цвет какой-то другой свечи, например, предпоследней, нужно изменить индекс с 0 на 1. Но не будете же вы вставлять этот код в любом месте, где понадобиться узнать цвет свечи! А если таких мест будет несколько десятков? Именно поэтому нужно использовать функции. Давайте подумаем, как она должна работать. Подобная функция должна принимать один аргумент - индекс свечи, цвет которой нужно определить, и возвращать цвет - переменную типа bool. Представим, что функция уже написана и мы вызываем ее:

bool color; // сюда будет записан цвет нужной нам свечи
 
color = GetColor(0);

Как вы догадались, наша функция будет называться GetColor. В этом вызове мы захотели узнать цвет последней свечи, поэтому единственный аргумент равен нулю. Функция возвращает цвет свечи, поэтому мы сразу выполняем присваивание. Это очень важный момент! Внутри функции создается переменная, а потом ее значение подставляется вместо самого вызова функции. В конечном итоге вызов функции и код определения функции, тот, что мы писали выше, дадут одинаковый результат - в переменной color будет записан цвет последней свечи, но используя функции мы затрачиваем меньше усилий.

А теперь давайте неожиданно остановимся на коде пустого скрипта. Создайте новый скрипт. Дело в том, что там уже включено полное описание функции start(). Самое интересное то, что все это время вы писали скрипты в этой функции! Когда вы запускаете ваш скрипт, то терминал просто вызывает функцию start(). Рассмотрим подробнее код пустого скрипта:

int start()

Эта строка очень важна! Она включает

название функции

, то есть ключевое слово, которое вы будете писать, когда захотите вызвать эту функцию. В нашем случае это - start. Также указывается

тип возвращаемого значения

- int. Это значит, что после выполнения функции, она возвратит нам какое-то значение типа int. В скобках размещается

перечень аргументов

, но в нашем случае функция не принимает никаких параметров.



После этого в фигурных скобках следует описание самой функции, то есть тот код, который будет выполняться при вызове:

  {
    //----
    // код, который будет выполняться 
    // при вызове функции.
    //----
    return(0);
  }

Как видно, все это время мы писали код в теле функции start(). В самом конце функции находится оператор

return

, который возвращает значение функции. В нашем случае возвращается нуль.

Теперь посмотрите на общую форму написания функций:

[тип возвращаемого значения] [название функции] ([перечень аргументов])
  {
    // код функции
    return([значение, которое функция возвращает]);
  }

А теперь давайте вернемся к нашим свечам и функции GetColor. Посмотрите на код этой функции:

bool GetColor(int index)
  {
    bool color;
    if(Close[index] > Open[index])
        color = true;      // белая свеча
    if(Open[index] > Close[index])
        color = false;     // черная свеча
    return(color);
  }

Рассмотрим первую строку детально:

bool GetColor(int index)

Здесь имеем: bool - тип возвращаемого значения; GetColor - название функции; int - тип аргумента; index - название аргумента. Обратите внимание, что мы используем index для обращения в теле функции, но при непосредственном вызове функции это названия никогда не упоминается, например:

bool lastColor = GetColor(0);

Смотрим дальше:

{
   bool color;
   if(Close[index]>Open[index])
       color=true;      // белая свеча
   if(Open[index]>Close[index])
       color=false;     // черная свеча

Это тело функции - основной код, который будет выполняться при каждом вызове. Смотрим дальше:

   return(color);
}

Оператор return указывает, что должна возвращать функция. Возвращаемое значение должно соответствовать типу, который мы определили до этого в самом начале. Вы можете использовать несколько операторов return в одной функции, если нужно, например:

bool GetColor(int index)
  {
   if(Close[index] > Open[index])
       return(true);      // белая свеча
   if(Open[index] > Close[index])
       return(false);     // черная свеча
  }

Как видно, использование нескольких операторов return позволило избавиться от переменной color. Кроме того в операторе return можно использовать даже логические выражения:

return(Close[index] > Open[index]);

Это возможно из-за того, что операторы сравнения также возвращают значения типа bool (истина или ложь) подобно некоторым обычным функциям. Выглядит сложновато, но скоро привыкните.


Теперь давайте вернемся к списку аргументов. В нашей функции используется лишь один аргумент - int index. Если нужно использовать несколько аргументов, то перечислите их через запятую:

bool SomeСomplicatedFunction(int fistArgument, int secondArgument, 
                             sting stringArgument)

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

порядок следования аргументов

: ничего не перепутайте! Если функция не должна возвращать никакого значения, то используйте ключевое слово

void

, чтобы указать это. Обратите внимание, оператор return в этом случае не используется:

void function()
  {
    // код
  }

Есть еще одна тонкость: вы можете задать значения для аргументов функций по умолчанию. Что это такое? Допустим, вы написали какую-то сложную функцию, у которой есть 5 аргументов, которые настраивают ее поведение. Но последние несколько аргументов почти всегда используются с одними и теми же значениями. Только на два десятка вызовов вам приходится применять какие-то специальные значения. Что бы не указывать каждый раз значения последних аргументов, которые почти всегда одинаковы, используются значения аргументов по умолчанию. В таком случае вы просто пропускаете последние аргументы, как будто их не существует, но на самом деле они используются, только им присваиваются значения по умолчанию. Когда же возникает тот самый специальный случай, то вы указываете все аргументы. Посмотрим как можно объявить функцию с аргументами по умолчанию:

void someFunction(int argument1, int argument2, 
                  int specialArgument = 1)
  {
    // код
  }

Как видите, все очень просто: мы присваиваем нужному аргументу нужное значение и теперь его можно пропускать при вызове:

someFunction(10,20);   // мы пропустили последний аргумент, но 
                       // на самом деле ему присвоено значение по умолчанию
 
someFunction(10,20,1); // этот вызов полностью аналогичен предыдущему
 
someFunction(10,20,2); // а тут мы указали другое значение, обычно 
                       // такое редко бывает

Вы можете указывать сколько угодно значений аргументов по умолчанию, но помните важное правило: все они должны размещаться последними. Например:

void someFunction(int argument1, int argument2, 
                  int specialArgument = 1)   // все верно
 
void someFunction(int argument1, int argument2 = 10, 
                  int specialArgument=1)     // верно
 
void someFunction(int argument1, int argument2 = 10, 
                  int specialArgument)     // неправильно! значения 
                                           // по умолчанию должны
                                           // быть в конце списка 
                                           // аргументов
                                                                           
void someFunction(int argument1 = 0, int argument2 = 10, 
                  int specialArgument = 1) // можете хоть всем 
                                           // аргументам присвоить 
                                           // значения по умолчанию

Многомерные массивы

Вы часто будете использовать массивы при программировании, и в большинстве случаев вам будет достаточно одномерных массивов. Но иногда вам понадобятся и двухмерные, и трехмерные. Сейчас вы научитесь ими пользоваться.

Для начала давайте наглядно представим себе одномерный массив, вспомним объявление, инициализацию, индексы и значения:


Как видно, любой одномерный массив удобно представить себе в виде ряда значений единого типа. Посмотрите как будут обрабатываться различные обращения к одномерному массиву:



Двухмерные массивы подобны обычным таблицам, посмотрите:


Как видно из рисунка, у двухмерных массивов имеется уже два индекса для обращения к значению: первый индекс определяет ряд, а второй - столбец. Для инициализации используются перечень значений как и в одномерном массиве. Посмотрите, как происходит обращение к значениям ячеек таблицы:




Как видно, все достаточно просто. Давайте посмотрим как можно "пройтись" по всем значениям двухмерного массива. Для этого нужно использовать 2 цикла:

int array2D[3][3]={10,20,30,
                   40,50,60,
                   70,80,90};
 
for(int y=0;y<3;y++)
   for(int x=0;x<3;x++)
      MessageBox("array2D["+y+"]["+x+"]="+array2D[y][x]);

В этом примере обращение к каждой ячейке будет проходить сверху вниз слева направо. В качестве упражнения попробуйте изменить направление проходов, например, снизу вверх.

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

int array3D[3][3][3] = {11, 12, 13,
                        14, 15, 16,
                        17, 18, 19,
 
                        21, 22, 23,
                        24, 25, 26,
                        27, 28, 29,
 
                        31, 32, 33,
                        34, 35, 36,
                        37, 38, 39};
 
for(int z = 0; z < 3; z++)
    for(int y = 0; y < 3; y++)
        for(int x = 0; x < 3; x++)
            MessageBox("array3D["+z+"]["+y+"]["+x+"]=" + 
                       array3D[z][y][x]);

Основательно разберитесь в двухмерных и трехмерных массивах - это очень важно. Внимательно еще раз просмотрите пояснительные рисунки. Очень много различных задач решается с использованием массивов, так что, уделите им достаточно внимания, они еще не раз вас выручат. Если вы поймете принцип работы с двухмерным массивом, то вам будет легко разобраться в любом n-мерном массиве.


Некоторые функции для работы с массивами

Начнем с простых функций.

int ArraySize(object array[]);

Эта функция возвращает количество элементов, которые вмещает массив. Работает со всеми типами. Например:

// создаем два разных массива
int arrayInt[] = {1, 2, 3, 4};           
double arrayDouble[] = {5.9, 2.1, 4.7};
// здесь будем хранить количество элементов 
int amount;                        
 
amount = ArraySize(arrayInt);      // обратите внимание: 
                                   // что бы указать конкретный
                                   // массив, нужно указать только
                                   // его название.
                                   // Теперь amount равно 4
 
 
amount = ArraySize(arrayDouble);   // amount равно 3

Следующая функция:

int ArrayInitialize(object array[],double value);
ArrayInitialize

присваивает всем элементам массива значение value. Возвращает количество элементов, которым было присвоено значение. Используйте эту функцию с массивами типа int и double.

Дальше:

int ArrayMaximum(double array[], int count = WHOLE_ARRAY, 
                 int start = 0);
int ArrayMinimum(double array[], int count = WHOLE_ARRAY, 
                 int start = 0);

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

int array[] = {10, 100, 190, 3, 1};
// будет возвращено 1, так как array[1] - максимальное значение 
ArrayMaximum(array); 
// будет возвращено 4, потому что, array[4] - минимальное значение
ArrayMinimum(array);

int ArrayDimension(object array[]);

С помощью этой функции можно узнать размерность массива. То есть определить одномерный он, двухмерный или n-мерный. Пример:

int array1D[15];
int array4D[3][3][3];
 
ArrayDimension(array1D); // получим 1
ArrayDimension(array3D); // 3

А вот более сложные и полезные функции:

int ArraySort(double&array[], int count = WHOLE_ARRAY, int start = 0,
              int sort_dir = MODE_ASCEND);

Эта функция

сортирует

элементы. Если явно не указывать аргументы по умолчанию, например так:

int array[5] = {1, 10, 5, 7, 8};
 
ArraySort(array);

То элементы будут отсортированы

по возрастанию

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

  • int count - количество элементов, которые вы хотите отсортировать
  • int start - индекс элемента, с которого следует начать сортировку
  • int sort_dir - направление сортировки (по возрастанию - MODE_ASCEND или по убыванию - MODE_DESCEND)

Тут у вас должен возникнуть вопрос: что это за MODE_ASCEND и MODE_DESCEND?? Ведь указано, что должно быть целое число! Не волнуйтесь, все стает на свои места в следующем разделе - "Препроцессор". Если вам нужно, например, отсортировать 5 элементов, начиная с второго по убыванию следует написать, что-то вроде этого:

ArraySort(array, 5, 1, MODE_DESCEND);

И последняя на сегодня функция:

int ArrayCopy(object&dest[], object source[], int start_dest = 0,
              int start_source=0, int count=WHOLE_ARRAY);

Она предназначена для

копирования

одного массива в другой. Рассмотрим обязательные параметры:

  • dest[] - в какой массив копировать
  • source[] - из какого массива копировать

Необязательные параметры:

  • start_dest - индекс элемента массива, в который будет произведено копирование
  • start_source - индекс элемента массива, из которого будет произведено копирование
  • int count - количество элементов для копирования

Функция возвращает количество скопированных элементов. Используйте ArrayCopy с осторожностью: убедитесь, что массивы обладают достаточной вместимостью, когда что-то копируете в них!


Препроцессор

Что же это такое? Препроцессор - это специальный механизм, который предназначен для обработки исходного кода. То есть сначала препроцессор подготавливает код, а потом передает его для компиляции. Сегодня мы ознакомимся с одной полезной возможностью - константами.

В чем суть? Что бы разобраться, давайте вспомним пример из раздела про switch:

switch(marketState)
  {
    case 1:
    // стратегия торговли для восходящего тренда
    break;
 
    case 2:
    // стратегия для нисходящего тренда
    break;
 
    case 3:
    // стратегия для флета
    break;
 
    default:
    // ошибка: такого состояния не предусмотрено!
    break;
  }

Здесь мы реализовывали механизм, который действует по разному в зависимости от состояния рынка. Вспомнили? Так вот, было бы намного удобнее и нагляднее вместо 1, 2 и 3 написать, что вроде TREND_UP, TREND_DOWN, FLET:

switch(marketState)
  {
    case TREND_UP:
    // стратегия торговли для восходящего тренда
    break;
 
    case TREND_DOWN:
    // стратегия для нисходящего тренда
    break;
 
    case FLET:
    // стратегия для флета
    break;
 
    default:
    // ошибка: такого состояния не предусмотрено!
    break;
  }

В таком случае исходный код выглядит намного понятнее и нагляднее, согласны? Так вот, константы позволяют заменить TREND_UP, TREND_DOWN и FLET на соответствующие значения 1,2 и 3, до начала компиляции. Все что вам нужно, это указать что на что заменять препроцессору. Это делается с помощью директив препроцессора, которые начинаются с специального символа "#". Директивы препроцессора следует размещать в начале исходного файла вместе с другими директивами. Давайте посмотрим на законченный пример с использованием констант:

//+------------------------------------------------------------------+
//|                                                 preprocessor.mq4 |
//|         Copyright © 2007, Antonio Banderass. All rights reserved |
//|                                               banderassa@ukr.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Antonio Banderass. All rights reserved"
#property link      "banderassa@ukr.net"
 
#define TREND_UP   1
#define TREND_DOWN 2
#define FLET       3
 
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
   MessageBox("TREND_UP=" + TREND_UP + " TREND_DOWN=" + TREND_DOWN + 
              " FLET=" + FLET);
 
   return(0);
  }

Обратите внимание, мы разместили объявления констант в начале файла, под остальными директивами препроцессора. Рассмотрим объявление детальнее:

#define TREND_UP 1

Сначала следует ключевое слово #define. Оно указывает препроцессору, что дальше пойдет объявление константы. Потом идет название константы, ее идентификатор, то есть слово, по которому вы будете обращаться к значению константы. У нас это - TREND_UP. Потом следует само значение - 1. Теперь когда препроцессор увидит в исходном коде TREND_UP, он заменит это на 1 и так со всеми константами. Посмотрите на исходный код нашего примера до обработки препроцессором:

int start()
  {
   MessageBox("TREND_UP=" + TREND_UP + " TREND_DOWN=" + 
              TREND_DOWN + " FLET=" + FLET);
   return(0);
  }

и

после

:

int start()
  {
   MessageBox("TREND_UP=" + 1 + " TREND_DOWN=" + 2 + 
              " FLET=" + 3);
   return(0);
  }

Теперь вы должны понимать, что это за MODE_ASCEND и MODE_DESCEND из предыдущего раздела. Это были всего лишь константы, которым соответствуют определенные значения.



Заключение

Итак, в этой статье вы почерпнули много свежего материала: новый вид циклов - while; новый вид условий - switch; операторы break и continue, вы научились писать собственные функции и работать с многомерными массивами, а также узнали как использовать константы. Все это - ваш основной инструмент, фундамент для написания более продвинутых вещей, таких как пользовательские индикаторы и советники. Поэтому убедитесь, что основательно разобрались в этом, так как материал, изложенный в этой статье очень важен; и в будущем будет использоваться постоянно.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/1483

Прикрепленные файлы |
preprocessor.mq4 (0.86 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (19)
Antoniuk Oleg
Antoniuk Oleg | 17 авг. 2007 в 12:25
Pingvin-man писал(а):
banderass:
новая статья опубликована, приятного чтения)):
http://articles.mql4.com/ru/articles/1496

А когда же будет следующая (четвёртая) статья?
(в третьей не получается добавить коментарий).
Автору респект.


Ой, не могу сказать точно, пару недель, сейчас потихоньку пишу ее ...
[Удален] | 30 нояб. 2007 в 18:43

Статья очень полезная! Спасибо автору.

Однако есть пожелание более подробно описать работу с массивами. В частности обязательно ли задавить длину массива при объявлении? Особенности работы с предопределенными массивами-таймсесиями , массивами типа indicator_buffers и массивами объявляемыми внутри индикатора. Хотелось бы так же увидеть примеры применения ArrayGetAsSeries.

Думаю это будет полезно многим.

Antoniuk Oleg
Antoniuk Oleg | 1 дек. 2007 в 14:05
PalMen:

Статья очень полезная! Спасибо автору.


Однако есть пожелание более подробно описать работу с массивами.
В частности обязательно ли задавить длину массива при объявлении?
Особенности работы с предопределенными массивами-таймсесиями
, массивами типа indicator_buffers и массивами объявляемыми внутри индикатора.
Хотелось бы так же увидеть примеры применения ArrayGetAsSeries.


Думаю это будет полезно многим.

Длину массива задавать не обязательно, если вы явно инициализируете его, например, так:
int array[]={1,2,3,4,5}; 
// длина будет определена автоматически
Массивы типа indicator_buffers были рассмотрены в статье Язык MQL4 для "чайников". Пользовательские индикаторы (часть 1)
Остальные вопросы по возможности будут рассмотрены в следующих статьях.
[Удален] | 16 авг. 2011 в 21:43

видимо опечатка


   № Индексов  0    1    2   3  4
int array[] = {10, 100, 190, 3, 1};
 // будет возвращён индекс  2, так как ему соответствует максимальное значение 190
ArrayMaximum(array); 
mql-coach
mql-coach | 21 нояб. 2014 в 15:17

Научу Вас программировать советников на MQL4.

 

Репетитор по программированию на MQL4 для MetaTrader 4

Научу программировать собственные советники, индикаторы, скрипты.

Индивидуальные занятия по скайпу, могу уделять много времени на обучение.

Будем работать до результата. Получите большой практический опыт.

Вместе напишем советника по вашему алгоритму.

Форму обучения выстраиваем индивидуально.

После обучения сможете самостоятельно создавать собственные индикаторы и советники.

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

Звоните или пишите прямо сейчас, пообщаемся, отвечу на все вопросы.

Скайп: kresh91

Почта: mql.coach@gmail.com

ZUP - зигзаг универсальный с паттернами Песавенто. Часть 2 ZUP - зигзаг универсальный с паттернами Песавенто. Часть 2
ZUP - зигзаг универсальный с паттернами Песавенто. Часть 2 - описание встроенных инструментов
Практическое использование Виртуального Частного Сервера (VPS) для автоторговли Практическое использование Виртуального Частного Сервера (VPS) для автоторговли
Автоторговля с помощью VPS. Данная статья предназначена исключительно для автотрейдеров и сторонников автоторговли.
Индикатор Alternative Ichimoku – настройка, примеры использования Индикатор Alternative Ichimoku – настройка, примеры использования
Как правильно настроить Alternative Ichimoku. Прочтите описание настройки параметров. Статья поможет разобраться в способах настройки параметров не только индикатора Alternative Ichimoku. Конечно Вы лучше поймете как настроить стандартный Ichimoku Kinko Hyo тоже.
Прогнозирование цен с помощью нейронных сетей Прогнозирование цен с помощью нейронных сетей
Многие трейдеры говорят о нейронных сетях, но что это такое и на что они в реальности способны - мало кто представляет. Данная статья немного приоткрывает дверь в мир искуственного интеллекта. В ней рассказывается о том, как нужно правильно подготавливать данные для сети, а также приводится пример прогнозирования средствами программы Matlab.