MQL4で直近のローソク足の始値・終値を知りたいとき、

`iOpen(NULL,0,1)`のように、

バーの位置(シフト)を1つ指定するだけで、その1本の値が返ってきました。

このコードをMQL5にそのまま持ち込もうとすると、

`iOpen`という同名の関数自体が見つからず、

コンパイルエラーになります。

MQL5では「1本ずつ値を取り出す」設計自体が廃止され、

代わりに`CopyRates()`や`CopyClose()`のように、

指定した本数のデータを一度に配列へコピーする設計に変わっています。

この記事では、直近10本の四本値をまとめて取得するコードを例に、

この設計変更と、並び順(どちらが最新のバーか)で読者がよくつまずくポイントを解説します。

MQL4: iOpen/iClose/iHigh/iLowで1本ずつ取得

MQL4では、取得したいバーの位置(シフト、0が現在のバー)を指定して、

1回の呼び出しで1つの値を受け取ります(詳しくはiOpen・iClose・iHigh・iLowの記事を参照)。

double open1  = iOpen(NULL,0,1);
double close1 = iClose(NULL,0,1);
double high1  = iHigh(NULL,0,1);
double low1   = iLow(NULL,0,1);

複数本のデータが欲しい場合は、

このシフトの値を変えながらforループで1本ずつ呼び出す必要がありました。

MQL5: CopyRates/CopyClose等で配列にまとめて取得

MQL5では、値の種類ごとに、指定した本数を一括で配列にコピーする関数が用意されています。

int CopyRates(string symbol_name, ENUM_TIMEFRAMES timeframe, int start_pos, int count, MqlRates rates_array[]);
int CopyOpen (string symbol_name, ENUM_TIMEFRAMES timeframe, int start_pos, int count, double open_array[]);
int CopyHigh (string symbol_name, ENUM_TIMEFRAMES timeframe, int start_pos, int count, double high_array[]);
int CopyLow  (string symbol_name, ENUM_TIMEFRAMES timeframe, int start_pos, int count, double low_array[]);
int CopyClose(string symbol_name, ENUM_TIMEFRAMES timeframe, int start_pos, int count, double close_array[]);

`CopyRates()`は、

始値・高値・安値・終値・出来高・スプレッドを1つにまとめた、

`MqlRates`構造体の配列で受け取ります。

struct MqlRates
  {
   datetime time;         //時刻
   double   open;         //始値
   double   high;         //高値
   double   low;          //安値
   double   close;        //終値
   long     tick_volume;  //ティック出来高
   int      spread;       //スプレッド
   long     real_volume;  //実出来高
  };

終値だけ・高値だけのように1種類の値しか使わない場合は、

`CopyClose()`/`CopyHigh()`のように専用の関数を使う方が、

配列がシンプルになり扱いやすくなります。

戻り値は取得できた件数(`int`)で、失敗した場合は`-1`が返ります。

実務的な落とし穴: 配列の並び順(as-series)

`start_pos`に0を指定すると現在のバーから数える点は、

MQL4の`shift=0`と同じ考え方ですが、

コピー後の配列でどちらが最新のバーになるかは、

配列の`as-series`設定に依存します。

デフォルトの状態や宣言方法によって並び順の挙動が変わりうるため、

「index[0]が必ず最新バーだろう」と決め打ちでコードを書くと、

集計結果がバーの新旧で逆転する事故につながります。

これを避けるため、`CopyRates()`等を呼び出す前に、

必ず`ArraySetAsSeries()`で並び順を明示しておくことを推奨します。

MqlRates rates[];
ArraySetAsSeries(rates, true);//index[0]が最新バーになるよう明示する
int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, 10, rates);

この1行を入れておけば、

`start_pos=0`で取得した最新バーが必ず、

`rates[0]`に入ることが保証され、

読者が並び順を推測する必要がなくなります。

実際に動くコード全体

直近10本の四本値をCopyRatesで取得し、1本ずつログに表示する、

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

//+------------------------------------------------------------------+
//|                                    CopyRatesDemo.mq5              |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property script_show_inputs

input int BarsToShow = 10;//表示するバーの本数

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   MqlRates rates[];
   ArraySetAsSeries(rates, true);

   int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, BarsToShow, rates);

   if(copied <= 0)
     {
      PrintFormat("CopyRatesに失敗しました。要求本数=%d エラーコード=%d", BarsToShow, GetLastError());
      return;
     }

   if(copied < BarsToShow)
      PrintFormat("警告: 要求した%d本のうち%d本しか取得できませんでした(ヒストリーデータ不足の可能性)。",
                  BarsToShow, copied);

   for(int i=0; i<copied; i++)
     {
      PrintFormat("[%d] %s  O=%.5f H=%.5f L=%.5f C=%.5f  出来高=%d",
                  i,
                  TimeToString(rates[i].time, TIME_DATE|TIME_MINUTES),
                  rates[i].open, rates[i].high, rates[i].low, rates[i].close,
                  (int)rates[i].tick_volume);
     }
  }
//+------------------------------------------------------------------+

MetaEditorで実際に動かす手順

  1. MetaTrader 5の「ツール」→「MetaQuotes Language Editor」でMetaEditorを開く
  2. 「ファイル」→「新規作成」→「スクリプト」を選び、名前を「CopyRatesDemo」にして作成する(中身は空のテンプレートで構いません)
  3. テンプレートの中身を全て削除し、上のコードを丸ごと貼り付ける
  4. F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  5. チャートへドラッグ&ドロップし、「エキスパート」タブに直近10本の四本値が新しい順に表示されることを確認する

コピペしてコンパイルが通らない・想定と違う結果になるときに確認すること

  • `ArraySetAsSeries()`を呼び忘れていないか(呼び忘れると、環境によって`rates[0]`が最新バーか最古バーか判断できないコードになる)
  • 戻り値(`copied`)が要求した本数より少ない場合、その通貨ペア・時間足のヒストリーデータがMT5端末に十分読み込まれていない可能性がある。チャートを一度開いてスクロールし、過去データを読み込ませてから再実行する
  • `CopyRates()`と、別記事で解説した`CopyBuffer()`(CopyBufferの記事)を混同していないか。`CopyRates()`は生のOHLC値、`CopyBuffer()`はインジケーターの計算結果を取得する、別の関数である
  • 戻り値が`-1`の場合、`_Symbol`のスペルミスや、板情報にない通貨ペアを指定していないか確認する(`SymbolSelect()`で市場ウォッチに追加してから再試行する)

まとめ

MQL4のiOpen/iClose/iHigh/iLowが、

1本ずつ値を取得する設計だったのに対し、

MQL5ではCopyRates/CopyOpen/CopyHigh/CopyLow/

CopyCloseという、

指定した本数をまとめて配列にコピーする設計に変わりました。

並び順の事故を防ぐため、

Copy系関数を呼ぶ前には必ずArraySetAsSeries()で並び順を明示し、

戻り値が要求本数と一致しているかも確認する習慣をつけてください。

関連記事