FX

MQL5のCopyBufferでFX自動売買の指標値を取得・確認する方法

指標チャートと取得した二件のデータを比較するイメージ

MQL5のCopyBufferは、指標ハンドルで指定したインジケーターの値を、配列へ取り出す関数です。FX自動売買で移動平均やRSIなどの値を使うときは、ハンドルが作れたこと、必要な件数をコピーできたこと、配列のどこに目的の足が入ったかを分けて確認します。

特に「配列の0番はいつも現在足」という思い込みには注意が必要です。開始位置、取得件数、配列の参照方向によって、添字が指す足は変わります。この記事では固定長配列で確定足二本を読む例を使い、コピーの成否、計算待ち、同じ日時での値の照合まで説明します。

この記事のポイント
  • 指標ハンドルと実際の数値を区別して取得できる
  • 開始位置・取得件数・配列の添字の対応が分かる
  • 計算待ちと取得失敗を分けて未取得値を使わずに済む
  • 発注しないEAの例で確定足の値を照合できる

MQL5のCopyBufferとFX自動売買の値取得

指標ハンドルから配列へ値を取り出す模式図
操作場面のイメージ画像です。実際の画面ではありません。

ハンドルと指標値は別のもの

MQL5のiMAなどの指標関数は、計算した数値そのものではなく指標ハンドルを返します。ハンドルは、どの計算対象を使うかを示す識別子のようなものです。ハンドルの数値を移動平均の価格として比較しても、目的の売買条件にはなりません。

処理は、初期化時にハンドルを作成し、使用するときにCopyBufferで値を取り出し、不要になったらハンドルを解放する流れです。毎ティック新しいハンドルを作る前提にせず、同じ条件で使う計算対象は保持して再利用します。

ハンドルの作成に失敗した場合はINVALID_HANDLEを確認し、値取得へ進めません。ただし、ハンドルが有効だからといって、その瞬間に必要な履歴と指標計算がすべて準備できたとは限りません。作成成功とデータ取得成功を別の確認段階にする理由です。

MQL4では同名の指標関数が直接数値を返すことがあるため、コードを移した際に混乱しやすい部分です。移植全体の設計はMQL4からMQL5へEAを移植する手順で扱っています。本記事ではハンドル作成後の値の取り出しと、受け取った配列の読み方に絞ります。

具体的なハンドル作成の引数はMetaQuotesのiMAリファレンスを確認してください。銘柄、時間足、期間、計算方法、適用価格が変われば、別の条件の移動平均になります。値を比較するときは、ハンドルを作ったときの条件までそろえる必要があります。

バッファ番号と開始位置を分ける

よく使う書式はCopyBuffer(handle, buffer_num, start_pos, count, values)です。buffer_numは指標の中のどのバッファを読むか、start_posはどの足から読み始めるか、countは何件取り出すかを表します。三つとも整数ですが、それぞれ違う対象を指定します。

例えば移動平均のバッファ0を、一本前の足から二本分読むなら、バッファ番号0、開始位置1、件数2です。バッファ番号1に変えても一本前の足になるわけではありません。複数の線を持つ指標では、線とバッファ番号の対応を指標の仕様で確認します。

引数指定するもの混同しやすい点
indicator_handle作成済みの指標ハンドル指標値そのものではない
buffer_num指標内のバッファ番号何本前かの指定ではない
start_pos読み始める足の位置0は現在足、1は一本前
countコピーしたい件数時間足や日数の指定ではない
buffer配列数値を受け取る先添字と足の対応を確認する

CopyBufferには時刻や期間を指定する別の書式もありますが、最初は開始位置と件数の書式を一つに絞ると理解しやすくなります。引数を一度に変えて原因を探すより、同じ指標の同じバッファを一件だけ読み、その後に二件へ増やす順番がよいでしょう。

戻り値はコピーできたデータの件数です。二件を要求したなら、二件取得できたことを確認してから配列を読みます。正の値が返ったことだけで、要求した数がすべてそろったとは判断しません。仕様はMetaQuotes公式のCopyBufferを基準にしてください。

固定長配列では古い値が先頭に入る

CopyBufferは、コピー対象の中で古いデータを物理メモリの先頭へ配置します。配列の参照方向を変えていない固定長のdouble values[2]へ、開始位置1、件数2でコピーすると、values[0]は二本前、values[1]は一本前です。現在足の値はこの二件には含まれません。

古い足と新しい足を配列の位置へ対応させる模式図
操作場面のイメージ画像です。実際の画面ではありません。
呼び出し条件通常方向の固定長配列入っている足
start_posが1・countが2values[0]二本前の確定足
start_posが1・countが2values[1]一本前の確定足
start_posが0・countが2values[0]一本前の確定足
start_posが0・countが2values[1]現在足

