Скачать MetaTrader 5

Пауза между торговыми операциями

19 мая 2006, 19:04
Andrey Khatimlianskii
2
1 883


1. Обязанность или добрая воля?

В клиентском терминале МetaТrader 3 было невозможно провести 2 торговые операции с интервалом меньше 10 секунд. Разрабатывая МТ4, компания MetaQuotes пошла навстречу трейдерам-пользователям и убрала это ограничение. Действительно, есть ситуации, в которых вполне допустимо провести несколько операций подряд (передвижение уровней Стоп Лосс для нескольких позиций, удаление отложенных ордеров и т.п.). Но некоторые трейдеры восприняли это по-другому и начали писать эксперты-"убийцы", открывающие несколько позиций подряд без какой-либо передышки. Результат - заблокированный счёт, или, как минимум, плохое отношение брокера.

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

2. Один эксперт или несколько экспертов - в чём разница?

Если у вас запущена одна копия терминала и на ней работает один эксперт, организовать паузу между торговыми операциями проще простого - достаточно завести глобальную переменную (переменную, объявленную на глобальном уровне, не путать с Глобальными Переменными терминала) и запоминать в неё время последней операции. И, конечно, перед каждой торговой операцией проверять,  достаточно ли прошло времени после последней попытки торговать.

Выглядеть это будет так:

datetime LastTradeTime = 0;
int start()
 {
  // определяем необходимость входа в рынок
  ...
  // рассчитываем уровни Стоп Лосс, Тейк Профит и размер лота
  ...
  // проверяем, достаточно ли прошло времени после последней торговой операции
  if(LocalTime() - LastTradeTime < 10)
   {
    Comment("Со времени последней торговой операции прошло меньше 10 секунд!",
            " Эксперт не будет торговать!"); 
    return(-1);
   }
 
  // открываем позицию
  if(OrderSend(...) < 0)
   {
    Alert( "Ошибка открытия позиции № ", GetLastError() );
    return(-2);
   }    
  // запоминаем время последней торговой операции
  LastTradeTime = LocalTime();
  return(0);
 }

   
Этот пример подходит только для работы одного эксперта на одном терминале. Если параллельно запустить ещё один или несколько экспертов, они не будут соблюдать десятисекундную паузу. Они просто не будут знать, когда торговал другой эксперт. У каждого эксперта  будет своя, локальная переменная LastTradeTime. Выход из этой ситуации очевиден: необходимо создать Глобальную Переменную и запоминать в ней время торговой операции. Здесь речь идёт именно о Глобальной Переменной терминала, доступ к которой будет у всех экспертов.

3. Функция _PauseBeforeTrade()

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

    Перед тем, как начать писать код, давайте конкретизируем задачу - это нам сэкономит много времени и усилий. Итак, что должна делать функция:

  • проверять, создана ли глобальная переменная, и, если нет, создавать её. Это логичнее и экономнее было бы делать из функции init() эксперта, но тогда существовала бы вероятность, что пользователь её удалит, и все работающие в тот момент эксперты перестанут соблюдать паузу между торговыми операциями. Поэтому мы разместим её в теле создаваемой функции;
  • запоминать текущее время в глобальную переменную, чтобы другие эксперты соблюдали паузу;
  • проверять, достаточно ли прошло времени после последней торговой операции. Для удобства использования также необходимо добавить внешнюю переменную, устанавливающую размер необходимой паузы. Её значение можно будет менять для каждого эксперта отдельно;
  • выводить информацию о ходе работы и о всех ошибках, произошедших в процессе работы;
  • в зависимости от результата выполнения возвращать разные значения.

Если функция определит, что после последней торговой операции прошло недостаточно времени, она должна ждать. Функция Sleep() обеспечит и ожидание, и проверку IsStopped(). То есть если эксперта удалить с графика во время "сна", он не зависнет, и не будет принудительно остановлен.

Но, для большей информативности, давайте во время "сна" каждую секунду выводить на экран информацию о том, сколько осталось ждать.

