2018年7月7日土曜日
[MQL4] INDICATOR_CALCULATIONS を有効にするには IndicatorBuffers を使う
@MetaTrader 4 (Version 4.00 Build 1090) MT4 のカスタムインジケーターでは計算用のバッファーを用意する機能があり、計算の途中結果等を保存しておくことが出来ます。 当然、それは非表示になる…はずなのですが、普通に作ると何故か表示されてしまうのです。 これを想定通りに動作させるには少し工夫が必要なので書いておきます。 ### 途中計算が表示されてしまうバージョン 途中計算を必要とするインジケーターを真面目に作ると複雑になってしまうので、単純移動平均(SMA)から少し値を引くだけのインジケーターを作ってみました 。 ```mq4 `highlight: [3, 4, 22]; #property strict #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 1 //--- plot Label1 #property indicator_type1 DRAW_LINE #property indicator_color1 clrRed #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- input parameters input int MaPeriod=14; // Period //--- indicator buffers double Line0[]; double Dummy[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { SetIndexLabel(0, StringFormat("Test (%d)", MaPeriod)); SetIndexBuffer(0, Line0); SetIndexBuffer(1, Dummy, INDICATOR_CALCULATIONS); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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 <= MaPeriod) { return 0; } int num_uncalculated = rates_total - prev_calculated; if (prev_calculated > 0 && num_uncalculated == 0) { num_uncalculated = 1; // 最新の足は必ず更新する } for (int i = num_uncalculated - 1; i >= 0; i--) { if (i + MaPeriod + 1 > rates_total) { Line0[i] = EMPTY_VALUE; } else { double sum = 0; if (i + MaPeriod + 1 == rates_total) { for (int j = 0; j < MaPeriod; j++) { sum += Close[i + j]; } } else { sum = Line0[i + 1] * MaPeriod; sum -= Close[i + MaPeriod]; sum += Close[i]; } Line0[i] = sum / MaPeriod; Dummy[i] = Line0[i] - 0.01; } } //--- return value of prev_calculated for next call return rates_total; } ``` ポイントは `indicator_buffers` と `indicator_plots` で用意するバッファーの数と表示するラインの数を指定しているところです。 さらに、`SetIndexBuffer` で `INDICATOR_CALCULATIONS` を指定して、計算用バッファであることを明示しています 。 これで `Dummy` の値は表示されないはずなのですが、実際にチャートに適用させてみると以下のようになりました。 よく見るとわかるのですが、実際の SMA(赤線)の下に黒い線が描かれています。 さらに、Data Window にも以下のように Dummy の値が表示されてしまいました。 ### 表示させないようにする いろいろ検証してみた結果、`indicator_buffers` と `indicator_plots` が内部で混同されているようです。 なので、以下のように `indicator_buffers` の値を `indicator_plots` と同じにします。 ```mq4 `first-line: 3; highlight: 3; #property indicator_buffers 1 #property indicator_plots 1 ``` このままだとバッファが足りなくて以下のエラーが出てしまいます。 > array out of range in 'Test.mq4' そこで、`OnInit()` の中で動的にバッファの数を増やします。 ```mq4 `first-line: 18; highlight: 20; int OnInit() { IndicatorBuffers(2); SetIndexLabel(0, StringFormat("Test (%d)", MaPeriod)); SetIndexBuffer(0, Line0); SetIndexBuffer(1, Dummy, INDICATOR_CALCULATIONS); return(INIT_SUCCEEDED); } ``` これで、想定通り Dummy が表示されなくなります。 ### MQL5 由来の機能は疑ってみる MQL4 は Build 600 以降、MQL5 の構文を取り込んで来たわけですが、一部の動作は完全移植とはいかないようです。 特に `#property` の動作はかなり不安定な部分があり、動作を疑ってかかった方がよいところも多々あります。