Перейти к содержимому

Theme© by Fisana
 

Фотография
* * * * - 1 Голосов

Недокументированные возможности по работе в MQL4


  • Авторизуйтесь для ответа в теме
Сообщений в теме: 34

#1 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 14 February 2011 - 16:24

Увидел я здесь тему:
"Недокументированные возможности по работе в МТ4"
- очень интересная тема, но там описываются возможности самого терминала...
Но есть ряд возможностей языка MQL4, которые не встречаются нигде в описаниях, но очень нужны бы писателям роботов-советников (ну и индикаторов, скриптов ... естественно).

Ещё есть такая тема
"Азы программирования на MQL4"
Но подумалось, что если туда добавлять ещё и "трюкачество" - то это значит совсем заморочить голову...

Вот поэтому решил, что нужна такая тема: я поделюсь тем, что я видел - может и мне кто чего нового подскажет? :)
  • akacuk21 это нравится

 
 

#2 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 14 February 2011 - 16:35

Трюк №1 : передача переметров в функцию по ссылке (по адресу), т.е. возможность изменить значение переменной изнутри вызванной функции (функции с побочным эффектом):

  int start() {
     static int nTick = 0;
     IncTick( nTick );
     alert( "новый тик: " + nTick );
  
  }
  //------------------------------------------------------------------
  void IncBars( int& n ) { n++; }

Смысл здесь большой: функции MQL4 не могут возвращать какие-то структурированные данные (например массивы), только единичные (int, string, ...).
За счёт побочных эффектов функции можно передать много параметров, которые могут быть изменены, т.е. фактически много возвращаемых значений.
  • valenok2003 это нравится

#3 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 14 February 2011 - 16:38

Трюк №2 : передача функции пераметром массива и по ссылке:

Это близко к трюку №1, см. выше, но здесь возможности (и сопутствующие эффекты) существенно больше:

1.
   extern int N = 7;
   int A[];
   //------------------------------------------------------------------
   int init() {
      ArrayResize( A, N );
      alert( "размер массива " + ArraySize( A ) );
      for( int i = 0; i < N; i++ ) A[ i ] = i + 1;
   }
   //------------------------------------------------------------------
   int start() {
      static nBars = 0;
      if( nBars != Bars ) {   // для того, чтобы не частить, запустите на M1
         nBars = Bars;
         IncArray( A );
         ShowArray( A );
      }
   }
   //------------------------------------------------------------------
   void IncArray( int& Array[] ) {
      int n = ArraySize( Array );
      for( int i = 0; i < n; i++ ) Array[ i ]++;
   }
   
   void ShowArray( int Array[] ) {
      int n = ArraySize( Array );
      string Msg = "";
      for( int i = 0; i < n; i++ ) {
         Msg = Msg + Array[ i ];
         if( i < n - 1 ) Msg = Msg + " , ";
      }
      alert( Msg );
   }

