English Русский Español Deutsch 日本語 Português
preview
从基础到中级:指标(三)

从基础到中级:指标(三)

MetaTrader 5示例 |
14 2
CODE X
CODE X

在上一篇文章“从基础到中级:指标(二)”中,我们学到了很多,因为我们演示了如何以非常简单、实用和功能齐全的方式实现移动平均线。然而,所展示的内容只能被视为对 MQL5 编程世界的简要介绍,因为该材料非常基础、简单和直接。但我们还可以做得更多。

所以,请努力理解这里提出的概念。不要只是复制代码,也不要认为如果你做不到,其他人也做不到。理解概念比理解代码本身更重要,因为代码可能会根据编写者而变化,而概念将始终保持不变。我们将从一些非常简单的东西开始,因为如果我们突然应用某些功能,您将看到的内容可能会变得非常复杂。


一个指标和多种图形表现形式

你可能已经见过有人编写程序,甚至尝试创建一个系统或运营方案,以便在金融市场中赚钱。这些系统中经常会使用各种移动平均线,而且在很多情况下,这些移动平均线是以相反的顺序应用的。此外,还有一些通道系统,例如著名的布林带,它基于两条移动平均线:一条代表上轨,另一条代表下轨。但我们在这里不是来解释如何将这样的系统用于市场交易的。我们的目标是展示如何以简单实用的方式完成类似的事情。

以布林带为例(但也可能是其他任何东西),我们有两条线(有时是三条),其中一条代表通道的内部平均值。但我们会保持简单,只使用两条线,这样更容易理解如何实现任意数量的线。首先,我们将使用以下代码。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buffer[];
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
15. 
16.    return INIT_SUCCEEDED;
17. };
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
20. {
21.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
22.        gl_buffer[c] = High[c];
23. 
24.    return rates_total;
25. };
26. //+------------------------------------------------------------------+

代码 01

我认为没有必要展示这段代码运行并放置在图表上会产生什么效果,因为前两篇文章已经详细解释过了。我们只需要查看事件处理函数(在本例中为 OnCalculate 函数)中执行的操作即可。您可以看到,这里我们明确地使用了最大值。换句话说,我们将有一条图形表示线,沿着每个柱的高点,因为我们没有进行任何平滑计算。是的,移动平均计算的目的正是平滑将在屏幕上以图形方式显示的曲线,在计算中使用的多个值之间创建平均值。但这需要解释一些数学知识,而这超出了本文的范围。

好的,代码 01 中显示的这个指标运行完美。请注意,我们使用的是 OnCalculate 函数的扩展版本。现在,原因就显而易见了。现在,我有一个问题:我们如何在图表中添加第二条图形表示线?比如,目标是显示每个柱形的最低价。你可能会说,只需创建一个新的指标,并将代码的第 22 行更改为使用 Low 而不是 High 就足够了。你说得对。这个想法一点也没有错 —— 它可以奏效。

然而,这会使事情变得复杂,因为你最终会在图表上看到两个单独的指标。一个小问题:每个指标只能处理一条图形表示线。但是,我们可以做得更好 —— 将这两个指标合并成一个代码。本主题的目标是演示如何做到这一点。我知道这乍一看可能很复杂,但如果你理解了所展示的内容,你就能创建任何具有多条图形表示线的指标。记住,目前,我们正在寻找最简单的方法来做到这一点。以后,如果你愿意,我们可以探讨更高级的形式。

因此,我们将修改代码 01 以创建第二条图形表示线。我们将一步一步地做这件事,这样每个人都可以跟随并了解它是如何发生的。

首先要做的是创建一个新的缓冲区来保存新的图形表示数据。请注意,我们使用的 DRAW_LINE 类型需要为每条图形表示线信息分配一个缓冲区。因此,我们将拥有以下内容:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buff_max[],
11.          gl_buff_min[];
12. //+------------------------------------------------------------------+
                   .
                   .
                   .

代码 02

