【MQL4】配列関数を完全マスター!ArraySize・ArrayResize・ArraySort・ArrayCopy・ArrayInitialize・ArraySetAsSeriesの使い方

【中級編】MQLプログラムの読み方・書き方

MQL4で自動売買(EA)やカスタムインジケーターを開発するとき、配列の操作は避けて通れません。価格データの格納、インジケーター値の計算、ソートによるランキング処理など、あらゆる場面で配列関数が活躍します。

この記事では、MQL4で特に使用頻度の高い6つの配列関数を体系的に解説します。

  • ArraySize() – 要素数の取得
  • ArrayResize() – サイズの動的変更
  • ArrayInitialize() – 全要素の一括初期化
  • ArrayCopy() – 配列のコピー
  • ArraySort() – ソート(並び替え)
  • ArraySetAsSeries() – 時系列インデックスへの変換

それぞれの関数について、基本的な使い方・注意点・実践的なコードサンプルを交えながら丁寧に解説していきます。

ArraySize() – 配列の要素数を取得する

ArraySize()は、配列に含まれる全要素数を返す関数です。ループ処理で配列の全要素を走査するときに欠かせません。

基本構文

int ArraySize(
   const void& array[]   // 対象の配列
);

基本的な使い方

void OnStart()
{
   double prices[5] = {100.0, 101.5, 102.3, 99.8, 100.7};

   int size = ArraySize(prices);
   Print("要素数: ", size);  // 出力: 要素数: 5

   // ループ処理での活用
   for(int i = 0; i < ArraySize(prices); i++)
   {
      Print("prices[", i, "] = ", prices[i]);
   }
}

多次元配列での注意点

ArraySize()は多次元配列に対しては、全次元の要素数の積(全要素数)を返します。特定の次元の要素数だけを知りたい場合はArrayRange()を使いましょう。

void OnStart()
{
   double matrix[3][4];  // 3行4列の二次元配列

   Print("ArraySize: ", ArraySize(matrix));       // 出力: 12(3×4)
   Print("第1次元: ", ArrayRange(matrix, 0));      // 出力: 3
   Print("第2次元: ", ArrayRange(matrix, 1));      // 出力: 4
}

ArrayResize() – 配列のサイズを動的に変更する

ArrayResize()は、動的配列のサイズを変更する関数です。EAの運用中にデータが増減する場面で大活躍します。

基本構文

int ArrayResize(
   void& array[],         // サイズ変更する配列
   int    new_size,        // 新しいサイズ
   int    reserve_size=0   // 予約サイズ(省略可)
);

戻り値は、成功した場合は新しいサイズ、失敗した場合は-1です。

基本的な使い方

void OnStart()
{
   double data[];  // 動的配列(サイズ未指定)

   // サイズを5に設定
   ArrayResize(data, 5);
   Print("サイズ: ", ArraySize(data));  // 出力: 5

   // データを格納
   for(int i = 0; i < 5; i++)
      data[i] = i * 1.5;

   // サイズを10に拡張(既存データは保持される)
   ArrayResize(data, 10);
   Print("拡張後サイズ: ", ArraySize(data));  // 出力: 10
   Print("data[2] = ", data[2]);  // 出力: 3.0(既存データは保持)
}

reserve_sizeでパフォーマンスを改善する

ループ内で何度もArrayResize()を呼ぶと、メモリの再割り当てが頻繁に発生しパフォーマンスが低下します。第3引数のreserve_sizeを指定することで、あらかじめメモリを多めに確保し、再割り当ての回数を減らせます。

void OnStart()
{
   double ticks[];
   int count = 0;

   // 悪い例:毎回メモリ再割り当てが発生
   // for(int i = 0; i < 10000; i++)
   // {
   //    ArrayResize(ticks, ++count);
   //    ticks[count - 1] = Bid;
   // }

   // 良い例:reserve_sizeで事前にメモリを確保
   for(int i = 0; i < 10000; i++)
   {
      ArrayResize(ticks, ++count, 10000);  // 最大10000要素分を予約
      ticks[count - 1] = Bid;
   }
}

大量のデータを扱うEAでは、このreserve_sizeの指定が処理速度に大きく影響します。ぜひ覚えておきましょう。

