Canvasクラスを使用したカスタム指標の開発

Alexander Fedosov | 24 7月, 2017


内容

はじめに

カスタム指標は最新のMetaTrader 5取引では不可欠な一部となっており、自動取引システム(アルゴリズムの一部として)と手動取引の両方で使用されます。これまでには、指標を開発するにあたって描画スタイルを設定して18種類のグラフィカル描画を適用することができましたが、このプラットフォームはより広いグラフィカルな機能を備えています。CCanvasカスタムグラフィックスライブラリを使用すると、無限の視覚化機能を備えたカスタム非標準指標を開発できます。 本稿では、読者にこのライブラリの機能を紹介してそのアプリケーションの例を示し、個別のカスタム指標のライブラリを開発します。


基本カスタムグラフィックスクラスの開発

基本クラスを開発するには、カスタムグラフィックスオブジェクトの基盤を作成し、共通のプロパティセットを含む一連のメソッドを記述する必要があります。これには、<data folder>\MQL5\Includeディレクトリ内のCustomGUIフォルダにCanvasBase.mqh ファイルを作成します。このファイルには、将来的に、すべての種類のカスタムグラフィックス用のCCanvasBase基本クラスが含まれます。

//+------------------------------------------------------------------+
//|                                                  CCanvasBase.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/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 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| 数値と説明付きの円形指標                                  |
//+------------------------------------------------------------------+
class CCircleSimple : public CCanvasBase

EAおよび指標ファイルで必要なときに手動でグラフィカルオブジェクトを手動で入力しないですむようにCustomGUI フォルダにCustomGUI.mqhファイルを作成します。このファイルにはIndicatorsフォルダのすべてのクラスが含まれているため、このファイルのみをインクルードすればすべてのライブラリクラスにアクセスできます。現在のものをインクルードします。

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/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()メソッドです。リストにあるように、これには3つの引数しかありません。私はそれらを最も重要だと感じました。追加の引数を提供することはメソッドの実装を複雑にするため、他のすべてのプロパティは別々のメソッドに入れられます。したがって、すべての変数はクラスコンストラクタで初期化されます。

//+------------------------------------------------------------------+
//| コンストラクタ                                                     |
//+------------------------------------------------------------------+
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")
  {
  }

これは、1種類の指標を作成するのには、そのクラスのインスタンスを作成してCreateCanvas ()メソッドを使用するだけなため便利です。作成する前に、変更するプロパティのみを指定できます。Canvasライブラリを使用して複雑なグラフィカルオブジェクトを構築する場合は、プリミティブを実装するメソッドが順番にレイヤーで描画されることに注意してください。これは実際のキャンバスとよく似ています。アーティストは通常、初めに背景を描いてからオブジェクトを描写し、オブジェクトの詳細が続きます。 

//+------------------------------------------------------------------+
//| 指標を作成する                                                     |
//+------------------------------------------------------------------+
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("Error. Can not create Canvas.");
   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基本クラスから作成されます。
  • フレームを実装する際には、上記のスーパーインポーズ原理を使用しました。特に、最初の円(塗りつぶされた)と、最初の円よりも半径がフレーム幅だけ小さい2番目の円の、2つの円を作成しました。
  • これらのオブジェクトの上に数値と説明要素が作成されます。

次に、指標の数値の表示を実時間で更新する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();
  }

指標の数値が正しく更新されるためには、3つのオブジェクト(背景要素とテキスト要素)を作成時と同じ順序で再描画する必要があるので、NewValue()メソッドで背景に続いて値および説明テキスト要素を再描画します。

さて、MetaTrader 5ターミナルで円形指標の実装を確認します。これを行うには、空の指標を作成してCustomGUI.mqh ファイルをインクルードし、CCircleSimpleクラスの2つのインスタンスを作成します。

//+------------------------------------------------------------------+
//|                                              CustomIndicator.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/ja/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("Failed to get indicator handle");
      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指標クラス

円形指標を実装する最も簡単な方法を提供するクラスを検討してきました。タスクを少し複雑にして、1つの数字以外の追加のグラフィック表示要素、つまり円弧を持つクラスを作成しましょう。この円弧表示を有する指標の基本的な要素の構造を図3に示します。


図3 円弧表示を有する指標の基本的な要素

見て分かるように、このタイプには円弧指標要素があります。円弧表示を実装するには塗りつぶした楕円セクタを描画するPie()メソッドを使用できます。このメソッドのすべてのオーバーロードのうち、次のものを使用することに決めました。

void  Pie( 
   int         x,       // 楕円中心X座標 
   int         y,       // 楕円中心Y座標
   int         rx,      // 楕円半径X座標
   int         ry,      // 楕円半径Y座標
   int         fi3,     // 最初の弧の境界を設定する楕円の中心からのX線の角度
   int         fi4,     // 2番目の弧の境界を設定する楕円の中心からのX線の角度
   const uint  clr,     // 線の色
   const uint  fill_clr // 塗りつぶしの色
   );

fi3は最初の弧の境界線を設定して円弧スケールの始まりとして使用される一方、fi4 は指標に渡される数値に応じて動的に変更できます。Indicators フォルダにCircleArc.mqhファイルを作成してCustomGUI.mqhファイルに下記を追加します。

