文章 "轻松快捷开发 MetaTrader 程序的函数库(第十部分):与 MQL4 的兼容性 - 开仓和激活挂单的事件" - 页 3

 
Alexey Viktorov:

Artem,请告诉我突出显示的代码部分起什么作用。

如果定时器没有启用,如何执行这段代码?

如果删除这部分代码,事件信息就不会打印出来。但有了它,一切都能正常运行。

我希望在获得事件消息的同时,还能获得票据、价格以及仓位 和订单的其他属性

我认为您修改了注释。在OnTimer() 中,EA 被检查, 而不是 在测试器中:

/************************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 中,测试器中的定时器不起作用,而我们在测试器中处理 ticks。

我现在举个例子。

 
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();
  };
//+------------------------------------------------------------------+

在 EA 中添加此代码

//+------------------------------------------------------------------+
//| 专家勾选功能|
//+------------------------------------------------------------------+
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]