カスタムインジケータの作成
ここでは、最初のステップとして、ローソク足の終値をラインで結ぶだけのカスタムインジケータを作成してみます。
Meta Editor 上で Control + N
を押して、Custom Indicator を選択すると、カスタムインジケータのファイルを新規作成することができます。
下記は、終値を結ぶラインを引く簡単なインジケーターの実装例です。
各パートの説明
プロパティ設定
このプロパティでは、カスタムインジケータをローソクチャート上に重ねて表示すること、ラインを 1 本だけ使用することを宣言しています。
仮に、別ウィンドウでインジケータを表示したい場合は、indicator_chart_window
の代わりに indicator_separate_window
を指定します。
上記のプロパティでは、表示用の 1 本目のラインの設定を行っています。ここではラインが 1 本だけですが、複数のラインを表示するカスタムインジケータを作る場合は、各プロパティ名のサフィックスを 2、3、4 と増やしていきます。
OnInit 関数
最初に一度だけ呼ばれる OnInit
関数の中では、配列 gBuffer
を、画面表示用のバッファ (INDICATOR_DATA
) として割り当てています。
このバッファに値を設定することで、画面上にインジケータが表示されることになります。
デフォルトで INDICATOR_DATA
が使われるので、SetIndexBuffer(0, gBuffer)
と省略して記述することもできます。
もし、計算用としてだけ使うバッファを追加で割り当てたい場合は、INDICATOR_CALCULATIONS
というタイプを指定して割り当てます(この場合、プログラム先頭部分の indicator_buffers
プロパティの値を 2 に増やします)。
OnCalculate 関数
計算のメイン部分となるのが OnCalculate
関数です。
OnCalculate
関数は、tick(価格の更新)が発生するごとに呼び出されます。
チャート上のローソク足の数や、始値、終値などの情報がパラメーターとして渡されるので、これらの値を使って指標の値を計算します。
OnCalculate
関数のパラメーター(抜粋)int rates_total
… ローソク足の数const datetime &time[]
… ローソク足が確定した時刻(time[0] が最新のローソク足)const double &open[]
… ローソク足ごとの始値(open[0] が最新のローソク足)const double &high[]
… ローソク足ごとの高値(high[0] が最新のローソク足)const double &low[]
… ローソク足ごとの安値(low[0] が最新のローソク足)const double &close[]
… ローソク足ごとの終値(close[0] が最新のローソク足)
価格などを示すデータは、配列の形で渡されます。
配列のインデックス 0 が最新のローソク足の価格を表しています(直感とは逆かもしれないので注意)。
ローソク足の本数は rates_total
で渡されるので、配列のインデックスとしては、0
(最新の値)〜 rates_total-1
(一番古い値)の範囲で指定することができます。
例えば以下のような感じです。
close[rates_total - 1]
… チャート上の左端のローソク足の終値close[0]
… チャート上の右端のローソク足の終値
以下のようにすべてのローソク足の終値を、インジケータ用のバッファにそのまま設定してやることで、終値を結ぶインジケータを表示しています。
実は、OnCalculate
関数が呼び出されるたびに gBuffer[]
のすべての値を再設定する必要はありません。
前回の呼び出しでセットした値は、gBuffer[]
に保持されているためです。
次に、これを利用した最適化方法を説明します。
カスタムインジケータの OnCalculate 関数を最適化する
カスタムインジケータの計算部分を担う OnCalculate
関数は、価格の変化(NewTick イベント)ごとに呼び出されますが、prev_calculated
パラメーターの値をうまく使うことで、すでに計算済みの値を再計算しないでも済むように処理を最適化できます。
下記の OnCalculate
実装では、インジケータ用のバッファー (gBuffer
) に、各ローソク足の終値をセットしています。
パラメータで渡される prev_calculated
の値には、前回の OnCalculate
関数の戻り値として返した値が入ってきます。
つまり、OnCalculate
関数の戻り値として、いくつのローソク足の計算を終えたか (= rates_total
) を返しておくことで、次回の OnCalculate
関数のパラメータ prev_calculated
でその値を受け取ることができるということです。
OnCalculate
関数が呼び出された時、gBuffer
配列には、すでに prev_calculated
の数だけ計算結果が格納されています。
例えば、現在のローソク足の数が 5 本 (rates_total=5
) で、前回の計算数が 4 本 (prev_calculated=4
) である場合、gBuffer
の構成は下記のようになります。
gBuffer
の要素が 1 つシフトしていることに注意してください。前回の OnCalculate()
で計算した gBuffer[0]
〜gBuffer[3]
の値は、今回の呼び出し時点では gBuffer[1]
〜gBuffer[4]
の位置に保持されています。常にインデックス 0 が最新の要素になるということです。
通常の配列は勝手に値がシフトしていくことはありませんが、gBuffer
は、OnInit
関数の中で SetIndexBuffer
を使ってデータバッファとして設定したため、このような動作をすることになります。
rates_total
にはローソク足の総数、prev_calculated
には計算済みの数が入っているので、新たに計算すべきローソク足の本数は下記のように計算することができます。
例えば、新しく計算しなければならない足の本数が 10 本ならば、changed
が 10 になります。
最初はひとつも計算していない状態 (prev_calculated == 0
) なので、ローソク足の総数 (rates_total
) の分すべて計算する必要があります。
2 回目以降の計算では、すでに前回の計算結果が保持されている (prev_calculated != 0
) なので、その分だけ計算すべきローソク足の数は減ります。
ここで、1 を足しているのは、最新のローソク足の分を常に再計算するためです。
なぜなら、OnCalculate
関数はティック(価格変化)ごとに呼び出されるので、多くの場合最新のローソク足の終値が更新されているからです。