開発実務ノート

MQL5 CopyBufferとインジケーターハンドル管理を実コードで解説|安全取得helperとIndicatorRelease

EAファンクラブ

MQL5でEAからインジケーター値を取得する時は、インジケーターハンドルを作成し、そのhandleを使ってCopyBufferで値を取得します。

しかし、CopyBufferの不具合は、CopyBuffer関数だけを見ても解決できないことが多くあります。handleを毎tick作り直している、INVALID_HANDLEを確認していない、BarsCalculatedが不足している、IndicatorReleaseのタイミングが不適切、複数時間足のhandleを混同している、ログが不足している、といった周辺設計が原因になるためです。

特に、EA化や複数インジケーター連携では、「バックテスト開始直後だけ値が取れない」「時間足変更後に値取得が不安定になる」「iCustomのhandleは作れているのにCopyBufferが失敗する」「古いhandleを使い続けているのか判断できない」といった問題が起きやすくなります。

このページでは、MQL5のCopyBufferとインジケーターハンドル管理を、OnInit、OnTick、OnDeinit、INVALID_HANDLE、BarsCalculated、IndicatorRelease、複数handle、ログ設計、実コードの観点から整理します。

なお、このページはMQL5開発における技術確認を目的とした内容です。売買判断、推奨エントリー、推奨ロット、利益保証、損失回避保証を行うものではありません。

CopyBufferとhandle管理の関係

CopyBufferは、インジケーターのbuffer値を取得するための関数です。ただし、CopyBufferを使うには、先に対象インジケーターのhandleを作成しておく必要があります。

要素役割確認ポイント
handleインジケーターを参照する識別子INVALID_HANDLEではないか
CopyBufferhandleからbuffer値を取得するbuffer番号、shift、取得本数、戻り値を確認する
BarsCalculatedインジケーター計算済みバー数を確認する必要本数以上あるか
IndicatorRelease不要になったhandleを解放するOnDeinitや再生成時に扱う
ログ取得失敗の原因を追うhandle、copied、last_errorを記録する

CopyBufferが失敗した時は、まずhandleが正しく作られているかを確認します。handleがINVALID_HANDLEであれば、CopyBuffer以前の問題です。

handleはOnInitで作り、OnTickで使う

EAでは、インジケーターハンドルをOnTickで毎回作り直すのではなく、OnInitで作成し、OnTickではそのhandleを使って値を取得する構成が基本です。

場所主な役割注意点
OnInithandle生成、初期チェックINVALID_HANDLEならINIT_FAILEDで止める判断も可能
OnTickCopyBufferで値を取得し、判定へ使う毎tickでhandleを作り直さない
OnDeinitIndicatorReleaseでhandleを解放するhandleが有効な時だけ解放する
再初期化時時間足変更、EA再セットなど古いhandleを使い続けない

handle生成と値取得を分けることで、取得失敗時に「handle生成の問題」なのか「CopyBufferの問題」なのかを切り分けやすくなります。

基本のhandle管理コード例

以下は、iMAのhandleをOnInitで作成し、OnTickでCopyBufferを使い、OnDeinitでIndicatorReleaseする基本例です。

int ma_handle = INVALID_HANDLE;

int OnInit()
{
   ResetLastError();

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

   Print("HANDLE_CREATE",
         " indicator=iMA",
         " symbol=", _Symbol,
         " timeframe=", PERIOD_CURRENT,
         " handle=", ma_handle,
         " last_error=", err);

   if(ma_handle == INVALID_HANDLE)
      return INIT_FAILED;

   return INIT_SUCCEEDED;
}

void OnTick()
{
   double values[];
   ArraySetAsSeries(values, true);

   ResetLastError();

   int copied = CopyBuffer(ma_handle, 0, 0, 3, values);
   int err = GetLastError();

   Print("COPYBUFFER_TRACE",
         " copied=", copied,
         " last_error=", err);

   if(copied < 2)
      return;

   double ma_closed = values[1];
   Print("MA_CLOSED_VALUE value=", DoubleToString(ma_closed, _Digits));
}

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

この例では、EA判定に1本前の確定足であるvalues[1]を使っています。現在足を使うのか、確定足を使うのかはEA仕様として明示してください。

OnTickでhandleを作り直さない理由

OnTickで毎回iMAやiCustomを呼び、handleを作り直す実装は避けるべきです。