「コピー対象を数える方向」と「受け取り配列を添字で読む方向」を分けて考えるのがポイントです。開始位置0は現在足から対象を選ぶという指定であり、受け取った配列の0番が無条件に現在足になるという意味ではありません。

動的配列にはArraySetAsSeriesで参照方向を指定できますが、物理的にコピーされる順序そのものを逆にする操作ではありません。また、固定長配列や多次元配列へ同じ設定を使えるわけではありません。今回の例では固定長二件の通常方向に統一し、対応をコード中に明記します。

自分の実装で動的配列を使うなら、ArraySetAsSeriesの公式仕様を確認し、設定の成否と実際の参照方向まで調べます。二つの方式を途中で混ぜると、見た目には正常な数値でも、別の足の値を売買条件へ渡してしまいます。

件数不足と計算待ちは見送る

履歴の読込や指標の計算が終わっていない段階では、CopyBufferで要求した数を取得できないことがあります。失敗や不足を確認したら、その回の条件判定を見送り、次のイベントで改めて準備状態を確認します。配列に以前の値が残っている可能性もあるため、不足したまま読む設計にはしません。

データの計算完了を待って受け取るイメージ
操作場面のイメージ画像です。実際の画面ではありません。

BarsCalculatedは、指定ハンドルの計算済みデータ数を確認する関数です。まだ計算されていない場合などは-1となり得ます。これは事前の準備確認に使えますが、要求したバッファのコピーが必ず成功する保証ではありません。最終的にはCopyBufferの戻り値で必要件数がそろったか確認します。

また、取得した値がEMPTY_VALUEや無効な数値でないかは、指標の性質に応じて確認します。コピーできた件数と、そこに売買判定に使える有効値があることは別の条件です。特定の足では値を出さない指標もあるので、空値を0の正常値へ置き換えないようにします。

待つためにイベント処理内で無制限のループを回すのは避けます。取得失敗が起きた理由と時刻を把握し、必要な履歴やハンドルの条件が整うかを次回確認する形にします。準備状態の定義はBarsCalculatedのリファレンスで確認できます。

MQL5のCopyBufferを確定足で検証

確定足の指標値をコードで取得するイメージ
操作場面のイメージ画像です。実際の画面ではありません。

確定足二本を読む発注しないEA例

以下は一時間足・期間14・終値の単純移動平均を読み、確定足二本の値をログへ出すMQL5のEA例です。注文を送る処理は含めていません。EAを開始した時点で未処理の足を一度確認し、その後は新しい一時間足が始まった際に確認します。

