文章 "跨平台的EA交易: 信号"

 

新文章 跨平台的EA交易: 信号已发布:

本文讨论了 CSignal 和 CSignals 类,它们将用于创建跨平台的EA交易。它检验了MQL4和MQL5的区别,看它们在评估交易信号时需要怎样特别的数据,这样来确保写出的代码可以兼容两种编译器。

所有生成当前信号所需的计算都移动到 CSignal 和 CSignals 对象中,这样,我们所要做的就是让 CSignals 进行一次检查,然后通过调用它的 CheckOpenLong 和 CheckOpenShort 方法来取得输出。以下屏幕截图显示了在 MetaTrader 4 和 MetaTrader 5 中 EA 的测试结果。

(MT4)

signal_ordermanager (MT4)

作者:Enrico Lambino

 
MetaQuotes Software Corp.:

新文章:跨平台智能交易系统:信号》已出版:

作者: Enrico Lambino恩里科-兰比诺


你好,恩里科。我刚刚看了您的作品,希望能找到解决问题的办法。在编译 StopBase 类时,我发现了一系列错误,请参见下文

implicit conversion from 'number' to 'string'   OrderStopBase.mqh       395     25
implicit conversion from 'number' to 'string'   OrderStopBase.mqh       467     26
implicit enum conversion        OrderStopBase.mqh       468     33
implicit enum conversion        OrderStop.mqh   37      33
implicit conversion from 'number' to 'string'   OrderStop.mqh   44      43
'COrderBase' - import not defined       OrderStop.mqh   54      7
'else' - semicolon expected     OrderStop.mqh   81      4
')' - unexpected token  OrderStop.mqh   81      46
implicit conversion from 'number' to 'string'   StopBase.mqh    767     22
implicit conversion from 'number' to 'string'   StopBase.mqh    774     28
implicit conversion from 'number' to 'string'   StopBase.mqh    796     22
implicit conversion from 'number' to 'string'   StopBase.mqh    803     28
implicit conversion from 'number' to 'string'   StopBase.mqh    698     36
'OrderType' - cannot convert enum       Stop.mqh        227     52
'StopLossCustom' - no one of the overloads can be applied to the function call  Stop.mqh        228     61
could be one of 2 function(s)   Stop.mqh        228     61
   double CStopBase::StopLossCustom(const string,const ENUM_ORDER_TYPE,const double)    StopBase.mqh    136     22
   bool CStopBase::StopLossCustom()     StopBase.mqh    99      22
'OrderType' - cannot convert enum       Stop.mqh        215     71
'TakeProfitCustom' - no one of the overloads can be applied to the function call        Stop.mqh        215     98
could be one of 2 function(s)   Stop.mqh        215     98
   double CStopBase::TakeProfitCustom(const string,const ENUM_ORDER_TYPE,const double)  StopBase.mqh    140     22
   bool CStopBase::TakeProfitCustom()   StopBase.mqh    111     22
implicit conversion from 'number' to 'string'   Stop.mqh        250     39
implicit enum conversion        Stop.mqh        291     31
implicit conversion from 'number' to 'string'   Stop.mqh        292     36

忽略警告,这些错误似乎是由顺序类型问题引起的。此外,在编译您的示例 signal_ma 时还出现了以下错误

'GetPointer' - parameter conversion not allowed OrderStopVirtualBase.mqh        51      39
'GetPointer' - parameter conversion not allowed OrderStopVirtualBase.mqh        58      41
'=' - type mismatch     OrderStopsBase.mqh      106     23
'=' - type mismatch     OrderStopsBase.mqh      108     23
'=' - type mismatch     OrderStopsBase.mqh      110     23
'=' - type mismatch     OrderStopsBase.mqh      180     20
'=' - type mismatch     OrderStopsBase.mqh      182     20
'=' - type mismatch     OrderStopsBase.mqh      184     20

请问这些错误来自何处,我该如何解决? 关于相关问题,据我所知,利斯科夫替代法规定,我们应该按照接口编程,因为超类可以用基类替代。你为什么选择将对象传入函数(函数本身就是接口),而不是使用它们的基类?我的理解可能有误,但我认为这是最佳做法,不是吗?

提前感谢您的帮助和您所做的出色工作。
 

