MQL5 CopyRatesの使い方と実践コード例|初心者向けに完全解説

目次

1. MQL5のCopyRatesとは何か

MQL5のCopyRates関数は、過去の価格データ(OHLC)をまとめて取得するための基本関数です。
EA(自動売買)やインジケーター開発において、「過去の価格を使って判断する」処理は必須であり、その中核になるのがCopyRatesです。

取得できるデータは以下の通りです。

  • Open(始値)
  • High(高値)
  • Low(安値)
  • Close(終値)
  • Time(時間)
  • TickVolume(ティック数)

これらはMqlRates構造体(複数の値をまとめたデータ形式)として配列に格納されます。

つまりCopyRatesは、
「チャートのローソク足データをそのままプログラムで扱うための関数」
と理解すれば問題ありません。


1.1 CopyRatesの基本機能

CopyRatesの最大の特徴は、複数本のローソク足データを一括取得できる点です。

例えば、以下のような処理が可能になります。

  • 直近100本の価格データを取得
  • 過去N本の高値・安値を比較
  • トレンド判定に使用

基本的な流れはシンプルです。

MqlRates rates[];
int copied = CopyRates(_Symbol, PERIOD_M1, 0, 100, rates);

このコードで行っていること:

  • _Symbol:現在の通貨ペア
  • PERIOD_M1:1分足
  • 0:最新バーから取得(0が最新)
  • 100:100本取得
  • rates:データ格納先

取得後は以下のようにアクセスできます。

double close_price = rates[0].close;
datetime time = rates[0].time;

ポイント

  • rates[0]は「最新のバー」
  • インデックスが増えるほど過去に遡る

1.2 CopyRatesと他関数の違い

初心者が最も混乱するのが、Copy系関数の違いです。

関数 内容 用途
CopyRates OHLCすべて 価格分析
CopyTime 時刻のみ 時間管理
CopyBuffer インジ値 指標取得

特に注意すべきポイント:

  • CopyRates=価格そのもの
  • CopyBuffer=インジケーターの計算結果

例えば:

  • 移動平均 → CopyBuffer
  • ローソク足 → CopyRates

この区別が曖昧だと、設計が破綻します。


1.3 使用される典型シーン

CopyRatesは、ほぼすべてのEAで使用されます。主な用途は以下の通りです。

シグナル生成

  • 過去データから条件判定
  • 例:高値更新・トレンド判定

バックテストロジック

  • 過去データを使った検証
  • ロジックの再現性確保

マルチタイムフレーム分析

  • 上位足のデータ取得
  • 例:1時間足で方向確認

よくある失敗・注意点

初心者がつまずくポイントはほぼ共通しています。

① 配列の型ミス

double arr[]; // NG

→ CopyRatesはMqlRates型のみ対応


② CopyBufferとの混同

  • 「インジケーター値が欲しいのにCopyRatesを使う」
  • 結果:意図しない値になる

③ インデックスの誤解

  • rates[0]=最新
  • rates[1]=1本前

これを逆に理解するとロジックが崩壊します。


④ データ未取得状態

  • 初回実行時はヒストリが不足する場合あり
  • VPSや初回起動時は特に注意

CopyRatesはシンプルですが、
「配列」「データ方向」「用途の違い」を理解していないとバグの原因になります。

2. CopyRatesの基本構文と引数

CopyRatesを正しく使うためには、構文と引数の意味を正確に理解することが最優先です。
ここが曖昧なまま進むと、データ取得ミス・ロジック崩壊・エラー多発につながります。


2.1 基本構文

CopyRatesの定義は以下の通りです。

int CopyRates(
   string symbol,
   ENUM_TIMEFRAMES timeframe,
   int start_pos,
   int count,
   MqlRates rates_array[]
);

戻り値は「取得できたデータ数」です。

  • 成功:取得件数(例:100)
  • 失敗:-1

2.2 各引数の意味

symbol(通貨ペア)

取得対象のシンボルを指定します。

"EURUSD"
_Symbol // 現在のチャート

実務では _Symbol が基本です。


timeframe(時間足)

どの時間足のデータを取得するかを指定します。

PERIOD_M1
PERIOD_M5
PERIOD_H1
PERIOD_D1

例:

  • スキャルピング → M1
  • トレンド判定 → H1

start_pos(開始位置)

ここが最重要ポイントです。

  • 0:最新バー
  • 1:1本前
  • 2:2本前

