MQL5 CopyBufferの使い方と実践例|初心者向けに完全解説(コード付き)

目次

1. MQL5 CopyBufferとは何か

1.1 CopyBufferの基本概念

MQL5のCopyBufferは、インジケーターが内部で計算している値(バッファ)を取得するための関数です。
ここでいう「バッファ」とは、インジケーターが各バーごとに保持している数値配列のことを指します。

例えば以下のような指標は、すべて内部にバッファを持っています。

  • 移動平均(iMA)
  • RSI(iRSI)
  • MACD(iMACD)

これらの値は、チャート上に表示されるだけでなく、プログラムから取得して売買ロジックに利用することができます。そのために使うのがCopyBufferです。

重要なポイントは以下です。

  • MQL5ではインジケーターはhandle(ハンドル:識別子)で管理される
  • CopyBufferは、そのhandleから数値データをコピーする関数
  • 戻り値は「取得できたデータ数」

1.2 CopyBufferが必要になる理由

EA(自動売買プログラム)では、価格データだけでなくテクニカル指標を使った判断がほぼ必須になります。

例えば以下のようなロジックです。

  • 移動平均クロスでエントリー
  • RSIが70以上なら売り、30以下なら買い
  • MACDのシグナル交差で判断

これらを実装するためには、インジケーターの値を取得する処理が必要になります。

基本的な流れは以下です。

// 1. インジケーターのhandleを取得
int handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

// 2. 配列を用意
double buffer[];

// 3. データを取得
CopyBuffer(handle, 0, 0, 3, buffer);

このように、
「インジケーター生成 → CopyBufferで値取得 → 条件判定」
という構造が、MQL5のEA開発の基本パターンになります。


1.3 対象読者のつまずきポイント

CopyBufferは非常に重要な関数ですが、初心者が最もつまずきやすいポイントでもあります。
特に以下の点で混乱が発生します。

■ よくある混乱①:handleの意味が分からない

  • MQL4では直接値を取得できたが、MQL5ではhandle経由になる
  • handleは「インジケーターのインスタンスID」と考えると理解しやすい

■ よくある混乱②:buffer番号の意味が分からない

  • インジケーターによっては複数のラインを持つ
  • 例:MACD
    • 0 → メインライン
    • 1 → シグナルライン

■ よくある混乱③:値が取得できない

主な原因は以下です。

  • handleが正しく生成されていない
  • CopyBufferの戻り値を確認していない
  • インジケーターの計算がまだ完了していない(特にOnInit直後)
  • 配列サイズが不足している

■ よくある失敗(実務で多い)

  • CopyBufferを毎Tick無駄に呼びすぎる(パフォーマンス低下)
  • ArraySetAsSeriesを設定していない(インデックス逆転バグ)
  • start_posの意味を誤解している

CopyBufferは単なる関数ではなく、MQL5における「データ取得レイヤーの中核」です。
ここを正しく理解すると、EA開発の実装速度と安定性が大きく向上します。

2. CopyBufferの基本構文と引数

2.1 基本構文

CopyBufferは以下の形式で使用します。

int CopyBuffer(
   int       indicator_handle,
   int       buffer_num,
   int       start_pos,
   int       count,
   double    buffer[]
);

戻り値は「取得できたデータ数」です。
正常に取得できれば count と同じ値が返ります。


2.2 各引数の詳細解説

■ indicator_handle(インジケーターハンドル)

  • iMA / iRSI / iCustomなどで取得した値
  • インジケーターを識別するID
int handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

注意点:

  • INVALID_HANDLE の場合はエラー
  • OnInitで作るのが基本

■ buffer_num(バッファ番号)

  • どのラインの値を取得するか

例:

インジケーター buffer_num
iMA 0
iRSI 0
iMACD 0(メイン) / 1(シグナル)

例:

CopyBuffer(handle, 0, 0, 3, buffer);

注意点:

  • インジケーターごとに意味が異なる
  • 誤ると「値がズレる」ので要注意

