【MQL4リファレンス】MqlRates構造体の使い方!CopyRates()でローソク足データを配列取得する方法

構造体

MqlRates構造体とは

MqlRates構造体は、MQL4でローソク足(バー)の価格データを格納するための構造体です。1本のローソク足に関する情報(始値・高値・安値・終値・出来高・時刻・スプレッド)をひとまとめに扱うことができます。

この構造体は、主にCopyRates()関数と組み合わせて使用し、過去のローソク足データを配列として一括取得する際に活用されます。

MqlRates構造体のメンバー

MqlRates構造体は以下のメンバーで構成されています。

メンバー名 説明
time datetime バーの開始時刻
open double 始値
high double 高値
low double 安値
close double 終値
tick_volume long ティックボリューム
spread int スプレッド
real_volume long 出来高(取引所データが利用可能な場合)

構造体の定義

struct MqlRates
  {
   datetime time;         // バーの開始時刻
   double   open;         // 始値
   double   high;         // 高値
   double   low;          // 安値
   double   close;        // 終値
   long     tick_volume;  // ティックボリューム
   int      spread;       // スプレッド
   long     real_volume;  // 出来高
  };

CopyRates()関数の書式

MqlRates構造体の配列にデータを格納するには、CopyRates()関数を使用します。CopyRates()には3つの呼び出し方法があります。

書式1:開始位置と本数を指定

int CopyRates(
   string           symbol_name,   // 通貨ペア名
   ENUM_TIMEFRAMES  timeframe,     // 時間足
   int              start_pos,     // 開始位置(0が最新バー)
   int              count,         // 取得するバーの本数
   MqlRates         rates_array[]  // 格納先の配列
);

書式2:開始日時と本数を指定

int CopyRates(
   string           symbol_name,   // 通貨ペア名
   ENUM_TIMEFRAMES  timeframe,     // 時間足
   datetime         start_time,    // 開始日時
   int              count,         // 取得するバーの本数
   MqlRates         rates_array[]  // 格納先の配列
);

書式3:開始日時と終了日時を指定

int CopyRates(
   string           symbol_name,   // 通貨ペア名
   ENUM_TIMEFRAMES  timeframe,     // 時間足
   datetime         start_time,    // 開始日時
   datetime         stop_time,     // 終了日時
   MqlRates         rates_array[]  // 格納先の配列
);

引数の説明

引数 説明
symbol_name 通貨ペア名。NULL または Symbol() で現在のチャートの通貨ペアを指定
timeframe 時間足。PERIOD_CURRENT で現在の時間足を指定
start_pos 取得開始位置。0が最新のバー
count 取得するバーの本数
start_time 取得開始日時
stop_time 取得終了日時
rates_array[] MqlRates型の配列(データ格納先)

戻り値

CopyRates()関数は、コピーに成功した要素数を返します。失敗した場合は-1を返します。エラー内容はGetLastError()で取得できます。

プログラム例1:直近のローソク足データを取得して表示する(基本)

まずは最もシンプルな使い方から見ていきましょう。直近10本のローソク足データを取得し、エキスパートログに出力する例です。

//+------------------------------------------------------------------+
//| スクリプト:直近10本のローソク足データを表示                      |
//+------------------------------------------------------------------+
void OnStart()
  {
   // MqlRates型の配列を宣言
   MqlRates rates[];

   // 配列を時系列順に設定(最新のバーが[0]になる)
   ArraySetAsSeries(rates, true);

   // 現在の通貨ペア・時間足で直近10本のバーデータを取得
   int copied = CopyRates(Symbol(), PERIOD_CURRENT, 0, 10, rates);

   // 取得に失敗した場合のエラーチェック
   if(copied <= 0)
     {
      Print("CopyRates()でエラーが発生しました。エラーコード: ", GetLastError());
      return;
     }

   // 取得成功:バーデータを1本ずつ表示
   Print("===== 直近 ", copied, " 本のローソク足データ =====");

   for(int i = 0; i < copied; i++)
     {
      Print("バー[", i, "] ",
            "時刻=", TimeToString(rates[i].time, TIME_DATE | TIME_MINUTES), " | ",
            "始値=", DoubleToString(rates[i].open, Digits), " | ",
            "高値=", DoubleToString(rates[i].high, Digits), " | ",
            "安値=", DoubleToString(rates[i].low, Digits), " | ",
            "終値=", DoubleToString(rates[i].close, Digits), " | ",
            "出来高=", rates[i].tick_volume, " | ",
            "スプレッド=", rates[i].spread);
     }
  }

この例のポイントは、ArraySetAsSeries(rates, true)を呼び出している点です。これにより配列のインデックス[0]が最新のバーを指すようになり、通常のバー番号と同じ感覚でアクセスできます。

プログラム例2:ローソク足の実体とヒゲの長さを分析する

MqlRates構造体の各メンバーを活用して、ローソク足の形状を分析するスクリプトです。陽線・陰線の判定や、実体・ヒゲの長さを計算します。

//+------------------------------------------------------------------+
//| スクリプト:ローソク足の形状分析                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   MqlRates rates[];
   ArraySetAsSeries(rates, true);

   // 直近20本のバーデータを取得
   int copied = CopyRates(Symbol(), PERIOD_CURRENT, 0, 20, rates);
   if(copied <= 0)
     {
      Print("データ取得に失敗しました。");
      return;
     }

   // 各バーのローソク足形状を分析
   for(int i = 0; i < copied; i++)
     {
      // --- ローソク足の各部分の長さを計算 ---

      // 実体の長さ(始値と終値の差の絶対値)
      double body = MathAbs(rates[i].close - rates[i].open);

      // バー全体の長さ(高値と安値の差)
      double total_range = rates[i].high - rates[i].low;

      // 上ヒゲの長さ
      double upper_shadow = rates[i].high - MathMax(rates[i].open, rates[i].close);

      // 下ヒゲの長さ
      double lower_shadow = MathMin(rates[i].open, rates[i].close) - rates[i].low;

      // 陽線か陰線かを判定
      string candle_type;
      if(rates[i].close > rates[i].open)
         candle_type = "陽線";
      else if(rates[i].close < rates[i].open)
         candle_type = "陰線";
      else
         candle_type = "同値";

      // ピンバー(上ヒゲまたは下ヒゲが実体の2倍以上)の判定
      string pattern = "";
      if(total_range > 0)
        {
         if(upper_shadow > body * 2.0 && upper_shadow > lower_shadow * 2.0)
            pattern = " [上ヒゲピンバー]";
         else if(lower_shadow > body * 2.0 && lower_shadow > upper_shadow * 2.0)
            pattern = " [下ヒゲピンバー]";
        }

      // 結果を出力
      Print("バー[", i, "] ",
            TimeToString(rates[i].time, TIME_DATE | TIME_MINUTES), " | ",
            candle_type, " | ",
            "実体=", DoubleToString(body / Point, 1), "pts | ",
            "上ヒゲ=", DoubleToString(upper_shadow / Point, 1), "pts | ",
            "下ヒゲ=", DoubleToString(lower_shadow / Point, 1), "pts",
            pattern);
     }
  }

ローソク足の分析では、open, high, low, closeの4つの値を使って実体やヒゲの長さを算出します。ピンバーなどのパターン検出は、裁量トレードの判断支援やEAのエントリー条件に応用できます。

プログラム例3:異なる時間足のデータを比較するEA

CopyRates()の強みは、現在のチャートとは異なる時間足のデータも取得できる点です。この例では、マルチタイムフレーム分析として日足と1時間足のデータを同時に取得し、トレンド判定に活用するEAを作成します。

//+------------------------------------------------------------------+
//| Expert:マルチタイムフレーム分析によるトレンド判定EA              |
//+------------------------------------------------------------------+
#property strict

//+------------------------------------------------------------------+
//| OnTick関数:ティックごとに実行される                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // --- 日足データの取得(上位時間足のトレンド判定用) ---
   MqlRates daily_rates[];
   ArraySetAsSeries(daily_rates, true);

   int daily_copied = CopyRates(Symbol(), PERIOD_D1, 0, 5, daily_rates);
   if(daily_copied < 5)
     {
      Print("日足データの取得に失敗しました。");
      return;
     }

   // --- 1時間足データの取得(エントリータイミング用) ---
   MqlRates h1_rates[];
   ArraySetAsSeries(h1_rates, true);

   int h1_copied = CopyRates(Symbol(), PERIOD_H1, 0, 10, h1_rates);
   if(h1_copied < 10)
     {
      Print("1時間足データの取得に失敗しました。");
      return;
     }

   // --- 日足のトレンド判定 ---
   // 直近3本の日足の終値が連続して上昇しているか確認
   bool daily_uptrend = (daily_rates[1].close > daily_rates[2].close) &&
                        (daily_rates[2].close > daily_rates[3].close);

   // 直近3本の日足の終値が連続して下降しているか確認
   bool daily_downtrend = (daily_rates[1].close < daily_rates[2].close) &&
                          (daily_rates[2].close < daily_rates[3].close);

   // --- 1時間足のシグナル判定 ---
   // 直前の1時間足が陽線かどうか
   bool h1_bullish = (h1_rates[1].close > h1_rates[1].open);

   // 直前の1時間足が陰線かどうか
   bool h1_bearish = (h1_rates[1].close < h1_rates[1].open);

   // --- マルチタイムフレームの総合判定 ---
   string signal = "待機";

   if(daily_uptrend && h1_bullish)
      signal = "買いシグナル(日足上昇トレンド+1時間足陽線)";
   else if(daily_downtrend && h1_bearish)
      signal = "売りシグナル(日足下降トレンド+1時間足陰線)";

   // チャート左上にシグナルを表示
   Comment("=== マルチタイムフレーム分析 ===\n",
           "日足トレンド: ", daily_uptrend ? "上昇" : (daily_downtrend ? "下降" : "レンジ"), "\n",
           "日足[1] 始値=", DoubleToString(daily_rates[1].open, Digits),
           " 終値=", DoubleToString(daily_rates[1].close, Digits), "\n",
           "1時間足[1] 始値=", DoubleToString(h1_rates[1].open, Digits),
           " 終値=", DoubleToString(h1_rates[1].close, Digits), "\n",
           "判定: ", signal);
  }

CopyRates()の第2引数で時間足を指定することで、現在のチャートの時間足に関係なく任意の時間足のデータを取得できます。PERIOD_D1(日足)で全体のトレンドを確認し、PERIOD_H1(1時間足)でエントリータイミングを計るという実践的なマルチタイムフレーム分析を実装しています。

プログラム例4:日時指定でデータ取得し、期間内の最高値・最安値を求める

CopyRates()の書式3(開始日時と終了日時を指定)を使って、特定期間のローソク足データを取得し、その期間内の最高値・最安値やボラティリティを計算する実用的なスクリプトです。

//+------------------------------------------------------------------+
//| スクリプト:特定期間の価格レンジとボラティリティ分析              |
//+------------------------------------------------------------------+
void OnStart()
  {
   // --- 分析する期間を設定 ---
   datetime start_time = D'2024.01.01 00:00'; // 開始日時
   datetime end_time   = D'2024.01.31 23:59'; // 終了日時

   // MqlRates配列を宣言(時系列設定はしない=古い順)
   MqlRates rates[];

   // 指定期間のH1(1時間足)データを取得
   int copied = CopyRates(Symbol(), PERIOD_H1, start_time, end_time, rates);

   if(copied <= 0)
     {
      Print("指定期間のデータ取得に失敗しました。エラーコード: ", GetLastError());
      return;
     }

   Print("取得したバー数: ", copied);

   // --- 期間内の最高値・最安値を検索 ---
   double highest_price = rates[0].high;   // 最高値の初期値
   double lowest_price  = rates[0].low;    // 最安値の初期値
   datetime highest_time = rates[0].time;  // 最高値の時刻
   datetime lowest_time  = rates[0].time;  // 最安値の時刻
   long total_volume = 0;                  // 合計出来高
   double total_range = 0;                 // 各バーのレンジ合計

   for(int i = 0; i < copied; i++)
     {
      // 最高値の更新チェック
      if(rates[i].high > highest_price)
        {
         highest_price = rates[i].high;
         highest_time  = rates[i].time;
        }

      // 最安値の更新チェック
      if(rates[i].low < lowest_price)
        {
         lowest_price = rates[i].low;
         lowest_time  = rates[i].time;
        }

      // 出来高の合計
      total_volume += rates[i].tick_volume;

      // 各バーのレンジ(高値−安値)を合計
      total_range += (rates[i].high - rates[i].low);
     }

   // --- 平均ボラティリティの計算 ---
   double avg_range = total_range / copied;           // 平均レンジ(price)
   double avg_range_pts = avg_range / Point;           // 平均レンジ(ポイント)
   double avg_volume = (double)total_volume / copied;  // 平均出来高

   // --- 分析結果を出力 ---
   Print("===========================================");
   Print("  期間分析レポート: ", Symbol(), " H1");
   Print("===========================================");
   Print("分析期間: ", TimeToString(start_time, TIME_DATE), " ~ ",
         TimeToString(end_time, TIME_DATE));
   Print("バー数: ", copied);
   Print("-------------------------------------------");
   Print("期間最高値: ", DoubleToString(highest_price, Digits),
         " (", TimeToString(highest_time, TIME_DATE | TIME_MINUTES), ")");
   Print("期間最安値: ", DoubleToString(lowest_price, Digits),
         " (", TimeToString(lowest_time, TIME_DATE | TIME_MINUTES), ")");
   Print("期間レンジ: ", DoubleToString((highest_price - lowest_price) / Point, 1), " pts");
   Print("-------------------------------------------");
   Print("平均レンジ(1バーあたり): ", DoubleToString(avg_range_pts, 1), " pts");
   Print("平均出来高(1バーあたり): ", DoubleToString(avg_volume, 0));
   Print("合計出来高: ", total_volume);
   Print("===========================================");

   // --- 最初の5本と最後の5本のデータを確認表示 ---
   Print("--- 期間先頭のバー ---");
   int show_count = MathMin(5, copied);
   for(int i = 0; i < show_count; i++)
     {
      Print("  ", TimeToString(rates[i].time, TIME_DATE | TIME_MINUTES),
            " O=", DoubleToString(rates[i].open, Digits),
            " H=", DoubleToString(rates[i].high, Digits),
            " L=", DoubleToString(rates[i].low, Digits),
            " C=", DoubleToString(rates[i].close, Digits));
     }

   Print("--- 期間末尾のバー ---");
   for(int i = MathMax(0, copied - 5); i < copied; i++)
     {
      Print("  ", TimeToString(rates[i].time, TIME_DATE | TIME_MINUTES),
            " O=", DoubleToString(rates[i].open, Digits),
            " H=", DoubleToString(rates[i].high, Digits),
            " L=", DoubleToString(rates[i].low, Digits),
            " C=", DoubleToString(rates[i].close, Digits));
     }
  }

書式3では開始日時と終了日時を直接指定するため、特定の期間に限定した分析に非常に便利です。この例では期間内の最高値・最安値の検出に加え、平均ボラティリティや出来高の統計情報も算出しています。なお、日時指定で取得した場合はArraySetAsSeries()を使わなければ古い順(時系列ではない順)で格納されます。

よくある使い方のポイント・注意事項

1. ArraySetAsSeries()の設定を忘れない

CopyRates()で取得した配列は、デフォルトでは古いデータから順番に格納されます。最新のバーを[0]としてアクセスしたい場合は、必ずCopyRates()の前にArraySetAsSeries(rates, true)を呼び出してください。

// 正しい使い方
MqlRates rates[];
ArraySetAsSeries(rates, true);  // ← CopyRatesの前に設定
CopyRates(Symbol(), PERIOD_CURRENT, 0, 10, rates);
// rates[0]が最新バー、rates[9]が10本前のバー

2. 戻り値のエラーチェックは必須

CopyRates()は、データが利用できない場合やサーバーとの接続が切れている場合に-1を返すことがあります。戻り値を必ずチェックしてからデータにアクセスしてください。

int copied = CopyRates(Symbol(), PERIOD_CURRENT, 0, 100, rates);
if(copied <= 0)
  {
   Print("データ取得失敗: ", GetLastError());
   return; // データがない状態で処理を続行しない
  }
// copiedの値を上限として配列にアクセスする
for(int i = 0; i < copied; i++)
  {
   // rates[i] を安全に使用できる
  }

3. 他の通貨ペアのデータ取得時の注意

現在のチャートとは異なる通貨ペアのデータを取得する場合、その通貨ペアのデータがダウンロード済みでないとエラーになることがあります。気配値表示ウィンドウに該当通貨ペアを追加し、過去のデータがダウンロードされていることを確認してください。

4. 配列サイズは自動調整される

CopyRates()に渡す配列は動的配列である必要があります。関数が自動的に必要なサイズにリサイズしてくれるため、事前にArrayResize()でサイズを指定する必要はありません。ただし、静的配列(固定サイズの配列)を渡すとエラーになる場合があるので注意してください。

// OK:動的配列
MqlRates rates[];
CopyRates(Symbol(), PERIOD_CURRENT, 0, 100, rates);

// NG:静的配列は避ける
// MqlRates rates[100]; ← CopyRatesには動的配列を使うこと

5. iOpen()等の従来関数との使い分け

MQL4にはiOpen()、iClose()、iHigh()、iLow()などの関数もありますが、複数のバーの情報をまとめて取得したい場合はCopyRates()の方が効率的です。1本のバー情報だけが必要な場合は従来の関数でも問題ありません。

// 1本だけ必要な場合 → 従来の関数が簡潔
double close_1 = iClose(Symbol(), PERIOD_CURRENT, 1);

// 複数本まとめて処理する場合 → CopyRates()が効率的
MqlRates rates[];
ArraySetAsSeries(rates, true);
CopyRates(Symbol(), PERIOD_CURRENT, 0, 100, rates);
// rates[0]~rates[99]まで一括でアクセス可能

6. インジケーターでの使用時はバッファとの混同に注意

カスタムインジケーター内でCopyRates()を使う場合、インジケーターバッファの配列とMqlRates配列のインデックス方向が一致しているか注意してください。ArraySetAsSeries()の設定がバッファと同じ方向になっていないと、バー番号がずれてしまいます。

まとめ

MqlRates構造体とCopyRates()関数は、MQL4でローソク足データを効率的に扱うための重要な機能です。以下のポイントを押さえておきましょう。

  • MqlRates構造体は1本のバーのOHLC、出来高、時刻、スプレッドを格納する
  • CopyRates()は3つの書式があり、位置指定・日時指定・期間指定で柔軟にデータ取得できる
  • ArraySetAsSeries()でインデックスの方向を制御できる
  • 戻り値のエラーチェックを必ず行い、安全にデータにアクセスする
  • 複数バーのデータをまとめて処理する場合に特に威力を発揮する