開発実務ノート

MQL5 iCustomでインジケーターをEA化する時の注意点

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は、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と固定時間足の混同
handleiCustomの戻り値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側の扱い
0BuyBufferBUYサイン取得用
1SellBufferSELLサイン取得用
2FilterBuffer内部計算用なら直接使わない

チャート上で見えている順番と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 11本前の確定足バー確定型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
CopyBufferbuffer値を取得するICUSTOM_COPY
value validationEMPTY_VALUEやNaNを確認するVALUE_VALIDATE
signalBUY / SELL / WAITへ変換するSIGNAL_EVAL
entry gateスプレッド、時間、最大ポジション数を見るENTRY_GATE
executionOrderSendや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配下にあるか配置場所とファイル名を確認する
handleINVALID_HANDLEではないかOnInitログを見る
BarsCalculated計算済みバー数が足りているかウォームアップ期間を設ける
ヒストリカルデータ対象時間足のバーがあるかテスト期間と履歴を確認する
複数時間足上位足データがあるかiBarsやCopyTimeを確認する

テスター起動直後の取得失敗は、必ずしも致命的なエラーではありません。必要本数がそろうまで判定を見送る設計にすると、初期バーの誤判定を避けやすくなります。

ログに残すべき項目

iCustomのEA化では、値が取れない原因をログで追えるようにしておくことが重要です。

ログ名出す内容目的
ICUSTOM_HANDLEsymbol、timeframe、indicator、handle、last_errorhandle生成の成否を確認する
ICUSTOM_ARGSinput引数の値EA側の指定値を確認する
BARS_CALCULATEDcalculated、required計算済みバー数を確認する
ICUSTOM_COPYbuffer、shift、copied、last_errorCopyBufferの取得結果を確認する
VALUE_VALIDATEEMPTY_VALUE、NaN、有効値値の有無を確認する
SIGNAL_EVALBUY / SELL / WAIT、basis、shiftEA内部判定を確認する
REPAINT_CHECKshift 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化や値取得不具合を相談する場合は、事前に以下を整理しておくと確認が進めやすくなります。

整理項目記入例確認理由
対象インジ名MyCustomIndicatoriCustomの指定名を確認するため
配置場所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、発注処理を分けて設計してください。ログで各段階を追えるようにしておくと、バックテスト、実運用、問い合わせ対応で原因を確認しやすくなります。

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