文章 "轻松快捷开发 MetaTrader 程序的函数库(第 二十一部分):交易类 - 基准跨平台交易对象"

 

新文章 轻松快捷开发 MetaTrader 程序的函数库(第 二十一部分):交易类 - 基准跨平台交易对象已发布:

在本文中,我们将着手开发新的函数库部分 - 交易类。 此外,我们将研究开发一套统合 MetaTrader 5 和 MetaTrader 4 平台的基准交易对象。 当向服务器发送请求时,即意味着传递给这种交易对象的交易请求参数已被验证和校正。

随时可以轻松访问各种数据是多么惬意啊。 然而,如果我们不能将数据应用到交易,则数据毫无意义。 意即不光已经存在的功能,我们还需要新的交易功能。
本章节相对庞大,我们需要逐步完成所有操作。

  • 我们应能够从任意平台(无论是 MetaTrader 5 亦或 MetaTrader 4)发送任何交易请求,而无需考虑它们之间的差异。 一切都应该统合。
  • 首先,我们需要验证交易请求,以避免因错误请求而加重服务器负载。
  • 我们需要考虑并正确处理交易服务器的返回码。 EA 向服务器发送请求时会做什么? 它维护终端与服务器之间的“请求-响应”会话。 我们的任务是正确分配这种“通信管道”,即创建处理交易服务器响应的方法。
  • 我们需要创建若干处理服务器响应的选项,因为有时我们需要“优先不惜一切代价”开仓。 为此,若下单时遇到服务器拒绝的万一情况,我们需要布置向服务器重复发送请求 — 我们既可调整交易请求参数,亦或重新发送,再或者保留所有参数不变,但等待符合这些请求参数的正确时机,并立即将其发送。 此外,我们需要考虑价位,以避免明知价格较差却重新发送订单。
    有时,无论请求的结果如何,我们都需要发送交易请求并继续操作。
  • 此外,我们需要管理交易类的操作,以避免基于函数库的程序在 MQL5 市场 上架后出现故障。 该程序应能平滑地通过 所有检查
这就是目前我针对交易类的计划。

作者:Artyom Trishkin

 

我仔细阅读了其中一半以上的内容。再往后,我的意识开始恍惚。这篇文章的篇幅非常大。

阿纳托利-科扎尔斯基总是在文章末尾附上图书馆的总体规划,这样人们就可以清楚地了解图书馆的结构。我在您的文章中没有看到过这样的方案,因此,我觉得自己就像爬在 "大象 "身上的 "蚂蚁"。我正在建立一个关于图书馆的简明概念,但读了您的文章后,我很难把它编造出来。有很多 "被咀嚼 "过的信息,如细节、代码解释、对其他文章的引用等,但却没有一个完整、简洁的库描述和方案。

当然,实施水平非常高。一切都非常专业,由此可见一斑。但是,我对是否需要这么多 "包装器 "表示质疑。例如:"CTradeObj::SetOrder" 方法有以下包装器:

//--- 设置挂单 (1) BuyStop,(2) BuyLimit,(3) BuyStopLimit
   bool                 PlaceBuyStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- 设置挂单 (1) SellStop、(2) SellLimit、(3) SellStopLimit
   bool                 PlaceSellStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- 
                        

它们都没有太大区别,但却占用了大量空间。有了这种 "深度包装",材料和代码就会像 "酵母 "一样生长,功能的使用也会变得更加复杂,与实体数量的增长成正比,呈指数级增长。

不,我不反对。我想这就是多态性的 意义所在--当一个方法衍生出许多包装器时,每个包装器都有自己的特殊性。问题是,采用这种方法的解决方案结构增长太快,不再具有实用性。我们需要限制材料的增长,否则它将变得越来越难以理解和使用。

 
Реter Konow:

我仔细阅读了其中一半以上的内容。再往后,我的意识开始恍惚。这篇文章的篇幅非常大。

阿纳托利-科扎尔斯基总是在文章末尾附上图书馆的总体规划,这样人们就可以清楚地了解图书馆的结构。我在您的文章中没有看到过这样的方案,因此,我觉得自己就像爬在 "大象 "身上的 "蚂蚁"。我正在建立一个关于图书馆的简明概念,但读了您的文章后,我很难把它编造出来。有很多 "被咀嚼 "过的信息,如细节、代码解释、对其他文章的引用等,但却没有完整、简洁的图书馆描述和方案。

当然,实施水平非常高。一切都非常专业,这一点显而易见。但是,我对是否需要这么多 "封装器 "表示质疑。 例如:"CTradeObj::SetOrder"方法有以下包装器:

它们都没有太大区别,但却占用了大量空间。这种 "深度包装 "会使材料和代码像 "酵母 "一样生长,功能的使用会变得更加复杂,与实体数量的增长成正比,呈指数级增长。