代码 02 展示了我们即将实现的部分内容,我们将分步实现 —— 这样会更容易。请注意,我们已更改第 10 行中的缓冲区名称,并在第 11 行中添加了一个新的缓冲区名称。这样,我们就开始实现第二条图形表示线了。我们将更快地进行下一步,以确保一切都正确完成。之后,我们将得到以下代码:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buff_max[],
11.          gl_buff_min[];
12. //+------------------------------------------------------------------+
13. int OnInit()
14. {
15.    SetIndexBuffer(0, gl_buff_max, INDICATOR_DATA);
16.    SetIndexBuffer(1, gl_buff_min, INDICATOR_DATA);
17. 
18.    return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
22. {
23.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
24.    {
25.       gl_buff_max[c] = High[c];
26.       gl_buff_min[c] = Low[c];
27.    }
28. 
29.    return rates_total;
30. };
31. //+------------------------------------------------------------------+

代码 03

好了,这是主要部分。但是,这段代码还有一个小问题需要解决。但首先,我们需要了解这个问题是什么以及为什么会出现。请注意,在第 16 行,我们创建了一个新索引。MetaTrader 5 需要维护和监控的指标中使用的每个缓冲区都需要一个唯一的索引。一旦定义了第 16 行,就可以像第 26 行所示那样使用缓冲区。

该缓冲区用于图形表示系统,可以提供各种功能。目前,我们将只使用旨在包含用于表示图表上某些元素的数据的类型。好的,代码 03 可以编译。然而,将其应用于图表时,结果如下:

图 01

“但是这里发生了什么?我不明白为什么我们只能看到最小值对应的图形表示线?”原因是,我们告诉 MetaTrader 5,该指标只包含一个图形表示缓冲区及其信息。您可以在第 07 行和第 08 行看到这一点。由于我们实际上有两个缓冲区和两条线,因此我们只需要更改第 07 行和第 08 行中提到的信息。在这种情况下,让我们按如下方式修改代码:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         2
08. #property indicator_plots           2
09. //+----------------+
10. double   gl_buff_max[],
11.          gl_buff_min[];
12. //+------------------------------------------------------------------+
                   .
                   .
                   .

代码 04

请注意,我们只修改了上面提到的几行代码。但是,当将该指标应用于图表时,结果如下:

图 02

“还是这样?我现在真的不明白了。代码难道不应该以图形方式显示两条线吗?为什么我只能看到一条?”亲爱的读者们,请冷静。要有耐心,不用担心。你只能看到一条线的原因是第二条线没有设置颜色。为了更清楚地说明这一点,请查看该指标的配置方式:

动画 01

请注意,第二条线没有颜色。即使尝试为其指定颜色值,它也不会显示出来。要解决这个问题,你需要指示编译器预留并创建第二条线。为此,您需要再次修改代码,如下所示:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrRed
06. //+----------------+
07. #property indicator_type2           DRAW_LINE
08. #property indicator_color2          clrGreen
09. //+----------------+
10. #property indicator_buffers         2
11. #property indicator_plots           2
12. //+----------------+
13. double   gl_buff_max[],
14.          gl_buff_min[];
15. //+------------------------------------------------------------------+
                   .
                   .
                   .

代码 05

让我们来看看代码 05 中添加了什么内容。为了增强其视觉效果,我们使用了不同的颜色。因此,当您将该指标放置在图表上时,您可以看到以下内容:

图 03

看到了吧,在同一个指标中实现不同的图形表示线是多么容易?你只需要逐步添加元素,并且要清楚自己在做什么。由于我们这里不进行任何计算,所以这些线永远不会相交。这与我们实现区带指标大致相同。区别在于,一条线会从一个区带接收值,另一条线会从另一个区带接收值,从而形成类似于著名的布林带的东西。这是一个非常有趣的话题,不是吗?

我相信,仅凭这些知识,你现在就能创建不同类型和风格的指标。但是,在我们深入探讨这个话题之前 —— 因为编程的这一部分非常有趣 —— 你难道不想修改图 03 中的指标,使线具有颜色吗?“看起来很难。”但真的很难吗?还是你仍然不知道该如何进行?让我们来看看。为此,我们将转向一个新的话题。


图形表示中的彩色线

最能吸引人们注意力并让他们兴奋的一点是带有颜色变化的线条的指标。到目前为止,我们创建了一个非常简单的系统,其中只能使用一种颜色或另一种颜色 —— 但是如何切换颜色呢?“实现起来似乎非常复杂,特别是对于像我这样刚开始学习 MQL5 编程的人来说。”我明白了,亲爱的读者。但即便您刚刚起步,你也可能对这里展示的内容感兴趣。不,创建用多种颜色图形化表示的线形指标并不难 —— 只是有点不同而已。

为了让您了解我们所讨论的内容,让我们以上一主题中实现的代码为例。要显示图 03 的结果,它应该包含与下面我们看到的内容非常类似的内容:

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#property indicator_type1           DRAW_LINE
#property indicator_color1          clrRed
//+----------------+
#property indicator_type2           DRAW_LINE
#property indicator_color2          clrGreen
//+----------------+
#property indicator_buffers         2
#property indicator_plots           2
//+----------------+
double   gl_buff_max[],
         gl_buff_min[];
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, gl_buff_max, INDICATOR_DATA);
   SetIndexBuffer(1, gl_buff_min, INDICATOR_DATA);

   return INIT_SUCCEEDED;
};
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
{
   for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
   {
      gl_buff_max[c] = High[c];
      gl_buff_min[c] = Low[c];
   }

   return rates_total;
};
//+------------------------------------------------------------------+

