独立子窗口中的指标:大小与层级

到目前为止,我们仅讨论了在主图表窗口中运行的指标,即使用#property indicator_chart_window指令的指标。现在是时候研究位于价格图表下方独立子窗口中的指标。回顾一下,这类指标应使用 #property indicator_separate_window指令来声明。

我们之前所学的一切同样适用于子窗口指标,具体包括描述和绑定缓冲区、设置绘制类型和样式以及选择使用完整或简化形式的OnCalculate函数。但子窗口指标也有一些特性和额外设置。

由于子窗口有其自身的值刻度,MQL5 允许设置其最大值和最小值(用户也可以在指标设置对话框的Scale选项卡中设置类似限制)。这可以通过以下原型的 IndicatorSetDouble函数实现。

bool IndicatorSetDouble(ENUM_CUSTOMIND_PROPERTY_DOUBLE property, double value)

bool IndicatorSetDouble(ENUM_CUSTOMIND_PROPERTY_DOUBLE property, int modifier,
double value)

该函数用于为指标设置double特性值。需要两种形式的函数,因为某些特性可以是多个值,特别是水平层级(稍后会详细讨论)。可用特性已收集在 ENUM_CUSTOMIND_PROPERTY_DOUBLE 枚举中。

标识符

说明

INDICATOR_MINIMUM

纵轴最小值

INDICATOR_MAXIMUM

纵轴最大值

INDICATOR_LEVELVALUE

水平层级值(通过 modifier 参数设置数值)。

许多振荡指标(如 WPR)使用固定刻度范围。我们将通过示例演示本节涉及的所有函数(特性)。

该函数成功时返回 true,否则返回 false

正如我们所了解的,除了控制刻度范围以外,子窗口指标还可以设置水平层级。要设置水平层级的数量和特性,需使用另一个函数IndicatorSetInteger。用户可以在指标设置对话框的 Levels选项卡中执行类似操作。

bool IndicatorSetInteger(ENUM_CUSTOMIND_PROPERTY_INTEGER property, int value)

bool IndicatorSetInteger(ENUM_CUSTOMIND_PROPERTY_INTEGER property, int modifier,
int value)

该函数也有两种形式,支持为指标设置 int类型或等效类型(例如color 或枚举值)的特性值。可用特性已收集在 ENUM_CUSTOMIND_PROPERTY_INTEGER 枚举中。除了与水平层级相关的特性外,该枚举还包含对所有类型指标通用的 INDICATOR_DIGITS 特性,我们将在 下一节中介绍。

标识符

说明

INDICATOR_DIGITS

指标值的显示精度(小数点后的位数)

INDICATOR_HEIGHT

