If semicolon expected mql4 ошибка

Введение

При использовании новой версии компилятора языка MQL4 некоторые старые программы могут выдавать ошибки.

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

Новый компилятор позволяет обнаружить реальные или потенциальные источники ошибок и повысить качество кода.

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

  1. Ошибки компиляции
    • 1.1. Идентификатор совпадает с зарезервированным словом
    • 1.2. Специальные символы в наименованиях переменных и функций
    • 1.3. Ошибки использования оператора switch
    • 1.4. Возвращаемые значения у функций
    • 1.5. Массивы в аргументах функций
  2. Ошибки времени выполнения
    • 2.1. Выход за пределы массива (Array out of range)
    • 2.2. Деление на ноль (Zero divide)
    • 2.3. Использование 0 вместо NULL для текущего символа
    • 2.4. Строки в формате Unicodе и их использование в DLL
    • 2.5. Совместное использование файлов
    • 2.6. Особенность преобразования datetime
  3. Предупреждения компилятора
    • 3.1. Пересечения имен глобальных и локальных переменных
    • 3.2. Несоответствие типов
    • 3.3. Неиспользуемые переменные

1. Ошибки компиляции

При наличии ошибок в коде программа не может быть скомпилирована.

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

#property strict

Этот режим значительно упрощает поиск ошибок.

1.1. Идентификатор совпадает с зарезервированным словом

Если наименование переменной или функции совпадает с одним из зарезервированных слов:

int char[];  
int char1[]; 
int char()   
{
 return(0);
}

то компилятор выводит сообщения об ошибках:

Рис.1. Ошибки "unexpected token" и "name expected"

Рис.1. Ошибки «unexpected token» и «name expected»

Для исправления данной ошибки нужно исправить имя переменной или функции.

1.2. Специальные символы в наименованиях переменных и функций

Если наименования переменных или функций содержат специальные символы ($, @, точка):

int $var1; 
int @var2; 
int var.3; 
void f@()  
{
 return;
}

то компилятор выводит сообщения об ошибках:

Рис.2. Ошибки "unknown symbol" и "semicolon expected"

Рис.2. Ошибки «unknown symbol» и «semicolon expected»

Для исправления данной ошибки нужно скорректировать имена переменных или функций.

1.3. Ошибки использования оператора switch

Старая версия компилятора позволяла использовать любые значения в выражениях и константах оператора switch:

void start()
  {
   double n=3.14;
   switch(n)
     {
      case 3.14: Print("Pi");break;
      case 2.7: Print("E");break;
     }
  }

В новом компиляторе выражения и константы оператора switch должны быть целыми числами, поэтому при использовании подобных конструкций возникают ошибки:

Рис.3. Ошибки "illegal switch expression type" и "constant expression is not integral"

Рис.3. Ошибки «illegal switch expression type» и «constant expression is not integral»

В таких случаях можно использовать явные сравнения численных значений, например:

void start()
  {
   double n=3.14;
   if(n==3.14) Print("Pi");
   else
      if(n==2.7) Print("E");
  }

1.4. Возвращаемые значений функций

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

int function()
{
}

При строгом режиме компиляции (strict) возникает ошибка:

Рис.4. Ошибка "not all control paths return a value"

Рис.4. Ошибка «not all control paths return a value»

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.5. Предупреждение "not all control paths return a value"

Рис.5. Предупреждение «not all control paths return a value»

Если возвращаемое значение функции не соответствует объявлению:

int init()                         
  {
   return;                          
  }

то при строгом режиме компиляции возникает ошибка:

Рис.6. Ошибка "function must return a value"

Рис.6. Ошибка «function must return a value»

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.7. Предупреждение 'return - function must return a value"

Рис.7. Предупреждение ‘return — function must return a value»

Для исправления таких ошибок в код функции нужно добавить оператор возврата return c возвращаемым значением соответствующего типа.

1.5. Массивы в аргументах функций

Массивы в аргументах функций теперь передаются только по ссылке.

double ArrayAverage(double a[])
{
 return(0);
}

Данный код при строгом режиме компиляции (strict) приведет к ошибке:

Рис.8. Ошибка компилятора "arrays passed by reference only"

Рис.8. Ошибка компилятора «arrays passed by reference only»

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.9. Предупреждение компилятора "arrays passed by reference only"

Рис.9. Предупреждение компилятора «arrays passed by reference only»

Для исправления таких ошибок нужно явно указать передачу массива по ссылке, добавив префикс & перед именем массива:

double ArrayAverage(double &a[])
{
 return(0);
}

Следует отметить, что теперь константные массивы (Time[], Open[], High[], Low[], Close[], Volume[]) не могут быть переданы по ссылке. Например, вызов:

ArrayAverage(Open);

вне зависимости от режима компиляции приводит к ошибке:

Рис.10. Ошибка 'Open' - constant variable cannot be passed as reference

Рис.10. Ошибка ‘Open’ — constant variable cannot be passed as reference

Для устранения подобных ошибок нужно скопировать необходимые данные из константного массива:

   
   double OpenPrices[];
   
   ArrayCopy(OpenPrices,Open,0,0,WHOLE_ARRAY);
   
   ArrayAverage(OpenPrices);

2. Ошибки времени выполнения

Ошибки, возникающие в процессе исполнения кода программы принято называть ошибками времени выполнения (runtime errors). Такие ошибки обычно зависят от состояния программы и связаны с некорректными значениями переменных.

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

2.1. Выход за пределы массива (Array out of range)

Эта ошибка часто возникает в индикаторах при обращении к индикаторным буферам. Функция IndicatorCounted() возвращает количество баров, неизменившихся после последнего вызова индикатора. Значения индикаторов на уже рассчитанных ранее
барах не нуждаются в пересчете, поэтому для ускорения расчетов
достаточно обрабатывать только несколько последних баров.

Большинство индикаторов, в которых используется данный способ оптимизации вычислений, имеют вид:



int start()
  {
   
   if (Bars<100) 
     return(-1); 

   
   int counted_bars=IndicatorCounted();
   
   if(counted_bars<0) return(-1);
      
   
   int limit=Bars-counted_bars;

   
   if(counted_bars==0) 
     {
      limit--;  
      
      limit-=10;
     }
   else 
     {     
      
      limit++;
     } 
   
   for (int i=limit; i>0; i--)
   {
     Buff1[i]=0.5*(Open[i+5]+Close[i+10]) 
   }
}

Часто встречается некорректная обработка случая counted_bars==0 (начальную позицию limit нужно уменьшить на значение, равное 1 + максимальный индекс относительно переменной цикла).

Также следует помнить о том, что в момент исполнения функции start() мы можем обращаться к элементам массивов индикаторных буферов от 0 до Bars()-1. Если есть необходимость работы с массивами, которые не являются индикаторными буферами, то их размер следует увеличить при помощи функции ArrayResize() в соответствии с текущим размером индикаторных буферов. Максимальный индекс элемента для адресации также можно получить вызовом ArraySize() с одним из индикаторных буферов в качестве аргумента.

2.2. Деление на ноль (Zero divide)

Ошибка «Zero divide» возникает в случае, если при выполнении операции деления делитель оказывается равен нулю:

void OnStart()
  {

   int a=0, b=0,c;
   c=a/b;
   Print("c=",c);
  }

При выполнении данного скрипта во вкладке «Эксперты» возникает сообщение об ошибке и завершении работы программы:

Рис.11. Сообщение об ошибке "zero divide"

Рис.11. Сообщение об ошибке «zero divide»

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

Самый простой способ — проверять делитель перед операцией деления и выводить сообщение об некорректном значении параметра:

void OnStart()
  {

   int a=0, b=0,c;
   if(b!=0) {c=a/b; Print(c);}
   else {Print("Error: b=0"); return; };
  }

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

Рис. 12. Сообщение о некорректном значении делителя

Рис. 12. Сообщение о некорректном значении делителя

2.3. Использование 0 вместо NULL для текущего символа

В старой версии компилятора допускалось использование 0 (нуля) в качестве аргумента в функциях, требующих указания финансового инструмента.

Например, значение технического индикатора Moving Average для текущего символа можно было запрашивать следующим образом:

AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); 

В новом компиляторе для указания текущего символа нужно явно указывать NULL:

AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); 

Кроме того, текущий символ и период графика можно указать при помощи функций Symbol() и Period().

AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); 

2.4. Строки в формате Unicodе и их использование в DLL

Строки теперь представляют собой последовательность символов Unicode.

Следует учитывать этот факт и использовать соответствующие функции Windows. Например, при использовании функций библиотеки wininet.dll вместо InternetOpenA() и InternetOpenUrlA() следует вызывать InternetOpenW() и InternetOpenUrlW().

В MQL4 изменилась внутренняя структура строк (теперь она занимает 12 байт), поэтому при передаче строк в DLL следует использовать структуру MqlString:

#pragma pack(push,1)
struct MqlString
  {
   int      size;       
   LPWSTR   buffer;     
   int      reserved;   
  };
#pragma pack(pop,1)

2.5. Совместное использование файлов

В новом MQL4 при открытии файлов необходимо явно указывать флаги FILE_SHARE_WRITE и FILE_SHARE_READ для совместного использования.

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

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

   
   ExtHandle=FileOpenHistory(c_symbol+i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ);

