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), ")");
}
MetaEditorでの実行手順
- MT4のMetaEditorで「新規作成」→「スクリプト」を選ぶ
- `BarsAndBarShiftDemo`として保存し、コードを貼り付ける
- F7でコンパイルし、エラー・警告が0件であることを確認する
- 任意のチャートへ実行し、「エキスパート」タブに3行のログが出ることを確認する
- TargetTimeframeをPERIOD_H4やPERIOD_D1に変えて、shiftの値が変わることを確認する
よくあるエラーと勘違い
- 配列参照エラーになる:別通貨ペア・別時間足のループ回数に`Bars`をそのまま使っている(現在チャートのバー数と一致しない)
- `iBars()`が0を返す:その通貨ペア・時間足のヒストリーデータがまだ端末にダウンロードされていない(チャートを一度開いて読み込ませる必要がある)
- `iBarShift()`が`-1`を返す:`exact=true`にした状態で、完全に一致する開始時刻のバーが存在しない
- `iBarShift()`の結果が想定とずれる:`exact`を省略(既定`false`)している場合、市場が閉まっている時間帯の日時を渡すと直前バーに丸められることを忘れている
まとめ
`Bars`予約変数は「今のチャート」専用の値であり、コード内で別の通貨ペア・時間足を扱う場合はループ回数として使えません。
任意の通貨ペア・時間足のバー数が必要なら`iBars()`、
特定の日時が何本目のバーかを知りたいなら`iBarShift()`を使う、
と役割で覚えておくと、マルチタイムフレーム処理での配列参照エラーを避けやすくなります。
コメント