#property strict
input int MAPeriod = 14;
input ENUM_TIMEFRAMES MATimeframe = PERIOD_H1;
int ma_handle = INVALID_HANDLE;
datetime checked_bar = 0;
int OnInit()
{
   if(MAPeriod < 1) return(INIT_PARAMETERS_INCORRECT);
   ma_handle = iMA(_Symbol,MATimeframe,MAPeriod,0,
                   MODE_SMA,PRICE_CLOSE);
   if(ma_handle == INVALID_HANDLE)
   {
      Print("Handle creation failed: ",GetLastError());
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}
void OnTick()
{
   datetime current = iTime(_Symbol,MATimeframe,0);
   if(current == 0 || current == checked_bar) return;
   if(BarsCalculated(ma_handle) < MAPeriod+2) return;
   double values[2];
   ResetLastError();
   int copied = CopyBuffer(ma_handle,0,1,2,values);
   int copy_error = GetLastError();
   if(copied != 2 || copy_error != 0)
   {
      Print("Copy incomplete: ",copied," err=",copy_error);
      return;
   }
   if(values[0] == EMPTY_VALUE || values[1] == EMPTY_VALUE)
      return;
   if(!MathIsValidNumber(values[0]) ||
      !MathIsValidNumber(values[1])) return;
   datetime previous = iTime(_Symbol,MATimeframe,1);
   if(previous == 0 || iTime(_Symbol,MATimeframe,0) != current)
      return;
   // Normal fixed array: [0]=shift2, [1]=shift1.
   Print(_Symbol," tf=",EnumToString(MATimeframe),
         " time1=",TimeToString(previous,TIME_DATE|TIME_MINUTES),
         " shift1=",DoubleToString(values[1],8),
         " shift2=",DoubleToString(values[0],8));
   checked_bar = current;
}
void OnDeinit(const int reason)
{
   if(ma_handle != INVALID_HANDLE)
      IndicatorRelease(ma_handle);
}

説明用の例であり、利用者の端末でのコンパイルや実行結果を掲載したものではありません。MetaEditorで確認し、既存EAを載せていないデモ用チャートで使ってください。休場中などティックが届かないときはOnTickが呼ばれないため、ログが出ないことと取得に失敗したことを分けて考えます。

この例では必要な件数を取得できた後にだけchecked_barを更新しています。取得前に更新すると、最初の一回が計算待ちだった場合、その一時間足の間は再確認しない設計になってしまうためです。成功と見送りを区別する状態管理も、数値の読取と合わせて確認しましょう。

また、コピー前後で現在足の開始時刻が変わっていないか確認しています。処理の途中で新しい足が始まる境界では、同じshiftでも対象が入れ替わり得るからです。これは例の範囲での境界確認であり、複数銘柄・複数指標の同期をすべて解決する仕組みではありません。

画面と同じ条件で値を比較する

画面側には、同じ銘柄・一時間足・期間14・Simple・Close・表示移動0の移動平均を設定します。ログに出た一本前の開始時刻を探し、その足の移動平均値をデータウィンドウで確認します。現在足の表示と、ログの確定足の値を比べないようにしてください。

同じ足の指標値を画面と照合する模式図
操作場面のイメージ画像です。実際の画面ではありません。

比較する条件は、銘柄、時間足、期間、計算方法、適用価格、表示移動、足の時刻です。期間だけそろえても、EMAとSMA、Closeと別の適用価格なら結果は変わります。別口座を使う場合は、サーバーの価格や履歴の違いも確認します。

小数点以下の表示桁が違う場合は、画面と同じ桁へ丸めた値を先に比較します。画面の見た目だけでなく、ログがどの時刻の値を示しているかを確かめることが大切です。チャートのスクロール後に選び直したshift1が、実行時のshift1と同じ日時であるとは限りません。

標準指標で一致を確認できた後に、複数バッファの指標へ広げます。バッファの番号は指標ごとに確認し、見えている線の順番から推測しません。カスタム指標の名前や入力引数の調べ方は、iCustomの使い方と値の照合手順で確認できます。

取得できない原因を段階で探す

最初にハンドルがINVALID_HANDLEでないかを調べます。次にBarsCalculated、CopyBufferの件数、配列の有効値、読み取った足の時刻へ進みます。すべてを「CopyBufferが動かない」という一つの症状にまとめると、入力条件とデータ準備の問題が混ざってしまいます。

観察した状態確認する段階次の確認
ハンドルが無効指標の作成指標関数の引数とエラー
計算済み件数が不足履歴・計算の準備銘柄・時間足とデータの読込
要求よりコピー件数が少ないCopyBufferの結果取得範囲と準備状態
件数は合うが値が違う配列と指標設定添字・バッファ番号・適用価格
値は近いが足がずれる日時の対応現在足と確定足、実行時刻

一時的な計算待ちを直すために、すぐハンドルを作り直すとは限りません。同じ指標の準備を毎回やり直すと、かえってどの計算対象を確認しているか追いにくくなります。入力条件が変わったのか、まだ準備中なのかを分けてから、再作成が必要か判断します。

ログへ同じエラーが大量に出る場合は、確認の頻度も見直します。この例は仕組みを示す最小構成なので、本番へ拡張するなら一定間隔での報告や状態変化時だけの通知を検討します。ログを減らすために取得失敗を無視し、古い値で注文を続ける設計にはしないでください。

MQL5のCopyBufferは対応表で確認

CopyBufferを使う際は、「どの指標の何番のバッファを、何本前から何件取り、どの添字で読むか」を一行で説明できるようにします。今回の例なら、移動平均のバッファ0を一本前から二件取得し、通常方向の固定長配列の1番で一本前を読む、という対応です。

指標を作成して値取得に再利用し解放する模式図
操作場面のイメージ画像です。実際の画面ではありません。

使い終えたハンドルはIndicatorReleaseで解放する設計にします。解放の仕様やストラテジーテスター内での扱いはIndicatorReleaseの公式説明で確認してください。初期化、値の取得、終了時の処理を分けると、計算対象の作成を繰り返す必要がある場面も整理しやすくなります。

値の一致が確認できた後に、クロス判定やフィルターへ進みます。数値を正しく読めることは、その指標を使ったFX自動売買が利益を生むことを示すものではありません。売買条件と決済、取引コストなどは別途検証します。

まずは一つの標準指標、二本の確定足、発注しないログ出力に絞りましょう。必要件数がそろうこと、配列の対応が正しいこと、画面と同じ時刻の値が一致することを確かめてから、読み取る指標や銘柄を増やすと原因を追いやすくなります。

EA作成ツールの検証範囲を確認する

生成コードの動作テスト・コンパイル結果と、その限界 →記事内の戦略や個別のEAの収益性を検証した結果ではありません。

学んだルールを、形にしてみよう。

EA作成ツールを開く ↗