股票量化软件:构建自动运行的 EA(第 05 部分):手工触发器(II)

概述

在上一篇题为赫兹量化软件的文章中,我已经展示了如何经由编程来发送市价订单,并用功能键和鼠标的组合下挂单。

在上一篇文章的末尾,我建议允许手工操作 EA 是合适的,至少在一段时间内。 事实证明,这比预期的要有趣得多,因为最初的想法是发表 3-4 篇文章,展示如何开始开发可以真正自动交易的 EA。 虽然这对程序员来说相当容易,但对于刚开始学习编程的初学者来说可能很困难,因为很少有材料可以清楚地解释如何实际针对某些东西编程。 甚至,局限于一个知识水平,并非每个人都应该擅长所有的事情。

由于许多人可能利用这个社区中发表的文章来开始学习编程,我认为这是一个分享我基于多年 C/C++ 编程经验的机会,并展示如何在 MQL5 中实现一些与 C/C++ 非常相似的东西。 我想展示,编程并不神秘,它都是很实在的。

好吧,为了令我们的 EA 在手工模式下的操作更加舒适,我们需要做一些事情。 这项工作对于程序员来说既简单又容易,因此我们可以直奔主题。 也就是说,我们将创建水平线,指示我们发送到交易服务器的订单限价位置。

当我们用鼠标下订单时,即当我们创建挂单时,这些限价更适合观察。 一旦订单已经在服务器上,指示就由 MetaTrader 5 平台管理。 但在实际发生这种情况之前,我们需要向用户显示最有可能放置订单限价的位置。 这是由我们程序员完成的。 我们从 MetaTrader 5 获得的唯一支持是在图表上使用水平线的可能性。 除此之外,所有工作都必须通过 EA 编程实现。

为此,我们简单地编写将这些指示线放置在图表正确位置的代码。 但我们不想以某种随机的方式做到这一点。 这应该受到相应的控制,因为我们不想破坏我们已经创建的代码,并且我们不想增加工作量,以防将来必须从 EA 中删除 C_Mouse 类和 OnChartEvent 事件处理程序。 这是因为自动 EA 不需要这些东西,但手工 EA 需要它们。 我们要确保这些东西的最低限度可用性。 

创建 C_Terminal 类

为此目的,我们将依手工操作创建一切便利。 我们需要添加指示线,表示将要发送的订单或持仓的可能限价。 在此过程中,我们将删除 C_Orders 和 C_Mouse 类中的重复代码。 故此,我们将有一个新的类:C_Terminal 类,它将帮助我们构建和隔离一些东西,从而令我们可以更舒适地工作。 通过使用此类,我们就能够在将来构建自动和手工 EA,而不会冒着在新 EA 中产生某种灾难性故障的风险。

最大的问题是,在构建新的自动化 EA 时,许多人经常从头开始。 这种方式通常会导致许多错误,因为没有足够的检查。

诚然,将这些类转换为私有库会非常有趣。 但由于我们在模式上的意图不同,我们现在不会考虑它。 或许,我将来会这样做。 我们看看实际上我们要做什么。 我们将从以下内容开始:如往常一样,我们创建一个名为 C_Terminal.mqh 的头文件。 这是最基本的代码,它始终存在于我们将要创建的每个类当中。 它如下所示:

class C_Terminal
{
        private :
        public  :
};

始终在类中初始化代码,以这种方式,您就永远不会忘记某些关键点必须在私密部分中,而其它要点可以放在公开部分之中。 即使您在类里没有任何私密内容,澄清事情总是一个好主意。 主要是因为您可以向其他人展示您的代码。

分隔良好、且编写精良的代码,即易于阅读的代码,肯定会吸引其他人的兴趣,并鼓励他们在您需要帮助解决问题时对其进行分析。 杂乱无章、没有任何组织、没有分栏、且没有解释性注释的代码则变得无趣。 即便这个想法很好,也没有人喜欢花时间组织别人的代码,以便理解您在让它干什么。

这是我的建议。 当然,我自己的代码也并不完美,但是:始终清理您的代码,每当您需要在一个过程中嵌套多行时都使用分栏,这很有帮助。 不仅仅是其他人,主要是您。 有时代码组织得非常糟糕,甚至它的创建者事后也无法弄明白。 其他程序员将如何做到这一点?

那好,我们开始编写代码,先为我们的类添加结构。 初期的代码行如下所示。

class C_Terminal
{
        protected:
//+------------------------------------------------------------------+
                struct stTerminal
                {
                        ENUM_SYMBOL_CHART_MODE ChartMode;
                        int     nDigits;
                        double  VolMinimal,
                                VolStep,
                                PointPerTick,
                                ValuePerPoint,
                                AdjustToTrade;
                };
//+------------------------------------------------------------------+
        private :
        public  :
};