Вот что у нас должно получиться:

extern int PauseBeforeTrade = 10; // пауза между торговыми операциями (в секундах)
 
/////////////////////////////////////////////////////////////////////////////////
// int _PauseBeforeTrade()
//
// Функция устанавливает глобальной переменной LastTradeTime значение локального 
// времени.
// Если в момент запуска локальное время меньше, чем значение LastTradeTime + 
// PauseBeforeTrade, функция ждёт.
// Если глобальной переменной LastTradeTime не существует, функция создаёт её.
// Коды возвратов:
//  1 - успешное завершение
// -1 - работа эксперта была прервана пользователем (эксперт удалён с графика, закрыт
//      терминал, изменился период или символ графика, ... )
/////////////////////////////////////////////////////////////////////////////////
int _PauseBeforeTrade()
 {
  // при тестировании нет смысла выдерживать паузу - просто завершаем работу функции
  if(IsTesting()) 
    return(1); 
  int _GetLastError = 0;
  int _LastTradeTime, RealPauseBeforeTrade;
 
  //+------------------------------------------------------------------+
  //| Проверяем, существует ли гл. переменная и, если нет, создаём её  |
  //+------------------------------------------------------------------+
  while(true)
   {
    // если эксперт был остановлен пользователем, прекращаем работу
    if(IsStopped()) 
     { 
      Print("Эксперт был остановлен пользователем!"); 
      return(-1); 
     }
    // проверяем, существует ли гл. переменная
    // если она есть, выходим из этого цикла
    if(GlobalVariableCheck("LastTradeTime")) 
      break;
    else
     // если GlobalVariableCheck вернула FALSE, значит либо переменной нет, либо при 
     // проверке возникла ошибка
     {
      _GetLastError = GetLastError();
      // если это всё-таки ошибка, выводим информацию, ждём 0,1 секунды и начинаем 
      // проверку сначала
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableCheck(\"LastTradeTime\")-Error #",
              _GetLastError );
        Sleep(100);
        continue;
       }
     }
    // если ошибки нет, значит глобальной переменной просто нет, пытаемся создать её
    // если GlobalVariableSet > 0, значит глобальная переменная успешно создана. 
    // Выходим из ф-ции
    if(GlobalVariableSet("LastTradeTime", LocalTime() ) > 0) 
      return(1);
    else
     // если GlobalVariableSet вернула значение <= 0, значит при создании переменной 
     // возникла ошибка
     {
      _GetLastError = GetLastError();
      // выводим информацию, ждём 0,1 секунды и начинаем попытку сначала
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableSet(\"LastTradeTime\", ", 
              LocalTime(), ") - Error #", _GetLastError );
        Sleep(100);
        continue;
       }
     }
   }
  //+--------------------------------------------------------------------------------+
  //| Если выполнение функции дошло до этого места, значит глобальная переменная     |
  //| существует.                                                                    |
  //| Ждём, пока LocalTime() станет > LastTradeTime + PauseBeforeTrade               |
  //+--------------------------------------------------------------------------------+
  while(true)
   {
    // если эксперт был остановлен пользователем, прекращаем работу
    if(IsStopped()) 
     { 
      Print("Эксперт был остановлен пользователем!"); 
      return(-1); 
     }
    // получаем значение гл. переменной
    _LastTradeTime = GlobalVariableGet("LastTradeTime");
    // если при этом возникает ошибка, выводим информацию, ждём 0,1 секунды и начинаем 
    // попытку сначала
    _GetLastError = GetLastError();
    if(_GetLastError != 0)
     {
      Print("_PauseBeforeTrade()-GlobalVariableGet(\"LastTradeTime\")-Error #", 
            _GetLastError );
      continue;
     }
    // считаем, сколько прошло секунд со времени последней торговой операции
    RealPauseBeforeTrade = LocalTime() - _LastTradeTime;
    // если прошло меньше, чем PauseBeforeTrade секунд,
    if(RealPauseBeforeTrade < PauseBeforeTrade)
     {
      // выводим информацию, ждём секунду, и проверяем снова
      Comment("Пауза между торговыми операциями. Осталось ", 
               PauseBeforeTrade - RealPauseBeforeTrade, " сек." );
      Sleep(1000);
      continue;
     }
    // если прошло больше, чем PauseBeforeTrade секунд, останавливаем выполнения цикла
    else
      break;
   }
  //+--------------------------------------------------------------------------------+
  //| Если выполнение функции дошло до этого места, значит глобальная переменная     |
  //| существует, и локальное время больше, чем LastTradeTime + PauseBeforeTrade     |
  //| Устанавливаем глобальной переменной LastTradeTime значение локального времени  |
  //+--------------------------------------------------------------------------------+
  while(true)
   {
    // если эксперт был остановлен пользователем, прекращаем работу
    if(IsStopped()) 
     { 
      Print("Эксперт был остановлен пользователем!"); 
      return(-1);
     }

    // Устанавливаем глобальной переменной LastTradeTime значение локального 
    // времени.
    // Если успешно - выходим
    if(GlobalVariableSet( "LastTradeTime", LocalTime() ) > 0) 
     { 
      Comment(""); 
      return(1); 
     }
    else
    // если GlobalVariableSet вернула значение <= 0, значит возникла ошибка
     {
      _GetLastError = GetLastError();
      // выводим информацию, ждём 0,1 секунды и начинаем попытку сначала
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableSet(\"LastTradeTime\", ", 
              LocalTime(), " ) - Error #", _GetLastError );
        Sleep(100);
        continue;
       }
     }
   }
 }


