MQL4で「他の通貨ペア」や「上位足」のデータをまとめて処理するループを`for(int i=0; i<Bars; i++)`のように書いて、

`iOpen()`などで配列参照エラー(添字が配列の範囲外)が出たことはないでしょうか。

原因の多くは、`Bars`という予約変数が「現在チャートのバー数」

しか返さないのに、別の通貨ペア・時間足のデータを同じ数だけループしてしまっていることにあります。

この記事では、`Bars`予約変数と、任意の通貨ペア・時間足のバー数を取得できる`iBars()`関数の違い、

そして特定の日時が何本目のバーに当たるかを調べる`iBarShift()`関数の使い方を、

実際に動くコードで解説します。

Bars予約変数は「今開いているチャート」専用

int Bars;  //予約変数(関数ではない)。現在チャートに読み込まれているバー(ローソク足)の総数

`Bars`は関数ではなく、MT4があらかじめ用意している予約変数です。

値を代入し直すことはできず、読み取り専用として使います。

重要なのは、この値が「EAやインジケーターが実行されているチャート」

のバー数であり、コード内で別の通貨ペアや別の時間足を指定して`iOpen()`等を呼んでも、

`Bars`の値はそれに連動して変わらないという点です。

典型的な事故: マルチタイムフレーム処理でBarsを使い回す

上位足のデータを走査するようなコードで、次のように`Bars`をそのままループ回数に使ってしまうケースがあります。

//誤り: 現在チャートがM15なのに、H4のデータをBars(M15のバー数)回ループしている
for(int i=0; i<Bars; i++)
  {
   double h4open = iOpen(Symbol(), PERIOD_H4, i);
   //H4のバー数はM15よりずっと少ないため、存在しないバーを読もうとして
   //配列参照エラー(ERR_ARRAY_OUT_OF_RANGE)になることがある
  }

現在チャートの時間足によって`Bars`の値は大きく変わりますが、

`iOpen()`で指定した`PERIOD_H4`のバー数はそれとは無関係に決まります。

`Bars`は「今のチャート」専用の値であり、コード内で扱う時間足が変われば、

対応するバー数も別の関数で取得し直す必要があります。

iBars関数: 任意の通貨ペア・時間足のバー数を取得する

int iBars(
   string symbol,      //通貨ペア(NULLで現在のチャートの通貨ペア)
   int    timeframe    //時間足(0で現在のチャートの時間足)
   );

`iBars()`は、`Bars`予約変数と違い、引数で指定した通貨ペア・時間足のバー数をその場で取得できる関数です。

先ほどの例は、次のように書き直すことで実際のH4バー数に合わせたループになります。

//正しい: H4のバー数をiBars()で取得してからループする
int h4Bars = iBars(Symbol(), PERIOD_H4);
for(int i=0; i<h4Bars; i++)
  {
   double h4open = iOpen(Symbol(), PERIOD_H4, i);
  }

対象の通貨ペア・時間足の履歴データがまだサーバーからダウンロードされていない場合、

`iBars()`は0を返すことがあります。

ループの前に戻り値を確認する習慣をつけてください。

iBarShift関数: 「あの日時」が何本目のバーかを調べる

経済指標の発表時刻や、特定のイベントが起きた日時が分かっていて、

それが現在から何本前のバーに当たるかを調べたい場面があります。

この変換を行うのが`iBarShift()`です。

int iBarShift(
   string   symbol,      //通貨ペア(NULLで現在のチャートの通貨ペア)
   int      timeframe,   //時間足(0で現在のチャートの時間足)
   datetime time,        //調べたい日時
   bool     exact=false  //trueの場合、完全一致するバーがなければ-1を返す
   );

戻り値は、指定した日時を含むバーのインデックス(0が最新)です。

`exact`を省略した場合(既定値`false`)は、指定した日時がバーとバーの間(市場が閉まっている時間帯など)に当たっても、

直前の最も近いバーのインデックスを返します。

`exact=true`にすると、完全に一致するバーが存在しない場合は`-1`が返るため、

「その時刻ちょうどのバーがあるかどうか」自体を判定したい場合に使います。

実際に動くコード全体

現在チャートのBarsと、指定した通貨ペア・時間足のiBarsを両方表示し、

24時間前の日時がH1チャートで何本目のバーに当たるかをiBarShiftで調べて表示する、

実際にMetaEditorでコンパイルできるスクリプトです。

//+------------------------------------------------------------------+
//|                                        BarsAndBarShiftDemo.mq4    |
//+------------------------------------------------------------------+
#property strict
#property script_show_inputs

input string TargetSymbol    = "";//空欄なら現在の通貨ペア
input int    TargetTimeframe = PERIOD_H1;//調べたい時間足

void OnStart()
  {
   string symbol = (TargetSymbol == "") ? Symbol() : TargetSymbol;

   //①現在チャートのバー数(予約変数)
   Print("現在チャートのBars=", Bars);

   //②任意の通貨ペア・時間足のバー数(関数)
   int targetBars = iBars(symbol, TargetTimeframe);
   if(targetBars == 0)
     {
      Print(symbol, " のこの時間足のヒストリーデータが未取得の可能性があります。");
      return;
     }
   Print(symbol, " Period=", TargetTimeframe, " のiBars=", targetBars);

   //③24時間前の日時が、その時間足で何本目のバーかを調べる
   datetime target = TimeCurrent() - 24 * 60 * 60;
   int shift = iBarShift(symbol, TargetTimeframe, target, false);
   if(shift < 0)
     {
      Print("iBarShiftで該当バーが見つかりませんでした。");
      return;
     }

   datetime barTime = iTime(symbol, TargetTimeframe, shift);
   Print("24時間前(", TimeToString(target, TIME_DATE|TIME_MINUTES),
         ")は shift=", shift,
         " のバー(バー開始時刻=", TimeToString(barTime, TIME_DATE|TIME_MINUTES), ")");
  }
Copy

MetaEditorでの実行手順

  1. MT4のMetaEditorで「新規作成」→「スクリプト」を選ぶ
  2. `BarsAndBarShiftDemo`として保存し、コードを貼り付ける
  3. F7でコンパイルし、エラー・警告が0件であることを確認する
  4. 任意のチャートへ実行し、「エキスパート」タブに3行のログが出ることを確認する
  5. TargetTimeframeをPERIOD_H4やPERIOD_D1に変えて、shiftの値が変わることを確認する

よくあるエラーと勘違い

  • 配列参照エラーになる:別通貨ペア・別時間足のループ回数に`Bars`をそのまま使っている(現在チャートのバー数と一致しない)
  • `iBars()`が0を返す:その通貨ペア・時間足のヒストリーデータがまだ端末にダウンロードされていない(チャートを一度開いて読み込ませる必要がある)
  • `iBarShift()`が`-1`を返す:`exact=true`にした状態で、完全に一致する開始時刻のバーが存在しない
  • `iBarShift()`の結果が想定とずれる:`exact`を省略(既定`false`)している場合、市場が閉まっている時間帯の日時を渡すと直前バーに丸められることを忘れている

まとめ

`Bars`予約変数は「今のチャート」専用の値であり、コード内で別の通貨ペア・時間足を扱う場合はループ回数として使えません。

任意の通貨ペア・時間足のバー数が必要なら`iBars()`、

特定の日時が何本目のバーかを知りたいなら`iBarShift()`を使う、

と役割で覚えておくと、マルチタイムフレーム処理での配列参照エラーを避けやすくなります。

関連記事