■ start_pos(取得開始位置)

  • どのバーから取得するか(0が最新)
意味
0 現在バー
1 1本前
2 2本前

例:

CopyBuffer(handle, 0, 0, 1, buffer); // 最新1本
CopyBuffer(handle, 0, 1, 1, buffer); // 1本前

重要:

  • 0は未確定バー(形成中)の可能性あり
  • 確定値を使うなら1を使うことが多い

■ count(取得数)

  • 取得するデータの個数
CopyBuffer(handle, 0, 0, 3, buffer);

この場合:

buffer[0] → 現在
buffer[1] → 1本前
buffer[2] → 2本前

注意点:

  • 配列サイズ以上を指定するとエラー
  • 必要最小限にするのが基本

■ buffer[](格納先配列)

  • 結果を受け取る配列
double buffer[];
ArrayResize(buffer, 3);

または

double buffer[3];

2.3 よくあるミス

CopyBufferで最も多い失敗パターンを整理します。


■ ミス①:配列サイズ未確保

double buffer[];
CopyBuffer(handle, 0, 0, 3, buffer); // NG

対策:

ArrayResize(buffer, 3);

■ ミス②:戻り値をチェックしていない

CopyBuffer(handle, 0, 0, 3, buffer);

これだけでは不十分です。

対策:

int copied = CopyBuffer(handle, 0, 0, 3, buffer);
if(copied <= 0)
{
   Print("CopyBuffer error: ", GetLastError());
}

■ ミス③:start_posの誤解

  • 0を「確定値」と思って使う → バグの原因
  • 特にバックテストと実運用で差が出る

■ ミス④:buffer_numの誤指定

  • MACDなどでラインがズレる
  • 意図しない値になる

■ ミス⑤:handleの生成失敗

if(handle == INVALID_HANDLE)
{
   Print("handle error");
}

実務での最低チェックテンプレ

double buffer[];
ArrayResize(buffer, 3);

int copied = CopyBuffer(handle, 0, 0, 3, buffer);

if(copied != 3)
{
   Print("CopyBuffer failed: ", GetLastError());
   return;
}

CopyBufferは「引数の理解」が8割です。
ここを曖昧にすると、EAのロジック全体が崩れます。

3. CopyBufferの基本的な使い方(シンプル例)

3.1 iMA(移動平均)で値を取得する最小構成

まずは最もシンプルな例として、移動平均(iMA)の値を取得します。
「CopyBufferの動き」を理解するには、このパターンが最短です。

// グローバル変数
int ma_handle;

