Изучаем и пишем вместе на MQL5 - страница 35

 

Кто знает, могу ли я передать функции не члену класса в качестве аргумента ссылку (адрес) на другую функцию, которая вообще не является членом никакого класса?

Или, могу ли я передать функции члену класса в качестве аргумента ссылку (адрес) на другую функцию, которая вообще не является членом никакого класса?

Документация по MQL5: Основы языка / Типы данных / Структуры и классы
Документация по MQL5: Основы языка / Типы данных / Структуры и классы
  • www.mql5.com
Основы языка / Типы данных / Структуры и классы - Документация по MQL5
 
victorg:

Кто знает, могу ли я передать функции не члену класса в качестве аргумента ссылку (адрес) на другую функцию, которая вообще не является членом никакого класса?

Или, могу ли я передать функции члену класса в качестве аргумента ссылку (адрес) на другую функцию, которая вообще не является членом никакого класса?

Нет. Нет.

Не можете. В MQL5 нет понятия "адрес функции" или "ссылка на функцию". 

 

Спасибо!

Другой вопрос.

Есть два файла с кодом на mql5. Первый файл основной – индикатор или скрипт. Второй файл mqh.

// f_01.mqh
double extfunc(int a);
//-------------------------------------
double example(void)
  {
  double a;
  a=extfunc(35);
  return(a);
  }
//-------------------------------------
Второй mqh-файл подключается в основном в самом его начале. Компиляция основного файла проходит без ошибок и предупреждений. Но при попытке отдельно откомпилировать mqh-файл получаем - ‘ function must have a body’. Вопрос – как сказать компилятору что с функцией все в порядке, просто ее тело в другом файле?


Документация по MQL5: Файловые операции / FileMove
Документация по MQL5: Файловые операции / FileMove
  • www.mql5.com
Файловые операции / FileMove - Документация по MQL5
 
victorg:

Второй mqh-файл подключается в основном в самом его начале. Компиляция основного файла проходит без ошибок и предупреждений. Но при попытке отдельно откомпилировать mqh-файл получаем - ‘ function must have a body’. Вопрос – как сказать компилятору что с функцией все в порядке, просто ее тело в другом файле?
Никак. Не рвите по живому, это не облегчает понимание при чтении кода.
 
Yedelkin:

Возможно, перестраховываюсь, но ещё такой вопрос. Перед отправкой запроса на постановку рыночного ордера (с целью открытия позиции) обнуляю тикет сделки, т.е. делаю result.deal=0. Может ли произойти такая ситуация, что сервер в структуре ответа  MqlTradeResult вернёт нулевой тикет сделки, но при этом чуть позже сделка всё-таки совершится и позиция будет открыта? Или же возврат сервером нулевого тикета сделки гарантированно означает, что позицию не получилось открыть и она в дальнейшем не будет открыта на основании этого запроса?

ОК, в силу отсутствия ответа и в силу вот этой строчки

struct MqlTradeResult
  {
   ulong    deal;             // Тикет сделки, если она совершена
  };

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

 

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

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

для простоты взял фрагмент кастом кода МА , и пытаюсь понять что происходит в нем (отразил в комментах)

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

 void CalculateSimpleMA(int rates_total,int prev_calculated,int begin,const double &price[])    //разобрался вроде с &price[]

  {
   int i,limit;
//--- first calculation or number of bars was changed
   if(prev_calculated==0)// first calculation
     {
      limit=InpMAPeriod+begin;                                               //почему переменная begin=0 ???
      //--- set empty value for first limit bars
      for(i=0;i<limit-1;i++) ExtLineBuffer[i]=0.0;                            //здесь инициализируются значения индикатора на барах с индексами от 0 до limit-1 ??? крайне правых на графике???
      //--- calculate first visible value
      double firstValue=0;                                                    //при инициализации переменной имеющей тип double не обязательно использовать значения типа double???
      for(i=begin;i<limit;i++)
         firstValue+=price[i];                                               //разобрался, здесь идет накопление переданной цены
      firstValue/=InpMAPeriod;
      ExtLineBuffer[limit-1]=firstValue;                                      
     }
   else limit=prev_calculated-1;                                              //в результате чего prev_calcutated не должно равняться 0, если индикатор поместили на оффлайн график?
//--- main loop
   for(i=limit;i<rates_total && !IsStopped();i++)                              //цикл для индикатора на баре с индексами от limit до последнего на графике
      ExtLineBuffer[i]=ExtLineBuffer[i-1]+(price[i]-price[i-InpMAPeriod])/InpMAPeriod;
//---
  }

 вывел в распринтовку и вижу что значение в ExtLineBuffer присваиваются с индекса limit-1 по индекс rates_total-1, а на графике индикатор отрисован на всем пространстве, хм, где же тогда происходит присвоение значения буферу индикатора на промежутке от 1 до limit-1 ???

