CCanvasでのカスタム指標とインフォグラフィックス
内容
はじめに
前回の記事では、CCanvasクラスのシンプルプリミティブを開発するためのメソッドを使用してグラフィカル指標を構築する原則について検討しました。しかし、カスタムグラフィックスライブラリはより幅広い機能を持っているため、より複雑な構造的実装を伴う新しい種類の指標を見てみようと思います。それに加えて、疑似3D型指標と動的インフォグラフィックスもプロットしてみます。
CLineRoundedクラス
本稿では、グラフィカルオブジェクトを実装するために基本クラスの一般的な構造を作り直すつもりはありません。その代わりに前稿で作成したCustomGUIライブラリを使用し、特に、CCanvasBaseクラスを基本クラスとして使用します。新しく開発されたクラスはCustomGUI.mqh ファイル内のリストに追加されます。
簡単な線形指標を開発するには、その構造と基本要素を定義する必要があります。図1は、この種類の指標を使用するときに管理できる要素を示しています。これらは単純要素ではないことに注意してください。
図1 簡単な線形指標の基本構造
CCanvasクラスメソッドで実装された要素は単純(基本)とみなされています。たとえば、線形指標では、CCanvas::TextOut()メソッドを使用して実装されているValueのみが単純であるとみなされます。残りの3つの要素は似ていて、主に2つの塗りつぶし円(CCanvas::FillCircle()の実装)と1つの長方形(CCanvas::FillRectangle())の3つの基本図形を含みます。
CustomGUI/IndicatorsフォルダにLineRounded.mqhファイルを作成して、この新しく作成されたファイルにСLineRoundedクラスを作成し、以前に作成されたCCanvasBaseクラスをその基本クラスとして割り当てます。
//+------------------------------------------------------------------+ //| LineRounded.mqh | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #include "..\CanvasBase.mqh" //+------------------------------------------------------------------+ //| 丸みを帯びた線状指標 | //+------------------------------------------------------------------+ class CLineRounded : public CCanvasBase
また、 CustomGUI.mqhファイルのリストに追加することで、すべてのライブラリクラスにすばやくアクセスできます。以前に開発されたクラスを考慮すると、そのコードは次のようになります。
//+------------------------------------------------------------------+ //| CustomGUI.mqh | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #include "Indicators\CircleSimple.mqh" #include "Indicators\CircleArc.mqh" #include "Indicators\CircleSection.mqh" #include "Indicators\LineGraph.mqh" #include "Indicators\LineRounded.mqh" //+------------------------------------------------------------------+
СLineRoundedクラスのプロパティとメソッドはCustomGUI/Indicators/LineRounded.mqhファイルで完全に一覧することができるため、指標の作成と設定、値の更新方法に焦点を当てましょう。上述したように、指標は複合要素からなるものとします。これらの要素の実装は、以下のリストの個々のブロックでコメントされています。
//+------------------------------------------------------------------+ //| 指標を作成する | //+------------------------------------------------------------------+ void CLineRounded::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(YSize()/2,0,XSize()-YSize()/2,YSize(),ColorToARGB(m_border_color,m_transparency)); m_canvas.FillCircle(YSize()/2,YSize()/2-1,YSize()/2,ColorToARGB(m_border_color,m_transparency)); m_canvas.FillCircle(XSize()-YSize()/2-1,YSize()/2,YSize()/2,ColorToARGB(m_border_color,m_transparency)); //--- バックドロップ m_canvas.FillRectangle(YSize()/2,m_border,XSize()-YSize()/2,YSize()-m_border,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillCircle(XSize()-YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency)); m_canvas.Update(); }
ご覧のとおり、フレームとバックドロップは2つのメソッドで実装されています。これらのオブジェクトがレイヤーであり、互いに重なって描画されていることを忘れないでください。したがって、最初にフレームが描画され、小さい方のバックドロップ、指標スケール、数値が続きます。
CLineRounded::NewValue()メソッドについて考えます。
//+------------------------------------------------------------------+ //| 指標値を設定して更新する | //+------------------------------------------------------------------+ void CLineRounded::NewValue(double value,double maxvalue,int digits=0) { int v; v=int((XSize()-YSize()/2)/maxvalue*value); //--- バックドロップ m_canvas.FillRectangle(YSize()/2,m_border,XSize()-YSize()/2,YSize()-m_border,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillCircle(XSize()-YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_bg_color,m_transparency)); //--- スケール if(v>=YSize()/2 && v<=(XSize()-YSize()/2)) { m_canvas.FillRectangle(YSize()/2,m_border,v,YSize()-m_border,ColorToARGB(m_scale_color,m_transparency)); m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency)); m_canvas.FillCircle(v,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency)); } else if(v>(XSize()-YSize()/2)) { m_canvas.FillRectangle(YSize()/2,m_border,XSize()-YSize()/2,YSize()-m_border,ColorToARGB(m_scale_color,m_transparency)); m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency)); m_canvas.FillCircle(XSize()-YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency)); } else if(v>0 && v<YSize()/2) m_canvas.FillCircle(YSize()/2,YSize()/2,YSize()/2-m_border,ColorToARGB(m_scale_color,m_transparency)); //--- 数値 m_canvas.FontSizeSet(m_font_size); m_canvas.TextOut(XSize()/2,YSize()/2,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER); m_canvas.Update(); } //+------------------------------------------------------------------+ご覧のとおり、バックドロップの実装は再びメソッドの冒頭に存在します。何故でしょうか。対応するメソッドを一貫して適用することによって、要素がレイヤーごとに描画されることを覚えておいてください。言い換えれば、最初に、スケールの背景を表示してから長さを最大パラメータ値で定義した指標を表示します。新しい指標値が到着すると、バックドロップレイヤーが最初に再描画され、次に新しい長さのスケールが描画されます。
値の作成と指標への受け渡しは、非常に簡単なメソッドで実装されます。下のリストでは、小さな設定の2つの指標が表示されます。そのうち1つは、あと1つの指標の2倍の最大値を持ちます。
//+------------------------------------------------------------------+ //| 001.mq5 | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #property version "1.00" #property indicator_plots 0 #property indicator_chart_window #include <CustomGUI\CustomGUI.mqh> //--- CLineRounded ind1,ind2; //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ int OnInit() { //--- ind1.XSizeInd(350); ind1.YSizeInd(30); ind1.Create("line1",300,100); //--- ind2.XSizeInd(350); ind2.YSizeInd(30); ind2.ScaleColor(clrFireBrick); ind2.Create("line2",300,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[]) { //--- ind1.NewValue(50,100,2); ind2.NewValue(50,200,2); //--- 次の呼び出しのためにprev_calculatedを戻す return(rates_total); } //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ind1.Delete(); ind2.Delete(); } //+------------------------------------------------------------------+
図2は、似たような値では、指標スケールの長さは異なるスケールで適切な値を有することをはっきりと示しています。表示結果を視覚的に評価できるように、50と最大値(最初の値は100、2番目の値は200)が選択されています。
図2 丸みを帯びた線状指標の例
CHexagonクラス
CCanvasクラスによって設定されたプリミティブを使用して、指標を正六角形の形でプロットするためのさまざまなメソッドを使用できます。これには、ポリラインの構築やその後の領域の塗りつぶし、ピザのスライスのような6つの正三角形の「組み立て」が含まれます。しかし、ここでの目的は単純さを最大限にすることであるため、1つの四角形と2つの二等辺三角形という3つのプリミティブを使用して正六角形を構築します。
図3 正六角形の構造
正方形のキャンバスに右の六角形を挿入します。この形の属性を覚えておいてください。
- 正六角形の一辺は、その周りを囲む円の半径に等しくなります(この場合、これはキャンバスの半分です)。この特徴は、長方形をプロットするときに必要です。
- 六角形の角度は120度なので、二等辺三角形の底角はそれぞれ30度です。このデータはCCanvas::FillTriangle()メソッドを使用する際に三角形の高さを定義し、三角形の基点の座標を見つけるために必要です。
指標自体の基本的な構造は非常に単純で、六角形に加えて、数値と説明の2つのテキストオブジェクトが含まれています(図4)。
図4 六角形指標の基本構造
CustomGUI/IndicatorsフォルダにHexagon.mqhファイルを作成して、この新しく作成されたファイルにСHexagon クラスを作成し、以前に作成されたCCanvasBaseクラスをその基本クラスとして割り当てます。一般リストにインクルードします。
//+------------------------------------------------------------------+ //| CustomGUI.mqh | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #include "Indicators\CircleSimple.mqh" #include "Indicators\CircleArc.mqh" #include "Indicators\CircleSection.mqh" #include "Indicators\LineGraph.mqh" #include "Indicators\LineRounded.mqh" #include "Indicators\Hexagon.mqh" //+------------------------------------------------------------------+
プロパティとメソッドはすべて上で作成したファイルで一覧できるので、指標の視覚化を担当するメソッドに集中しましょう。
//+------------------------------------------------------------------+ //| 指標を作成する | //+------------------------------------------------------------------+ void CHexagon::Create(string name,int x,int y) { int a,r; r=m_size; //--- 指標の位置を座標軸に関連して変更する x=(x<r/2)?r/2:x; y=(y<r/2)?r/2:y; Name(name); X(x); Y(y); XSize(r); YSize(r); if(!CreateCanvas()) Print("Error. Can not create Canvas."); //--- 三角形の高さを計算する a=int(YSize()/2*MathSin(30*M_PI/180)); //--- 六角形 m_canvas.FillTriangle(XSize()/2,0,0,a,XSize(),a,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillRectangle(0,a,XSize(),a+YSize()/2,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillTriangle(0,a+YSize()/2,XSize(),a+YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency)); //--- 説明と数値 m_canvas.FontNameSet("Trebuchet MS"); m_canvas.FontSizeSet(m_value_font_size); m_canvas.TextOut(r/2,r/2,"-",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER); m_canvas.FontSizeSet(m_label_font_size); m_canvas.TextOut(r/2,r/2+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER); //--- m_canvas.Update(); }
Create() メソッドでは、二等辺三角形の値が1つの変数に割り当てられます。実際、このようにして、縦軸(Y軸)に沿った三角点の座標を求めます。数値を転送して更新するメソッドは、数値を描画するテキストオブジェクトがvalue 引数を受け取るという点でのみ異なります。
//+------------------------------------------------------------------+ //| 指標値を設定して更新する | //+------------------------------------------------------------------+ void CHexagon::NewValue(double value,int digits=2) { int a,r; r=m_size; //--- 三角形の高さを計算する a=int(YSize()/2*MathSin(30*M_PI/180)); //--- 六角形 m_canvas.FillTriangle(XSize()/2,0,0,a,XSize(),a,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillRectangle(0,a,XSize(),a+YSize()/2,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillTriangle(0,a+YSize()/2,XSize(),a+YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency)); //--- テキスト m_canvas.FontNameSet("Trebuchet MS"); m_canvas.FontSizeSet(m_value_font_size); m_canvas.TextOut(r/2,r/2,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER); m_canvas.FontSizeSet(m_label_font_size); m_canvas.TextOut(r/2,r/2+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER); //--- m_canvas.Update(); } //+------------------------------------------------------------------+
例として小さな六角形指標を実装しましょう。
//+------------------------------------------------------------------+ //| 002.mq5 | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #property version "1.00" #property indicator_plots 0 #property indicator_chart_window #include <CustomGUI\CustomGUI.mqh> //--- CHexagon ind1,ind2,ind3,ind4,ind5,ind6,ind7,ind8,ind9,ind10,ind11,ind12,ind13,ind14; //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ int OnInit() { //--- ind1.BgColor(clrWhite); ind1.Size(110); ind1.Create("hex1",300,200); ind2.BgColor(C'60,170,220'); ind2.Create("hex2",300,200); //--- ind3.BgColor(clrWhite); ind3.Size(110); ind3.Create("hex3",300,80); ind4.BgColor(C'230,80,25'); ind4.Create("hex4",300,80); //--- ind5.BgColor(clrWhite); ind5.Size(110); ind5.Create("hex5",300,320); ind6.BgColor(C'150,190,15'); ind6.Create("hex6",300,320); //--- ind7.BgColor(clrWhite); ind7.Size(110); ind7.Create("hex7",180,140); ind8.BgColor(C'10,115,185'); ind8.Create("hex8",180,140); //--- ind9.BgColor(clrWhite); ind9.Size(110); ind9.Create("hex9",420,140); ind10.BgColor(C'20,150,150'); ind10.Create("hex10",420,140); //--- ind11.BgColor(clrWhite); ind11.Size(110); ind11.Create("hex11",420,280); ind12.BgColor(C'225,0,80'); ind12.Create("hex12",420,280); //--- ind13.BgColor(clrWhite); ind13.Size(110); ind13.Create("hex13",180,280); ind14.BgColor(C'240,145,5'); ind14.Create("hex14",180,280); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ind1.Delete(); ind2.Delete(); ind3.Delete(); ind4.Delete(); ind5.Delete(); ind6.Delete(); ind7.Delete(); ind8.Delete(); ind9.Delete(); ind10.Delete(); ind11.Delete(); ind12.Delete(); ind13.Delete(); ind14.Delete(); } //+------------------------------------------------------------------+
結果は図5に示されています。この基本テンプレートを構成及び変更するのは非常に簡単です。
図5 CHexagonクラスを使用して指標セットを実装する例
CPetalクラス
花弁型指標の実装には、Canvasクラスのメソッドからの2-3個のプリミティブが必要です。これらは塗りつぶされた円(FillCircle() メソッド)と、1つか2つ(形式による)の塗りつぶした三角形(FillTriangle())です。構造と要素を図6に示します。
図6 花弁状指標の基本構造
ご覧のように、ここでは2つの三角形が使用されていますが、 'enum'列挙型を使用して、いくつかの形式をクラスに含めます。これらの形式をもっと詳しく考えてみましょう。
enum ENUM_POSITION { TOPRIGHT=1, TOPLEFT=2, BOTTOMRIGHT=3, BOTTOMLEFT=4, BOTHRIGHT=5, BOTHLEFT=6 };
- TOPRIGHT — 円と頂点が右上隅に向かう三角形で構成
- TOPLEFT — 円と頂点が左上隅に向かう三角形で構成
- BOTTOMRIGHT — 円と頂点が右下隅に向かう三角形で構成
- BOTTOMLEFT — 円と頂点が左下隅に向かう三角形で構成
- BOTHRIGHT — 円と2つの三角形で構成(上三角は右上隅)
- BOTHLEFT— 円と2つの三角形で構成(上三角は左上隅)
図7 花弁指標の形式
実装を始めましょう。 CustomGUI/IndicatorsフォルダにPetal.mqhファイルを作成して、この新しく作成されたファイルにСPetalクラスを作成し、以前に作成されたCCanvasBaseクラスをその基本クラスとして割り当てます。そして、次のような共通リストにインクルードします。
//+------------------------------------------------------------------+ //| CustomGUI.mqh | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #include "Indicators\CircleSimple.mqh" #include "Indicators\CircleArc.mqh" #include "Indicators\CircleSection.mqh" #include "Indicators\LineGraph.mqh" #include "Indicators\LineRounded.mqh" #include "Indicators\Hexagon.mqh" #include "Indicators\Petal.mqh" //+------------------------------------------------------------------+
標準的なプロパティは抜かし、指標を作成してその値を更新するメソッドについて考えてみましょう。塗りつぶされた円を作成した後、Create() メソッドは、以前に考えられた「形式」プロパティに応じたENUM_POSITION列挙体を使用して指定された位置に塗りつぶされた三角形を描画します。
//+------------------------------------------------------------------+ //| 指標を作成する | //+------------------------------------------------------------------+ void CPetal::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."); //--- m_canvas.FillCircle(r,r,r,ColorToARGB(m_bg_color,m_transparency)); if(m_orientation==TOPRIGHT) m_canvas.FillTriangle(XSize()/2,0,XSize(),0,XSize(),YSize()/2,ColorToARGB(m_bg_color,m_transparency)); else if(m_orientation==TOPLEFT) m_canvas.FillTriangle(0,0,XSize()/2,0,0,YSize()/2,ColorToARGB(m_bg_color,m_transparency)); else if(m_orientation==BOTTOMRIGHT) m_canvas.FillTriangle(XSize(),YSize(),XSize(),YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency)); else if(m_orientation==BOTTOMLEFT) m_canvas.FillTriangle(0,YSize(),0,YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency)); else if(m_orientation==BOTHRIGHT) { m_canvas.FillTriangle(XSize()/2,0,XSize(),0,XSize(),YSize()/2,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillTriangle(0,YSize(),0,YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency)); } else if(m_orientation==BOTHLEFT) { m_canvas.FillTriangle(0,0,XSize()/2,0,0,YSize()/2,ColorToARGB(m_bg_color,m_transparency)); m_canvas.FillTriangle(XSize(),YSize(),XSize(),YSize()/2,XSize()/2,YSize(),ColorToARGB(m_bg_color,m_transparency)); } //--- 説明と数値 m_canvas.FontNameSet("Trebuchet MS"); m_canvas.FontSizeSet(m_value_font_size); m_canvas.TextOut(r,r,"-",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER); 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.Update(); }
例として蝶々型の指標を作成するためのテンプレートを開発してみましょう。
//+------------------------------------------------------------------+ //| 003.mq5 | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #property version "1.00" #property indicator_plots 0 #property indicator_chart_window #include <CustomGUI\CustomGUI.mqh> //--- CPetal ind1,ind2,ind3,ind4; //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ int OnInit() { int b=2; //--- ind1.BgColor(C'230,80,80'); ind1.Orientation(BOTHLEFT); ind1.Create("petal1",200-b,100-b); ind2.BgColor(C'35,170,190'); ind2.Orientation(BOTHRIGHT); ind2.Create("petal2",300+b,100-b); ind3.BgColor(C'245,155,70'); ind3.Orientation(BOTHRIGHT); ind3.Create("petal3",200-b,200+b); ind4.BgColor(C'90,200,130'); ind4.Orientation(BOTHLEFT); ind4.Create("petal4",300+b,200+b); 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[]) { //--- //--- 次の呼び出しのためにprev_calculatedを戻す return(rates_total); } //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ind1.Delete(); ind2.Delete(); ind3.Delete(); ind4.Delete(); } //+------------------------------------------------------------------+
図8 花弁指標の使用
CHistogramクラス
ヒストグラムクラスは、CLIneGraphクラス(より正確には座標軸、軸スケール、およびそれらの値を構築する一般的な構造)に基づいています。したがって、この段階を記述する必要はありません。ヒストグラムのタイプとCCanvasクラスプリミティブを使用した実装について詳しく説明しましょう。ヒストグラム指標の実装に移る前に、形式を定義します。
enum ENUM_TYPE_HISTOGRAM { SIMPLE=1, TRIANGLE=2, RECTANGLE=3 };
- SIMPLE — 簡単な形式は三角形のヒストグラムに基づいており(図10)、その高さはチャートの数値として固定されています。
- TRIANGLE — 疑似体積型の三角形のヒストグラム(図11)
- RECTANGLE — 列の形での標準的なヒストグラム
図10SIMPLE型ヒストグラム
図11 TRIANGLE型ヒストグラム
図12 RECTANGLE型ヒストグラム
CustomGUI/IndicatorsフォルダにHistogram.mqhファイルを作成して、この新しく作成されたファイルにСHistogram クラスを作成し、以前に作成されたCCanvasBaseクラスをその基本クラスとして割り当てます。また CustomGUI.mqh ファイルの共通リストにインクルードします。Create()など、ほとんどのクラスのメソッドとプロパティはCLineGraphクラスに似ています。したがって、基本的な違い、つまり SetArrayValue()メソッドを見てみましょう。
//+------------------------------------------------------------------+ //| 入力配列を設定する | //+------------------------------------------------------------------+ void CHistogram::SetArrayValue(double &data[]) { int x,y,y1,y2; //--- グラフフレームとバックドロップを作成する 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)); ArrayResize(m_label_value,ArraySize(data)); //--- データ配列に基づいてヒストグラムを作成する for(int i=0;i<ArraySize(data);i++) { if(data[i]>0) { x=int(m_x[i]+(m_x[i+1]-m_x[i])/2); y=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]); y1=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]*0.3); y2=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]*0.1); y=(y<m_gap)?m_gap:y; if(m_type==SIMPLE) m_canvas.FillTriangle(m_x[i],YSize()-m_gap,m_x[i+1],YSize()-m_gap,x,y,ColorToARGB(m_graph_color1,m_transparency)); else if(m_type==TRIANGLE) { m_canvas.FillTriangle(m_x[i],YSize()-m_gap,x,y,x,y1,ColorToARGB(m_graph_color1,m_transparency)); m_canvas.FillTriangle(m_x[i],YSize()-m_gap,m_x[i+1],YSize()-m_gap,x,y1,ColorToARGB(m_graph_color2,m_transparency)); m_canvas.FillTriangle(x,y,x,y1,m_x[i+1],YSize()-m_gap,ColorToARGB(m_graph_color3,m_transparency)); } else if(m_type==RECTANGLE) { int x1,x2; x1=int(m_x[i]+(m_x[i+1]-m_x[i])/3); x2=int(m_x[i]+2*(m_x[i+1]-m_x[i])/3); m_canvas.FillRectangle(x1,y,x2,YSize()-m_gap,ColorToARGB(m_graph_color1,m_transparency)); } //--- 説明と数値 m_canvas.FontNameSet("Trebuchet MS"); m_canvas.FontSizeSet(m_value_font_size); m_canvas.TextOut(x,y2,DoubleToString(data[i],2),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER); m_canvas.TextOut(x,y-5,m_label_value[i],ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER); } } m_canvas.Update(); }
基本的な相違点は、上記のENUM_TYPE_HISTOGRAMヒストグラム型が、指定されたデータ配列によってヒストグラムプロットブロック内で考慮されることです。使用例として、異なる期間の3つのRSI指標を視覚的に表示するメソッドが実装されています。
//+------------------------------------------------------------------+ //| 004.mq5 | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Alexander Fedosov" #property link "https://www.mql5.com/jp/users/alex2356" #property version "1.00" #property indicator_plots 0 #property indicator_chart_window #include <CustomGUI\CustomGUI.mqh> //+------------------------------------------------------------------+ //| 指標の入力 | //+------------------------------------------------------------------+ input int RSIPeriod1=10; // 指標1の期間 input int RSIPeriod2=14; // 指標2の期間 input int RSIPeriod3=18; // 指標3の期間 input ENUM_TYPE_HISTOGRAM Type=RECTANGLE; // ヒストグラムのタイプ //--- CHistogram ind; int InpInd_Handle1,InpInd_Handle2,InpInd_Handle3; double rsi1[],rsi2[],rsi3[],ex[3]; string descr[3]; //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ int OnInit() { //---- 指標ハンドルを取得する InpInd_Handle1=iRSI(Symbol(),PERIOD_CURRENT,RSIPeriod1,PRICE_CLOSE); InpInd_Handle2=iRSI(Symbol(),PERIOD_CURRENT,RSIPeriod2,PRICE_CLOSE); InpInd_Handle3=iRSI(Symbol(),PERIOD_CURRENT,RSIPeriod3,PRICE_CLOSE); if(InpInd_Handle1==INVALID_HANDLE || InpInd_Handle2==INVALID_HANDLE || InpInd_Handle3==INVALID_HANDLE ) { Print("Failed to get indicator handle"); return(INIT_FAILED); } //--- descr[0]="RSI("+IntegerToString(RSIPeriod1)+")"; descr[1]="RSI("+IntegerToString(RSIPeriod2)+")"; descr[2]="RSI("+IntegerToString(RSIPeriod3)+")"; ind.NumGrid(10); ind.YMax(100); ind.Type(Type); ind.Create("rsi",350,250); ind.SetArrayLabel(descr); //--- ArraySetAsSeries(rsi1,true); ArraySetAsSeries(rsi2,true); ArraySetAsSeries(rsi3,true); //--- 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_Handle1,0,0,1,rsi1)<=0 || CopyBuffer(InpInd_Handle2,0,0,1,rsi2)<=0 || CopyBuffer(InpInd_Handle3,0,0,1,rsi3)<=0 ) return(0); ex[0]=rsi1[0]; ex[1]=rsi2[0]; ex[2]=rsi3[0]; ind.SetArrayValue(ex); //--- 次の呼び出しのためにprev_calculatedを戻す return(rates_total); } //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ind.Delete(); ChartRedraw(); } //+------------------------------------------------------------------+
図13 3つのRSI期間のヒストグラムの形での指標の結果
CPyramidクラス
これまではプリミティブを使って複雑な形状を構築するためのクラスと原則を調べてきました。ヒストグラム型の指標を作成するクラスについて説明する際に、擬似3Dオブジェクトを色の選択によってプロットすることについて述べてきました(図13)。しかし、ピラミッドは平らな姿ではありません。したがって、与えられた2次元座標系で等値投影を使用する必要があります。基本的な構造にはあまり多くの要素が含まれていません(図14)が、ピラミッド投影法と可視化の方法は、クラスの実装の主要部分です。
図14 CPyramidクラスの基本構造
CustomGUI/IndicatorsフォルダにPyramid.mqhファイルを作成して、この新しく作成されたファイルにСPyramidクラスを作成し、以前に作成されたCCanvasBaseクラスをその基本クラスとして割り当てます。また CustomGUI.mqh ファイルの共通リストにインクルードします。インクルードされたクラスの一般的なリストは次のようになります。
//+------------------------------------------------------------------+ //| CustomGUI.mqh | //+------------------------------------------------------------------+ #include "Indicators\CircleSimple.mqh" #include "Indicators\CircleArc.mqh" #include "Indicators\CircleSection.mqh" #include "Indicators\LineGraph.mqh" #include "Indicators\LineRounded.mqh" #include "Indicators\Hexagon.mqh" #include "Indicators\Petal.mqh" #include "Indicators\Histogram.mqh" #include "Indicators\Pyramid.mqh" //+------------------------------------------------------------------+
プロパティとメソッドはクラス自体の中で一覧できます。重要なものに焦点を当てましょう。基本的なCreate()メソッドとNewValue()メソッドを考察する前に、その中で使われているプライベートメソッドについて考えます。
//+------------------------------------------------------------------+ //| 配列に二点を通る直線の方程式の比を書く | //+------------------------------------------------------------------+ void CPyramid::Equation(double x1,double y1,double x2,double y2,double &arr[]) { ArrayResize(arr,2); arr[0]=(y1-y2)/(x1-x2); arr[1]=y2-arr[0]*x2; }
二点を通る直線の方程式を求めるにはEquation()メソッドが使用されます。特に、従来の二点を通る直線の方程式から、一般的なy = kx + b の形での式でのkとbの比率が求められます。
このメソッドは、指定された点で直線方程式をさらに定義して、辺ABと辺ACとピラミッドの底辺に平行な直線の交点を見つけます。図15に示す点の座標は、 ピラミッドの側面と似た三角形を構成するために必要です。それは、指標のセクションです。
図15 ピラミッドの辺とピラミッドの底辺に平行な直線の交差点
2つの直線の比がわかるので、方程式系を使って交差点座標を計算します。これにはCross()メソッドが使われます。
//+------------------------------------------------------------------+ //| 2つの直線の交点の座標をそれらの比で書く | //+------------------------------------------------------------------+ void CPyramid::Cross(double k1,double b1,double k2,double b2,double &arr[]) { double x,y; ArrayResize(arr,2); y=(b1*k2-b2*k1)/(k2-k1); x=(y-b2)/k2; arr[0]=x; arr[1]=y; } //+------------------------------------------------------------------+
ピラミッドをプロットする際に使用される関数がわかったのでCreate()メソッドに完全に焦点を当てることができます。
//+------------------------------------------------------------------+ //| 指標を作成する | //+------------------------------------------------------------------+ void CPyramid::Create(string name,int x,int y) { int size=m_size; //--- 指標の位置をそのサイズに合わせて変更する x=(x<size/2)?size/2:x; y=(y<size/2)?size/2:y; Name(name); X(x); Y(y); XSize(size); YSize(size); if(!CreateCanvas()) Print("Error. Can not create Canvas."); //--- 説明と数値 m_canvas.FontNameSet("Trebuchet MS"); m_canvas.FontSizeSet(m_font_size); m_canvas.FontAngleSet(200); //--- ABの直線方程式を求める double x1,x2,y1,y2; x1=XSize()/2;y1=0; x2=0;y2=4*YSize()/5; Equation(x1,y1,x2,y2,m_line1); //--- 指標の左セクションを作成する for(int i=5;i>0;i--) { //--- 2つの点の縦座標を定義する y1=i*YSize()/5; y2=(i-1)*YSize()/5; Equation(x1,y1,x2,y2,m_line2); //--- 辺ABとピラミッド底辺に平行な直線との交点の座標を定義する Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross); m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1],m_transparency)); //--- 目盛りをプロットする m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_scale_color,m_transparency)); } //--- ACの直線方程式を求める x1=XSize()/2;y1=0; x2=XSize();y2=4*YSize()/5; Equation(x1,y1,x2,y2,m_line1); //--- 指標の右セクションを作成する for(int i=5;i>0;i--) { //--- 2つの点の縦座標を定義する y1=i*YSize()/5; y2=(i-1)*YSize()/5; Equation(x1,y1,x2,y2,m_line2); //--- 辺ACとピラミッド底辺に平行な直線との交点の座標を定義する Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross); m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1])); //--- 目盛りをプロットする m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_scale_color,m_transparency)); //--- スケールの値をプロットする m_canvas.TextOut(int(x1+2),int(y2+YSize()/6)," - ",ColorToARGB(m_label_color,m_transparency),TA_LEFT|TA_VCENTER); } m_canvas.LineVertical(XSize()/2,0,YSize(),ColorToARGB(m_scale_color,m_transparency)); m_canvas.Update(); }
ピラミッド構築アルゴリズムは次のとおりです。
- ピラミッドの左側を構築します。A点とB点の座標を定義し、それらを使って直線方程式を求めます。
- その結果、ピラミッドの底辺に平行な直線方程式を見つけ、ループで 辺ABとの交差点を見つけます。
- 得られた点を使ってセクションと目盛り(ディビジョン)を作成します。
- 同様の方法でピラミッドの右側を構築します。
- セクションと目盛りとは別に、右側に目盛り値を追加します。
- 2つのセクションの垂直方向の分離
データを設定して更新するメソッドには、現在渡された値と最大値の2つの引数があります。メソッドの本質は最大設定値に基づいて閾値を定義することです。それらを渡すと、各セクションの色が変わります。閾値を超えると、セクションは指定された色を受け取ります。値が下向きに横切られると、セクションは非アクティブとして設定された色を受け取ります。
//+------------------------------------------------------------------+ //| 指標値を設定して更新する | //+------------------------------------------------------------------+ void CPyramid::NewValue(double value,double maxvalue) { //--- double s; color col; //--- ABの直線方程式を求める double x1,x2,y1,y2; x1=XSize()/2;y1=0; x2=0;y2=4*YSize()/5; Equation(x1,y1,x2,y2,m_line1); //--- 指標の左セクションを作成する for(int i=5;i>0;i--) { //--- 目盛りの値をプロットする s=maxvalue-(i-1)*maxvalue/5; //--- セクションの色を定義する col=(value>=s)?m_section_color[i-1]:m_bg_color; //--- 2つの点の縦座標を定義する y1=i*YSize()/5; y2=(i-1)*YSize()/5; Equation(x1,y1,x2,y2,m_line2); //--- 辺ABとピラミッド底辺に平行な直線との交点の座標を定義する Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross); m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1],m_transparency)); m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(col,m_transparency)); //--- 目盛りをプロットする m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(clrWhite)); } //--- ACの直線方程式を求める x1=XSize()/2;y1=0; x2=XSize();y2=4*YSize()/5; Equation(x1,y1,x2,y2,m_line1); //--- 指標の右セクションを作成する for(int i=5;i>0;i--) { //--- 目盛りの値をプロットする s=maxvalue-(i-1)*maxvalue/5; //--- セクションの色を定義する col=(value>=s)?m_section_color[i-1]:m_bg_color; //--- 2つの点の縦座標を定義する y1=i*YSize()/5; y2=(i-1)*YSize()/5; Equation(x1,y1,x2,y2,m_line2); //--- 辺ACとピラミッド底辺に平行な直線との交点の座標を定義する Cross(m_line1[0],m_line1[1],m_line2[0],m_line2[1],m_cross); m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_section_color[i-1],m_transparency)); m_canvas.FillTriangle(int(x1),0,int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(col,m_transparency)); //--- 目盛りを定義する m_canvas.LineAA(int(x1),int(y1),int(m_cross[0]),int(m_cross[1]),ColorToARGB(m_scale_color,m_transparency)); //--- スケールの値をプロットする m_canvas.TextOut(int(x1+2),int(y2+YSize()/6),DoubleToString(s,0),ColorToARGB(m_label_color,m_transparency),TA_LEFT|TA_VCENTER); } m_canvas.LineVertical(XSize()/2,0,YSize(),ColorToARGB(m_scale_color,m_transparency)); m_canvas.Update(); }
指標のレンダリングの実装が複雑なのにもかかわらず、その作成、設定、および適用は、以前のものに比べて難しくはありません。小さな例として標準のADX値を表示するために適用しましょう。指標の位置に応じて、比較と表示のために左上に円形指標を追加します。
//+------------------------------------------------------------------+ //| 005.mq5 | //| Copyright 2017, Alexander Fedosov | //| https://www.mql5.com/jp/users/alex2356 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Alexander Fedosov" #property link "https://www.mql5.com/jp/users/alex2356" #property version "1.00" #property indicator_plots 0 #property indicator_chart_window #include <CustomGUI\CustomGUI.mqh> //+------------------------------------------------------------------+ //| 指標の入力 | //+------------------------------------------------------------------+ input int period=10; // ADX期間 input double maxval=50; // 最大値 //--- CPyramid ind1; CCircleSimple ind2; int InpInd_Handle; double adx[]; //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ int OnInit() { //---- 指標ハンドルを取得する InpInd_Handle=iADX(Symbol(),PERIOD_CURRENT,period); if(InpInd_Handle==INVALID_HANDLE) { Print("Failed to get indicator handle"); return(INIT_FAILED); } ArraySetAsSeries(adx,true); //--- ind1.Size(250); ind1.Create("pyramid",200,0); //--- ind2.FontSize(ind1.FontSize()); ind2.LabelSize(ind1.FontSize()); ind2.Label("ADX"); ind2.Radius(30); ind2.Create("label",ind1.X()-ind1.Size()/3,ind1.Y()-ind1.Size()/4); 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,2,adx)<=0) return(0); ind1.NewValue(adx[0],maxval); ind2.NewValue(adx[0]); //--- 次の呼び出しのためにprev_calculatedを戻す return(rates_total); } //+------------------------------------------------------------------+ //| カスタム指標初期化関数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ind1.Delete(); ind2.Delete(); ChartRedraw(); } //+------------------------------------------------------------------+
図16が示すように、円形指標は3番目の閾値である30を超える値を表示するので、3つのセクションは現在の最大値(この場合は50)で着色されます。
図16 ピラミッド型指標の使用
終わりに
CCanvasベースのクラスの実装は、ライブラリのグラフィックスに関した能力がかなり広範であることを証明します。このタイプの指標を表示する形と方法は、想像力によってのみ制限されます。そのような指標を、ライブラリのクラスをインクルードする形で作成及び整理することは、特別な知識や努力を必要としません。
添付されたアーカイブには、本稿に登場したすべてのファイルが含まれています。これらのファイルは、適切なフォルダに配置されています。正しい操作のためには、MQL5フォルダを端末のルートフォルダに保存してください。
本稿で使用されたアーカイブされたプログラム は次の通りです。
# |
名称 |
種類 |
説明 |
---|---|---|---|
1 |
CanvasBase.mqh | ライブラリ | 基本カスタムグラフィックスクラス |
2 |
CustomGUI.mqh | ライブラリ | すべてのライブラリクラスのインクルードリストのファイル |
3 | CircleArc.mqh | ライブラリ | CCircleArc指標クラス |
4 | CircleSection.mqh | ライブラリ | CCircleSection指標クラス |
5 | CircleSimple.mqh | ライブラリ | CCircleSimle指標クラス |
6 | LineGraph.mqh | ライブラリ | CLineGraph線グラフクラス |
7 | LineRounded.mqh | ライブラリ | CLineRoundedの丸みを帯びた線形指標クラスを含む |
8 | Hexagon.mqh | ライブラリ | CHexagon六角形指標クラスを含む |
9 | Petal.mqh | ライブラリ | CPetal花弁指標クラスを含む |
10 | Histogram.mqh | ライブラリ | CHistogramヒストグラム指標クラスを含む |
11 | Pyramid.mqh | ライブラリ | CPyramidピラミッド指標クラスを含む |
12 | CLineRounded.mq5 | 指標 | CLineRoundedクラスの実装例 |
13 | CHexagon.mq5 | 指標 | CHexagonクラスの実装例 |
14 | CPetal.mq5 | 指標 | CPetalクラスの実装例 |
15 | CHistogram.mq5 | 指標 | CHistogramクラスの実装例 |
16 | CPyramid.mq5 | 指標 | CPyramidクラスの実装例 |
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/3298
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索