Подробности можно найти в статье в статье «Оффлайновые графики и новый MQL4«.

2.6. Особенность преобразования datetime

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

  datetime date=D'2014.03.05 15:46:58';
  string str="mydate="+date;

Например, попытка работы с файлами, имя которых содержит двоеточие, приведет к ошибке.

3. Предупреждения компилятора

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

Чистый код не должен содержать предупреждений.

3.1. Пересечения имен глобальных и локальных переменных

Если на глобальном и локальном уровнях присутствуют переменные с одинаковыми именами:

int i; 
void OnStart()
  {

   int i=0,j=0; 
   for (i=0; i<5; i++) {j+=i;}
   PrintFormat("i=%d, j=%d",i,j);
  }

то компилятор выводит предупреждение и укажет номер строки, на которой объявлена глобальная переменная:

Рис.13. Предупреждение "declaration of '%' hides global declaration at line %"

Рис.13. Предупреждение «declaration of ‘%’ hides global declaration at line %»

Для исправления таких предупреждений нужно скорректировать имена глобальных переменных.

3.2. Несоответствие типов

В новой версии компилятора введена операция приведения типов.

#property strict
void OnStart()
  {
   double a=7;
   float b=a;
   int c=b;
   string str=c;
   Print(c);
  }

В строгом режиме компиляции при несоответствии типов компилятор выводит предупреждения:

Рис.14. Предупреждения "possible loss of data due to type conversion" и "implicit conversion from 'number' to 'string'

Рис.14. Предупреждения «possible loss of data due to type conversion» и «implicit conversion from ‘number’ to ‘string’

В данном примере компилятор предупреждает о возможной потере точности при присвоении различных типов данных и неявном преобразовании типа int в string.

Для исправления нужно использовать явное приведение типов:

#property strict
void OnStart()
  {
   double a=7;
   float b=(float)a;
   int c=(int)b;
   string str=(string)c;
   Print(c);
  }

3.3. Неиспользуемые переменные

Наличие переменных, которые не используются в коде программы (лишние сущности) не является хорошим тоном.

void OnStart()
  {
   int i,j=10,k,l,m,n2=1;
   for(i=0; i<5; i++) {j+=i;}
  }

Сообщения о таких переменных выводятся вне зависимости от режима компиляции:

Рис.15. Предупреждения "variable '%' not used'

Рис.15. Предупреждения «variable ‘%’ not used’

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

Выводы

В статье рассмотрены типичные проблемы, с которыми могут столкнуться программисты при компиляции старых программ, содержащих ошибки.

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

Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.

  • #1

Решил вот создать тему. Как-то хотел выучить MQL4, прочел весь справочник и видеоуроки смотрел. Но времени, чтобы практикаваться как-то пожалел. Но польза всё таки была и мало-мальскими знаниями я овладел. Думаю будет полезно, за одно и сам чего-нибудь нового узнаю. Глядишь и нормально разбираться начну:)

  • #2

Ошибки со старых билдов.

Исправляем ошибку char .

1) Попался индикатор под названием @Ratio_MACD_v3.mq4
2) Кидаем его на график и видим ошибку во вкладке «Эксперты»

334755

3) В навигаторе щелкаем правой кнопкой мыши по нашему индикатору и выбираем «Изменить»,щёлкаем левой кнопкой мыши.

334756

4) После этого у нас откроется MetaEditor — это программка предназначена для написания, редактирования кода.
5) Нажимем кнопку «Компилировать»

334757

6) Внизу во вкладке «Ошибки» увидим следующую картину

334758

7) Наводим курсор на первую ошибку и два раза щелкаем левой кнопкой мыши. При этом мы переместимся на строку этой ошибки.

334759

8) Если хотим узнать, почему возникла эта ошибк, нажимаем F1. При нажатии этой клавиши открывается справочник, где в нашем случае мы увидим следующее.

334760

Из этого следует, что (так скажем) слово char выражает, показывает нам какой тип данных перед нами. Иными словами, это слово занято программой и его использовать нельзя.
Вот что по этому поводу говорят на сайте самого mql

h_ttps://www.mql5.com/ru/articles/1391 (уберите черточку после h, чтобы перейти на сайт)

334762

9) Меняем это слово на любое другое, или просто добавляем какой-нибудь символ к нему.
На картинке выше, добавили 1. Я обычно добавляю букву p.

334764

При этом слово становится черного цвета, а не синего как раньше.
В нашем примере всего 9 слов надо исправить.

10) Снова нажимаем кнопку компилировать. Внизу видим, что никаких ошибок нет.

334765

11) Закрываем метаэдитор. Заходим в терминал, кидаем индикатор на график и видим что он работает.

334767

Последнее редактирование модератором:

  • #3

Нерабочий вариант индикатора.
Чтобы работал, надо исправить как описано выше.

  • @Ratio_MACD_v3.mq4

    9,5 КБ · Просмотры: 28

  • #4

Перед тем как использовать переменную нужно её объявить (захвалить как в игре 1000).
При объявлении надо указать тип и имя переменной, можно сразу присвоить значение.
Типы переменных заданы в языке и определяет свойство переменной (диапазон значений и точность), имя сочиняет программист на своё усмотрение.
Формат: тип имя; или тип имя=значение. Например: int peremennaya=0;
Здесь int тип переменной. Означает что она целочисленная и её значения от -2 147 483 648 до 2 147 483 647. Это можно посмотреть в справочнике. peremennaya это имя переменной. =0 присвоенное значение.
char то же тип целочисленной переменной и её значения от -128 до 127.
Проблема оказалась в том что нельзя в качестве имени переменной использовать тип переменной. В старом языке не было такого типа как char, по этому программист спокойно использовал это имя переменной. В ново языке такое имя использовать нельзя. Именно по этому переименование этих переменных помогло.

  • #5

В силу разных причин основная масса подходит к штанге, пробует и не поднимает
Перед купанием многие стоят в воде
Простые надобности на примерах
1. Ошибки со старых билдов
2. Толщина и цвет линии
3. Ограничение истории
4. Выставление стрелки и отступ
5. Вывод в настройки
6. Как найти буфер сигнала
7. Сообщения
8. Вывод сигнала или обьединение в другой индюк или сов
Это навскидку
Мона дополнять

  • #6

При изучении любого языка программирования, на начальном этапе, очень полезно решать простейшие задачки.
Напишите скрипт который выводит все числа от 1 до 1000, которые отвечают хотя бы одному условию.

  • Число четное
  • Число оканчивается цифрой 1 (пример 771).

  • #7

При изучении любого языка программирования, на начальном этапе, очень полезно решать простейшие задачки.
Напишите скрипт который выводит все числа от 1 до 1000, которые отвечают хотя бы одному условию.

  • Число четное
  • Число оканчивается цифрой 1 (пример 771).

Да все это хорошо и по карйней мере мне интересно. И я благодарен всем кто участвует.
Но как мне кажется, вот эти задачки надо все же перекинуть в тему вопросы по MQL4. А тут оставить конкретные примеры,ситуации, которые возникают у большинства обычных пользователей, как предложил Dersu.

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

  • #8

Да все это хорошо и по карйней мере мне интересно. И я благодарен всем кто участвует.
Но как мне кажется, вот эти задачки надо все же перекинуть в тему вопросы по MQL4. А тут оставить конкретные примеры,ситуации, которые возникают у большинства обычных пользователей, как предложил Dersu.

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

Хорошо.
Начинающих изучение языка программирования MQL4, приглашаю в специально созданную ветку для обучения.

  • #9

Ошибки со старых билодов.

Ошибка

‘.’ — semicolon expected — другими словами, недопустимый символ.

334840

1) Открываем индикатор в метаэдиторе, нажимаем «компилировать», видим в окне ошибки. Щелкаем по первой два раза левой кнопкой мыши, попадаем на строку ошибки.
2) Выделяем точку в этой строке, нажимаем «поиск» — «заменить».

334841

3) Откроется окошко. Где в поле «Найти» будет вписана точка, в поле «Заменить на» вписываем какой-либо символ кроме запрещенных $ @ . Нажимаем «Заменить».

334842

Так заменяем точку в строках 40, 43-48

334843

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

4) Далее курсор перейдет на строку, где точка находится между цифрами.

334845

Там и далее пропускаем замену, нажав на кнопку «Найти далее».
5) Далее делаем замену во всех предложенных случаях, в строках от 170 до 227.
6) Все последующие строки, где точка является частью дробного числа, пропускаем.
Просматриваем точки до конца кода, после этого курсор перейдет на начало документа.
7) Нажимаем «Компилировать», если увидим такую картину, то все сделали правильно.
Желтый треугольник это предупреждение, и оно не является критической ошибкой, его можно пропустить.

334850

8) Если там все еще есть ошибки, то значит что-то пропустили. Исправляем.
9) Закрываем редактор и пользуемся индикатором.

334851

Нерабочий индикатор прикрепляю.

  • FXi 3 Semafor.mq4

    11,2 КБ · Просмотры: 22

Последнее редактирование модератором:

  • #10

Например вот здесь , точно такая же ошибка с точкой, о которой написал выше.

  • #11

В данном примере лучше использовать замену Bars.Count на Bars_Count, чтобы не попадать на точки в числах.
В поле «Найти» вставляем Bars.Count, а в поле «Заменить на» вставляем Bars_Count, это удобнее.

  • #12

