可视化数据缺口(空元素)

在许多情况下,指标读数应仅显示在某些柱线上,其余柱线则保持原样(视觉上不出现额外的线条或标签)。例如,许多信号指标仅在出现买入或卖出建议的柱线上,才显示向上或向下箭头。但信号出现的频率通常很低。

使用 PlotIndexSetDouble函数可以设置空值,该空值既不会在图表上显示,也不会出现在 Data Window 中。

bool PlotIndexSetDouble(int index, ENUM_PLOT_PROPERTY_DOUBLE property, double value)

该函数用于为指定 index的绘图设置double 特性。此类特性的集合在 ENUM_PLOT_PROPERTY_DOUBLE 枚举中列出,但目前该枚举仅包含一个元素:PLOT_EMPTY_VALUE。它还设置空值。实际空值通过最后一个参数 value传递。

作为一个数值稀疏型指标的示例,我们将讨论分形检测器。该指标会在图表上标记满足以下条件的价格:高价 (High):高于左右两侧各 N 个相邻柱线的高点;低价 (Low):低于左右两侧各 N 个相邻柱线的低点。指标文件名为 IndFractals.mq5

该指标将包含两个缓冲区和两个 DRAW_ARROW 类型的绘图。

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
   
// rendering settings
#property indicator_type1   DRAW_ARROW
#property indicator_type2   DRAW_ARROW
#property indicator_color1  clrBlue
#property indicator_color2  clrRed
#property indicator_label1  "Fractal Up"
#property indicator_label2  "Fractal Down"
   
// indicator buffers
double UpBuffer[];
double DownBuffer[];

FractalOrder输入变量允许你设置用于确定上极值或下极值的相邻柱线数量。

input int FractalOrder = 3;

为了提高可见性,我们将让箭头符号与极值保持 10 像素的间距。

const int ArrowShift = 10;

OnInit函数中,声明数组作为缓冲器并将其绑定到绘图上。

int OnInit()
{
   // binding buffers
   SetIndexBuffer(0UpBufferINDICATOR_DATA);
   SetIndexBuffer(1DownBufferINDICATOR_DATA);
   
   // up and down arrow character codes
   PlotIndexSetInteger(0PLOT_ARROW217);
   PlotIndexSetInteger(1PLOT_ARROW218);
   
   // padding for arrows
   PlotIndexSetInteger(0PLOT_ARROW_SHIFT, -ArrowShift);
   PlotIndexSetInteger(1PLOT_ARROW_SHIFT, +ArrowShift);
   
   // setting an empty value (can be omitted, since EMPTY_VALUE is the default)
   PlotIndexSetDouble(0PLOT_EMPTY_VALUEEMPTY_VALUE);
   PlotIndexSetDouble(1PLOT_EMPTY_VALUEEMPTY_VALUE);
   
   return FractalOrder > 0 ? INIT_SUCCEEDED : INIT_PARAMETERS_INCORRECT;
}

请注意,默认的空值是特殊常量 EMPTY_VALUE,因此上述对 PlotIndexSetDouble的调用是可选的。

OnCalculate处理程序中,第一次调用时,使用 EMPTY_VALUE 初始化两个数组,随后随着柱线的形成,将该值赋给新的元素。这种填充是必要的,因为指标缓冲器分配的内存可能包含任意数据(垃圾数据)。

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[])
{
   if(prev_calculated == 0)
   {
      // at the start, fill the arrays entirely
      ArrayInitialize(UpBufferEMPTY_VALUE);
      ArrayInitialize(DownBufferEMPTY_VALUE);
   }
   else
   {
      // on new bars we also clean the elements
      for(int i = prev_calculatedi < rates_total; ++i)
      {
         UpBuffer[i] = EMPTY_VALUE;
         DownBuffer[i] = EMPTY_VALUE;
      }
   }
   ...

在主循环中,逐根柱线执行以下操作:将 highlow 价格与相邻柱线的同类型价格进行对比,并在发现当前价格在左右两侧各 FractalOrder 根柱线范围内构成极值的位置设置标记。

   // view all or new bars that have bars in the FractalOrder environment
   for(int i = fmax(prev_calculated - FractalOrder - 1FractalOrder);
       i < rates_total - FractalOrder; ++i)
   {
      // check if the upper price is higher than neighboring bars
      UpBuffer[i] = high[i];
      for(int j = 1j <= FractalOrder; ++j)
      {
         if(high[i] <= high[i + j] || high[i] <= high[i - j])
         {
            UpBuffer[i] = EMPTY_VALUE;
            break;
         }
      }
      
      // check if the lower price is lower than neighboring bars
      DownBuffer[i] = low[i];
      for(int j = 1j <= FractalOrder; ++j)
      {
         if(low[i] >= low[i + j] || low[i] >= low[i - j])
         {
            DownBuffer[i] = EMPTY_VALUE;
            break;
         }
      }
   }
   
   return rates_total;
}

让我们看看该指标在图表上的表现。

分形指标

分形指标

现在我们将绘图类型从 DRAW_ARROW 改为 DRAW_ZIGZAG,并比较这两个选项在空值情况下的效果。结果应是基于分形的锯齿线。脚本的修改版本附在文件 IndFractalsZigZag.mq5中。

主要变更之一与图表数量有关:现在只有一个图表,因为 DRAW_ZIGZAG 函数会“占用”两个缓冲器。

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   1
   
// rendering settings
#property indicator_type1   DRAW_ZIGZAG
#property indicator_color1  clrMediumOrchid
#property indicator_width1  2
#property indicator_label1  "ZigZag Up;ZigZag Down"
...

所有与设置箭头相关的函数调用已从 OnInit中移除。

int OnInit()
{
   SetIndexBuffer(0UpBufferINDICATOR_DATA);
   SetIndexBuffer(1DownBufferINDICATOR_DATA);
   
   PlotIndexSetDouble(0PLOT_EMPTY_VALUEEMPTY_VALUE);
   
   return FractalOrder > 0 ? INIT_SUCCEEDED : INIT_PARAMETERS_INCORRECT;
}

源代码其余部分保持不变。

下图展示了同时应用锯齿线及分形的图表,因此,你可以直观地比较它们的结果。这两个指标完全独立运行,但由于算法相同,所找到的极值是一样的。

基于分形的锯齿线指标

基于分形的锯齿线指标

需要注意的是,如果同一类型的极值连续出现,锯齿线会采用第一个极值。这是由于分形被用作极值的必然结果。当然,这种情况在标准锯齿线中不会发生。如有必要,开发者可以通过先对分形序列进行细化处理,对算法进行优化。

还需注意,对于渲染 DRAW_ZIGZAG(以及 DRAW_SECTION),可见线段连接的是非空元素,因此严格来说,每条柱形上都会绘制部分线段片段,包括值为 EMPTY_VALUE(或其他指定替代值)的柱形。然而,在 Data Window中可以看到,空元素确实为空:这些元素未显示任何值。