English Русский Español Deutsch 日本語 Português
利用 CCanvas 类开发自定义指标

利用 CCanvas 类开发自定义指标

MetaTrader 5指标 | 24 七月 2017, 10:08
4 526 0
Alexander Fedosov
Alexander Fedosov

内容

概述

自定义指标是现代 MetaTrader 5 交易的组成部分。它们均用于自动交易系统 (作为算法的一部分) 和手工交易。迄今, 在开发指标时, 可以设置 绘图样式并应用 18 种图形绘图。但平台具有更广泛的图形功能。CCanvas 自定义图形 库能够开发自定义的具有无限视效能力的非标准指标。本文为读者介绍函数库功能, 并提供其应用的示例。并且还开发了一个单独的自定义指标函数库。


开发自定义图形基类

为了开发一个基类, 我们需要编写一组方法来创建任意自定义图形对象的基础, 并包括一组通用的属性。为达此目的, <数据文件夹>\MQL5\Include 目录的 CustomGUI 文件夹里创建 CanvasBase.mqh 文件。这个文件将包含 CCanvasBase 基类, 用于所有未来类型的自定义图形。

//+------------------------------------------------------------------+
//|                                                  CCanvasBase.mqh |
//|                                 版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include <Canvas\Canvas.mqh>
//+------------------------------------------------------------------+
//| 用于自定义图形开发的基类                                              |
//+------------------------------------------------------------------+
class CCanvasBase
  {
private:
   //--- 画布名称
   string            m_canvas_name;
   //--- 画布坐标
   int               m_x;
   int               m_y;
   //--- 画布尺寸
   int               m_x_size;
   int               m_y_size;
protected:
   CCanvas           m_canvas;
   //--- 创建对象的图形资源
   bool              CreateCanvas(void);
   //--- 删除图形资源
   bool              DeleteCanvas(void);
public:
                     CCanvasBase(void);
                    ~CCanvasBase(void);
   //--- 设置和获取坐标
   void              X(const int x)                         { m_x=x;                      }
   void              Y(const int y)                         { m_y=y;                      }
   int               X(void)                                { return(m_x);                }
   int               Y(void)                                { return(m_y);                }
   //--- 设置和获取尺寸
   void              XSize(const int x_size)                { m_x_size=x_size;            }
   void              YSize(const int y_size)                { m_y_size=y_size;            }
   int               XSize(void)                            { return(m_x_size);           }
   int               YSize(void)                            { return(m_y_size);           }
   //--- 在创建时设置指标名称
   void              Name(const string canvas_name) { m_canvas_name=canvas_name;  }
  };
//+------------------------------------------------------------------+
//| 构造器                                                            |
//+------------------------------------------------------------------+
CCanvasBase::CCanvasBase(void) : m_x(0),
                                 m_y(0),
                                 m_x_size(200),
                                 m_y_size(200)
  {
  }
//+------------------------------------------------------------------+
//| 析构器                                                            |
//+------------------------------------------------------------------+
CCanvasBase::~CCanvasBase(void)
  {
  }
//+------------------------------------------------------------------+
//| 为一个对象创建图形资源                                               |
//+------------------------------------------------------------------+
bool CCanvasBase::CreateCanvas(void)
  {
   ObjectDelete(0,m_canvas_name);
   if(!m_canvas.CreateBitmapLabel(m_canvas_name,m_x,m_y,m_x_size,m_y_size,COLOR_FORMAT_ARGB_NORMALIZE))
      return(false);
   ObjectSetInteger(0,m_canvas_name,OBJPROP_CORNER,CORNER_LEFT_UPPER);
   ObjectSetInteger(0,m_canvas_name,OBJPROP_ANCHOR,ANCHOR_CENTER);
   ObjectSetInteger(0,m_canvas_name,OBJPROP_BACK,false);
   return(true);
  }
//+------------------------------------------------------------------+
//| 删除图形资源                                                       |
//+------------------------------------------------------------------+
bool CCanvasBase::DeleteCanvas()
  {
   return(ObjectDelete(0,m_canvas_name)?true:false);
  }
//+------------------------------------------------------------------+

正如我们所见, 初始对象坐标, 名称和尺寸, 以及与其相辅相成的图形资源创建和删除方法, 形成了绘制自定义图形元素的基础 (画布)。此外, 在任何时候, 在任何图形对象的初始构造期间, 均要调用 CreateCanvas()DeleteCanvas() 方法。因此, 这些方法中使用的变量应被初始化, 如同在构造函数中一样。


CCircleSimple 类

我们用 "简单到复杂的基础" 来掌握开发自定义图形的理论。首先, 我们将开发一个简单的圆形指标, 拥有框架, 数值和描述的特征。图例. 1 展示了基础元素的结构。

  • 框架 (边界)。轮廓边缘。
  • 背景。文本元素所在的空间。
  • 数值。显示数值的文本元素。
  • 描述。指标说明文字(名称, 周期以及其它特色信息)。


图例. 1. 简单圆形指标的基本结构

我们在之前创建的 CustomGUI 里创建另一个文件夹, 并将其命名为 Indicator。此文件夹包含今后所有指标的类。创建第一个名为 CircleSimple.mqh 的文件。首先, 我们需要在图形对象的所有类型中包含以前创建的 CanvaseBase.mqh 文件, 并以 CCanvaseBase 类作为当前类的基础, 因此所有类方法在当前的 CCircleSimple 内均可使用。