つまり:

start_pos = 0 → 最新から取得

インデックスと完全に連動しています。


count(取得数)

取得するバーの本数です。

100 → 100本取得

注意点:

  • 多すぎると失敗する場合あり
  • 必要最小限が基本

rates_array(格納配列)

MqlRates rates[];

ここにデータが格納されます。

必ず MqlRates型で定義する必要があります。


2.3 戻り値の扱い(最重要)

CopyRatesは必ず戻り値をチェックします。

int copied = CopyRates(_Symbol, PERIOD_M1, 0, 100, rates);

if(copied <= 0)
{
   Print("CopyRates failed: ", GetLastError());
}

戻り値の意味

意味
>0 取得成功
0 データなし
-1 エラー

実務ルール

  • 戻り値チェックは必須
  • エラー無視=本番で事故

よくある失敗・注意点

① start_posの誤解

start_pos = 0 // 最新

→ 「0が最古」と勘違いするケースが非常に多い


② 配列サイズ未確保

MqlRates rates[];

だけでは不十分なケースあり

ArrayResize(rates, 100);

※環境によりCopyRates側で拡張される場合もあるが、
明示的確保が安全


③ シンボル未選択

SymbolSelect(_Symbol, true);

これをしないと取得できないケースあり(特に他シンボル)


④ ヒストリ未ロード

  • 初回起動
  • VPS環境

→ データが存在しない

対策:

RefreshRates();

⑤ countの過剰指定

CopyRates(..., 10000, ...)

→ 失敗・遅延の原因


実務的な安全テンプレ

MqlRates rates[];
int count = 100;

ArrayResize(rates, count);

int copied = CopyRates(_Symbol, PERIOD_M1, 0, count, rates);

if(copied <= 0)
{
   Print("Error: ", GetLastError());
   return;
}

CopyRatesは「構文は簡単」ですが、
引数の理解とエラーハンドリングがすべてです。

3. CopyRatesの実用サンプルコード(初心者向け)

ここでは、そのまま使えるレベルの実用コードを提示します。
単なる例ではなく、「実務でそのまま流用できる安全な書き方」を前提に解説します。


3.1 最小構成サンプル(まずは動かす)

まずは、CopyRatesを使って直近100本のデータを取得して表示するコードです。

void OnTick()
{
   MqlRates rates[];
   int count = 100;

   ArrayResize(rates, count);

   int copied = CopyRates(_Symbol, PERIOD_M1, 0, count, rates);

   if(copied <= 0)
   {
      Print("CopyRates failed: ", GetLastError());
      return;
   }

   // 最新の終値を表示
   Print("Latest Close: ", rates[0].close);

   // 1本前の終値
   Print("Previous Close: ", rates[1].close);
}

このコードの動作

  • 1分足の直近100本を取得
  • 最新の終値をログ出力
  • 1本前と比較可能

初心者向けポイント

  • rates[0] → 最新
  • rates[1] → 1本前
  • OnTick()内で実行 → 常に最新更新

3.2 典型パターン①:高値・安値の取得

過去データを使った分析の基本は「最大・最小」です。

double highest = rates[0].high;
double lowest  = rates[0].low;

for(int i = 1; i < copied; i++)
{
   if(rates[i].high > highest)
      highest = rates[i].high;

   if(rates[i].low < lowest)
      lowest = rates[i].low;
}

Print("Highest: ", highest);
Print("Lowest: ", lowest);

用途

  • ブレイクアウト判定
  • レンジ分析
  • サポート/レジスタンス

3.3 典型パターン②:シンプルなトレンド判定

if(rates[0].close > rates[10].close)
{
   Print("Uptrend");
}
else
{
   Print("Downtrend");
}

ロジック

  • 現在と10本前を比較
  • 上昇 or 下降を判定

注意

  • ノイズが多い → 本番ではフィルタ必要
  • 単純比較は誤判定が多い

3.4 典型パターン③:マルチタイムフレーム

上位足のデータを取得する例です。

MqlRates h1_rates[];

ArrayResize(h1_rates, 50);

int copied = CopyRates(_Symbol, PERIOD_H1, 0, 50, h1_rates);

if(copied > 0)
{
   Print("H1 Close: ", h1_rates[0].close);
}

用途

  • 上位足でトレンド確認
  • 下位足でエントリー

よくある失敗・注意点

① 配列アクセスミス

rates[100] // 範囲外

copied以内でループする


② copied無視

