- 1 1. MQL5のCopyRatesとは何か
- 2 2. CopyRatesの基本構文と引数
- 3 3. CopyRatesの実用サンプルコード(初心者向け)
- 4 4. CopyRatesの応用例(EAロジックへの組み込み)
- 5 5. CopyRatesを使うときのパフォーマンス最適化と設計上の注意点
- 6 6. CopyRatesでよくあるエラーと対処法
- 7 7. CopyRatesとCopyBufferの違いと使い分け
- 8 8. CopyRatesを実務で使うためのチェックリスト
- 9 9. FAQ(よくある質問)
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段階です。
- データ取得(CopyRates)
- 条件判定(ロジック)
- 注文処理(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や再試行処理を併用するのが安全です。