ArrayInitialize() – 全要素を一括で初期化する

ArrayInitialize()は、配列の全要素を指定した値で一括初期化する関数です。

基本構文

int ArrayInitialize(
   double& array[],   // 初期化する配列
   double  value       // セットする値
);

基本的な使い方

void OnStart()
{
   double buffer[10];

   // 全要素を0.0で初期化
   ArrayInitialize(buffer, 0.0);

   for(int i = 0; i < ArraySize(buffer); i++)
      Print("buffer[", i, "] = ", buffer[i]);  // すべて0.0
}

ArrayResizeとの組み合わせが重要

ArrayResize()でサイズを拡張した直後、新しく追加された要素の値は不定(ゴミ値)です。そのため、拡張後にArrayInitialize()で初期化するのが安全なパターンです。

void OnStart()
{
   double data[];

   ArrayResize(data, 10);
   ArrayInitialize(data, 0.0);  // 拡張後は必ず初期化!

   Print("data[5] = ", data[5]);  // 安全に0.0が入っている
}

ArrayCopy() – 配列を別の配列にコピーする

ArrayCopy()は、配列の全体または一部を別の配列にコピーする関数です。

基本構文

int ArrayCopy(
   void& dst_array[],      // コピー先
   const void& src_array[], // コピー元
   int   dst_start=0,       // コピー先の開始位置
   int   src_start=0,       // コピー元の開始位置
   int   count=WHOLE_ARRAY  // コピーする要素数
);

戻り値はコピーされた要素数です。

基本的な使い方

void OnStart()
{
   double source[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
   double dest[];

   // 全要素をコピー(destは自動でリサイズされる)
   ArrayCopy(dest, source);
   Print("dest[2] = ", dest[2]);  // 出力: 3.3

   // 部分コピー:sourceのインデックス1から3要素をdest2のインデックス0にコピー
   double dest2[3];
   ArrayCopy(dest2, source, 0, 1, 3);
   // dest2 = {2.2, 3.3, 4.4}
   for(int i = 0; i < ArraySize(dest2); i++)
      Print("dest2[", i, "] = ", dest2[i]);
}

部分コピーを使いこなすと、直近N本のデータだけを抜き出す処理などが簡潔に書けます。

ArraySort() – 配列をソート(並び替え)する

ArraySort()は、配列の要素を昇順にソートする関数です。

基本構文

bool ArraySort(
   void& array[]   // ソートする配列
);

MQL4のArraySort()昇順のみに対応しています。

基本的な使い方

void OnStart()
{
   double values[5] = {3.5, 1.2, 4.8, 2.1, 3.9};

   // 昇順ソート
   ArraySort(values);

   for(int i = 0; i < ArraySize(values); i++)
      Print("values[", i, "] = ", values[i]);
   // 出力: 1.2, 2.1, 3.5, 3.9, 4.8
}

降順にしたい場合

MQL4のArraySort()には降順オプションがないため、昇順ソート後に逆順で読むか、別の配列に逆順コピーする方法を使います。

void OnStart()
{
   double values[5] = {3.5, 1.2, 4.8, 2.1, 3.9};

   // まず昇順ソート
   ArraySort(values);

   // 逆順で読み取ることで降順として扱う
   int size = ArraySize(values);
   for(int i = size - 1; i >= 0; i--)
      Print("降順 values[", (size - 1 - i), "] = ", values[i]);
   // 出力: 4.8, 3.9, 3.5, 2.1, 1.2
}

注意点:時系列配列はソートできない

ArraySetAsSeries()で時系列フラグが設定された配列は、ArraySort()でソートできません。ソートしたい場合は、まず通常の配列にコピーしてからソートしましょう。

ArraySetAsSeries() – 時系列インデックスに変換する

ArraySetAsSeries()は、配列のインデックスの方向を時系列方向に変更する関数です。MetaTraderのチャートデータは最新のバー(ローソク足)がインデックス0になっていますが、通常の配列は先頭がインデックス0です。この違いを吸収するのがこの関数です。

基本構文

bool ArraySetAsSeries(
   const void& array[],  // 対象の配列
   bool        flag       // true=時系列, false=通常
);

通常配列と時系列配列の違い

// 通常の配列(左から右へインデックスが増加)
// index:  [0]  [1]  [2]  [3]  [4]
// データ:  古い ←――――――――――→ 新しい

// 時系列配列(右から左へインデックスが増加)
// index:  [4]  [3]  [2]  [1]  [0]
// データ:  古い ←――――――――――→ 新しい

基本的な使い方

void OnStart()
{
   double closeArray[];

   // 直近10本の終値を取得
   ArraySetAsSeries(closeArray, true);  // 時系列方向に設定
   CopyClose(Symbol(), PERIOD_CURRENT, 0, 10, closeArray);

   // closeArray[0] = 最新バーの終値
   // closeArray[1] = 1つ前のバーの終値
   // closeArray[9] = 9つ前のバーの終値
   Print("最新の終値: ", closeArray[0]);
   Print("1つ前の終値: ", closeArray[1]);
}

インジケーター開発での活用

カスタムインジケーターのOnCalculate()関数では、引数として渡される価格配列のインデックス方向が不定の場合があります。そのため、関数の冒頭でArraySetAsSeries()を呼んでインデックス方向を統一するのが定番のパターンです。

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[])
{
   // インデックス方向を時系列に統一
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);

   // これで close[0] が最新バーの終値になる
   // ... インジケーターの計算処理 ...

   return(rates_total);
}

