Обсуждение статьи "Библиотека для простого и быстрого создания программ для MetaTrader (Часть X): Совместимость с MQL4 - События открытия позиции и активации отложенных ордеров" - страница 3

 
Alexey Viktorov:

Артём, скажи пожалуйста какую роль играет выделенный участок кода

Как может выполняться этот код, если таймер не включен?

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

И хотелось-бы вместе с сообщением о событии была возможность получить тикет, цены и возможно какие-то ещё свойства позиций и ордеров.

Мне кажется, что ты поменял комментарий. В OnTimer() советника проверяется работа не в тестере:

/************************Expert tick function************************/
void OnTick()
{
//--- Инициализация последнего торгового события
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
     }
//--- Если последнее торговое событие изменилось
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
      Comment("last_event: ",EnumToString(last_event));
      Print(__FUNCTION__, " last_event: ",EnumToString(last_event));
      //engine.ResetLastTradeEvent();
      //Print("last_event: ",EnumToString(last_event));
     }

}
/***************************Timer function***************************/
void OnTimer()
{
//--- Инициализация последнего торгового события
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Если работа не в тестере
   if(!MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
     }
}
/*******************************************************************/

И сравни два обработчика: в OnTick() таймер библиотеки запускается только в тестере, а в OnTimer() таймер библиотеки запускается только не в тестере - ведь в MQL4 таймер в тестере не работает, и работаем по тикам в тестере.

Сейчас пример сделаю.

 
Artyom Trishkin:

Мне кажется, что ты поменял комментарий.

Комментарий сам поменялся, я не трогал.)))

Просто копировал в одном месте, а вставлял в разные и не обратил внимания. Но потом поставил отрицание в виде ! а не важное не трогал.

Но вопрос остался без ответа: Если в OnInit() таймер не запускается, то какой смысл вообще в обработчике OnTimer() и почему код в нём выполняется как минимум один раз.

В принципе я получил сообщения в журнал. Осталось получить свойства позиций. Тип, тикет, цены и время открытия, закрытия, модификации.

 
Alexey Viktorov:

И хотелось-бы вместе с сообщением о событии была возможность получить тикет, цены и возможно какие-то ещё свойства позиций и ордеров.

Не в тестере ты можешь получить тикет ордера последнего события в OnChartEvent() - параметр lparam. В dparam хранится цена. В sparam - символ.

Чтобы получать данные в тестере, нужно на данный момент использовать код события, который получаешь при помощи engine.LastTradeEvent() - ведь всё зависит от события - если это модификация, то нужно получать список модификаций, а если это изменение количества ордеров, то нужно получать списки этих новых ордеров.

Нужно добавить в CEngine возврат нужных значений в программу. Я же ещё не дошёл до отправки в программу сведений - пока идёт описание подготовки необходимых данных. В последующих статьях дойдём до класса, дающего простой доступ ко всем нужным данным. А сейчас - если нужно прям срочно, то надо добавить в CEngine возврат списка из класса-коллекции событий - в самом классе есть получение этого списка, вот они - все списки в публичной секции CEventCollection:

public:
//--- Выбирает события из коллекции со временем в диапазоне от begin_time до end_time
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- Возвращает полный список-коллекцию событий "как есть"
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_events;                                           }
//--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   CArrayObj        *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }

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

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

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

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

 
Alexey Viktorov:

Комментарий сам поменялся, я не трогал.)))

Просто копировал в одном месте, а вставлял в разные и не обратил внимания. Но потом поставил отрицание в виде ! а не важное не трогал.

Но вопрос остался без ответа: Если в OnInit() таймер не запускается, то какой смысл вообще в обработчике OnTimer() и почему код в нём выполняется как минимум один раз.

В принципе я получил сообщения в журнал. Осталось получить свойства позиций. Тип, тикет, цены и время открытия, закрытия, модификации.

Поясни что имеешь в виду? Ты говоришь о создании таймера? Он создаётся в конструкторе CEngine:

//+------------------------------------------------------------------+
//| CEngine конструктор                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT)
  {
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
         ::Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
         ::Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
   #endif 
  }
//+------------------------------------------------------------------+
 
Artyom Trishkin:

Не в тестере ты можешь получить тикет ордера последнего события в OnChartEvent() - параметр lparam. В dparam хранится цена. В sparam - символ.

Это я уже нашёл, спасибо. А получив тикет можно получить всё что надо. За исключением с какой цены была модификация. Или делать действительно костыль чтобы знать цену до модификации. В принципе не очень пока надо.

 
Alexey Viktorov:

