2018年6月7日木曜日

[MQL4] カスタムインジケーターを最新値で更新する方法2種

2019/06/05 追記
コードを少し修正しました。 理由については以下にまとめてあります。

カスタムインジケーターを更新する際、最新値だけでは足りない

MQL のカスタムインジケーターは便利ですが、 作り方を間違えると不思議な動作をするので注意が必要です。

今回は最新(右端)のインジケーターの値が更新されない問題について書いてみたいと思います。

NG な例

まず問題点を説明するために、単純に終値を結ぶだけのカスタムインジケーターを作ってみました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot Close
#property indicator_label1  "Close"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double Buffer[];
 
int OnInit()
{
    SetIndexBuffer(0,Buffer);
    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 (rates_total == 0) {
        return 0;
    }
 
    int num_uncalculated = rates_total - prev_calculated;
    for (int i = num_uncalculated - 1; i >= 0; i--) {
        Buffer[i] = close[i];
    }   
 
    // return value of prev_calculated for next call
    return rates_total;
}

これをチャートに適用すると以下のように描画されました。

一見、正しく動作しているように見えますが、黄色の丸の中をよく見ると、値が終値からずれてしまっています。

これは、描画した瞬間の終値で値が確定し、その後更新されないことに起因します。

このインジケーターをリアルタイムチャートに適用させ続けると以下のようになります。

よく見てください。終値に追従しなければならないのに、始値に張り付いています。

描画した瞬間、つまり足が出来た瞬間は「始値=終値」なので、このような動作になってしまうわけです。

解決方法1 - 戻り値を変更する

OnCalculate() の戻り値は次の計算時に prev_calculated として与えられます

つまり、この値を rates_total - 1 にすることで、常に num_uncalculated が 1 以上になり、再計算が行われます。

40
41
42
    // return value of prev_calculated for next call
    return rates_total - 1;
}

解決方法2 - 最新値を常に計算する

以下のように num_uncalculated が 0 の場合も Buffer[0] が更新されるようにします。

35
36
37
38
39
40
41
42
    int num_uncalculated = rates_total - prev_calculated;
    for (int i = num_uncalculated; i >= 0; i--) {
        Buffer[i] = close[i];
    }
 
    // return value of prev_calculated for next call
    return rates_total;
}

なお、Buffer[0] だけを強制的に更新すると、タイミングによっては値が最新にならない可能性があります。

参考

カスタムインジケーターを更新する際、最新値だけでは足りない | Strategy of C

どちらを使ったら良いか?

どちらも、最終的に行われる計算は同じですし、 どちらを使っている例も見かけるので、どちらでも良いかと思います。

ただ、更新すべき足が複数本になる場合1は、方法2の方が良いかもしれません。 更新すべき足の本数を直前で決められるからです。

2018/6/8 追記

[MQL4] マルチタイムフレーム対応インジケーターの作り方 を書きました。 この中で複数本を常に更新するサンプルを公開しています。

  1. マルチタイムフレーム対応のインジケーター等 
?