問題内容影響
処理負荷が増えるtickごとにhandle生成処理が走る複数インジや複数時間足で重くなる
状態が安定しない計算準備前にCopyBufferを呼ぶ可能性がある取得失敗や初期値混入が起きやすい
解放漏れが起きる古いhandleをReleaseしないまま増えるリソース管理が不明瞭になる
原因調査が難しくなるhandle生成とCopyBuffer失敗が混ざるログから原因を追いにくい

handleは、対象symbol、timeframe、period、inputが変わらない限り、基本的には使い回します。パラメータ変更や時間足変更が必要な場合は、古いhandleを解放してから再生成する流れを明示します。

INVALID_HANDLEを確認する

handle生成後は、必ずINVALID_HANDLEかどうかを確認します。

INVALID_HANDLEのままCopyBufferを呼んでも、正しい値は取得できません。特にiCustomでは、ファイル名、サブフォルダ、input順序、input型の不一致が原因でhandle生成に失敗することがあります。

handle生成元主な失敗原因確認ログ
iMA / iRSI / iATRsymbol、timeframe、period不正HANDLE_CREATE
iCustomインジ名、配置パス、input順序不一致ICUSTOM_HANDLE
複数時間足対象時間足のデータ不足HANDLE_CREATE / BARS_CHECK
複数銘柄Market Watch未選択、suffix違いSYMBOL_CHECK

handle生成失敗は、CopyBuffer側ではなく初期化処理の問題として扱います。EA全体を止めるのか、該当インジケーターだけ無効化するのかは仕様として決めてください。

handle生成専用関数を作る

複数のインジケーターを扱うEAでは、handle生成を関数化するとログや失敗時の扱いを統一できます。

int CreateRsiHandle(const string symbol,
                    const ENUM_TIMEFRAMES timeframe,
                    const int period)
{
   ResetLastError();

   int handle = iRSI(symbol, timeframe, period, PRICE_CLOSE);
   int err = GetLastError();

   Print("HANDLE_CREATE",
         " indicator=iRSI",
         " symbol=", symbol,
         " timeframe=", timeframe,
         " period=", period,
         " handle=", handle,
         " last_error=", err);

   if(handle == INVALID_HANDLE)
      return INVALID_HANDLE;

   return handle;
}

このように関数化しておくと、handle生成時のログ形式を統一できます。複数インジケーターを使うEAでは、インジケーター名、symbol、timeframe、period、handle、last_errorを必ず追えるようにします。

BarsCalculatedで計算準備を確認する

handleが作れていても、インジケーターの計算がまだ終わっていない場合は、CopyBufferで期待する本数を取得できないことがあります。

この時に使うのがBarsCalculatedです。BarsCalculatedで計算済みバー数を確認し、必要本数に達していない場合は判定を見送ります。

bool IsHandleReady(const int handle, const int required_bars)
{
   if(handle == INVALID_HANDLE)
   {
      Print("HANDLE_READY status=NG reason=INVALID_HANDLE");
      return false;
   }

   int calculated = BarsCalculated(handle);

   Print("HANDLE_READY",
         " calculated=", calculated,
         " required=", required_bars);

   if(calculated < required_bars)
      return false;

   return true;
}

EA起動直後、バックテスト開始直後、時間足変更直後、重いカスタムインジケーターを呼んだ直後は、BarsCalculatedが不足する場合があります。この状態を異常と断定せず、準備未完了として扱います。

CopyBufferの戻り値を必ず確認する

CopyBufferの戻り値は、実際に取得できたデータ数です。

戻り値意味確認すること
要求本数と同じ取得成功値の中身を確認する
0取得できた本数が0BarsCalculated、対象バー、buffer番号を確認する
-1取得失敗GetLastError、handle、buffer番号を確認する
要求本数未満一部しか取得できていない配列参照前に本数不足を扱う

CopyBufferが失敗しているのに配列を参照すると、古い値や未初期化値を使う可能性があります。必ずcopiedを確認してから配列を参照してください。

安全に値を取得するhelper例

実務では、CopyBufferを直接あちこちに書くより、安全取得helperを作ると確認しやすくなります。