#include "Indicators\CircleArc.mqh"

したがって、すべての指標の包括的リストに将来のクラスを追加します。共通のプロパティとメソッドのセットを定義します。これは以前のクラスのメソッドと違いはありませんがENUM_ORIENTATION列挙型を考える価値はあります。これは2つの値(VERTICALとHORIZONTAL)を持ち、弧のスケールの最初の弧の境界線の位置を定義します。値が垂直の場合、円弧は90度から始まり、水平の場合はゼロから始まります。表示は反時計回りに数えられます。

//+------------------------------------------------------------------+
//|                                                    CircleArc.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/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("Error. Can not create Canvas.");
//---
   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();
  }

1番目と2番目の弧の境界の初期値(すなわち、指標の開始点と現在値)を設定する際は、その初期の向きと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();
  }
//+------------------------------------------------------------------+

最初の位置を確認して設定した後、2番目の円弧境界の角度をラジアンで再計算します。その後、すべての指標要素が新しい値で再描画されます。適用例として、閾値に達すると指標の円弧スケールの色が変わる簡単なスプレッド指標を開発しました。

//+------------------------------------------------------------------+
//|                                                                  |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/ja/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指標クラス

単純な円弧表示とは異なり、分割指標は等間隔を分割するラベルを持つように見えます。この種の指標のレイアウトを作成するときは、10個のセクションを作成し、新要素、つまり内部フレームを追加することにしました。円弧分割指標を有する基本的な構造を図5に示します。


図5 円弧分割指標を有する円形指標の基本構造

セクションを持つ円弧の表示は、CCanvasライブラリのPie() メソッドを1回使用した以前のクラスとはまったく異なります。このメソッドでは、値が変化すると、第2の楕円セクタ弧の位置が変更されます。ここでは、メソッドが10回適用されて、円弧の位置は静的なままです。簡単に言えば、指標は、円状で隣り合わせの10の満たされた楕円形のセクタを特徴とします。特定のセクションの色の変化は、視覚的な表示となります。

IndicatorsフォルダにCircleSection.mqhファイルを作成して前と同じようにCustomUI.mqhファイルにインクルードします。リストは下記のようになります。

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"

指標クラスは構造や機能が複雑な順に提示されるので、共通のプロパティやメソッドはここでは提供しません。代わりに、新しい機能を追加し、異なる実装を持つものに注目してみましょう。したがって、現在のクラスでは、ほとんどのメソッドは前のものと似ていますが、以下の追加があります。

//+------------------------------------------------------------------+
//|                                                CircleRounded.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/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() メソッドの実装は以前のものとは異なるため省かれました。下のリストからわかるように、半径を基準とした位置の修正の後に、ループを使用して10のセクションを作成するブロックが続きます。水平は0度から、垂直は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("Error. Can not create Canvas.");
//--- 指標のセクション
   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の場合、2つのセクションの色が変更されます。しかし、最大値が200の場合、色が変更されるのは1つのセクションだけです。これが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 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/ja/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("Drawdown");
   ind.Create("drawdown",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();
  }
//+------------------------------------------------------------------+

チャートから指標を正しく削除するには、初期化解除時にDelete()メソッドを使用することを忘れないでください。


CLineGraph線グラフクラス

カスタムグラフィックスを使用して線形グラフを作成するには、まずグラフ自体の要素を決定する必要があります。すなわち

  • グラフのバックドロップ:サイズ(オブジェクト自体のサイズも含む)と色の2つの機能があります。
  • グラフのフレーム:色のみ。この要素は、2つの塗りつぶされた長方形で構成されます。2番目の長方形(グラフの背景としても機能する)の座標は、1だけシフトされてフレーム効果を提供します。
  • グラフの背景:色のみ。 
  • グラフの軸:色のみ。2つの長方形を重ねてグラフフレームと同じ方法で実装します。上の座標は1つシフトされています。
  • 軸のスケール:色のみ。Y軸のLineHorizontal()メソッドとX軸の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 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/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()メソッドを使用してグラフィックリソースを作成し、それをシンボルチャートに配置します。クラスのコンストラクタで初期化された設定に基づいて、または作成前にクラスメソッドを使用して、縦横比を設定する2つのプライベートメソッドもここで使用されます。

//+------------------------------------------------------------------+
//| グラフを作成する                                                   |
//+------------------------------------------------------------------+
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("Error. Can not create Canvas.");
//--- グラフフレームとバックドロップを作成する
   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 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/ja/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/ja/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("Failed to get indicator handle");
      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メソッドを使用して書き込まれる配列は元の形式である必要があります。時系列とは異なり、インデックスの変更は必要ありません。


終わりに

本稿では、Cappanvasカスタムグラフィックスライブラリの助けを借りて、任意のフォームのカスタム指標の段階的な作成を扱ってきました。また、4つの要素のみからなる単純な円形指標から断面表示を持つ円形指標まで、さまざまな複雑さをもつ例を分析しました。また、すでに知られている指標タイプの実装を新しい方法で検討しました。これまで見てきたように、オプションは厳密に定義されたプロパティのセットによって制限されません。実装例はクラスとして構成されており、既に完成している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クラスの実装例