代码 06

代码 06 是对代码 01 进行所有修改后的结果,使我们能够在图表上绘制两条线。现在,要使这些线呈现多种颜色,我们只需按照以下步骤操作即可。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_COLOR_LINE
05. #property indicator_color1          clrMaroon, clrTomato, clrBlue
06. //+----------------+
07. #property indicator_type2           DRAW_COLOR_LINE
08. #property indicator_color2          clrGreen, clrPaleGreen
09. //+----------------+
                   .
                   .
                   .

代码 07

现在,让我们来比较一下代码 06 和代码 07。请注意,我们将图形表示中使用的对象类型从 DRAW_LINE 更改为 DRAW_COLOR_LINE。之后,我们可以为每条线的颜色属性指定多种颜色。颜色数量完全是任意的,根据 MQL5 文档,最多可以有 64 个颜色值。事实上,我们在一条线中声明了三种颜色,而在另一条线中声明了两种颜色。不过,您可以使用任何您喜欢的颜色。但这还不够,我们需要对 MetaTrader 5 进行一些额外的配置,才能让它理解我们想要做的事情。

现在,请注意。使用多色图形系统时,我们需要一个额外的缓冲区,如下图所示,该缓冲区可在文档中找到。

图 04

如您所见,所有这些突出显示的点都需要颜色缓冲区。如果没有这个缓冲区,MetaTrader 5将不知道使用哪种颜色。因此,我们需要做的下一个更改是在缓冲区声明中。这一点可以从下面的代码片段中看出:

                   .
                   .
                   .
09. //+----------------+
10. #property indicator_buffers         4
11. #property indicator_plots           2
12. //+----------------+
13. double   gl_buff_max[],
14.          gl_buff_min[],
15.          gl_color_max[],
16.          gl_color_min[];
17. //+------------------------------------------------------------------+
                   .
                   .
                   .

代码 08

在代码片段 08 中,我们实际定义了缓冲区。请注意,在第 10 行,我们指定需要 4 个缓冲区。但如果我们只打算绘制两条线,为什么正好需要 4 个缓冲区呢?原因是我们需要一个缓冲区来存储表示的值,还需要另一个缓冲区来存储每种颜色。想象一个简单的双数值结构,虽然 MetaTrader 5 没有使用这种结构,但我们必须这样理解。并且由于 MetaTrader 5 没有使用结构进行此类建模,我们最终需要两个缓冲区。而由于我们需要绘制两条线,则最终需要 4 个缓冲区。它们从第 30 行开始声明,如代码片段 08 所示。

好的,现在只剩最后一步了。我们在下面的代码片段中演示了这一点:

                   .
                   .
                   .