for(int i = 0; i < 100; i++)

→ NG
必ず:

for(int i = 0; i < copied; i++)

③ データ未更新

  • OnInitだけで取得 → 古いデータ
  • OnTickまたはOnCalculateで更新する

④ 最新バーの未確定問題

rates[0]

→ 未確定バー(形成中)

対策:

rates[1] // 確定バー

⑤ パフォーマンス悪化

  • 毎Tickで大量取得
  • VPSで遅延

対策:

  • 必要本数だけ取得
  • 更新頻度を制御

実務での基本設計

最低限これを守る:

  • CopyRatesは必要最小限
  • copiedチェック必須
  • 配列範囲厳守
  • 未確定バーの扱いを明確化

CopyRatesは「動くコード」は簡単に書けますが、
実務で使えるコードにするには細部の設計が重要です。

4. CopyRatesの応用例(EAロジックへの組み込み)

ここでは、CopyRatesを単なるデータ取得ではなく、実際のトレードロジックに組み込む方法を解説します。
重要なのは「取得 → 判定 → 実行」という流れを明確にすることです。


4.1 基本設計:データ取得 → 条件判定 → 発注

EAの基本構造は以下の3段階です。

  1. データ取得(CopyRates)
  2. 条件判定(ロジック)
  3. 注文処理(OrderSendなど)

まずはシンプルな例です。

void OnTick()
{
   MqlRates rates[];
   int count = 20;

   ArrayResize(rates, count);

   int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

   if(copied <= 0)
      return;

   // シグナル判定
   bool buy_signal = rates[1].close > rates[5].close;
   bool sell_signal = rates[1].close < rates[5].close;

   if(buy_signal)
   {
      Print("BUY signal");
   }
   else if(sell_signal)
   {
      Print("SELL signal");
   }
}

ポイント

  • rates[1]を使用 → 確定足ベース
  • 過去比較 → シンプルなトレンド判定
  • シグナルと実行を分離

4.2 応用①:ブレイクアウト戦略

過去高値を更新したらエントリーする例です。

double highest = rates[1].high;

for(int i = 2; i < copied; i++)
{
   if(rates[i].high > highest)
      highest = rates[i].high;
}

// 現在価格が過去高値を超えたら
if(rates[0].high > highest)
{
   Print("Breakout BUY");
}

ロジックの意味

  • 過去の最大値を取得
  • 現在がそれを突破 → 上昇トレンド発生

注意

  • フェイクブレイク多発
  • ボラティリティ考慮が必要

4.3 応用②:レンジ判定

相場がトレンドかレンジかを判断する例です。

double highest = rates[1].high;
double lowest  = rates[1].low;

for(int i = 2; i < copied; i++)
{
   if(rates[i].high > highest)
      highest = rates[i].high;

   if(rates[i].low < lowest)
      lowest = rates[i].low;
}

double range = highest - lowest;

if(range < 0.0010)
{
   Print("Range market");
}
else
{
   Print("Trending market");
}

ポイント

  • 高値・安値の差で判断
  • 閾値は通貨ペアに依存(環境により異なる)

4.4 応用③:マルチタイムフレーム戦略

上位足で方向を決め、下位足でエントリーするパターンです。

MqlRates h1[];
ArrayResize(h1, 50);

int copied_h1 = CopyRates(_Symbol, PERIOD_H1, 0, 50, h1);

if(copied_h1 <= 0)
   return;

// 上位足トレンド
bool uptrend = h1[1].close > h1[10].close;

// 下位足(M5)
MqlRates m5[];
ArrayResize(m5, 20);

int copied_m5 = CopyRates(_Symbol, PERIOD_M5, 0, 20, m5);

if(copied_m5 <= 0)
   return;

// エントリー条件
if(uptrend && m5[1].close > m5[2].close)
{
   Print("MTF BUY");
}

ロジック構造

  • H1 → 方向
  • M5 → タイミング

よくある失敗・注意点

① 未確定バーを使う

rates[0]

→ ノイズが多く誤判定

対策:

rates[1]

② ロジックとデータ取得が混在

  • CopyRatesを複数回呼ぶ
  • 可読性低下

対策:

  • 最初にまとめて取得
  • ロジックは分離

③ 条件が単純すぎる

rates[1].close > rates[2].close

→ 勝てないロジック


④ 過去データの取りすぎ

  • パフォーマンス低下
  • VPS負荷増加