指标窗口的固定高度,以像素为单位(预处理命令 #property indicator_height

INDICATOR_LEVELS

指标窗口中水平层级的数量

INDICATOR_LEVELCOLOR

水平层级线条颜色(类型为 colormodifier 参数设置水平层级编号)

INDICATOR_LEVELSTYLE

水平层级线条风格(类型为 ENUM_LINE_STYLEmodifier参数设置水平层级编号)

INDICATOR_LEVELWIDTH

水平层级线条粗细 (1-5)(modifier参数设置水平层级编号)

水平层级可以是文本标签。要分配它们,使用 IndicatorSetString函数。

bool IndicatorSetString(ENUM_CUSTOMIND_PROPERTY_STRING property, string value)

bool IndicatorSetString(ENUM_CUSTOMIND_PROPERTY_STRING property, int modifier,
string value)

ENUM_CUSTOMIND_PROPERTY_STRING 包含字符串指标参数列表。需注意 INDICATOR_SHORTNAME 特性,它与水平层级无关:它是所有指标共有的特性,我们将在 下一节中介绍。

标识符

说明

INDICATOR_SHORTNAME

指标公开名称

INDICATOR_LEVELTEXT

水平层级说明(通过 modifier 指定数值)

所有 intdouble 数值类型的函数都通过特殊指令复制(下面是汇总表):

水平层级特性的 
指令

模拟函数

特性 
类型

说明

indicator_levelN

IndicatorSetDouble(
INDICATOR_LEVELVALUE,
N-1, value)

double

纵轴上第 N 个水平层级的数值

indicator_levelcolor

IndicatorSetInteger(
INDICATOR_LEVELCOLOR,
N-1, color)

color

水平层级颜色(不同编号的水平层级需通过函数设置不同颜色)

indicator_levelwidth

IndicatorSetInteger(
INDICATOR_LEVELWIDTH,
N-1, width)

int

水平层级线条粗细,以像素为单位(不同编号的水平层级需通过函数设置不同粗细)

indicator_levelstyle

IndicatorSetInteger(
INDICATOR_LEVELSTYLE,
N-1, style)

ENUM
_LINE
_STYLE

水平层级线条风格(不同编号的水平层级需通过函数设置不同风格)

indicator_minimum

IndicatorSetDouble(
INDICATOR_MINIMUM, minimum)

double

纵轴固定最小值,刻度下限

indicator_maximum

IndicatorSetDouble(
INDICATOR_MAXIMUM, maximum)

double

纵轴固定最大值,刻度上限

请注意,使用 #property指令时,特性实例(修饰符)的编号从 1 开始,而函数中使用的编号从 0 开始。

细心的读者会注意到,某些特性没有对应的指令。T它们包括 INDICATOR_LEVELTEXT、INDICATOR_SHORTNAME、INDICATOR_DIGITS。这些特性应根据输入变量以及指标所依附的图表,通过 MQL 代码动态填充。INDICATOR_LEVELS 可通过为水平层级指定多个指令间接设置。

最后,子窗口指标的标志是程序能够“冻结”其窗口的垂直大小。

子窗口大小的 
指令

模拟函数

说明

indicator_height

IndicatorSetInteger(
INDICATOR_HEIGHT, height)

指标子窗口的固定高度,以像素为单位(用户将无法更改高度)

固定子窗口高度通常仅用于包含通过 图形对象实现的控件(按钮、标志或输入字段)的控制面板。

遗憾的是,特性设置函数没有对应的反函数(IndicatorGetIntegerIndicatorGetDoubleIndicatorGetString)。除此之外,这还导致无法实现某些功能,例如,当用户修改了水平层级的数量和值时,无法获取这些信息。

作为使用固定刻度和水平层级的示例,我们来看看IndWPR.mq5指标。在该指标中,我们将使用标准的 WPR 算法:在指定的历史柱线(WPR 周期)内,找到价格的最高点 H 和最低点 L(即价格区间)。然后,计算当前价格 C 与最低点 L 的差值 C-L(或差值 -(H-C),带负号)与整个价格区间的比值,并将结果归一化到 0 至 -100 的范围内。下面是计算 WPR 的标准公式:

R% = (-(H – C) / (H – L)) * 100

让我们在源代码开头添加一些指令。除了将指标放置在单独窗口的特性外,我们还将值的范围从 0 设置为 -100。

#property indicator_separate_window
#property indicator_maximum    0.0
#property indicator_minimum    -100.0

一个缓冲区和一个折线图就足以存储数值并显示该指标。

#property indicator_buffers    1
#property indicator_plots      1
#property indicator_type1      DRAW_LINE
#property indicator_color1     clrDodgerBlue

在 WPR 指标中,通常会划分出两个水平层级:-20 和-80,分别作为超买区和超卖区的边界。我们来为它们创建一对水平线。

#property indicator_level1     -20.0
#property indicator_level2     -80.0
#property indicator_levelstyle STYLE_DOT
#property indicator_levelcolor clrSilver
#property indicator_levelwidth 1

唯一的输入变量允许你设置 WPR 的计算周期。

input int WPRPeriod = 14// Period

缓冲区数组在全局层面声明,并向 OnInit注册。

double WPRBuffer[];
   
void OnInit()
{
   // check for correct input
   if(WPRPeriod < 1)
   {
      Alert(StringFormat("Incorrect Period value (%d). Should be 1 or larger"
         WPRPeriod));
   }
   
   // binding array as buffer
   SetIndexBuffer(0WPRBuffer);
}

OnInit处理程序声明为void 类型,这隐含表示初始化成功。但是,如果周期设置小于 1,则无法进行计算,并会向用户发出警告。

为了简化 OnCalculate函数头,我们准备了适用于指标的头文件 IndCommon.mqh,其中包含两个宏,用于描述两种形式的事件处理程序的标准参数列表。

#define ON_CALCULATE_STD_FULL_PARAM_LIST \
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[]
   
#define ON_CALCULATE_STD_SHORT_PARAM_LIST \
const int rates_total,     \
const int prev_calculated, \
const int begin,           \
const double &data[]

现在,我们可以该指标和其他指标中使用简洁的 OnCalculate定义(前提是我们接受宏中提议的参数名称)。

#include <MQL5Book/IndCommon.mqh>
 
int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
{
   if(rates_total < WPRPeriod || WPRPeriod < 1return 0;
   ...
   return rates_total;
}

OnCalculate的开始部分,我们会检查是否可以使用当前的 WPRPeriodrates_total 值进行计算。如果数据不足或周期过短,函数将返回 0,这会使指标窗口保持空白。

接下来,我们会用空值填充前几个无法计算给定周期 WPR 的柱线。

int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
{
   ...
   if(prev_calculated == 0)
   {
      ArrayFill(WPRBuffer0WPRPeriod - 1EMPTY_VALUE);
   }
   ...
}

最后,我们执行 WPR 计算并将结果存入缓冲区。注意,每笔分时报价都会更新最后一根柱线:这通过从 prev_calculated - 1开始循环实现。

int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
{
   ...
   for(int i = fmax(prev_calculated - 1WPRPeriod - 1);
      i < rates_total && !IsStopped(); i++)
   {
      double max_high = high[fmax(ArrayMaximum(highi - WPRPeriod + 1WPRPeriod), 0)];
      double min_low = low[fmax(ArrayMinimum(lowi - WPRPeriod + 1WPRPeriod), 0)];
      if(max_high != min_low)
      {
         WPRBuffer[i] = -(max_high - close[i]) * 100 / (max_high - min_low);
      }
      else
      {
         WPRBuffer[i] = WPRBuffer[i - 1];
      }
   }
   return rates_total;
}

ArrayMaximumArrayMinimum函数允许搜索最高点 high 和最低点 low 的索引。

该指标会显示在一个独立窗口中,如下所示。

WPR 指标

WPR 指标

在接下来的章节中,我们将继续完善该指标,逐步添加其他常用特性。