Документация по MQL5: Основы языка / Операции и выражения / Операции присваивания
Документация по MQL5: Основы языка / Операции и выражения / Операции присваивания
  • www.mql5.com
Основы языка / Операции и выражения / Операции присваивания - Документация по MQL5
 
Profi_R:

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

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

для простоты взял фрагмент кастом кода МА , и пытаюсь понять что происходит в нем (отразил в комментах)

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

Подробно комментировать не буду, возможно достаточно будет поправить основную ошибку вашего восприятия, и тогда сами соберёте пазл - это гораздо полезнее.

Итак, основа вашей запутанности в том, что многие индикаторы в mql5 (в частности этот) пишутся без разворота индексации индикаторных буферов, т.е. при значении AsSeries=false.

Это значит, что индекс самого старого бара на истории = 0, а самого "свежего" = RatesTotal-1

// Проясняется что-то??..

Смысл такого подхода - некоторый выигрыш в скорострельности, т.к. индексация при  обращении к буферу не требует [скрытого] пересчёта (остаётся "аппаратной")

// Путаница у вас возникла вероятно из убеждённости (ошибочной) что для индикаторных буферов индексация ВСЕГДА происходит от конца истории к началу. Это всегда верно в mql4, но в mql5 не обязательно.

// Здесь направление индексации по умолчанию всегда от начала истории к концу, для переворота индексации нужно пользоваться функцией SetAsSeries(...) в явном виде.

// Причём в справке разработчики вообще не рекомендуют опираться на умолчания (на тот случай если они таки поменяются) и ВСЕГДА пользоваться функцией SetAsSeries() для задания направления индексации.

 void CalculateSimpleMA(int rates_total,int prev_calculated,int begin,const double &price[])    //разобрался вроде с &price[]

 вывел в распринтовку и вижу что значение в ExtLineBuffer присваиваются с индекса limit-1 по индекс rates_total-1, а на графике индикатор отрисован на всем пространстве, хм, где же тогда происходит присвоение значения буферу индикатора на промежутке от 1 до limit-1 ???

Я думаю с этим сами разберётесь в свете вышеизложенного, но на всяк случай загляну завтра ещё в ветку.
 
MetaDriver: ..

Спасибо что отозвались )

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

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

 Остались вопросы про

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

2. переменную типа double можно значением типа int?

3. судя по всему, за значение переменной prev_calculated тоже отвечает терминал

4. непонятно? где происходит расчет индикатора на промежутке от 0 до limit-1 

Документация по MQL5: Основы языка / Функции / Функции обработки событий
Документация по MQL5: Основы языка / Функции / Функции обработки событий
  • www.mql5.com
Основы языка / Функции / Функции обработки событий - Документация по MQL5
 
Profi_R:

 Остались вопросы про

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

3. судя по всему, за значение переменной prev_calculated тоже отвечает терминал

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

Profi_R:

 Остались вопросы про

2. переменную типа double можно значением типа int?

 Да, можно. См. раздел про неявное приведение типов. Компилятор часто будет выдавать предупреждение о возможности потери данных при использовании неявного приведения типов.

Profi_R:

 Остались вопросы про

4. непонятно? где происходит расчет индикатора на промежутке от 0 до limit-1 

Отвечают ли эти строчки на Ваш вопрос:

//--- set empty value for first limit bars
      for(i=0;i<limit-1;i++) ExtLineBuffer[i]=0.0;                            
//--- calculate first visible value
... и далее по коду
?
 
Profi_R:

Спасибо что отозвались )

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

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

ОК.

 Остались вопросы про

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

Да,  за его передачу в индикатор отвечает терминал.


Здесь важно отметить назначение этого параметра.  Этот параметр указывает индикатору сколько начальных исторических значений входного ряда необходимо проигнорировать (пропустить), так как они являются некорректными и не должны принимать участия в расчётах.  Откуда может взяться такая некорректность, каково её происхождение?   Она связана с с возможностью построения индикаторов рассчитываемых не на ценовых данных, а на данных предоставленных другими индикаторами.  В МТ5 существует три механизма позволяющих получить на вход индикатора данные другого индикатора.

Первый способ.  Последовательность шагов: 

  1.  Создать хендл входного индикатора при помощи одной из функций серии iIndicator(...). либо функции IndicatorCreate(...).

  2.  По мере необходимости забирать его значения из его буферов при помощи функции CopyBuffer(...).