⑤ シンボル・時間足の不一致

  • 異なる足で比較
  • 意図しない結果

実務的な設計原則

CopyRatesをEAに組み込む際は以下を徹底します。

  • データ取得は最小回数
  • 確定足ベースで判定
  • ロジックは分離
  • 時間足の役割を明確化

CopyRatesは単なる取得関数ではなく、
EAロジックの土台(データ層)そのものです。

5. CopyRatesを使うときのパフォーマンス最適化と設計上の注意点

CopyRatesは便利ですが、使い方を誤るとEAの動作が重くなります
特に、毎Tickごとに大量データを取得する設計は、バックテスト速度・VPS負荷・実運用安定性の面で不利です。

この章では、実務で壊れにくい設計を前提に、CopyRates運用時の最適化ポイントを整理します。


5.1 必要な本数だけ取得する

最も基本的で重要なのは、必要以上のバーを取得しないことです。

例えば、直近20本しか使わないロジックなのに、毎回500本や1000本を取得すると無駄が発生します。

悪い例

MqlRates rates[];
ArrayResize(rates, 1000);
int copied = CopyRates(_Symbol, PERIOD_M5, 0, 1000, rates);

良い例

MqlRates rates[];
int count = 20;

ArrayResize(rates, count);
int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

実務上の考え方

  • 高値・安値判定だけなら20〜100本程度で十分なことが多い
  • 長期フィルターを使う場合のみ本数を増やす
  • 本数は「ロジックに必要な最小限」で決める

5.2 毎Tickで無駄に再取得しない

初心者がやりがちな失敗は、新しいバーができていないのに毎Tickで同じデータを取り直すことです。
価格が1回動くたびにCopyRatesを呼ぶと、EAが無駄に重くなります。

改善方針

  • 「新しいバーが出たときだけ」取得する
  • Tickごとの細かい判定が不要なら更新回数を減らす

例:新バー時のみ更新

datetime last_bar_time = 0;

void OnTick()
{
   MqlRates rates[];
   ArrayResize(rates, 2);

   int copied = CopyRates(_Symbol, PERIOD_M5, 0, 2, rates);
   if(copied <= 0)
      return;

   if(rates[0].time == last_bar_time)
      return;

   last_bar_time = rates[0].time;

   Print("New bar detected");
}

この方法の利点

  • 無駄な計算を減らせる
  • バックテストが軽くなる
  • ロジックの再現性が上がる

5.3 確定足ベースで判定する

CopyRatesで取得した rates[0] は、通常は形成中のバーです。
形成中バーは値が変わるため、これを直接売買判定に使うとシグナルがぶれます。

基本ルール

  • 売買判定は rates[1] 以降を使う
  • rates[0] は監視用、rates[1] は確定判定用と分ける

bool buy_signal = rates[1].close > rates[2].high;

よくある失敗

  • rates[0] の終値で判定してエントリーが不安定になる
  • バックテストではよく見えても実運用で崩れる

これはCopyRatesの問題ではなく、バーの性質を無視した設計ミスです。


5.4 配列の扱いを固定化する

CopyRatesは MqlRates 配列にデータを入れますが、
配列の向きや参照ルールが曖昧だと、後からバグを生みやすくなります。

推奨ルール

  • rates[0] を最新として扱う前提を統一する
  • ロジック全体で同じ参照ルールを使う
  • コメントで意味を残す

// rates[0] = current forming bar
// rates[1] = last closed bar
// rates[2] = previous closed bar

注意点

  • チーム開発や後日修正時に特に重要
  • 「自分だけ分かる」状態を避ける

5.5 他シンボル・他時間足は初回取得に注意する

現在のチャート以外のシンボルや時間足を使う場合、初回はヒストリ不足で失敗することがあります
特にVPS移行直後や端末起動直後は注意が必要です。

MqlRates h1_rates[];
ArrayResize(h1_rates, 50);

int copied = CopyRates("EURUSD", PERIOD_H1, 0, 50, h1_rates);
if(copied <= 0)
{
   Print("H1 data not ready: ", GetLastError());
   return;
}

実務上の対策

  • 戻り値を必ず確認する
  • 他シンボル利用時は SymbolSelect() も検討する
  • 初回失敗を前提にリトライ可能な設計にする

5.6 ロジック関数とデータ取得処理を分ける

CopyRatesをロジックの中に直接何度も書くと、コードが読みにくくなります。
実務では、取得処理と判定処理を分ける方が保守しやすいです。