//+------------------------------------------------------------------+
//|                                                 CircleSimple.mqh |
//|                                 版权所有 2017, Alexander Fedosov  |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| 带有数值和描述的圆形指标                                              |
//+------------------------------------------------------------------+
class CCircleSimple : public CCanvasBase

为了免于在 EA 和指标文件中手工输入所需的图形对象, 请在 CustomGUI 文件夹中创建 CustomGUI.mqh 文件。文件将包括 Indicators 文件夹下所有类的内容。因此, 我们只需要包含这个文件即可访问函数库中的所有类。现在, 让我们来看一看:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                 版权所有 2017, Alexander Fedosov  |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"

当实现简单圆形指标类时, 还需要仔细考虑一组属性和方法, 为这个看似简单的图形指标提供设置模式的能力。以下列表包含此集合:

//+------------------------------------------------------------------+
//| 带有数值和描述的圆形指标                                             |
//+------------------------------------------------------------------+
class CCircleSimple : public CCanvasBase
  {
private:
   //--- 背景颜色 
   color             m_bg_color;
   //--- 框架颜色
   color             m_border_color;
   //--- 文本颜色
   color             m_font_color;
   //--- 标签颜色
   color             m_label_color;
   //--- 透明度
   uchar             m_transparency;
   //--- 框架宽度
   int               m_border;
   //--- 指标尺寸
   int               m_radius;
   //--- 数值字号
   int               m_font_size;
   //--- 标签字号
   int               m_label_font_size;
   //---
   int               m_digits;
   //--- 标签
   string            m_label;
public:
                     CCircleSimple(void);
                    ~CCircleSimple(void);
   //--- 设置和获取背景颜色
   color             Color(void)                      { return(m_bg_color);            }
   void              Color(const color clr)           { m_bg_color=clr;                }
   //--- 设置和获取尺寸
   int               Radius(void)                     { return(m_radius);              }
   void              Radius(const int r)              { m_radius=r;                    }
   //--- 设置和获取数值字号
   int               FontSize(void)                   { return(m_font_size);           }
   void              FontSize(const int fontsize)     { m_font_size=fontsize;          }
   //--- 设置和获取标签字号
   int               LabelSize(void)                  { return(m_label_font_size);    }
   void              LabelSize(const int fontsize)    { m_label_font_size=fontsize;   }
   //--- 设置和获取数值字体颜色
   color             FontColor(void)                  { return(m_font_color);          }
   void              FontColor(const color fontcolor) { m_font_color=fontcolor;        }
   //--- 设置和获取标签字体颜色
   color             LabelColor(void)                 { return(m_label_color);         }
   void              LabelColor(const color fontcolor){ m_label_color=fontcolor;       }
   //--- 设置框架颜色和宽度
   void              BorderColor(const color clr)     { m_border_color=clr;            }
   void              BorderSize(const int border)     { m_border=border;               }
   //--- 设置和获取透明度
   uchar             Alpha(void)                      { return(m_transparency);        }
   void              Alpha(const uchar alpha)         { m_transparency=alpha;          }
   //--- 设置和获取标签值
   string            Label(void)                      { return(m_label);               }
   void              Label(const string label)        { m_label=label;                 }
   //--- 创建指标
   void              Create(string name,int x,int y);
   //--- 删除指标
   void              Delete(string name);
   //--- 设置和获取指标值
   void              NewValue(int value);
   void              NewValue(double value);
  };

从描述中能够看出它们所用变量和方法的目的。我们来深入研究呈现在图例 .1 中这种形式的指标绘图的实现。我们要研究的第一种方法是 CreateCanvas()。正如我们所见, 它只有三个参数。我发现它们是最重要的。提供额外的参数是多余的, 因为这令方法的实现变得复杂。因此, 所有其它属性都划分到单独的方法之中。接下来, 所有变量都会在类构造函数中初始化:

//+------------------------------------------------------------------+
//| 构造器                                                            |
//+------------------------------------------------------------------+
CCircleSimple::CCircleSimple(void) : m_bg_color(clrAliceBlue),
                                     m_border_color(clrRoyalBlue),
                                     m_font_color(clrBlack),
                                     m_label_color(clrBlack),
                                     m_transparency(255),
                                     m_border(5),
                                     m_radius(40),
                                     m_font_size(17),
                                     m_label_font_size(20),
                                     m_digits(2),
                                     m_label("label")
  {
  }

当您创建此类型的指标, 这其实很方便, 您仅需使用 CreateCanvas () 创建一个类的实例。在创建之前, 您可以只指定打算修改的属性。当使用 CCanvas 函数库构建复杂的图形对象时, 请记住, 方法是按层次, 顺序进行基元绘制。这与在实际的画布上作画有很多共同点。首先, 艺术家通常画一个背景, 然后他们描摹对象, 随后才是细节, 等等。 

