| / | Статьи |
Cтатьи
Примеры
Спать или не спать?
Авторизуйтесь или зарегистрируйтесь , чтобы добавить новую статью
|
Спать или не спать? [ en ]ВведениеВо время работы торгового эксперта периодически возникают ситуации, при которых между производимыми операциями следует выдержать паузу. Это может быть связано, как с необходимостью выполнять требования по задержкам между повторными запросами к торговому серверу (в случае возникновения ошибок исполнения), так и с ожиданием некоторого события (восстановления соединения с торговым сервером, освобождения торгового потока и т.п.). Sleep() или не Sleep()?Для реализации пауз в языке MQL4 имеется функция Sleep(), которая в качестве параметра принимает значение интервала времени, выраженного в количестве миллисекунд. Функция Sleep() останавливает исполнение программного кода и возобновляет его лишь после истечения заданного времени. На мой взгляд, использование этой функции имеет два недостатка. Во-первых, нерационально используется машинное время - во время паузы в действиях одного типа можно было бы осуществлять действия другого, не зависящего от первого (например, во время паузы в торговых операциях можно было бы производить какие-либо расчеты, осуществлять мониторинг поступающих тиков и т.п.). Во-вторых, и это более существенно, функцию Sleep() нельзя вызывать из пользовательских индикаторов (см. документацию). Но язык программирования за тем и нужен, чтобы программировать! Рассмотрим реализацию десятисекундной паузы в программе на языке MQL4. Ниже представлен вариант с использованием функции Sleep(). if ( /* условие, при выполнение которого требуется выдержать паузу */ ) Sleep(10000); // остановка на 10 секунд // программный блок, выполнение которого произойдет по окончании паузы // ... В качестве альтернативы рассмотрим программный код с использованием дополнительной переменной. В случае возникновения потребности в паузе перед каким-либо действием, она будет проинициализирована значением локального времени окончания паузы. Сравнивая локальное время со значением этой переменной можно установить факт окончания паузы. Для получения преимуществ от использования альтернативного подхода следует на период ожидания окончания паузы организовать циклическое исполнение программного кода, для которого пауза не требуется. Это может быть достигнуто как использованием цикла (например, while), так и посредством соответствующей реализации функции start(), цикличность вызова которой обеспечивается цикличностью поступления тиков. Первый вариант подходит для применения в пользовательских скриптах, второй - в советниках и индикаторах. Ниже представлены оба варианта. Вариант №1 (с использованием цикла while). int _time_waiting=0; // ... if ( ... ) // условие, при выполнение которого требуется выдержать паузу _time_waiting = TimeLocal() + 10; // окончание паузы через 10 секунд от текущего локального времени while ( TimeLocal() < _time_waiting ) { // программный блок, выполнение которого производится во время ожидания времени окончания паузы циклически // ... } // программный блок, выполнение которого произойдет не ранее времени окончания паузы // ... Вариант №2 (с использованием цикличности вызова функции start()). static int _time_waiting=0; // ... if ( ... ) // условие, при выполнение которого требуется выдержать паузу _time_waiting = TimeLocal() + 10; // окончание паузы через 10 секунд от текущего локального времени if ( TimeLocal() >= _time_waiting ) { // программный блок, выполнение которого не произойдет ранее времени окончания паузы // ... } // программный блок, выполнение которого происходит на каждом тике и не связано с ожиданием времени окончания паузы // ... Основное отличие представленных вариантов заключается в том, что вариант №1 гарантирует исполнение отложенного на время паузы программного блока, а вариант №2 не гарантирует. Связано это с тем, что поток тиков может быть прерван по каким-либо причинам и не возобновлен. Кроме того, в варианте №2 переменная _time_waiting описана как static, это гарантирует сохранность ее значения между вызовами функции start(), что не требуется варианте №1. Некоторая избыточность кода в альтернативных вариантах, по сравнению с реализацией паузы посредством вызова Sleep(), является платой за избавление от простоя. Отсутствие же простаивания порождает проблему многократного использования переменной _time_waiting в программном коде - нельзя инициализировать её новым значением, пока не выдержана предыдущая пауза. Эту проблему можно решить, по крайней мере, двумя способами, выбор того или иного варианта зависит от стиля и предпочтений программиста: 1) под каждую группу условий использовать собственную переменную для хранения времени окончания соответствующей паузы; 2) описать _time_waiting в виде массива. Мне представляется не лишенным смысла создание небольшой библиотеки с реализацией функций таймера. Такой "таймер" должен содержать достаточное количество переменных-счетчиков и иметь функции инициализации, добавления и удаления контролируемых выдержек. Кроме того, можно реализовать не только обратный отсчет (таймер ожидания), но и прямой (секундомер). Пример реализации такой библиотеки прикреплен к статье (файл с исходным текстом - "ExLib_Timer.mq4"), ниже представлен заголовочный файл с прототипами реализованных функций. #import "ExLib_Timer.ex4" // // Примечание. // Идентификатор счетчика <CounterID> может принимать любое значение кроме "0", // т.к. значение "0" зарезервировано для обозначения незадействованных счетчиков // в общем массиве. // void timer_Init(); // начальная инициализация // int timer_NumberTotal(); // запрос общего количества счетчиков таймера // Возврат: // общее количество счетчиков // int timer_NumberUsed(); // запрос количества задействованных счетчиков // Возврат: // количество задействованных счетчиков // int timer_NumberFree(); // запрос количества незадействованных (свободных) счетчиков // Возврат: // количество свободных счетчиков // void timer_ResetAll(); // сброс (обнуление) всех счетчиков // bool timer_Reset(int CounterID); // сброс (обнуление) счетчика по идентификатору // Параметры: // <CounterID> - идентификатор счетчика // Возврат: // true - сбос произведен // false - счетчик с заданным идентификатором не обнаружен // void timer_DeleteAll(); // удаление всех счетчиков // bool timer_Delete(int CounterID); // удаление счетчика по идентификатороу // Параметры: // <CounterID> - идентификатор счетчика // Возврат: // true - удаление произведено // false - счетчик с заданным идентификатором не обнаружен // bool timer_StartWaitable(int CounterID, int timeslice); // старт счетчика типа "таймер ожидания" // Параметры: // <CounterID> - идентификатор счетчика (в случае отсутствия, счетчик добавляется) // <timeslice> - периода ожидания (секунд) // Возврат: // true - старт произведен // false - нет свободных счетчиков // bool timer_StartStopwatch(int CounterID); // старт счетчика типа "секундомер" // Параметры: // <CounterID> - идентификатор счетчика (в случае отсутствия, счетчик добавляется) // Возврат: // true - старт произведен // false - нет свободных счетчиков // int timer_GetCounter(int CounterID); // запрос показаний счетчика с идентификатором <CounterID> // Возврат: // для счетчика типа "секундомер" - количество секунд, прошедших от начала старта // для счетчика типа "таймер ожидания" - количество секунд, оставшихся до окончания периода ожидания // Примечание: // в случае отсутствия счетчика с идентификатором <CounterID> возвращается -1 // int timer_GetType(int CounterID); // запрос типа счетчика с идентификатором <CounterID> // Параметры: // <CounterID> - идентификатор счетчика // Возврат: // 1 - счетчик типа "секундомер" // -1 - счетчик типа "таймер ожидания" // 0 - счетчик с заданным идентификатором не обнаружен // int timer_GetCounterIDbyPOS(int CounterPOS); // запрос идентификатора счетчика по его позиции среди задействованных // Параметры: // <CounterPOS> - номер позиции счетчика среди задействованных // Возврат: // идентификатор счетчика или "0", если задана несуществующая позиция // Примечание: // <CounterPOS> может принимать значения от 0 до количества задействованных счетчиков, // возвращаемого функцией timer_NumberUsed(), в противном случае возвращается 0 // #import Различия между использованием вызова Sleep() и альтернативным вариантом реализации паузы наглядного демонстрирует посредством вывода сообщений в журнал небольшой эксперт, текст которого приведен ниже. Вариант реализации паузы задается входным параметром Use_Sleep - "true" для использования Sleep(), "false" для отказа от использования Sleep(). #include "include/ExLib_Timer.mqh" // -- //---- input parameters extern bool Use_Sleep = false; // =true - использование функции "Sleep()" // =false - использование таймера //+------------------------------------------------------------------+ //| expert initialization function | //+------------------------------------------------------------------+ int init() { timer_Init(); if ( Use_Sleep ) Print("init(). * Использование функции Sleep() *"); else Print("init(). * Использование таймера *"); timer_StartWaitable(101, 10); timer_StartStopwatch(102); return(0); } //+------------------------------------------------------------------+ //| expert deinitialization function | //+------------------------------------------------------------------+ int deinit() { timer_DeleteAll(); Comment(""); return(0); } //+------------------------------------------------------------------+ //| expert start function | //+------------------------------------------------------------------+ int start() { Comment("Waitable:",timer_GetCounter(101)," / Stopwatch:",timer_GetCounter(102)); Print("start() - начальный блок -"); // -- if ( Use_Sleep ) { // - использование функции "Sleep()" - Sleep( 10000 ); // пауза 10 секунд // программный блок, выполнение которого произойдет по истечению времени паузы Print("start() - исполняемый после паузы блок -"); } else { // - использование таймера - if ( timer_GetType(101) == 0 ) timer_StartWaitable(101, 10); // пауза 10 секунд if ( timer_GetCounter(101) == 0 ) { // программный блок, выполнение которого произойдет не ранее истечения времени паузы timer_Delete(101); Print("start() - исполняемый после паузы блок -"); } } // -- Print("start() - конечный блок -"); return(0); } Кроме приведенного выше, к статье прикреплены два демонстрационных эксперта: Несущественные недостатки или "ложка дёгтя"?К сожалению, представленная библиотека не может заменить Sleep() в полной мере. Есть два негативных момента, на которые следует обратить внимание читателя. Во-первых, при использовании Sleep() выполнение программы будет продолжено сразу после истечения периода паузы, а в случае использования представленного таймера - на первом тике, поступившем после паузы. Это может привести к тому, что задержанный блок будет выполнен слишком поздно, или, как указывалось выше, вовсе не будет выполнен (например, в случае возникновения неустранимой неисправности в канале связи). Во-вторых, функция Sleep() позволяет задавать паузу с шагом 0.1 сек, а представленный таймер - 1 сек. ВыводыКак часто бывает, преимущества описанной альтернативной реализации пауз над использованием Sleep() не безусловны, имеются нюансы. Везде есть свои сильные и слабые стороны, а их значимость зависит от преследуемых целей. Sleep() или не Sleep(), чем пожертвовать и что предпочесть следует определять не вообще, а в рамках реализуемого проекта. Состав прикрепленных файлов: Библиотека функций:
Примеры использования:
Важно! Эксперты "Ex_Timer_OrderLimits_TrailByTime" и "Ex_Timer_OrderSend_wMinTimeLimit" расчитаны на работу только на демо счетах! Прикрепленные файлы:
Предупреждение:
все права на данные материалы
принадлежат MetaQuotes Software Corp. Полная или частичная перепечатка запрещена.
iSeq писал(а):
Для тех кто в поезде, обясните плз. 1) Где обнуление _time_waiting`a? На каком тике исполнится условие: _time_waiting = TimeLocal() + 10; // окончание паузы через 10 секунд от текущего локального времени if ( TimeLocal() >= _time_waiting ) ведь как я понял, каждый тик, переменная _time_waiting будет на 10сек больше текущего времени. Обнуление не нужно. Строка " _time_waiting = TimeLocal() + 10;" выполнится только внутри if ( условие, при выполнение которого требуется выдержать паузу ).
19.05.2009 00:36 komposter
static int _time_waiting=0; // ... if ( ... ) // условие, при выполнение которого требуется выдержать паузу _time_waiting = TimeLocal() + 10; // окончание паузы через 10 секунд от текущего локального времени if ( TimeLocal() >= _time_waiting ) { // программный блок, выполнение которого не произойдет ранее времени окончания паузы // ... } Для тех кто в поезде, обясните плз. 1) Где обнуление _time_waiting`a? На каком тике исполнится условие: _time_waiting = TimeLocal() + 10; // окончание паузы через 10 секунд от текущего локального времени if ( TimeLocal() >= _time_waiting ) ведь как я понял, каждый тик, переменная _time_waiting будет на 10сек больше текущего времени. сенк.
18.05.2009 21:55 iSeq
tadbor писал(а):
Мне лично больше нравятся скрипты в while цикле чем советники зависимые от тиков. Советника тоже можно зациклить ;)
17.11.2008 16:47 komposter
Похожая статья - Пауза между торговыми операциями. И стоило упомянуть, что Sleep() имеет встроенную проверку на IsStopped() - иногда это может быть важно.
17.11.2008 16:46 komposter
Contender писал(а):
tadbor писал(а):
Важно! Эксперты "Ex_Timer_OrderLimits_TrailByTime" и "Ex_Timer_OrderSend_wMinTimeLimit" расчитаны на работу только на демо счетах!
Ex_Timer_OrderLimits_TrailByTime Не предполагает наличие открытых ордеров на символе отличном от выбранного в окне с установленным советником. Ну да- это большие проблемы. Мне лично больше нравятся скрипты в while цикле чем советники зависимые от тиков. А статья дельная.
07.11.2008 16:41 tadbor
tadbor писал(а):
Важно! Эксперты "Ex_Timer_OrderLimits_TrailByTime" и "Ex_Timer_OrderSend_wMinTimeLimit" расчитаны на работу только на демо счетах!
Ex_Timer_OrderLimits_TrailByTime Не предполагает наличие открытых ордеров на символе отличном от выбранного в окне с установленным советником. Ex_Timer_OrderSend_wMinTimeLimit Проверяет лишь отсутствие открытых ордеров, какие-либо иные условия (даже торговые) не проверяются. Если нет открытых позиций, то производится выбор направления (для четных секунд - BUY, для нечетных - SELL) и попытка открыться. В случае удачи, запускается таймер с выдержкой равной выбранному на графике периоду (т.е. для M5 выдержка составит 300 секунд, для H1 - 3600 секунд и т.д.). Пока есть открытые или отложенные ордера или не истекло время выдержки после предыдущего открытого этим советником ордера, Ex_Timer_OrderSend_wMinTimeLimit не открывает новых.
07.11.2008 15:38 Contender
Важно! Эксперты "Ex_Timer_OrderLimits_TrailByTime" и "Ex_Timer_OrderSend_wMinTimeLimit" расчитаны на работу только на демо счетах!
07.11.2008 13:25 tadbor
K6-rip писал(а): в индикаторах можно использовать WinAPI функцию SleepEx() Насколько я понимаю, Вы не сможете использовать функции WinAPI на чемпионате
07.11.2008 08:36 Contender
10 комментариев
|