将数组分配为缓冲区:SetIndexBuffer

在程序从启动到终止的整个运行周期内,任何 double类型 动态数组 均可充当指标缓冲区。定义这类数组的最常见方式是在全局层面声明。但在某些情况下,将数组设置为类成员,然后创建包含这些数组的全局对象会更加便捷。我们将在实现多货币对指标(请参阅 多货币和多时间范围指标章节的 IndUnityPercent.mq5示例)和增量成交量指标(请参阅 等待数据和管理可见性章节的 IndDeltaVolume.mq5示例)时探讨这种实现方式的示例。

因此,让我们在全局层面声明一个动态数组 buffer(无需指定大小)。

double buffer[];

可以通过终端中的特殊函数SetIndexBuffer将其注册为缓冲区。通常,该函数会与其他许多用于配置指标的函数一样,在 OnInit 处理程序中调用,我们将在后续章节中详细讨论。

bool SetIndexBuffer(int index, double buffer[],
ENUM_INDEXBUFFER_TYPE mode = INDICATOR_DATA)

该函数在 index指定的指标缓冲区与buffer 动态数组之间建立关联。index值必须介于 0 和 N-1 之间,其中 N 是由 #property indicator_buffers指令指定的缓冲区数量。

绑定操作完成后,数组尚未准备好处理数据,甚至不会改变其大小,因此初始化和所有计算都应在OnCalculate函数中执行。将动态数组分配为指标缓冲区后,不可再更改其大小。对于指标缓冲区,所有大小调整操作均由终端本身自动完成。

将数组与指标缓冲区关联后,索引方向默认与普通数组一致。必要时,可使用 ArraySetAsSeries 函数进行更改。

SetIndexBuffer函数成功时返回 true,失败时返回 false

可选mode参数用于告知系统该缓冲区的使用方式。可能的值在 ENUM_INDEXBUFFER_TYPE 枚举中提供。

标识符

说明

INDICATOR_DATA

要渲染的数据

INDICATOR_COLOR_INDEX

渲染颜色

INDICATOR_CALCULATIONS

中间计算的内部结果

默认情况下,指标缓冲区用于绘制数据 (INDICATOR_DATA)。该值除了在图表上显示数组外,还有另一作用:鼠标光标所在柱线对应的每个缓冲区值会显示在Data window中。不过,这种行为可以通过某些指标设置更改(请参阅 图形绘图设置 章节的 PLOT_SHOW_DATA 特性)。本章的大多数示例都采用 INDICATOR_DATA 模式。

如果指标计算需要存储每根柱线的中间结果,可以为它们分配一个辅助性的非显示缓冲区 (INDICATOR_CALCULATIONS)。相较于使用普通数组实现目的,这种方式更加便捷,因为使用普通数组时,程序员必须自行管理数组大小。本章将展示使用 INDICATOR_CALCULATIONS 的两个示例:IndTripleEMA.mq5(请参阅 跳过初始柱线的绘制)和 IndSubChartSimple.mq5(请参阅 多货币和多时间范围指标)。

某些结构允许为每根柱线设置显示颜色。颜色缓冲区 (INDICATOR_COLOR_INDEX) 用于存储颜色信息。颜色由整数类型 color表示,但所有指标缓冲区必须为 double 类型,在这种情况下,缓冲区将存储由开发者设置的特殊调色板中的颜色编号(请参阅 图表逐元素着色 章节以及其示例指标 IndColorWPR.mq5)。

颜色和辅助缓冲区的值不会显示在 Data window中,并且无法使用 CopyBuffer 函数获取这些值(我们将在 MQL5 内置指标与自定义指标使用 章节中详细探讨该函数)。

指标缓冲区在初始化时不会填充任何数值。如果由于某种原因未计算其某些元素(例如,指标设置中存在最大柱线数量限制,或者图形构建本身要求在重要元素之间保留间隔,如锯齿线顶点之间),则应显式将这些位置填充为特殊的“空值”。空值不会显示在图表上,也不会显示在Data Window中。默认情况下,它存在 EMPTY_VALUE (DBL_MAX) 常量,但如有必要,也可替换为其他值,例如 0。这可以通过 PlotIndexSetDouble 函数实现。

基于对 SetIndexBuffer函数的新认识,让我们完善上一节开始的示例IndReplica1.mq5。具体来说,我们需要 OnInit处理程序。

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
 
#include <MQL5Book/PRTF.mqh>
 
double buffer[]; // global dynamic array
 
int OnInit()
{
   // register an array as an indicator buffer
   PRTF(SetIndexBuffer(0buffer)); // true / ok
   // the second incorrect call is made here intentionally to show an error
   PRTF(SetIndexBuffer(1buffer)); // false / BUFFERS_WRONG_INDEX(4602)
   // check size: still 0
   PRTF(ArraySize(buffer)); // 0
   return INIT_SUCCEEDED;
}

由于通过指令定义的缓冲区数量为 1,因此为单个缓冲区分配数组时应使用索引 0(即SetIndexBuffer的第一个参数)。第二个函数调用是错误示例,仅用于演示问题:由于索引 1 对应两个已声明的缓冲区,这将产生 BUFFERS_WRONG_INDEX (4602) 错误。

OnCalculate函数的最开始,让我们再次打印输出数组的大小。此时,数组已根据柱线数量分配完毕。

int OnCalculate(const int rates_total
                const int prev_calculated
                const int begin
                const double &data[])
{
   // after starting, check that the platform automatically manages the size of the array
   if(prev_calculated == 0)
   {
      PRTF(ArraySize(buffer)); // 10189 - actual number of bars
   }
   ...

现在,让我们讨论指标的计算逻辑。如前所述,我们暂不加入复杂公式,仅尝试将data参数传入的时间序列复制到缓冲区。这也反映在指标的命名中。

   ...
   // on each new bar or set of bars (including the first calculation)
   if(prev_calculated != rates_total)
   {
      // fill in all new bars
      ArrayCopy(bufferdataprev_calculatedprev_calculated);
   }
   else // ticks on the current bar
   {
      // update the last bar
      buffer[rates_total - 1] = data[rates_total - 1];
   }
   
   // we report the number of processed bars to ourselves in the future
   return rates_total;
}

现在,该指标可以正常编译,无警告。我们可以在图表上运行该指标,在默认设置下,它应在缓冲区中复制柱线收盘价的值。这基于 OnCalculate的简化形式,我们在 主要指标事件:OnCalculate中探讨过这一方面。

但存在一个特殊现象:Data window中的缓冲区数值显示正确,但图表上却未出现对应线条。这是因为负责显示的是图形结构,而非缓冲区。在当前版本的指标中,我们仅配置了缓冲区。在下一章节中,我们将创建新版本的 IndReplica2.mq5并补充必要的指令。

同时,上述效果可用于创建“隐藏指标”,这类指标不在图表上显示线条,但可被其他 MQL 程序通过编程方式读取。如果需要,开发者甚至可以在 Data windows中隐藏指标缓冲区的显示(请参阅下一节的 PLOT_SHOW_DATA)。

关于如何通过 MQL5 代码管理指标,我们将在 下一章中讨论