MQL5 iCustomでインジケーターをEA化する時の注意点
MQL5で既存インジケーターをEA化する場合、iCustomを使ってカスタムインジケーターをEAから呼び出し、CopyBufferで値を取得する構成がよく使われます。
ただし、iCustomは「インジケーター名を書けばそのままEA化できる」仕組みではありません。インジケーターの配置場所、input引数の順序、buffer番号、SetIndexBufferの対応、EMPTY_VALUE、未確定足、リペイント、複数時間足、バックテストでの読み込み状態を確認する必要があります。
特に、「チャート上ではサインが出ているのにEAが反応しない」「CopyBufferで値が取れない」「iCustomのhandleがINVALID_HANDLEになる」「EA化するとエントリータイミングが違う」「バックテストとチャート表示が合わない」といった問題は、iCustomとCopyBufferの役割を分けて確認すると切り分けやすくなります。
このページでは、MQL5のiCustomでインジケーターをEA化する時の注意点を、インジケーター名、配置パス、input引数、buffer番号、CopyBuffer、リペイント、確定足、ログ確認、EA側のsignal化という観点から整理します。
なお、このページはMQL5開発における技術確認を目的とした内容です。売買判断、推奨エントリー、推奨ロット、利益保証、損失回避保証を行うものではありません。
- iCustomとは何か
- iCustomでEA化する時の基本構成
- iCustomで最初に確認すること
- インジケーター名と配置パスの注意点
- input引数の順序と型を確認する
- buffer番号と表示ラインを対応させる
- CopyBufferでiCustomの値を取得する例
- 現在足と確定足を分けて確認する
- 確定足基準で取得するコード例
- EMPTY_VALUEとサインなしを区別する
- リペイントするインジケーターをEA化する時の注意点
- リペイント確認用のログ例
- EA側では値取得とsignal判定を分ける
- signal判定へ変換するコード例
- バックテストでiCustomが動かない時の確認
- ログに残すべき項目
- よくある失敗パターン
- 開発・改修依頼前に整理したい情報
- 実務チェックリスト
- よくある質問
- 関連ページ
- まとめ
iCustomとは何か
iCustomは、MQL5でカスタムインジケーターをEAや別のプログラムから呼び出すための関数です。
iMAやiRSIのような標準インジケーターは専用関数でhandleを作成できますが、独自インジケーターや配布されたカスタムインジケーターをEAから使う場合は、iCustomでhandleを作成し、CopyBufferで対象bufferの値を取得します。
| 処理 | 役割 | 確認ポイント |
|---|---|---|
| iCustom | カスタムインジケーターのhandleを作成する | インジ名、パス、input引数、timeframeを確認する |
| CopyBuffer | 作成したhandleからbuffer値を取得する | buffer番号、shift、取得本数、戻り値を確認する |
| SetIndexBuffer | インジ側でbufferを割り当てる | EA側で読むbuffer番号と対応させる |
| EMPTY_VALUE | 値なし、サインなし、未描画を表す場合がある | エラーとシグナルなしを分ける |
| 確定足 | リペイントや未確定値を避けるために使う | shift 0とshift 1を分けて確認する |
iCustomでEA化する時は、インジケーター側の設計とEA側の取得処理をセットで確認します。EA側のコードだけを見ても、buffer番号やinput順序が正しいか判断できない場合があります。
iCustomでEA化する時の基本構成
基本的な流れは、OnInitでiCustomのhandleを作成し、OnTickでCopyBufferを使って値を取得し、OnDeinitでIndicatorReleaseを行う形です。
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;
}
void OnDeinit(const int reason)
{
if(custom_handle != INVALID_HANDLE)
{
IndicatorRelease(custom_handle);
custom_handle = INVALID_HANDLE;
}
}この例では、iCustomでカスタムインジケーターのhandleを作成しています。実際には、インジケーター名、input引数、引数型、並び順を対象インジケーターに合わせる必要があります。
EA化でよくある失敗は、インジケーター側のinputが増減しているのに、EA側のiCustom引数が古いままになっているケースです。この場合、handleが作れない、値が取れない、想定と違う設定で計算されるといった問題が起きます。
iCustomで最初に確認すること
iCustomで値が取れない時は、CopyBufferより前に、iCustomの指定が正しいかを確認します。
| 確認項目 | 内容 | よくあるミス |
|---|---|---|
| インジケーター名 | 呼び出すカスタムインジケーター名 | ファイル名違い、拡張子指定、全角半角、スペース違い |
| 配置パス | MQL5/Indicators配下の場所 | サブフォルダ指定漏れ |
| input引数 | インジ側inputの順序と型 | 途中のinput省略、順序ズレ、型違い |
| symbol | 対象銘柄 | _Symbolと別銘柄指定の混同、suffix違い |
| timeframe | 対象時間足 | PERIOD_CURRENTと固定時間足の混同 |
| handle | iCustomの戻り値 | INVALID_HANDLEを確認していない |
iCustomのhandle作成に失敗している場合、CopyBuffer側を修正しても値は取れません。まず、handle作成ログを出し、INVALID_HANDLEになっていないか確認してください。
インジケーター名と配置パスの注意点
iCustomで指定するインジケーター名は、MQL5/Indicators配下の配置と一致している必要があります。
| 配置例 | iCustom指定例 | 注意点 |
|---|---|---|
| MQL5/Indicators/MySignal.ex5 | "MySignal" | 通常は拡張子なしで指定する |
| MQL5/Indicators/Custom/MySignal.ex5 | "Custom\\MySignal" | サブフォルダを含める |
| ファイル名にスペースあり | "My Signal" | スペース違いに注意する |
| mq5はあるが未コンパイル | handle作成失敗の可能性 | ex5が生成されているか確認する |
ファイル名やフォルダ名を推測で書くと、EA側ではhandleが作れません。特に配布されたインジケーターをEA化する場合は、実際のファイル名と配置場所を確認してください。
input引数の順序と型を確認する
iCustomでは、カスタムインジケーター側のinput順序に合わせて、EA側から引数を渡します。
たとえば、インジケーター側に以下のinputがあるとします。
input int InpPeriod = 20;
input double InpThreshold = 2.0;
input bool InpUseFilter = true;この場合、EA側のiCustomでも同じ順序で値を渡します。
custom_handle = iCustom(_Symbol,
PERIOD_CURRENT,
"MyCustomIndicator",
20,
2.0,
true);途中のinputだけを省略したり、intとdouble、boolとint、enumとintを混同したりすると、handle生成失敗や想定外の値取得につながる場合があります。
buffer番号と表示ラインを対応させる
iCustomでEA化する時に重要なのが、取得したい表示ラインとbuffer番号の対応です。
インジケーター側では、SetIndexBufferで各bufferを割り当てます。EA側では、CopyBufferの第2引数にそのbuffer番号を指定します。
double BuyBuffer[];
double SellBuffer[];
double FilterBuffer[];
int OnInit()
{
SetIndexBuffer(0, BuyBuffer, INDICATOR_DATA);
SetIndexBuffer(1, SellBuffer, INDICATOR_DATA);
SetIndexBuffer(2, FilterBuffer, INDICATOR_CALCULATIONS);
return INIT_SUCCEEDED;
}この例では、BuyBufferはbuffer 0、SellBufferはbuffer 1です。FilterBufferは計算用で、EA側のシグナル取得対象ではない可能性があります。
| buffer番号 | 例 | EA側の扱い |
|---|---|---|
| 0 | BuyBuffer | BUYサイン取得用 |
| 1 | SellBuffer | SELLサイン取得用 |
| 2 | FilterBuffer | 内部計算用なら直接使わない |
チャート上で見えている順番とbuffer番号が必ず一致するとは限りません。ソースコードがある場合は、SetIndexBufferの順序を確認してください。ソースコードがない場合は、Data Windowや開発元の仕様説明を確認します。
CopyBufferでiCustomの値を取得する例
iCustomで作成したhandleから値を取得する時も、標準インジケーターと同じくCopyBufferを使います。
void CheckCustomSignal()
{
if(custom_handle == INVALID_HANDLE)
{
Print("SIGNAL_SKIP reason=INVALID_HANDLE");
return;
}
double buy_values[];
double sell_values[];
ArraySetAsSeries(buy_values, true);
ArraySetAsSeries(sell_values, true);
ResetLastError();
int copied_buy = CopyBuffer(custom_handle, 0, 0, 3, buy_values);
int err_buy = GetLastError();
ResetLastError();
int copied_sell = CopyBuffer(custom_handle, 1, 0, 3, sell_values);
int err_sell = GetLastError();
Print("ICUSTOM_COPY",
" copied_buy=", copied_buy,
" err_buy=", err_buy,
" copied_sell=", copied_sell,
" err_sell=", err_sell);
}CopyBufferの戻り値は、取得できたデータ数です。要求本数分を取得できたか、0や-1になっていないか、GetLastErrorに何が出ているかを確認してください。
現在足と確定足を分けて確認する
EA化では、現在足の値を使うのか、確定足の値を使うのかを決める必要があります。
チャート上のインジケーター表示は現在足で変化していることがあります。一方、EAでは安定した判定のために1本前の確定足を使う設計にすることが多くあります。
| 参照位置 | 用途 | 注意点 |
|---|---|---|
| shift 0 | 現在足の値 | 未確定で変動する。リペイント確認が必要 |
| shift 1 | 1本前の確定足 | バー確定型EAで使いやすい |
| shift 2 | さらに1本前 | クロスや変化確認に使う |
「チャートではサインが出ているのにEAが入らない」という場合、チャートで見ている値がshift 0、EAが使っている値がshift 1というズレが原因になることがあります。
確定足基準で取得するコード例
bool ReadClosedBarSignal(const int handle,
const int buffer_index,
double &signal_value)
{
signal_value = EMPTY_VALUE;
double values[];
ArraySetAsSeries(values, true);
ResetLastError();
int copied = CopyBuffer(handle, buffer_index, 0, 3, values);
int err = GetLastError();
Print("READ_CLOSED_SIGNAL",
" buffer=", buffer_index,
" copied=", copied,
" last_error=", err);
if(copied < 2)
return false;
signal_value = values[1];
return true;
}この例では、CopyBufferで0番から3本分を取得し、EA判定にはvalues[1]を使っています。values[0]は現在足、values[1]は1本前の確定足として扱う前提です。
EMPTY_VALUEとサインなしを区別する
カスタムインジケーターのサインバッファでは、サインがないバーにEMPTY_VALUEが入ることがあります。
この場合、CopyBufferは成功していても、そのバーに有効なサインがないだけです。これをエラー扱いすると、EAのログが不要に増えたり、正常なサインなし状態を異常として判断したりする原因になります。
| 状態 | 意味 | EA側の扱い |
|---|---|---|
| CopyBuffer失敗 | 値を取得できていない | handle、BarsCalculated、GetLastErrorを確認 |
| CopyBuffer成功 + EMPTY_VALUE | そのバーにサインがない | WAITまたはNO_SIGNALとして扱う |
| CopyBuffer成功 + 有効値 | サインまたはライン値がある | signal判定へ進める |
| 0.0 | 有効値か未初期化かはインジ仕様次第 | インジ側仕様を確認する |
EA化では、値取得の成功とシグナル成立を分けます。CopyBufferが成功しても、EMPTY_VALUEなら「値取得は成功、シグナルはなし」と判断する方が自然です。
リペイントするインジケーターをEA化する時の注意点
カスタムインジケーターには、未確定足で値が変わるものがあります。これをリペイントと呼ぶことがあります。
リペイントするインジケーターをEA化する場合、チャート上で一時的に見えていたサインをEAがどう扱うかを明確にする必要があります。
| 確認項目 | 内容 | EA化での扱い |
|---|---|---|
| 現在足のサイン | 足確定前に出たり消えたりする | リアルタイムEA以外では慎重に扱う |
| 確定足のサイン | 足確定後に残った値 | バー確定型EAで使いやすい |
| 過去サインの再描画 | 過去バーの値が後から変わる | バックテストとの整合に注意する |
| MTFインジ | 上位足確定まで値が変わる | 上位足の確定タイミングを確認する |
リペイントするインジケーターを使うこと自体が常に悪いわけではありません。ただし、EA化する場合は、未確定足の変動値を使うのか、確定足だけを使うのかを仕様として明示してください。
リペイント確認用のログ例
void PrintRepaintCheck(const double shift0_value,
const double shift1_value)
{
Print("REPAINT_CHECK",
" shift0_current=", DoubleToString(shift0_value, _Digits),
" shift1_closed=", DoubleToString(shift1_value, _Digits));
}現在足と確定足を同時にログへ出すと、インジケーターが未確定足でどの程度変化しているか確認できます。
EA側では値取得とsignal判定を分ける
iCustomで取得した値をそのまま発注処理へつなげるのは避けます。
実務では、値取得、値の妥当性確認、signal判定、entry gate、executionを分けると、原因を追いやすくなります。
| 責務 | 内容 | ログ例 |
|---|---|---|
| iCustom handle | インジケーターを呼び出す | ICUSTOM_HANDLE |
| CopyBuffer | buffer値を取得する | ICUSTOM_COPY |
| value validation | EMPTY_VALUEやNaNを確認する | VALUE_VALIDATE |
| signal | BUY / SELL / WAITへ変換する | SIGNAL_EVAL |
| entry gate | スプレッド、時間、最大ポジション数を見る | ENTRY_GATE |
| execution | OrderSendやCTradeを実行する | ORDER_SEND |
この分離により、「iCustomで呼べていない」のか、「CopyBufferで値が取れていない」のか、「値は取れているがシグナル条件を満たしていない」のか、「entry gateで止まっている」のかを切り分けできます。
signal判定へ変換するコード例
enum ESignalSide
{
SIGNAL_WAIT = 0,
SIGNAL_BUY = 1,
SIGNAL_SELL = 2
};
ESignalSide ResolveSignal(const double buy_value,
const double sell_value)
{
bool has_buy = (buy_value != EMPTY_VALUE && MathIsValidNumber(buy_value));
bool has_sell = (sell_value != EMPTY_VALUE && MathIsValidNumber(sell_value));
if(has_buy && !has_sell)
return SIGNAL_BUY;
if(has_sell && !has_buy)
return SIGNAL_SELL;
return SIGNAL_WAIT;
}この例では、BUY用bufferとSELL用bufferの値を見て、EA内部のsignalへ変換しています。実際のEAでは、ここからさらにスプレッド、時間帯、既存ポジション、Magic Number、リスク条件などのentry gateを通してから発注処理へ進めます。
バックテストでiCustomが動かない時の確認
Strategy TesterでiCustomを使う場合、カスタムインジケーターがテスター環境で読み込めているか確認します。
実チャートでは動いていても、テスターで対象インジケーターが見つからない、履歴データが不足している、初期化直後にBarsCalculatedが不足している、といった問題が起きることがあります。
| 確認項目 | 見る内容 | 対応 |
|---|---|---|
| インジファイル | Indicators配下にあるか | 配置場所とファイル名を確認する |
| handle | INVALID_HANDLEではないか | OnInitログを見る |
| BarsCalculated | 計算済みバー数が足りているか | ウォームアップ期間を設ける |
| ヒストリカルデータ | 対象時間足のバーがあるか | テスト期間と履歴を確認する |
| 複数時間足 | 上位足データがあるか | iBarsやCopyTimeを確認する |
テスター起動直後の取得失敗は、必ずしも致命的なエラーではありません。必要本数がそろうまで判定を見送る設計にすると、初期バーの誤判定を避けやすくなります。
ログに残すべき項目
iCustomのEA化では、値が取れない原因をログで追えるようにしておくことが重要です。
| ログ名 | 出す内容 | 目的 |
|---|---|---|
ICUSTOM_HANDLE | symbol、timeframe、indicator、handle、last_error | handle生成の成否を確認する |
ICUSTOM_ARGS | input引数の値 | EA側の指定値を確認する |
BARS_CALCULATED | calculated、required | 計算済みバー数を確認する |
ICUSTOM_COPY | buffer、shift、copied、last_error | CopyBufferの取得結果を確認する |
VALUE_VALIDATE | EMPTY_VALUE、NaN、有効値 | 値の有無を確認する |
SIGNAL_EVAL | BUY / SELL / WAIT、basis、shift | EA内部判定を確認する |
REPAINT_CHECK | shift 0とshift 1の比較 | 未確定足の変動を確認する |
ログを出す時は、成功時に毎tick大量出力しないよう注意してください。失敗時、初回のみ、状態変化時、検証モードON時など、目的に応じて出力条件を分けると扱いやすくなります。
よくある失敗パターン
| 失敗パターン | 起きる問題 | 確認すること |
|---|---|---|
| インジ名を推測で指定する | INVALID_HANDLEになる | 実ファイル名と配置場所を確認する |
| input引数を省略する | 順序ズレや設定違いが起きる | インジ側input定義と一致させる |
| buffer番号を見た目で決める | 別bufferや空bufferを読む | SetIndexBufferを確認する |
| 現在足をそのままEA判定に使う | サインが出たり消えたりする | 確定足基準を検討する |
| EMPTY_VALUEをエラー扱いする | サインなしを異常と誤認する | 取得失敗とサインなしを分ける |
| リペイントを確認しない | チャート表示とEA結果が合わない | shift 0とshift 1を比較する |
| ログが不足している | 原因が追えない | handle、buffer、shift、copiedを出す |
開発・改修依頼前に整理したい情報
iCustomを使ったEA化や値取得不具合を相談する場合は、事前に以下を整理しておくと確認が進めやすくなります。
| 整理項目 | 記入例 | 確認理由 |
|---|---|---|
| 対象インジ名 | MyCustomIndicator | iCustomの指定名を確認するため |
| 配置場所 | MQL5/Indicators/Custom | サブフォルダ指定の有無を見るため |
| input一覧 | period、threshold、modeなど | EA側引数の順序を合わせるため |
| buffer番号 | Buy=0、Sell=1など | CopyBufferの対象を確認するため |
| ソース有無 | mq5あり / ex5のみ | SetIndexBufferを確認できるか判断するため |
| リペイント有無 | 不明、現在足で変化するなど | EA判定basisを決めるため |
| Expertsログ | ICUSTOM_HANDLE、ICUSTOM_COPYなど | handleとCopyBuffer結果を確認するため |
| EA化したい動作 | サイン確定後に通知、発注前確認など | signalとexecutionの責務を分けるため |
口座番号、認証情報、Webhook URL、APIキー、個人情報は共有しないでください。ログを共有する場合は、必要な箇所だけを伏せ字にして整理してください。
実務チェックリスト
- iCustomで指定するインジケーター名を実ファイル名で確認した
- サブフォルダにある場合、iCustomのパス指定を確認した
- インジ側inputの順序とEA側iCustom引数を照合した
- input型の違いがないか確認した
- handleがINVALID_HANDLEではないことを確認した
- GetLastErrorをhandle生成直後にログへ出した
- SetIndexBufferとCopyBufferのbuffer番号を対応させた
- CopyBufferの戻り値copiedを確認した
- EMPTY_VALUEをサインなしとして扱うか決めた
- shift 0とshift 1の違いを確認した
- リペイントの有無を現在足と確定足で確認した
- EA側で値取得、妥当性確認、signal判定、entry gateを分けた
よくある質問
iCustomでINVALID_HANDLEになる原因は何ですか?
インジケーター名、配置パス、input引数の順序、input型、対象symbol、timeframeが合っていない可能性があります。まずOnInitでiCustomの戻り値とGetLastErrorをログへ出してください。
チャートではサインが出ているのにEAで取得できないのはなぜですか?
EA側が見ているbuffer番号やshiftが、チャートで見ているサインと違う可能性があります。表示ライン、SetIndexBuffer、CopyBufferのbuffer番号、shift 0 / shift 1を確認してください。
iCustomのinputは全部指定する必要がありますか?
対象インジケーターの仕様によります。途中のinputだけを省略すると順序がズレる場合があります。EA化では、インジ側input定義を確認し、必要な引数を正しい順序と型で渡してください。
リペイントするインジケーターはEA化できますか?
可能な場合もありますが、未確定足で値が変わることを前提に設計する必要があります。バー確定型EAでは、shift 1の確定足を使うなど、判定basisを明確にしてください。
EMPTY_VALUEはエラーですか?
必ずしもエラーではありません。サイン系インジケーターでは、そのバーにサインがないことをEMPTY_VALUEで表す場合があります。CopyBufferの失敗と、CopyBuffer成功後のサインなしは分けて扱ってください。
EA化する時は現在足と確定足のどちらを使うべきですか?
EAの仕様によります。安定した判定を優先するなら確定足、リアルタイム反応を優先するなら現在足を使う場合があります。ただし、現在足を使う場合は値の変動、リペイント、同一バー重複発注防止を設計してください。
関連ページ
| 関連テーマ | 確認ページ | 確認できること |
|---|---|---|
| CopyBufferの値取得 | MQL5 CopyBufferで値が取れない原因と確認ポイント | CopyBufferで値が取れない時の原因別確認 |
| ハンドル管理 | 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の役割と初期化失敗の確認 | iCustom handle生成やINIT_FAILEDの確認 |
| ログ設計 | MQL5デバッグ・ログファースト開発完全ガイド | iCustomやCopyBufferの失敗をログで追う考え方 |
| EAサンプルコード | MQL5 EAサンプルコードの読み方 | OnInit、OnTick、CopyBufferを含むEA構造の読み方 |
| 開発・改修相談 | MT4/MT5の開発・改修相談はこちら | iCustomやCopyBufferを使ったEA化相談前の整理 |
まとめ
MQL5のiCustomは、カスタムインジケーターをEAから呼び出すために便利な関数です。ただし、インジケーター名、配置パス、input引数、buffer番号、CopyBuffer、EMPTY_VALUE、リペイント、確定足の扱いを確認しないままEA化すると、チャート表示とEA判定が合わない原因になります。
まずは、iCustomのhandleがINVALID_HANDLEではないか、GetLastErrorに何が出ているか、インジ側inputとEA側引数が一致しているかを確認してください。
次に、SetIndexBufferとCopyBufferのbuffer番号を照合し、shift 0とshift 1、EMPTY_VALUE、CopyBuffer戻り値、BarsCalculatedを確認します。
EA化では、iCustomで値を取る処理と、EA内部のsignal判定、entry gate、発注処理を分けて設計してください。ログで各段階を追えるようにしておくと、バックテスト、実運用、問い合わせ対応で原因を確認しやすくなります。
