MQL5 CopyBufferとインジケーターハンドル管理を実コードで解説|安全取得helperとIndicatorRelease
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管理の関係
- handleはOnInitで作り、OnTickで使う
- 基本のhandle管理コード例
- OnTickでhandleを作り直さない理由
- INVALID_HANDLEを確認する
- handle生成専用関数を作る
- BarsCalculatedで計算準備を確認する
- CopyBufferの戻り値を必ず確認する
- 安全に値を取得するhelper例
- EMPTY_VALUEと取得失敗を分ける
- 複数インジケーターのhandle管理
- 複数時間足のhandle管理
- 複数時間足handleのコード例
- iCustomのhandle管理
- IndicatorReleaseの考え方
- 安全にhandleを解放するコード例
- バックテスト開始直後の値取得に注意する
- 値取得とsignal判定を分ける
- 取得値をsignalへ変換する例
- ログ設計で原因を追えるようにする
- よくある失敗パターン
- 開発・改修依頼前に整理したい情報
- 実務チェックリスト
- よくある質問
- 関連ページ
- まとめ
CopyBufferとhandle管理の関係
CopyBufferは、インジケーターのbuffer値を取得するための関数です。ただし、CopyBufferを使うには、先に対象インジケーターのhandleを作成しておく必要があります。
| 要素 | 役割 | 確認ポイント |
|---|---|---|
| handle | インジケーターを参照する識別子 | INVALID_HANDLEではないか |
| CopyBuffer | handleから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を使って値を取得する構成が基本です。
| 場所 | 主な役割 | 注意点 |
|---|---|---|
| OnInit | handle生成、初期チェック | INVALID_HANDLEならINIT_FAILEDで止める判断も可能 |
| OnTick | CopyBufferで値を取得し、判定へ使う | 毎tickでhandleを作り直さない |
| OnDeinit | IndicatorReleaseで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 / iATR | symbol、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 | 取得できた本数が0 | BarsCalculated、対象バー、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のMA | ma_m15_handle | M15のバー数と確定足を確認する |
| H1のMA | ma_h1_handle | H1のBarsCalculatedを別に確認する |
| H4のATR | atr_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 |
| execution | OrderSendや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_CREATE | indicator、symbol、timeframe、handle、last_error | handle生成の成否を確認する |
HANDLE_READY | BarsCalculated、required | 計算準備状態を確認する |
COPYBUFFER_TRACE | buffer、shift、count、copied、last_error | CopyBuffer結果を確認する |
VALUE_VALIDATE | EMPTY_VALUE、NaN、有効値 | 値の妥当性を確認する |
HANDLE_RELEASE | label、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管理の不具合を相談する場合は、事前に以下を整理しておくと原因確認が進めやすくなります。
| 整理項目 | 記入例 | 確認理由 |
|---|---|---|
| 対象EA | SampleEA.mq5 | どのEAで値取得しているか確認するため |
| 対象インジ | iMA、iRSI、CustomSignalなど | handle生成元を確認するため |
| 対象銘柄・時間足 | USDJPY M15、XAUUSD H1など | symbol、timeframe、履歴不足を確認するため |
| 取得buffer | buffer 0、buffer 1など | CopyBuffer対象を確認するため |
| 参照shift | 0、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を分けておくと、バックテスト、実運用、問い合わせ対応で原因を確認しやすくなります。