悪い例

  • 条件式の中でCopyRatesを何回も呼ぶ
  • 取得・判定・発注が1つの塊になる

良い方向

  • 先に必要データを取得
  • その後でシグナル判定
  • 最後に注文処理

イメージ

bool LoadRates(MqlRates &rates[], int count)
{
   ArrayResize(rates, count);
   int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);
   return (copied > 0);
}

この分離をしておくと、

  • デバッグしやすい
  • エラー原因を切り分けやすい
  • ロジック差し替えが簡単

という利点があります。


よくある失敗・注意点

① 毎Tickで大量取得する

  • 無駄が多い
  • VPS負荷が増える

② 確定足と未確定足を混同する

  • 実運用でシグナルが安定しない

③ 戻り値を見ない

  • データが取れていないのに処理を進める
  • array out of range の原因になりやすい

④ 他時間足・他シンボルの初回失敗を想定しない

  • テストでは動くが本番で止まることがある

⑤ 取得と判定を混ぜる

  • 修正時に壊れやすい
  • バグ調査に時間がかかる

CopyRatesは高速な基本関数ですが、
設計が悪いとEA全体の品質を下げます
逆に、取得本数・更新頻度・確定足判定を整理するだけで、かなり安定した実装になります。

6. CopyRatesでよくあるエラーと対処法

CopyRatesは構文自体は単純ですが、実際の開発では「取得できない」「値がおかしい」「配列エラーになる」といった問題が頻発します。
この章では、初心者が特につまずきやすいエラーを、原因 → 対処の順で整理します。


6.1 戻り値が -1 になる

CopyRatesの戻り値が -1 の場合、取得失敗です。
このときは、そのまま配列を読んではいけません

失敗例

MqlRates rates[];
ArrayResize(rates, 100);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, 100, rates);

// copiedが-1でもそのまま使ってしまう
Print(rates[0].close);

安全な書き方

MqlRates rates[];
ArrayResize(rates, 100);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, 100, rates);

if(copied <= 0)
{
   Print("CopyRates failed: ", GetLastError());
   return;
}

主な原因

  • シンボルが利用可能になっていない
  • 指定時間足のヒストリが未読込
  • 取得本数が過大
  • 起動直後でデータ準備が未完了

6.2 データが0件しか取れない

-1 ではなく 0 の場合は、エラーというより取得対象がまだ存在しないケースが多いです。

典型例

  • 他シンボルの初回取得
  • 稼働直後のVPS
  • 市場休止中で履歴反映が不十分
  • カスタムシンボルや特殊環境

対処例

MqlRates rates[];
ArrayResize(rates, 50);

int copied = CopyRates("EURUSD", PERIOD_H1, 0, 50, rates);

if(copied == 0)
{
   Print("No data loaded yet");
   return;
}

実務上の考え方

  • 0件 は珍しくない
  • 初回は失敗前提で設計する
  • 数秒後や次のTickで再試行できる構造にする

6.3 array out of range が出る

CopyRates周辺で最も多い実行時エラーの1つです。
原因は単純で、取得できた本数以上を読んでいることです。

失敗例

MqlRates rates[];
ArrayResize(rates, 100);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, 100, rates);

if(copied <= 0)
   return;

// copied が 2 しかないのに 10本前を読む
Print(rates[10].close);

安全な書き方

if(copied > 10)
{
   Print(rates[10].close);
}

ループも注意

for(int i = 0; i < copied; i++)
{
   Print(rates[i].close);
}

よくある原因

  • count=100 を指定したから100本あると思い込む
  • 実際にはヒストリ不足で数本しかない
  • copiedを見ずに固定インデックスを使う

6.4 最新バーと確定バーを混同する

「値は取れているのにロジックがおかしい」という場合、
rates[0]rates[1] の使い分けミスが多いです。

基本ルール

  • rates[0]:形成中のバー
  • rates[1]:直近の確定バー

失敗しやすい例

if(rates[0].close > rates[1].high)
{
   Print("BUY");
}

この条件は、バー形成中に何度も変化します。
バックテストでは良く見えても、本番では安定しないことがあります。

安全寄りの例

if(rates[1].close > rates[2].high)
{
   Print("BUY");
}

注意点

  • 裁量補助なら rates[0] 利用もあり得る
  • 自動売買の条件判定は rates[1] 以降が基本

6.5 他シンボル・他時間足で取れない