4. Интеграция в эксперты и использование

Для проверки работоспособности функции был создан тестовый эксперт, основная задача которого торговать, соблюдая паузу. Функция _PauseBeforeTrade() была предварительно помещена в файл PauseBeforeTrade.mq4, который включен в эксперт при помощи директивы #include.

Внимание! Этот эксперт предназначен исключительно для проверки работоспособности функции! Использовать его для торговли нельзя!

#include <PauseBeforeTrade.mq4>
 
int ticket = 0;
int start()
 {
  // если нет позиции, открытой этим экспертом
  if(ticket <= 0)
   {
    // выдерживаем паузу между торговыми операциями, если произошла ошибка, выходим
    if(_PauseBeforeTrade() < 0) 
      return(-1);
    // обновляем рыночную информацию
    RefreshRates();
 
    // и пытаемся открыть позицию
    ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "PauseTest", 123, 0, 
                       Lime);
    if(ticket < 0)
      Alert("Ошибка OrderSend № ", GetLastError());
   }
  // если есть позиция, открытая этим экспертом
  else
   {
    // выдерживаем паузу между торговыми операциями (если произошла ошибка, выходим)
    if(_PauseBeforeTrade() < 0)
      return(-1);
    // обновляем рыночную информацию
    RefreshRates();
 
    // и пытаемся закрыть позицию
    if (!OrderClose( ticket, 0.1, Bid, 5, Lime ))
      Alert("Ошибка OrderClose № ", GetLastError());
    else
      ticket = 0;
   }
  return(0);
 }
   

После этого один эксперт был присоединен к графику EURUSD, M1 и второй, точно такой же, к графику GBPUSD, M1. Результат не заставил себя долго ждать - оба эксперта начали торговать, соблюдая предписанную паузу 10 секунд:







5. Возможные проблемы

При работе нескольких экспертов с одной глобальной переменной могут возникать ошибки. Чтобы этого избежать, доступ к переменной необходимо разграничить. Работающий алгоритм такого "разграничения" подробно описан в статье "Ошибка 146 ("Торговый поток занят") и как с ней бороться", его мы и будем использовать.

Окончательный вариант тестового эксперта будет выглядеть так:

#include <PauseBeforeTrade.mq4>
#include <TradeContext.mq4>
 