// 初期化
int OnInit()
{
   ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

   if(ma_handle == INVALID_HANDLE)
   {
      Print("iMA handle error");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

// 毎Tick処理
void OnTick()
{
   double ma_buffer[];
   ArrayResize(ma_buffer, 1);

   int copied = CopyBuffer(ma_handle, 0, 1, 1, ma_buffer);

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

   double ma_value = ma_buffer[0];

   Print("MA value: ", ma_value);
}

3.2 処理の流れ(重要)

上記コードは、以下の流れで動いています。

  • OnInitでインジケーター生成(handle取得)
  • OnTickでCopyBufferを実行
  • 配列に値を格納
  • 値をロジックで使用

特に重要なのは以下です。

  • handleは毎回作らない(OnInitで1回)
  • CopyBufferは必要なときだけ呼ぶ
  • 戻り値チェックは必須

3.3 start_pos=1を使う理由

CopyBuffer(ma_handle, 0, 1, 1, ma_buffer);

ここで 1 を使っている理由は非常に重要です。

状態
0 未確定(現在バー)
1 確定済み(1本前)

実務では基本的に:

  • シグナル判定 → 1を使う
  • 可視化・参考 → 0でもOK

理由:

  • 0はTickごとに変化するため、シグナルがブレる
  • バックテストと実運用で差が出る原因になる

3.4 よくある失敗(ここが最重要)

■ 失敗①:OnTickでhandleを作る

int handle = iMA(...); // NG

問題:

  • 毎Tickインジケーター生成 → パフォーマンス劣化
  • メモリリークの原因

■ 失敗②:ArrayResizeを忘れる

double buffer[];
CopyBuffer(...); // NG

必ず:

ArrayResize(buffer, 1);

■ 失敗③:戻り値を無視する

CopyBufferは失敗することがあります。

  • データ未ロード
  • handle不正
  • 通信遅延

必ずチェック:

if(copied <= 0)

■ 失敗④:ArraySetAsSeriesを理解していない

通常の配列:

buffer[0] → 古いデータ

Series配列:

ArraySetAsSeries(buffer, true);
buffer[0] → 最新データ

※ CopyBufferは基本的に「Series形式」で扱うのが安全


3.5 実務向け最適テンプレ

以下が実務で使われる最小構成です。

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

int copied = CopyBuffer(ma_handle, 0, 1, 2, buffer);

if(copied != 2)
{
   Print("CopyBuffer failed: ", GetLastError());
   return;
}

double current = buffer[0];
double prev    = buffer[1];

これで:

  • 最新確定値
  • 1本前

の比較が可能になります。


3.6 この段階で理解すべき本質

CopyBufferは以下の役割です。

  • インジケーターの「内部配列」をコピーする
  • 配列操作でロジックを組む
  • EAの判断材料を提供する

つまり:

👉 CopyBuffer = データ取得の入口

ここが不安定だと、すべてのロジックが崩れます。

4. CopyBufferの実践例(RSI・MACD)

4.1 iRSIの値を取得する例

RSIは単一バッファ(buffer_num=0)のため、CopyBufferの基本形をそのまま適用できます。

// グローバル
int rsi_handle;

// 初期化
int OnInit()
{
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

   if(rsi_handle == INVALID_HANDLE)
   {
      Print("RSI handle error");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

// Tick処理
void OnTick()
{
   double rsi_buffer[];
   ArrayResize(rsi_buffer, 2);
   ArraySetAsSeries(rsi_buffer, true);

   int copied = CopyBuffer(rsi_handle, 0, 1, 2, rsi_buffer);

   if(copied != 2)
   {
      Print("RSI CopyBuffer error: ", GetLastError());
      return;
   }

   double rsi_current = rsi_buffer[0];
   double rsi_prev    = rsi_buffer[1];

   // シンプルな条件例
   if(rsi_current > 70)
   {
      Print("Overbought");
   }
   else if(rsi_current < 30)
   {
      Print("Oversold");
   }
}

4.2 RSIでの注意点

■ つまずき①:0番を使ってしまう

CopyBuffer(rsi_handle, 0, 0, 1, buffer); // NG(ブレる)

→ 必ず start_pos=1 を使う


■ つまずき②:期間変更の影響

  • RSI期間(14など)によって値の性質が変わる
  • ロジックに直接影響する

👉 パラメータは外部入力にするのが基本


■ つまずき③:初期バー不足

  • 計算に必要なバー数が足りないと取得できない
  • 特にテスト初期で発生

対策:

if(Bars(_Symbol, PERIOD_CURRENT) < 50) return;

4.3 iMACDの値を取得する例(複数バッファ)

MACDは複数バッファを持つ代表例です。

buffer_num 内容
0 メインライン
1 シグナルライン
int macd_handle;

int OnInit()
{
   macd_handle = iMACD(_Symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE);

   if(macd_handle == INVALID_HANDLE)
   {
      Print("MACD handle error");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

void OnTick()
{
   double macd_main[];
   double macd_signal[];

   ArrayResize(macd_main, 2);
   ArrayResize(macd_signal, 2);

   ArraySetAsSeries(macd_main, true);
   ArraySetAsSeries(macd_signal, true);

   int copied_main   = CopyBuffer(macd_handle, 0, 1, 2, macd_main);
   int copied_signal = CopyBuffer(macd_handle, 1, 1, 2, macd_signal);

   if(copied_main != 2 || copied_signal != 2)
   {
      Print("MACD CopyBuffer error: ", GetLastError());
      return;
   }

   double main_current   = macd_main[0];
   double main_prev      = macd_main[1];
   double signal_current = macd_signal[0];
   double signal_prev    = macd_signal[1];

   // クロス判定
   if(main_prev < signal_prev && main_current > signal_current)
   {
      Print("Golden Cross");
   }

   if(main_prev > signal_prev && main_current < signal_current)
   {
      Print("Dead Cross");
   }
}

4.4 MACDでの重要ポイント

■ buffer_numの理解が最重要

  • 0と1を間違えるとロジックが崩壊
  • 見た目と一致しない原因になる

■ 同時取得のタイミング

  • mainとsignalは同じタイミングで取得する
  • バラバラにするとズレる可能性あり

■ 配列の同期

ArraySetAsSeries(..., true);

これを忘れると:

  • indexの意味が逆転
  • クロス判定が誤作動

4.5 実務での最適パターン

複数バッファの場合の定型パターン:

double buf1[], buf2[];
ArrayResize(buf1, 2);
ArrayResize(buf2, 2);

ArraySetAsSeries(buf1, true);
ArraySetAsSeries(buf2, true);

if(CopyBuffer(handle, 0, 1, 2, buf1) != 2) return;
if(CopyBuffer(handle, 1, 1, 2, buf2) != 2) return;

4.6 この章の本質

  • 単一バッファ → RSIのようにシンプル
  • 複数バッファ → MACDのように構造理解が必要

そして最重要は:

👉 「buffer_numの意味」と「配列の扱い」

ここを間違えると、正しく動いているように見えて誤動作するため、最も危険な領域です。

5. CopyBufferのエラー原因と対処法

5.1 CopyBufferが失敗する主な原因

CopyBufferは安定して動く関数ですが、実務では一定確率で失敗します。
重要なのは「なぜ失敗するか」を構造的に理解することです。

主な原因は以下の3系統に分かれます。

  • データ未準備(ヒストリ未ロード・計算未完了)
  • handle不正(生成失敗・解放済み)
  • 引数ミス(配列サイズ・buffer番号など)

5.2 代表的なエラー①:戻り値が0または-1

int copied = CopyBuffer(handle, 0, 1, 2, buffer);
if(copied <= 0)
{
   Print("Error: ", GetLastError());
}

■ 原因

  • データがまだ存在しない
  • インジケーターの計算が終わっていない
  • チャートの履歴が不足

■ 対処法

if(Bars(_Symbol, PERIOD_CURRENT) < 100)
{
   return;
}

または:

if(copied != count)
{
   return;
}

5.3 代表的なエラー②:ERR_INDICATOR_DATA_NOT_FOUND

■ 原因

  • 指定したバーにデータが存在しない
  • start_posが大きすぎる

例:

CopyBuffer(handle, 0, 10000, 1, buffer); // NG

■ 対処法

  • 取得範囲を現実的な値にする
  • Barsでチェック
int bars = Bars(_Symbol, PERIOD_CURRENT);
if(bars < 50) return;

5.4 代表的なエラー③:INVALID_HANDLE

if(handle == INVALID_HANDLE)

■ 原因

  • iMA / iRSIの生成失敗
  • パラメータ不正
  • シンボルや時間足の問題

■ 対処法

if(handle == INVALID_HANDLE)
{
   Print("Handle creation failed: ", GetLastError());
   return(INIT_FAILED);
}

5.5 代表的なエラー④:配列関連の問題

■ ケース1:サイズ不足

double buffer[];
CopyBuffer(...); // NG

→ 必ずArrayResize


■ ケース2:Series設定ミス

buffer[0] // どの位置か分からない

対策:

ArraySetAsSeries(buffer, true);

5.6 実務で最も多い「見えないバグ」

■ ケース:動いているが結果が間違っている

原因:

  • buffer_numミス
  • start_pos=0使用
  • Series未設定

これが最も危険です。

👉 エラーが出ないため発見が遅れる


5.7 安全な実装テンプレ(実務推奨)

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

// データ数チェック
if(Bars(_Symbol, PERIOD_CURRENT) < 100)
{
   return;
}

// CopyBuffer実行
int copied = CopyBuffer(handle, 0, 1, 2, buffer);

// 成功チェック
if(copied != 2)
{
   Print("CopyBuffer failed: ", GetLastError());
   return;
}

// 使用
double current = buffer[0];
double prev    = buffer[1];

5.8 デバッグの基本戦略

CopyBufferで問題が出た場合、以下の順で確認します。

  1. handleが正常か
  2. Barsが十分か
  3. CopyBufferの戻り値
  4. GetLastError
  5. bufferの中身をPrint
Print("value: ", buffer[0]);

5.9 環境依存の注意点

以下は環境によって挙動が変わることがあります。

  • ブローカーのデータ提供範囲
  • シンボルのヒストリ
  • VPSの通信状況
  • ストラテジーテスターの設定

👉 「同じコードでも結果が違う」原因になるため注意


この章の本質

CopyBufferのエラーは偶発ではなく、構造的に発生します。

  • データ未準備
  • 引数ミス
  • 状態管理不足

👉 この3つを潰せば、ほぼ安定します

6. CopyBufferの最適化と実務テクニック

6.1 CopyBufferは最小回数で呼ぶ

CopyBufferは軽量な関数ではありません。
毎Tick無制限に呼び続けると、CPU負荷増加・遅延・バックテスト速度低下につながります。

■ NGパターン

void OnTick()
{
   CopyBuffer(handle, 0, 1, 1, buffer); // 毎Tick実行
}

■ 推奨パターン(新バー判定)

datetime last_time = 0;

void OnTick()
{
   datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);

   if(current_time == last_time)
      return;

   last_time = current_time;

   // 新バー時のみ実行
   CopyBuffer(handle, 0, 1, 1, buffer);
}

👉 新バー単位で処理することで負荷を大幅削減


6.2 必要最小限のデータだけ取得する

■ NGパターン

CopyBuffer(handle, 0, 0, 100, buffer);

■ 推奨

CopyBuffer(handle, 0, 1, 2, buffer);

理由:

  • 多くのロジックは「現在+1本前」で十分
  • 無駄なデータ取得はパフォーマンス悪化

6.3 handleは再生成しない

■ NG

void OnTick()
{
   int handle = iMA(...); // 毎回生成
}

■ 正解

int handle;

int OnInit()
{
   handle = iMA(...);
}

👉 handleは状態として保持する


6.4 複数インジケーターの管理

実務では複数の指標を同時に使います。

■ 推奨構造

int ma_handle;
int rsi_handle;
int macd_handle;

■ 初期化でまとめて生成

ma_handle   = iMA(...);
rsi_handle  = iRSI(...);
macd_handle = iMACD(...);

■ OnTickでまとめて取得

CopyBuffer(ma_handle,   0, 1, 2, ma_buf);
CopyBuffer(rsi_handle,  0, 1, 2, rsi_buf);
CopyBuffer(macd_handle, 0, 1, 2, macd_buf);

👉 構造化することで保守性が向上


6.5 キャッシュ戦略(重要)

同じ値を何度も取得するのは非効率です。

■ 改善例

double ma_value;

void UpdateIndicators()
{
   double buffer[];
   ArrayResize(buffer, 1);

   if(CopyBuffer(ma_handle, 0, 1, 1, buffer) > 0)
   {
      ma_value = buffer[0];
   }
}

👉 値を変数に保持して使い回す


6.6 ArraySetAsSeriesは常に統一

■ 推奨ルール

ArraySetAsSeries(buffer, true);

理由:

  • indexの意味を統一できる
  • ロジックのバグを防ぐ

👉 「常にtrue」運用が最も安全


6.7 ストラテジーテスター最適化

バックテストではCopyBufferの使い方が結果に影響します。

■ 注意点

  • start_pos=0 → リペイントの原因
  • 過剰なCopyBuffer → テスト遅延

■ 推奨

  • start_pos=1固定
  • 新バー判定でのみ取得

6.8 実務での最終テンプレ

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

// 新バー判定
static datetime last_time = 0;
datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);

if(current_time == last_time)
   return;

last_time = current_time;

// データ取得
if(CopyBuffer(handle, 0, 1, 2, buffer) != 2)
{
   Print("CopyBuffer error: ", GetLastError());
   return;
}

// 使用
double current = buffer[0];
double prev    = buffer[1];

6.9 この章の本質

CopyBufferの最適化は以下の3点に集約されます。

  • 呼び出し回数を減らす
  • 取得データを最小化する
  • 状態(handle・値)を管理する

👉 これにより

  • パフォーマンス向上
  • バグ削減
  • 実運用安定性向上

が同時に達成されます。

7. CopyBufferと関連関数の関係

7.1 CopyBufferは単独では使わない

CopyBufferは重要な関数ですが、これ単体では完結しません
実際のMQL5開発では、以下の流れの一部として使います。

  • インジケーターを作成する
  • handleを取得する
  • CopyBufferで値を取り出す
  • 条件判定に使う

つまり、CopyBufferは「値を取り出す担当」であり、前段には必ず「インジケーター生成」があります。

典型的な流れは次の通りです。

int ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

double ma_buffer[];
ArrayResize(ma_buffer, 2);
ArraySetAsSeries(ma_buffer, true);

if(CopyBuffer(ma_handle, 0, 1, 2, ma_buffer) == 2)
{
   double ma_current = ma_buffer[0];
   double ma_prev    = ma_buffer[1];
}

この構造を理解しておくと、CopyBufferを「魔法の関数」のように誤解せずに済みます。


7.2 iMA・iRSI・iMACD・iCustomとの関係

CopyBufferと最も密接に関係するのは、handleを返す関数群です。
代表例は以下です。

  • iMA:移動平均
  • iRSI:RSI
  • iMACD:MACD
  • iCustom:カスタムインジケーター

これらはすべて、インジケーターそのものの値を直接返すのではなく、インジケーターのhandleを返します。
そのため、MQL5では次の2段階が基本になります。

■ 手順

    1. iMA などでhandleを取得する
    1. CopyBuffer で必要な値を配列へコピーする

たとえばiRSIなら次のようになります。

int rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

double rsi_buffer[];
ArrayResize(rsi_buffer, 1);
ArraySetAsSeries(rsi_buffer, true);

if(CopyBuffer(rsi_handle, 0, 1, 1, rsi_buffer) == 1)
{
   double rsi_value = rsi_buffer[0];
}

iCustomでも考え方は同じです。

int custom_handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator");

double custom_buffer[];
ArrayResize(custom_buffer, 1);
ArraySetAsSeries(custom_buffer, true);

if(CopyBuffer(custom_handle, 0, 1, 1, custom_buffer) == 1)
{
   double custom_value = custom_buffer[0];
}

■ よくある誤解

  • iMA を呼べばそのまま値が返ると思ってしまう
  • iCustom は特別で、CopyBuffer不要だと思ってしまう

どちらも誤りです。
handle型のインジケーターは、原則としてCopyBufferで値を取得する、と覚えるのが安全です。


7.3 CopyRates・CopyTimeとの違い

CopyBufferと名前が似ている関数に、CopyRatesCopyTime があります。
ここを混同すると、設計が崩れます。

■ CopyBuffer

  • 対象:インジケーターの計算結果
  • 取得先:インジケーターバッファ
  • 型:通常は double[]

■ CopyRates

  • 対象:価格データ一式
  • 取得先:OHLC(始値・高値・安値・終値)や出来高
  • 型:MqlRates[]
MqlRates rates[];
ArrayResize(rates, 2);

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

■ CopyTime

  • 対象:時間データ
  • 取得先:各バーの時刻
  • 型:datetime[]
datetime times[];
ArrayResize(times, 2);

int copied = CopyTime(_Symbol, PERIOD_CURRENT, 0, 2, times);

■ 使い分けの基準

  • 価格そのものが欲しい → CopyRates
  • 時刻が欲しい → CopyTime
  • RSIやMAなどの指標値が欲しい → CopyBuffer

この3つは用途が明確に異なります。
CopyBufferは価格データ取得関数ではなく、指標値取得関数です。


7.4 BarsCalculatedとの関係

CopyBufferの前に確認しておくと便利なのが BarsCalculated です。
これは、インジケーターが何本分計算済みかを確認する関数です。

int calculated = BarsCalculated(ma_handle);

if(calculated < 20)
{
   return;
}

これは特に次の場面で有効です。

  • OnInit直後
  • カスタムインジケーター利用時
  • マルチタイムフレーム処理時
  • テスター初期バー不足時

■ よくある失敗

  • CopyBufferだけ実行して失敗理由が分からない
  • 実際は「まだ計算されていないだけ」

そのため、安定性を重視するなら次のような順番が有効です。

  • handle生成
  • BarsCalculated(handle) を確認
  • 十分なら CopyBuffer

7.5 IndicatorReleaseとの関係

生成したhandleは、不要になったら解放するのが原則です。
そのために使うのが IndicatorRelease です。

void OnDeinit(const int reason)
{
   if(ma_handle != INVALID_HANDLE)
   {
      IndicatorRelease(ma_handle);
   }
}

■ なぜ必要か

  • 不要なインジケーターを保持し続けないため
  • リソース管理を明確にするため
  • 長時間稼働EAでの安定性向上のため

■ 注意点

  • 解放後のhandleをCopyBufferで使うと失敗する
  • 再初期化時にhandleを作り直す必要がある

7.6 実務での位置付け

実務で見ると、CopyBufferは次のような層に位置します。

  • 価格データ層:CopyRates / CopyTime
  • 指標データ層:CopyBuffer
  • 売買判断層:シグナル判定
  • 執行層:OrderSend など

この分離ができているEAは強いです。
逆に、すべてをOnTickにベタ書きすると、以下の問題が起きやすくなります。

  • バグ発見が難しい
  • 指標差し替えが面倒
  • 最適化しにくい
  • 再利用性が低い

そのため、CopyBufferは単なる取得関数ではなく、EA設計における「指標データ取得モジュールの中核」として扱うのが実務的です。


7.7 この章で押さえるべき結論

CopyBufferの周辺関数を整理すると、役割は次のように分かれます。

  • iMA / iRSI / iMACD / iCustom
    → handleを作る
  • BarsCalculated
    → 計算済み本数を確認する
  • CopyBuffer
    → 指標値を取り出す
  • IndicatorRelease
    → handleを解放する
  • CopyRates / CopyTime
    → 価格や時間を取得する

この関係が整理できると、CopyBufferだけでなく、MQL5のデータ取得全体の構造が見えてきます。

8. FAQ(よくある質問と解決方針)

8.1 CopyBufferとは何ですか?

回答方針:
インジケーターの計算結果(バッファ)を配列として取得する関数であることを説明する。
EAでテクニカル指標を使うための必須機能である点も補足する。


8.2 CopyBufferで値が取得できないのはなぜですか?

回答方針:
主な原因を3つに整理する。

  • データ未ロード(Bars不足)
  • handle不正(INVALID_HANDLE)
  • CopyBuffer戻り値未確認

対処として「戻り値チェック」と「Bars確認」を提示する。


8.3 start_posは0と1どちらを使うべきですか?

回答方針:
原則として「1(確定バー)」を使う理由を説明する。

  • 0は未確定で変動する
  • バックテストと実運用のズレ原因になる

例外として、リアルタイム監視用途では0も可と補足する。


8.4 buffer_numとは何ですか?

回答方針:
インジケーター内のライン番号であることを説明する。

  • RSI → 0のみ
  • MACD → 0(メイン)、1(シグナル)

誤るとロジックが崩れるため、最重要ポイントであることを強調する。


8.5 ArraySetAsSeriesは必要ですか?

回答方針:
実務では「true固定」を推奨する理由を説明する。

  • buffer[0]=最新データになる
  • インデックスの混乱を防ぐ

設定しない場合のバグリスクも明示する。


8.6 CopyBufferは毎Tick呼んでも問題ないですか?

回答方針:
技術的には可能だが非推奨であることを説明する。

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

対策として「新バー判定での実行」を提示する。


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

回答方針:
用途の違いを明確にする。

  • CopyBuffer → インジケーター値
  • CopyRates → 価格データ(OHLC)

混同すると設計ミスになる点を補足する。


8.8 iCustomでもCopyBufferは必要ですか?

回答方針:
必要であることを明確にする。

  • iCustomはhandleを返すだけ
  • 値取得にはCopyBufferが必須

初心者の誤解ポイントとして強調する。


8.9 CopyBufferの最小構成は何ですか?

回答方針:
実務で使える最小テンプレを提示する。

double buffer[];
ArrayResize(buffer, 1);
ArraySetAsSeries(buffer, true);

if(CopyBuffer(handle, 0, 1, 1, buffer) == 1)
{
   double value = buffer[0];
}

「これを基準に拡張すればよい」という位置づけで説明する。

9. まとめ(実務で迷わないための要点整理)

9.1 CopyBufferの役割を一言で

CopyBufferは、インジケーターの値を取得するための唯一の入口です。
MQL5では「指標=handle管理」が前提のため、CopyBufferを理解しないとEAは成立しません。


9.2 実務で守るべきルール(最重要)

以下のルールを守るだけで、ほとんどのバグは防げます。

■ ルール①:handleはOnInitで作る

int handle;

int OnInit()
{
   handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
}

■ ルール②:start_posは基本1

CopyBuffer(handle, 0, 1, 2, buffer);

理由:

  • 0は未確定で不安定
  • 実運用とテストの乖離を防ぐ

■ ルール③:ArraySetAsSeriesはtrue固定

ArraySetAsSeries(buffer, true);

■ ルール④:戻り値は必ずチェック

if(CopyBuffer(handle, 0, 1, 2, buffer) != 2)
{
   return;
}

■ ルール⑤:新バーでのみ実行

if(current_time == last_time) return;

9.3 よくある失敗と回避

■ 見た目正常だがロジックが間違っている

原因:

  • buffer_numミス
  • indexの逆転
  • start_pos=0

👉 最も危険なバグ


■ 値が取得できない

原因:

  • Bars不足
  • handleエラー
  • CopyBuffer失敗

👉 戻り値+GetLastErrorで必ず検証


■ EAが重い・遅い

原因:

  • CopyBuffer多用
  • 無駄なデータ取得
  • 毎Tick実行

👉 新バー処理+最小データ取得で改善


9.4 CopyBufferの構造的理解

CopyBufferは以下の構造の一部です。

  • handle生成(iMA / iRSI / iCustom)
  • データ取得(CopyBuffer)
  • 判定ロジック
  • 発注処理

👉 この「分離」ができると、EAの品質が一段上がります。


9.5 最終テンプレ(これだけ覚えればOK)

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

if(Bars(_Symbol, PERIOD_CURRENT) < 100)
   return;

if(CopyBuffer(handle, 0, 1, 2, buffer) != 2)
   return;

double current = buffer[0];
double prev    = buffer[1];

9.6 本記事の本質

CopyBufferで重要なのは以下の3点です。

  • 構文ではなく「使い方」
  • 値ではなく「配列の扱い」
  • 関数ではなく「設計」

これを理解できれば、

  • RSI・MACD・ボリンジャーバンド
  • カスタムインジケーター
  • マルチタイムフレーム

すべて同じ構造で扱えるようになります。

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