可视化数据缺口(空元素)
在许多情况下,指标读数应仅显示在某些柱线上,其余柱线则保持原样(视觉上不出现额外的线条或标签)。例如,许多信号指标仅在出现买入或卖出建议的柱线上,才显示向上或向下箭头。但信号出现的频率通常很低。
使用 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(0, UpBuffer, INDICATOR_DATA);
SetIndexBuffer(1, DownBuffer, INDICATOR_DATA);
// up and down arrow character codes
PlotIndexSetInteger(0, PLOT_ARROW, 217);
PlotIndexSetInteger(1, PLOT_ARROW, 218);
// padding for arrows
PlotIndexSetInteger(0, PLOT_ARROW_SHIFT, -ArrowShift);
PlotIndexSetInteger(1, PLOT_ARROW_SHIFT, +ArrowShift);
// setting an empty value (can be omitted, since EMPTY_VALUE is the default)
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_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(UpBuffer, EMPTY_VALUE);
ArrayInitialize(DownBuffer, EMPTY_VALUE);
}
else
{
// on new bars we also clean the elements
for(int i = prev_calculated; i < rates_total; ++i)
{
UpBuffer[i] = EMPTY_VALUE;
DownBuffer[i] = EMPTY_VALUE;
}
}
...
|
在主循环中,逐根柱线执行以下操作:将 high和 low 价格与相邻柱线的同类型价格进行对比,并在发现当前价格在左右两侧各 FractalOrder 根柱线范围内构成极值的位置设置标记。
// view all or new bars that have bars in the FractalOrder environment
for(int i = fmax(prev_calculated - FractalOrder - 1, FractalOrder);
i < rates_total - FractalOrder; ++i)
{
// check if the upper price is higher than neighboring bars
UpBuffer[i] = high[i];
for(int j = 1; j <= 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 = 1; j <= 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(0, UpBuffer, INDICATOR_DATA);
SetIndexBuffer(1, DownBuffer, INDICATOR_DATA);
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
return FractalOrder > 0 ? INIT_SUCCEEDED : INIT_PARAMETERS_INCORRECT;
}
|
源代码其余部分保持不变。
下图展示了同时应用锯齿线及分形的图表,因此,你可以直观地比较它们的结果。这两个指标完全独立运行,但由于算法相同,所找到的极值是一样的。

基于分形的锯齿线指标
需要注意的是,如果同一类型的极值连续出现,锯齿线会采用第一个极值。这是由于分形被用作极值的必然结果。当然,这种情况在标准锯齿线中不会发生。如有必要,开发者可以通过先对分形序列进行细化处理,对算法进行优化。
还需注意,对于渲染 DRAW_ZIGZAG(以及 DRAW_SECTION),可见线段连接的是非空元素,因此严格来说,每条柱形上都会绘制部分线段片段,包括值为 EMPTY_VALUE(或其他指定替代值)的柱形。然而,在 Data Window中可以看到,空元素确实为空:这些元素未显示任何值。