int ticket = 0;
int start()
 {
  // если нет позиции, открытой этим экспертом
  if(ticket <= 0)
   {
    // ждём освобождения торгового потока и занимаем его (если произошла ошибка, 
    // выходим)
    if(TradeIsBusy() < 0)
      return(-1);
    // выдерживаем паузу между торговыми операциями
    if(_PauseBeforeTrade() < 0)
     {
      // если произошла ошибка, освобождаем торговый поток и выходим
      TradeIsNotBusy();
      return(-1);
     }
    // обновляем рыночную информацию
    RefreshRates();
 
    // и пытаемся открыть позицию
    ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "PauseTest", 123, 0, 
                       Lime);
    if (ticket < 0)
      Alert("Ошибка OrderSend № ", GetLastError());
    // освобождаем торговый поток
    TradeIsNotBusy();
   }
  // если есть позиция, открытая этим экспертом
  else
   {
    // ждём освобождения торгового потока и занимаем его (если произошла ошибка, 
    // выходим)
    if(TradeIsBusy() < 0)
      return(-1);
    // выдерживаем паузу между торговыми операциями
    if(_PauseBeforeTrade() < 0)
     {
      // если произошла ошибка, освобождаем торговый поток и выходим
      TradeIsNotBusy();
      return(-1);
     }
    // обновляем рыночную информацию
    RefreshRates();
 
    // и пытаемся закрыть позицию
    if(!OrderClose( ticket, 0.1, Bid, 5, Lime))
      Alert("Ошибка OrderClose № ", GetLastError());
    else
      ticket = 0;
 
    // освобождаем торговый поток
    TradeIsNotBusy();
   }
  return(0);
 }

Прикрепленные файлы |
TradeContext.mq4 (9.27 KB)
MQL4 Comments
MQL4 Comments | 26 сен 2006 в 14:52

А можно для примера эти функции <PauseBeforeTrade.mq4> и <TradeContext.mq4>
поставить в ваш советник CrossMACD_DeLuxe из темы:" Генетические алгоритмы в MetaTrader 4_ Сравнение с прямым перебором оптимизатора".

С уважением Вячеслав.

Andrey Khatimlianskii
Andrey Khatimlianskii | 27 сен 2006 в 16:16
Vyacheslav:

А можно для примера эти функции <PauseBeforeTrade.mq4> и <TradeContext.mq4>
поставить в ваш советник CrossMACD_DeLuxe из темы:" Генетические алгоритмы в MetaTrader 4_ Сравнение с прямым перебором оптимизатора".

В статье приведен пример эксперта, используещего эти функции - PauseTest_expert.mq4
Любого другого эксперта можно переделать по аналогии.
Требования к статьям для публикации на MQL4.com Требования к статьям для публикации на MQL4.com

Требования к статьям для публикации на сайте MQL 4 Community

Ошибка 146 ("Торговый поток занят") и как с ней бороться Ошибка 146 ("Торговый поток занят") и как с ней бороться

Статья посвящена бесконфликтной торговле нескольких экспертов на одном терминале МТ 4 и рассчитана на пользователя, обладающего базовыми навыками работы с терминалом и программирования на MQL 4.

MagicNumber - "магический" идентификатор ордера MagicNumber - "магический" идентификатор ордера

Статья посвящена бесконфликтной торговле нескольких экспертов на одном терминале МТ 4. Она научит эксперта управлять только "своими" ордерами, не модифицируя и не закрывая "чужие" (открытые вручную или другими экспертами) позиции. Статья рассчитана на пользователя, обладающего базовыми навыками работы с терминалом и программирования на MQL 4.

События в МetaТrader 4 События в МetaТrader 4

Статья посвящена программному отслеживанию событий в терминале МetaТrader 4, таких как открытие, закрытие и модификация ордеров, и рассчитана на пользователя, обладающего базовыми навыками работы с терминалом и программирования на MQL 4.