Второй способ. Является расширением первого.  Необходим, если при первом способе на вход входного индикатора нужно подать не ценовой ряд, а индикаторный.  Т.е. в этом случае мы намереваемся получать в программе значения рассчитываемые входным индикатором (2) берущим на свой собственный вход данные другого индикатора (1).  Т.е. построить цепочку индикатор-от-индикатора.

Последовательность шагов:

  1.  Создать хендл первого (1) входного индикатора при помощи одной из функций серии iIndicator(...). либо функции IndicatorCreate(...).

  2.  Создать хендл второго (2) индикатора тем же способом, но при создании  указать в качестве последнего параметра (applied_price) хендл первого (1).

  3. По мере необходимости забирать его значения из его буферов при помощи функции CopyBuffer(...).

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

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

Остановимся лишь на параметрах короткой формы вызова OnCalculate().    Итак:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])

Их назначение вполне внятно  документировано. Здесь я хочу лишь объяснить саму необходимость (обоснованность) их передачи в данную функцию.  С первым и последним параметром, надеюсь, всё достаточно ясно. Для вычислений мы должны иметь буфер с входными данными (price[]) и знать его текущую длину. (не забываем что длина его растёт по мере послупления котировок в терминал).

Но кроме того нам необходимо знать также с самого ли начала входного ряда его данные строго корректны или начальные значения следует проигнорировать, в связи с их (возможной или гарантированной) некорректностью. Некорректность в большинстве случаев гарантированно возникает когда входные данные являются выходом другого индикатора.  Ну а как иначе? Большинство индикаторов для вычисления значения на каком либо баре, должны использовать некоторый объём исторических данных.  А где им взяться "в начале времён"?  Их там нетути, и поэтому они вынужденны приступить к генерации выходных значений не со стартового исторического бара, а несколько позже (правее), с того бара, раньше ( левее) которого уже есть необходимое количество исторических данных.

Вот теперь, благодаря расписанным мной выше деталям, ответ на вопрос

откуда берется его значение?  // речь про параметр  begin

и будет понятен целиком и полностью.   Отвечаю:  несмотря на то, что этот параметр в функцию передаёт терминал, о его содержимом должен должен позаботиться входной индикатор!  Терминал сам в состоянии проконтролировать только ценовые входные ряды (в этом случае значение  begin будет равно 0, и это правильное значение).  Поэтому, при написании любого индикатора (кроме разве что чисто экспериментальных) следует позаботиться о том, чтобы он сообщал терминалу индекс начала корректных данных в его выходном буфере.   Это понятно? Иначе "потомки" этого индикатора могут скушать очень невкусные некорректные данные и в ряде случаев даже сильно отравиться... :)  Теперь о том как это делается.  Для этого используется функция PlotIndexSetInteger() с указанием идентификатора свойства PLOT_DRAW_BEGIN.  Важно!  Для 100% корректности формируемого свойства индикатора, невозможно ограничиться однократным вызовом PlotIndexSetInteger(...,PLOT_DRAW_BEGIN,...) в OnInit() !  Почему так?  А потому, что наш индикатор может быть сам, в свою очередь, сформирован на данных другого индикатора, который уже имеет начальный отступ на истории.! Т.е. мы имеем ненулевое значение begin на входной истории, и получить его в OnInit() нет возможности.  Итак, мы обязаны сделать примерно такой

  PlotIndexSetInteger(MySymbol,PLOT_DRAW_BEGIN,MyBeginPeriod-1+begin);

вызов.  И сделать его мы вынуждены (желательно однократно) в OnCalculate(), поскольку в OnInit значение begin неизвестно.

Что конечно оставляет за нами право сделать также предварительный (хотя и не очень осмысленный) вызов

  PlotIndexSetInteger(MySymbol,PLOT_DRAW_BEGIN,MyBeginPeriod);

в OnInit().

Именно так и сделано в том индикаторе (Custom Moving Average.mq5) из которого вы взяли ваш образец для изучения.

2. переменную типа double можно значением типа int?

Да, переменную типа double совершенно безболезненно можно инициализировать значением типа int, если оно задано константой. // как это и обстоит в вашем примере

3. судя по всему, за значение переменной prev_calculated тоже отвечает терминал

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

4. непонятно? где происходит расчет индикатора на промежутке от 0 до limit-1 

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

// я бы предпочёл присвоить им соответствующие входные данные, но это суть дела особо не меняет.

Причина обращения: