Недокументированные возможности по работе в MQL4
#1
Отправлено 14 February 2011 - 16:24
"Недокументированные возможности по работе в МТ4"
- очень интересная тема, но там описываются возможности самого терминала...
Но есть ряд возможностей языка MQL4, которые не встречаются нигде в описаниях, но очень нужны бы писателям роботов-советников (ну и индикаторов, скриптов ... естественно).
Ещё есть такая тема
"Азы программирования на MQL4"
Но подумалось, что если туда добавлять ещё и "трюкачество" - то это значит совсем заморочить голову...
Вот поэтому решил, что нужна такая тема: я поделюсь тем, что я видел - может и мне кто чего нового подскажет?
- akacuk21 это нравится
 
#2
Отправлено 14 February 2011 - 16:35
int start() { static int nTick = 0; IncTick( nTick ); alert( "новый тик: " + nTick ); } //------------------------------------------------------------------ void IncBars( int& n ) { n++; }
Смысл здесь большой: функции MQL4 не могут возвращать какие-то структурированные данные (например массивы), только единичные (int, string, ...).
За счёт побочных эффектов функции можно передать много параметров, которые могут быть изменены, т.е. фактически много возвращаемых значений.
- valenok2003 это нравится
#3
Отправлено 14 February 2011 - 16:38
Это близко к трюку №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
Отправлено 14 February 2011 - 17:20
Сама по себе возможность в любое время изменить размер массива - это уже трюк языка 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 );После чего получим, как ни странно:
- т.е. массивы не переразмещаются полностью, а ... "дополняются" до нужной размерности (как, например, списки STL) - все старые присвоенные значения элементам сохраняются!333, 333, 333, 333, 333, 0, 0
Но без такого трюкачества было бы невозможно реализовать-представить тайм-серии в 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
Отправлено 16 February 2011 - 17:33
Это уже не есть, в полном смысле, "недокументированная возможность" (в описании языка упоминается только 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
Отправлено 20 February 2011 - 12:51
Возможно здесь ошибка - вызывается IncTick(), а описана IncBars().Трюк №1 : передача переметров в функцию по ссылке (по адресу), т.е. возможность изменить значение переменной изнутри вызванной функции (функции с побочным эффектом):
int start() { static int nTick = 0; IncTick( nTick ); alert( "новый тик: " + nTick ); } //------------------------------------------------------------------ void IncBars( int& n ) { n++; }
Смысл здесь большой: функции MQL4 не могут возвращать какие-то структурированные данные (например массивы), только единичные (int, string, ...).
За счёт побочных эффектов функции можно передать много параметров, которые могут быть изменены, т.е. фактически много возвращаемых значений.
#7
Отправлено 20 February 2011 - 14:59
Возможно здесь ошибка - вызывается IncTick(), а описана IncBars().
Да, конечно, это "выхвачено" из живого кода, кое-что подправлялось на ходу...
Но идея сама понятна: побочный эффект изменения переменных внутри вызываемых функций.
Особо интересно это в связи с тем, что MQL4 в силу своей ограниченности не позволяет возвращать из функций каких-либо составных значений: структур, или, на худой конец, тех же массивов... За счёт побочных эффектов мы получаем возможность возвращать из функций сколь угодно сложные данные.
#8
Отправлено 20 February 2011 - 15:30
Но если раньше я описал некоторые возможности, то теперь у меня вопрос, ответ на который я нигде не могу найти... Возможно кто-то может подсказать?
Иначе придётся его выяснять экспериментально, что не хотелось бы...
Итак. Хочу выбрать из истории ордера законченные... (закрытые + удалённые ... на самом деле меня интересуют закрытые, но я их потом отбракую).
В справочнике MQL4 читаем:
и т.д.; ну и нам естественно интересно: OrderSelect( i, SELECT_BY_POS, MODE_HISTORY ) где i пробегает в цикле ... ну, что-то типа:bool OrderSelect( int index, int select, int pool = MODE_TRADES )
Функция выбирает ордер для дальнейшей работы с ним.
...
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
Отправлено 21 February 2011 - 14:49
- никогда не модифицируйте внутри программы (для нормализации, приведения к разрядности Digits и т.п.) значения настраиваемых параметров советника, т.е. переменных, объявленных как extern !
Почему?
Потому, что это не совсем нормальные переменные: при переинициализации советника он будет перезапущен со старыми (исходными) значениями таких переменных, в то время, когда вы будете твёрдо уверены, что вы их уже только-что изменили...
А переинициализация работающего советника может происходить по совершенно безобидным и малозначащим поводам, напимер: перещёлкнулись не секунду с одного тайм-фрейма и вернулись обратно - произошло 2 переинициализации!
Поэтому для хранения таких значений лучше иметь 2 переменных: одна, "торчащая внаружу" как настраиваемый параметр эксперта, а вторая - её внутреннее пересчитанное значение, которое и используется в вычислениях.
- valenok2003 это нравится
#10
Отправлено 21 February 2011 - 17:17
Ещё раз о недокументированных возможностях...
Если время закрытия ордера не равно 0, то ордер является
закрытым или удаленным отложенным и был выбран из истории терминала.
Если время закрытия ордера не равно 0, то ордер является
закрытым или удаленным отложенным и был выбран из истории терминала.
Я так учебник понял.
#11
Отправлено 21 February 2011 - 17:24
#12
Отправлено 21 February 2011 - 17:27
На мокле спросите у производителей.Ещё раз о недокументированных возможностях...
Но если раньше я описал некоторые возможности, то теперь у меня вопрос, ответ на который я нигде не могу найти... Возможно кто-то может подсказать?
Иначе придётся его выяснять экспериментально, что не хотелось бы...
Меня другой вопрос мучает, никто не хочет ответить на этот дурацкий вопрос. Как терминал отличает советника, индикатор и скрипт.
#13
Отправлено 21 February 2011 - 18:11
Но вот вопрос! Если задаётся позиция i в списке ордеров, то этот список, естественно, упорядочен по какому-то признаку... достаточно естественно, чтобы это было время закрытия-удаления - OrderCloseTime()...
Если не ошибаюсь, то порядок сортировки ордеров в истории счета задает сам пользователь (вкладка история счета, если кликнуть например на "тип" - ордера будут отсортированы по типу). Лучше, на мой взгляд, занести ордера в массив и сортировать как удобно (по времени открытия/закрытия), по крайней мере я так делаю обычно. Это надежнее, чем зависеть от каких-либо других факторов.
Обсуждалось где-то в этом разделе. По месторасположению файла, если скрипт поместить в папку experts/ , то он будет выполняться на каждом тике, как советник.Как терминал отличает советника, индикатор и скрипт.
#14
Отправлено 22 February 2011 - 03:23
По поводу 5 поста. Я так и не понял, в чём преимущество вашего подхода, хотя несколько раз перечитал. Библиотека и так подключается по директиве инклюде. К чему лишние движения, проясните...
А там и нет никакого "моего подхода"... Просто мне нужно было создать библиотеку MQL4 (не DLL), они многократно упоминаются в справочнике языка, в учебниках, в описаниях языка ... но как-то вскользь, и нигде толком.
Конечно, библиотека и так подключается по директиве инклюде... но это когда библиотека уже существует. А мне нужно её создать с нуля. И файл (*.mqh) подключаемый по директиве инклюде должен, в свою очередь, содержать описания #import для подключаемых из библиотеки функций (так же и для DLL, там это особенно важно)...
Поэтому я и делаю:
1. сам файл *.mq4 реализации функций;
2. соответствующий ему *.mqh ...
3. туда же вписывается #import, чтобы не вписывать в вызывающий файл;
4. в файл реализации дописал
#property library- только для того, чтобы компилятор не матерился на неиспользуемые функции.
И ничего более...
#15
Отправлено 22 February 2011 - 03:28
Обсуждалось где-то в этом разделе. По месторасположению файла, если скрипт поместить в папку experts/ , то он будет выполняться на каждом тике, как советник.
Да, как не странно - местоположением!
... а если советник (тот же код) перенести в experts/scripts, то он выполняется однократно как скрипт.
Индикаторы ... прежде всего ищутся в experts/indicators, например, если вы попробуете его использовать из другого советника как custom.
Но индикаторы как раз и по коду наиболее отличаются от скриптов и советников - есть целые группы функций, которые допустимы в индикаторах, но недопустимы в скриптах с советниками, и наоборот.
Я для себя разобрался пока так.