В данном примере лучше использовать замену Bars.Count на Bars_Count, чтобы не попадать на точки в числах.
В поле «Найти» вставляем Bars.Count, а в поле «Заменить на» вставляем Bars_Count, это удобнее.

Там дело в том, что не только в Bars.Count надо заменить точку, там разных имен еще штук 5.

Чтоб не запутаться где надо,а где нет. Можно щелкать по каждой ошибке в окне ошибок и исправлять по отдельности. Но долго)

  • #13

Там дело в том, что не только в Bars.Count надо заменить точку, там разных имен еще штук 5.
Чтоб не запутаться где надо,а где нет. Можно щелкать по каждой ошибке в окне ошибок и исправлять по отдельности. Но долго)

В этих вещах всегда нужно искать золотую середину.

  • #14

Нельзя бездумно уничтожать точки. Так можно не исправить, а натворить ошибок. Точки могут не только разделять дробную часть числа, при использовании структур и классов то же точки. При том в структурах они похожи на неправильную переменную. Например in.dist может быть переменной из старого языка, новый компилятор будет ругаться, а может быть элементом структуры in из нового языка и компилятор не будет ругаться. По этому, надо обязательно ориентироваться на компилятор пока не знаете как отличить.

  • #15

Ограничение истории (подвал, гистограмма)

В теме «Доработка ботов» попросили добавить историю отображения.
Попробовал разобраться, именно в этом индикаторе, сделать это оказалось просто.
Но думаю, не все случаи такие простые. Так как я до этого хотел попробовать сделать это в индикаторе, который выложил Dersu и я не додумался, как это делается.

1) Открываем индикатор в метаэдиторе и находим выведенные настройки индикатора. Они начинаются со слова extern (англ. «внешний»)
2) Так как нам тоже надо вывести историю в настройки, мы пишем строку
extern (выводим во внешние настройки)
int (так как количество баров у нас целое число)
CountBars (задаем имя для выводимого параметра, оно и будет видно в настройках)

335084

3) Идем ниже, находим функцию int start ,
(эта функция осталась в индикаторах написанных раньше, на старых билдах.На новыйх функция int OnInit() )

335103

4) И меняем переменную Bars (количество баров на текущем графике) на имя, которое мы задали в строке 33. Меняем его только в выражении Bars-counted_bars. Компилируем.

335104

screenshot_2-jpg.335080

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

Последнее редактирование:

  • #16

Ошибки со старых билдов.

По умолчанию не отображаются уровни.

Чтобы исправить, надо просто перекомпилировать индикатор.
Пункты 3 — 5,11.

  • Не видно уровней.jpg

    Не видно уровней.jpg

    62,8 КБ · Просмотры: 36

  • После перекомпилирования.jpg

    После перекомпилирования.jpg

    81,8 КБ · Просмотры: 35

  • #17

Errors Fixed in @Ratio_MACD_v3

  • @Ratio_MACD_v3.mq4

    9,5 КБ · Просмотры: 29

  • @Ratio_MACD_v3.ex4

    28 КБ · Просмотры: 18

  • #18

Errors Fixed in FXi Semafor Indicator

  • FXi 3 Semafor.ex4

    23,9 КБ · Просмотры: 16

  • FXi 3 Semafor.mq4

    22,3 КБ · Просмотры: 29

  • #19

If any one want to fix errors in MetaTrader 4 (mq4) indicators, i will try to fix issues for free of cost. based on my free time. I am learning mql coding, for my skills improvement only i am doing this one.

  • #20

If any one want to fix errors in MetaTrader 4 (mq4) indicators, i will try to fix issues for free of cost. based on my free time. I am learning mql coding, for my skills improvement only i am doing this one.

Я специально выкладывал не исправленные версии индикаторов, чтобы люди сами исправляли, а не скачивали готовое решение =)

100

File reading error

101

Error of opening an *. EX4 for writing

103

Not enough free memory to complete compilation

104

Empty syntactic unit unrecognized by compiler

105

Incorrect file name in #include

106

Error accessing a file in #include (probably the file does not exist)

108

Inappropriate name for #define

109

