
一张图表上多个指标(第 04 部分):晋升为一款智能交易系统
概述
在我之前的文章里,我已经解释了如何创建拥有多个子窗口的指标,在使用自定义指标时如此这般会变得很有趣。 这很容易做到。 但当我们尝试在智能交易系统中实现相同的功能时,事情会越加变得复杂,因为在自定义指标中我们没有可用的工具。 在这一点上,编程变得至关重要:能够编写正确的代码来创建子窗口至关重要。 尽管这项任务并非那么容易,但知道如何在 EA 中设置子窗口并不需要很多编码,只需通晓 MQL5 的工作原理。
计划
我们已经有了自定义指标,也就是说,我们的对象类已经功能齐备,而且由于这是一个对象类,我们可以轻松地将其转换到其它模型。 然而,在我们的 EA 中简单地声明并尝试使用这个类,并不能让事情如同我们在自定义指标中一样工作,原因是我们的 EA 中未提供子窗口功能。 但又冒出了这样一个想法:“如果我们用一个已经编译过,且可工作的自定义指标,然后用 iCustom 命令从 EA 调用它,会怎么样? 好吧,这也许是可行的,因为这样不需要子窗口,命令如下所示:
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ input string user01 = ""; //Used indicators input string user02 = ""; //Assets to follow //+------------------------------------------------------------------+ int OnInit() { int m_handleSub; //... Expert Advisor code ... if ((m_handleSub = iCustom(NULL, 0, "Chart In SubWindows\\Chart In SubWindow.ex5", user01, user02)) == INVALID_HANDLE) return INIT_FAILED; if (!ChartIndicatorAdd(ChartID(), 0, m_handleSub)) return INIT_FAILED; //... Expert Advisor code ... ChartRedraw(); return(INIT_SUCCEEDED); } //...The rest of the Expert Advisor code ...
这个简单的代码片段能够加载我们的自定义指标,然而它还不能正常工作,因为我们没有子窗口。 在这种情况下,当代码在 EA 里执行时,EA 将直接在主窗口中应用我们的指标,这意味着图表会被指标加载的模板掩盖,这绝对不是我们想要的。
因此,我们真正的主要问题是创建一个可用的子窗口,以便我们可以使用已经功能齐备的指标。 但为何我们要为随后启动的指标创建一个子窗口呢? 这没有意义,最好直接往 EA 里添加功能,从而克服可能出现的任何限制。
有基于此,我们需要执行若干个任务:
任务 | 目的 |
---|---|
1 => 创建一个通用指标。 | 它允许在不污染图表的情况下创建和使用 iCustom 命令。 |
2 => 在 EA 里以某种方式包含该指标。 | 这令您能够毫无问题地将其转换为具有完整功能的智能交易系统。 |
3 => 针对子窗口生成通用对象类 | 允许经由 EA 添加子窗口 |
4 => 获取已绑定到 window 类的 C_TemplateChart 类。 | 这允许我们管理子窗口的内容,而无需更改功能齐备的代码中的任何内容。 |
虽然这看起来很难,但困难很简单就解决了。 那好,我们来逐点处理。
实现:创建通用指标
这一部分可以通过创建一个完全干净、但功能齐全的自定义指标代码来解决。 本例中的代码如下所示:
#property copyright "Daniel Jose" #property version "1.00" #property description "This file only enables support of indicators in SubWin." #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ int OnInit() { return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+
只有这些,没别的。 我们将此文件另存为 SubSupport.mq5。 但其所在与其它指标不同 — 我们将其移到智能交易系统的 RESOURCE 目录。 因此,文件结构如下图所示:
实现:在 EA 里包含通用指标
为此,我们需要在 EA 的顶部添加以下代码。
//+------------------------------------------------------------------+ #define def_Resource "Resources\\SubSupport.ex5" //+------------------------------------------------------------------+ #resource def_Resource //+------------------------------------------------------------------+
这将把通用指标的编译代码包含到我们的 EA 当中。 这步一旦完成,即可把通用指标的 .ex5 文件删除,因为不再需要它。 现在,您应该注意这样一个事实:如果在编译 EA 代码时未找到 SubSupport.ex5 文件,编译器将自动编译通用指标的代码 SubSupport. mq5,并将这个新编译的可执行文件添加到我们的智能交易系统之中。 那么,如果您曾经编辑过 SubSupport.mq5 文件,并且需要将更改添加到智能交易系统当中,您应该先删除已有的 SubSupport.ex5;否则,新的更改不会被添加。
这个细节很重要:有时候您真的需要知道如何将新实现的修改添加到资源当中。
好了,通用指标现在是智能交易系统的一部分了,如此我们进入下一个任务。
实现 : 创建一个子窗口对象类
这一部分也很简单。 在此,我们需要在编码之前定义一些要点,即:在这个类中,我们真正需要什么特性? 最初,我决定如下使用:
函数 | 说明 |
---|---|
Init | 允许经由 EA 添加子窗口 |
Close | 允许经由 EA 添加子窗口 |
这些函数不会被测试,所以我假设它们在 EA 的生存期内只会被调用一次。 但随着我们的 EA 增长,故考虑让它在未来更加实用是个好主意。 因此,我们来创建一个名为 C_Terminal 的新对象类 — 这个类将支持一些与图形终端相关的东西。 稍后我们将学习更多有关它的内容。 我们看看最后一项任务,因为部分解决方案无法实现。
实现: C_TemplateChart 类继承
当我决定运用 OOP(面向对象编程)创建一些新东西时(我这么做是因为我已经知道运用这种方式有很大的优势,包括安全性和继承性)。 还有多态性,我们将在稍后创建交叉订单系统时用到它。 在这种特殊情况下,我们将用到 OOP 的一项优势 — 继承。 C_TemplateChart 已经是一个功能齐全的类。 看到这一点,您就不想再为所有内容重新编程,或冒着风险往类中添加代码,因为这会阻碍该类在其它地方使用。 解决方案是运用继承,它允许添加新代码或函数,而不必改变原始代码。
运用继承有很多优势,包括以下几点:已经测试过的代码仍然保留测试过的状态;复杂度会随着代码量的增长而增加;只有新功能真正需要测试;只是继承不会改变原类,故可提供稳定性。 换言之,事情会以最小的代价得以改善,但却会带来最大的安全性。 为了理解这一点,我们来看下面的示意图。
祖父类是最基本类,在其中我们拥有的数据操作级别最低,但当父类从祖父类继承某些内容时,祖父类中声明为公开的所有内容都可以由父类查看和使用。 我们还可以向父类添加新的内容,这不会影响其继承和支持的内容。 如果父类已经完成并可工作,且我们希望在不更改类中任何内容的情况下对其进行扩展,那么我们应创建一个子类,它将拥有前类的所有功能。 我们还可以改变工作方式,这是关于继承的有趣之处,因为这些更改不会影响其它类。 然而,这与允许多重继承的 C++ 不同,此处有一个限制。 如果子类可以同时从父类和祖父类继承函数,那么在 MQL5 中这是不可能的。 但您仍然从遗产中受益。 多重继承的例子如下所示:
好的,但是在 MQL5 中如何实现呢? 如何声明继承,以便我们可以利用它的优势? 理解这一点最准确的方法是阅读面向对象编程(OOP)的内容,但在这里我们直奔主题。 继承将使用以下行完成:
#include "C_TemplateChart.mqh" //+------------------------------------------------------------------+ class C_SubWindow : public C_TemplateChart { // ... Class code };
可见 C_SubWindow 类公开继承自 C_TemplateChart 类,那么现在我们可以使用 C_SubWindow 类来访问 C_TemplateChart 类的功能。
在上面的代码片段中,我强调了一件事。 请注意,它通常在引号(")中,而非尖括号(< > )里。 那我为什么要这么做? 与 C++ 语言一样,MQL5 也有一些非常有趣的东西,但有些东西会令那些刚刚开始学习编程艺术的人感到困惑。 当我们将头文件放在尖括号(<>)之间时,我们指示的是一个绝对路径 — 在这种情况下,编译器将完全遵循我们指定的路径。 但是当我们使用引号时(就像我们这次所做的那样),编译器将采用相对路径,或者更清楚地说,它将首先从工作文件所在的当前目录开始。 这可能看起来很奇怪,但有时我们会用到相同名称但不同内容的文件,且它们位于不同的目录中,若我们仍然希望引用当前目录,那么我们就可以用引号来表示。
我们在前面计划使用的两个函数 INIT 和 CLOSE 如下所示:
//+------------------------------------------------------------------+ bool Init(void) { if (m_handleSub != INVALID_HANDLE) return true; if ((m_handleSub = iCustom(NULL, 0, "::" + def_Resource)) == INVALID_HANDLE) return false; m_IdSub = (int) ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL); if (!ChartIndicatorAdd(Terminal.Get_ID(), m_IdSub, m_handleSub)) return false; return true; } //+------------------------------------------------------------------+ void Close(void) { ClearTemplateChart(); if (m_handleSub == INVALID_HANDLE) return; IndicatorRelease(m_IdSub); ChartIndicatorDelete(Terminal.Get_ID(), m_IdSub, ChartIndicatorName(Terminal.Get_ID(), m_IdSub, 0)); ChartRedraw(); m_handleSub = INVALID_HANDLE; } //+------------------------------------------------------------------+
看,代码非常简短。 但有些事情我们必须小心,注意高亮显示的部分。 在添加此部件时,您必须小心不要出错,因为如果不保持原样,则我们将要添加到 EA 中的 SubSupport.ex5 可执行文件在 EA 内部不可见 — 而在 EA 外部则可见。 相关的更多详情,请阅读参考资料。 但基本上,如果您使用 ( :: ),这表明 EA 应该使用其内部提供的内部资源。 但如果我们只指示资源的名称,EA 将在 MQL5 目录中搜索它,如果指定位置不存在该文件,即使该文件被添加为 EA 资源,该函数也将失败。
然后,一旦加载资源后,我们检查存在的子窗口的数量,并向该子窗口添加一个指标。
代码的实际操作,如下所见:
input string user01 = ""; //Used indicators input string user02 = ""; //Assets to follows //+------------------------------------------------------------------+ int OnInit() { int m_handleSub; //... if ((m_handleSub = iCustom(NULL, 0, "Chart In SubWindows\\Chart In SubWindow.ex5", user01, user02)) == INVALID_HANDLE) return INIT_FAILED; if (!ChartIndicatorAdd(ChartID(), (int) ChartGetInteger(ChartID(), CHART_WINDOWS_TOTAL), m_handleSub)) return INIT_FAILED; //... ChartRedraw(); return(INIT_SUCCEEDED); } //...The rest of the Expert Advisor code ...
这两段代码的工作原理相同,但对象类版本将允许我们随着时间的推移添加更多内容,因为上面显示的版本是稳固版本,不会被更改。 两个版本都做了相同的事情:它们从 EA 创建一个子窗口,并将所有以前创建的自定义指标放在这个子窗口当中。 与本文开头的代码相比,请注意针对代码所做的更改 — 更改部分以彩色高亮显示。
结束语
我们如何决定实现目标的途径是非常有趣的。 有时我们会遇到困难,认为很难实现我们的目标;但只要有一点耐心和奉献精神,我们就可以克服起初似乎无法克服的障碍。 在本文中,我演示了如何通过继承来扩展类的功能,且无需对其进行修改。 与此同时,我将展示如何在图表中添加指标,以便它们能够像已经曾经测试过的那样工作。 我们在 EA 内部添加了 ex5 程序,并可调用它,而无需再通过 EA 来加载原始的 ex5。
附件包含了迄今为止开发的所有改进,但很快就会有更多有趣的东西出现在这段代码中。 😁👍
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/10241
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.



新文章:一张图表上的多个指标(第 04 部分):从 EA 开始》已发布:
作者: Daniel Jose丹尼尔-何塞
你好,丹尼尔,我一直在关注你的文章,但第 03 部分怎么样了?
在申请发布时出了点小问题,但我已经授权发布第 03 部分,很快也会发布,这个问题更多是由我已经发送的文章数量造成的......目前还有 15 篇文章需要他们分析,所有这些文章都与该 EA 的开发有关,而且每一篇文章的内容都会变得越来越复杂 ....,但还是要感谢您关注这个系列......期待从第 05 条开始的大量新内容,从那时起,它真的会变得很有价值,因为它将成为一件大事,前几条只是向您介绍即将发生的事情......😁👍
你好,丹尼尔、
我在使用多色指标时遇到了一些问题,我很喜欢您的文章,您可能知道解决方法。
我想创建一个不使用 #property 选项就能设置所有指标属性的函数,例如 #property indicator_color1 clrCrimson,clrWhite,clrLime
我在下面的测试程序中发现,如果包含#property indicator_color1 clrCrimson,clrWhite,clrLime 选项,程序就能正常运行,而如果将其注释掉,程序就不能正常运行。在这种情况下,程序似乎只绘制了部分数据点,好像使用了 "更高的 "时间框架,或者跳过了几个数据点。 我怀疑在指定多种颜色时,属性颜色指令设置了更多我没有识别的属性。
第二个问题是,我显然不了解使用多色曲线图的细节和要求。 我搜索了文档,但没有找到任何概述如何使用多色指标 的文章。 虽然第一个曲线图可以正确改变颜色,但第二个曲线图(绘制低点)并没有根据我的函数调用改变颜色。我使用 Mladen 的 Laguerre Adaptive Filter 指标确定,属性指标使用情节编号 2 而不是指标编号 3 才能正确显示。
如果能提供任何建议、参考资料或帮助,我将不胜感激。
卡普科达
如果能提供任何建议、参考或帮助,将不胜感激。
真诚的,科达角
你有点困惑,我能理解......但所有的困惑都在于你没有真正关注代码中的细节。我先试着解释一下评论中的一些细节,然后再谈谈你的代码....。
第一点是,您甚至可以不使用#property indicator_colorN 而生成多色指标,但对用户甚至对您来说,这样更实用、更安全、更易于理解和修改代码,因为您只需进入属性修改其中的颜色即可,而对用户来说则更简单,因为他只需在 MT5 为指标创建的标准窗口中选择要修改的颜色并进行修改即可。当您使用PlotIndexSetInteger 命令生成颜色变化时,您已经在做正确的事情了,当我们不使用#property indicator_colorN 时就是这样,但当我们使用属性时,使用 PlotIndexSetInteger 命令设置其他颜色往往没有意义、这是因为维护起来会更复杂,用户也会更困惑,因为他可能并不真正理解颜色标准想要表达的意思,即使代码是你的,而且只有你一个人使用该指标,也没有太大意义,除非在极少数情况下,你创建了一个动态颜色模式。
现在谈谈第二点:在这种情况下,问题在于你将指标绘制的数量(2 条线为 2 个)与对象属性(本例中为线)混淆了,要绘制线,实际上至少需要声明 3 条信息,即TypeN、ColorN、WidthN,其中N 表示对象的编号。不要放弃......继续学习,很快你就会明白的😁👍
现在让我们看看你的代码...我不会告诉你具体的修复方法(如果我这样做就不好玩了...... .... 😁👍✌ ),我希望你注意以下事实,这很重要:
请注意,我在您的代码中标注了两点......现在让我们看看在图 .... 上播放时发生了什么。
看到了吗,只有一个标签与您在代码中声明的一样,只有 HIGH .... 和 LOW?!!她在哪里?!!这是您应该纠正的第一点,因为没有显示 LOW 标签这一事实表明,正在使用的颜色模式是在#property indicator_color2 中声明的,也就是说,您在这一点上失败了,如果您尝试删除实际创建 LOW 线的指标 2 的属性,即使保留代码的其余部分,HIGH 线也会绘制,但 LOW 线不会。......为什么?!!!因为事实上,您并没有定义绘制 LOW 线所 需的信息,这是通过使用PlotIndexSetInteger 调用动态定义的......这看起来很奇怪......但事实就是这样 ....。
当您解决了这个问题,如果您真的想使用 PlotIndexSetInteger 动态声明行对象的数据,您就可以从场景中移除#property indicator_color 编译命令,因为必要的数据将被动态设置,但如果您不想这样做,也没关系....。
现在,我想让您看一下上面的图片,并将其与您在 #property indicator_color 中使用的颜色进行比较......仔细观察这些颜色 ....,如果您这样做,您会发现一些奇怪的地方 .... 我不会再说不要失去乐趣,但请尝试使用不同的颜色,不要重复任何一种颜色......当您重复这些颜色时,就会更难理解错误出在哪里...😁👍
最后一个细节:你认为他只绘制了某些点,而跳过了其他点,可能有两个原因:颜色模式与图表背景不一致,请尝试使用与图表背景一致的颜色;第二种,但我不认为是实际情况,可能是 OnCalcule 事件失败,您返回了 -1 或 i 的值,正确的做法是返回 rates_total,因此请在代码中修改这一点,以避免期货问题...
你好,丹尼尔、
我很困惑,我以为绘图特性是通过 MQ4 等缓冲区规范定义的,而至少 DRAW_COLOR... 规范的绘图特性是通过顺序绘图标识符定义的。我尚未确定 DRAW_LINE 等是否也需要绘图规范。 此外,indicator_colorX 属性实际上有两个功能,首先是计算和设置颜色的数量,然后将每种指定的颜色设置到适当的数组位置。我在此附上两个文件,一个是 Color Test,它现在可以正常运行,不过还需要进一步完善;另一个是 MLADEN 的 MACD Original2_1,稍作修改。 Mladen 的程序很有意思,他定义了两个图,但只使用了一个颜色索引缓冲区。
感谢您的帮助