現在のチャート以外のデータを取る場合、現チャートと同じ感覚では動かないことがあります。

MqlRates usdjpy[];
ArrayResize(usdjpy, 50);

int copied = CopyRates("USDJPY", PERIOD_H1, 0, 50, usdjpy);

これで失敗することがあります。

対策

  • シンボルが端末で利用可能か確認
  • 必要に応じて SymbolSelect() を使う
  • 初回取得失敗を想定する

SymbolSelect("USDJPY", true);

MqlRates usdjpy[];
ArrayResize(usdjpy, 50);

int copied = CopyRates("USDJPY", PERIOD_H1, 0, 50, usdjpy);

if(copied <= 0)
{
   Print("USDJPY H1 not ready: ", GetLastError());
   return;
}

6.6 取得本数が多すぎて重い

動作はするが重い場合、CopyRatesの設計が無駄になっていることがあります。

重い例

MqlRates rates[];
ArrayResize(rates, 5000);
int copied = CopyRates(_Symbol, PERIOD_M1, 0, 5000, rates);

改善方針

  • 必要最小限の本数にする
  • 毎Tickで取り直さない
  • 新バー確定時のみ更新する

int count = 50;
ArrayResize(rates, count);
int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

6.7 GetLastErrorだけで原因を断定しない

CopyRatesが失敗したとき、GetLastError() は有用ですが、
それだけで根本原因を断定しない方が安全です。

理由:

  • 直前の別処理の影響が残る場合がある
  • 実際にはヒストリ未読込や環境依存のこともある
  • VPS・ブローカー・端末状態で挙動が異なることがある

実務的な見方

  • 戻り値
  • 取得本数
  • シンボル
  • 時間足
  • 実行タイミング

をまとめて確認する方が再現性があります。


6.8 実務で使う最低限の安全テンプレ

迷ったら、まずは以下の形で実装すると事故が減ります。

MqlRates rates[];
int count = 50;

ArrayResize(rates, count);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

if(copied <= 0)
{
   Print("CopyRates failed. error=", GetLastError());
   return;
}

if(copied < 3)
{
   Print("Not enough bars");
   return;
}

// 確定バーのみ使用
double last_close = rates[1].close;
double prev_close = rates[2].close;

Print("last_close=", last_close, " prev_close=", prev_close);

この形なら、少なくとも以下を防げます。

  • 取得失敗なのに読み進める
  • 本数不足で範囲外アクセス
  • 未確定バーでの誤判定

よくある失敗・注意点

count を指定した本数が必ず取れると思い込む

→ 実際には copied が真実

rates[0] を確定値だと思う

→ 形成中バーであることが多い

③ 失敗時も処理続行

array out of range の原因

④ 他シンボル取得を現チャートと同じ感覚で扱う

→ 初回読込失敗を見落としやすい

⑤ 毎Tickで大量取得

→ 重くなり、検証速度も落ちる


CopyRatesのトラブルは、ほとんどが
「戻り値未確認」「本数不足無視」「未確定バー誤用」
の3系統に集約されます。ここを押さえるだけで、実装の安定性はかなり上がります。

7. CopyRatesとCopyBufferの違いと使い分け

MQL5で最も混同されやすいのが、CopyRatesとCopyBufferの違いです。
この2つを誤って使うと、ロジック自体が成立しなくなるため、明確に切り分けて理解する必要があります。


7.1 結論:取得対象がまったく違う

まずは本質的な違いです。

関数 取得内容 用途
CopyRates ローソク足(OHLC) 価格分析
CopyBuffer インジケーター値 指標分析

CopyRates

  • 価格そのものを取得
  • 市場の「事実データ」

CopyBuffer

  • 指標の計算結果を取得
  • 市場の「加工データ」

7.2 CopyRatesの役割(基礎データ)

CopyRatesは、EAの基礎データ層です。

MqlRates rates[];
CopyRates(_Symbol, PERIOD_M5, 0, 50, rates);

取得できるのは:

  • Open
  • High
  • Low
  • Close
  • Time

使用例

  • 高値更新判定
  • トレンド分析
  • ボラティリティ計測

7.3 CopyBufferの役割(指標データ)

CopyBufferは、インジケーターの計算結果を取得します。

int handle = iMA(_Symbol, PERIOD_M5, 20, 0, MODE_SMA, PRICE_CLOSE);

double ma[];
CopyBuffer(handle, 0, 0, 50, ma);

