【MT5/MQL5】新規ローソク足発生時のみ計算するインジケータ

この記事は約15分で読めます。

今回作成するインジケータの概要

今回は、新規ローソク足が発生したタイミングのみ計算処理を動かすインジケータを作ろうと思います。

MT5(MQL5)でインジケータを作成する場合、OnCalculate()関数の中に実装をしていくのですが、この関数は「ローソク足に変化があった場合」に呼び出されるので、ローソク足が未確定の状態で価格変動(ティック変動)があった場合(下図の青丸箇所)、全てのタイミングで呼び出されてしまいます。

インジケータを裁量トレードの補助として利用するのであれば、それでも問題ないと思いますが、過去データでの検証やEAに組み込む場合に不都合が多いので、新規ローソク足が発生した時(下図の赤丸箇所)のみ、計算処理が動くインジケータを作っていきます。

<図1>チャート説明

【サンプル1】全てのローソク足変更時にインジケータ値を計算

サンプルコード

まずは、動作を比較するために、全てのローソク足変更時(「<図1>チャート説明」の青丸箇所)に計算処理をするインジケータを作ってみます。サンプルコードは以下の通りです。

//+------------------------------------------------------------------+
//|                                              CalculateAllBar.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#property indicator_buffers  1
#property indicator_plots    1

#property indicator_type1    DRAW_LINE
#property indicator_style1   STYLE_SOLID
#property indicator_color1   clrRed
#property indicator_width1   1
double    bufHigh[];

//+------------------------------------------------------------------+
//| 【初期化関数】
//|  ・チャートの初期表示時、または時間足変更等のチャート初期化が必要な
//|    タイミングで呼び出される。
//+------------------------------------------------------------------+
int OnInit()
  {
   ArraySetAsSeries(bufHigh, true);
   ArrayInitialize(bufHigh,  0);
   SetIndexBuffer(0,         bufHigh,          INDICATOR_DATA);
   PlotIndexSetDouble(0,     PLOT_EMPTY_VALUE, 0.0);
   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[])
  {

//+------------------------------------------------------------------+
//| 過去チャートのインジケータ値を全て計算する。
//+------------------------------------------------------------------+
   for(int i=rates_total-1; i>=0; i--)
     {
      // 現在Bar位置に、1つ前のBarの高値をプロットする。
      bufHigh[i] = iHigh(Symbol(),PERIOD_CURRENT,i+1);
     }

//+------------------------------------------------------------------+
//| 今回の処理でインジケータ値を計算したBar数を返却する。
//|   → このreturn値は、次回OnCalculate関数が呼び出された際、
//|     引数のprev_calculatedに設定される。
//+------------------------------------------------------------------+

   return(rates_total);

  }

//+------------------------------------------------------------------+
Expand

コード解説

短いコードですが、簡単に解説していきます。

53~57行目

過去チャートのBar(ローソク足)数分、全てのインジケータ値を計算しています。このサンプルでは、現在Bar位置に前Barの高値をプロットしています。

「高値を使うのであれば引数のhigh[]を使えばいいのでは?」という声が聞こえてきそうですが、MQL5のhigh[]引数は、時系列データにも関わらず何故かインデックス順が逆(インデックス0のデータは最も過去のデータ)になっています。(昔、結構ハマりました・・)

MT4と同じようにhigh[]を使う場合は、ArraySetAsSeries()関数でインデックス順を変更してあげれば良いのですが、結構忘れるし面倒なので、いつもiHigh系統の関数を使用しています。

65行目

おまじないのようなコードですが、初回なので解説しておきます。

OnCalculate()関数は、returnで「今回計算したBar数」を返却する仕様になっています。次回OnCalculate()関数が呼び出された際、引数のprev_calculatedに「前回計算されたBar数」が設定されてくるため、次にどのBarから計算すればよいか分かるようになっています。

実行結果

サンプル1のインジケータを実行すると以下のように表示されます。現在Bar位置に、前Barの高値がプロットされています。ただ、前述の通り全てのティック変動時に全過去データ分の計算が実行されてしまうので、非常に重いです。相場が動いている時にインジケータを設定してみれば、いかに処理が重いかが実感できると思います。

【サンプル2】新規ローソク足発生時のみインジケータ値を計算

サンプルコード

次は、新規ローソク足が発生した時(「<図1>チャート説明」の赤丸箇所)のみ、計算処理が動くインジケータを作ります。サンプルコードは以下の通りです。

//+------------------------------------------------------------------+
//|                                          CalculateOnlyNewBar.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#property indicator_buffers  1
#property indicator_plots    1

#property indicator_type1    DRAW_LINE
#property indicator_style1   STYLE_SOLID
#property indicator_color1   clrRed
#property indicator_width1   1
double    bufHigh[];

//+------------------------------------------------------------------+
//| 【初期化関数】
//|  ・チャートの初期表示時、または時間足変更等のチャート初期化が必要な
//|    タイミングで呼び出される。
//+------------------------------------------------------------------+
int OnInit()
  {
   ArraySetAsSeries(bufHigh, true);
   ArrayInitialize(bufHigh,  0);
   SetIndexBuffer(0,         bufHigh,          INDICATOR_DATA);
   PlotIndexSetDouble(0,     PLOT_EMPTY_VALUE, 0.0);

   Print("OnInit Function Called.");

   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[])
  {

//+------------------------------------------------------------------+
//| 1. 新規チャートのインジケータ値がまだ計算されていない場合
//|   → 過去チャートのインジケータ値を計算する。
//+------------------------------------------------------------------+
   if(prev_calculated == 0)
     {
      for(int i=rates_total-1; i>=0; i--)
        {
         setBufferValue(i,false);
        }
     }

//+------------------------------------------------------------------+
//| 2. 過去チャートのインジケータ値は計算済で、
//|    前回計算済Bar数 < チャートの最大Bar数 の場合 (Liveで新規Barが発生した) 
//|    → 新規Barのインジケータ値を計算する。
//+------------------------------------------------------------------+
   else
     {
      if(prev_calculated < rates_total)
        {
         setBufferValue(0,true);
        }
     }

//+------------------------------------------------------------------+
//| 3. 同Bar内でのティック変更によって呼び出された場合
//|    → インジケータ値は計算しない
//+------------------------------------------------------------------+

// NOP

//+------------------------------------------------------------------+
//| 今回の処理でインジケータ値を計算したBar数を返却する。
//|   → このreturn値は、次回OnCalculate関数が呼び出された際、
//|     引数のprev_calculatedに設定される。
//+------------------------------------------------------------------+

   return(rates_total);

  }

//+------------------------------------------------------------------+
//| 【インデックスバッファ設定関数】
//|  ・指定されたBar位置のインジケータ値を計算する。
//+------------------------------------------------------------------+
void setBufferValue(int iCurrentBar, bool bIsLiveNewBar)
  {

// 現在Bar位置に、1つ前のBarの高値をプロットする。
   bufHigh[iCurrentBar] = iHigh(Symbol(),PERIOD_CURRENT,iCurrentBar+1);

// Liveで新規Barが発生した場合のみ、Logを出力する。
// 過去チャートのインジケータ値を計算している時は出力しない。
   if(bIsLiveNewBar)
     {
      Print("Live New Bar Created!");
     }
  }

//+------------------------------------------------------------------+
Expand

コード解説

57行目

引数のprev_calculatedを見て、今表示しているチャートでインジケータ計算が実行されたかどうかを調べています。この値が0の場合、チャートが表示されてからまだ一度もインジケータ計算が実行されていない事になるため、過去データに対してインジケータ値を計算します。

70~76行目

過去チャートのインジケータ値は計算済(prev_calculated > 0)かつ、計算済Bar数(prev_calculated)よりも、チャートの最大Bar数(rates_total)のほうが大きい場合、新規ローソク足が発生したと判断します。

新規ローソク足が発生した場合、最新bar位置(最新Bar位置は常に0になります)を引数に設定して、インデックスバッファ設定関数(setBufferValue)を呼び出します。

102行目

引数で渡されたBar位置(iCurrentBar)に、前Barの高値を設定します。

32, 107~110行目

Liveで新規ローソク足が発生した場合のみ、関数が呼び出される事を確認しています。109行目のログは、過去チャートのインジケータ値を計算している時は出力されず、Liveで新規ローソク足が発生した場合のみ出力されます。

実行結果

サンプル2のインジケータを実行すると以下のように表示されます。インジケータの表示内容自体はサンプル1と同じです。

チャートが初期化されたOnInit()関数呼び出し時のログ”OnInit Function Called.”の直後に、1回だけ”Live New Bar Created!”のログが表示されている事から、新規ローソク足が発生した場合のみ計算処理が動いているのが分かります。

こちらも、相場が動いている時にインジケータを設定してみれば、サンプル1との処理速度の違いが実感できると思います。(ただ、最近のPCは性能が良くなってきているので、CPUパワーが凄い場合は実感できないかもしれませんが・・・)

まとめ

今回は、新規ローソク足が発生したタイミングのみ計算処理を動かすインジケータを作りました。

繰り返しになりますが、過去データ検証やEAに組み込む場合には、今回の技術はかなり有効なテクニックだと思うので、お役に立てれば幸いです。

MT5(MQL5)は、MetaQutes社が力を入れているにも関わらず、なかなか普及が進まず、Googleで検索しても情報がなかなか見つからない状況で苦労しますが、サンプルコードを含んだノウハウを今後も公開していければと思います。

コメント

タイトルとURLをコピーしました