不,我不反对。我想这就是多态性的 意义所在--当一个方法衍生出许多包装器时,每个包装器都有自己的特殊性。问题是,采用这种方法的解决方案结构增长太快,不再具有实用性。我们需要限制材料的增长,否则它将变得越来越难以理解和使用。

谢谢您的评价,彼得。您应该只使用 CEngine 提供的内容,而这些内容又可以从课程中获得。

其余的都是方法的实现,所有必要的参数都会传递给这些方法。在您自己的程序中,这些方法并不是实际使用所必需的,而是库本身所必需的。因此,对于最终用户来说,方法总是不多,这一点在测试顾问中有所体现。

当然,一切都将被描述。包括结构和用户可用的所有方法。用户函数将被创建,隐藏 OOP 实现,不需要使用显式访问库对象。

但一切都在我们的前方--我们需要编写主要功能,这些功能可以结构化和表格化描述,以便描述完整且无需修改。

随着每篇后续文章的推出,方法的差异将大幅增加。本文只是贸易类工作的开端。此外,请查看OrderSend() 中要发送的每种订单类型--它需要哪些参数。所有这些参数都会传递给方法。当然,也可以像在指标中那样通过描述结构传递参数,但这是不必要的。
您也可以让用户填写必要的结构,但直接将参数传递给方法比事先填写结构然后再传递给方法更简单友好。这样做只是为了方便最终用户使用,而不是为了方便解析库组件。
 
Artyom Trishkin:

谢谢您的评价,彼得。有必要仅使用 CEngine 提供的内容,而 CEngine 又可从该计划中获取。

其余的都是方法的实现,所有必要的参数都会传递给这些方法。在您自己的程序中,这些方法并不是实际使用所必需的,而是库本身所必需的。因此,对于最终用户来说,方法总是不多,这一点在测试顾问中有所体现。

当然,一切都将被描述。包括结构和用户可用的所有方法。用户函数将被创建,隐藏 OOP 实现,不需要使用显式访问库对象。

但一切都在我们的前方--我们需要编写主要功能,这些功能可以结构化和表格化描述,以便描述完整且无需修改。

随着每篇后续文章的推出,方法的差异将大大增加。本文只是贸易类工作的开端。

为了避免不靠谱,我建议采取以下措施来限制 "包装":

重写 " CTradeObj::SetOrder" 方法,使其多接收一个参数--要设置的订单类型。此外,该方法本身将决定如何进行设置。它将初始化所需的结构、设置所需的值并调用所需的方法。因此,您将把所有包装器压缩为一个附加参数,发送给" CTradeObj::SetOrder" 方法。它将接收PlaceBuyStop、PlaceSellStopLimit 或PlaceBuyLimit 作为参数,然后执行必要的工作。

当然,这种方法实现起来比较复杂,但可以确保 "压缩 "库的功能并方便使用。

祝您好运

 
Реter Konow:

为了避免证据不足,我建议采取以下措施来限制 "包装":

CTradeObj::SetOrder" 方法应重写,使其多接收一个参数--要设置的订单类型。此外,该方法本身将决定如何执行。它将初始化所需的结构、设置所需的值并调用所需的方法。因此,您将把所有包装器压缩为一个附加参数,发送给" CTradeObj::SetOrder" 方法。它将接收PlaceBuyStop、PlaceSellStopLimit 或PlaceBuyLimit 作为参数,然后执行必要的工作。

当然,这种方法实现起来比较复杂,但可以确保 "压缩 "库的功能并方便使用。

祝您好运

库用户不需要这种方法。图书馆本身需要它。

彼得尝试仅通过设置类型来打开一个位置。就在终端中。然后看看还需要什么。终端不会为您决定用什么符号开仓、用什么手数开仓、设置什么止损位等。

 
Artyom Trishkin:

...

此外,请查看OrderSend() 发送的每种订单类型 - 它需要哪些参数。所有这些参数都会传递给方法。当然,也可以像在指标中那样通过描述的结构传递参数,但这是不必要的。

您也可以让用户填写必要的结构,但直接将参数传递给方法比事先填写结构然后再传递给方法更简单友好。这样做是为了方便最终用户,而不是为了方便解析库组件。
在我看来,最友好的选择是只为 OrderSend() 提供一个封装器。这个封装器将接受用户提供的参数,并决定如何格式化调用。我会这样做。不过,这取决于您。
 
Реter Konow:
在我看来,最友好的方案是为 OrderSend() 设计一个封装器。这个封装器将接受用户提供的参数,并决定调用的格式。我会这样做。但是,你知道得更清楚。

我是这样做的--方法接受来自用户的参数。

 
Artyom Trishkin:

我是这样做的--方法接受来自用户的参数。