bool CopyOneIndicatorValue(const int handle,
                           const int buffer_index,
                           const int shift,
                           double &out_value)
{
   out_value = EMPTY_VALUE;

   if(handle == INVALID_HANDLE)
   {
      Print("COPY_ONE status=NG reason=INVALID_HANDLE");
      return false;
   }

   int calculated = BarsCalculated(handle);

   if(calculated <= shift)
   {
      Print("COPY_ONE status=WAIT reason=BARS_NOT_READY",
            " calculated=", calculated,
            " shift=", shift);
      return false;
   }

   double temp[];
   ArraySetAsSeries(temp, true);

   ResetLastError();

   int copied = CopyBuffer(handle, buffer_index, shift, 1, temp);
   int err = GetLastError();

   Print("COPY_ONE",
         " buffer=", buffer_index,
         " shift=", shift,
         " copied=", copied,
         " calculated=", calculated,
         " last_error=", err);

   if(copied != 1)
      return false;

   out_value = temp[0];
   return true;
}

このhelperでは、INVALID_HANDLE、BarsCalculated不足、CopyBuffer戻り値、GetLastErrorをまとめて確認しています。実際のEAでは、ログ出力頻度を制御し、成功ログを毎tick出しすぎないようにします。

EMPTY_VALUEと取得失敗を分ける

CopyBufferが成功していても、取得した値がEMPTY_VALUEの場合があります。

特に、矢印サインや条件成立時だけ値を出すインジケーターでは、サインがないバーにEMPTY_VALUEが入ることがあります。この場合、CopyBufferの失敗ではなく、サインなしとして扱う方が自然です。

状態意味EA側の扱い
CopyBuffer失敗値を取得できていないhandle、BarsCalculated、GetLastErrorを確認
CopyBuffer成功 + EMPTY_VALUEそのバーにサインがないWAITまたはNO_SIGNALとして扱う
CopyBuffer成功 + 有効値判定に使える値があるsignal評価へ進む
0.0有効値か未初期化かはインジ仕様次第インジ側仕様を確認する

取得失敗とサインなしを混同すると、正常なWAIT状態をエラー扱いしてしまいます。EA内部では、値取得の成否とsignal成立を分けて扱ってください。

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

EAで複数のインジケーターを使う場合は、それぞれのhandleを分けて管理します。

たとえば、MA、RSI、ATRを使う場合、それぞれのhandle、buffer番号、必要本数、取得値、ログを分けておくと原因を追いやすくなります。

int ma_handle  = INVALID_HANDLE;
int rsi_handle = INVALID_HANDLE;
int atr_handle = INVALID_HANDLE;

int OnInit()
{
   ma_handle  = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
   atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);

   if(ma_handle == INVALID_HANDLE)
      return INIT_FAILED;

   if(rsi_handle == INVALID_HANDLE)
      return INIT_FAILED;

   if(atr_handle == INVALID_HANDLE)
      return INIT_FAILED;

   return INIT_SUCCEEDED;
}

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

   if(rsi_handle != INVALID_HANDLE)
      IndicatorRelease(rsi_handle);

   if(atr_handle != INVALID_HANDLE)
      IndicatorRelease(atr_handle);

   ma_handle  = INVALID_HANDLE;
   rsi_handle = INVALID_HANDLE;
   atr_handle = INVALID_HANDLE;
}

この例では簡略化していますが、実務では各handle作成時にログを出し、どのインジケーターで失敗したか分かるようにします。

複数時間足のhandle管理

複数時間足の値を使う場合は、同じインジケーターでも時間足ごとに別handleを作成します。

handle注意点
M15のMAma_m15_handleM15のバー数と確定足を確認する
H1のMAma_h1_handleH1のBarsCalculatedを別に確認する
H4のATRatr_h4_handle上位足の履歴不足に注意する
現在チャートPERIOD_CURRENT時間足変更時に再初期化される点を考慮する

複数時間足では、現在チャートのバー数と上位足のバー数が一致しません。CopyBufferで値を取得する前に、対象時間足のバーが十分あるかを確認します。

複数時間足handleのコード例

int ma_m15_handle = INVALID_HANDLE;
int ma_h1_handle  = INVALID_HANDLE;

int OnInit()
{
   ma_m15_handle = iMA(_Symbol, PERIOD_M15, 20, 0, MODE_SMA, PRICE_CLOSE);
   ma_h1_handle  = iMA(_Symbol, PERIOD_H1,  20, 0, MODE_SMA, PRICE_CLOSE);

   if(ma_m15_handle == INVALID_HANDLE)
   {
      Print("INIT_FAILED reason=MA_M15_HANDLE");
      return INIT_FAILED;
   }

   if(ma_h1_handle == INVALID_HANDLE)
   {
      Print("INIT_FAILED reason=MA_H1_HANDLE");
      return INIT_FAILED;
   }

   return INIT_SUCCEEDED;
}

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

   if(ma_h1_handle != INVALID_HANDLE)
      IndicatorRelease(ma_h1_handle);

   ma_m15_handle = INVALID_HANDLE;
   ma_h1_handle  = INVALID_HANDLE;
}