2.
Поменяйте строку:
  void IncArray( int& Array[] ) {
на (всего то):
  void IncArray( int Array[] ) {
Можно ожидать, что при этом всё будет работать, но изменения в Array[] не будут отражаться в вызывающей программе...
Так нет! - получите ошибку ещё на этапе компиляции, что-то типа:

'Array' - array item cannot be assigned C:\Program Files\MetaTrader - Alpari\experts\@parm.mq4 (26, 33)


- т.е. контроль L-value (доступность на присваивание) делается ещё компилятором!

3. теперь то же самое проверю, но для единичных параметров (трюк №1):
- меняем
void IncBars( int& n ) { n++; }
- на
void IncBars( int n ) { n++; }
И теперь всё ОК: компилируется, работает, но изменения в n++ не отразятся внаружу функции IncBars . Меняйте безбоязненно переданные параметры функции! (достаточно частый случай, когда передаётся параметр для цикла в функции).

#4 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 14 February 2011 - 17:20

Трюк №3 : переразмещение в функции пераметра-массива, переданного по ссылке:

Сама по себе возможность в любое время изменить размер массива - это уже трюк языка MQL4:
    int A[ 5 ];
    for( i = 0; i < ArraySize( A ); i++ ) A[ i ] = 333;
    ShowArray( A );
    ArrayResize( A, 7 );
    for( i = 0; i < ArraySize( A ); i++ ) A[ i ] = 333;
   ShowArray( A );
После чего получим, как ни странно:

333, 333, 333, 333, 333, 0, 0

- т.е. массивы не переразмещаются полностью, а ... "дополняются" до нужной размерности (как, например, списки STL) - все старые присвоенные значения элементам сохраняются!
Но без такого трюкачества было бы невозможно реализовать-представить тайм-серии в MT4.

А теперь сам трюк с ... переразмерностью в функции:
- переписываем функцию из предыдущего (трюк №2):
    void IncArray( int& Array[] ) {
       int n = ArraySize( Array );
       ArrayResize( A, n + 1 );
       for( int i = 0; i <= n; i++ ) Array[ i ] = i + 1;
    }
тогда получим вывод типа (вывод в "журнал" терминала снизу-вверх):

...
2011.02.10 19:54:00 @parm EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14
2011.02.10 19:53:02 @parm EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13
2011.02.10 19:52:08 @parm EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12
...



#5 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 16 February 2011 - 17:33

Пользовательские библиотеки (библиотеки MQL4).
Это уже не есть, в полном смысле, "недокументированная возможность" (в описании языка упоминается только 10 раз), но нигде нет собранного в одно место внятно описанных правил, как сделать такую новую библиотеку (даже с DLL ситуация понятнее).

Итак:

1. создаём новый файл исходных кодов новой библиотеки (к примеру библиотеки mmlib.ex4) .../experts/libraries/mmlib.mq4 (можно .../experts/mmlib.mq4, но это уже как-то налюбителя) с кодом своих функций:
  #property library
  #include <mmlib.mqh>
  void fun1() { 
      /*... здесь код своей функции */
 }    // и так сколь угодно много fun1, fun2, ... funN 
Можете накидать сюда ... хоть все понравившиеся вам функции из темы Полезные mql функции :)

2. создаём новый файл соответствующих описаний (заголовков - в точности повторяющих заголовки всех функций из mmlib.mq4) .../experts/libraries/mmlib.mqh (можно .../experts/mmlib.mqh, но это опять же... ):
  #import "mmlib.ex4"
  /*... здесь заголовки своих функций, в точности повторяющие то, 
       что выше... для всех fun1, fun2, ... funN
 */ 
  void fun1();
#import 

3. открыв в редакторе mmlib.mq4 компилируем его.

4. в коде своего эксперта (или многих экспертов!) достаточно, для использования всех функций из этой библиотеки, иметь строку:
  #include <mmlib.mqh>                    // подключение библиотеки

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

P.S. любопытно, что в mmlib.mq4 вы можете иметь множество описанных и используемых переменных, которые будут внутренними статическими переменными библиотеки.
  • valenok2003 это нравится

#6 valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений

Отправлено 20 February 2011 - 12:51

Трюк №1 : передача переметров в функцию по ссылке (по адресу), т.е. возможность изменить значение переменной изнутри вызванной функции (функции с побочным эффектом):

  int start() {
     static int nTick = 0;
     IncTick( nTick );
     alert( "новый тик: " + nTick );
  
  }
  //------------------------------------------------------------------
  void IncBars( int& n ) { n++; }

Смысл здесь большой: функции MQL4 не могут возвращать какие-то структурированные данные (например массивы), только единичные (int, string, ...).
За счёт побочных эффектов функции можно передать много параметров, которые могут быть изменены, т.е. фактически много возвращаемых значений.

Возможно здесь ошибка - вызывается IncTick(), а описана IncBars().

#7 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 20 February 2011 - 14:59

Возможно здесь ошибка - вызывается IncTick(), а описана IncBars().


Да, конечно, это "выхвачено" из живого кода, кое-что подправлялось на ходу... :)

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

#8 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 20 February 2011 - 15:30

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

Итак. Хочу выбрать из истории ордера законченные... (закрытые + удалённые ... на самом деле меня интересуют закрытые, но я их потом отбракую).
В справочнике MQL4 читаем:

bool OrderSelect( int index, int select, int pool = MODE_TRADES )
Функция выбирает ордер для дальнейшей работы с ним.
...

и т.д.; ну и нам естественно интересно: OrderSelect( i, SELECT_BY_POS, MODE_HISTORY ) где i пробегает в цикле ... ну, что-то типа:
   int i,accTotal=OrdersHistoryTotal();
   for(i=0;i<accTotal;i++) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) {
         Print("Ошибка при доступе к исторической базе (",GetLastError(),")");
         break;
      }
      // работа с ордером ...
   }