Unknown command of preprocessor (valid #include, #define, #property, #import)

110

Symbol unknown to compiler

111

Function not implemented (description is present, but no body)

112

Double quote («) omitted

113

Opening angle bracket (<) or double quote («) omitted

114

Single quote (‘) omitted

115

Closing angle bracket «>» omitted

116

Type not specified in declaration

117

No return operator or return is found not in all branches of the implementation

118

Opening bracket of call parameters was expected

119

Error writing EX4

120

Invalid access to an array

121

The function is not of void type and the return operator must return a value

122

Incorrect declaration of the destructor

123

Colon «:» is missing

124

Variable is already declared

125

Variable with such identifier already declared

126

Variable name is too long (> 250 characters)

127

Structure with such identifier already defined

128

Structure is not defined

129

Structure member with the same name already defined

130

No such structure member

131

Breached pairing of brackets

132

Opening parenthesis «(» expected

133

Unbalanced braces (no «}»)

134

Difficult to compile (too much branching, internal stack levels are overfilled)

135

Error of file opening for reading

136

Not enough memory to download the source file into memory

137

Variable is expected

138

Reference cannot be initialized

140

Assignment expected (appears at declaration)

141

Opening brace «{» expected

142

Parameter can be a dynamic array only

143

Use of «void» type is unacceptable

144

No pair for «)» or «]», i.e. «(or» [ » is absent

145

No pair for «(or» [ «, i.e. «) «or»] » is absent

146

Incorrect array size

147

Too many parameters (> 64)

149

This token is not expected here

150

Invalid use of operation (invalid operands)

151

Expression of void type not allowed

152

Operator is expected

153

Misuse of break

154

Semicolon «;» expected

155

Comma «,» expected

156

Must be a class type, not struct

157

Expression is expected

158

«non HEX character» found in HEX or too long number (number of digits> 511)

159

String-constant has more than 65534 characters

160

Function definition is unacceptable here

161

Unexpected end of program

162

Forward declaration is prohibited for structures

163

Function with this name is already defined and has another return type

164

Function with this name is already defined and has a different set of parameters

165

Function with this name is already defined and implemented

166

Function overload for this call was not found

167

Function with a return value of void type cannot return a value

168

Function is not defined

170

Value is expected

171

In case expression only integer constants are valid

172

The value of case in this switch is already used

173

Integer is expected

174

In #import expression file name is expected

175

Expressions are not allowed on global level

176

Omitted parenthesis «)» before «;»

177

To the left of equality sign a variable is expected

178

The result of expression is not used

179

Declaring of variables is not allowed in case

180

Implicit conversion from a string to a number

181

Implicit conversion of a number to a string

182

Ambiguous call of an overloaded function (several overloads fit)

183

Illegal else without proper if

184

Invalid case or default without a switch

185

Inappropriate use of ellipsis

186

The initializing sequence has more elements than the initialized variable

187

A constant for case expected

188

A constant expression required

189

A constant variable cannot be changed

190

Closing bracket or a comma is expected (declaring array member)

191

Enumerator identifier already defined

192

Enumeration cannot have access modifiers (const, extern, static)

193

Enumeration member already declared with a different value

194

There is a variable defined with the same name

195

There is a structure defined with the same name

196

Name of enumeration member expected

197

Integer expression expected

198

Division by zero in constant expression

199

Wrong number of parameters in the function

200

Parameter by reference must be a variable

201

Variable of the same type to pass by reference expected

202

A constant variable cannot be passed by a non-constant reference

203

Requires a positive integer constant

204

Failed to access protected class member

205

Import already defined in another way

208

Executable file not created

209

‘OnCalculate’ entry point not found for the indicator

210

The continue operation can be used only inside a loop

211

Error accessing private (closed) class member

213

Method of structure or class is not declared

214

Error accessing private (closed) class method

216

Copying of structures with objects is not allowed

218

Index out of array range

219

Array initialization in structure or class declaration not allowed

220

Class constructor cannot have parameters

221

Class destructor can not have parameters

222

Class method or structure with the same name and parameters have already been declared

223

Operand expected

224

Class method or structure with the same name exists, but with different parameters (declaration!=implementation)

225

Imported function is not described

226

ZeroMemory() is not allowed for objects with protected members or inheritance

227

Ambiguous call of the overloaded function (exact match of parameters for several overloads)

228

Variable name expected

229

A reference cannot be declared in this place

230

Already used as the enumeration name

232

Class or structure expected

235

Cannot call ‘delete’ operator to delete the array

236

Operator ‘ while’ expected

237

Operator ‘delete’ must have a pointer

238

There is ‘default’ for this ‘switch’ already

239

Syntax error

240

Escape-sequence can occur only in strings (starts with ‘\’)

241

Array required — square bracket ‘[‘ does not apply to an array, or non arrays are passed as array parameters

242

Can not be initialized through the initialization sequence

243

Import is not defined

244

Optimizer error on the syntactic tree

245

Declared too many structures (try to simplify the program)

246

Conversion of the parameter is not allowed

247

Incorrect use of the ‘delete’ operator

248

It’s not allowed to declare a pointer to a reference

249

It’s not allowed to declare a reference to a reference

250

It’s not allowed to declare a pointer to a pointer

251

Structure declaration in the list of parameter is not allowed

252

Invalid operation of typecasting

253

A pointer can be declared only for a class or structure

256

Undeclared identifier

257

Executable code optimizer error

258

Executable code generation error

260

Invalid expression for the ‘switch’ operator

261

Pool of string constants overfilled, simplify program

262

Cannot convert to enumeration

263

Do not use ‘virtual’ for data (members of a class or structure)

264

Cannot call protected method of class

265

Overridden virtual functions return a different type

266

Class cannot be inherited from a structure

267

Structure cannot be inherited from a class

268

Constructor cannot be virtual (virtual specifier is not allowed)

269

Method of structure cannot be virtual

270

Function must have a body

271

Overloading of system functions (terminal functions) is prohibited

272

Const specifier is invalid for functions that are not members of a class or structure

274

Not allowed to change class members in constant method

276

Inappropriate initialization sequence

277

Missed default value for the parameter (specific declaration of default parameters)

278

Overriding the default parameter (different values in declaration and implementation)

279

Not allowed to call non-constant method for a constant object

280

An object is necessary for accessing members (a dot for a non class/structure is set)

281

The name of an already declared structure cannot be used in declaration

284

Unauthorized conversion (at closed inheritance)

285

Structures and arrays cannot be used as input variables

286

Const specifier is not valid for constructor/destructor

287

Incorrect string expression for a datetime

288

Unknown property (#property)

289

Incorrect value of a property

290

Invalid index for a property in #property

291

Call parameter omitted — <func (x,)>

293

Object must be passed by reference

294

Array must be passed by reference

295

Function was declared as exportable

296

Function was not declared as exportable

297

It is prohibited to export imported function

298

Imported function cannot have this parameter (prohibited to pass a pointer, class or structure containing a dynamic array, pointer, class, etc.)

299

Must be a class

300

#import was not closed

302

Type mismatch

303

Extern variable is already initialized

304

No exported function or entry point found

305

Explicit constructor call is not allowed

306

Method was declared as constant

307

Method was not declared as constant

308

Incorrect size of the resource file

309

Incorrect resource name

310

Resource file opening error

311

Resource file reading error

312

Unknown resource type

313

Incorrect path to the resource file

314

The specified resource name is already used

315

Argument expected for the function-like macro

316

Unexpected symbol in macro definition

317

Error in formal parameters of the macro

318

Invalid number of parameters for a macro

319

Too many parameters for a macro

320

Too complex, simplify the macro

321

Parameter for EnumToString() can be only an enumeration

322

The resource name is too long

323

Unsupported image format (only BMP with 24 or 32 bit color depth is supported)

324

An array cannot be declared in operator

325

The function can be declared only in the global scope

326

The declaration is not allowed for the current scope

327

Initialization of static variables with the values of local variables is not allowed

328

Illegal declaration of an array of objects that do not have a default constructor

329

Initialization list allowed only for constructors

330

No function definition after initialization list

331

Initialization list is empty

332

Array initialization in a constructor is not allowed

333

Initializing members of a parent class in the initialization list is not allowed

334

Expression of the integer type expected

335

Memory required for the array exceeds the maximum value

336

Memory required for the structure exceeds the maximum value

337

Memory required for the variables declared on the global level exceeds the maximum value

338

Memory required for local variables exceeds the maximum value

339

Constructor not defined

340

Invalid name of the icon file

341

Could not open the icon file at the specified path

342

The icon file is incorrect and is not of the ICO format

343

Reinitialization of a member in a class/structure constructor using the initialization list

344

Initialization of static members in the constructor initialization list is not allowed

345

Initialization of a non-static member of a class/structure on a global level is not allowed

346

The name of the class/structure method matches the name of an earlier declared member

347

The name of the class/structure member matches the name of an earlier declared method

348

Virtual function cannot be declared as static

349

The const modifier is not allowed for static functions

350

Constructor or destructor cannot be static

351

Non-static member/method of a class or a structure cannot be accessed from a static function

352

An overload operation (+,-,[],++,— etc.) is expected after the operator keyword

353

Not all operations can be overloaded in MQL4

354

Definition does not match declaration

355

An invalid number of parameters is specified for the operator

356

Event handling function not found

357

Method cannot be exported

358

A pointer to the constant object cannot be normalized by a non-constant object

359

Class templates are not supported yet

360

Function template overload is not supported yet

361

Function template cannot be applied

362

Ambiguous parameter in function template (several parameter types can be applied)

363

Unable to determine the parameter type, by which the function template argument should be normalized

364

Incorrect number of parameters in the function template

365

Function template cannot be virtual

366

Function templates cannot be exported

367

Function templates cannot be imported

368

Structures containing the objects are not allowed

369

String arrays and structures containing the objects are not allowed

370

A static class/structure member must be explicitly initialized

371

Compiler limitation: the string cannot contain more than 65 535 characters

372

Inconsistent #ifdef/#endif

373

Object of class cannot be returned, copy constructor not found

374

Non-static members and methods cannot be used

375

OnTesterInit() impossible to use without OnTesterDeinit()

376

Redefinition of formal parameter ‘%s’

377

Macro __FUNCSIG__ and __FUNCTION__ cannot appear outside of a function body

378

Invalid returned type. For example, this error will be produced for functions imported from DLL that return structure or pointer.

379

Template usage error

380

Not used

381

Illegal syntax when declaring pure virtual function, only «=NULL» or «=0» are allowed

382

Only virtual functions can be declared with the pure-specifier («=NULL» or «=0»)

383

Abstract class cannot be instantiated

384

A pointer to a user-defined type should be applied as a target type for dynamic casting using the dynamic_cast operator

385

«Pointer to function» type is expected

386

Pointers to methods are not supported

387

Error — cannot define the type of a pointer to function

388

Type cast is not available due to private inheritance

389

A variable with const modifier should be initialized during declaration

Don’t listen to the trolls at the mql5.com website who are spreading false information. Their only goal is to confuse you, so you don’t know how to continue with your struggle and finally spend money buying useless services from them scammers.

This project is written in classical MQL4. Classical MQL4 is not the same as the MQL5 dialect distributed with current versions of MetaTrader4. Officially it’s labeled as MQL4 but have a look at the compiler’s (MetaEditor) «About» dialog. It says «version 5.+» which stands for MQL5.
MetaEditor - About dialog
MetaTrader4 supports both classical MQL4 and MQL5 and will do so until the world ends. Again, this project is written in classical MQL4 (which I admit is not clearly enough stated).

In the README in the bin directory I describe how to compile classical MQL4. And I understand that most non-technical users have issues with following those instructions.

The most asked question is «Why don’t you convert classical MQL4 to new MQL4/MQL5?» Because ecosystem/functionalities in the framework are large and migration/conversion causes a massive amount of work. Furthermore everything would need to be tested again on multiple platforms to run reliably. What would be the advantage of such an effort? The project would support images in charts, graphic UI elements (buttons, dialogs, etc) and other non-important gimmicks. This little gain is not worth the massive effort.

Together with the MT4Expander indicators/experts build with this framework are much more powerful than any MetaTrader version ever released by MetaQuotes. Long story short: I don’t have the incentive to migrate the project to another language because the gain is too little. Unfortunately interest from the community also doesn’t justify such a decision.

Nonetheless I’m thinking about an improvement for affected users. There’s a Github Release page, but again updates are few and not often enough. For the future my solution will be to provide pre-compiled nightly builds. I will setup an automated CI/CD chain which will compile ready to use binaries every night.

Рассмотрим дополнительные функции и возможности MQL4, которые могут быть полезны в программировании советников.

Escape символы

Если вы хотите добавить кавычки или символ обратной черты в строку, вам нужно экранировать символ, используя обратную косую черту (\). Например, если вам нужно вставить двойную кавычку, escape-символ будет \». Для одиночной кавычки escape-символ — \’. Для обратной косой черты в качестве escape-символа используйте две обратные косые черты: \\

string EscQuotes = "Эта строка содержит \"двойные кавычки\""; // Эта строка содержит "двойные кавычки"
string EscQuote = "Эта строка содержит \'одинарные кавчки\'"; // Эта строка содержит 'одинарные кавычки'
string EscSlash = "Эта строка содержит обратную черту \\"; // Эта строка содержит обратную черту \

Если вам нужно, чтобы строка занимала несколько строк, используйте escape-символ \n для добавления новой строки:

string NewLine = "Эта строка \n новая строка"; // Output: Эта строка
новая строка

Использование комментариев на графике

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

Один из способов отображения комментариев к графику — объявить несколько строковых переменных и объединить их вместе с символами новой строки. Одна строка может использоваться для отображения настроек, другая — для отображения информационных сообщений или статуса ордера и т. д. Строка будет передана в функцию Comment().

Поместите функцию Comment() в конец функции start(), чтобы обновить комментарий на графике:

string SettingsComment = "FastMAPeriod: "+FastMAPeriod+" SlowMAPeriod: "+SlowMAPeriod; string StatusComment = "Размещен ордер на покупку";
Comment(SettingsComment+"\n"+StatusComment);

Мы объявляем и устанавливаем значения строк SettingsComment и StatusComment внутри функции start(). В конце функции start мы вызываем функцию Comment() и используем ее для вывода наших комментариев на графике. Мы используем символ новой строки (\n), чтобы разделить комментарий на две строки.

Проверка настроек

Существует несколько настроек советника, которые должны быть проверены, прежде чем советнику будет разрешено торговать. Эти настройки находятся на вкладке «Общие» в диалоговом окне «Свойства эксперта».

Параметр Allow live trading должен быть включен до начала торговли. Если он не включен, в правом верхнем углу графика рядом с именем советника появится эмоция недовольство. Вы можете проверить это условие в своем советнике, используя функцию IsTradeAllowed(). Если она возвращает false, настройка Allow live trading отключена.

Если вы хотите отобразить пользователю сообщение о том, что этот параметр должен быть активирован, вы можете сделать следующее:

if(IsTradeAllowed() == false) Alert("Активируйте настройку \'Allow live trading\' в свойствах советника!");

Если ваш эксперт использует внешнюю библиотеку .ex4, в свойствах эксперта должен быть включен параметр Разрешить импорт внешних экспертов. Вы можете проверить это с помощью функции IsLibrariesAllowed():

if(IsLibrariesAllowed() == false) Alert("Активруйте настройку \'Allow import of external experts\' в свойствах советника!");

То же самое можно сделать для DLL, используя функцию IsDllsAllowed():

if(IsDllsAllowed() == false) Alert("Активруйте настройку \'Allow DLL imports\' в свойствах советника!");

настройки советника

Ограчения аккаунтов

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

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

if(IsDemo() == false) {
Alert("Этот советник работает только на демо-аккаунте!");
return(0); }

Вы можете использовать функции учетной записи AccountName(), AccountNumber() и AccountBroker(), чтобы проверить имя учетной записи, номер аккаунта и брокера соответственно. Ограничение использования по номеру счета является распространенным и простым в реализации способом защиты:

int CustomerAccount = 123456;
if(AccountNumber() != CustomerAccount) {
Alert("Номер аккаунта не совпадает!");
return(0); 
}

Вы можете использовать AccountName() или AccountBroker() аналогичным образом. Для AccountBroker() вам сначала нужно использовать Print(), чтобы получить правильное возвращаемое имя брокера. Это значение будет напечатано в журнале экспертов.

Если вы решили продавать советник на коммерческой основе, имейте в виду, что файлы MQL легко декомпилировать. Существуют различные методы, которые могут использовать хакеры для взлома вашего советника, например, размещение функций во внешних библиотеках или DLL.

MessageBox()

До сих пор в этой книге мы использовали встроенную функцию Alert() для отображения сообщений об ошибках. Но что, если вы хотите настроить диалоги оповещений или запросить ввод данных у пользователя? Функция MessageBox() позволит вам создать собственный всплывающий диалог с помощью Windows API.

Чтобы использовать функцию MessageBox(), мы должны сначала выполнить #include файл WinUser32.mqh, который устанавливается вместе с MetaTrader. Этот файл импортирует функции из файла Windows user32.dll и определяет константы, необходимые для работы функции MessageBox(). Вот синтаксис функции MessageBox():

int MessageBox(string Text, string Title, int Flags);

Чтобы использовать функцию MessageBox(), мы должны определить текст, который будет отображаться во всплывающем диалоговом окне, а также заголовок, отображаемый в строке заголовка. Нам также нужно будет указать флаги, которые указывают, какие кнопки и значки должны появляться в нашем всплывающем окне. Если флаги не указаны, кнопка ОК будет использоваться по умолчанию. Флаги должны быть разделены символом (|).

Вот пример окна сообщения с кнопками Да / Нет и значком вопросительного знака:

// #include <WinUser32.mqh>
// start() function
int YesNoBox = MessageBox("Открыть сделку?","Подтверждение торговли",
      MB_YESNO|MB_ICONQUESTION);
if(YesNoBox == IDYES) {
        // Разместить ордер
      }

Флаг MB_YESNO указывает, что мы будем использовать кнопки «Да / Нет» в нашем окне сообщений, в то время как флаг MB_ICONQUESTION помещает значок вопросительного знака в диалоговом окне. Переменная YesNoBox содержит возвращаемое значение функций MessageBox(), которое будет указывать, какая кнопка была нажата.

Если кнопка «Да» была нажата, значением YesNoBox будет IDYES, и будет размещен ордер. Если кнопка «Нет» была нажата, флаг возврата будет IDNO. Вы можете использовать возвращаемое значение MessageBox() в качестве входных данных, чтобы определить порядок действий, таких как размещение ордера.

Ниже приведен неполный список флагов, которые можно использовать в окнах сообщений.

Флаги для кнопок

Эти флаги указывают, какие кнопки появляются в вашем окне сообщения:

  • MB_OKCANCEL — кнопки ОК и Отмена.
  • MB_YESNO — кнопки Да и Нет.
  • MB_YESNOCANCEL — кнопки Да, Нет и Отмена.

Флаги для иконок

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

  • MB_ICONSTOP — значок знака остановки.
  • MB_ICONQUESTION — значок знака вопроса.
  • MB_ICONEXCLAMATION — значок с восклицательным знаком.
  • MB_ICONINFORMATION — информационный значок.

Возвращаемые флаги

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

  • IDOK — нажата кнопка «ОК».
  • IDCANCEL — была нажата кнопка «Отмена».
  • IDYES — была нажата кнопка «Да».
  • IDNO — была нажата кнопка «Нет».

Уведомления по E-mail

Ваш эксперт может уведомить вас по электронной почте о размещенных сделках, возможных настройках торговли и многом другом. Функция SendMail() отправит электронное письмо с выбранной темой и текстом на адрес электронной почты, указанный в диалоговом окне «Сервис — Параметры» на вкладке «Электронная почта».

На вкладке «Электронная почта» необходимо сначала указать почтовый сервер SMTP с номером порта, например, mail.yourdomain.com:25, а также имя пользователя и пароль, если это необходимо.

Вы можете использовать любой адрес электронной почты в поле От. Поле «Кому» — это адрес электронной почты, на который отправляются сообщения.

Функция SendMail() имеет два аргумента: первый — это строка темы письма, а второй — содержимое самого письма. Вы можете использовать новые строки, экранированные символы, переменные и константы в теле вашего письма.

настройки e-mail

Вот пример использования функции SendMail():

string EmailSubject = "Ордер на покупку открыт";
string EmailBody = Ордер на покупку "+Ticket+" открыт на "+Symbol()+" по цене "+Ask; // "Ордер на покупку 12584 открыт на EURDUSD по цене 1.4544"
SendMail(EmailSubject,EmailBody);

Повтор после ошибки

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

Чтобы повторить ордер, мы поместим функцию OrderSend() в цикл while. Если OrderSend() не возвращает номер тикета, мы попробуем открыть ордер еще раз:

int Ticket = 0; while(Ticket <= 0) {
Ticket = OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,BuyTakeProfit); 
}

Сначала мы объявляем переменную для номера тикета, в данном случае Ticket. Пока Ticket не больше 0, цикл while с функцией OrderSend() будет выполняться снова и снова.

В случае ошибки или какой-либо другой неисправленной торговой ошибки, цикл будет повторяться бесконечно, и ваш эксперт будет зависать. Мы можем исправить это, добавив максимальное количество повторов:

int Retries = 0;
int MaxRetries = 5;
int Ticket = 0; while(Ticket <= 0) {
Ticket = OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,BuyTakeProfit);
        if(Retries <= MaxRetries) Retries++;
else break;
}

Мы объявляем переменную для использования в качестве счетчика (Retries) и максимальной настройки повторов (MaxRetries). Пока мы не превысили MaxRetries, переменная Retries увеличивается, и цикл повторяется снова. Как только MaxRetries достигнут, цикл завершается. После этого вы можете при необходимости предупредить пользователя об ошибке.

Если вы хотите сделать цикл повторных попыток зависимым от определенного состояния ошибки, мы можем проверить код ошибки по списку и вернуть значение true, если оно совпадение. Эта функция содержит некоторые распространенные коды ошибок:

    bool ErrorCheck(int ErrorCode)
      {
switch(ErrorCode)
  {
    case 128: // Trade timeout
    return(true);
 
    case 136: // Off quotes
    return(true);
 
 
  case 138: // Requotes
  return(true);
 
  case 146: // Trade context busy
  return(true);
 
default:
  return(false);
}
}

Эта функция использует оператор switch. Мы ищем метку, значение которой соответствует выражению, назначенному оператору switch (в данном примере ErrorCode).

Когда совпадение наблюдений найдено, блок switch должен быть завершен с оператором прерывания или возврата. В этом примере мы используем оператор return для возврата значения true / false вызывающей функции.

Вот как мы используем ErrorCheck(), чтобы повторить попытку размещения ордера:

int Retries;
int MaxRetries = 5;
int Ticket; while(Ticket <= 0) {
Ticket = OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,BuyTakeProfit);
if(Ticket == -1) int ErrCode = GetLastError();
if(Retries <= MaxRetries && ErrorCheck(ErrCode) == true) Retries++; else break;
}

Если Ticket возвращает -1, указывая на то, что произошла ошибка, мы получаем код ошибки с помощью GetLastError(). Мы передаем код ошибки в нашу функцию ErrorCheck(). Если код ошибки соответствует какой-либо из ошибок в функции проверки ошибок, ErrorCheck() вернет true, а функция OrderSend() будет повторена до 5 раз.

Использование комментариев к ордеру в качестве идентификатора

Мы использовали «магическое число» в качестве идентификатора ордера, который однозначно идентифицирует ордера, размещаемые конкретным советником. Если ваш эксперт размещает несколько ордеров одновременно, и вы хотите иметь возможность обрабатывать каждый из этих ордеров, вы можете использовать комментарий к ордеру в качестве необязательного идентификатора.

Допустим, ваш эксперт разместит два типа ордеров. Вы хотите иметь возможность изменить или закрыть эти ордера по отдельности. Вы можете использовать две функции OrderSend() и разместить разные комментарии к каждому из них. Затем, при выборе ордеров с использованием цикла, вы можете использовать OrderComment() в качестве одного из условий для нахождения ордеров для их изменения или закрытия.

string OrderComment1 = "Первый ордер"; 
string OrderComment2 = "Второй ордер";
 
// Размещение ордеров
int Ticket1 = OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,BuyTakeProfit,OrderComment1,MagicNumber,0,Green);
int Ticket2 = OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,BuyTakeProfit,OrderComment2,MagicNumber,0,Green);
 
// Изменение ордера
for(int Counter = 0; Counter <= OrdersTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS);
if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() && OrderComment() == OrderComment1)
{
             // Изменение первого ордера
          }
else if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() && OrderComment() == OrderComment2)
{
             // Изменение второго ордера
          }
}

Мы объявляем две строковые переменные для использования в качестве комментариев к ордеру. Функции OrderSend() размещают два ордера, каждый со своим комментарием. В следующем примере цикла модификации ордеров функция OrderComment() используется в качестве условия при выборе ордеров для модификации.

Вы можете использовать проверку OrderComment(), чтобы закрывать ордера независимо от других ордеров, использовать другие настройки трейлинг-стопа или что-либо другое, что требует ваша торговая система.

Проверка маржинальных требований

MetaTrader поставляется с функциями, которые позволяют вам проверить текущую свободную маржу или уровень стопов перед размещением ордера. Уровень Stop Out — это процент или сумма свободной маржи, ниже которой вы не сможете размещать ордера. Однако ручная проверка уровня свободной маржи или уровня стоп-ордера на самом деле не нужна, поскольку при попытке разместить ордер с слишком малым запасом маржипроизойдет ошибка.

Более полезная идея — определить собственный уровень стоп-аута и прекратить торговлю, если текущий капитал опустится ниже этого уровня. Давайте начнем с объявления внешней переменной MinimumEquity, которая является минимальной суммой капитала, необходимой на нашем счете, прежде чем мы сможем разместить ордер.

Мы сравним MinimumEquity с нашим текущим счетом. Если текущий эквити меньше нашего минимального эквити, ордер не будет размещен, и предупреждающее сообщение проинформирует пользователя о состоянии. Предположим, у нас есть баланс на счете 10000 долларов. Если мы потеряем более 20% этого капитала, мы не будет размещать ордера. Вот код для проверки минимального эквити:

// Внешние переменные
extern int MinimumEquity = 8000;
 
    // Размещение ордера
    if(AccountEquity() > MinimumEquity)
      {
        // Открываем ордер
}
else if(AccountEquity() <= MinimumEquity)
{
Alert("Размер эквити слишком низкий!");
}

Внешняя переменная MinimumEquity помещается в начало файла. Если текущий капитал, как указано AccountEquity(), больше MinimumEquity, ордер будет размещен. В противном случае заказ не будет размещен, и будет отображено предупреждающее сообщение.

Проверка спреда

Возможно, вы захотите избегать размещения ордеров в периоды, когда спред значительно расширяется. Мы можем установить максимальный спред и проверить текущий спред перед торговлей. Мы объявим внешнюю переменную MaximumSpread и используем MarketInfo() для проверки текущего спреда.

    // внешние переменные
extern int MaximumSpread = 5;
extern int MinimumEquity = 8000;
 
if(AccountEquity() > MinimumEquity && MarketInfo(Symbol(),MODE_SPREAD) < MaximumSpread) {
        // Place order
      }
else {
}
if(AccountEquity() <= MinimumEquity) Alert("Размер эквити ниже минимального!");
if(MarketInfo(Symbol(),MODE_SPREAD) > MaximumSpread) Alert("Размер спреда слишком большой!");
}

Обратите внимание, что мы выполняем проверку минимального эквити и спреда перед размещением ордера. Если одно из условий ложно, мы переходим в блок else и проверяем, какое из условий привело к тому, что ордер не был размещен. Мы будем отображать одно или несколько предупреждений в зависимости от того, какое условие выполняется.

Размещение нескольких ордеров

Вы можете разместить несколько ордеров с разными уровнями стоп-лосс и тейк-профит, а также размерами лота. Есть несколько способов сделать это. Один из способов — просто использовать разные операторы OrderSend() для каждого ордера. Это предполагает, что вы планируете размещать одинаковое количество ордеров каждый раз.

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

Начнем с определения внешних переменных для трех уровней стоп-лосс и тейк-профит. Любые дополнительные ордера выше трех не будут иметь стоп-лосс или тейк-профит. Мы также добавим внешнюю переменную, чтобы настроить количество размещаемых ордеров.

extern int StopLoss1 = 20; 
extern int StopLoss2 = 40; 
extern int StopLoss3 = 60;
 
extern int TakeProfit1 = 40; 
extern int TakeProfit2 = 80; 
extern int TakeProfit3 = 120;
 
extern int MaxOrders = 3;

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

double BuyTakeProfit[3];
double BuyStopLoss[3];
 
BuyTakeProfit[0] = CalcBuyTakeProfit(Symbol(),TakeProfit1,Ask); 
BuyTakeProfit[1] = CalcBuyTakeProfit(Symbol(),TakeProfit2,Ask); 
BuyTakeProfit[2] = CalcBuyTakeProfit(Symbol(),TakeProfit3,Ask);
 
BuyStopLoss[0] = CalcBuyStopLoss(Symbol(),StopLoss1,Ask); 
BuyStopLoss[1] = CalcBuyStopLoss(Symbol(),StopLoss2,Ask); 
BuyStopLoss[2] = CalcBuyStopLoss(Symbol(),StopLoss3,Ask);

Мы начнем с объявления массивов для хранения стоп-лосса и тейк-профита, BuyTakeProfit и BuyStopLoss. Количество элементов массива должно быть указано при объявлении массива. Индексы массива начинаются с нуля, поэтому, объявив размер измерения массива 3, наш начальный индекс равен 0, а наш самый большой индекс равен 2.

Далее мы рассчитываем цены стоп-лосс и тейк-профит, используя функции CalcBuyStopLoss() и CalcBuyTakeProfit(). Мы присваиваем рассчитанный стоп-лосс или тейк-профит соответствующему элементу массива. Обратите внимание, что первый индекс массива равен 0, а третий индекс массива равен 2.

Вот цикл for для размещения ордеров:

for(int Count = 0; Count <= MaxOrders - 1; Count++) {
int OrdInt = Count + 1;
OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,BuyStopLoss[Count], BuyTakeProfit[Count],"Buy Order "+OrdInt,MagicNumber,0,Green);
}

Переменная Count начинается с 0 и соответствует первому элементу массива. Количество циклов (т.е. количество размещаемых ордеров) определяется MaxOrders — 1. Для каждой итерации цикла мы увеличиваем стоп-лосс и тейк-профит на единицу.

Мы используем переменную OrdInt для увеличения количества ордеров в комментарии к ордеру. Первым комментарием к ордеру будет «Ордер на покупку 1», следующим будет «Ордер на покупку 2» и так далее. Функция OrderSend() размещает ордер с соответствующим стоп-лоссом и значением тейк-профита, используя переменную Count для выбора соответствующего элемента массива.

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

extern int StopLossStart = 20; 
extern int StopLossIncr = 20;
extern int TakeProfitStart = 40; 
extern int TakeProfitIncr = 40;
extern int MaxOrders = 5;

В приведенном выше примере стоп-лосс для нашего первого ордера составит 20 пунктов. Мы будем увеличивать стоп-лосс на 20 пунктов за каждый дополнительный ордер. То же самое для тейк-профита, за исключением того, что мы начнем с 40 и увеличиваем на 40. Вместо использования массивов мы рассчитаем стоп-лосс и тейк-профит в цикле for:

for(int Count = 0; Count <= MaxOrders - 1; Count++) {
int OrdInt = Count + 1;
 
int UseStopLoss =  StopLossStart + (StopLossIncr * Count);
 int UseTakeProfit =  TakeProfitStart + (TakeProfitIncr * Count);
double BuyStopLoss = CalcBuyStopLoss(Symbol(),UseStopLoss,Ask); double BuyTakeProfit = CalcBuyTakeProfit(Symbol(),UseTakeProfit,Ask);
OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,BuyStopLoss, BuyTakeProfit,"Buy Order "+OrdInt,MagicNumber,0,Green);
}

Мы определяем уровень тейк-профита и стоп-лосса в пунктах, умножая переменную StopLossIncr или TakeProfitIncr на значение Count и добавляя его к значению StopLossStart или TakeProfitStart. Для первого ордера уровень стоп-лосса или тейк-профита будет равен StopLossStart или TakeProfitStart.

