MQL5 CopyBufferで値が取れない原因と確認ポイント
MQL5でEAからインジケーター値を取得する時は、CopyBufferを使う場面が多くあります。
しかし、CopyBufferは「呼べば必ず値が返る」関数ではありません。インジケーターハンドルの生成失敗、BarsCalculated不足、buffer番号の間違い、shiftの指定ミス、取得本数不足、配列の向き、EMPTY_VALUE、iCustomの引数不一致、未確定足の扱いなどにより、値が取れないことがあります。
特に、EA化やカスタムインジケーター連携では、「チャート上ではインジケーターが表示されているのにEA側では値が取れない」「CopyBufferの戻り値が0または-1になる」「error 4806が出る」「取得した値が0やEMPTY_VALUEに見える」といった問題が起きやすくなります。
このページでは、MQL5のCopyBufferで値が取れない原因を、handle、INVALID_HANDLE、BarsCalculated、buffer番号、shift、取得本数、EMPTY_VALUE、error 4806、iCustom、ログ確認の観点から整理します。
なお、このページはMQL5開発における技術確認を目的とした内容です。売買判断、推奨エントリー、推奨ロット、利益保証、損失回避保証を行うものではありません。
- CopyBufferとは何か
- CopyBufferで値が取れない時の確認順序
- 基本的なCopyBufferの流れ
- 原因1:INVALID_HANDLEになっている
- handle作成時にログへ出す項目
- 原因2:BarsCalculatedが不足している
- 原因3:buffer番号が間違っている
- buffer番号を確認する時の考え方
- 原因4:shiftの指定を取り違えている
- 現在足と確定足を分けてログに出す
- 原因5:取得本数と配列参照が合っていない
- 原因6:ArraySetAsSeriesの向きが合っていない
- 原因7:EMPTY_VALUEを値なしと区別できていない
- EMPTY_VALUE確認コード例
- 原因8:error 4806が出ている
- error 4806確認用のログ例
- 原因9:iCustomの指定が合っていない
- iCustomの基本確認コード例
- 原因10:インジケーター側がまだ値を出していない
- チャート表示とEA取得値が違う時の確認
- 複数時間足でCopyBufferを使う時の注意点
- バックテストで値が取れない時の確認
- EAのENTRY判定へ使う時の責務分離
- ログに残すべき項目
- よくある失敗パターン
- 開発・改修依頼前に整理したい情報
- 実務チェックリスト
- よくある質問
- 関連ページ
- まとめ
CopyBufferとは何か
CopyBufferは、MQL5でインジケーターのバッファ値を取得するための関数です。
iMA、iRSI、iATR、iBandsなどの標準インジケーターや、iCustomで呼び出したカスタムインジケーターは、通常、インジケーターハンドルを作成してからCopyBufferで値を取得します。
| 項目 | 役割 | 確認ポイント |
|---|---|---|
| handle | インジケーターを参照する識別子 | INVALID_HANDLEになっていないか |
| buffer番号 | 取得するインジケーターバッファの番号 | 表示ラインとbuffer番号が一致しているか |
| start position / shift | どのバーから取得するか | 0が現在足、1が1本前の確定足として扱われる場面が多い |
| count | 取得する本数 | 必要本数以上を要求しているか |
| 配列 | 取得値の格納先 | ArraySetAsSeriesの向きと参照位置を確認する |
CopyBufferで値が取れない場合は、CopyBufferだけを見るのではなく、handle生成、インジケーター計算状況、buffer番号、バー本数、取得後の配列参照まで順番に確認する必要があります。
CopyBufferで値が取れない時の確認順序
CopyBufferの不具合確認では、いきなりコード全体を書き換えるのではなく、確認順序を固定すると原因を切り分けやすくなります。
| 順番 | 確認項目 | 見る内容 |
|---|---|---|
| 1 | handle | INVALID_HANDLEではないか |
| 2 | GetLastError | handle生成やCopyBuffer失敗時のエラー番号 |
| 3 | BarsCalculated | インジケーター計算済みバー数が足りているか |
| 4 | Bars / iBars | チャートや対象時間足にバーが十分あるか |
| 5 | buffer番号 | 取得したいラインのbuffer番号が正しいか |
| 6 | shift | 現在足か確定足か、参照バーを取り違えていないか |
| 7 | 取得本数 | CopyBufferのcountと配列サイズが合っているか |
| 8 | EMPTY_VALUE | 値は取得できているが未描画値ではないか |
| 9 | iCustom | ファイル名、パス、input順序、引数型が一致しているか |
この順序で確認すれば、「ハンドルが作れていない」のか、「計算がまだ終わっていない」のか、「buffer番号が違う」のか、「値は取れているが参照位置が違う」のかを分けやすくなります。
基本的なCopyBufferの流れ
標準インジケーターの値を取得する場合、基本的な流れは以下です。
int ma_handle = INVALID_HANDLE;
int OnInit()
{
ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("HANDLE_ERROR name=iMA last_error=", GetLastError());
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnTick()
{
double ma_values[];
ArraySetAsSeries(ma_values, true);
int copied = CopyBuffer(ma_handle, 0, 0, 3, ma_values);
if(copied <= 0)
{
Print("COPYBUFFER_ERROR copied=", copied,
" last_error=", GetLastError());
return;
}
Print("MA_VALUE current=", DoubleToString(ma_values[0], _Digits),
" prev=", DoubleToString(ma_values[1], _Digits));
}
void OnDeinit(const int reason)
{
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
ma_handle = INVALID_HANDLE;
}
}この例では、OnInitでiMAのhandleを作成し、OnTickでCopyBufferを呼び、OnDeinitでIndicatorReleaseを行っています。
重要なのは、毎tickでiMAを作り直さないことです。handle生成はOnInitなどの初期化処理で行い、OnTickでは既存handleを使って値を取得する形にすると、処理負荷や不安定な挙動を抑えやすくなります。
原因1:INVALID_HANDLEになっている
CopyBufferで値が取れない時に最初に確認するのは、handleがINVALID_HANDLEになっていないかです。
handleが正しく作成できていない状態でCopyBufferを呼んでも、期待する値は取得できません。
| 原因 | 起きやすい状況 | 確認方法 |
|---|---|---|
| インジケーター名が違う | iCustomでファイル名やパスを間違えている | handle作成直後にINVALID_HANDLEを確認する |
| input引数が違う | カスタムインジケーターのinput順序とEA側指定が一致していない | iCustomの引数をインジ側input順に合わせる |
| 対象銘柄が不正 | Market Watchにない銘柄やsuffix違い | SymbolSelectや_symbol名を確認する |
| 時間足指定が不正 | 想定外のtimeframeを渡している | PERIOD_CURRENTやENUM_TIMEFRAMESを確認する |
| 初期化前に使っている | handle生成前にCopyBufferを呼ぶ | OnInit完了ログとCopyBufferログの順序を見る |
handle生成失敗時は、CopyBuffer側ではなく、iMA、iRSI、iCustomなどのhandle作成部分を先に確認してください。
handle作成時にログへ出す項目
handle作成時は、単に「失敗」と出すだけでは原因が追いにくくなります。少なくとも、symbol、timeframe、indicator name、input、last_errorをログに残します。
int CreateMaHandle(const string symbol,
const ENUM_TIMEFRAMES timeframe,
const int period)
{
ResetLastError();
int handle = iMA(symbol, timeframe, period, 0, MODE_SMA, PRICE_CLOSE);
int err = GetLastError();
Print("HANDLE_CREATE",
" indicator=iMA",
" symbol=", symbol,
" timeframe=", timeframe,
" period=", period,
" handle=", handle,
" last_error=", err);
if(handle == INVALID_HANDLE)
return INVALID_HANDLE;
return handle;
}このようにログ化すると、対象銘柄、時間足、periodのどれで失敗しているかを確認できます。
原因2:BarsCalculatedが不足している
handleが作れていても、インジケーター側の計算がまだ終わっていない場合、CopyBufferで値が取れないことがあります。
この時に確認するのがBarsCalculatedです。BarsCalculatedは、対象インジケーターで計算済みのバー数を確認するために使います。
bool IsIndicatorReady(const int handle, const int required_bars)
{
int calculated = BarsCalculated(handle);
Print("BARS_CALCULATED",
" handle=", handle,
" calculated=", calculated,
" required=", required_bars);
if(calculated < required_bars)
return false;
return true;
}EA起動直後、時間足変更直後、銘柄変更直後、テスター開始直後、iCustomで重いカスタムインジケーターを呼んだ直後は、BarsCalculatedが十分でない場合があります。
| 場面 | 起きやすい問題 | 対応 |
|---|---|---|
| EA起動直後 | handleは作れたが、まだ計算済みバー数が不足 | BarsCalculatedが必要本数以上になるまで待つ |
| 時間足変更直後 | 内部計算が再構築される | 初回数tickは取得失敗を許容し、ログを抑制する |
| 複数時間足 | 上位足のバー数が不足する | 対象timeframeのiBarsも確認する |
| カスタムインジケーター | 内部計算やバッファ初期化に時間がかかる | iCustom成功後、BarsCalculatedとCopyBufferを分けて確認する |
BarsCalculated不足は異常ではなく、準備未完了の状態です。売買判定に使うEAでは、準備未完了のままENTRY判定へ進まないようにします。
原因3:buffer番号が間違っている
CopyBufferの第2引数には、取得するインジケーターバッファ番号を指定します。
このbuffer番号が違うと、意図したラインではなく別のラインを読んだり、何も値が入っていないバッファを読んだりすることがあります。
| 確認対象 | 内容 | 注意点 |
|---|---|---|
| 標準インジケーター | iMAなら通常buffer 0 | 複数ライン系ではbufferが複数ある |
| iBands | 上限・中央・下限など複数buffer | どのbufferがどのラインか確認する |
| カスタムインジケーター | SetIndexBufferの順序に依存 | チャート上の見た目だけで判断しない |
| 非表示バッファ | 計算用bufferが存在する場合がある | 表示ラインと取得bufferが一致しない場合がある |
カスタムインジケーターでは、SetIndexBufferで割り当てた順序と、EA側で指定するbuffer番号を一致させる必要があります。
buffer番号を確認する時の考え方
カスタムインジケーターのEA化では、インジ側のバッファ定義を確認します。
double BuyBuffer[];
double SellBuffer[];
int OnInit()
{
SetIndexBuffer(0, BuyBuffer, INDICATOR_DATA);
SetIndexBuffer(1, SellBuffer, INDICATOR_DATA);
return INIT_SUCCEEDED;
}このようなインジケーターなら、EA側ではBuyBufferをbuffer 0、SellBufferをbuffer 1として取得する設計になります。
double buy_signal[];
double sell_signal[];
ArraySetAsSeries(buy_signal, true);
ArraySetAsSeries(sell_signal, true);
int copied_buy = CopyBuffer(custom_handle, 0, 0, 3, buy_signal);
int copied_sell = CopyBuffer(custom_handle, 1, 0, 3, sell_signal);buffer番号が分からない場合は、インジケーターのソースコード、SetIndexBuffer、PlotIndexSetInteger、表示ラインの順序、データウィンドウでの表示を確認します。
原因4:shiftの指定を取り違えている
CopyBufferでは、どのバーから値を取得するかを指定します。
EAの判定では、現在足の値を使うのか、1本前の確定足を使うのかを明確にする必要があります。
| shift | 意味の目安 | 注意点 |
|---|---|---|
| 0 | 現在足 | 未確定で変動中。リペイントや値変化の影響を受けやすい |
| 1 | 1本前の確定足 | バー確定型EAで使いやすい |
| 2以降 | さらに過去のバー | クロス判定や連続条件で使う |
「チャート上ではサインが出ているのにEAが反応しない」という場合、EA側がshift 1を見ているのに、チャート表示はshift 0の変動中サインを見ていることがあります。
逆に、EA側でshift 0を使うと、足が確定する前に値が変わり、バックテストや実運用でシグナルの見え方が変わる場合があります。
現在足と確定足を分けてログに出す
shiftの取り違えを避けるため、現在足と確定足を同時にログへ出すと確認しやすくなります。
double values[];
ArraySetAsSeries(values, true);
int copied = CopyBuffer(handle, 0, 0, 3, values);
if(copied >= 3)
{
Print("COPYBUFFER_VALUES",
" current_shift0=", DoubleToString(values[0], _Digits),
" closed_shift1=", DoubleToString(values[1], _Digits),
" closed_shift2=", DoubleToString(values[2], _Digits));
}EAのENTRY判定に使う値は、現在足か確定足かを仕様として決め、ログにも basis=SHIFT0 や basis=SHIFT1 のように残すと切り分けしやすくなります。
原因5:取得本数と配列参照が合っていない
CopyBufferで取得する本数が少ないのに、配列の過去要素を参照すると、想定外の値や配列範囲外の問題につながります。
たとえば、クロス判定で現在値と1本前、2本前を比較するなら、少なくとも3本以上を取得する必要があります。
| 用途 | 必要本数の目安 | 例 |
|---|---|---|
| 現在値だけ確認 | 1本 | values[0] |
| 確定足を見る | 2本以上 | values[1] |
| クロス判定 | 3本以上 | values[1]とvalues[2]を比較 |
| 連続条件 | 条件本数以上 | 直近3本連続など |
CopyBufferの戻り値は、実際にコピーできた要素数です。要求した本数と同じとは限らないため、戻り値を確認してから配列を参照します。
原因6:ArraySetAsSeriesの向きが合っていない
配列の向きも、CopyBufferで値を扱う時の混乱原因になります。
ArraySetAsSeriesをtrueにした配列では、通常、index 0を最新バーとして扱いやすくなります。一方、falseの場合は、配列の並びを別の前提で読むことになります。
| 設定 | 考え方 | 注意点 |
|---|---|---|
ArraySetAsSeries(values, true) | values[0]を最新側として扱いやすい | EAの時系列処理でよく使う |
ArraySetAsSeries(values, false) | 通常配列として扱う | 参照位置を取り違えないようにする |
どちらが正しいというより、EA内で一貫していることが重要です。取得後にvalues[0]、values[1]、values[2]をログに出し、想定したバーの値になっているか確認してください。
原因7:EMPTY_VALUEを値なしと区別できていない
CopyBuffer自体は成功していても、取得した値がEMPTY_VALUEの場合があります。
EMPTY_VALUEは、インジケーター側で「このバーには描画しない」「この位置にはサインがない」という意味で使われることがあります。サイン系インジケーターや矢印インジケーターでは特に重要です。
| 状態 | 意味 | 対応 |
|---|---|---|
| CopyBuffer戻り値が0以下 | 値のコピー自体に失敗 | GetLastError、BarsCalculated、handleを確認 |
| CopyBufferは成功したがEMPTY_VALUE | そのバーに描画値がない | サインなしとして扱う |
| 0.0が返る | 実値として0の場合と未初期化が混ざる | インジ側の仕様を確認する |
| DBL_MAXに近い値 | EMPTY_VALUE相当の可能性 | EMPTY_VALUE比較を行う |
サイン系インジケーターでは、「値が取れない」のではなく、「値は取れているが、そのバーにサインがない」だけの場合があります。CopyBufferの戻り値と取得値の中身を分けて確認してください。
EMPTY_VALUE確認コード例
bool HasSignalValue(const double value)
{
if(value == EMPTY_VALUE)
return false;
if(!MathIsValidNumber(value))
return false;
return true;
}
void PrintSignalValue(const double value, const int shift)
{
if(value == EMPTY_VALUE)
{
Print("SIGNAL_VALUE shift=", shift, " status=EMPTY_VALUE");
return;
}
if(!MathIsValidNumber(value))
{
Print("SIGNAL_VALUE shift=", shift, " status=INVALID_NUMBER");
return;
}
Print("SIGNAL_VALUE shift=", shift,
" value=", DoubleToString(value, _Digits));
}EA側では、EMPTY_VALUEをエラー扱いするのか、単に「サインなし」と扱うのかを仕様として分けます。通常、サイン系インジケーターではEMPTY_VALUEは異常ではなく、シグナルなしの状態として扱います。
原因8:error 4806が出ている
CopyBufferで値が取れない時に、GetLastErrorでerror 4806が出る場合があります。
このエラーは、インジケーターのデータ取得や計算状態に関係して発生することがあります。原因を1つに断定するのではなく、handle、BarsCalculated、対象buffer、バー本数、iCustom引数、テスター条件を順番に確認してください。
| 確認項目 | 見る内容 | 対応 |
|---|---|---|
| handle | INVALID_HANDLEではないか | handle生成直後のログを確認する |
| BarsCalculated | 必要本数以上あるか | 計算不足なら判定を待つ |
| buffer番号 | 存在するbufferを読んでいるか | SetIndexBufferの順序を確認する |
| count | 要求本数が過大ではないか | まず1〜3本で確認する |
| iCustom引数 | input順序・型・ファイル名が合っているか | インジ側inputとEA側指定を照合する |
| 履歴データ | 対象時間足のバーが十分あるか | iBars、チャート読み込み、テスター期間を確認する |
error 4806が出た時に、すぐにロジック全体を作り直す必要はありません。まずは、CopyBuffer直前の状態をログへ出し、どの前提が不足しているかを確認します。
error 4806確認用のログ例
bool CopyIndicatorValue(const int handle,
const int buffer_index,
const int shift,
double &out_value)
{
out_value = EMPTY_VALUE;
if(handle == INVALID_HANDLE)
{
Print("COPYBUFFER_BLOCK reason=INVALID_HANDLE");
return false;
}
int calculated = BarsCalculated(handle);
if(calculated <= shift)
{
Print("COPYBUFFER_BLOCK 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("COPYBUFFER_TRACE",
" handle=", handle,
" buffer=", buffer_index,
" shift=", shift,
" copied=", copied,
" calculated=", calculated,
" last_error=", err);
if(copied != 1)
return false;
out_value = temp[0];
return true;
}このように、handle、buffer、shift、copied、BarsCalculated、last_errorを同じログに出すと、CopyBuffer失敗時の原因を追いやすくなります。
原因9:iCustomの指定が合っていない
カスタムインジケーターをEAから呼ぶ場合は、iCustomの指定ミスが原因になることがあります。
iCustomでは、インジケーター名、配置パス、input引数の順序、input型、対象symbol、timeframeを正しく指定する必要があります。
| 確認項目 | 内容 | よくあるミス |
|---|---|---|
| ファイル名 | インジケーター名 | 拡張子、フォルダ名、全角半角、スペース違い |
| 配置場所 | MQL5/Indicators配下 | サブフォルダ指定漏れ |
| input順序 | インジ側inputとEA側引数の順序 | 途中のinputを省略して順序がズレる |
| input型 | int、double、bool、string、enumなど | 型違いによりhandle生成失敗 |
| buffer番号 | 取得したいライン | 表示ラインとbuffer番号の誤認 |
iCustomは、EA側だけ見ても原因が分からないことがあります。カスタムインジケーター側のinput定義、SetIndexBuffer、描画条件、EMPTY_VALUE出力条件を合わせて確認してください。
iCustomの基本確認コード例
int custom_handle = INVALID_HANDLE;
int OnInit()
{
ResetLastError();
custom_handle = iCustom(_Symbol,
PERIOD_CURRENT,
"MyCustomIndicator",
20,
2.0,
true);
int err = GetLastError();
Print("ICUSTOM_HANDLE",
" symbol=", _Symbol,
" timeframe=", PERIOD_CURRENT,
" handle=", custom_handle,
" last_error=", err);
if(custom_handle == INVALID_HANDLE)
return INIT_FAILED;
return INIT_SUCCEEDED;
}この例では、iCustomに3つのinput値を渡しています。実際には、対象インジケーターのinput順序と完全に一致させる必要があります。
インジケーター側でinputが増減した場合、EA側のiCustom引数も修正しなければ、handle生成や値取得が崩れる可能性があります。
原因10:インジケーター側がまだ値を出していない
CopyBufferが成功しても、インジケーター側がそのバーに値を出していなければ、EA側では有効な値として使えません。
たとえば、矢印サイン、条件成立時だけ値を出すバッファ、一定期間を過ぎないと計算しないインジケーターでは、値が空になるバーがあります。
| インジケーターの種類 | 値がない理由 | EA側の扱い |
|---|---|---|
| 移動平均などの連続線 | 計算期間不足の初期バー | 必要本数がそろうまで判定しない |
| 矢印サイン | サインが出たバーだけ値が入る | EMPTY_VALUEはサインなしとして扱う |
| ヒストグラム | 条件により0やEMPTY_VALUEを出す | 0とEMPTY_VALUEの意味を分ける |
| リペイント系 | 未確定足で値が変わる | 確定足基準で確認する |
| 複数時間足系 | 上位足の確定まで値が変わる | 時間足同期と確定足を確認する |
「値が取れない」と判断する前に、そのインジケーターが本当に毎バー値を出す設計なのか、条件成立時だけ値を出す設計なのかを確認してください。
チャート表示とEA取得値が違う時の確認
チャート上では値やサインが見えているのに、EA側で取得した値と違う場合があります。
この場合は、表示と取得の基準が一致しているかを確認します。
| 確認項目 | 内容 | 確認方法 |
|---|---|---|
| 対象時間足 | チャート時間足とEAのtimeframe指定 | PERIOD_CURRENTか固定時間足かを見る |
| 対象バー | 現在足か確定足か | shift 0 / 1をログで比較する |
| buffer番号 | 表示ラインと取得buffer | SetIndexBufferとData Windowを確認する |
| リペイント | 未確定足で値が変わるか | 確定後の値と比較する |
| 表示条件 | 描画だけ非表示になっていないか | EMPTY_VALUEやPlot設定を確認する |
EA化では、チャート上の見た目ではなく、EAがどのbufferのどのshiftを見ているかが重要です。ログにbuffer、shift、value、bar timeを出すと、チャート表示との照合がしやすくなります。
複数時間足でCopyBufferを使う時の注意点
複数時間足のインジケーター値をEAへ取り込む場合、現在チャートのバーと上位足のバーが一致するとは限りません。
たとえば、M5チャート上でH1のインジケーター値を取得する場合、M5の新バーごとにH1の確定値が変わるわけではありません。
| 確認項目 | 内容 | 注意点 |
|---|---|---|
| 対象timeframe | PERIOD_H1など | チャート時間足と混同しない |
| iBars | 上位足のバー数 | 対象時間足に十分な履歴があるか確認する |
| バー時刻 | iTimeやCopyTime | どの上位足バーを参照しているか確認する |
| 確定足 | shift 1を使うか | 上位足の未確定値を使わない設計にする |
複数時間足では、CopyBufferだけでなく、CopyTime、iTime、iBarShiftなどと組み合わせて、どのバーの値を使っているか確認する場合があります。
バックテストで値が取れない時の確認
Strategy TesterでCopyBufferの値が取れない場合、実チャートとは別の確認が必要です。
テスターでは、対象期間、ヒストリカルデータ、時間足、使用インジケーター、初期化タイミングによって、起動直後に必要本数が足りない場合があります。
| 確認項目 | 見る内容 | ログ例 |
|---|---|---|
| テスト期間 | インジ計算に必要な過去バーがあるか | TEST_PERIOD |
| BarsCalculated | 計算済みバー数 | BARS_CALCULATED |
| CopyBuffer戻り値 | 要求本数を取得できたか | COPYBUFFER_TRACE |
| 初期バー | 計算期間不足の最初のバー | WARMUP_SKIP |
| カスタムインジ | テスターで読み込めているか | ICUSTOM_HANDLE |
バックテストでは、開始直後の数バーで値が取れないことがあります。この場合は、すぐに異常扱いせず、ウォームアップ期間として判定を見送る設計が必要です。
EAのENTRY判定へ使う時の責務分離
CopyBufferで取得した値をそのまま発注処理へ渡すのは避けます。
実務上は、値取得、値の妥当性確認、signal判定、entry可否、executionを分けると原因を追いやすくなります。
| 責務 | 内容 | ログ例 |
|---|---|---|
| 取得 | 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条件不成立なのか、entry gateで止まったのか、発注処理で失敗したのかが分かりにくくなります。
ログに残すべき項目
CopyBuffer関連の不具合は、ログが不足していると原因を追えません。
| ログ名 | 出す内容 | 目的 |
|---|---|---|
HANDLE_CREATE | indicator、symbol、timeframe、handle、last_error | handle生成の成否を確認する |
BARS_CALCULATED | handle、calculated、required | 計算済みバー数を確認する |
COPYBUFFER_TRACE | buffer、shift、count、copied、last_error | CopyBufferの取得結果を確認する |
VALUE_VALIDATE | 取得値、EMPTY_VALUE、NaN判定 | 値の妥当性を確認する |
SIGNAL_EVAL | 判定basis、shift、signal結果 | EA側の判定理由を確認する |
ICUSTOM_HANDLE | インジ名、input、handle、last_error | iCustom連携の失敗を確認する |
ログ名を固定すると、Expertsログで検索しやすくなります。特に、error 4806や値なし状態を調べる時は、CopyBufferの戻り値だけでなく、直前のBarsCalculatedとhandle状態も一緒に確認してください。
よくある失敗パターン
| 失敗パターン | 起きる問題 | 確認すること |
|---|---|---|
| OnTickで毎回handleを作る | 処理が重くなり、値取得が不安定になる | handleはOnInitで作る |
| INVALID_HANDLEを確認しない | CopyBuffer失敗の原因が分からない | handle生成直後にログを出す |
| BarsCalculatedを見ない | 計算前に値を取りに行く | 必要本数以上になるまで待つ |
| buffer番号を推測する | 別ラインや空バッファを読む | SetIndexBufferの順序を確認する |
| shift 0とshift 1を混同する | チャート表示とEA判定がズレる | 現在足と確定足を分ける |
| EMPTY_VALUEをエラー扱いする | サインなしを異常と誤認する | CopyBuffer成功と値の有無を分ける |
| iCustom引数がズレる | handle生成失敗や値取得失敗 | インジ側input順と一致させる |
| ログが少ない | 原因調査ができない | handle、buffer、shift、copiedを出す |
開発・改修依頼前に整理したい情報
CopyBufferやiCustomの値取得不具合を相談する場合は、事前に以下を整理しておくと、原因確認が進めやすくなります。
| 整理項目 | 記入例 | 確認理由 |
|---|---|---|
| 対象EA / インジ名 | SampleEA、CustomSignal.ex5 | どのファイルを確認するか特定するため |
| MT5環境 | デモ / リアル、ブローカー、銘柄 | 銘柄名やsuffixの違いを確認するため |
| 対象時間足 | M15、H1など | チャート時間足と取得時間足の違いを確認するため |
| 取得したいbuffer | BuyBuffer、SellBuffer、MAラインなど | buffer番号を確認するため |
| shift | 0、1、確定足基準など | 現在足か確定足かを確認するため |
| Expertsログ | HANDLE_CREATE、COPYBUFFER_TRACE | handle、copied、last_errorを確認するため |
| インジ側input | period、threshold、modeなど | iCustom引数の順序を確認するため |
| ソース有無 | mq5あり / ex5のみ | SetIndexBufferや内部仕様を確認できるか判断するため |
口座番号、認証情報、Webhook URL、APIキー、個人情報は共有しないでください。ログを共有する場合は、必要な箇所だけを伏せ字にして整理してください。
実務チェックリスト
- handleがINVALID_HANDLEではないことを確認した
- handle生成時にGetLastErrorをログへ出した
- BarsCalculatedが必要本数以上あることを確認した
- 対象symbolとtimeframeが正しいことを確認した
- buffer番号が取得したいラインと一致していることを確認した
- shift 0とshift 1の違いを確認した
- CopyBufferの戻り値copiedを確認した
- 取得本数と配列参照位置が合っていることを確認した
- ArraySetAsSeriesの向きと参照位置を確認した
- EMPTY_VALUEをサインなしとして扱うか、異常として扱うかを決めた
- error 4806が出る時は、handle、BarsCalculated、buffer、shiftを順番に確認した
- iCustomでは、インジ名、パス、input順序、input型を確認した
- チャート表示とEA取得値を、bar time、buffer、shiftで照合した
- EAのENTRY判定へ使う前に、値取得、妥当性確認、signal判定を分けた
よくある質問
CopyBufferの戻り値が0になるのはなぜですか?
取得できたデータ数が0という意味です。handle、BarsCalculated、対象時間足のバー数、buffer番号、取得本数、iCustom引数を確認してください。EA起動直後や時間足変更直後は、まだインジケーター計算が終わっていない場合があります。
CopyBufferの戻り値が-1になる場合は何を見ますか?
GetLastErrorを確認し、handleがINVALID_HANDLEになっていないか、buffer番号が正しいか、iCustomの指定が正しいかを確認します。あわせてBarsCalculatedと対象時間足のバー数もログに出してください。
error 4806が出た場合はどうすればよいですか?
まず、handle、BarsCalculated、buffer番号、shift、取得本数、iCustom引数を順番に確認します。error番号だけで原因を断定せず、CopyBuffer直前の状態をログへ出して、計算不足なのか指定ミスなのかを切り分けてください。
チャートではサインが出ているのにEAで取得できないのはなぜですか?
EA側が見ているbuffer番号やshiftが、チャート上で見ているサインと違う可能性があります。また、チャート表示は現在足の変動中サインで、EAは確定足を見ている場合もあります。buffer、shift、bar timeをログで照合してください。
EMPTY_VALUEはエラーですか?
必ずしもエラーではありません。サイン系インジケーターでは、そのバーにサインがないことをEMPTY_VALUEで表す場合があります。CopyBufferの失敗と、CopyBuffer成功後のEMPTY_VALUEは分けて扱ってください。
iCustomで値が取れない時は何を確認しますか?
インジケーター名、サブフォルダ、input引数の順序、input型、buffer番号、SetIndexBuffer、EMPTY_VALUEの出力条件を確認します。EA側だけでなく、カスタムインジケーター側の仕様確認が必要です。
関連ページ
| 関連テーマ | 確認ページ | 確認できること |
|---|---|---|
| iCustom連携 | MQL5 iCustomでインジケーターをEA化する時の注意点 | iCustomでカスタムインジケーターをEAから呼ぶ時の確認 |
| ハンドル管理 | MQL5 CopyBufferとインジケーターハンドル管理を実コードで解説 | handle、IndicatorRelease、CopyBuffer取得helperの整理 |
| インジケーター関数 | 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、OrderSend、CopyBufferを含むEA構造の読み方 |
| 開発・改修相談 | MT4/MT5の開発・改修相談はこちら | CopyBufferやiCustomの値取得不具合を相談する前の整理 |
まとめ
MQL5のCopyBufferで値が取れない場合は、CopyBufferの呼び出し部分だけを見ても原因を特定できないことがあります。
まずは、handleがINVALID_HANDLEではないか、BarsCalculatedが必要本数以上あるか、buffer番号が正しいか、shiftと取得本数が合っているか、EMPTY_VALUEを誤解していないかを確認してください。
iCustomでカスタムインジケーターを呼んでいる場合は、インジケーター名、配置パス、input順序、input型、SetIndexBuffer、表示ラインとbuffer番号の対応も確認が必要です。
error 4806が出る場合も、エラー番号だけで原因を断定せず、handle、buffer、shift、copied、BarsCalculated、GetLastErrorを同じログに出して切り分けることが重要です。
EA化では、CopyBufferの値取得、値の妥当性確認、signal判定、entry gate、発注処理を分けて設計してください。ログで各段階を追えるようにしておくと、バックテスト、実運用、問い合わせ対応で原因を確認しやすくなります。