Но вот вопрос! Если задаётся позиция i в списке ордеров, то этот список, естественно, упорядочен по какому-то признаку... достаточно естественно, чтобы это было время закрытия-удаления - OrderCloseTime()...

Вот и вопрос: по какому признаку упорядоченными будут "вываливаться" ордера в показанном цикле? Если по времени закрытия, то в возрастающем или убывающем порядке? какой будет получен при i==0 : самый старый или самый последний?

P.S. в этом месте в справочнике по MQL4 вообще есть противоречивость:
- описание OrderCloseTime() говорит:

Возвращает время закрытия для выбранного ордера. Только закрытые ордера имеют время закрытия, не равное 0.

т.е. удалённые (отсроченные) ордера, вроде как должны иметь 0...
- но описание OrderSelect() говорит по-другому:

Если время закрытия ордера не равно 0, то ордер является
закрытым или удаленным отложенным и был выбран из истории терминала.

Как?

#9 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 21 February 2011 - 14:49

Ещё одно наблюдение из области ... слабо документированных особенностей MQL4:
- никогда не модифицируйте внутри программы (для нормализации, приведения к разрядности Digits и т.п.) значения настраиваемых параметров советника, т.е. переменных, объявленных как extern !

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

Поэтому для хранения таких значений лучше иметь 2 переменных: одна, "торчащая внаружу" как настраиваемый параметр эксперта, а вторая - её внутреннее пересчитанное значение, которое и используется в вычислениях.
  • valenok2003 это нравится

#10 valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений

Отправлено 21 February 2011 - 17:17

Ещё раз о недокументированных возможностях...


Если время закрытия ордера не равно 0, то ордер является
закрытым или удаленным отложенным и был выбран из истории терминала.



Если время закрытия ордера не равно 0, то ордер является
закрытым или удаленным отложенным и был выбран из истории терминала.


Я так учебник понял.

#11 valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений

Отправлено 21 February 2011 - 17:24

По поводу 5 поста. Я так и не понял, в чём преимущество вашего подхода, хотя несколько раз перечитал. Библиотека и так подключается по директиве инклюде. К чему лишние движения, проясните...

#12 valenok2003

valenok2003

    Пользователи

  • Заблокированные
  • PipPipPipPipPipPipPipPipPipPipPip
  • 606 сообщений

Отправлено 21 February 2011 - 17:27

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

На мокле спросите у производителей.


Меня другой вопрос мучает, никто не хочет ответить на этот дурацкий вопрос. Как терминал отличает советника, индикатор и скрипт.

#13 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 21 February 2011 - 18:11

Но вот вопрос! Если задаётся позиция i в списке ордеров, то этот список, естественно, упорядочен по какому-то признаку... достаточно естественно, чтобы это было время закрытия-удаления - OrderCloseTime()...


Если не ошибаюсь, то порядок сортировки ордеров в истории счета задает сам пользователь (вкладка история счета, если кликнуть например на "тип" - ордера будут отсортированы по типу). Лучше, на мой взгляд, занести ордера в массив и сортировать как удобно (по времени открытия/закрытия), по крайней мере я так делаю обычно. Это надежнее, чем зависеть от каких-либо других факторов.

Как терминал отличает советника, индикатор и скрипт.

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

#14 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 22 February 2011 - 03:23

По поводу 5 поста. Я так и не понял, в чём преимущество вашего подхода, хотя несколько раз перечитал. Библиотека и так подключается по директиве инклюде. К чему лишние движения, проясните...


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

Конечно, библиотека и так подключается по директиве инклюде... но это когда библиотека уже существует. А мне нужно её создать с нуля. И файл (*.mqh) подключаемый по директиве инклюде должен, в свою очередь, содержать описания #import для подключаемых из библиотеки функций (так же и для DLL, там это особенно важно)...
Поэтому я и делаю:
1. сам файл *.mq4 реализации функций;
2. соответствующий ему *.mqh ...
3. туда же вписывается #import, чтобы не вписывать в вызывающий файл;
4. в файл реализации дописал
#property library
- только для того, чтобы компилятор не матерился на неиспользуемые функции.

И ничего более...

#15 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 22 February 2011 - 03:28

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


Да, как не странно - местоположением!
... а если советник (тот же код) перенести в experts/scripts, то он выполняется однократно как скрипт.

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

Я для себя разобрался пока так. :)



Copyright © 2024 Your Company Name