在此,我们有一个新东西 — 保留字 protected。 但它告诉我们什么? 我们通常只使用 private 和 public 声明。 那,这又是什么? 实际上,它介于公开和私密之间。 若要理解正在发生的事情,我们需要了解面向对象编程的一些基本概念。

其中一个概念是继承。 但在我们深入研究继承主题之前,我们以独立个人为例来研究该类。 故此,为了更好地理解这个概念,请将每个类视为一个单独的、独特的、排他性的生物。 现在我们可以继续解释。

有些信息是公开的,不光维护它的个人,任何人都可以从使用它、并获取知识中受益。 这些信息始终放置在代码的公开部分。 而其他信息是个人的私密数据,也就是说,只有此人才能访问。 当该个体不复存在时,这些信息将随他一起消亡;他是唯一可以从中受益的人。 将信息视为个人独门绝技。 这个人不能教授、或传授给其他人,也没有人可以把它从他那里夺走。 这种类型的信息位于代码的私密部分。

但也有一些信息不符合这些概念中的任何一个。 它位于受保护部分,如此个人可能会、也可能不会用到它。 最主要的是,它可以传给氏族后代成员。 为了理解这是如何发生的,我们来深入研究继承的主题。

当我们探讨遗传的话题时,最简单的理解方法是参考血统。 有三种类型的继承:公开继承、私密继承和受保护继承。 在此,我谈及的是继承,而不是氏族中每个成员的个别问题。

在公开继承中,父母的信息、数据和内容被传递给子女、及其所有后代,包括孙辈、及以后的后代,理论上,血统以外的任何人都可以访问这些东西。 请注意该短语,从理论上讲,因为这种转移存在一些细微差别。 稍后我们将更详细地介绍这一点。 我们先关注继承。 至于私密继承,只有第一代才能接触到这些信息,而后代即便他们也是血统当中的一脉,也无法获得这些信息。

最后一件我们要解释的事是受保护的继承。 它创造了一些与私密继承非常相似的东西。 但是我们有一个激进的因素,这让很多人难以理解这些概念:父辈条款。 这是因为,即使在公开继承的情况下,也有一种传递信息的规则。 有些东西在血统之外是无法访问的。 若要理解这一点,请参阅下表,其中我简要总结了这个问题:

父类中的定义 继承类型 从子类访问 由调用子类访问 
private public: 访问被拒绝 不可访问基类数据或过程
public: public: 访问被允许 可以访问基类数据或过程
protected public: 访问被允许 不可访问基类数据或过程
private private 访问被拒绝 不可访问基类数据或过程
public: private 访问被允许 不可访问基类数据或过程
protected private 访问被允许 不可访问基类数据或过程
private protected 访问被拒绝 不可访问基类数据或过程
public: protected 访问被允许  不可访问基类数据或过程
protected protected 访问被允许 不可访问基类数据或过程

表 1)基于信息定义的继承系统

请注意,根据继承期间数据类型定义中所用的代码部分,子类可能有权访问数据,也可能无法访问数据。 但是,任何血统之外的调用都将无法访问,除非父类的数据被宣布为公开,且子类以公开方式继承时才会发生的独特情况。 此外,无法访问氏族之外的任何信息。

如果不理解 表 01 所示的思维逻辑,许多缺乏经验的程序员难以遵循面向对象编程。 这是因为实际上他们不明白事情是如何运作的。 那些关注我的文章,和我的代码的人,应该已经注意到我经常使用面向对象编程。

这是因为它在实现非常复杂的事情时提供了最高级别的安全性,否则这是不可能做到的。 我说的不仅仅是继承。 除此之外,我们还有多态性和封装,但这些都是在另外的时间谈论的主题。 虽然封装是表 01 的一部分,但它值得更详细的解释,但其超出了本文的范畴。

那么,我们继续。 如果您仔细观察,就会注意到上面代码中的结构与 C_Orders 类中的结构相同。 请注意这一点,因为 C_Order 类将丢失此数据的定义,并开始从 C_Terminal 类继承数据。 但现在,我们先留在 C_Terminal 类中。

接下来要添加到 C_Terminal 类的是函数,其对于 C_Mouse 类和 C_Orders 类两者通用。 这些函数将被添加到 C_Terminal 类的受保护部分,因此当 C_Mouse 和 C_Orders 类继承自 C_Terminal 时,这些函数和过程将遵循表 01。 如此,我们添加以下代码:

//+------------------------------------------------------------------+
inline double AdjustPrice(const double value)
                        {
                                return MathRound(value / m_TerminalInfo.PointPerTick) * m_TerminalInfo.PointPerTick;
                        }
//+------------------------------------------------------------------+
inline double FinanceToPoints(const double Finance, const uint Leverage)
                        {
                                double volume = m_TerminalInfo.VolMinimal + (m_TerminalInfo.VolStep * (Leverage - 1));
                                
                                return AdjustPrice(MathAbs(((Finance / volume) / m_TerminalInfo.AdjustToTrade)))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赫兹量化软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值