17. //+------------------------------------------------------------------+
18. int OnInit()
19. {
20.     SetIndexBuffer(0, gl_buff_max, INDICATOR_DATA);
21.     SetIndexBuffer(1, gl_color_max, INDICATOR_COLOR_INDEX);
22.     SetIndexBuffer(2, gl_buff_min, INDICATOR_DATA);
23.     SetIndexBuffer(3, gl_color_min, INDICATOR_COLOR_INDEX);
24. 
25.     return INIT_SUCCEEDED;
26. };
27. //+------------------------------------------------------------------+
28. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
29. {
30.     double  mhs = 0,
31.             mhi = 0,
32.             ml = 0;
33.     const double desv = 0.01;
34. 
35.     for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
36.     {
37.         gl_color_max[c] = (mhi > High[c] ? 0 : (mhs < High[c] ? 1 : 2));
38.         mhs = mhi = gl_buff_max[c] = High[c];
39.         mhs += (mhs * desv);
40.         mhi -= (mhi * desv);
41.         gl_color_min[c] = (ml < Low[c] ? 0 : 1);
42.         ml = gl_buff_min[c] = Low[c];
43.     }
44. 
45.     return rates_total;
46. };
47. //+------------------------------------------------------------------+

代码 09

在解释代码片段 09 中我们正在做什么之前,让我们先来看看将此指标应用于任何工具的图表时的结果。如下图所示:

图 05

我们所看到的仅用于演示目的,因为该指标纯粹是出于教育原因而创建的。然而,很明显,指标初始化阶段的代码几乎没有变化 —— 只添加了第 21 行和第 23 行。但是,了解每行新代码的作用至关重要。首先,请注意每个缓冲区都有自己的标识符。无论我们打算在其中存储什么内容,每个缓冲区都必须有自己的标识符,否则 MetaTrader 5 在使用它们时会感到困惑。

在这种情况下,按特定顺序声明所有内容非常重要。也就是说,在声明下一个数据缓冲区之前,我们必须声明将在给定图形表示线中使用的所有数据缓冲区。在这里,包括颜色缓冲区。如果我们以不合逻辑的顺序声明数据或颜色缓冲区,您将无法达到预期的结果。因此,在此处声明元素时要格外小心,声明的顺序会影响最终结果。初始化完成后,我们可以专注于处理由 MetaTrader 5 触发的 Calculate 事件的过程。

请注意,在这种情况下,我们会进行一些计算和检查。这是因为我们需要告诉 MetaTrader 5 在屏幕上绘制线形时将使用代码 07 中定义的哪些颜色。“但是等等,我不明白这是怎么做到的。这里只有数字,而代码 07 中有颜色值。MetaTrader 5 如何知道使用哪种颜色呢?”

亲爱的读者朋友们,在这个阶段,重要的是要理解另一篇文章中已经解释过的内容:数组是怎样的。代码 07 中包含的是颜色数组。每种颜色都将位于数组中的特定索引位置。因此,在第 21 行和第 23 行中,我们使用枚举 INDICATOR_COLOR_INDEX。我们不是指定要使用哪种颜色,而是指定颜色在特定数组中的索引。但是是哪个数组呢?如果代码 07 中有两个数组,MetaTrader 5 如何知道使用哪个数组?

这就引出了最近讨论过的另一个话题。换句话说,组件的顺序会影响最终结果。由于我们通常以单一形式(即一个指标,一条线)使用这种图形表示,MetaTrader 5 将使用与所表示类型的标识符直接关联的配色方案。我知道这可能看起来令人困惑,但请注意,在第 04 行和第 05 行中,我们有一个标识符值 —— 在本例中,是数字 1。在第 07 行和第 08 行中,我们还有另一个标识符 —— 数字 2。因此,当我们把一个值链接到要以图形方式表示的指标时,编译器将使用此标识符来知道要应用哪种配色方案。

为了证明这一点,我们在代码 07 中的每个声明中定义了不同数量的颜色,这样当你开始练习真正的代码时,你就能掌握这一点。

虽然这部分有点令人困惑 —— 正是因为我们静态地使用所有内容,并且同时放置多个图形表示线 —— 但在实践中,你会发现理解如何声明和使用这些元素其实非常简单。

总之,当用户想要更改配色方案时,他们将访问类似这样的界面:

图 06

请注意,由于代码 07 的第 05 行和第 08 行中的声明,用户可以配置多种颜色。因此,建议界面尽可能清晰简单。否则,即使代码对于某些类型的操作可能非常有用,用户使用它的体验也会很差。

