MеtaTrader 5 チャート上の水平図
Andrei Novichkov | 4 3月, 2019
イントロダクション
水平方向の図は、ターミナルのチャート上で一般的ではありませんが、使用することができます。例えば、特定の期間のボリュームや価格分布を表示するインジケータを開発するとき、様々なマーケットデプスのバージョンを作成するときなどです。 カスタム (標準) インジケータ値の分布に関するよりエキゾチックなタスクがあるかもしれません。 しかし、どのような場合でも、作成、配置、拡大縮小、移動、削除する必要があるダイアグラムは共通の特徴を備えています。 次の点を強調してみましょう。
- いくつかの図があるかもしれません (ほとんどの場合そうです)。
- 興味があるほとんどの図は、足で構成されます。
- ダイアグラム足は水平方向に配置されます。
このような図がどのように見えるかを確認するために、よく知られた例を使用してみましょう。
以下に別の例を示します。 グラフィカルなプリミティブによって描画される同じダイアグラム:
この場合は、日によるティックボリューム分布を表示するインジケータです。 この例では、開発者が解決すべきタスクを明確に示しています。
- 複数のグラフィカルオブジェクトを作成し、一意の名前を割り当てて、チャート上に配置します。
- 必要に応じてオブジェクトをスケールおよび移動します。
- インジケータのタスクが完了したら、チャートから削除します。
もう一度、図の配列について話すことができるように、図があることに注意してください。
直近の例を次に示します。
ここでは、ティックボリューム分布のより複雑なバージョンを見ることができます。 図が全体を見ているという事実それにも関わらず、1つの場所にある3つの異なる図で構成されています。
- "売りティックボリューム"
- "買いティックボリューム"
- "合計ティックボリューム "
"データを表示する簡単な方法がないかと疑問に思うかもしれません。 このような多数のグラフィカルなプリミティブを管理する必要があるでしょうか。より簡単な方法で、その効率を分析する必要があります。 ただし、この記事の冒頭で説明した一連のタスク全体を解決する最も簡単な方法は、例に示されている水平方向のダイアグラムを使用することです。
タスクの設定
2つの主要な部分で計画の概要を説明しましょう。
- すべてのグラフィカルなプリミティブは、時間と価格の形でバインディングの座標があるので、チャート上の図を配置することができ、バインディングの配列を受け取る必要があることは明白です。
- 第一段階で得られた配列を用いて、図を表示し、管理する必要があります。
例として、ケースを使用してグラフィカルオブジェクトをバインドする主な方法を定義してみましょう。 最初のスクリーンショットは、おそらく、水平方向の図の最も一般的な場所を示します。 一定の期間の開始にバインドされています。 ここでは、日の始まりです。
もちろん、時間座標のバインドオプションのリストを排出しません。 別のオプションは、ターミナルウィンドウの左側または右側にバインドします。 たとえば、ダイアグラムが長い期間を扱い、その先頭が可視ウィンドウの外側にある場合などに使用できます。 その場合は、ウィンドウの左側へのバインドを使用できます。
さらに別のオプションでは、チャートのタスク部分上のオブジェクトの過剰な数を回避しながら、現在の期間に図をバインドします。 この場合、ターミナルの右側を使用することができます。 いずれの場合も、ダイアグラムを構成するグラフィカルプリミティブの時間座標の1つが同じままです。 別の座標 (図の "列の横の長さ " を計算するもの) が計算されます。
このケースは、グラフィカルなプリミティブの価格バインディングに関してははるかに簡単です。 一定の価格区分は、固定ステップを有する間隔に分割されます。 たとえば、価格セグメントは、ステップが 10% に等しい間、100パーセントに等しいと仮定することができます。 この場合、一定数の「水平足」を持つダイアグラムを取得して、結果をかなり丸めてしまう場合があります。 したがって、この記事では、より効率的な方法を適用します。
以上のことから、この場合、価格バインディングの配列は必要ないかもしれないと結論づけることができます。 次のシンプルな式を使用して、i 番目の価格バインディングを計算できます。
i番目価格バインディング = 図の価格間隔 + i * 間隔分割ステップの開始。
ティックボリュームを使用して既に提供されている例では、ダイアグラムが構築される価格間隔は、検査期間の Low とHighの間の間隔で表されます。
図の表示の問題も詳しく説明する必要があります。 このように、「1つのダイアグラム-バインド用の配列の1つのセット」という原則に従って、ダイアグラムのバインドを持つ配列を取得しました。 チャートは、多くの場合、同じ色と "style " (例えば、2番目と最初のスクリーンショットの図) の類似のグラフィカルなプリミティブに基づいて、図を備えています。 3番目のスクリーンショットでは、すべての図が色と "方向" によって異なります。 1つのダイアグラムには、右から左への「ディレクテッド」という列があります。 「同じタイプ 」の図(最初の2つのスクリーンショットのように) を1つの配列に結合し、制御するマネージャを割り当てるのは論理的なことです。 次の原則を使用して、タスクをさらに標準化してみましょう。
- セットが1つのダイアグラムで構成されている場合でも、 "同じ型 " ダイアグラムの各セットには独自のマネージャが必要です。 したがって、1つのマネージャーによって管理される少なくとも3つのダイアグラムの配列は最初の2つのスクリーンショットに作成されますが、3番目のスクリーンショットのダイアグラムでは、3つの配列 (それぞれが1つのダイアグラムで構成) と3つのマネージャー (各配列に1つ) を作成する必要があります。
そこで、主な開発ポイントを概説しました。 しかし、ここでは基本的な特異性があります。 覚えているかもしれませんが、水平方向の図にはティック量や価格などのさまざまな分布が表示されます。 したがって、グラフィカルなプリミティブをバインドするためのソース配列を取得する方法と原則は異なる場合があり、タスクのこの部分に対してライブラリファイルを作成するのは妥当ではありません。
特に、日によるティック量分布のトレーニングインジケータを開発する場合は、より効率的なアプローチを使用します。 この記事では、さらに別の方法を提供します。 つまり、タスクの最初の部分は、各機会に異なる方法で解決されます。 逆に、タスクの2番目の部分 (図の配列を作成し、この配列を使うか、または "マネージャ-ダイアグラムの配列 " アソシエーション) を開発することは、すべての場合でほぼ同じです。 水平方向の図を含むすべてのプロジェクトに含まれるライブラリファイルを作成できます。
最後に指定する必要があるのは、図が正確に構成されていることです。 図は、水平線セグメントまたは長方形のいずれかで構成されます。 2つの最も自然なオプションです。
次に、コードに直接進みましょう。
定数とインプット
前述の図のパラメータの多くは、列挙型を使用して格納されることは明らかです。
図の場所:
enum HD_POSITION { HD_LEFT = -1, HD_RIGHT = 1, HD_CNDLE = 2 };
3つのダイアグラム配置オプション—ターミナルの左側 (HD_LEFT) にバインドし、右の1つ (HD_RIGHT) にバインドし、ロウソク足 (HD_CNDLE) またはロウソク足にバインドします。 この記事の冒頭にある3つのスクリーンショットでは、HD_CNDLE を使用してダイアグラムが配置されています。 最初の2つでは、バインディングは、特定の期間 (1 日の開始) の開始時にロウソク足に実行され、3番目のものでは、バインディングは、現在の日の開始時にある単一のロウソクに行われます。
図の外観 (グラフィカルなプリミティブ):
enum HD_STYLE { HD_LINE = OBJ_HLINE, HD_RECTANGLE = OBJ_RECTANGLE, };
2つの外観オプション—水平線セグメント (HD_LINE) と長方形 (HD_RECTANGLE) があります。 記事の冒頭にある最初と3番目のスクリーンショットでは、図は HD_LINE のプリミティブで構成され、2番目のものでは HD_RECTANGLE でできています。
図の方向 "水平足 ":
enum HD_DIRECT { HD_LEFTRIGHT = -1, HD_RIGHTLEFT = 1 };
3番目のスクリーンショットでは、赤のセグメントで構成されるダイアグラムは HD_RIGHTLEFT として表示され、他の2つは HD_LEFTRIGHT として示されます。
直近の列挙型は、ダイアグラムレベルの数に関連付けられています。 すでに最高の方法は、レベルの指定された数に価格の間隔のシンプルな分割ではなく、レベルの数を計算するために使用することを述べてきました。 この列挙型は Setting a task の最初の部分に関連しているため、最終的なライブラリファイルには含まれないことに注意してください。
適用されたメソッドは簡単で、価格レベルを最も近い10または100に丸めます。 したがって、価格レベルのステップサイズも10または100です。 この方法では、価格レベルの数が異なります。 最大計算通貨 (リソースの消費量の増加と共に) を取得しようとする人には、丸めなしの HD_MIN メソッドが残ります。
enumHD_ZOOM { HD_MIN = 0, //1 HD_MIDDLE = 1, //10 HD_BIG = 2 //100 };
HD_MIDDLE メソッドは、デフォルトで適用されます。
次の例を考えてみましょう。 開発オブジェクトとしてティック量分布を表示するトレーニングインジケータを使用します。 同様のインジケータのタスクは、記事の冒頭にある最初の2つのスクリーンショットの例として提供されています。
インプットブロックに移動してみましょう:
input HD_STYLE hdStyle = HD_LINE; input int hdHorSize = 20; input color hdColor = clrDeepSkyBlue; input int hdWidth = 2; input ENUM_TIMEFRAMES TargetPeriod = PERIOD_D1; input ENUM_TIMEFRAMES SourcePeriod = PERIOD_M1; input HD_ZOOM hdStep = HD_MIDDLE; input int MaxHDcount = 5; input int iTimer = 1;
ポジション決めを行うパラメータがないのはなぜでしょうか。 答えは明白です。 必要なインジケータ-HD_CNDLE には、1つのポジショニングメソッドだけが適用されます。 したがって、未指定のままにしてもよいです。
HD_STYLE パラメータ関数は明白であり、追加の明確化は必要ありません。
- hdHorSize パラメータは重要な役割を果たします。 ロウソクのダイアグラムの "水平足 " の最大サイズを定義します。 この場合、最も長い "水平列 " は20ロウソク足を超えることはできません。 このパラメータが大きいほど、ダイアグラムの精度が高くなることは明らかです。 ただし、パラメータが大きすぎると、ダイアグラムは互いに重なり合い始めます。
- hdColor および hdWidth パラメータは、図の外観 (応じて色と線幅) を扱います。
- TargetPeriod には分析された時間枠が含まれます。 この場合、インジケータは1日以内にティック量の分布を表示します。
- SourcePeriod パラメータには、インジケータが分布を構築するためにソースデータを取得する時間枠があります。 この場合、M1 時間枠が使用します。 このパラメータは注意して使用してください。 月単位の時間枠が分析対象として選択されている場合、計算に時間がかかることがあります。
- hdStep パラメータは、価格レベルを丸めます。 すでに上記を説明しました。
- MaxHDcount パラメータには、チャート上のダイアグラムの最大数があります。 各図は複数のグラフィカルなプリミティブで構成されていることを覚えておいてください。 図が多すぎるとターミナルの動作が遅くなることがあります。
- iTimer パラメータには、タイマーのトリガー頻度があります。 トリガーすると、新しいロウソク足の作成がチェックされ、必要なアクションが実行されます。 PeriodSeconds (SourcePeriod) の呼び出し結果がここに配置されている可能性があります。 しかし、デフォルト値は1秒で、新しいロウソクがより正確に表示される正確なモーメントを決定することができます。
初期化
この段階では、ダイアグラムマネージャのオブジェクトを作成する必要があります。 すべてのダイアグラムが同じタイプであるため、管理者は1人だけ必要です。 manager 自体のクラスはまだ書かれていないので、ここでは OnInit() ハンドラで作成されていることを覚えておいてください。 ここでは、2つのダイアグラムも作成されます (ただし描画はありません)。
- 現在の期間のティック数量分布を表示するダイアグラム。 このダイアグラムは定期的にリペイントされます。
- ヒストリーにティックのボリューム分布を表示する図 分布はリペイントされないので、図は表示された後について「忘れて」、ターミナルに制御が与えられます。
アプローチの言及された効率は、インジケータが外観が変化しないダイアグラムのグラフィカルなプリミティブを管理していないということです。
次に、変数はステップで価格レベルのトレーリングの計算に初期化されます。 そのような変数は2つあります。 Digit() と Point() から派生します。
hdDigit = Digits() - (int)hdStep; switch (hdStep) { case HD_MIN: hdPoint = Point(); break; case HD_MIDDLE: hdPoint = 10 * Point(); break; case HD_BIG: hdPoint = 100 * Point(); break; default: return (INIT_FAILED); }
マイナーなアクションとタイマーの起動もこのハンドラで行われます。
基本的な計算
次のタスクは、2つの段階に分かれています。
- 必要なデータを計算し、現在のものを除いて必要な数のダイアグラムを描画します。 ダイアグラムは以上変更されず、一度表示するだけで十分です。
- 現在のものを含む期間の必要なデータと図面図を計算します。 この点の計算は、定期的に繰り返す必要があります。 OnTimer() ハンドラで行うことができます。
擬似コードの形式で、OnCalculate() ハンドラでタスクを実行してみましょう。
int OnCalculate(...) { if (prev_calculated == 0 || rates_total > prev_calculated + 1) { }else { if (!bCreateHis) { int br = 1; while (br < MaxHDcount) { { if(Calculate for bar "br") { sdata.bRemovePrev = false; Print("Send data to the new Diagramm"); } } ChartRedraw(); bCreateHis = true; } } return(rates_total); }
必要な計算が実行され、現在のもの以外の必要な数のダイアグラムがここに描画されます。 これを達成するために、計算は最初から始まり、MaxHDcount で終わる TargetPeriod 時間枠の各足のループで実行されます。 計算が成功した場合は、マネージャーにコマンドを指定して、新しいデータを同じループ内のマネージャーに渡すようにダイアグラムを描画します。 ループ全体の最後に、ダイアグラムがリペイントされ、タスクのこの部分が不要になったことを示すフラグが設定されます。 ダイアグラム自体がターミナルによって制御されるようになりました。
現在の期間を含むダイアグラムの作成と描画は、OnTimer() ハンドラで実行されます。 問題の明白なシンプルさと明確性にここで擬似コードを表示するつもりはありません:
- SourcePeriod の時間枠で新しいロウソク足を待ちます。
- 必要な計算を実行します。
- 新しいプリミティブを作成および描画するために、現在の期間の図にデータを送信します。
その他のハンドラとインジケータ関数はさほど面白くないですが、添付されたコードで使用できます。 次に、ダイアグラムの作成、描画、および管理を担当するクラスについて説明します。
ダイアグラム管理クラス
水平図を扱うマネージャから始めましょう。 このようなプリミティブを含む他のクラスの配列を管理しながら、グラフィカルなプリミティブ自体を機能させないクラスです。 すべてのダイアグラムは1つのマネージャで同じタイプであるため (前述のとおり)、そのようなダイアグラムの多くのプロパティは同じです。 したがって、各ダイアグラムで同じプロパティのセットを保持することは意味がありません。 代わりに、マネージャーにプロパティの1つのセットを配置する価値があります。 マネージャクラスに "CHDiags" という名前をつけ、コードの記述を開始します。
- CHDiags クラスの閉じたフィールドには、このマネージャの制御下にあるすべてのダイアグラムで同じプロパティのセットがあります。
private: HD_POSITION m_position; HD_STYLE m_style; HD_DIRECT m_dir; int m_iHorSize; color m_cColor; int m_iWidth; int m_id; int m_imCount; long m_chart; datetime m_dtVis; static const string m_BaseName; CHDiagDraw* m_pHdArray[];
セットの説明:
- m_position、m_style、m_dir —3つのパラメータは、ダイアグラムのバインディングと外観を記述します。 これすでに説明しました。
- m_iHorSize —ダイアグラムの可能な最大の水平サイズ。 同様に説明しました。
- m_cColor と m_iWidth-ダイアグラムの色と線の幅。
- m_id —一意のマネージャー id。 複数存在する可能性がある場合は、それぞれに一意の ID を指定する必要があります。 一意のオブジェクト名を形成する必要があります。
- m_chart —ダイアグラムが表示されているチャートの ID。 デフォルトでは、このフィールドの値はゼロ (現在のチャート) です。
- m_imCount —チャート上のダイアグラムの最大数。 最終的には、チャート上のダイアグラムの数は、次のフィールドによって決まります。
- m_dtVis —このタイムスタンプの左側に図を作成しません。
- m_BaseName — "base" の名前を定義する重要なパラメータです。 ダイアグラムのすべての要素、およびダイアグラム自体には、正常に作成される一意の名前を指定する必要があります。 このような名前は、 "base " の名前の後に与えられます。
- m_pHdArray [] —個々のダイアグラムを含むオブジェクトへのポインタを持つ配列です。 このフィールドはプロパティではなく、GetXXXX() 関数はありません。
プロパティに SetXXXX() 関数はありません。 すべて (m_BaseName を除く) は、クラスコンストラクタで設定されます。 m_dtVis フィールドは別の例外です。 次の意味を持つ bool 型パラメータによってコンストラクタで設定されます。
- ターミナルウィンドウに表示されているロウソクにのみ図を表示します。 左側の端子の境界線の左側にある図を表示することによって、ターミナルをロードしないようにするために行われます。 デフォルトは ' true ' です。
マネージャを作成したら、オブジェクト (図) を作成できます。 CHDiags クラスメソッドによって行われます。
int CHDiags::AddHDiag(datetime dtCreatedIn)
このメソッドは、作成された CHDiagDraw クラスオブジェクトのインデックスをマネージャーの m_pHdArray 配列に返すか、エラーが発生した場合は-1 を返します。 ダイアグラムの開始タイミングは、パラメータとして dtCreatedIn メソッドに渡されます。 例えば、日のロウソク足オープン時間は、ここで分析されたインジケータに渡されます。 タイムスタンプが使用されていない場合 (ロウソク足はターミナルウィンドウの境界にバインドされます)、TimeCurrent() はここで渡されるべきです。 図が m_dtVis フィールドのタイムスタンプの左側にある場合、オブジェクトは作成されません。 次のコードは、メソッドの動作を示します。
int CHDiags::AddHDiag(datetime dtCreatedIn) { if(dtCreatedIn < m_dtVis ) return (-1); int iSize = ArraySize(m_pHdArray); if (iSize >= m_imCount) return (-1); if (ArrayResize(m_pHdArray,iSize+1) == -1) return (-1); m_pHdArray[iSize] = new CHDiagDraw(GetPointer(this) ); if (m_pHdArray[iSize] == NULL) { return (-1); } return (iSize); }//AddHDiag()
ご覧のように、メソッドはチェックを実行し、ダイアグラムを保存する配列のサイズを増やし、後でプロパティにアクセスするために、マネージャー自体へのポインタを渡す目的のオブジェクトを作成します。
このマネージャには、ダイアグラムマネージャを使用して直接ではなく、ダイアグラムを対話的に操作できる他の方法もあります。
bool RemoveDiag(const string& dname); void RemoveContext(int index, bool bRemovePrev); int SetData(const HDDATA& hddata, int index);
- 最初の方法は、名前から明らかなように、図表の名前をパラメータとして使用して、図表をチャートから完全に削除します。 現在、これは予約済みのオプションです。
- 2つ目は、ダイアグラムが構成するグラフィカルプリミティブのみを削除します。 ダイアグラムはチャートから削除されますが、マネージャには存在しますが、 "empty" です。 bRemovePrev フラグ値がさらに明確化されます。
- 3番目のメソッドは、グラフィカルなプリミティブを作成し、ダイアグラムを描画するためのインプットデータを使用して構造体を渡します。 マネージャの m_pHdArray 配列のダイアグラムインデックスは、現在のメソッドと以前の方法のパラメータとして使用します。
言及する価値がある直近のメソッドは、CHDiags クラスメソッドです。
void Align();
このメソッドは、ダイアグラムがターミナルウィンドウの左辺または右辺にバインドされている場合に呼び出されます。 この場合、メソッドは、CHARTEVENT_CHART_CHANGE イベントの OnChartEvent ハンドラで、ダイアグラムを前の場所に返すように呼び出されます。
CHDiags ダイアグラムマネージャクラスの他のメソッドは補助的なものであり、添付ファイルで使用できます。
図のグラフィカルなプリミティブを描画および管理するためのクラス
このクラスを "CHDiagDraw" と呼び、CObject から派生させます。 クラスコンストラクタで、マネージャへのポインタを取得します (m_pProp フィールドに保存します)。 ここでは、一意のダイアグラム名も定義されています。
次に、Type() メソッドを実装する必要があります。
int CHDiagDraw::Type() const { switch (m_pProp.GetHDStyle() ) { case HD_RECTANGLE: return (OBJ_RECTANGLE); case HD_LINE: return (OBJ_TREND); } return (0); }
表示されるダイアグラムの種類は、適用されるグラフィカルプリミティブの種類に一致しますが、極めて論理的です。
CHDiagDraw クラスの主な計算は、マネージャーの SetData メソッドによって呼び出されるメソッドによって実行されます。
int SetData(const HDDATA& hddata);
このメソッドの目的は、ダイアグラムのサイズを定義し、チャートの特定のポイントで必要な数のプリミティブを作成することです。 これを実現するために、構造体インスタンスへのリンクがコールポイントに渡されます。
struct HDDATA { double pcur[]; double prmax; double prmin; int prsize; double vcur[]; datetime dtLastTime; bool bRemovePrev; };
構造フィールドについて詳しく説明します。
- pcur [] —図の価格レベルの配列。 作成されたグラフィカルプリミティブの場合、価格バインディングの配列です。
- prmax —ダイアグラムで使用できる水平方向の最大値。 この場合、特定のレベルでトレードされるティックボリュームの最大値です。
- prmin —予約済みパラメータ。
- prsize —ダイアグラムレベルの数。 つまり、ダイアグラムが構成するプリミティブの数です。
- vcur [] —ダイアグラムの足の「水平サイズ」を定義する値の配列。 この場合、配列には、pcur [] 配列の適切なレベルでトレードされるティック・ボリュームがあります。 pcur および vcur 配列のサイズは、prsize と一致し、等しい必要があります。
- dtLastTime —ダイアグラムの場所。 グラフィカルなプリミティブの場合、時間バインディングです。 このフィールドは、マネージャーの AddHDiag メソッドの引数よりも高い優先順位を持ちます。
- bRemovePrev — ' true ' の場合、ダイアグラムは完全にリペイントされ、前のグラフィカルプリミティブは削除されます。 ' false ' に設定すると、ダイアグラムは以前のグラフィカルなプリミティブの管理をストップし、「忘れる」かのように削除せずに新しいダイアグラムを描画します。
SetData メソッドのコードは、その重要性に完全に以下に提供されています。
int CHDiagDraw::SetData(const HDDATA &hddata) { RemoveContext(hddata.bRemovePrev); if(hddata.prmax == 0.0 || hddata.prsize == 0) return (0); double dZoom=NormalizeDouble(hddata.prmax/m_pProp.GetHDHorSize(),Digits()); if(dZoom==0.0) dZoom=1; ArrayResize(m_hItem,hddata.prsize); m_hItemCount=hddata.prsize; int iTo,t; datetime dtTo; string n; double dl=hddata.pcur[0],dh=0; GetBorders(hddata); for(int i=0; i<hddata.prsize; i++) { if (hddata.vcur[i] == 0) continue; t=(int)MathCeil(hddata.vcur[i]/dZoom); switch(m_pProp.GetHDPosition()) { case HD_LEFT: case HD_RIGHT: iTo=m_iFrom+m_pProp.GetHDPosition()*t; dtTo=m_pProp.GetBarTime(iTo); break; case HD_CNDLE: iTo = m_iFrom + m_pProp.GetHDDirect() * t; dtTo = m_pProp.GetBarTime(iTo); break; default: return (-1); }//switch (m_pProp.m_position) n=CHDiags::GetUnicObjNameByPart(m_pProp.GetChartID(),m_hname,m_iNameBase); m_iNameBase++; bool b=false; switch(m_pProp.GetHDStyle()) { case HD_LINE: b=CHDiags::ObjectCreateRay(m_pProp.GetChartID(),n,dtTo,hddata.pcur[i],m_dtFrom,hddata.pcur[i]); break; case HD_RECTANGLE: if(dl!=hddata.pcur[i]) dl=dh; dh=(i == hddata.prsize-1) ? hddata.pcur[i] :(hddata.pcur[i]+hddata.pcur[i+1])/2; b = ObjectCreate(m_pProp.GetChartID(),n,OBJ_RECTANGLE,0,dtTo,dl,m_dtFrom,dh); break; }//switch(m_pProp.m_style) if(!b) { Print("ERROR while creating graphic item: ",n); return (-1); } else { m_hItem[i]=n; ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_COLOR, m_pProp.GetHDColor() ); ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_WIDTH, m_pProp.GeHDWidth() ); ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_SELECTABLE, false); ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_BACK, true); }//if (!ObjectCreateRay (n、dtTo、hddata pcur [i]、m_dtFrom、hddata pcur [i]) }//(int = 0;< l;=""> return (hddata.prsize); }//int CHDiagDraw:: SetData (const HDDATA & HDDATA)
このメソッドが最初に行うことは、クリーンアップしてスケールを計算することです。 また、ダイアグラムの最大長である「水平足」と、ローソク足でのダイアグラムの最大水平サイズも機能します。 比例比率を得ることができます。 図のサイズ "ロウソク" が整数であるため、丸めた結果として適用されることはわかりやすいです。
次に、プリミティブの名前を格納する配列を用意します。 追加のダイアグラムバインドパラメータが計算されます—ロウソク足インデックスと一時的なバインディング、GetBorders メソッドです。 ダイアグラムの2回目のバインドは、その後のループで定義されます。 したがって、グラフィカルなプリミティブを作成するためのすべてのバインディングを使用できます。 プリミティブの一意の名前を取得し、受信したパラメータを使用して順番に作成します。 プリミティブの名前が配列に保存され、そのプロパティが修正されます。 ダイアグラムが作成されます。 このメソッドは、ダイアグラムレベルの数を返します。
おそらく、メソッドが長すぎるように見えます。 プリミティブを作成してレンダリングするためのコードを、別のプロテクトメソッドに移動することもできます。 しかし、このメソッドの両方のパートは、有機的に互換性があるように見え、第2の部分は、最初の明確な継続です。 この考察は、多数の引数を持つ新しいメソッドの複数の追加の呼び出しを作成することに不本意ではなく、SetData メソッドが現在の形で開発されたという事実につながりました。
CHDiagDraw クラスの他の関数は、補助とマネージャーとの統合に使用します。
ユーザーは、CHDiagDraw クラスのメソッドを直接呼び出すことはありません。 代わりに、水平ダイアグラムマネージャを使用して排他的に動作します。
上記の水平ダイアグラムマネージャクラス、ダイアグラム描画クラス、構造体、および列挙型のコード全体は、添付された HDiagsE ファイルで使用できます。
EAコードに戻り、擬似コードを使用せずにその内容を詳細に確認できるようになりました。
インジケータに戻る
グローバルコンテキストで2つのオブジェクトと変数を宣言します。
CHDiags *pHd; int iCurr, iCurr0; HDDATA sdata;
ダイアグラムマネージャー、ダイアグラムのインデックス、およびダイアグラムにデータを渡すための構造体へのポインタです。
manager と両方のダイアグラムは、インジケータインプットを使用して OnInit() ですぐに作成されます。
pHd = new CHDiags(HD_CNDLE, hdStyle, HD_LEFTRIGHT, hdHorSize, hdColor, hdWidth, 0, MaxHDcount); if(pHd == NULL) return (INIT_FAILED); iCurr = pHd.AddHDiag(TimeCurrent() ); if(iCurr == -1) return (INIT_FAILED); iCurr0 = pHd.AddHDiag(TimeCurrent() ); if(iCurr0 == -1) return (INIT_FAILED);
以下は OnCalculate ハンドラの一部で、ここでは最近擬似コードを使用する必要がありました。
{ int br=1; while(br<MaxHDcount) { if(PrepareForBar(br++,sdata)) { sdata.bRemovePrev = false; if(iCurr!=-1) { Print(br-1," diag level: ",pHd.SetData(sdata,iCurr)); } } } ChartRedraw(); bCreateHis=true; }
ディストリビューション (TargetPeriod) が構築されている時間枠から選択された足のデータで HDDATA 型構造体を埋める関数を考慮する必要があります。
bool PrepareForBar(int bar, HDDATA& hdta) { hdta.prmax = hdta.prmin = hdta.prsize = 0; int iSCount; datetime dtStart, dtEnd; dtEnd = (bar == 0)? TimeCurrent() : iTime(Symbol(), TargetPeriod, bar - 1); hdta.dtLastTime = dtStart = iTime(Symbol(), TargetPeriod, bar); hdta.prmax = iHigh(Symbol(), TargetPeriod, bar); if(hdta.prmax == 0) return (false); hdta.prmax = (int)MathCeil(NormalizeDouble(hdta.prmax, hdDigit) / hdPoint ); hdta.prmin = iLow(Symbol(), TargetPeriod, bar); if(hdta.prmin == 0) return (false); hdta.prmin = (int)MathCeil(NormalizeDouble(hdta.prmin, hdDigit) / hdPoint ); iSCount = CopyRates(Symbol(), SourcePeriod, dtStart, dtEnd, source); if (iSCount < 1) return (false); hdta.prsize = (int)hdta.prmax - (int)hdta.prmin + 10; ArrayResize(hdta.pcur, hdta.prsize); ArrayResize(hdta.vcur, hdta.prsize); ArrayInitialize(hdta.pcur, 0); ArrayInitialize(hdta.vcur, 0); double avTick; int i, delta; hdta.prmax = 0; for (i = 0; i < hdta.prsize; i++) hdta.pcur[i] = (hdta.prmin + i) * hdPoint; int rs = 0; for (i = 1; i < iSCount; i++) { if (source[i].tick_volume == 0.0) continue; if (!MqlRatesRound(source[i], (int)hdta.prmin) ) continue; delta = (int)(source[i].high - source[i].low); if (delta == 0) delta = 1; avTick = (double)(source[i].tick_volume / delta); int j; for (j = (int)source[i].low; j <= (int)(source[i].low) + delta; j++) { if (j >= hdta.prsize) { Print("Internal ERROR. Wait for next source period or switch timeframe"); return false; } hdta.vcur[j] += avTick; if (hdta.vcur[j] > hdta.prmax) hdta.prmax = (int)hdta.vcur[j]; }//for (int j = (int)source[i].low; j <= (int)(source[i].low) + delta; j++) if (j > rs) rs = j; //real size }//(int i = 1;< iscount;=""> hdta.prsize = rs + 1; return (true); }
最初に、メソッドは計算を実行する期間を見つけます。 ここでは、タスク価格範囲の境界を計算し、さらに図面のレベルに分割することも考慮します。
その後、新たに指定された期間のソースデータとして取得した時間枠からMqlRatesを読みます。 得られた各 MqlRates 構造について、tick_volume が安値から高値までの範囲で均等に分布していると考えられます。 未来のダイアグラム全体の価格範囲の境界は既にわかっているため、ティックのボリューム分布はダイアグラム内に配置されます。 これより、求められた期間内のティック量分布の配列が形成されます。
計算は、ティック量分布の配列の実際のサイズを定義することで終了し、したがって、未来の図におけるグラフィカルなプリミティブの数を示します。
したがって、ダイアグラムのデータ構造フィールドは、SetData (..) メソッドを使用して、格納され、渡す準備が整います。
一般に、記述されたクラスの操作は次のようになります。
- HDiagsE.mqhの接続
- "同じタイプ " の図の各グループごとに1つのマネージャオブジェクトを作成します。
- AddHDiag マネージャーメソッドを呼び出して、ダイアグラムを作成します。 このメソッドは、manager に認識されている配列内のインデックスを返します。
- RemoveContext マネージャーメソッドを呼び出して、無関係なデータのダイアグラムをクリアします。 新しいデータは、SetData マネージャーメソッドを呼び出し、データを含む HDDATA 型構造体を渡すことによって、ダイアグラムに渡されます。 この構造の分野を正しく埋めるための責任は、発信側によります。
- 必要に応じて、整列マネージャーメソッドを呼び出すことによって、ダイアグラムをターミナルウィンドウの左側または右側に揃えることができます。
- すべてのダイアグラムは、CHDiags マネージャークラスのデストラクターで破棄されます。
完全なインジケータコードは、添付された VolChart.mq5 ファイルに記載されています。 ライブラリファイル HDiagsE.mqhも以下に添付されています。
名前
水平図に関連するほとんどすべてのオブジェクトには名前があります。 次のように階層的に形成されます。
- マネージャは、 "ベース名 " を定義する m_BaseName プライベートフィールドを備えています。 残りのすべての名前は、そこから始まります。
- マネージャオブジェクトを作成すると、一意の ID が割り当てられます。 呼び出し元のコードは、このパラメータの一意性を担当します。 m_BaseName フィールドと ID は、マネージャーの名前を形成します。
- ダイアグラムオブジェクトを作成するときは、マネージャ1に基づいて一意の名前も受け取ります。
- 最後に、ダイアグラムオブジェクトで作成されたグラフィカルプリミティブは、プリミティブを特徴とするダイアグラムオブジェクトの名前に基づいて一意の名前を取得します。
必要なオブジェクトを整理し、管理することができます。
さらに別のインジケータ
すでに開発されたインジケータを、ターミナルウィンドウの右側に配置されたダイアグラムとしてティックボリュームの分布を表示するのにやり直してみましょう。
これを行うには、コードを少し変更します。
- 初期化コードは少し変更する必要があります
pHd = new CHDiags(HD_RIGHT, hdStyle, HD_RIGHTLEFT, hdHorSize, hdColor, hdWidth, 0, MaxHDcount); if(pHd == NULL) return (INIT_FAILED); iCurr = pHd.AddHDiag(TimeCurrent() ); if(iCurr == -1) return (INIT_FAILED);
- OnCalculate ハンドラからすべてのコードを削除します。
- OnChartEvent ハンドラを追加します。
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { switch (id) { case CHARTEVENT_CHART_CHANGE: pHd.Align(); break; default: break; }//switch (id) }
結論
1つのファイルを有効にして、水平方向の図を作成できるようになりました。 開発者のタスクは、主に現在の記事のトピックの外にある構築のデータの準備があります。 ライブラリファイルのコードは、予約されたメソッドのおかげで可能な改善を示唆します。 他のグラフィカルなプリミティブは、描画に追加することができます。
添付のインジケータはデモンストレーションとトレーニングのみを目的としていることを忘れないでください。 実際のトレードでは使用しないでください。 特に、データソースとして使用するタイムフレームにはアーティファクトが存在しないことに注意してください。
本稿で使用するプログラムとファイル
# | 名前 |
タイプ |
詳細 |
---|---|---|---|
1 | VolChart.mq5 | インジケータ |
ティック量分布インジケータ. |
2 |
HDiagsE.mqh | ライブラリファイル |
水平ダイアグラムマネージャと水平ダイアグラムを含むライブラリファイル。 |
3 |
VolChart1.mq5 |
インジケータ |
[ボリューム分配インジケータ] は、ターミナルウィンドウの右側に配置されています。 |