你好,Shephard,感谢您的评论。关于您的问题:

  1. 不幸的是,目前还无法单独编译 StopBase.mqh。虽然 MQL4 和 MQL5 编译器接受前向声明,但如果您尝试访问类中被前向声明的方法或成员,它们就会出错。您需要将 StopBase.mqh 与更大的类一起编译,如 COrderManager 或CExpertAdvisor(CStop 和 CStops 是这两个类的组件)。
  2. 关于类型不匹配错误,我在原始文件中没有发现(您是否修改了源文件?)
  3. 我认为在大多数情况下,基类可以顺利传递给类方法。当专家使用库编码时,假定基类根本不存在会更容易。但有些对象有新的虚拟方法和非虚拟方法。基类无法单独访问这两组方法。如果使用派生类而不是基类,编译器就能选择派生类的正确版本,因此使用派生类比单独使用基类更全面。
 
Enrico Lambino:

你好,Shephard,感谢您的评论。关于您的问题:

  1. 不幸的是,目前还无法单独编译 StopBase.mqh。虽然 MQL4 和 MQL5 编译器接受前向声明,但如果您尝试访问类中被前向声明的方法或成员,它们就会出错。您需要将 StopBase.mqh 与更大的类一起编译,如 COrderManager 或 CExpertAdvisor(CStop 和 CStops 是这两个类的组件)。
  2. 关于类型不匹配错误,我在原始文件中没有发现(您是否修改了源文件?)
  3. 我认为在大多数情况下,基类可以顺利传递给类方法。当专家使用库编码时,假定基类根本不存在会更容易。但有些对象有新的虚拟方法和非虚拟方法。基类无法单独访问这两组方法。如果使用派生类而不是基类,编译器就能选择派生类的正确版本,因此使用派生类比单独使用基类更全面。

你好,恩里科、

非常感谢你的快速回复。我理解您传递具体类而不是基类的原因。某些在 C++ 中可以直接实现的功能在 MQL 中却无法实现,但事实就是如此。

关于类型不匹配,我没有改动任何东西。我只是下载并编译。你的方法真的很棒。

我是这样开发我的系统的。我将一个信号输出系统视为一个策略。 假设我有一个 MA 交叉系统,如果我用欧元美元 创建一个对象,这就是一个策略。 我将这个策略添加到策略列表中。我可以创建另一个策略,并将其添加到列表中。 但这些策略不是通过 OnTick 调用的,而是观察者模式的一部分。 当特定事件发生时,例如 5 分钟的新条形图、10 Pip Renko 新条形图等,它们会被更新或调用。

再次感谢您抽出时间与我们分享您的知识和技能,以及您的及时回复。

 

你好,Shephard、

感谢您分享您的建议和见解。

我认为您的想法很好。我目前正在编写本系列的最后几篇文章(包括订单止损和止损)。 我不确定止损类是否合适(这取决于您的实现方式)。如果您能让它们独立于订单管理器 工作,那将会很好,因为这将有助于简化您的类对象。但我希望您会发现它们在您的工作中是有用的。

关于编译:

  1. 只有主源文件/头文件和基类文件可以无错编译(使用前向声明的文件除外)。这就是为什么在示例中,我在头文件中使用 #include 指令来处理基础文件(而不是特定语言文件)。
  2. 您应该使用正确的编译器(使用 MQL4 编译器编译 mq5 文件会导致编译器错误)。
 

你好,恩里科、

我正在测试您的信号模块。我有一个 SHORT 信号和一个 NEUTRAL 信号。为什么最后会出现 CMD_VOID?CMD_VOID 是什么时候出现的?

实际上,这出现在 CSignalsBase::Check 中。

if(signal.Entry())
        {
         if(m_signal_open>CMD_VOID)
           {
            ENUM_CMD signal_open=signal.SignalOpen();
            if(m_signal_open==CMD_NEUTRAL)
              {    
               m_signal_open=signal_open;
              }
            else if(m_signal_open!=signal_open)
              {               
               m_signal_open=CMD_VOID;
              }
           }
        }

只有 2 个信号。前一个信号是 CMD_SHORT。当前信号是 CMD_NEUTRAL。您能否确认 CMD_SHORT 和 CMD_NEUTRAL 的结果是 CMD_VOID?

如果我的第一个信号是 CMD_NEUTRAL,第二个信号是 CMD_SHORT,那么总信号就是 CMD_SHORT。但如果第一个信号是 CMD_SHORT,第二个信号是 CMD_NEUTRAL,那么结果就是 CMD_VOID。

我猜应该是这样的:

if(signal.Entry())
        {
         if(m_signal_open>CMD_VOID)
           {
            ENUM_CMD signal_open=signal.SignalOpen();
            if(m_signal_open==CMD_NEUTRAL)
              {    
               m_signal_open=signal_open;
              }
            else if(m_signal_open!=signal_open && signal_open!=CMD_NEUTRAL)
              {               
               m_signal_open=CMD_VOID;
              }
           }
        }
 
if(m_new_signal)
     {
      if(m_signal_open==m_signal_open_last)
         m_signal_open = CMD_NEUTRAL;
      if(m_signal_close==m_signal_close_last)
         m_signal_close= CMD_NEUTRAL;
     }

就收盘信号而言,这并不完全正确。为什么不能有两个随后的同方向收盘信号?

例如,我有进入信号 "空头",下一个退出信号 "多头"(平仓空头),下一个进入信号 "多头",下一个进入信号 "空头",下一个退出信号 "多头"。因此,最后一个退出空头头寸的信号将不起作用,因为它与前一个退出信号方向相同。

 
mbjen:

就收盘信号而言,这并不完全正确。为什么不能有两个随后的同方向收盘信号?

例如,我有进入信号 "空头",下一个退出信号 "多头"(平仓空头),下一个进入信号 "多头",下一个进入信号 "空头",下一个退出信号 "多头"。因此,最后一个退出空头头寸的信号将不起作用,因为它与前一个退出信号方向相同。

至于如何评估信号,这取决于你想要达到什么目的。

您发布的最后一段代码只有在类成员 m_new_signal 设置为 true 时才会执行。这仅用于新信号的交易。您可以使用类中的 NewSignal 方法来设置这个受保护的类成员。

 
Enrico Lambino:

这取决于您希望如何评估信号。

您发布的最后一段代码只有在类成员 m_new_signal 设置为 true 时才会执行。这仅用于新信号的交易。您可以使用类中的 NewSignal 方法来设置这个受保护的类成员。


我知道这一点。但如果我将其设置为 false,也会影响我的进入信号。进入信号可以,但退出信号不行,因为退出规则可能不同。它可以是回调信号,也可以是退出信号,所以两个相同方向的退出信号是正常的。

 

m_signal_close 中似乎保存了 SignalBase.mqh 中之前的信号值。例如,我有一些退出信号。如果 其进行检查 且 Calculate() 方法返回 false,则会给出上次退出时的信号值。

 

嗨,mbjen、

mbjen:

你好,恩里科、

我正在测试您的信号模块。我有一个 SHORT 信号和一个 NEUTRAL 信号。为什么最后会出现 CMD_VOID?CMD_VOID 是什么时候出现的?

实际上,这出现在 CSignalsBase::Check 中。

只有 2 个信号。前一个信号是 CMD_SHORT。当前信号是 CMD_NEUTRAL。您能否确认 CMD_SHORT 和 CMD_NEUTRAL 的结果是 CMD_VOID?

如果我的第一个信号是 CMD_NEUTRAL,第二个信号是 CMD_SHORT,那么总信号就是 CMD_SHORT。但如果第一个信号是 CMD_SHORT,第二个信号是 CMD_NEUTRAL,结果就是 CMD_VOID。

我猜一定是这样的:

很抱歉忽略了你的第一个问题。我直到现在才注意到。这是一个非常好的反馈。

感谢您指出这一点。是的,您说得没错。我会更新代码并修改文章。

mbjen

我知道。但如果我将它设为 false,也会影响我的入市信号。进场信号可以,但出场信号不行,因为出场规则可能不同。它可以是回调信号,也可以是退出信号,所以两个相同方向的退出信号是正常的。

你说得有道理。但举例来说,如果我只想在 MA 交叉时发出退出信号,就需要这样做。我认为将它们分开是比当前代码更好的选择:

if(m_new_signal)
 {
  if(m_signal_open==m_signal_open_last)
  m_signal_open = CMD_NEUTRAL;
 }
if(m_new_signal_close)
 {
  if(m_signal_close==m_signal_close_last)
  m_signal_close= CMD_NEUTRAL;
 }
mbjen:

m_signal_close 中保存的是 SignalBase.mqh 中的前一个信号值。例如,我有一些退出信号。如果对其进行检查,并且 Calculate() 方法返回 false,则会给出上次退出时的最后一个信号值。

Calculate 方法是一个虚拟方法。如果该方法返回 false,则应在方法本身中将这些类成员重置为中性。