是的,我明白。这就是订单处理的 "多层 "实现。问题在于封装的 "厚度"。我混淆了哪些方法指的是机制,哪些方法指的是库的用户界面。对不起。如果封装层少一些,就会更容易理解,但这是你的选择。
 
Реter Konow:
是的,明白了。这就是订单处理的 "多层 "实现。问题在于封装的 "厚度"。我混淆了哪些方法指的是机制,哪些方法指的是库的用户界面。对不起。如果封装层少一些,就会更容易理解,但这是你的选择。

彼得,你看,我试图给出的不是一种工作方法,而是多种可能性。

在这篇文章中,组织了对交易功能的低级访问。交易对象所做的就是将接收到的参数正确地排列到向服务器发送订单的功能中。要发送交易指令,需要访问符号集合,获取所需符号的对象,并从该对象获取一个交易对象。然后处理交易对象。在此,我简化了用户填写交易订单的过程--只需传递必要的参数即可。但是必须独立检查这些参数的正确性,因为交易对象认为所有传给它的参数都已经正确。

这并不方便。但是有这样一种方法。有需要的人可以自己检查一切,然后发送交易指令。没有服务器响应处理。这是已经完成的第一件事。

下一篇文章将介绍一个交易类,它将拥有相同参数的交易方法集,但现在将检查所有内容的正确性,如果参数不正确(或其中之一不正确,或不可能进行交易),则返回程序。这是第二阶段--这里已经有两种方法--第一种已经存在,第二种--当库本身检查参数的正确性时,如果参数全部正确,则发送指令,如果至少有一个参数不正确,或者不可能进行交易,则直接返回控制程序。在这里,将组织更高级别的访问,并进行正确性检查。

但这还不是全部。我们还需要对错误做出反应。在下一篇文章中,我们将更进一步--我们将处理在交易订单参数中收到的错误(甚至在将它们发送到服务器之前)。毕竟,如果出现错误,您可以拒绝开仓,也可以更正参数并重新发送请求。这已经是交易对象工作的第三个层次。

接下来会有更高层次的方法--一切都将根据所需的设置自动运行。这就是延迟请求--当 Expert Advisor 等待时机再次发送所需订单时,或者只需知道在什么情况下需要发送请求。所有这些待处理的请求都将存放在它的储蓄罐中,等待合适的时机发送请求。

因此,彼得,现在下结论还为时过早。

你建议只提供一种交易功能,但我想提供多种方法--每个人都可以根据自己的需要来选择。当然,不可能让每个人都满意。

 

Artyom Trishkin:

...

我喜欢你的解释,但也因此产生了一个内部矛盾。

1.一方面,通过构建一个分层的 "封装 "结构,让用户可以访问库引擎的所有层级,在这里,不是压缩算法,而是扩展关系,算法的 "框架 "是按比例缩放的,以便于感知,从而让用户对解决方案的结构有一个舒适的理解。它美观、审美、优雅。当然,这是一个设计和用户友好度的问题。更确切地说,是对研究人员的友好性,因为用户更关心的是结果和效率。现在,让我们从另一个角度来看看解决方案。

2- 效率意味着简洁、最少的实体和包装器,以及尽可能短的数据访问时间。一切都应该很接近,这样就不会 "拉长",也不会将参数的 "负荷 "从一个封装器转移到另一个封装器。效率和部署、分层、优雅的风格与包装器之间 "游荡 "的参数 "小队 "是格格不入的,几乎是不协调的。

//-----------------------------------

鉴于这一矛盾,我建议采取一种折中方案。通过提高解决方案的简洁性来部分限制封装器数量的增长。原则上,对于OrderSend() 函数 来说,一个封装器就足够了。在极端情况下,则需要多个包装器。但在这种情况下,"研究人员 "很难理解解决方案,访问 "层 "也会变得更加复杂。因此,请尝试将压缩、高效的代码块与深受程序员喜爱的 OOP-jungle "藤蔓 "结合起来。你就会找到黄金分割点。

 
Artyom Trishkin:

...

还有一种更高级的方法--一切都将根据所需的设置以自动模式运行。然后是延迟请求--当 Expert Advisor 等待时机再次发送所需订单时,或只需知道在什么情况下需要发送请求即可。所有这些待处理的请求都将存放在它的储蓄罐中,等待合适的时机发送请求。

因此,彼得,现在下结论还为时过早。

您建议使用单一的交易功能,但我想根据每个人的需要提供多种方法。当然,不可能让每个人都满意。

将程序库分成不同级别,并允许用户对其进行操作是非常好的。但不要忘记,数量会毁了质量。用户获得的方法越多,他就越容易感到困惑。这一点要牢记。

除此之外,我同意。交易功能 的分层方法是件好事。