Далее мы рассчитываем стоп-лосс и цену тейк-профита для ордера. Наконец, мы размещаем ордер с помощью OrderSend().

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

Глобальные переменные

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

Текущий список глобальных переменных в терминале можно просмотреть, выбрав «Глобальные переменные» в меню «Инструменты» или нажав клавишу F3 на клавиатуре.

Одним из способов использования этих переменных является сохранение определенных глобальных или статических переменных в терминале, чтобы, если советник был закрыт, мы могли продолжить работу с того места, где остановились. Не все советники требуют этого, но более сложные советники будут поддерживать определенное состояние, которое, в случае прерывания, приведет к отказу в работе советника.

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

Чтобы объявить глобальную переменную, используйте функцию GlobalVariableSet(). Первый аргумент — это строка, обозначающая имя глобальной переменной, а второй аргумент — это значение типа double, присваиваемое ей.

GlobalVariableSet(GlobalVariableName,DoubleValue);

Чтобы ваши имена переменных были уникальными, вы можете создать префикс глобальной переменной. Объявите глобальную переменную в своем эксперте и установите значение в функции init(), используя текущий символ, точку, имя советника и магический номер, чтобы создать уникальный префикс переменной.

  // Глобальные переменные
    string GlobalVariablePrefix;
int init() {
GlobalVariablePrefix = Symbol()+Period()+"_"+"ProfitBuster"+"_"+MagicNumber+"_"; }

Мы используем текущий символ и период, а также идентификатор советника и внешнюю переменную MagicNumber. Теперь, когда мы устанавливаем глобальную переменную с помощью GlobalVariableSet(), мы используем префикс, который мы определили выше вместе с фактическим именем переменной:

GlobalVariableSet(GlobalVariablePrefix+Counter,Counter);

Поэтому, если мы торгуем на EURUSD на таймфрейме M15 с советником с именем «ProfitBuster», используя 11 в качестве магического числа и Counter в качестве имени переменной, именем нашей глобальной переменной будет EURUSD15_ProfitBuster_11_Counter.

Чтобы получить значение глобальной переменной, используйте функцию GlobalVariableGet() с именем переменной в качестве аргумента:

Counter = GlobalVariableGet(GlobalVariablePrefix+Counter);

Чтобы удалить глобальную переменную, используйте функцию GlobalVariableDel() с именем переменной в качестве аргумента. Чтобы удалить все глобальные переменные, размещенные вашим советником, используйте функцию GlobalVariableDeleteAll() с префиксом в качестве аргумента.

    GlobalVariableDel(GlobalVariablePrefix+Counter);
    GlobalVariableDeleteAll(GlobalVariablePrefix);

Проверка прибыли ордера

Иногда может быть полезно проверить текущую прибыль по ордеру или проверить общую прибыль по ордеру, который уже закрыт. Есть два способа проверить прибыль. Чтобы получить прибыль в валюте депозита, используйте функцию OrderProfit(). Сначала вы должны выбрать ордер, используя OrderSelect().

OrderSelect(Ticket,SELECT_BY_TICKET); 
double GetProfit = OrderProfit(Ticket);

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

Чтобы получить прибыль или убыток в пунктах, вам необходимо рассчитать разницу между ценой открытия ордера и ценой закрытия ордера. Вам также нужно будет использовать функцию OrderSelect() для получения цен открытия и закрытия.

OrderSelect(Ticket,SELECT_BY_TICKET);
if(OrderType() == OP_BUY) double GetProfit = OrderClosePrice() - OrderOpenPrice(); else if(OrderType() == OP_SELL) GetProfit = OrderOpenPrice() - OrderClosePrice();
GetProfit /= PipPoint(Symbol());

Для заявок на покупку мы рассчитываем прибыль, вычитая цену открытия из цены закрытия. Для ордеров на продажу мы делаем наоборот. После того, как мы вычислили разницу, мы можем преобразовать прибыль или убыток в целое число, разделив его на пункт, используя нашу функцию PipPoint().

Например, если цена открытия ордера на покупку составляет 1,4650, а цена закрытия — 1,4700, разница между OrderClosePrice() и OrderOpenPrice() составляет 0,0050. Когда мы делим это на нашу функцию PipPoint(), результат равен 50. Таким образом, для этого ордера мы получаем 50 пунктов прибыли. Если бы цена закрытия ордера была 1.4600, то мы бы потеряли 50 пунктов.

Мартингейл

Мартингейл — это система ставок, обычно используемая в рулетке и блэкджеке, где размер ставки удваивается после каждого последовательного проигрыша. Теория состоит в том, что одна выигрышная ставка вернет наш баланс к безубыточности. Недостатком мартингейла является то, что вам потребуется большой размер капитала, чтобы выдержать просадки.

Например, если размер вашего начального лота составляет 0,1 лота, то после 4 последовательных потерь ваш размер лота составит 1,6 лота — в 16 раз больше исходного размера лота. После 7 последовательных проигрышей размер вашего лота составит 12,8 лота — в 128 раз больше исходного размера лота! Долгая полоса неудач может легко уничтожить ваш аккаунт, прежде чем вы сможете вернуть его в безубыточность.

Тем не менее, вы можете включить систему увеличения размера лота при последовательных выигрышах или проигрышах, и это можно сделать, не уничтожая свой счет. Самый простой способ — установить ограничение на количество раз, чтобы увеличить размер лота. Надежная торговая система не должна иметь более 3 или 4 максимальных последовательных убытков. Вы можете определить это, изучив максимальное количество последовательных потерь на вкладке «Отчет» в окне «Тестер стратегий».

Другой способ — увеличить размер вашего лота на меньший множитель. Классическая стратегия Мартингейла удваивает размер лота после каждого последующего проигрыша. Возможно, вы захотите использовать множитель меньше 2. Существует также стратегия анти-мартингейл, где вы увеличиваете размер лота после каждого последовательного выигрыша.

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

Пользователь сможет выбирать между стратегией Мартингейл (проигрыш) или Анти-Мартингейл (выигрыш). Будет включена настройка ограничения максимального количества последовательных увеличений лота, и множитель лота будет настраиваться.

Сначала посчитаем количество последовательных побед или проигрышей. Нам нужно будет перебрать пул истории ордеров в обратном порядке, начиная с самого последнего закрытого ордера. Мы будем увеличивать счетчик для каждого выигрыша или проигрыша. Пока сохраняется схема последовательных выигрышей или проигрышей, мы продолжим цикл. Как только паттерн нарушается (выигрыш определяется после одного или нескольких проигрышей или наоборот), цикл завершается.

int WinCount;
int LossCount;
for(int Count = OrdersHistoryTotal()-1; ; Count--) {
OrderSelect(Count,SELECT_BY_POS,MODE_HISTORY);
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
if(OrderProfit() > 0 && LossCount == 0) WinCount++;
else if(OrderProfit() < 0 && WinCount == 0) LossCount++; else break;
} 
}

Мы начнем с объявления переменных для счетчиков выигрышей и проигрышей. Обратите внимание, что в операторе for мы используем OrdersHistoryTotal() для определения начальной позиции. OrdersHistoryTotal() возвращает количество ордеров в пуле истории. Вычитаем 1, чтобы определить позицию индекса для самого последнего ордера, который хранится в переменной Count.

Обратите внимание, что мы пропустили второе выражение в цикле for — то, которое определяет условие прекращения цикла. Мы будем уменьшать переменную Count на каждой итерации цикла.

Мы используем MODE_HISTORY в качестве третьего аргумента в функции OrderSelect(), чтобы указать, что мы перебираем закрытый пул истории ордеров. По умолчанию OrderSelect() использует пул открытых ордеров, поэтому мы должны указать MODE_HISTORY при проверке пула закрытых ордеров.

Мы проверяем, что текущий выбранный ордер соответствует нашему символу графика и нашему магическому номеру. Затем мы проверяем прибыль по ордеру с помощью функции OrderProfit(). Если возвращаемое значение указывает на прибыль (то есть больше нуля), то мы увеличиваем переменную WinCount. Если это потеря, мы увеличиваем LossCount.

Поскольку мы ищем последовательные выигрыши или проигрыши, нам нужно завершить цикл, как только будет найдено альтернативное условие. Для этого мы проверяем переменную WinCount или LossCount при проверке прибыли ордера.

Например, если у нас есть 2 последовательных убытка — это означает, что LossCount = 2 — и наш следующий ордер будет выигрышным, тогда оба наших оператора if будут ложными, и управление перейдет к оператору break, который завершит цикл.

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

Переменная WinCount или LossCount будет содержать количество последовательных выигрышей или проигрышей. Если мы хотим реализовать стратегию Мартингейла, мы используем LossCount, чтобы определить фактор, с помощью которого можно увеличить размер лота. Если мы делаем анти-Мартингейл, вместо этого мы используем WinCount.

Мы будем использовать внешнюю целочисленную переменную с именем MartingaleType. Если MartingaleType установлен в 0, мы будем использовать стратегию Martingale. Если установлено значение 1, мы будем использовать стратегию анти-мартингейла.

// Внешние переменные
extern int MartingaleType = 0; // 0: Martingale, 1: Anti-Martingale
extern int LotMultiplier = 2; 
extern int MaxMartingale = 4; 
extern double BaseLotSize = 0.1;
 
