English Русский Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
一张图表上多个指标(第 04 部分):晋升为一款智能交易系统

一张图表上多个指标(第 04 部分):晋升为一款智能交易系统

MetaTrader 5交易 |
2 072 5
Daniel Jose
Daniel Jose

概述

在我之前的文章里,我已经解释了如何创建拥有多个子窗口的指标,在使用自定义指标时如此这般会变得很有趣。 这很容易做到。 但当我们尝试在智能交易系统中实现相同的功能时,事情会越加变得复杂,因为在自定义指标中我们没有可用的工具。 在这一点上,编程变得至关重要:能够编写正确的代码来创建子窗口至关重要。 尽管这项任务并非那么容易,但知道如何在 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

附加的文件 |
最近评论 | 前往讨论 (5)
Mateus Cerqueira Lopes
Mateus Cerqueira Lopes | 25 2月 2022 在 17:50
MetaQuotes:

新文章:一张图表上的多个指标(第 04 部分):从 EA 开始》已发布:

作者: Daniel Jose丹尼尔-何塞

你好,丹尼尔,我一直在关注你的文章,但第 03 部分呢?
Daniel Jose
Daniel Jose | 26 2月 2022 在 13:28
Mateus Lopes #:
你好,丹尼尔,我一直在关注你的文章,但第 03 部分怎么样了?

在申请发布时出了点小问题,但我已经授权发布第 03 部分,很快也会发布,这个问题更多是由我已经发送的文章数量造成的......目前还有 15 篇文章需要他们分析,所有这些文章都与该 EA 的开发有关,而且每一篇文章的内容都会变得越来越复杂 ....,但还是要感谢您关注这个系列......期待从第 05 条开始的大量新内容,从那时起,它真的会变得很有价值,因为它将成为一件大事,前几条只是向您介绍即将发生的事情......😁👍

CapeCoddah
CapeCoddah | 13 5月 2022 在 13:00

你好,丹尼尔、

我在使用多色指标时遇到了一些问题,我很喜欢您的文章,您可能知道解决方法。

我想创建一个不使用 #property 选项就能设置所有指标属性的函数,例如 #property indicator_color1 clrCrimson,clrWhite,clrLime

我在下面的测试程序中发现,如果包含#property indicator_color1 clrCrimson,clrWhite,clrLime 选项,程序就能正常运行,而如果将其注释掉,程序就不能正常运行。在这种情况下,程序似乎只绘制了部分数据点,好像使用了 "更高的 "时间框架,或者跳过了几个数据点。 我怀疑在指定多种颜色时,属性颜色指令设置了更多我没有识别的属性。

第二个问题是,我显然不了解使用多色曲线图的细节和要求。 我搜索了文档,但没有找到任何概述如何使用多色指标 的文章。 虽然第一个曲线图可以正确改变颜色,但第二个曲线图(绘制低点)并没有根据我的函数调用改变颜色。我使用 Mladen 的 Laguerre Adaptive Filter 指标确定,属性指标使用情节编号 2 而不是指标编号 3 才能正确显示。

如果能提供任何建议、参考资料或帮助,我将不胜感激。

卡普科达

Daniel Jose
Daniel Jose | 21 5月 2022 在 17:18
CapeCoddah 指示器 的文章。第一个图形的颜色变化是正确的,而第二个图形(追踪低点)并没有根据我的函数调用改变颜色。这个图形需要所有三个属性:type2、color2 和 width2 才能正常工作。我还感到困惑的是,为什么属性声明使用 2 而不是 3。我使用 Mladen 的 Laguerre Adaptive Filter 指标确定,属性指标使用图表编号 2 而不是指标编号 3 才能正确显示。

如果能提供任何建议、参考或帮助,将不胜感激。

真诚的,科达角

你有点困惑,我能理解......但所有的困惑都在于你没有真正关注代码中的细节。我先试着解释一下评论中的一些细节,然后再谈谈你的代码....。

第一点是,您甚至可以不使用#property indicator_colorN 而生成多色指标,但对用户甚至对您来说,这样更实用、更安全、更易于理解和修改代码,因为您只需进入属性修改其中的颜色即可,而对用户来说则更简单,因为他只需在 MT5 为指标创建的标准窗口中选择要修改的颜色并进行修改即可。当您使用PlotIndexSetInteger 命令生成颜色变化时,您已经在做正确的事情了,当我们不使用#property indicator_colorN 时就是这样,但当我们使用属性时,使用 PlotIndexSetInteger 命令设置其他颜色往往没有意义、这是因为维护起来会更复杂,用户也会更困惑,因为他可能并不真正理解颜色标准想要表达的意思,即使代码是你的,而且只有你一个人使用该指标,也没有太大意义,除非在极少数情况下,你创建了一个动态颜色模式。

现在谈谈第二点:在这种情况下,问题在于你将指标绘制的数量(2 条线为 2 个)与对象属性(本例中为线)混淆了,要绘制线,实际上至少需要声明 3 条信息,即TypeN、ColorN、WidthN,其中N 表示对象的编号。不要放弃......继续学习,很快你就会明白的😁👍