使用例

  • 移動平均(MA)
  • RSI
  • MACD

7.4 よくある誤用パターン

① インジ値をCopyRatesで取ろうとする

// NG:MAをCopyRatesで取得しようとする

→ CopyRatesは価格しか持っていない


② 価格をCopyBufferで取ろうとする

// NG:価格をCopyBufferで取得

→ CopyBufferは指標専用


③ 両者の役割が曖昧

  • ロジックが混乱する
  • 設計が破綻する

7.5 実務的な使い分け

パターン①:価格だけで判断

→ CopyRatesのみ使用

if(rates[1].close > rates[2].high)
{
   Print("Breakout");
}

パターン②:指標ベース

→ CopyBuffer使用

if(ma[1] > ma[2])
{
   Print("MA Uptrend");
}

パターン③:組み合わせ(実務で最も多い)

// 価格取得
CopyRates(_Symbol, PERIOD_M5, 0, 50, rates);

// MA取得
CopyBuffer(handle, 0, 0, 50, ma);

// 条件判定
if(rates[1].close > ma[1])
{
   Print("BUY");
}

7.6 設計レベルでの違い(重要)

CopyRates

  • データ取得コスト:低
  • 処理負荷:軽い
  • 再現性:高い

CopyBuffer

  • インジ計算あり
  • ハンドル管理が必要
  • 初回ロードが遅いことがある

実務判断

  • 軽量EA → CopyRates中心
  • 複雑ロジック → CopyBuffer併用

7.7 どちらを優先すべきか

A案:CopyRates中心設計

  • シンプル
  • 高速
  • 再現性が高い

→ スキャルピング・軽量EA向き


B案:CopyBuffer併用設計

  • 高機能
  • 表現力が高い
  • 設計が複雑

→ スイング・複合ロジック向き


比較軸

観点 CopyRates CopyBuffer
速度
柔軟性
安定性
実装難易度

よくある失敗・注意点

① CopyBufferのハンドル未確認

INVALID_HANDLE のまま使用


② インジと価格の時間軸ズレ

  • CopyRatesとCopyBufferで時間足が違う
  • 意図しない判定になる

③ データ更新タイミングのズレ

  • CopyRatesは最新
  • CopyBufferは遅延することあり

④ 配列の同期ミス

  • ratesとmaのインデックスがズレる
  • 判定が崩れる

実務での最適構成

最も安定する構成は以下です。

  • CopyRatesで基礎データ取得
  • 必要な指標のみCopyBuffer
  • 判定は確定足ベースで統一

CopyRatesとCopyBufferは似ているようで、
役割が完全に異なる別物です。

ここを正しく分離できるかどうかで、
EAの設計品質は大きく変わります。

8. CopyRatesを実務で使うためのチェックリスト

CopyRatesはシンプルな関数ですが、実務では設計・運用・エラー管理まで含めて扱う必要があります。
ここでは、開発・検証・本番運用で使える実践チェックリストを整理します。


8.1 実装前チェック(設計段階)

データ設計

  • 使用する時間足は明確か
  • 必要なバー本数は最小限に絞っているか
  • 確定足(rates[1])を使う設計になっているか

ロジック設計

  • CopyRatesのデータとロジックが分離されているか
  • データ取得タイミング(毎Tick or 新バー)が決まっているか
  • 他時間足・他シンボル使用の有無を整理しているか

リスク

  • データ不足時の挙動を定義しているか
  • 初回取得失敗を想定しているか
  • VPS環境での動作を想定しているか

8.2 実装時チェック(コード品質)

基本ルール

if(copied <= 0)
   return;
  • 戻り値チェックを必ず入れているか
  • copied を使って配列範囲を管理しているか

配列管理

  • ArrayResize を適切に使っているか
  • 固定インデックスアクセスを避けているか

データの扱い

  • rates[0](未確定)と rates[1](確定)を区別しているか
  • インデックスの意味をコードコメントに残しているか

8.3 パフォーマンスチェック

無駄な処理の排除

  • 毎TickでCopyRatesを呼んでいないか
  • 必要以上のバー数を取得していないか

改善ポイント

  • 新バー検出ロジックを導入しているか
  • 重い処理を分離しているか

8.4 実運用チェック(VPS・本番)

初期動作

  • 起動直後にCopyRatesが失敗しても問題ない設計か
  • データ未取得時に停止しないか