実践テクニック:6つの関数を組み合わせて使う

ここまで学んだ6つの関数を組み合わせた実践的なサンプルを見てみましょう。直近N本のバーから最高値トップ3を抽出する処理です。

void OnStart()
{
   int period = 20;  // 直近20本のバーを対象

   // 1. 高値データを格納する動的配列を準備
   double highPrices[];
   ArrayResize(highPrices, period);       // サイズを設定
   ArrayInitialize(highPrices, 0.0);      // 初期化

   // 2. 直近の高値データを取得
   double tempHigh[];
   ArraySetAsSeries(tempHigh, true);      // 時系列方向に設定
   CopyHigh(Symbol(), PERIOD_CURRENT, 0, period, tempHigh);

   // 3. 作業用配列にコピー
   ArrayCopy(highPrices, tempHigh, 0, 0, period);

   // 4. 昇順ソートしてトップ3を取得(末尾3要素が最大値)
   ArraySort(highPrices);

   int size = ArraySize(highPrices);
   Print("=== 直近", period, "本の高値トップ3 ===");
   for(int i = 0; i < 3; i++)
   {
      Print("第", (i + 1), "位: ", highPrices[size - 1 - i]);
   }
}

このように、ArrayResizeArrayInitializeArraySetAsSeriesArrayCopyArraySortArraySizeと、6つの関数を自然に組み合わせて使えるようになると、実践的なEA開発がスムーズに進みます。

配列関数 早見表

関数名 機能 主な使用場面
ArraySize() 要素数を取得 ループの上限値、配列が空かの判定
ArrayResize() サイズを動的に変更 データ数が実行時に決まる場合
ArrayInitialize() 全要素を一括初期化 ArrayResize後のゴミ値対策
ArrayCopy() 配列の全体/部分コピー データのバックアップ、部分抽出
ArraySort() 昇順ソート ランキング処理、中央値の算出
ArraySetAsSeries() インデックス方向を時系列に変換 インジケーター開発、価格データ操作

まとめ

今回はMQL4の主要な配列関数6つについて解説しました。ポイントを振り返りましょう。

  • ArraySize()はループ処理の必須パートナー。多次元配列では全要素数が返る点に注意
  • ArrayResize()は第3引数のreserve_sizeを活用してパフォーマンスを改善
  • ArrayInitialize()はArrayResize後のゴミ値を防ぐために必ずセットで使う
  • ArrayCopy()は部分コピーが便利。引数の順番(コピー先が最初)を間違えないように
  • ArraySort()はMQL4では昇順のみ。降順が必要なら逆順で読み取る
  • ArraySetAsSeries()はインジケーター開発の定番。close[0]を最新バーにするために使う

これら6つの関数を使いこなせれば、EA開発やインジケーター作成の幅が大きく広がります。まずは実際にスクリプトやEAの中で試してみて、配列操作に慣れていきましょう!