这些代码将在附录中提供,以便您研究如何处理此类情况。但在结束今天的文章之前,我想讨论另一个介于这两个概念之间的指标,具体取决于我们如何应用它 —— 它可以是区带指标,甚至是移动平均线交叉指标。但是,为了避免混淆,我们将另开一个主题来探讨这个问题。


DRAW_FILLING 指标

这个指标很有意思,因为根据它的实现方式,它可以产生移动平均线交叉指标,甚至是区带指标。然而,新手程序员常常会觉得它令人困惑,正是因为它的声明方式比较特殊。为了帮助您理解声明和使用这种类型的指标有多简单,我们将以本文开头提供的代码 06 作为起点。从那里,将更容易掌握我们尝试用 DRAW_FILING 指标实现的目标。

以代码 06 为起点,对其进行修改,以了解 DRAW_FILLING 指标。从本质上讲,代码 06 看起来是这样的:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_FILLING
05. #property indicator_color1          clrRed, clrGreen
06. //+----------------+
07. #property indicator_buffers         2
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buff_max[],
11.          gl_buff_min[];
12. //+------------------------------------------------------------------+
13. int OnInit()
14. {
15.     SetIndexBuffer(0, gl_buff_max, INDICATOR_DATA);
16.     SetIndexBuffer(1, gl_buff_min, INDICATOR_DATA);
17. 
18.     return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
22. {
23.     for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
24.     {
25.         gl_buff_max[c] = Open[c];
26.         gl_buff_min[c] = Close[c];
27.     }
28. 
29.     return rates_total;
30. };
31. //+------------------------------------------------------------------+

代码 10

“有趣,据我所知,代码 06 几乎没有发生过任何变化。但我对某些方面仍有疑问,例如:为什么在处理计算事件的过程中要使用开盘价和收盘价?这样做是为了触发最大值和最小值的反转或交叉,如代码 10 中所述。亲爱的读者们请注意:我们有时会有卖出柱形,有时也会有买入柱形。

现在想象一下,这个 DRAW_FILLING 指标就像一块双面印花的布料。当第 15 行和第 16 行定义的缓冲区中的值交叉时,就会发生反转,导致 MetaTrader 5 显示织物的一侧或另一侧。将在织物上显示或印刷的信息在第 05 行声明。因此,我们可以用一种或另一种颜色对该区域进行着色。如果没有交叉或反转,我们将只看到第 05 行中定义的一种印花或颜色。

然而,我们必须考虑一个小细节:尽管代码 10 极其简单,但当应用于图表时,它看起来如下图所示:

图 07

它看起来不太吸引人,事实上,根据计算和图形表示的信息类型,它可能会非常令人困惑。但是,在第 05 行,用户可以配置一些数据,如下图所示:

图 08

也就是说,根据计划的工作类型,这种 DRAW_FILLING 类型的指标可能非常有趣。然而,正是由于图 07 中所示的情况,该指标通常不是直接显示在主图表上,而是显示在单独的窗口中。然后,有人可能会开始想:“我的朋友,事情变得太复杂了。我才刚学会怎么创建彩色指标,现在你又要说把它放到另一个窗口里?你提供的信息量太大了,我得努力学习才行。”

别担心,这并不像看起来那么可怕。事实上,专为 MetaTrader 5 编程设计的 MQL5 提供了许多有趣且实用的功能。

其中包括将指标放置在单独的窗口中的功能。但是,这个窗口仍然会与主图表关联。实现这一目标的最简单和最简单的方法之一是指示编译器生成指标代码,这样当应用于图表时,MetaTrader 5 就会明白它需要打开一个新窗口或子窗口。这是通过在指标代码中添加一行代码来实现的。因此,前面提到的代码 10 将如下所示:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_separate_window
05. //+----------------+
06. #property indicator_type1           DRAW_FILLING
07. #property indicator_color1          clrRed, clrGreen
08. //+----------------+
09. #property indicator_buffers         2
10. #property indicator_plots           1
11. //+----------------+
12. double   gl_buff_max[],
13.          gl_buff_min[];
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {
17.     SetIndexBuffer(0, gl_buff_max, INDICATOR_DATA);
18.     SetIndexBuffer(1, gl_buff_min, INDICATOR_DATA);
19. 
20.     return INIT_SUCCEEDED;
21. };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
24. {
25.     for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
26.     {
27.         gl_buff_max[c] = Open[c];
28.         gl_buff_min[c] = Close[c];
29.     }
30. 
31.     return rates_total;
32. };
33. //+------------------------------------------------------------------+

代码 11

请注意,代码 10 和代码 11 之间的唯一区别在于第 04 行,代码 10 中没有该行,但代码 11 中有该行。编译完这段代码后,你可以让 MetaTrader 5 将其添加到图表中。因此,之前可能令人困惑的图 07 现在看起来会是这样:

图 09

图 09 当然要简单得多,尽管在解释此图表时,它最初可能会让一些用户感到困惑。


总结性思考

在本文中,我们展示了如何使用两种不同类型的指标,这两种指标可以以各种方式进行调整。目标是创建一个适合自己需求的应用程序。尽管材料看似简单,但我相信,凭借创造力和好奇心,人们可以理解这些内容可以扩展到其他类型的指标:DRAW_SECTION, DRAW_HISTOGRAM, DRAW_ARROW, DRAW_ZIGZAG, DRAW_BARS, 和 DRAW_CANDLES。它们的功能都与我们在本文中看到的类似,尽管我们只介绍了两种 —— 或者三种,如果算上 DRAW_COLOR_LINE 类型的话。但这只是个小细节。

真正重要的是,你要练习并理解图表上显示的所有内容,无论它看起来多么复杂,其构造都非常简单。本质上,您只需要使用完全静态的实现来指示编译器如何创建指标。创建静态指标很简单,甚至很有趣,但真正的乐趣在于动态指标。我们将很快对此进行探讨。与此同时,您可以研究和应用我们在本文中看到的内容,因为我们很快就会使这种实现更加令人兴奋和有趣。

本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/15828

附加的文件 |
Anexo.zip (3.03 KB)
最近评论 | 前往讨论 (2)
Nguyen Tuấn Anh
Nguyen Tuấn Anh | 11 2月 2026 在 15:25
正试图在 iBand 的基础上创建自定义指标。
经过大量搜索,我决定在这里发帖,也许有人可以帮助我。
这是完整的指标
//这是 MQL5\Indicators\Anhnt\iBand_Display.mq5
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots   3

//--- plot Middle
#property indicator_label1  "Middle"
#property indicator_type1   DRAW_LINE

//--- plot Upper
#property indicator_label2  "Upper"
#property indicator_type2   DRAW_LINE

//--- plot Lower
#property indicator_label3  "Lower"
#property indicator_type3   DRAW_LINE

//==================================================
// 输入参数
//==================================================
input int      InpBBPeriod      = 14;
input ENUM_APPLIED_PRICE inp_Applied_Price   = PRICE_MEDIAN;
input double   InpBBDeviation   = 2.0;
input int      InpBBShift       = 0;

input color    InpMiddleColor   = clrYellow;
input color    InpUpperColor    = clrYellow;
input color    InpLowerColor    = clrYellow;

input int      InpMiddleWidth   = 2;
input int      InpUpperWidth    = 2;
input int      InpLowerWidth    = 2;

input bool inp_BB_Show_Upper   = true;
input bool inp_BB_Show_Middle  = true;
input bool inp_BB_Show_Lower   = true;

input ENUM_LINE_STYLE InpMiddleStyle = STYLE_DOT;
input ENUM_LINE_STYLE InpUpperStyle  = STYLE_DOT;
input ENUM_LINE_STYLE InpLowerStyle  = STYLE_DOT;
//==================================================
#include <Anhnt/Configuration/NamingConfiguration.mqh>
//==================================================
// 指示器缓冲区
//==================================================
double MiddleBuffer[];
double UpperBuffer[];
double LowerBuffer[];

//==================================================
// 全局变量
//==================================================
int g_bb_handle = INVALID_HANDLE;

//https://www.mql5.com/zh/docs/indicators/ibands
//--- 我们将保留布林线指标中的数值数量

//+------------------------------------------------------------------+
int OnInit()
{
   //==================================================
   // 设置缓冲区
   //==================================================
   SetIndexBuffer(BASE_LINE,  MiddleBuffer, INDICATOR_DATA);
   SetIndexBuffer(UPPER_BAND, UpperBuffer,  INDICATOR_DATA);
   SetIndexBuffer(LOWER_BAND, LowerBuffer,  INDICATOR_DATA);

   ArraySetAsSeries(MiddleBuffer, true);
   ArraySetAsSeries(UpperBuffer,  true);
   ArraySetAsSeries(LowerBuffer,  true);   

   //================================================== 
   // 将 INPUT 值应用于绘图(运行时安全方法)
   //==================================================
   PlotIndexSetInteger(BASE_LINE,  PLOT_LINE_COLOR, InpMiddleColor);
   PlotIndexSetInteger(UPPER_BAND, PLOT_LINE_COLOR, InpUpperColor);
   PlotIndexSetInteger(LOWER_BAND, PLOT_LINE_COLOR, InpLowerColor);

   PlotIndexSetInteger(BASE_LINE,  PLOT_LINE_STYLE, InpMiddleStyle);
   PlotIndexSetInteger(UPPER_BAND, PLOT_LINE_STYLE, InpUpperStyle);
   PlotIndexSetInteger(LOWER_BAND, PLOT_LINE_STYLE, InpLowerStyle);

   PlotIndexSetInteger(BASE_LINE,  PLOT_LINE_WIDTH, InpMiddleWidth);
   PlotIndexSetInteger(UPPER_BAND, PLOT_LINE_WIDTH, InpUpperWidth);
   PlotIndexSetInteger(LOWER_BAND, PLOT_LINE_WIDTH, InpLowerWidth);   

   PlotIndexSetInteger(
         BASE_LINE,
         PLOT_DRAW_TYPE,
         inp_BB_Show_Middle ? DRAW_LINE : DRAW_NONE
   );

   PlotIndexSetInteger(
         UPPER_BAND,
         PLOT_DRAW_TYPE,
         inp_BB_Show_Upper ? DRAW_LINE : DRAW_NONE
   );

   PlotIndexSetInteger(
         LOWER_BAND,
         PLOT_DRAW_TYPE,
         inp_BB_Show_Lower ? DRAW_LINE : DRAW_NONE
   );
   string name = SMT_PREFIX + SMT_BB_NAME +
                 "(" + (string)InpBBPeriod + "," +
                 DoubleToString(InpBBDeviation, 1) + ")";  
   
                                   
   IndicatorSetString(INDICATOR_SHORTNAME, name);
   //==================================================
   // 创建 iBands 句柄
   //==================================================
   g_bb_handle = iBands(
      _Symbol,
      _Period,
      InpBBPeriod,
      InpBBShift,
      InpBBDeviation,
      inp_Applied_Price
   );
   if(g_bb_handle == INVALID_HANDLE)
   {
      Print("iBand_Display INIT FAILED. Unable to create iBands handle. GetLastError = ", GetLastError());
      return INIT_FAILED;
   }
      
   Print("iBand_Display INIT SUCCESS");
   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(
   const int rates_total,
   const int prev_calculated,
   const datetime &time[],
   const double &open[],
   const double &high[],
   const double &low[],
   const double &close[],
   const long &tick_volume[],
   const long &volume[],
   const int &spread[]
)
{
   //https://www.mql5.com/zh/docs/indicators/ibands&amp;nbsp;  
   //--- 等待 iBands 准备就绪
   int calculated = BarsCalculated(g_bb_handle);
   if(calculated <= InpBBPeriod)
      return 0;   
      // Print("DEBUG from OnCalculate After Waiting BarsCalculated | Symbol=", _Symbol、
      // " | Period=", _Period、
      // " | BarsCalculated(iBands)=", calculated); 
   //--- 该代码块在指标最初连接到图表时执行
   if(prev_calculated == 0)
   {
      ArrayInitialize(MiddleBuffer, EMPTY_VALUE);
      ArrayInitialize(UpperBuffer,  EMPTY_VALUE);
      ArrayInitialize(LowerBuffer,  EMPTY_VALUE);

      int to_copy = MathMin(calculated, rates_total);

      // 一次性复制所有可用数据
      CopyBuffer(g_bb_handle, BASE_LINE,  0, to_copy, MiddleBuffer);
      CopyBuffer(g_bb_handle, UPPER_BAND, 0, to_copy, UpperBuffer);
      CopyBuffer(g_bb_handle, LOWER_BAND, 0, to_copy, LowerBuffer);
      // Print("DEBUG from OnCalculate First Initial | Symbol=", _Symbol、
      // " | Period=", _Period、
      // " | BarsCalculated(iBands)=", calculated);
      return rates_total;    
   }   
   //--- 每打开一个新的条形图块,都会执行该代码块
   if(prev_calculated != rates_total && prev_calculated != 0)
   {
      //==================================================
   // NEXT RUNS:
   // 只更新最新的条形图(索引 0)。
   // 不要手动移动数组(序列会处理)。
   //==================================================
      double tmp[1];
      if(CopyBuffer(g_bb_handle, BASE_LINE, 0, 1, tmp) > 0)
      MiddleBuffer[0] = tmp[0];

      if(CopyBuffer(g_bb_handle, UPPER_BAND, 0, 1, tmp) > 0)
         UpperBuffer[0] = tmp[0];

      if(CopyBuffer(g_bb_handle, LOWER_BAND, 0, 1, tmp) > 0)
         LowerBuffer[0] = tmp[0];
      return rates_total;      
   }  
   return rates_total;
}
//+------------------------------------------------------------------+

bool FillArraysFromBuffers(
   double &base_values  [],   // 中部缓冲区
   double &upper_values [],   // 上层缓冲区
   double &lower_values [],   // LowerBuffer
   int shift,                 // shift = 0 → realtime
   int ind_handle,
   int amount
)
{
   // NOTE:
   // 目前未使用。
   // 按计划保留用于未来的辅助/EA 逻辑。

   ResetLastError();
   if(CopyBuffer(ind_handle, BASE_LINE,  -shift, amount, base_values) < 0)
      return false;
   if(CopyBuffer(ind_handle, UPPER_BAND, -shift, amount, upper_values) < 0)
      return false;
   if(CopyBuffer(ind_handle, LOWER_BAND, -shift, amount, lower_values) < 0)
      return false;
   return true;
}
非常简单,我只想让用户能够显示或隐藏单条线,如 BASE_LINE、UPPER_BAND、LOWER_BAND 和自定义颜色。
问题 1 是:
您可以在附件图片中看到,我不知道为什么我的图表下有绿色。
CODE X
CODE X | 12 2月 2026 在 11:11
Nguyen Tuấn Anh # :
Caros.
我想在 iBand 的基础上制作一个个性化的指示器。
经过大量搜索,我决定在这里发布,希望有人能帮我。
这是完整的指示器。 非常简单,我们只希望允许用户显示或查看单独的线条,如 BASE_LINE、UPPER_BAND、LOWER_BAND 和个性化的边框。
问题 1 是:
正如您在附件图像中看到的那样,我不知道在屏幕下方有一块绿色区域。
这些绿线是音量指示器。要禁用此功能,必须打开图表属性并关闭成交量指示器。或者干脆将成交量颜色设置为 CL_NONE,这样这些绿线就会消失。
交易策略 交易策略
各种交易策略的分类都是任意的,下面这种分类强调从交易的基本概念上分类。
计算机视觉在MQL5中的集成(第一部分):构建基础函数 计算机视觉在MQL5中的集成(第一部分):构建基础函数
基于计算机视觉与深度学习的欧元兑美元(EURUSD)汇率预测系统。探索卷积神经网络(CNN)如何识别外汇市场中的复杂价格形态,并实现最高达54%的汇率波动预测准确率。本文将分享一种突破传统技术指标的算法设计方法 —— 通过人工智能(AI)技术对K线图进行可视化分析。作者演示了将价格数据转换为“图像”的过程、神经网络的处理流程,以及通过激活热力图和注意力热图窥视AI“思维”的独特机会。通过基于MetaTrader 5库的Python实践代码,读者可完整复现系统并将其应用于自身的交易中。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
算法交易策略:人工智能(AI)铸就的“点金”之路 算法交易策略:人工智能(AI)铸就的“点金”之路
本文展示了利用机器学习创建黄金交易策略的一种方法。通过多角度考量所提出的金融时间序列分析与预测方法,相较于单纯依赖此类分析构建交易系统的其他方法,我们能够明确该方法的优势和劣势。