環境依存

  • ブローカーによるヒストリ差を考慮しているか
  • スプレッド・流動性の違いで挙動が変わらないか

再現性

  • バックテストとフォワードで同じロジックになるか
  • 未確定バー依存の処理がないか

8.5 エラー対策チェック

最低限の防御コード

if(copied <= 0)
{
   Print("Error: ", GetLastError());
   return;
}

if(copied < 必要本数)
{
   Print("Not enough data");
   return;
}

確認すべき項目

  • GetLastErrorをログに出しているか
  • 異常時に安全に停止できるか
  • 無限ループや連続エラーにならないか

8.6 実務での推奨テンプレ構成

最も安定する構成は以下です。

MqlRates rates[];
int count = 50;

ArrayResize(rates, count);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

if(copied <= 0)
   return;

if(copied < 3)
   return;

// 確定バーのみ使用
double close1 = rates[1].close;
double close2 = rates[2].close;

// ロジック
if(close1 > close2)
{
   Print("Up");
}

8.7 最終チェック(重要)

実務では以下の3点を満たしているかが最重要です。

① データ取得が安定している

  • 取得失敗を考慮している
  • 初回・VPS環境で問題が出ない

② ロジックが再現性を持つ

  • 確定足ベースで判定
  • バックテストと一致する

③ パフォーマンスが適切

  • 無駄な取得がない
  • EAが軽量で安定

よくある失敗・注意点

① 「動くからOK」で終わる

→ 本番で壊れる原因


② copiedを無視

→ 配列エラーの温床


③ 未確定バー依存

→ バックテスト詐欺状態


④ 初回取得失敗を想定しない

→ VPSで停止


⑤ CopyRatesを乱用

→ パフォーマンス低下


CopyRatesは単体では単純ですが、
「設計」「エラー管理」「運用」まで含めて初めて実務レベルになります。

このチェックリストを満たすだけで、
初心者レベルから一段上の安定したEA設計に移行できます。

9. FAQ(よくある質問)

9.1 MQL5のCopyRatesとは何ですか?

CopyRatesは、指定したシンボルと時間足のローソク足データ(OHLC)をまとめて取得する関数です。EAやインジケーターで過去価格を扱う際の基本機能です。


9.2 CopyRatesで取得できるデータは何ですか?

以下の情報を取得できます。

  • 始値(Open)
  • 高値(High)
  • 安値(Low)
  • 終値(Close)
  • 時間(Time)
  • ティックボリューム(TickVolume)

これらはMqlRates構造体として配列に格納されます。


9.3 CopyRatesとCopyBufferの違いは何ですか?

  • CopyRates:価格データ(ローソク足)
  • CopyBuffer:インジケーターの計算結果

価格そのものを扱うならCopyRates、
移動平均やRSIなどの指標はCopyBufferを使います。


9.4 rates[0]とrates[1]の違いは何ですか?

  • rates[0]:現在形成中のバー(未確定)
  • rates[1]:直近の確定バー

自動売買では、基本的にrates[1]を使って判定するのが安全です。


9.5 CopyRatesが-1を返す原因は何ですか?

主な原因は以下です。

  • ヒストリデータ未読込
  • シンボル未選択
  • 取得本数が多すぎる
  • 起動直後でデータ準備中

対策として、戻り値チェックと再試行設計が重要です。


9.6 CopyRatesで配列エラー(array out of range)が出るのはなぜですか?

取得できた本数(copied)よりも大きいインデックスにアクセスしているのが原因です。

if(copied > 10)
{
   Print(rates[10].close);
}

のように、必ずcopiedを基準に範囲管理を行います。


9.7 CopyRatesは毎Tick呼ぶべきですか?

基本的には不要です。

  • 新しいバーが確定したタイミングのみ取得する方が効率的
  • 毎Tick取得はパフォーマンス低下の原因

EA設計では、更新頻度の制御が重要です。


9.8 CopyRatesで他通貨や他時間足のデータは取得できますか?

可能です。

CopyRates("USDJPY", PERIOD_H1, 0, 50, rates);

ただし、初回はヒストリ未読込で失敗する場合があるため、
SymbolSelectや再試行処理を併用するのが安全です。

This website stores cookies on your computer. These cookies are used to provide a more personalized experience and to track your whereabouts around our website in compliance with the European General Data Protection Regulation. If you decide to to opt-out of any future tracking, a cookie will be setup in your browser to remember this choice for one year.

Accept or Deny