複数時間足では、どのhandleがどの時間足を見ているかログや変数名で分かるようにします。M15の値とH1の値を同じ変数で扱うと、判定ミスやログ確認ミスにつながります。

iCustomのhandle管理

iCustomでカスタムインジケーターを呼ぶ場合も、基本は同じです。OnInitでhandleを作成し、OnTickでCopyBufferを使います。

ただし、iCustomではインジケーター名、サブフォルダ、input順序、input型、buffer番号の確認が追加で必要になります。

int custom_handle = INVALID_HANDLE;

int OnInit()
{
ResetLastError();

custom_handle = iCustom(_Symbol,
PERIOD_CURRENT,
"Custom\u005c\u005cMySignal",
20,
2.0,
true);

int err = GetLastError();

Print("ICUSTOM_HANDLE",
" handle=", custom_handle,
" last_error=", err);

if(custom_handle == INVALID_HANDLE)
return INIT_FAILED;

return INIT_SUCCEEDED;
}

サブフォルダ内のインジケーターを呼ぶ場合、パス指定を間違えるとhandle生成に失敗します。また、インジケーター側inputが変更された場合、EA側のiCustom引数も更新する必要があります。

IndicatorReleaseの考え方

IndicatorReleaseは、不要になったインジケーターハンドルを解放するために使います。

EA終了時、インジケーター再生成時、設定変更により古いhandleが不要になった時などに使います。ただし、使用中のhandleを誤って解放すると、その後のCopyBufferで値が取れなくなります。

場面IndicatorReleaseの扱い注意点
EA終了時OnDeinitで解放するINVALID_HANDLEでない時だけ実行する
handle再生成時古いhandleを先に解放する新旧handleを混同しない
設定変更時必要なら再生成するruntime変更とinput初期値を分ける
OnTick中通常は毎tick解放しない値取得直後に解放し続ける設計は避ける

IndicatorRelease後は、対象handle変数をINVALID_HANDLEへ戻しておくと、古いhandleを誤って使い続けるリスクを下げられます。

安全にhandleを解放するコード例

void ReleaseHandle(int &handle, const string label)
{
   if(handle == INVALID_HANDLE)
   {
      Print("HANDLE_RELEASE_SKIP label=", label,
            " reason=INVALID_HANDLE");
      return;
   }

   bool released = IndicatorRelease(handle);

   Print("HANDLE_RELEASE",
         " label=", label,
         " released=", released ? "Y" : "N");

   handle = INVALID_HANDLE;
}

このように、解放対象のラベルをログに出しておくと、どのhandleを解放したのか確認しやすくなります。

バックテスト開始直後の値取得に注意する

Strategy Testerでは、テスト開始直後にインジケーター計算や履歴データの準備が完了していない場合があります。

この状態でCopyBufferを呼ぶと、戻り値が0や-1になったり、BarsCalculatedが不足していたりすることがあります。

確認項目見る内容対応
テスト開始直後必要本数がそろっているかウォームアップ期間を設ける
BarsCalculated計算済みバー数required未満なら判定しない
複数時間足上位足の履歴があるか対象timeframeごとに確認する
iCustomテスターで読み込めているかICUSTOM_HANDLEログを見る

EAでは、ウォームアップ不足の状態をエラーとして扱うのではなく、判定待ちとして扱う方が自然です。ログ上も、ERRORではなくWAITやNOT_READYとして出すと確認しやすくなります。

値取得とsignal判定を分ける

CopyBufferで取得した値を、そのまま発注処理へつなげるのは避けます。

EA側では、値取得、値の妥当性確認、signal判定、entry gate、executionを分けます。こうすることで、どの段階で止まったのかログから追いやすくなります。

責務内容ログ例
handle管理インジケーターhandleを作成・保持・解放するHANDLE_CREATE / HANDLE_RELEASE
値取得CopyBufferで値を取るCOPYBUFFER_TRACE
妥当性確認EMPTY_VALUEやNaNを確認するVALUE_VALIDATE
signal判定BUY / SELL / WAITへ変換するSIGNAL_EVAL
entry gateスプレッド、時間、ポジション数などを見るENTRY_GATE
executionOrderSendやCTradeを実行するORDER_SEND