// Расчет размера лота
if(MartingaleType == 0) int ConsecutiveCount = LossCount; else if(MartingaleType = 1) ConsecutiveCount = WinCount;
if(ConsecutiveCount > MaxMartingale) ConsecutiveCount = MaxMartingale; double LotSize = BaseLotSize * MathPow(LotMultiplier,ConsecutiveCount);

Мы устанавливаем значение ConsecutiveCount либо в WinCount, либо в LossCount, в зависимости от настройки MartingaleType. Мы сравним это с нашей настройкой MaxMartingale. Если количество наших последовательных ордеров больше, чем MaxMartingale, мы изменим его размер, чтобы он был равен MaxMartingale. (Вы также можете изменить его размер до размера лота по умолчанию, если хотите). Размер лота будет оставаться на этом размере до тех пор, пока выигрыш или проигрыш не нарушат нашу последовательную серию ордеров.

Размер лота определяется умножением нашего BaseLotSize на LotMultiplier, который экспоненциально увеличивается на ConsecutiveCount. Функция MathPow() увеличивает число до указанной степени. Например, если размер нашего начального лота равен 0,1, множитель лота равен 2, и у нас есть четыре последовательных ордера, уравнение равно 0,1 * 24 = 1,6.

Настраивая LotMultiplier и используя стратегии как Мартингейл, так и Анти-Мартингейл, это даст вам достаточно возможностей для экспериментов с использованием экспоненциального размера лотов. Вы можете легко изменить код выше, чтобы использовать другие варианты. Например, вы можете масштабировать размеры лотов в обратном порядке, от самого большого до самого маленького. Или вы можете использовать внешний счетчик вместо ConsecutiveCount.

Отладка работы вашего советника

В отличие от большинства программных средств разработки, MetaEditor не поддерживает современные методы отладки. Поэтому вам придется использовать операторы Print() и журнал для отладки ваших экспертов.

Распечатав содержимое переменных и функций в журнал, вы можете проверить вывод вашего кода и исправить любые ошибки.

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

Журналы тестера стратегий хранятся в папке \tester\logs. Щелкните правой кнопкой мыши в любом месте окна журнала и выберите «Открыть» во всплывающем меню. Откроется окно проводника Windows, отображающее содержимое папки журнала. Имена файлов имеют формат yyyymmdd.log, где yyyy — год из четырех цифр, mm — месяц из двух цифр, а dd — дата из двух цифр. Вы можете просматривать журналы в блокноте или любом текстовом редакторе.

Давайте проиллюстрируем пример того, как вы можете использовать журнал, чтобы найти проблему. В приведенном ниже коде есть ошибка, и она не работает так, как мы ожидали. Чтобы иметь возможность диагностировать проблему, нам нужно проверить ввод или вывод функции. Давайте создадим оператор Print() и напечатаем содержимое всех соответствующих переменных в журнале.

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

Далее мы перейдем на вкладку «Журнал» и проверим, какая информация нам нужна. Если нам нужно просмотреть весь журнал или если на вкладке «Журнал» не отображаются сделки, мы можем щелкнуть правой кнопкой мыши и выбрать «Открыть» во всплывающем меню, а затем напрямую открыть файл журнала.

Этот код дает нам ошибку 130: «недействительные стопы» каждый раз, когда мы размещаем заказ на покупку. Мы знаем, что ошибка 130 означает, что либо стоп-лосс, либо тейк-профит неверны.

if(Close[0] > MA && BuyTicket == 0) {
double OpenPrice = Ask;
double BuyStopLoss = OpenPrice + (StopLoss * UsePoint); double BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint);
BuyTicket = OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage, BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green);
SellTicket = 0; }

Мы будем использовать функцию Print() для проверки параметров, передаваемых в функцию OrderSend(). Мы сосредоточимся на цене открытия ордера, стоп-лоссе и тейк-профите.

Print("Price:"+OpenPrice+" Stop:"+BuyStopLoss+" Profit:"+BuyTakeProfit);

Вот вывод, когда мы запускаем советник в тестере стратегий. Стоп-лосс и тейк-профит в размере 50 пунктов:

11:52:12 2009.11.02 02:00 Example EURUSD,H1: OrderSend error 130
11:52:12 2009.11.02 02:00 Example EURUSD,H1: Price:1.47340000 Stop:1.47840000
Profit:1.47840000

Мы знаем, что стоп-лосс должен быть ниже цены открытия ордера на покупку. Здесь это значение выше цены. На самом деле это та же цена, что и тейк-профит. Беглый взгляд на наш код, и мы понимаем, что случайно вставили знак плюс в уравнение покупки стоп-лосса. Вот правильный код:

double BuyStopLoss = OpenPrice - (StopLoss * UsePoint);

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

Ошибка 129: недействительная цена — недействительная цена открытия. Для рыночных ордеров убедитесь, что текущая цена Bid или Ask передается в соответствии с типом ордера. Для отложенных ордеров убедитесь, что цена выше или ниже текущей цены, как того требует тип ордера. Также убедитесь, что цена отложенного ордера не слишком близка к текущей цене.

Ошибка 130: недопустимые стопы — неверный стоп-лосс или тейк-профит. Убедитесь, что цены стоп-лосс и тейк-профит расположены выше или ниже текущей цены, в зависимости от того, является ли тип ордера покупкой или продажей. Также убедитесь, что цена стоп-лосса или тейк-профита не слишком близка к текущей цене.

Ошибка 131: неверный объем сделки — неверный размер лота. Убедитесь, что размер лота не превышает минимального или максимального значения брокера и что размер лота нормализован до правильного значения шага (0,1 или 0,01 для большинства брокеров).

Описания всех сообщений об ошибках можно найти в справочнике по MQL в разделе Стандартные константы — коды ошибок.

Устранение ошибок в случае прерывания торговли

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

Мы добавим дополнительную функцию для регистрации информации о сделках и статусе в режиме реального времени, чтобы у нас была запись об этом при устранении неполадок. Мы будем использовать операторы Print(), как и ранее, но мы будем регистрировать значения индикаторов цены — любую информацию, которая будет полезна при отладке.

// Внешние переменные 
extern bool Debug = true;
 
if(Debug == true) Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:",MA," BuyTicket:",BuyTicket," SellTicket:",SellTicket));

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

Оператор отладки Print() должен быть помещен в конце функции start() после всех торговых функций. Если вы используете таймер, поместите оператор debug Print() в блок таймера, чтобы он работал только при необходимости. В противном случае строка отладки будет выводиться в журнал при каждом тике, что может привести к разрастанию файла журнала.

Обработка ошибок компиляции

Когда вы компилируете свой советник, компилятор проверит синтаксис вашего кода и убедится, что все пользовательские функции и переменные были правильно объявлены. Если вы что-то пропустили, компилятор остановится, и любые ошибки компиляции появятся на вкладке Errors в окне Toolbox.

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

Вот список распространенных ошибок компиляции и их решения:

Variable not defined — вы забыли объявить переменную с типом данных. Если это глобальная или внешняя переменная, объявите ее в верхней части файла. Если это локальная переменная, найдите первое вхождение и поместите перед ним объявление типа данных. В противном случае проверьте правильность написания или регистр (верхний / нижний) имени переменной.

Variable already defined — вы объявили одну и ту же переменную дважды. Удалите объявление типа данных из всех дублированных объявлений переменных.

Function is not defined — если рассматриваемая функция находится в файле включения или библиотеки, убедитесь, что директива #include или #import находится в верхней части файла и является правильной. В противном случае проверьте правильность написания или регистр имени функции и убедитесь, что оно существует либо в текущем файле, либо в соответствующих файлах включений или библиотек.

Illegal assignment used — обычно это относится к знаку равенства (=). Помните, что один знак равенства предназначен для присваивания переменной, а два знака равенства (==) — оператор сравнения. Исправьте оператор присваивания в соответствующем операторе сравнения.

Assignment expected — обычно это относится к оператору сравнения «равно» (==). Вы использовали два знака равенства вместо одного в присваивании переменной. Исправьте оператор в один знак равенства.

Unbalanced right parenthesis — обычно это происходит в операторе if при использовании вложенных скобок. Перейдите к строке, обозначенной первой ошибкой, и вставьте левую скобку в соответствующее место.

Unbalanced left parenthesis — это сложная задача. Ошибка обычно указывает на конец строки программы. По сути, вы где-то забыли правильную скобку. Дважды проверьте код, который вы недавно редактировали, и найдите пропущенную правую скобку. Возможно, вам придется закомментировать строки кода, чтобы найти проблему.

Wrong parameters count — у вас слишком мало или слишком много аргументов в функции. Дважды проверьте синтаксис функции в MQL Reference и исправьте аргументы.

Semicolon expected — вы, вероятно, забыли поставить точку с запятой в конце строки. Поставьте точку с запятой в конце предыдущей строки. Обратите внимание, что пропущенная точка с запятой может вызвать любую из перечисленных выше ошибок, поэтому обязательно ставьте точки с запятой.

Понравилась статья? Поделить с друзьями:
  • If name main python ошибка
  • Imperator rome ошибка 0xc0000142
  • Import sqlite3 ошибка
  • Iis код ошибки 0x00000000
  • Illegal opcode hp proliant ошибка