В принципе я получил сообщения в журнал. Осталось получить свойства позиций. Тип, тикет, цены и время открытия, закрытия, модификации.

Впрочем, для быстрого решения, добавь в публичную секцию CEngine возврат списка событий:

public:
   //--- Возвращает список рыночных (1) позиций, (2) отложенных ордеров и (3) маркет-ордеров
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Возвращает список исторических (1) ордеров, (2) удалённых отложенных ордеров, (3) сделок, (4) всех маркет-ордеров позиции по её идентификатору
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(const ulong position_id);
//--- Возвращает список событий
   CArrayObj*           GetListAllEvents(void)                          { return this.m_events.GetList();      }
//--- Сбрасывает последнее торговое событие
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Возвращает (1) последнее торговое событие, (2) флаг счёта-хедж, (3) флаг работы в тестере
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;      }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;              }
   bool                 IsTester(void)                            const { return this.m_is_tester;             }
//--- Создаёт счётчик таймера
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Таймер
   void                 OnTimer(void);
//--- Конструктор/Деструктор
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

В советнике добавь этот код:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Инициализация последнего торгового события
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- Если последнее торговое событие изменилось
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
      Comment("\nlast_event: ",EnumToString(last_event));
      CArrayObj* list=engine.GetListAllEvents();
      if(list!=NULL)
        {
         if(list.Total()>0)
           {
            CEvent* event=list.At(list.Total()-1);
            if(event!=NULL)
              {
               event.Print();
              }
           }
        }
     }
//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+

И последнее событие распечатается в журнале

 
Artyom Trishkin:

Поясни что имеешь в виду? Ты говоришь о создании таймера? Он создаётся в конструкторе CEngine:

Ну, так для этого-же надо было всю библиотеку проштудировать. )))

 
Alexey Viktorov:

Это я уже нашёл, спасибо. А получив тикет можно получить всё что надо. За исключением с какой цены была модификация. Или делать действительно костыль чтобы знать цену до модификации. В принципе не очень пока надо.

Я тебе уже дал код - там есть всё, и цена до модификации тоже.

 
Alexey Viktorov:

Ну, так для этого-же надо было всю библиотеку проштудировать. )))

Нет. Просто прочитать статьи

 

И вот такая непонятка случилась:

Пока я гонял этот код на демке был установлен и удалён лимитный ордер

443342388 2019.05.27 14:54:10 buy limit 0.01 eurusd 1.11835 0.00000 0.00000 2019.05.27 15:01:14 1.11972 cancelled 

И вдруг при очередном издевательстве был модифицирована одна позиция, одна открыта и закрыта. Но вот откуда появилась запись об удалении давно удалённого ордера?

2019.05.27 18:34:11.903 00 EURUSD,H1: OnChartEvent: id=1002, event=TRADE_EVENT_PENDING_ORDER_REMOVED, lparam=443342388, dparam=1.11835, sparam=EURUSD
2019.05.27 18:34:11.903 00 EURUSD,H1: OnChartEvent: id=1024, event=TRADE_EVENT_POSITION_CLOSED, lparam=443417294, dparam=1.11933, sparam=EURUSD
2019.05.27 18:34:11.903 00 EURUSD,H1: - Отложенный ордер удалён: 2019.05.27 14:54:10.000 -
EURUSD Удалён 0.01 Buy Limit #443342388 по цене 1.11835
2019.05.27 18:34:11.903 00 EURUSD,H1: - Позиция закрыта: 2019.05.27 18:33:02.000 -
EURUSD Закрыт Sell #443417294 по цене 1.11912, профит -0.21 USD
2019.05.27 18:33:02.755 00 EURUSD,H1: OnChartEvent: id=1022, event=TRADE_EVENT_POSITION_OPENED, lparam=443417294, dparam=1.11912, sparam=EURUSD
2019.05.27 18:33:02.755 00 EURUSD,H1: - Позиция открыта: 2019.05.27 18:33:02.000 -
EURUSD Открыт 0.01 Sell #443417294 [0.01 Market order Sell #443417294] по цене 1.11912
2019.05.27 18:29:21.913 00 EURUSD,H1: OnChartEvent: id=1050, event=TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT, lparam=443218277, dparam=1.12218, sparam=EURUSD
2019.05.27 18:29:21.913 00 EURUSD,H1: - Модифицирован TakeProfit позиции: 2019.05.27 18:27:45.000 -
EURUSD Buy #443218277: модифицирован TakeProfit: [1.12240 --> 1.12218]
Причина обращения: