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()でインデックスの方向を制御できる
- 戻り値のエラーチェックを必ず行い、安全にデータにアクセスする
- 複数バーのデータをまとめて処理する場合に特に威力を発揮する