现在让我们看看你的代码...我不会告诉你具体的修复方法(如果我这样做就不好玩了...... .... 😁👍✌ ),我希望你注意以下事实,这很重要:

      Buf=Set_IndexBuffer5(Buf, dataHi, INDICATOR_DATA , DRAW_COLOR_LINE , 4 ,
"High" , clrYellow , clrWhite , clrDodgerBlue );
      Buf=Set_IndexBuffer5(Buf, dataHic, INDICATOR_COLOR_INDEX );
      Buf=Set_IndexBuffer5(Buf, dataLo, INDICATOR_DATA , DRAW_COLOR_LINE , 4 ,
"Low" , clrYellow , clrWhite , clrDodgerBlue );
      Buf=Set_IndexBuffer5(Buf, dataLoc, INDICATOR_COLOR_INDEX );

请注意,我在您的代码中标注了两点......现在让我们看看在图 .... 上播放时发生了什么。


看到了吗,只有一个标签与您在代码中声明的一样,只有 HIGH .... 和 LOW?!!她在哪里?!!这是您应该纠正的第一点,因为没有显示 LOW 标签这一事实表明,正在使用的颜色模式是在#property indicator_color2 中声明的,也就是说,您在这一点上失败了,如果您尝试删除实际创建 LOW 线的指标 2 的属性,即使保留代码的其余部分,HIGH 线也会绘制,但 LOW 线不会。......为什么?!!!因为事实上,您并没有定义绘制 LOW 线 需的信息,这是通过使用PlotIndexSetInteger 调用动态定义的......这看起来很奇怪......但事实就是这样 ....。

当您解决了这个问题,如果您真的想使用 PlotIndexSetInteger 动态声明行对象的数据,您就可以从场景中移除#property indicator_color 编译命令,因为必要的数据将被动态设置,但如果您不想这样做,也没关系....。

现在,我想让您看一下上面的图片,并将其与您在 #property indicator_color 中使用的颜色进行比较......仔细观察这些颜色 ....,如果您这样做,您会发现一些奇怪的地方 .... 我不会再说不要失去乐趣,但请尝试使用不同的颜色,不要重复任何一种颜色......当您重复这些颜色时,就会更难理解错误出在哪里...😁👍

最后一个细节:你认为他只绘制了某些点,而跳过了其他点,可能有两个原因:颜色模式与图表背景不一致,请尝试使用与图表背景一致的颜色;第二种,但我不认为是实际情况,可能是 OnCalcule 事件失败,您返回了 -1 或 i 的值,正确的做法是返回 rates_total,因此请在代码中修改这一点,以避免期货问题...

CapeCoddah
CapeCoddah | 26 5月 2022 在 13:24

你好,丹尼尔、

我很困惑,我以为绘图特性是通过 MQ4 等缓冲区规范定义的,而至少 DRAW_COLOR... 规范的绘图特性是通过顺序绘图标识符定义的。我尚未确定 DRAW_LINE 等是否也需要绘图规范。 此外,indicator_colorX 属性实际上有两个功能,首先是计算和设置颜色的数量,然后将每种指定的颜色设置到适当的数组位置。我在此附上两个文件,一个是 Color Test,它现在可以正常运行,不过还需要进一步完善;另一个是 MLADEN 的 MACD Original2_1,稍作修改。 Mladen 的程序很有意思,他定义了两个图,但只使用了一个颜色索引缓冲区。


感谢您的帮助

一张图表上的多个指标(第 05 部分):将 MetaTrader 5 转变为 RAD 系统(I) 一张图表上的多个指标(第 05 部分):将 MetaTrader 5 转变为 RAD 系统(I)
有很多人不知道如何编程,但他们很有创造力,亦有杰出的想法。 然而,由于缺乏编程知识,他们无法实现这些想法。 我们一起看看如何利用 MetaTrader 5 平台本身创建图表交易,就如同它是一个 IDE。
DoEasy 函数库中的图形(第九十八部分):移动扩展的标准图形对象的轴点 DoEasy 函数库中的图形(第九十八部分):移动扩展的标准图形对象的轴点
在本文中,我将继续扩展的标准图形对象的开发,创建移动复合图形对象轴点的功能,通过控制点来管理图形对象轴点坐标。
了解如何设计基于轨道线(Envelopes)的交易系统 了解如何设计基于轨道线(Envelopes)的交易系统
在本文中,我将与您分享一种如何进行波带交易的方法。 这一次,我们将研究轨道线(Envelopes),并将看到创建一款基于轨道线的策略是多么容易。
来自专业程序员的提示(第三部分):日志。 连接到 Seq 日志收集和分析系统 来自专业程序员的提示(第三部分):日志。 连接到 Seq 日志收集和分析系统
Logger 类的实现能够统一和结构化打印到智能系统栏的日志消息。 连接到 Seq 日志收集和分析系统。 在线监视日志消息。