用于从历史中读取订单特性的函数

根据特性值的基本类型,按照将可用特性的标识符划分为 3 种枚举,这与将可用属性标识符划分为三个枚举的方式一致: ENUM_ORDER_PROPERTY_INTEGERENUM_ORDER_PROPERTY_DOUBLEENUM_ORDER_PROPERTY_STRING ,这在前面研究活动订单时的 单独的章节 中讨论过。

在调用这些函数之前,你需要以某种方式 在历史中选择一组合适的订单号

如果你尝试读取其订单号不在所选历史上下文内的订单或交易的特性,环境可能会生成 WRONG_INTERNAL_PARAMETER (4002) 错误,可以通过 _LastError对其进行分析。

对于每个基础特性类型,有两种函数形式:一种直接返回所请求特性的值,第二种将它写入通过引用传递的参数,并返回成功指示符 (true)或错误 (false)。

对于整数型及其兼容型(datetime 和枚举)特性,有一个专用函数 HistoryOrderGetInteger

long HistoryOrderGetInteger(ulong ticket, ENUM_ORDER_PROPERTY_INTEGER property)

bool HistoryOrderGetInteger(ulong ticket, ENUM_ORDER_PROPERTY_INTEGER property,
long &value)

该函数允许你通过订单号从所选历史中找出订单 property

对于实数型特性,分配了 HistoryOrderGetDouble 函数

double HistoryOrderGetDouble(ulong ticket, ENUM_ORDER_PROPERTY_DOUBLE property)

bool HistoryOrderGetDouble(ulong ticket, ENUM_ORDER_PROPERTY_DOUBLE property,
double &value)

最后,可以使用 HistoryOrderGetString 读取字符串型特性。

string HistoryOrderGetString(ulong ticket, ENUM_ORDER_PROPERTY_STRING property)

bool HistoryOrderGetString(ulong ticket, ENUM_ORDER_PROPERTY_STRING property,
string &value)

现在我们可以补充 OrderMonitor 类 (OrderMonitor.mqh) 来处理历史订单。首先,让我们将一个布尔变量添加到 history 类中,我们将根据选择带有传递订单号订单的片段将该变量填充到构造函数中:在活跃订单中 (OrderSelect) 或在历史 (HistoryOrderSelect) 中。

class OrderMonitorpublic OrderMonitorInterface
{
   bool history;
   
public:
   const ulong ticket;
   OrderMonitor(const long t): ticket(t), history(!OrderSelect(t))
   {
      if(history && !HistoryOrderSelect(ticket))
      {
         PrintFormat("Error: OrderSelect(%lld) failed: %s"ticketE2S(_LastError));
      }
      else
      {
         ResetLastError();
         ready = true;
      }
   }
   ...

我们需要在成功的 if 分支中调用 ResetLastError 函数,以便重置可能由 OrderSelect 函数设置的错误(如果订单在历史中)。

事实上,这个版本的构造函数包含了一个严重的逻辑错误,我们将在几段后返回来研究。

为了读取 get 方法中的特性,我们现在调用不同的内置函数,具体取决于 history 变量的值。

virtuallongget(constENUM_ORDER_PROPERTY_INTEGERproperty)constoverride

{

returnhistory?HistoryOrderGetInteger(ticketproperty) :OrderGetInteger(property);

}

 

virtualdoubleget(constENUM_ORDER_PROPERTY_DOUBLEproperty)constoverride

{

returnhistory?HistoryOrderGetDouble(ticketproperty) :OrderGetDouble(property);

}

 

virtualstringget(constENUM_ORDER_PROPERTY_STRINGproperty)constoverride

{

returnhistory?HistoryOrderGetString(ticketproperty) :OrderGetString(property);

}

...

OrderMonitor 类的主要用途是向其他分析类提供数据。OrderMonitor 对象用于筛选 OrderFilter 类中的活动订单,我们需要一个类似的类来根据历史中的任意条件选择订单:HistoryOrderFilter

让我们在同一个文件 OrderFilter.mqh 中编写这个类。其使用两个新函数来处理历史:HistoryOrdersTotalHistoryOrderGetTicket

class HistoryOrderFilterpublic TradeFilter<OrderMonitor,
   ENUM_ORDER_PROPERTY_INTEGER,
   ENUM_ORDER_PROPERTY_DOUBLE,
   ENUM_ORDER_PROPERTY_STRING>
{
protected:
   virtual int total() const override
   {
      return HistoryOrdersTotal();
   }
   virtual ulong get(const int iconst override
   {
      return HistoryOrderGetTicket(i);
   }
};

这个简单的代码继承自模板类 TradeFilter,其中该类作为模板 OrderMonitor 的第一个参数传递,以读取相应对象的特性(我们看到了一个用于仓位的模拟,很快也会为交易创建一个模拟)。

这就是 OrderMonitor 构造函数的问题所在。正如我们在 从历史中选择订单和交易一节中了解到的,为了分析账户,我们必须首先用一个函数(如 HistorySelect)设置上下文。因此,在源代码 HistoryOrderFilter 中,假设 MQL 程序已经选择了所需的历史片段。但是,OrderMonitor 构造函数的新中间版本可使用 HistoryOrderSelect 调用来检查订单号在历史中是否存在。同时,该函数还可以重置历史订单的先前上下文并选择单个订单。

因此,我们需要一个辅助方法 historyOrderSelectWeak,以一种“软”的方式验证订单号,而不破坏现有的上下文。为此,我们可以简单地检查 ORDER_TICKET 特性是否等于传递的订单号 t(HistoryOrderGetInteger(t, ORDER_TICKET) == t)。如果已经选择了这样的订单号(可用),则检查成功,并且监视器不需要操作历史。

class OrderMonitorpublic OrderMonitorInterface
{
   bool historyOrderSelectWeak(const ulong tconst
   {
      return (((HistoryOrderGetInteger(tORDER_TICKET) == t) ||
         (HistorySelect(0LONG_MAX) && (HistoryOrderGetInteger(tORDER_TICKET) == t))));
   }
   bool history;
   
public:
   const ulong ticket;
   OrderMonitor(const long t): ticket(t), history(!OrderSelect(t))
   {
      if(history && !historyOrderSelectWeak(ticket))
      {
         PrintFormat("Error: OrderSelect(%lld) failed: %s"ticketE2S(_LastError));
      }
      else
      {
         ResetLastError();
         ready = true;
      }
   }

在我们为交易准备了类似的功能后,下一节将考虑对历史应用订单筛选的示例。