1. MQL5「array out of range」エラーの意味
MQL5で開発を行っていると、ログやコンパイル結果、またはStrategy Testerの実行中に次のようなエラーが表示されることがあります。
array out of range
このエラーは、配列の範囲外(存在しないインデックス)にアクセスしようとしたときに発生する実行時エラーです。
プログラムにおける「配列(array)」とは、複数のデータを連続して格納するデータ構造です。MQL5でも価格データやインジケータ値を扱う際に頻繁に利用されます。しかし、配列には必ず有効なインデックス範囲があり、その範囲を超えてアクセスするとエラーになります。
たとえば、要素数が10の配列がある場合、使用できるインデックスは次の通りです。
0 〜 9
ここで 10 などのインデックスにアクセスすると、配列の範囲外となり array out of range エラーが発生します。
このエラーは、EA(Expert Advisor)開発やインジケータ開発において非常に頻繁に発生する典型的なミスのひとつです。特に次のような処理で発生しやすい傾向があります。
- CopyBufferで取得したデータの参照
- インジケータバッファ処理
- forループでの配列処理
- ArrayResize忘れ
- バー数不足の状態でのデータ参照
初心者にとっては「コード自体は正しく見えるのにエラーが出る」ため、原因を特定しにくいケースも少なくありません。
1.1 エラーメッセージの例
MQL5では、このエラーは主に実行時エラー(Runtime Error)としてログに表示されます。代表的な表示例は次の通りです。
array out of range in 'ExampleEA.mq5' (123,15)
このメッセージの意味は以下の通りです。
- ExampleEA.mq5
エラーが発生したファイル - 123
エラーが発生した行番号 - 15
行内の位置
つまり、「配列の範囲外アクセスが発生した場所」を示しています。
多くの場合、エラーの原因となるコードは次のような形になっています。
double price[10];
void OnTick()
{
Print(price[10]);
}
このコードでは配列のサイズが 10 ですが、配列インデックスは 0から始まるため、使用できるのは price[0] から price[9] までです。
そのため price[10] にアクセスすると、存在しない要素を参照することになり、array out of range エラーが発生します。
1.2 配列範囲外アクセスとは何か
配列範囲外アクセスとは、配列に存在しない位置のデータを読み書きしようとする操作を指します。
たとえば次のようなケースが典型例です。
ケース1:配列サイズを超えた参照
double arr[5];
arr[5] = 1.0;
この配列の有効範囲は
arr[0] 〜 arr[4]
です。そのため arr[5] は範囲外になります。
ケース2:forループの条件ミス
double arr[10];
for(int i=0; i<=10; i++)
{
arr[i] = i;
}
このコードでは <= を使っているため、最後に arr[10] にアクセスしてしまいます。
正しくは次のように書く必要があります。
for(int i=0; i<10; i++)
{
arr[i] = i;
}
このような ループ条件のミスは、EA開発でも非常によく発生します。
1.3 なぜMQL5でよく発生するのか
MQL5では、他のプログラミング言語と比較しても 配列処理が多い環境です。主な理由は次の通りです。
1. 価格データが配列で扱われる
例えば次のデータはすべて配列です。
- 時系列価格データ(OHLC)
- インジケータバッファ
- CopyBuffer取得データ
- CopyRates取得データ
そのため 配列操作を避けることができません。
2. データ数が変動する
EAやインジケータでは、次のようにデータ数が変動します。
- バー数
- CopyBuffer取得数
- バックテスト期間
つまり、固定サイズの配列を使うと簡単に範囲外アクセスが発生します。
3. 自動サイズ調整が行われない
MQL5では、次のような関数は 配列サイズを自動拡張しません。
CopyBuffer()CopyRates()CopyTime()
そのため、事前に ArrayResize() などで配列サイズを確保する必要があります。
つまずきやすいポイント
初心者が特に間違えやすいポイントは次の通りです。
1. 配列は0から始まる
0〜size-1
が有効範囲です。
2. ArrayResizeを忘れる
動的配列の場合、サイズ確保を忘れるとエラーになります。
3. CopyBufferの戻り値を確認しない
取得できたデータ数を確認しないと、範囲外アクセスが発生します。
4. forループの条件ミス
<=
を書いてしまうミスは非常に多いです。
2. 「array out of range」が発生する主な原因
MQL5で「array out of range」エラーが発生する原因は一つではありません。実際のEA開発やインジケータ開発では、複数の典型パターンが存在します。
ここでは、実務で特に多い原因を整理します。
多くの場合、次のいずれかに該当します。
- 配列サイズより大きいインデックス参照
- 配列サイズの確保忘れ
- CopyBufferの取得数ミス
- インジケータバッファ初期化ミス
- バー数不足による参照
これらは初心者だけでなく、経験者でもミスが起こりやすいポイントです。
2.1 配列サイズより大きいインデックスを参照
最も基本的な原因は、配列サイズより大きいインデックスにアクセスするケースです。
例:
double data[5];
void OnTick()
{
Print(data[5]);
}
この配列のサイズは 5 ですが、実際に使用できるインデックスは次の範囲です。
data[0]
data[1]
data[2]
data[3]
data[4]
つまり 最大インデックスは size-1 です。
そのため data[5] にアクセスするとエラーになります。
よくある失敗
次のようなループ条件のミスは非常に多いです。
for(int i=0;i<=ArraySize(data);i++)
{
Print(data[i]);
}
このコードは 最後に範囲外アクセスが発生します。
正しい書き方
for(int i=0;i<ArraySize(data);i++)
{
Print(data[i]);
}
2.2 配列サイズを設定していない(ArrayResize忘れ)
MQL5では、動的配列はサイズを明示的に確保する必要があります。
次のコードは典型的なミスです。
double buffer[];
buffer[0] = 1.0;
この配列はまだサイズが確保されていないため、アクセスするとエラーになります。
正しい書き方
double buffer[];
ArrayResize(buffer,10);
buffer[0] = 1.0;
注意点
動的配列を使用する場合は必ず
ArrayResize()
または
CopyBuffer()
CopyRates()
などでサイズを確保する必要があります。
2.3 CopyBuffer / CopyRates の取得数ミス
MQL5では、インジケータ値を取得するために CopyBuffer() をよく使用します。
しかし、この関数は 取得数と配列サイズが一致していないとエラーの原因になります。
例:
double ma[];
CopyBuffer(handle,0,0,100,ma);
Print(ma[50]);
もし ma のサイズが100未満だった場合、範囲外アクセスになります。
安全な書き方
double ma[];
ArrayResize(ma,100);
int copied = CopyBuffer(handle,0,0,100,ma);
if(copied > 0)
{
Print(ma[0]);
}
重要ポイント
CopyBuffer() は
- 配列サイズを自動調整しない
- 取得できるデータ数が環境により変わる
という特徴があります。
2.4 インジケータバッファの初期化ミス
カスタムインジケータでは、インジケータバッファの設定ミスでもエラーが発生します。
例:
double Buffer1[];
SetIndexBuffer(0,Buffer1);
この場合、OnCalculate内でサイズ処理を行わないと範囲外アクセスが発生することがあります。
特に次の処理が必要です。
- rates_total確認
- prev_calculated利用
- インデックス境界チェック
2.5 OnCalculate / OnTick のバー数処理ミス
EAやインジケータでは、バー数不足が原因でエラーが発生するケースも多いです。
例:
double ma[];
CopyBuffer(handle,0,0,10,ma);
Print(ma[5]);
しかし、まだチャートに 10本のバーが存在しない場合、配列サイズが不足します。
安全な書き方
int bars = Bars(_Symbol,_Period);
if(bars < 10)
return;
このように バー数チェックを行うことが重要です。
よくある失敗
実務EAで多いミスは次の通りです。
1. Bars数チェックをしない
バックテスト初期でエラーが発生します。
2. CopyBufferの戻り値を確認しない
CopyBuffer() == -1
になるケースがあります。
3. ArraySetAsSeriesを理解していない
時系列配列ではインデックス方向が逆になります。
4. インジケータ読み込み直後にアクセス
iCustom()の直後はデータが未生成のことがあります。
3. 最も多いケース:CopyBufferでのarray out of range
MQL5で「array out of range」エラーが発生する原因の中で、最も多いのが CopyBuffer() 使用時のミスです。
特にEA開発では、移動平均やRSIなどのインジケータ値を取得するために CopyBuffer() を頻繁に使用するため、この部分で配列範囲エラーが発生しやすくなります。
CopyBufferは便利な関数ですが、配列サイズの管理を自動で行わないため、正しく扱わないと簡単に範囲外アクセスが発生します。
3.1 CopyBufferの典型エラーコード
次のコードは初心者がよく書くパターンです。
int handle;
double ma[];
handle = iMA(_Symbol,_Period,20,0,MODE_SMA,PRICE_CLOSE);
CopyBuffer(handle,0,0,100,ma);
Print(ma[10]);
一見すると問題なさそうですが、このコードには大きな問題があります。
ma[] のサイズが確保されていない
MQL5では、動的配列はサイズが0の状態で宣言されるため、そのままアクセスするとエラーになります。
つまり次のような状態です。
ma = サイズ0
そこに ma[10] を参照すると、存在しない要素を読み込もうとして array out of range が発生します。
3.2 正しい配列確保の方法
CopyBufferを安全に使用するためには、配列サイズを事前に確保する必要があります。
例:
int handle;
double ma[];
handle = iMA(_Symbol,_Period,20,0,MODE_SMA,PRICE_CLOSE);
ArrayResize(ma,100);
CopyBuffer(handle,0,0,100,ma);
Print(ma[10]);
ここでは次の処理を行っています。
ArrayResize()で配列サイズを確保CopyBuffer()でデータ取得- 配列アクセス
これにより、範囲外アクセスのリスクを減らすことができます。
3.3 CopyBufferの安全な書き方(推奨パターン)
実務EAでは、次のように 取得結果を確認してからアクセスする書き方が安全です。
int handle;
double ma[];
handle = iMA(_Symbol,_Period,20,0,MODE_SMA,PRICE_CLOSE);
ArrayResize(ma,100);
int copied = CopyBuffer(handle,0,0,100,ma);
if(copied > 0)
{
Print(ma[0]);
}
このコードでは次の安全対策を行っています。
- 配列サイズ確保
- CopyBufferの戻り値チェック
- 配列アクセス前確認
CopyBuffer() の戻り値は 取得できたデータ数を返します。
>0 → 正常取得
0 → データなし
-1 → エラー
そのため、戻り値を確認せずに配列アクセスするとエラーの原因になります。
注意点
CopyBuffer使用時には、次の点を必ず確認してください。
1. 配列サイズを確保しているか
ArrayResize()
を忘れるとエラーになります。
2. 戻り値を確認しているか
環境によってはデータが取得できない場合があります。
3. インジケータ生成直後はデータがない
次のコードは危険です。
handle = iMA(...);
CopyBuffer(handle,0,0,10,ma);
インジケータがまだ計算されていない場合があります。
4. バー数不足
バックテスト初期では、必要なバーが存在しないことがあります。
よくある失敗
CopyBuffer関連で頻発するミスは次の通りです。
配列サイズを確保していない
double ma[];
CopyBuffer(...);
戻り値チェックをしていない
CopyBuffer(...)
Print(ma[0])
取得数より大きいインデックスを参照
Print(ma[50])
ArraySetAsSeriesの理解不足
時系列配列ではインデックス方向が変わります。
CopyBuffer使用の安全テンプレート
EA開発では、次のようなテンプレートを使うと安全です。
double ma[];
ArrayResize(ma,10);
int copied = CopyBuffer(handle,0,0,10,ma);
if(copied <= 0)
return;
double currentMA = ma[0];
この書き方は
- 配列サイズ確保
- データ取得確認
- 安全アクセス
の3点を満たしています。
4. array out of range を防ぐ基本パターン
MQL5で array out of range を防ぐには、場当たり的にエラー箇所を修正するのではなく、最初から配列アクセスを安全に書くことが重要です。
特にEAやインジケータでは、価格データ数、取得バー数、インジケータ計算状況が常に一定ではないため、「たまたま今は動くコード」ではなく「条件が変わっても壊れにくいコード」にする必要があります。
この章では、初心者でもそのまま使いやすい 基本の防御パターン を整理します。
4.1 ArrayResizeでサイズ確保
動的配列を使う場合、最初に確認すべきことは 配列サイズが本当に確保されているか です。
次のコードは危険です。
double values[];
values[0] = 1.23;
この時点では values[] のサイズは0なので、values[0] は範囲外アクセスになります。
安全な書き方は次の通りです。
double values[];
ArrayResize(values, 10);
values[0] = 1.23;
ArrayResize(values, 10); により、values[0] から values[9] まで使用可能になります。
注意点
- 動的配列は、宣言しただけでは使えません
- 使う前に
ArrayResize()でサイズを確保する必要があります CopyBuffer()やCopyRates()の前でも、必要に応じて事前確保を入れる方が安全です
よくある失敗
double arr[];を宣言しただけで使ってしまう- 必要数より小さいサイズで
ArrayResize()する - あとで使う最大インデックスを考えずにサイズを決める
4.2 ArraySizeで境界チェック
配列アクセス時は、今その配列に何個の要素があるか を確認してから使うと安全です。
そのために使うのが ArraySize() です。
例:
double values[];
ArrayResize(values, 5);
int size = ArraySize(values);
Print(size);
この場合、size は 5 です。
この値を使えば、範囲内かどうかを明確に判定できます。
int index = 3;
if(index >= 0 && index < ArraySize(values))
{
Print(values[index]);
}
この形なら、index が範囲外ならアクセス自体を行いません。
実務での考え方
配列にアクセスする前に、次の条件を満たしているか確認します。
index >= 0index < ArraySize(array)
この2つを満たしていないなら、読まない・書かない、が基本です。
よくある失敗
- 配列サイズを固定値で決め打ちする
ArraySize()を使わず、想定だけでアクセスする- データ取得数が変動する配列に対して固定のインデックスを書く
4.3 forループの安全な書き方
array out of range が最も起きやすい場所の一つが forループ です。
特に <= と < の違いでミスが起きます。
危険な例:
double values[10];
for(int i = 0; i <= 10; i++)
{
values[i] = i;
}
このコードでは i == 10 のとき values[10] にアクセスしてしまいます。
しかし有効範囲は values[0] から values[9] までです。
正しくは次のように書きます。
double values[10];
for(int i = 0; i < 10; i++)
{
values[i] = i;
}
さらに安全なのは、サイズを直接書かず ArraySize() を使う方法です。
double values[10];
for(int i = 0; i < ArraySize(values); i++)
{
values[i] = i;
}
この形なら、配列サイズが変わってもループ条件が自動で追従します。
つまずきやすい点
<=を使うと最後の1回で範囲外になる- サイズ変更後にループ条件を直し忘れる
ArraySize()-1を使うべき場面と、ループ条件に< ArraySize()を使う場面を混同する
実務向けの原則
ループ処理は原則として次の形に統一すると安全です。
for(int i = 0; i < ArraySize(array); i++)
{
// 処理
}
4.4 配列アクセス前のチェック
最も確実な方法は、配列に触る直前に条件を確認することです。
特に次のケースでは必須です。
- CopyBufferの取得直後
- CopyRatesの取得直後
- 外部条件でインデックスが変わる場合
- バー数不足の可能性がある場合
例:
double ma[];
ArrayResize(ma, 10);
int copied = CopyBuffer(handle, 0, 0, 10, ma);
if(copied <= 0)
return;
if(ArraySize(ma) > 0)
{
Print(ma[0]);
}
さらに、特定のインデックスを使うなら次のように書けます。
int target_index = 5;
if(target_index >= 0 && target_index < ArraySize(ma))
{
Print(ma[target_index]);
}
安全なチェック手順
- 配列サイズが0でないか確認
- 参照したいインデックスが範囲内か確認
- 取得関数の戻り値が正常か確認
この3点を通してからアクセスすると、実行時エラーをかなり減らせます。
つまずきやすい点・注意点・よくある失敗
1. size-1 を直接書きすぎるsize-1 は最後の要素を指すときには正しいですが、ループ条件に毎回直接書くと可読性が落ち、ミスの原因になります。
2. 配列サイズと取得データ数を混同するArrayResize(array, 10) をしたからといって、必ず10件の有効データが入っているとは限りません。
特に CopyBuffer() は取得失敗や取得不足があり得ます。
3. 0始まりを忘れる
配列の要素数が10なら、有効インデックスは 0〜9 です。10 ではありません。
4. 一度動いたコードを安全だと思い込む
相場データ数、テスター期間、初回起動時の状態が変わると、同じコードでも突然エラーになることがあります。
5. EA開発で特に注意するケース
array out of range は、単純な配列ミスだけでなく、EA特有の実行タイミングや相場データの不足によっても発生します。
特にEAは OnTick() ごとに動作し、チャートの状態、バー数、インジケータ計算状況が常に変化するため、静的な前提で書いたコードが壊れやすいのが特徴です。
この章では、実務で特に発生しやすい4つのケースを整理します。
5.1 バー数不足(Bars不足)
EAで多いのが、必要な本数のバーがまだ存在しない状態で配列やインジケータ値を参照してしまうケースです。
たとえば、移動平均の過去値を使う処理を考えます。
double ma[];
ArrayResize(ma, 10);
int copied = CopyBuffer(handle, 0, 0, 10, ma);
Print(ma[9]);
このコードは、常に10本分のデータがある前提で ma[9] を参照しています。
しかし実際には、次のような場面で10本未満しか取得できないことがあります。
- テスター開始直後
- 新しい銘柄や時間足に切り替えた直後
- 必要なヒストリカルデータがまだ不足している場合
この場合、copied が 10 未満でも ma[9] を読むとエラーになります。
安全な書き方は次の通りです。
double ma[];
ArrayResize(ma, 10);
int copied = CopyBuffer(handle, 0, 0, 10, ma);
if(copied < 10)
return;
Print(ma[9]);
さらに、バー数自体を先に確認する方法も有効です。
if(Bars(_Symbol, _Period) < 10)
return;
注意点
ArrayResize()で10要素を確保しても、有効データが10件入る保証はありません- 配列サイズと実データ数は別です
copiedまたはBars()の確認が必要です
5.2 インジケータ読み込み前の参照
MQL5では、iMA() や iCustom() でインジケータハンドルを作成した直後に、すぐ十分なデータが使えるとは限りません。
このため、ハンドル作成直後に CopyBuffer() して即参照すると、array out of range の原因になります。
危険な例:
int handle = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE);
double ma[];
ArrayResize(ma, 3);
CopyBuffer(handle, 0, 0, 3, ma);
Print(ma[2]);
このコードは、インジケータがまだ計算未完了でも ma[2] を読みに行きます。
その結果、取得件数不足でエラーになることがあります。
安全性を上げるには、計算済みバー数を確認します。
int calculated = BarsCalculated(handle);
if(calculated < 3)
return;
double ma[];
ArrayResize(ma, 3);
int copied = CopyBuffer(handle, 0, 0, 3, ma);
if(copied < 3)
return;
Print(ma[2]);
つまずきやすい点
- ハンドルが作れた = データが即使える、ではありません
- 特に
iCustom()は内部計算が重い場合があり、初回は遅れることがあります - テスターでは再現しやすく、本番ではたまにしか出ない、という形で見落としやすいです
5.3 マルチタイムフレーム処理
マルチタイムフレームEAでは、現在のチャート足と取得対象の時間足のバー数が一致しないため、範囲外アクセスが起きやすくなります。
たとえば、M15チャート上でH1の移動平均を参照する場合、単純に同じ感覚でインデックスを使うと危険です。
double ma_h1[];
ArrayResize(ma_h1, 10);
int copied = CopyBuffer(handle_h1, 0, 0, 10, ma_h1);
Print(ma_h1[5]);
このとき問題になるのは、H1側のデータ本数が十分でない可能性があることです。
また、現在チャートではバーが進んでいても、上位足ではまだ新バーが確定していないことがあります。
安全に扱うポイント
- 対象時間足ごとに
Bars()を確認する CopyBuffer()の戻り値を個別に確認する- 現在足と上位足の配列を同じ前提で扱わない
例:
if(Bars(_Symbol, PERIOD_H1) < 10)
return;
double ma_h1[];
ArrayResize(ma_h1, 10);
int copied = CopyBuffer(handle_h1, 0, 0, 10, ma_h1);
if(copied < 10)
return;
Print(ma_h1[5]);
よくある失敗
_Periodと別時間足を同じバー数感覚で扱う- 下位足では十分なバーがあるので上位足もあると思い込む
- インデックス
0は同じでも、更新タイミングが同じだと誤解する
5.4 初回Tick処理
EAは OnTick() で動くため、初回Tick時には必要な準備がまだ終わっていないケースがあります。
この状態で配列やインジケータ値を読みに行くと、エラーの原因になります。
たとえば、OnInit() でハンドルだけ作成し、最初の OnTick() で即参照する構成です。
int ma_handle;
int OnInit()
{
ma_handle = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
void OnTick()
{
double ma[];
ArrayResize(ma, 2);
int copied = CopyBuffer(ma_handle, 0, 0, 2, ma);
Print(ma[1]);
}
このコードは、初回Tick時に copied < 2 でも ma[1] を読む可能性があります。
安全な形は次の通りです。
void OnTick()
{
double ma[];
ArrayResize(ma, 2);
if(BarsCalculated(ma_handle) < 2)
return;
int copied = CopyBuffer(ma_handle, 0, 0, 2, ma);
if(copied < 2)
return;
Print(ma[1]);
}
実務での考え方
初回Tickでは、次のどれかが未準備の可能性があります。
- バー数
- インジケータ計算
- ヒストリカルデータ
- 他時間足データ
したがって、初回から必ず動く前提で書かないことが重要です。
「必要条件が揃っていなければ何もしない」という設計の方が安定します。
よくある失敗
1. バー数が足りる前提で固定インデックスを書くma[9] のようなアクセスは、取得件数確認なしでは危険です。
2. ハンドル作成直後に即参照するiMA() や iCustom() の直後は、まだ使えないことがあります。
3. マルチタイムフレームを同じ感覚で扱う
M15とH1ではバーの進み方も本数も一致しません。
4. 初回Tickで全部動かそうとする
条件未達でも処理を進めると、実行時エラーが出やすくなります。
6. デバッグ方法(原因を特定する)
array out of range エラーは、配列サイズ・インデックス値・取得データ数のいずれかが想定と一致していないときに発生します。
しかし、実際のEAでは次のような状況が多く、原因がすぐには分からないことがあります。
- テスターでは出るが実運用では出ない
- 特定の通貨ペアだけ発生する
- 初回Tickだけ発生する
- CopyBufferの戻り値が環境によって変わる
そのため、エラーを修正するには どの値が範囲外なのかを特定するデバッグ作業が必要になります。
この章では、実務でよく使われる3つの方法を紹介します。
6.1 Printで配列サイズ確認
最も簡単で効果的なのが、Print() を使って 配列サイズをログ出力する方法です。
例:
double ma[];
ArrayResize(ma,10);
int copied = CopyBuffer(handle,0,0,10,ma);
Print("Array size = ", ArraySize(ma));
Print("Copied = ", copied);
ログには次のように表示されます。
Array size = 10
Copied = 6
この結果を見ると、
配列サイズ = 10
実データ = 6
であることが分かります。
もしこの状態で次のコードを書いているとします。
Print(ma[9]);
このとき、ma[6]〜ma[9]は未取得データであり、環境によっては範囲外エラーの原因になります。
デバッグ時の基本ログ
配列処理では、次の情報を出力すると原因特定が早くなります。
ArraySize(array)
CopyBuffer戻り値
Bars数
BarsCalculated
例:
Print("ArraySize=",ArraySize(ma),
" Copied=",copied,
" Bars=",Bars(_Symbol,_Period));
6.2 インデックス値の確認
array out of range は、インデックス計算ミスでも発生します。
特に次のようなケースです。
- ループ処理
- 過去バー参照
- 条件式によるインデックス変更
例:
for(int i=0;i<=ArraySize(ma);i++)
{
Print(ma[i]);
}
このコードでは i == ArraySize(ma) のとき範囲外になります。
こうした問題を見つけるには、インデックス値をログ出力する方法が有効です。
for(int i=0;i<=ArraySize(ma);i++)
{
Print("index=",i);
Print(ma[i]);
}
ログを見ると次のようになります。
index=0
index=1
index=2
...
index=10
この時点で
配列サイズ = 10
index = 10
が一致していないことが分かります。
デバッグ時の基本ルール
インデックス使用時は次の条件を満たしているか確認します。
index >= 0
index < ArraySize(array)
これを満たしていない場合、配列アクセスは行わないようにします。
6.3 Strategy Testerでの確認
array out of range エラーは、Strategy Testerで再現するケースが非常に多いです。
特に次の状況で発生します。
- バックテスト開始直後
- データ不足
- インジケータ未計算
- マルチタイムフレーム参照
そのため、次の手順で確認すると原因が特定しやすくなります。
手順
- Strategy Testerを起動
- 「ビジュアルモード」を有効にする
- Journalログを確認
- エラー行番号を特定
ログには次のようなメッセージが表示されます。
array out of range in 'EA_name.mq5' (145,12)
この場合、
145行目
がエラー発生箇所です。
次にその行のコードを確認し、
- 配列サイズ
- インデックス
- CopyBuffer戻り値
をチェックします。
テスターで見落としやすいポイント
1. 初回Tick問題
バックテスト開始直後はバー数が少ないことがあります。
2. ヒストリカルデータ不足
特定通貨ペアでは必要な期間のデータが存在しない場合があります。
3. インジケータ遅延
iCustom() の内部計算が遅れる場合があります。
実務で使う簡易デバッグテンプレート
配列処理で問題が出た場合、次のログを入れると原因を特定しやすくなります。
Print("ArraySize=",ArraySize(buffer));
Print("Index=",index);
Print("Copied=",copied);
Print("Bars=",Bars(_Symbol,_Period));
これにより
- 配列サイズ
- 参照インデックス
- 取得データ数
- バー数
が同時に確認できます。
よくある失敗
ログを出さずにコードだけ見て修正する
配列問題は、実行時の値を見ないと原因が分からないことが多いです。
CopyBufferの戻り値を確認しない
取得件数が不足している可能性があります。
テスターで再現確認をしない
ライブ環境だけで確認すると、原因を特定しにくくなります。
インデックス計算を想像だけで判断する
ループ処理では実際の値をログで確認する方が確実です。
7. 再発防止のベストプラクティス
array out of range エラーは、一度修正しても別の箇所で再発することが多いエラーです。
その理由は、配列アクセスの前提条件がコードに明示されていないことが多いためです。
EAやインジケータは、次の条件が常に変化します。
- バー数
- インジケータ計算状態
- CopyBuffer取得数
- ヒストリカルデータ
そのため、実務では 防御的プログラミング(Defensive Programming) を採用します。
これは「前提条件が満たされない場合は処理を行わない」という設計です。
この章では、array out of range を防ぐための実践的な設計ルールを紹介します。
7.1 配列アクセス前のチェック
最も基本で重要なのは、配列にアクセスする前に範囲チェックを行うことです。
例:
int index = 5;
if(index >= 0 && index < ArraySize(buffer))
{
Print(buffer[index]);
}
このコードでは次の条件を確認しています。
index >= 0
index < ArraySize(buffer)
この2つを満たさない場合、配列アクセスは実行されません。
安全な書き方の基本ルール
配列アクセス前には次を確認します。
- 配列サイズが0ではない
- インデックスが範囲内
- 取得データ数が十分
例:
if(ArraySize(buffer) > index)
{
Print(buffer[index]);
}
7.2 CopyBufferのreturnチェック
CopyBuffer() は 取得データ数を戻り値として返します。
この値を確認せずに配列アクセスすると、array out of range の原因になります。
例:
double ma[];
ArrayResize(ma,10);
int copied = CopyBuffer(handle,0,0,10,ma);
if(copied < 10)
return;
Print(ma[9]);
この処理では
copied < 10
の場合、処理を中断します。
つまり 取得できたデータが不足している場合は配列に触らないという設計です。
よくある失敗
次のコードは危険です。
CopyBuffer(handle,0,0,10,ma);
Print(ma[9]);
戻り値を確認していないため、取得不足でもアクセスしてしまいます。
7.3 バー数チェック
EAでは、バー数不足が原因でエラーが発生するケースも多くあります。
特にバックテスト開始直後や新しいチャートでは、必要なバー数がまだ存在しないことがあります。
例:
if(Bars(_Symbol,_Period) < 50)
return;
このチェックを入れることで、バー不足時の処理を回避できます。
実務でよく使う条件
if(Bars(_Symbol,_Period) < 必要バー数)
return;
例えば
- 移動平均20期間 → 20以上
- RSI14 → 14以上
- 過去50バー参照 → 50以上
といった条件になります。
注意点
バー数チェックだけでは完全ではありません。
インジケータ計算状態も確認する必要があります。
7.4 防御的プログラミング
EA開発では、前提条件が満たされていない場合は処理しない設計が重要です。
例:
double ma[];
ArrayResize(ma,10);
if(Bars(_Symbol,_Period) < 10)
return;
if(BarsCalculated(handle) < 10)
return;
int copied = CopyBuffer(handle,0,0,10,ma);
if(copied < 10)
return;
double currentMA = ma[0];
このコードでは次の条件をすべて確認しています。
- バー数
- インジケータ計算数
- CopyBuffer取得数
これらを通過した場合のみ配列アクセスを行います。
防御コードの考え方
EAでは次の順序でチェックするのが一般的です。
バー数確認
↓
インジケータ計算確認
↓
データ取得
↓
取得件数確認
↓
配列アクセス
この順序を守ると、array out of range はほぼ防げます。
安全なEAテンプレート
実務でよく使われる安全テンプレートを紹介します。
double buffer[];
ArrayResize(buffer,10);
if(Bars(_Symbol,_Period) < 10)
return;
int copied = CopyBuffer(handle,0,0,10,buffer);
if(copied < 10)
return;
double value = buffer[0];
この形は
- バー不足回避
- CopyBuffer不足回避
- 配列範囲エラー回避
の3つを満たします。
よくある失敗
1. 一度動いたコードを安全だと思う
相場データやテスター条件が変わると、同じコードでもエラーが発生します。
2. 配列サイズだけ確認する
配列サイズが10でも、有効データが10件あるとは限りません。
3. CopyBuffer戻り値を無視する
取得不足時のエラー原因になります。
4. 初回Tick問題を考慮しない
初回Tickではバーやインジケータが不足することがあります。
8. まとめ(最短解決のチェックリスト)
MQL5の array out of range エラーは、配列の範囲外インデックスにアクセスしたときに発生する実行時エラーです。
EAやインジケータでは配列処理が多いため、このエラーは非常に頻繁に発生します。
しかし原因の多くは、次の3つに集約されます。
- 配列サイズ不足
- 取得データ数不足
- インデックス計算ミス
そのため、エラーが発生した場合は 配列サイズ → 取得件数 → インデックス の順に確認すると、原因を特定しやすくなります。
以下に、実務で使える 最短解決チェックリスト をまとめます。
8.1 配列サイズは確保されているか
動的配列を使用する場合、サイズを確保しないと配列アクセスはできません。
確認ポイント
ArrayResize()を実行しているか- 必要なインデックスまでサイズが確保されているか
例:
double buffer[];
ArrayResize(buffer,10);
この場合、有効なインデックスは
buffer[0] 〜 buffer[9]
です。
よくある失敗
- 配列宣言だけで使用してしまう
- 必要サイズより小さい
ArrayResize()を指定する
8.2 CopyBufferの戻り値を確認したか
CopyBuffer() は 取得できたデータ数を戻り値として返します。
この値を確認せずに配列アクセスすると、範囲外エラーの原因になります。
安全な書き方
double ma[];
ArrayResize(ma,10);
int copied = CopyBuffer(handle,0,0,10,ma);
if(copied < 10)
return;
このチェックにより、取得不足時の配列アクセスを防ぐことができます。
よくある失敗
CopyBuffer(handle,0,0,10,ma);
Print(ma[9]);
戻り値を確認していないため、取得不足でもアクセスしてしまいます。
8.3 インデックスは範囲内か
配列の有効インデックスは次の範囲です。
0 〜 ArraySize(array)-1
したがって、配列アクセス前には次の条件を確認します。
if(index >= 0 && index < ArraySize(array))
{
Print(array[index]);
}
この条件を満たさない場合、配列アクセスは行いません。
よくある失敗
<=を使ったループ条件sizeを最大インデックスと勘違いする
8.4 バー数は十分か
EAでは、必要なバー数が存在しない状態で配列アクセスするケースも多くあります。
例:
if(Bars(_Symbol,_Period) < 20)
return;
このチェックを入れることで
- バックテスト初期
- チャート変更直後
- データ不足
などの状態でのエラーを防げます。
8.5 インジケータは計算済みか
インジケータハンドルを作成しても、すぐにデータが取得できるとは限りません。
そのため BarsCalculated() を使って確認します。
if(BarsCalculated(handle) < 10)
return;
このチェックを入れることで、インジケータ計算遅延によるエラーを回避できます。
最短チェックリスト
array out of range が発生した場合、次の順序で確認すると原因を特定しやすくなります。
- 配列サイズは確保されているか
ArraySize()は想定通りか- インデックスは
size-1以下か CopyBuffer()の戻り値は十分か- バー数は必要本数以上か
- インジケータ計算は完了しているか
この6項目を確認すれば、ほとんどのケースで原因を特定できます。
安全な配列アクセステンプレート
EA開発では、次のテンプレートを使うと array out of range をほぼ防げます。
double buffer[];
ArrayResize(buffer,10);
if(Bars(_Symbol,_Period) < 10)
return;
if(BarsCalculated(handle) < 10)
return;
int copied = CopyBuffer(handle,0,0,10,buffer);
if(copied < 10)
return;
double value = buffer[0];
このコードでは
- バー数チェック
- インジケータ計算確認
- データ取得確認
- 配列アクセス
という順序で安全に処理を行っています。
FAQ
Q1. MQL5の「array out of range」とは何ですか?
array out of range は、配列の有効範囲を超えたインデックスにアクセスしたときに発生する実行時エラーです。
MQL5では配列のインデックスは 0から開始するため、配列サイズが 10 の場合、使用できるインデックスは 0〜9 になります。
例えば次のコードはエラーになります。
double arr[10];
Print(arr[10]);
arr[10] は存在しない要素のため、array out of range が発生します。
Q2. CopyBufferで「array out of range」が出る原因は?
多くの場合、次のいずれかが原因です。
- 配列サイズが確保されていない
- CopyBufferの取得データ数が不足している
- 戻り値を確認せず配列アクセスしている
安全なコード例
double ma[];
ArrayResize(ma,10);
int copied = CopyBuffer(handle,0,0,10,ma);
if(copied < 10)
return;
Print(ma[0]);
CopyBuffer() の戻り値を確認することで、取得不足時のエラーを防げます。
Q3. MQL5の配列は0から始まりますか?
はい。MQL5の配列インデックスは 0から開始します。
配列サイズが N の場合、有効なインデックスは次の通りです。
0 〜 N-1
例えばサイズが 5 の場合
arr[0]
arr[1]
arr[2]
arr[3]
arr[4]
までが有効です。
Q4. ArrayResizeは必ず必要ですか?
動的配列を使用する場合は 必須です。
次のようなコードはエラーになります。
double arr[];
arr[0] = 1;
動的配列を使う場合は、必ずサイズを確保します。
double arr[];
ArrayResize(arr,10);
ただし、次のような 固定配列 の場合は不要です。
double arr[10];
Q5. Strategy Testerでだけエラーが出るのはなぜですか?
主な原因は次の通りです。
- バックテスト開始直後のバー不足
- インジケータ未計算
- ヒストリカルデータ不足
例えば
if(Bars(_Symbol,_Period) < 20)
return;
のようにバー数を確認することで回避できます。
Q6. ArraySetAsSeriesは関係ありますか?
はい。ArraySetAsSeries() を使用すると、配列のインデックス方向が 逆順(最新バーが0)になります。
例:
index 0 = 最新バー
index 1 = 1本前
index 2 = 2本前
この設定を理解していないと、想定と違うインデックスを参照してしまう可能性があります。
Q7. EA開発で最も多い原因は何ですか?
実務で最も多い原因は次の3つです。
- CopyBufferの戻り値未確認
- バー数不足
- ループ条件ミス(
<=)
特に次のコードはエラーの原因になりやすいです。
for(int i=0;i<=ArraySize(arr);i++)
正しくは
for(int i=0;i<ArraySize(arr);i++)
です。
Q8. array out of rangeを完全に防ぐ方法はありますか?
完全に防ぐことは難しいですが、次の対策を徹底すれば発生率を大きく下げられます。
- 配列アクセス前に
ArraySize()を確認 CopyBuffer()の戻り値を確認Bars()でバー数チェックBarsCalculated()でインジケータ計算確認
特にEA開発では、次の順序で処理を書くと安全です。
バー数確認
↓
インジケータ計算確認
↓
CopyBuffer取得
↓
取得件数確認
↓
配列アクセス
この流れを守ることで、array out of range の多くを回避できます。