//+------------------------------------------------------------------+
//| 创建指标                                                          |
//+------------------------------------------------------------------+
void CCircleSimple::Create(string name,int x,int y)
  {
   int r=m_radius;
//--- 相对于半径修改指标位置
   x=(x<r)?r:x;
   y=(y<r)?r:y;
   Name(name);
   X(x);
   Y(y);
   XSize(2*r+1);
   YSize(2*r+1);
   if(!CreateCanvas())
      Print("错误。未能创建指标。");
   if(m_border>0)
      m_canvas.FillCircle(r,r,r,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(r,r,r-m_border,ColorToARGB(m_bg_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_font_size);
   m_canvas.TextOut(r,r,"0",ColorToARGB(m_font_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

我们不会纠缠于方法的实现细节。我们仅强调一些基础知识:

  • 首先, 调整图形资源相对于实际指标大小的位置, 令其不能超出主图表。
  • 之后, 使用方法设置名称, 大小和坐标。基本上创建自 CCanvasBase 基类。
  • 当实现框架时, 我们使用了上述的叠加原理。特别是, 我们创建了两个圆: 第一个 (已填充), 第二个圆的半径小于第一个圆的框架宽度。
  • 在这些对象之上创建数值和描述元素。

接下来, 我们来研究进行指标数值实时更新并显示的NewValue() 方法:

//+------------------------------------------------------------------+
//| 设置并更新指标数值                                                  |
//+------------------------------------------------------------------+
void CCircleSimple::NewValue(int value)
  {
   int r=m_radius;
   m_canvas.FillCircle(r,r,r-m_border,ColorToARGB(m_bg_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_font_size);
   m_canvas.TextOut(r,r,IntegerToString(value),ColorToARGB(m_font_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

为了使指标的数值正确地更新, 我们需要按照创建它们时的相同顺序重新绘制三个对象 (背景和文本元素)。因此, 背景将在 NewValue() 方法中重新绘制, 随后是数值和描述文本元素

现在, 是时候在 MetaTrader 5 终端中检验圆形指标的实现了。为此, 让我们创建一个空指标, 其中包含 CustomGUI.mqh 文件, 并创建两个 CCircleSimple 类的实例:

//+------------------------------------------------------------------+
//|                                              CustomIndicator.mq5 |
//|                                   版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/zh/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CCircleSimple ind1,ind2;

然后, 在指标初始化中, 设置圆形指标中获取数值所要用到的标准指标:

//---
int InpInd_Handle,InpInd_Handle1;
double adx[],rsi[];
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                                 |
//+------------------------------------------------------------------+
int OnInit()
  {

//---- 获取指标句柄
   InpInd_Handle=iADX(Symbol(),PERIOD_CURRENT,10);
   InpInd_Handle1=iRSI(Symbol(),PERIOD_CURRENT,10,PRICE_CLOSE);
   if(InpInd_Handle==INVALID_HANDLE)
     {
      Print("获取指标句柄失败");
      return(INIT_FAILED);
     }
   ArraySetAsSeries(adx,true);
   ArraySetAsSeries(rsi,true);

出于描绘目的, 我们来定义一些圆形指标的属性并创建它们:

//---
   ind1.Radius(60);
   ind1.Label("ADX");
   ind1.Color(clrWhiteSmoke);
   ind1.LabelColor(clrBlueViolet);
   ind1.Create("adx",100,100);
//---
   ind2.Radius(55);
   ind2.BorderSize(8);
   ind2.FontSize(22);
   ind2.LabelColor(clrCrimson);
   ind2.BorderColor(clrFireBrick);
   ind2.Label("RSI");
   ind2.Create("rsi",250,100);

现在, 我们只需要在指标的计算部分 为所创建的指标输入标准指标的当前数值:

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(CopyBuffer(InpInd_Handle,0,0,2,adx)<=0 || 
      CopyBuffer(InpInd_Handle1,0,0,2,rsi)<=0
      )
      return(0);
   ind1.NewValue(adx[0]);
   ind2.NewValue(rsi[0]);
//--- 返回 prev_calculated 的数值以便下次调用
   return(rates_total);
  }

剩下的唯一需要做的就是编写逆初始化中删除图形资源的方法, 以便在删除指标时正确删除图形对象。

//+------------------------------------------------------------------+
//| 自定义指标的逆初始化函数                                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind1.Delete();
   ind2.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

结果体现在图例. 2 中。正如我们所见, 这些指标的许多参数彼此间明显不同。

图例. 2. 圆形指标示例

CCircleArc 指标类

我们已经研究了一个用于实现圆形指标的最简单形式的类。我们来把这个任务稍微变得复杂, 并在数字之外创建一个附加的图形显示元素 – 弧形。在图例. 3 中提供了带有弧形指示的指标基本元件结构。


图例. 3. 带有弧形指示的圆形指标的基本结构

正如我们所见, 此类型具有弧形指标元素。为了实现弧形指示, 我们可以使用 绘制填充椭圆扇区方法, 并调用 Pie() 。此方法的所有重载中, 我决定使用以下方法:

void  Pie( 
   int         x,       // 椭圆中心的 X 坐标 
   int         y,       // 椭圆中心的 Y 坐标 
   int         rx,      // 椭圆半径的 X 坐标 
   int         ry,      // 椭圆半径的 Y 坐标 
   int         fi3,     // 为第一个弧形边界设置从椭圆中心的射线角度 
   int         fi4,     // 为第二个弧形边界设置从椭圆中心的射线角度 
   const uint  clr,     // 线颜色 
   const uint  fill_clr // 填充颜色 
   );

fi3 设置第一个圆弧边界, 并作为弧形刻度的开始, 而 fi4 可以依据传递到指标的数值动态地变化。在 Indicators 文件夹中创建 CircleArc.mqh 文件, 并在 CustomGUI.mqh 文件里添加以下字符串:

#include "Indicators\CircleArc.mqh"

因此, 我们将未来类添加到所有指标内含的整体列表中。定义一组常用的属性和方法。它们与以前的类方法没有区别, 但是 值得研究的是 ENUM_ORIENTATION 枚举。它具有两个值 (VERTICAL 和 HORIZONTAL), 并定义弧形刻度的第一个弧形边界的位置。如果该值是垂直, 则弧形自 90 度开始, 如果为水平 — 则为零。指示逆时针计数。

//+------------------------------------------------------------------+
//|                                                    CircleArc.mqh |
//|                                   版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| 带有数值和弧形指示的圆形指标                                          |
//+------------------------------------------------------------------+
class CCircleArc : public CCanvasBase
  {
private:
   //--- 指标背景颜色
   color             m_bg_color;
   //--- 指标弧形颜色
   color             m_arc_color;
   //--- 指示区域颜色
   color             m_area_color;
   //--- 指标描述颜色 
   color             m_label_color;
   //--- 指标数值颜色
   color             m_value_color;
   //--- 指标透明度
   uchar             m_transparency;
   //--- 指标尺寸
   int               m_radius;
   //--- 指标刻度宽度 
   int               m_scale_width;
   //--- 描述字号
   int               m_label_font_size;
   //--- 数值字号
   int               m_value_font_size;
   //--- 指标描述
   string            m_label_value;
   //--- 指标刻度方位类型
   ENUM_ORIENTATION  m_orientation;
public:
                     CCircleArc(void);
                    ~CCircleArc(void);
   //--- 设置并获取指标背景颜色
   color             BgColor(void)                                   { return(m_bg_color);            }
   void              BgColor(const color clr)                        { m_bg_color=clr;                }
   //--- 设置并获取指标弧形颜色
   color             ArcColor(void)                                  { return(m_arc_color);           }
   void              ArcColor(const color clr)                       { m_arc_color=clr;               }
   //--- 设置并获取指示区域颜色
   color             AreaColor(void)                                 { return(m_area_color);          }
   void              AreaColor(const color clr)                      { m_area_color=clr;              }
   //--- 设置并获取指标描述颜色 
   color             LabelColor(void)                                { return(m_label_color);         }
   void              LabelColor(const color clr)                     { m_label_color=clr;             }
   //--- 设置并获取指标数值颜色
   color             ValueColor(void)                                { return(m_value_color);         }
   void              ValueColor(const color clr)                     { m_value_color=clr;             }
   //--- 设置并获取指标透明度 
   uchar             Alpha(void)                                     { return(m_transparency);        }
   void              Alpha(const uchar trn)                          { m_transparency=trn;            }
   //--- 设置并获取指标尺寸
   int               Radius(void)                                    { return(m_radius);              }
   void              Radius(const int r)                             { m_radius=r;                    }
   //--- 设置并获取指标刻度宽度
   int               Width(void)                                     { return(m_scale_width);         }
   void              Width(const int w)                              { m_scale_width=w;               }
   //--- 设置并获取描述字号
   int               LabelSize(void)                                 { return(m_label_font_size);     }
   void              LabelSize(const int sz)                         { m_label_font_size=sz;          }
   //--- 设置并获取数值字号
   int               ValueSize(void)                                 { return(m_value_font_size);     }
   void              ValueSize(const int sz)                         { m_value_font_size=sz;          }
   //--- 设置并获取指标描述
   string            LabelValue(void)                                { return(m_label_value);         }
   void              LabelValue(const string str)                    { m_label_value=str;             }
   //--- 设置并获取刻度定位类型
   void              Orientation(const ENUM_ORIENTATION orietation)  { m_orientation=orietation;      }
   ENUM_ORIENTATION  Orientation(void)                               { return(m_orientation);         }
   //--- 创建指标
   void              Create(string name,int x,int y);
   //--- 删除指标
   void              Delete(void);
   //--- 设置并更新指标数值
   void              NewValue(double value,double maxvalue,int digits);
  };

我们来近距离观察 Create()NewValue() 方法, 因为它们的实现未能提供。请记住, 创建的对象层叠在一起, 因此创建它们的顺序如下:

  1. 弧形指标背景。作为填充的圆形。
  2. 弧形指标。作为填充的椭圆扇区。
  3. 文本背景。作为填充的圆形。
  4. 指标数值及其描述。

按照下面显示的既定顺序实施:

//+------------------------------------------------------------------+
//| 创建指标                                                           |
//+------------------------------------------------------------------+
void CCircleArc::Create(string name,int x,int y)
  {
   int r=m_radius;
   double a,b;
//--- 设置初始指标位置
   a=(m_orientation==VERTICAL)?M_PI_2:0;
   b=(m_orientation==VERTICAL)?M_PI_2:0;
   b+=90*M_PI/180;
//--- 相对于半径调整指标位置
   x=(x<r)?r:x;
   y=(y<r)?r:y;
   Name(name);
   X(x);
   Y(y);
   XSize(2*r+1);
   YSize(2*r+1);
   if(!CreateCanvas())
      Print("错误。未能创建指标。");
//---
   m_canvas.FillCircle(r,r,r,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_arc_color,m_transparency),ColorToARGB(m_arc_color,m_transparency));
   m_canvas.FillCircle(r,r,r-m_scale_width,ColorToARGB(m_area_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,"0",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

当设置第一个和第二个弧形边界的初始值 (即, 指标的起始点和当前值) 的同时, 我们要考虑到其初始方位, 以及为 Pie() 方法的边角设置弧度。作为示例, 对应于 b 变量的当前省缺值设置为 90, 然后将其转换为弧度, 否则显示不正确。NewValue() 方法使用相同的原理实现:

//+------------------------------------------------------------------+
//| 设置并更新指标数值                                                  |
//+------------------------------------------------------------------+
void CCircleArc::NewValue(double value,double maxvalue,int digits=2)
  {
   int r=m_radius;
   double a,b,result;
//--- 检查超界
   value=(value>maxvalue)?maxvalue:value;
   value=(value<0)?0:value;
//--- 设置初始指标位置
   a=(m_orientation==VERTICAL)?M_PI_2:0;
   b=(m_orientation==VERTICAL)?M_PI_2:0;
//---
   result=value*(360.0/maxvalue);
   b+=result*M_PI/180;
   if(b>=2*M_PI)
      b=2*M_PI-0.02;
//---
   m_canvas.FillCircle(r,r,r,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_arc_color,m_transparency),ColorToARGB(m_arc_color,m_transparency));
   m_canvas.FillCircle(r,r,r-m_scale_width,ColorToARGB(m_area_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }
//+------------------------------------------------------------------+

检查并设置初始位置后, 第二条弧形边界的弧度需重新计算角度。此后, 所有指标元素都用新值重新绘制。作为应用示例, 我们开发了一种简单的点差指标, 当达到阈值时, 它会改变弧度指示的颜色。

//+------------------------------------------------------------------+
//|                                                                  |
//|                                  版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/zh/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//+------------------------------------------------------------------+
//|  指标输入                                                         |
//+------------------------------------------------------------------+
input double         maxspr=12;                 // 最大值
input int            stepval=8;                 // 阀值
input color          stepcolor=clrCrimson;      // 阀值颜色
//---
CCircleArc arc;
double spr;
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                                 |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   arc.Orientation(HORIZONTAL);
   arc.LabelValue("Spread");
   arc.Create("spread_ind",100,100);
//---
   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[])
  {
   spr=double(SymbolInfoInteger(Symbol(),SYMBOL_SPREAD));
   if(spr>=stepval)
      arc.ArcColor(stepcolor);
   else
      arc.ArcColor(clrForestGreen);
//---
   arc.NewValue(spr,maxspr,0);
//--- 返回 prev_calculated 的数值以便下次调用
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| 自定义指标的逆初始化函数                                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   arc.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

正如我们从列表中所见, 使用 CCircleArc 类来实现, 对于创建和配置是极其简单的。请记住, 只有在创建指标 (Create() 方法) 之前, 或使用 NewValue() 方法之前, 才应修改和配置属性。因为这些方法在指标元素重绘和改变属性时需要用到。点差指标示于图例. 4。

图例. 4. 简单的带有弧形指示的圆形指标


CCircleSection 指标类

不同于简单的弧形指示, 扇区图看起来好像具有间隔相等的标签。当创建这类指标的布局时, 我决定制作十个扇区并添加一个新的元素 – 内框。具有弧形扇区指示的基本结构如图例. 5 所示。


图例. 5. 带有弧形指示的圆形指标的基本结构

显示带有扇区的弧形指示与前一个类完全不同, 前者仅一次性使用 CCanvas 函数库的 Pie() 方法。在该方法中, 当数值变化时, 第二个椭圆扇区弧形的位置也会改变。在此, 该方法被使用了十次, 且弧形位置保持静止。简单地说, 指标具有 10 个填充的椭圆扇区, 在一个圆圈中彼此衔接。改变某些部分的颜色可作为视觉指示。

Indicators 文件夹里创建 CircleSection.mqh 文件, 并像以前所有的做法一样, 在 CustomUI.mqh 文件里包含它。现在, 它看起来如同以下:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                   版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"

由于指标类是按照构造和功能复杂度的顺序呈现, 所以我不会提供它们的共同属性和方法。代之, 我们将注意力集中在添加新功能, 以及不同的实现。所以, 在当前类中, 大多数方法与前一个相似, 以下是额外部分:

//+------------------------------------------------------------------+
//|                                                CircleRounded.mqh |
//|                                  版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+--------------------------------------------------------------------------+
//| 带有数值和圆形扇区指示的圆形指标                                               |
//+--------------------------------------------------------------------------+
class CCircleSection : public CCanvasBase
  {
private:
   //--- 活跃/不活跃刻度分区的颜色
   color             m_scale_color_on;
   color             m_scale_color_off;
public:
   //--- 设置活跃/不活跃刻度分区的颜色
   void              ScaleColorOn(const color clr)                   { m_scale_color_on=clr;          }
   void              ScaleColorOff(const color clr)                  { m_scale_color_off=clr;         }
   //--- 创建指标
   void              Create(string name,int x,int y);
   //--- 设置并更新指标数值
   void              NewValue(double value,double maxvalue,int digits);
  };

保留 Create()NewValue() 方法, 因为它们的实现与以前的不同。我们从下面的列表中可以看出, 相对于半径的调整位置, 随后是使用循环创建十个扇区的模块。要考虑的起点: 水平 – 从零度, 垂直 – 从 90 度。

//+------------------------------------------------------------------+
//| 创建指标                                                           |
//+------------------------------------------------------------------+
void CCircleSection::Create(string name,int x,int y)
  {
   int r=m_radius;
   double a,b;
//--- 相对于半径修改指标位置
   x=(x<r)?r:x;
   y=(y<r)?r:y;
   Name(name);
   X(x);
   Y(y);
   XSize(2*r+1);
   YSize(2*r+1);
   if(!CreateCanvas())
      Print("错误。未能创建指标。");
//--- 指标扇区
   for(int i=0;i<10;i++)
     {
      if(m_orientation==HORIZONTAL)
        {
         a=36*i*M_PI/180;
         b=36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      else
        {
         a=M_PI_2+36*i*M_PI/180;
         b=M_PI_2+36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_bg_color,m_transparency),ColorToARGB(m_scale_color_off,m_transparency));
     }
//--- 边框和背景
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width-m_border_size,ColorToARGB(m_area_color,m_transparency));
//--- 描述
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
//--- 数字值
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,"0",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

正如我们已经说过的, 除了改变数值之外, 视觉指示是以扇区颜色变化的形式来体现。变化应该是顺序的, 并且基于当前和最高的指定值。例如, 如果数值为 20, 最大值为 100, 则两个扇区将改变颜色。但如果最大值为 200, 则只有一个扇区会改变其颜色。我们来更详细地研究在 NewValue() 方法里是如何做到的:

//+------------------------------------------------------------------+
//| 设置并更新指标数值                                                   |
//+------------------------------------------------------------------+
void CCircleSection::NewValue(double value,double maxvalue=100,int digits=2)
  {
//---
   int r=m_radius;
   double a,b;
   color clr;
//---
   for(int i=0;i<10;i++)
     {
      if(m_orientation==HORIZONTAL)
        {
         a=36*i*M_PI/180;
         b=36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      else
        {
         a=M_PI_2+36*i*M_PI/180;
         b=M_PI_2+36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      clr=(maxvalue/10*(i+1)<=value)?m_scale_color_on:m_scale_color_off;
      m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_bg_color,m_transparency),ColorToARGB(clr,m_transparency));
     }
//---
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width-m_border_size,ColorToARGB(m_area_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }
//+------------------------------------------------------------------+

正如我们在上表中可以看到的, 实现看起来很简单。相对于最大值检查当前值。如果超过或等于最大值除以扇区数量并乘以当前循环迭代, 则扇区颜色将从非活跃状态更改为活跃状态。

作为使用该类的示例, 我已经实现了回撤指标, 显示净值与当前账户余额的比率。因此, 我们可以直观地管理帐户的回撤, 从而替代跟踪平台中的数字。这样一个指标的实现清单呈现如下。

//+------------------------------------------------------------------+
//|                                              CustomIndicator.mq5 |
//|                                   版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/zh/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CCircleSection ind;
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                                 |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ind.Radius(70);
   ind.LabelValue("回撤");
   ind.Create("回撤",150,150);
//---
   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[])
  {
//---
   ind.NewValue(AccountInfoDouble(ACCOUNT_EQUITY),AccountInfoDouble(ACCOUNT_BALANCE));
//--- 返回 prev_calculated 的数值以便下次调用
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| 自定义指标的逆初始化函数                                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

不要忘记在 deinitialization 中使用 Delete() 方法从图表中正确地删除指标。


CLineGraph 线性图形类

若要使用自定义图形创建线性图形, 应首先确定图形本身的元素, 即:

  • 图形底图. 它有两个特征: 大小 (也是对象本身的大小) 和颜色。
  • 图形边框。 仅颜色。该元素由两个填充的矩形组成。后一个(也用作图形背景) 的坐标会移位一格产生边框效果。
  • 图形背景. 仅颜色。 
  • 图形坐标轴。仅颜色。与图形框架的方式相同, 通过两个重叠矩形来实现, 其中上面一个的坐标移动一格。
  • 坐标轴刻度。仅颜色。使用 LineHorizontal() 方法设置 Y 轴, 并用 LineVertical() 设置 Х 轴。
  • 坐标轴数值。颜色和字号属性。使用 TextOut() 方法设置文本对象。
  • 网格。仅颜色。选择 LineAA() 方法实现网格, 因为它可以设置线的样式。
  • 图形。仅颜色。元素由线和填充圆组成 — FillCircle() 方法。 

只有可定制的属性会在元素描述中指定。图例. 6 显示图形元素结构。 


图例. 6. 基本线性图形结构

首先, 在 Indicators 文件夹里创建 LineGraph.mqh 文件, 并将其包含在 CustomGUI.mqh 文件里:

//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"
#include "Indicators\LineGraph.mqh"
//+------------------------------------------------------------------+

然后, 在 LineGraph.mqh 文件里创建 CLineGraph 类, 并用 CCanvasBase 作为它的基类, 就像之前那样。在线性图形的基本结构中定义上述所有属性和方法。 

//+------------------------------------------------------------------+
//|                                                    LineGraph.mqh |
//|                                   版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| 线性图形                                                           |
//+------------------------------------------------------------------+
class CLineGraph : public CCanvasBase
  {
private:
   //--- 图形底图颜色
   color             m_bg_color;
   //--- 图形背景颜色
   color             m_bg_graph_color;
   //--- 图形边框颜色
   color             m_border_color;
   //--- 图形坐标轴数值颜色
   color             m_axis_color;
   //--- 图形网格颜色
   color             m_grid_color;
   //--- 图形坐标轴刻度颜色
   color             m_scale_color;
   //--- 图形线颜色
   color             m_graph_color;
   //--- 图形尺寸
   int               m_x_size;
   int               m_y_size;
   //--- 图形距底图边缘的缩进
   int               m_gap;
   //--- 图形坐标轴的字号
   int               m_font_size;
   //--- Y 轴数值数组
   int               m_x[];
   //--- Y 轴的最小和最大值
   double            m_y_min;
   double            m_y_max;
   //--- Y 轴刻度数量
   int               m_num_grid;
   //--- 图形透明度
   uchar             m_transparency;
public:
                     CLineGraph(void);
                    ~CLineGraph(void);
   //--- 设置和获取图形底图颜色
   color             BgColor(void)                                   { return(m_bg_color);                  }
   void              BgColor(const color clr)                        { m_bg_color=clr;                      }
   //--- 设置和获取图形底背景颜色
   color             BgGraphColor(void)                              { return(m_bg_graph_color);            }
   void              BgGraphColor(const color clr)                   { m_bg_graph_color=clr;                }
   //--- 设置和获取图形边框颜色
   color             BorderColor(void)                               { return(m_border_color);              }
   void              BorderColor(const color clr)                    { m_border_color=clr;                  }
   //--- 设置和获取图形坐标轴数值颜色
   color             AxisColor(void)                                 { return(m_axis_color);                }
   void              AxisColor(const color clr)                      { m_axis_color=clr;                    }
   //--- 设置和获取图形网格颜色
   color             GridColor(void)                                 { return(m_grid_color);                }
   void              GridColor(const color clr)                      { m_grid_color=clr;                    }
   //--- 设置和获取图形坐标轴刻度颜色
   color             ScaleColor(void)                                { return(m_scale_color);               }
   void              ScaleColor(const color clr)                     { m_scale_color=clr;                   }
   //--- 设置和获取图形线颜色
   color             GraphColor(void)                                { return(m_graph_color);               }
   void              GraphColor(const color clr)                     { m_graph_color=clr;                   }
   //--- 设置和获取图形尺寸
   int               XGraphSize(void)                                { return(m_x_size);                    }
   void              XGraphSize(const int x_size)                    { m_x_size=x_size;                     }
   int               YGraphSize(void)                                { return(m_y_size);                    }
   void              YGraphSize(const int y_size)                    { m_y_size=y_size;                     }
   //--- 设置和获取图形坐标轴上数字的字号
   int               FontSize(void)                                  { return(m_font_size);                 }
   void              FontSize(const int fontzise)                    { m_font_size=fontzise;                }
   //--- 设置和获取距底图边缘的缩进
   int               Gap(void)                                       { return(m_gap);                       }
   void              Gap(const int g)                                { m_gap=g;                             }
   //--- 设置和获取 Y 轴的最小和最大值
   double            YMin(void)                                      { return(m_y_min);                     }
   void              YMin(const double ymin)                         { m_y_min=ymin;                        }
   double            YMax(void)                                      { return(m_y_max);                     }
   void              YMax(const double ymax)                         { m_y_max=ymax;                        }
   //--- 设置和获取 Y 轴刻度数量
   int               NumGrid(void)                                   { return(m_num_grid);                  }
   void              NumGrid(const int num)                          { m_num_grid=num;                      }
   //--- 创建图形
   void              Create(string name,int x,int y);
   //--- 删除图形 
   void              Delete(void);
   //--- 设置输入数组
   void              SetArrayValue(double &data[]);
private:
   //---
   void              VerticalScale(double min,double max,int num_grid);
   void              HorizontalScale(int num_grid);
  };

我们来近距离观察以上清单未列出方法的实现。Create() 方法用于创建图形资源, 然后将其放置在品种图表上。在此还使用两个私有方法设置垂直和水平刻度: 无论是基于类构造函数中的初始化设置, 亦或在创建之前使用类方法。

//+------------------------------------------------------------------+
//| 创建图形                                                           |
//+------------------------------------------------------------------+
void CLineGraph::Create(string name,int x,int y)
  {
//--- 修改指标的相对位置
   x=(x<m_x_size/2)?m_x_size/2:x;
   y=(y<m_y_size/2)?m_y_size/2:y;
   Name(name);
   X(x);
   Y(y);
   XSize(m_x_size);
   YSize(m_y_size);
   if(!CreateCanvas())
      Print("错误。未能创建指标。");
//--- 创建图形的框架和底图
   m_canvas.FillRectangle(0,0,XSize(),YSize(),ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(1,1,XSize()-2,YSize()-2,ColorToARGB(m_bg_color,m_transparency));
//--- 创建坐标轴和图形背景
   m_canvas.FillRectangle(m_gap-1,m_gap-1,XSize()-m_gap+1,YSize()-m_gap+1,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(m_gap,m_gap,XSize()-m_gap,YSize()-m_gap,ColorToARGB(m_bg_graph_color,m_transparency));
//--- 创建坐标轴刻度和它们的数值
   VerticalScale(m_y_min,m_y_max,m_num_grid);
   HorizontalScale(5);
   m_canvas.Update();
  }

为了显示线性图表, 使用数据数组作为基础是合理的, 因为 MetaTrader 经常用数组来复制指标缓冲区的数值。当实现 SetArrayValue() 图形显示方法时, 数据数组作为基础 (方法参数), 此处数组索引对应于 X 轴, 而其数值 — 对应于 Y 轴。 

//+------------------------------------------------------------------+
//| 设置输入数组                                                       |
//+------------------------------------------------------------------+
void CLineGraph::SetArrayValue(double &data[])
  {
   int y0,y1;
//---  创建图形的框架和底图
   m_canvas.FillRectangle(0,0,XSize(),YSize(),ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(1,1,XSize()-2,YSize()-2,ColorToARGB(m_bg_color,m_transparency));
//--- 创建坐标轴和图形背景
   m_canvas.FillRectangle(m_gap-1,m_gap-1,XSize()-m_gap+1,YSize()-m_gap+1,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(m_gap,m_gap,XSize()-m_gap,YSize()-m_gap,ColorToARGB(m_bg_graph_color,m_transparency));
//--- 如果数据数组的最大值超过 Y 轴上边界, 则缩放数轴。
   if(data[ArrayMaximum(data)]>m_y_max)
      m_y_max=data[ArrayMaximum(data)];
//--- 创建坐标轴刻度和它们的数值
   VerticalScale(m_y_min,m_y_max,m_num_grid);
   HorizontalScale(ArraySize(data));
//--- 根据数据数组创建图形
   for(int i=0;i<ArraySize(data)-1;i++)
     {
      y0=int((YSize()-2*m_gap)*(1-data[i]/m_y_max));
      y0=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]);
      y0=(y0<m_gap)?m_gap:y0;
      y1=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i+1]);
      y1=(y1<m_gap)?m_gap:y1;
      m_canvas.LineAA(m_x[i+1],y0,m_x[i+2],y1,ColorToARGB(m_graph_color,m_transparency),STYLE_SOLID);
      m_canvas.FillCircle(m_x[i+1],y0,2,ColorToARGB(m_graph_color,m_transparency));
      m_canvas.FillCircle(m_x[i+2],y1,2,ColorToARGB(m_graph_color,m_transparency));
     }
   m_canvas.Update();
  }

作为类应用的一个示例, 我已经开发了一个基于所选振荡器数值的图形, 并与原始指标显示进行比较。标准 RSI 即用于此目的。下表显示使用基于 ClineGraph 类的缓冲区数值来实现构造。

//+------------------------------------------------------------------+
//|                                                            4.mq5 |
//|                                  版权所有 2017, Alexander Fedosov |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/zh/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CLineGraph ind;
int InpInd_Handle;
double rsi[];
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                                 |
//+------------------------------------------------------------------+
int OnInit()
  {
//---- 获取指标句柄
   InpInd_Handle=iRSI(Symbol(),PERIOD_CURRENT,10,PRICE_CLOSE);
   if(InpInd_Handle==INVALID_HANDLE)
     {
      Print("获取指标句柄失败");
      return(INIT_FAILED);
     }
//---
   ind.NumGrid(10);
   ind.YMax(100);
   ind.Create("rsi",350,250);

//---
   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[])
  {
//---
   if(CopyBuffer(InpInd_Handle,0,0,10,rsi)<=0)
      return(0);
   ind.SetArrayValue(rsi);
//--- 返回 prev_calculated 的数值以便下次调用
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| 自定义指标的逆初始化函数                                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

现在, 使用相同的参数设置原始指标, 并与自图例. 7 中获得的结果比较:

图例. 7. 使用 CLineGraph 类实现的构造与原始 RSI 的比较

请注意, 使用 CopyBuffer 方法将数据写入的数组应具有原始形式。不像时间序列, 无需改变索引。


结论

在本文中, 我们已经在 CСanvas 自定义图形库的帮助下, 逐步创建了任意形式的自定义指标。我们还分析了各种复杂的例子 – 从一个简单的只有四个元件组成的圆形指标, 直到带有扇区指示的圆形。此外, 我们还研究了以新的方式实现已知指标类型。正如我们所看到的, 选项不受一组刚性定义属性的限制。所实现的示例已构造为类, 即它们是已完成 CCanvas 类的补充或扩展。 

附带的存档包含所有列出的文件, 它们位于相应的文件夹中。为了正确的操作, 应将 MQL5 文件夹保存到终端根目录。 

文章中使用的程序:

#
 名称
类型
描述
1
CanvasBase.mqh 函数库  自定义图形类的基础
2
CustomGUI.mqh 函数库  函数库中所有类文件的包括列表 
3 CircleArc.mqh 函数库  包含 CCircleArc 指标类
4 CircleSection.mqh 函数库  包含 CCircleSection 指标类
5 CircleSimple.mqh 函数库  包含 CCircleSimle 指标类
LineGraph.mqh 函数库  包含 CLineGraph 线性图形类
7 1.mq5 指标  示例 CCirleSimple 类的实现
8 2.mq5 指标  示例 CCirleArc 类的实现
9 3.mq5 指标  示例 CCirleSection 类的实现
 10 4.mq5 指标  示例 CLineGraph 类的实现


本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/3236

附加的文件 |
MQL5.zip (234.78 KB)
利用 CCanvas 的自定义指标和信息图 利用 CCanvas 的自定义指标和信息图
本文研究结构更加复杂实现的新型指标。它还描述了如何开发伪 3D 指标类型和动态信息图。
使用贝叶斯分类和基于奇异频谱分析的指标预测市场走势 使用贝叶斯分类和基于奇异频谱分析的指标预测市场走势
本文研究建立高效交易的推荐制系统的思想和方法, 结合了贝叶斯定理基础之上的重要机器学习方法, 以及奇异频谱分析 (SSA) 的预测能力。
排序方法并利用 MQL5 进行可视化 排序方法并利用 MQL5 进行可视化
Graphic.mqh 函数库以 MQL5 设计, 用来处理图形。本文提供了一个实际应用的例子, 并解释了排序的思路。这里描述排序的一般概念, 因为每种排序类型至少已经具有一篇单独的论文, 而有些排序类型更是详细研究的对象。
跨平台的EA交易: 资金管理 跨平台的EA交易: 资金管理
本文讨论了跨平台EA交易中资金管理方法的实现,资金管理类是用于EA交易中下一次交易进场时进行交易的手数大小计算的。