この分離がないと、CopyBufferの値取得失敗なのか、シグナル条件不成立なのか、発注前制限で止まったのかが分かりにくくなります。

取得値をsignalへ変換する例

enum ESignalState
{
   SIGNAL_STATE_WAIT = 0,
   SIGNAL_STATE_BUY  = 1,
   SIGNAL_STATE_SELL = 2
};

ESignalState ResolveMaSignal(const double close_price,
                             const double ma_value)
{
   if(ma_value == EMPTY_VALUE)
      return SIGNAL_STATE_WAIT;

   if(!MathIsValidNumber(ma_value))
      return SIGNAL_STATE_WAIT;

   if(close_price > ma_value)
      return SIGNAL_STATE_BUY;

   if(close_price < ma_value)
      return SIGNAL_STATE_SELL;

   return SIGNAL_STATE_WAIT;
}

この例では、CopyBufferで取得したMA値をEA内部のsignal状態へ変換しています。実際のEAでは、この後にスプレッド、時間帯、既存ポジション、Magic Number、証拠金、OrderCheckなどの条件を確認してから発注処理へ進みます。

ログ設計で原因を追えるようにする

CopyBufferとhandle管理では、ログ設計が重要です。

単に「値が取れない」と出すだけでは、handle生成失敗なのか、BarsCalculated不足なのか、buffer番号の誤りなのか、EMPTY_VALUEなのか判断できません。

ログ名出す内容目的
HANDLE_CREATEindicator、symbol、timeframe、handle、last_errorhandle生成の成否を確認する
HANDLE_READYBarsCalculated、required計算準備状態を確認する
COPYBUFFER_TRACEbuffer、shift、count、copied、last_errorCopyBuffer結果を確認する
VALUE_VALIDATEEMPTY_VALUE、NaN、有効値値の妥当性を確認する
HANDLE_RELEASElabel、released解放処理を確認する
SIGNAL_EVAL取得値、basis、判定結果EA内部判定を確認する

成功ログを毎tick大量に出すと、Expertsログが読みにくくなります。検証中は詳細ログ、本番運用では失敗時または状態変化時だけ出すなど、ログレベルを分けると扱いやすくなります。

よくある失敗パターン

失敗パターン起きる問題確認すること
OnTickで毎回handleを作る処理が重くなり不安定になるhandleはOnInitで作る
INVALID_HANDLEを確認しないCopyBuffer失敗の原因が分からないhandle生成直後に確認する
BarsCalculatedを見ない計算前に値を取りに行く必要本数以上か確認する
CopyBuffer戻り値を見ない取得失敗後に配列を参照するcopiedを確認する
IndicatorReleaseしないhandle管理が不明瞭になるOnDeinitで解放する
使用中handleを解放するその後のCopyBufferで失敗する解放タイミングを限定する
複数時間足handleを混同する別時間足の値で判定する変数名とログで区別する
ログが不足している原因調査ができないhandle、buffer、shift、copiedを出す

開発・改修依頼前に整理したい情報

CopyBufferやhandle管理の不具合を相談する場合は、事前に以下を整理しておくと原因確認が進めやすくなります。

整理項目記入例確認理由
対象EASampleEA.mq5どのEAで値取得しているか確認するため
対象インジiMA、iRSI、CustomSignalなどhandle生成元を確認するため
対象銘柄・時間足USDJPY M15、XAUUSD H1などsymbol、timeframe、履歴不足を確認するため
取得bufferbuffer 0、buffer 1などCopyBuffer対象を確認するため
参照shift0、1、確定足基準など現在足と確定足の違いを確認するため
ExpertsログHANDLE_CREATE、COPYBUFFER_TRACEなど失敗箇所を追うため
ソース有無mq5あり / ex5のみSetIndexBufferやinputを確認できるか判断するため
バックテスト条件期間、モデル、時間足、スプレッドテスター特有の履歴不足を確認するため

口座番号、認証情報、Webhook URL、APIキー、個人情報は共有しないでください。ログを共有する場合は、必要な箇所だけを伏せ字にして整理してください。

