[MQL4] マルチタイムフレーム対応インジケーターの作り方
iBarShift() を使うと比較的簡単に作ることが出来ます。
iBarShift() の基本的な使い方
まずは、iBarShift() の動作を確認するために、MA(移動平均線)のマルチタイムフレーム対応版を作ってみます。
ただし、このインジケーターは正しく動作しません。 修正は後ほどしますので、まずは一度見てみてください。
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 43 44 45 46 47 48 49 50 51 | #property strict #property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 //--- plot #property indicator_type1 DRAW_LINE #property indicator_color1 clrAqua #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- input parameters input ENUM_TIMEFRAMES TimeFrame; // 対象の時間軸 input int MaPeriod; input int MaShift; input ENUM_MA_METHOD MaMethod; input ENUM_APPLIED_PRICE MaAppliedPrice; //--- indicator buffers double Buffer1[]; int OnInit() { // indicator buffers mapping SetIndexBuffer (0, Buffer1); SetIndexLabel (0, StringFormat ( "MA(%d) [%s]" , MaPeriod, EnumToString (TimeFrame))); 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--) { int shift = iBarShift ( NULL , TimeFrame, time[i]); Buffer1[i] = iMA ( NULL , TimeFrame, MaPeriod, MaShift, MaMethod, MaAppliedPrice, shift); } return rates_total; } |
iBarShift() で「対象のタイムフレーム(TimeFrame
)における shift
」を計算し、その値を「対象のタイムフレームのインジケーターiMA()」に適用します。
以下が実際の動作画面です。(4時間チャートに日足のMAを表示)
6本毎に値が更新されているのがわかるかと思います。
なお、iMA() の代わりに iCustom() を使用する事もできるので、あらゆるインジケーターのマルチタイムフレーム化が可能です。
最新値が更新されない問題を修正する
カスタムインジケーターを作り慣れた人だと気づいたと思いますが、上記の方法だと最新値が更新されません。
参考
そこで、最新値を更新するように修正します。 参考リンクでは最新の1本だけ更新していますが、マルチタイムフレームだと複数本更新しなければいけないので、少し工夫が必要です。
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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[]) { int num_need_recalculate = TimeFrame / Period (); // 簡易バージョン if (rates_total < num_need_recalculate) { return 0; } int num_uncalculated = rates_total - prev_calculated; for ( int i = MathMax (num_need_recalculate - 1, num_uncalculated - 1); i >= 0; i--) { int shift = iBarShift ( NULL , TimeFrame, time[i]); Buffer1[i] = iMA ( NULL , TimeFrame, MaPeriod, MaShift, MaMethod, MaAppliedPrice, shift); } return rates_total; } |
num_need_recalculate
が毎回必ず更新する足の本数です1。
例えば、「4時間チャートに日足」の場合だと 24 ÷ 4 = 6本 ということになります。
この方法、常に最大本数を更新してしまうので少し効率が悪いです。 例えば、上記画像の瞬間を考えると、本来最新の2本だけ更新すれば良いのに、さらに4本更新してしまうわけです。
さらに厳密に改造してみる
上記の例でも実用上問題ないのですが、せっかくなのでさらに改造してみます。
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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[]) { int shift_need_recalculate = iBarShift ( NULL , Period (), iTime ( NULL , TimeFrame, 0)); if (rates_total <= shift_need_recalculate) { return 0; } int num_uncalculated = rates_total - prev_calculated; for ( int i = MathMax (shift_need_recalculate, num_uncalculated - 1); i >= 0; i--) { int shift = iBarShift ( NULL , TimeFrame, time[i]); Buffer1[i] = iMA ( NULL , TimeFrame, MaPeriod, MaShift, MaMethod, MaAppliedPrice, shift); } return rates_total; } |
iBarShift() を使うと、対象の最新足に現在の時間軸で何本入るのかも求める事が出来ます2。
これにより、shift_need_recalculate
を求める事が出来ます。
なお、あえて変数の役割を変更し、名前も変更しました。shift
は num
より1小さい値をとるのでご注意ください。
この方法、より厳密にはなりましたが、実はそれほどパフォーマンスには影響しません。 iBarShift() や iTime() を使用している分、場合によっては遅くなるかもしれない3ので、どちらを使用するかは判断の難しいところです。