実務チェックリスト

  • handleをOnInitで作成している
  • OnTickで毎回handleを作り直していない
  • handle生成直後にINVALID_HANDLEを確認している
  • handle生成時にGetLastErrorをログへ出している
  • BarsCalculatedで計算済みバー数を確認している
  • CopyBufferの戻り値copiedを確認している
  • 取得本数が不足している時に配列を参照していない
  • EMPTY_VALUEと取得失敗を分けて扱っている
  • 複数インジケーターのhandleを別変数で管理している
  • 複数時間足のhandleを時間足別に区別している
  • OnDeinitでIndicatorReleaseを行っている
  • IndicatorRelease後にhandleをINVALID_HANDLEへ戻している
  • 値取得、妥当性確認、signal判定、entry gateを分けている
  • ログ名を固定し、Expertsログで検索しやすくしている

よくある質問

handleは毎tick作る必要がありますか?

通常は毎tick作る必要はありません。OnInitで作成し、OnTickでは既存handleを使ってCopyBufferを呼ぶ構成が基本です。毎tick作り直すと処理負荷や管理ミスの原因になります。

CopyBufferが失敗した時は何を最初に確認しますか?

まずhandleがINVALID_HANDLEではないかを確認します。次にBarsCalculated、buffer番号、shift、取得本数、CopyBufferの戻り値、GetLastErrorを順番に確認してください。

BarsCalculatedが不足している場合はエラーですか?

必ずしもエラーではありません。EA起動直後やバックテスト開始直後などは、まだ計算準備が完了していない場合があります。必要本数がそろうまで判定を見送る設計にしてください。

IndicatorReleaseは必ず必要ですか?

不要になったhandleはIndicatorReleaseで解放するのが基本です。OnDeinitや再生成時に解放し、解放後はhandle変数をINVALID_HANDLEへ戻しておくと管理しやすくなります。

複数時間足では同じhandleを使えますか?

同じインジケーターでも、時間足が違う場合は別handleとして管理します。M15用、H1用などを変数名とログで区別し、それぞれBarsCalculatedとCopyBuffer結果を確認してください。

EMPTY_VALUEはCopyBuffer失敗ですか?

CopyBufferが成功していても、取得値がEMPTY_VALUEになる場合があります。サイン系インジケーターでは、そのバーにサインがないことをEMPTY_VALUEで表す場合があるため、CopyBuffer失敗とサインなしは分けて扱ってください。

関連ページ

関連テーマ確認ページ確認できること
CopyBuffer不具合MQL5 CopyBufferで値が取れない原因と確認ポイントCopyBufferで値が取れない時の原因別確認
iCustom連携MQL5 iCustomでインジケーターをEA化する時の注意点iCustom、input引数、buffer番号、リペイント確認
インジケーター関数MQL5インジケーター関数辞典iCustom、CopyBuffer、SetIndexBuffer、OnCalculateの概要
EAへの値取り込みMQL5でインジケーター値をEAに取り込む方法標準インジケーターやカスタムインジケーター値をEAへ渡す基本
インジ開発とEA連携MQL5インジケーター開発・EA連携完全ガイドOnCalculate、SetIndexBuffer、CopyBuffer、EA連携の全体像
OnInit確認MQL5 OnInitの役割と初期化失敗の確認handle生成、初期化失敗、INIT_FAILEDの確認
イベント処理MQL5イベント処理完全ガイドOnInit、OnTick、OnTimer、OnDeinitの責務分離
ログ設計MQL5デバッグ・ログファースト開発完全ガイド値取得失敗や判定不成立をログで追う考え方
EAサンプルコードMQL5 EAサンプルコードの読み方OnInit、OnTick、CopyBufferを含むEA構造の読み方
開発・改修相談MT4/MT5の開発・改修相談はこちらCopyBufferやhandle管理の不具合を相談する前の整理

まとめ

MQL5でCopyBufferを安定して使うには、CopyBuffer関数だけでなく、インジケーターハンドル管理を含めて設計する必要があります。

handleはOnInitで作成し、OnTickでは既存handleを使って値を取得し、OnDeinitでIndicatorReleaseします。handle生成後はINVALID_HANDLEを確認し、CopyBuffer前にはBarsCalculatedで計算済みバー数を確認します。

CopyBufferの戻り値copied、GetLastError、buffer番号、shift、取得本数をログへ出すことで、値が取れない原因を切り分けやすくなります。

複数インジケーターや複数時間足を使う場合は、handleを個別に管理し、変数名とログで区別してください。EMPTY_VALUE、取得失敗、signal条件不成立、entry gateでの停止も分けて扱うことが重要です。

EA化では、handle管理、値取得、値の妥当性確認、signal判定、entry gate、executionを分けておくと、バックテスト、実運用、問い合わせ対応で原因を確認しやすくなります。

ABOUT ME
記事URLをコピーしました