MQL5インジケーターEA化実務ノート|サイン取得・確定足・リペイント確認の考え方
MQL5でサインインジケーターをEA化する時、単に「矢印が出たらエントリー」と考えると、実装後に問題が出やすくなります。
インジケーターのサインは、表示されている見た目だけで判断できるとは限りません。どのbufferにサイン値が入るのか、shift=0を見るのかshift=1を見るのか、未確定足で値が変わるのか、過去サインがリペイントするのか、CopyBufferが失敗した時にどう扱うのかを整理する必要があります。
インジケーターEA化で重要なのは、表示サイン、取得値、確定足、Signal Snapshot、Entry判断を分けることです。
この記事では、MQL5でインジケーターサインをEA化する時の設計判断を、開発実務ノートとして整理します。完成EAソース全文、独自インジケーターの中核条件、外部シート制御ロジック、認証ロジック、endpoint、token、URL実値、実運用set値、中核Entry条件は公開しません。
本記事は、投資判断や売買指示ではなく、MQL5のインジケーターEA化を設計・検証・サポートしやすくするための技術記事です。
- この記事で扱う実務課題
- なぜインジケーターEA化では設計整理が必要なのか
- インジケーターEA化で分けるべき責務
- iCustomとインジケーターハンドルの考え方
- CopyBufferで見るべきポイント
- ハンドルの生成・取得・解放を確認する
- shift=0とshift=1の違い
- 標準インジケーターの値をEAへ取り込む
- リペイント確認が必要な理由
- サイン表示とbuffer値は同じとは限らない
- iCustomの入力・バッファ・サインを照合する
- 実コード例1:インジケーターハンドルを管理する
- 実コード例2:CopyBuffer取得結果を分ける
- 実コード例3:Signal Snapshotとして固定する
- 実コード例4:確定足で判定する
- ログで確認するポイント
- バックテストとリアルで確認すること
- よくある失敗例
- 導入前・開発依頼前に整理する情報
- 値取得・EA化の検証条件を記録する
- よくある質問
- 関連する確認ページ
- あわせて確認したいページ
- まとめ
- 利用条件・相談窓口
- お問い合わせはEAFC公式LINEへ
この記事で扱う実務課題
| 項目 | この記事で扱う内容 | 扱わない内容 |
|---|---|---|
| 目的 | インジケーターサインをEA化する時の値取得・確定足・リペイント確認 | 利益を狙う売買ロジック、具体的なEntry条件 |
| 主な対象 | iCustom、CopyBuffer、buffer番号、shift、確定足、Signal Snapshot | 完成EAソース全文、実運用コード全文 |
| 設計観点 | サイン取得失敗、未確定足変動、リペイント、ログ追跡、検証再現性 | 独自インジケーターの中核ロジック、実運用set値 |
| コード例 | 一般化したhandle管理、CopyBuffer取得、Snapshot化、確定足判定 | 実サイン条件、独自優位性のある判定式 |
| 読者 | MQL5開発者、サインインジをEA化したい人、検証担当者 | 売買判断や推奨設定を知りたい人 |
なぜインジケーターEA化では設計整理が必要なのか
チャート上で矢印やサインが見えていても、EA側で同じタイミング・同じ値を取得できるとは限りません。
インジケーターEA化でよく起きる問題は次の通りです。
| 現象 | 原因の例 | 確認すべき責務 |
|---|---|---|
| サインが見えるのにEAが入らない | buffer番号違い、shift違い、CopyBuffer失敗 | indicator read / signal snapshot |
| EAが早すぎる場所で入る | 未確定足shift=0を使っている | confirmed bar / signal timing |
| 過去検証とリアルが違う | リペイント、未確定足変動、計算タイミング差 | repaint check / validation |
| サインが消えたのにEntryしている | 一時的な値をSnapshotとして固定している | snapshot policy / gate |
| EAを再起動すると挙動が変わる | handle再作成、初期バー不足、履歴読み込み不足 | OnInit / handle / bars ready |
| バックテストとリアルで一致しない | tick model、未確定足、インジ内部仕様の差 | BT condition / realtime validation |
インジケーターEA化では、表示されているサインとEAが取得する値を分けて確認して確認する必要があります。
インジケーターEA化で分けるべき責務
サインインジケーターをEA化する時は、次の責務を分けます。
| 責務 | 役割 | 混同すると起きる問題 |
|---|---|---|
| indicator handle | iCustomなどでインジケーターハンドルを作る | handle不正、再初期化不備、解放漏れが起きる |
| buffer read | CopyBufferで値を取得する | 取得失敗とサインなしを混同する |
| signal snapshot | 取得した値を判定用に固定する | 未確定足の変動に振り回される |
| confirmed bar | 確定足で判定するかを決める | リアルで早すぎるEntryになる |
| repaint check | 過去サインが変わるか確認する | バックテスト上だけ良く見える可能性がある |
| entry gate | サインがあってもEntryしてよいか確認する | riskや重複発注の制御を飛ばす |
| log | 取得値、shift、buffer、reasonを記録する | サイン不一致の原因を追えない |
特に重要なのは、CopyBufferで値が取れなかった状態と、サインがない状態を混同しないことです。
iCustomとインジケーターハンドルの考え方
MQL5でカスタムインジケーターをEAから使う場合、iCustomなどでインジケーターハンドルを作成します。
ハンドルが正しく作成できていない場合、CopyBufferで値を取得できません。また、EA終了時や再初期化時には、必要に応じてIndicatorReleaseで解放する設計も必要です。
| 確認項目 | 確認する理由 |
|---|---|
| handleがINVALID_HANDLEではないか | インジケーターが作成できているか確認するため |
| インジケーター名が正しいか | ファイル名、パス、拡張子の扱いを確認するため |
| input引数が一致しているか | チャート表示時とEA呼び出し時の条件差を防ぐため |
| 必要バー数が揃っているか | 計算前の値を使わないため |
| OnDeinitで解放するか | handle管理を明確にするため |
ハンドル作成失敗は、サインなしではありません。ログ上もHANDLE_FAILやINVALID_HANDLEとして分ける必要があります。
CopyBufferで見るべきポイント
CopyBufferは、インジケーターのbuffer値をEA側で取得するために使います。
CopyBufferで重要なのは、どのbuffer番号から、どのshiftの値を、何本取得するかです。
| 項目 | 意味 | 確認すること |
|---|---|---|
| buffer index | どのbufferを見るか | BUYサイン、SELLサイン、補助値がどのbufferか |
| shift | どの足を見るか | 0は未確定足、1は直近確定足として扱うことが多い |
| count | 何本取得するか | 1本だけで足りるか、過去比較が必要か |
| 戻り値 | 取得できた本数 | 取得失敗と値なしを分ける |
| EMPTY_VALUE | サインなし表現の候補 | インジ仕様により0やEMPTY_VALUEの扱いが違う |
CopyBufferが失敗した場合は、サインなしではなく、値取得失敗です。ENTRY判定へ進める前に、取得結果を検証します。
ハンドルの生成・取得・解放を確認する
以下のコード例は、個別の処理と確認項目を読むためのものです。複数の例をそのまま一つのEAへ連結する前提ではありません。初期化、値の取得、状態更新、終了処理をそれぞれの例で区別して確認してください。
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ログが読みにくくなります。検証中は詳細ログ、本番運用では失敗時または状態変化時だけ出すなど、ログレベルを分けると扱いやすくなります。
shift=0とshift=1の違い
CopyBuffer()ではstart_pos=0が現在足です。インジケーターEA化で確定足のサインを使う場合、現在足の変化中値を読むのか、確定済みバーを読むのかを仕様として固定してください。画面上の矢印とbuffer値のタイミングが同じとは限らないため、時刻・buffer番号・shiftをログで照合します。
インジケーターEA化で重要なのが、shiftの扱いです。
一般的に、shift=0は現在形成中の足、shift=1は直近の確定足を意味します。未確定足のサインは、足が確定するまで変わる場合があります。
| shift | 意味 | 注意点 |
|---|---|---|
| shift=0 | 現在足 | リアルタイムで値が変わる可能性がある |
| shift=1 | 直近確定足 | 確定後の値として扱いやすい |
| shift=2以降 | 過去足 | リペイント確認や過去比較に使う場合がある |
EA化では、サインを早く拾いたいのか、確定後に安定して拾いたいのかを仕様として決める必要があります。
未確定足で動くEAは、早く反応できる一方で、サインが消えたり変わったりする可能性を受け入れる必要があります。確定足で動くEAは、反応は遅くなりますが、検証再現性は高めやすくなります。
標準インジケーターの値をEAへ取り込む
以下のコード例は、個別の処理と確認項目を読むためのものです。複数の例をそのまま一つのEAへ連結する前提ではありません。初期化、値の取得、状態更新、終了処理をそれぞれの例で区別して確認してください。
MQL5でEAにインジケーター値を取り込む基本
MQL5では、EAからインジケーター値を使いたい場合、まずインジケーターハンドルを作成します。ハンドルとは、MT5内部で作成されたインジケーター計算結果へアクセスするための識別子です。
たとえば、移動平均線ならiMA、ATRならiATR、RSIならiRSIでハンドルを作成します。その後、CopyBufferを使って、指定したバッファ番号、開始位置、取得本数に応じた値を配列へコピーします。
MQL4の感覚で「iMAを呼べば値が返る」と考えていると、MQL5では混乱しやすくなります。MQL5では、ハンドル作成と値取得を分けて考えることが重要です。
| 工程 | 内容 | 確認ポイント |
|---|---|---|
| ハンドル作成 | iMA、iATR、iRSI、iCustomなどでハンドルを作成します。 | INVALID_HANDLEでないか確認します。 |
| 計算状態確認 | BarsCalculatedで計算済み本数を確認します。 | 必要本数が揃っているか確認します。 |
| 値取得 | CopyBufferで値を配列へ取得します。 | 戻り値が要求本数と一致しているか確認します。 |
| 判定利用 | 取得した値をシグナルやフィルターに使います。 | 現在足か確定足かを明確にします。 |
| 終了処理 | IndicatorReleaseでハンドルを解放します。 | OnDeinitで後処理します。 |
インジケーターハンドルとは
インジケーターハンドルは、インジケーターの計算結果をEAから参照するための識別子です。MQL5では、標準インジケーターやカスタムインジケーターを使う時に、まずハンドルを作成します。
ハンドル作成に失敗すると、CopyBufferで値を取得することはできません。ハンドルがINVALID_HANDLEでないかをOnInitで確認し、失敗した場合は理由をログに出すことが重要です。
int ma_handle = INVALID_HANDLE;
int OnInit()
{
ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
PrintFormat("HANDLE_FAIL name=iMA symbol=%s tf=%d last_error=%d",
_Symbol,
PERIOD_CURRENT,
GetLastError());
return INIT_FAILED;
}
PrintFormat("HANDLE_OK name=iMA handle=%d symbol=%s tf=%d",
ma_handle,
_Symbol,
PERIOD_CURRENT);
return INIT_SUCCEEDED;
}この例では、iMAで移動平均線のハンドルを作成し、INVALID_HANDLEかどうかを確認しています。ハンドル作成に失敗した状態でOnTickへ進むと、CopyBufferが失敗し続けるため、OnInitで止める設計が安全です。
iMAで移動平均線の値を取得する
iMAは、移動平均線のインジケーターハンドルを作成する関数です。EAでは、移動平均線の傾き、価格との位置関係、短期線と長期線のクロスなどを判定するために使うことがあります。
iMAで作成したハンドルから値を取得するにはCopyBufferを使います。移動平均線は通常バッファ0に値が入ります。現在足を使うか、確定足を使うかは、EAの仕様として明確にしてください。
bool GetMAValue(int handle, int shift, double &ma_value)
{
if(handle == INVALID_HANDLE)
{
Print("MA_VALUE_FAIL reason=INVALID_HANDLE");
return false;
}
double buffer[];
ArraySetAsSeries(buffer, true);
ResetLastError();
int copied = CopyBuffer(handle, 0, shift, 1, buffer);
if(copied != 1)
{
PrintFormat("COPYBUFFER_FAIL name=iMA shift=%d copied=%d last_error=%d",
shift,
copied,
GetLastError());
return false;
}
ma_value = buffer[0];
PrintFormat("COPYBUFFER_OK name=iMA shift=%d value=%.5f",
shift,
ma_value);
return true;
}この例では、指定shiftの移動平均線値を1本取得しています。shift=0なら現在足、shift=1なら1本前の確定足を参照する考え方になります。シグナル判定の再現性を重視する場合は、確定足を使う設計が一般的です。
iATRでATR値を取得する
iATRは、ATRのインジケーターハンドルを作成する関数です。ATRは、ボラティリティや値幅の確認に使われることがあります。EAでは、SL/TP幅、トレーリング幅、ボラティリティフィルターなどに使う場合があります。
ATRもCopyBufferで値を取得します。ATRが0に見える場合、値が本当に0なのか、CopyBufferに失敗しているのか、計算本数が不足しているのかを分けて確認してください。
int atr_handle = INVALID_HANDLE;
int OnInit()
{
atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);
if(atr_handle == INVALID_HANDLE)
{
PrintFormat("HANDLE_FAIL name=iATR last_error=%d", GetLastError());
return INIT_FAILED;
}
PrintFormat("HANDLE_OK name=iATR handle=%d", atr_handle);
return INIT_SUCCEEDED;
}
bool GetATRValue(int shift, double &atr_value)
{
double buffer[];
ArraySetAsSeries(buffer, true);
ResetLastError();
int copied = CopyBuffer(atr_handle, 0, shift, 1, buffer);
if(copied != 1)
{
PrintFormat("COPYBUFFER_FAIL name=iATR shift=%d copied=%d last_error=%d",
shift,
copied,
GetLastError());
return false;
}
atr_value = buffer[0];
PrintFormat("COPYBUFFER_OK name=iATR shift=%d value=%.5f",
shift,
atr_value);
return true;
}ATRをSL/TPやトレーリング幅に使う場合は、pointやdigits、銘柄仕様もあわせて確認する必要があります。ATR値をそのまま価格距離として使えるかどうかは、銘柄や仕様によって確認が必要です。
iRSIでRSI値を取得する
iRSIは、RSIのインジケーターハンドルを作成する関数です。EAでは、買われすぎ・売られすぎの確認、エントリーフィルター、逆張り条件、トレンド中の過熱確認などに使われることがあります。
RSIも、ハンドル作成後にCopyBufferで値を取得します。RSIは通常0〜100の範囲で表示されますが、値が取れていない場合に初期値や0.0を判定に使ってしまうと、誤判定につながります。
int rsi_handle = INVALID_HANDLE;
bool CreateRSIHandle()
{
rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
if(rsi_handle == INVALID_HANDLE)
{
PrintFormat("HANDLE_FAIL name=iRSI last_error=%d", GetLastError());
return false;
}
PrintFormat("HANDLE_OK name=iRSI handle=%d", rsi_handle);
return true;
}
bool GetRSIValue(int shift, double &rsi_value)
{
double buffer[];
ArraySetAsSeries(buffer, true);
ResetLastError();
int copied = CopyBuffer(rsi_handle, 0, shift, 1, buffer);
if(copied != 1)
{
PrintFormat("COPYBUFFER_FAIL name=iRSI shift=%d copied=%d last_error=%d",
shift,
copied,
GetLastError());
return false;
}
rsi_value = buffer[0];
PrintFormat("COPYBUFFER_OK name=iRSI shift=%d value=%.2f",
shift,
rsi_value);
return true;
}RSIを使う場合も、現在足か確定足かを明確にします。現在足のRSIは価格更新に応じて変化するため、シグナルが出たり消えたりすることがあります。確定足のRSIを使う場合は、shift=1を基準にすることが多くなります。
CopyBufferの基本構造
CopyBufferは、インジケーターハンドルから指定したバッファの値を取得する関数です。主に、ハンドル、バッファ番号、開始位置、取得本数、出力先配列を指定します。
CopyBufferを使う時は、戻り値を必ず確認します。戻り値は実際にコピーできた要素数です。要求本数を満たしていない場合は、値を使わずにログへ出して処理を止める方が安全です。
| 引数・要素 | 意味 | 確認ポイント |
|---|---|---|
| handle | インジケーターハンドル。 | INVALID_HANDLEでないか確認します。 |
| buffer number | 取得するバッファ番号。 | 標準インジなら0が多いですが、カスタムでは仕様確認が必要です。 |
| start position | 取得開始位置。 | shift=0かshift=1かを明確にします。 |
| count | 取得本数。 | 判定に必要な本数を指定します。 |
| buffer array | 値を受け取る配列。 | ArraySetAsSeriesの設定を確認します。 |
| return value | 実際に取得できた本数。 | 要求本数と一致しているか確認します。 |
bool CopyIndicatorValues(int handle, int buffer_index, int start_shift, int count, double &values[])
{
if(handle == INVALID_HANDLE)
{
Print("COPYBUFFER_FAIL reason=INVALID_HANDLE");
return false;
}
ArraySetAsSeries(values, true);
ResetLastError();
int copied = CopyBuffer(handle, buffer_index, start_shift, count, values);
if(copied != count)
{
PrintFormat("COPYBUFFER_FAIL buffer=%d start=%d count=%d copied=%d last_error=%d",
buffer_index,
start_shift,
count,
copied,
GetLastError());
return false;
}
PrintFormat("COPYBUFFER_OK buffer=%d start=%d count=%d value0=%.5f",
buffer_index,
start_shift,
count,
values[0]);
return true;
}この例では、CopyBufferの戻り値が要求本数と一致するかを確認しています。取得本数が不足した状態で配列を参照すると、誤判定や配列範囲エラーにつながる可能性があります。
BarsCalculatedで計算済み本数を確認する
BarsCalculatedは、インジケーターの計算済み本数を確認するために使います。ハンドルが作成できても、必要な本数がまだ計算されていない場合、CopyBufferで十分な値を取得できないことがあります。
EA起動直後、バックテスト開始直後、MTF、マルチシンボル、iCustom利用時などは、計算済み本数が不足することがあります。BarsCalculatedで必要本数に達しているか確認してからCopyBufferへ進むと、失敗原因を確認やすくなります。
bool IsIndicatorReady(int handle, int needed_bars)
{
if(handle == INVALID_HANDLE)
{
Print("INDICATOR_READY_FAIL reason=INVALID_HANDLE");
return false;
}
ResetLastError();
int calculated = BarsCalculated(handle);
if(calculated < needed_bars)
{
PrintFormat("INDICATOR_NOT_READY calculated=%d needed=%d last_error=%d",
calculated,
needed_bars,
GetLastError());
return false;
}
PrintFormat("INDICATOR_READY calculated=%d needed=%d",
calculated,
needed_bars);
return true;
}この例では、BarsCalculatedの値が必要本数を満たしているかを確認しています。計算済み本数が不足している場合は、シグナル判定へ進まず、ログを出して見送る方が安全です。
現在足と確定足を分けて扱う
インジケーター値をEAに取り込む時は、現在足と確定足を分けて扱う必要があります。現在足は価格更新に応じて値が変化します。確定足はすでに閉じた足なので、判定の再現性が高くなります。
CopyBufferでstart positionに0を指定すると、現在足の値を取得する構成になります。start positionに1を指定すると、1本前の確定足の値を取得する構成になります。どちらを使うかは、EAの仕様として明確にしてください。
| 指定 | 意味 | 注意点 |
|---|---|---|
| shift=0 | 現在足の値。 | 価格更新により変化します。 |
| shift=1 | 1本前の確定足の値。 | 判定の再現性を高めやすいです。 |
| 現在足判定 | 早い反応が可能。 | シグナルが出たり消えたりする場合があります。 |
| 確定足判定 | 値が確定してから判定。 | 反応は遅くなりますが、検証しやすくなります。 |
バックテストで再現性を重視する場合や、シグナルの出たり消えたりを避けたい場合は、確定足を使う設計が扱いやすいです。一方で、リアルタイム性を重視するEAでは現在足を使う場合もあります。その場合は、ログで値の変化を確認できるようにしてください。
ArraySetAsSeriesと配列方向の注意
CopyBufferで取得した値を配列で扱う時は、ArraySetAsSeriesの設定を確認します。ArraySetAsSeriesをtrueにすると、配列の0番目を最新側として扱いやすくなります。
配列方向を確認せずにbuffer[0]やbuffer[1]を使うと、現在足と確定足を取り違える可能性があります。特にCopyBuffer、CopyRates、CopyCloseを混在させるEAでは、配列方向を統一しておくと確認しやすくなります。
| 設定 | 意味 | 確認ポイント |
|---|---|---|
ArraySetAsSeries(buffer, true) | 0番目を最新側として扱います。 | buffer[0]が取得開始位置の値として扱いやすくなります。 |
ArraySetAsSeries(buffer, false) | 通常配列方向で扱います。 | インデックスの意味をログで確認します。 |
| buffer[0] | 配列方向とCopyBuffer指定により意味が変わります。 | 時刻やshiftとセットで確認します。 |
| buffer[1] | 2本以上取得した時に使います。 | クロス判定や前回値比較で注意します。 |
EMPTY_VALUEと取得失敗を混同しない
インジケーターによっては、値が存在しない場所にEMPTY_VALUEが入ることがあります。特に矢印系インジケーター、サイン系インジケーター、条件成立時だけ値を出すカスタムインジケーターでは、EMPTY_VALUEの確認が重要です。
CopyBufferの失敗とEMPTY_VALUEは別です。CopyBufferが成功していても、取得した値がEMPTY_VALUEの場合、そのバーではシグナルが出ていない可能性があります。一方で、CopyBuffer自体が失敗している場合は、インジケーター値を判定に使うべきではありません。
bool GetSignalValue(int handle, int buffer_index, int shift, double &signal_value)
{
double buffer[];
ArraySetAsSeries(buffer, true);
ResetLastError();
int copied = CopyBuffer(handle, buffer_index, shift, 1, buffer);
if(copied != 1)
{
PrintFormat("SIGNAL_COPY_FAIL buffer=%d shift=%d copied=%d last_error=%d",
buffer_index,
shift,
copied,
GetLastError());
return false;
}
if(buffer[0] == EMPTY_VALUE)
{
PrintFormat("SIGNAL_EMPTY buffer=%d shift=%d", buffer_index, shift);
return false;
}
signal_value = buffer[0];
PrintFormat("SIGNAL_VALUE buffer=%d shift=%d value=%.5f",
buffer_index,
shift,
signal_value);
return true;
}この例では、CopyBuffer失敗とEMPTY_VALUEを分けてログに出しています。サイン系インジケーターをEAへ取り込む場合は、この確認が非常に重要です。
iCustomでカスタムインジケーター値を取得する
iCustomは、カスタムインジケーターのハンドルを作成するために使います。EAから自作インジケーターや外部インジケーターの値を取得したい場合、iCustomでハンドルを作成し、CopyBufferでバッファ値を取得します。
iCustomを使う時は、インジケーターファイル名、input引数の順番、バッファ番号、サブウィンドウ、EMPTY_VALUE、再描画の有無を確認します。カスタムインジケーター側の仕様が分からないままEAへ取り込むと、値の意味を誤解する可能性があります。
int custom_handle = INVALID_HANDLE;
bool CreateCustomIndicatorHandle()
{
custom_handle = iCustom(_Symbol, PERIOD_CURRENT, "MyCustomIndicator", 14, 2.0);
if(custom_handle == INVALID_HANDLE)
{
PrintFormat("HANDLE_FAIL name=MyCustomIndicator last_error=%d",
GetLastError());
return false;
}
PrintFormat("HANDLE_OK name=MyCustomIndicator handle=%d",
custom_handle);
return true;
}この例では、MyCustomIndicatorというカスタムインジケーターをiCustomで呼び出しています。実際には、ファイル名、パス、input引数、バッファ番号が正しいかを確認してください。
カスタムインジケーター連携で確認すること
カスタムインジケーターをEAに取り込む場合は、標準インジケーターよりも確認項目が増えます。特に、どのバッファに何の値が入っているか、サインが出ない時にEMPTY_VALUEになるのか0になるのか、現在足で値が変わるのか、確定足で固定されるのかを確認します。
インジケーター側が描画用に作られている場合、EAから読み取る用途に適していないこともあります。バッファが公開されているか、CopyBufferで取得できるか、描画オブジェクトだけで値を出していないかを確認してください。
| 確認項目 | 内容 | 注意点 |
|---|---|---|
| ファイル名 | iCustomで指定するインジケーター名。 | 拡張子やフォルダ階層の扱いに注意します。 |
| input引数 | カスタムインジケーターの設定値。 | 順番や型が違うと期待値になりません。 |
| バッファ番号 | CopyBufferで指定する番号。 | 買いサイン・売りサインなどの対応を確認します。 |
| EMPTY_VALUE | 値なしの扱い。 | サインなしを注文条件と混同しないようにします。 |
| 現在足更新 | 現在足で値が変わるか。 | リペイントや再計算の有無を確認します。 |
| 確定足 | 確定足で値が固定されるか。 | バックテスト再現性に影響します。 |
| ログ | 値、shift、buffer、copied。 | EA側で取得状態を確認します。 |
IndicatorReleaseでハンドルを解放する
EA終了時には、作成したインジケーターハンドルをIndicatorReleaseで解放します。OnDeinitで後処理を行うことで、リソース管理が明確になります。
短いサンプルでは省略されることがありますが、複数インジケーター、MTF、マルチシンボル、iCustomを多用するEAでは、ハンドル管理を整理しておくと保守しやすくなります。
void OnDeinit(const int reason)
{
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
PrintFormat("HANDLE_RELEASE name=iMA handle=%d reason=%d",
ma_handle,
reason);
ma_handle = INVALID_HANDLE;
}
if(atr_handle != INVALID_HANDLE)
{
IndicatorRelease(atr_handle);
PrintFormat("HANDLE_RELEASE name=iATR handle=%d reason=%d",
atr_handle,
reason);
atr_handle = INVALID_HANDLE;
}
if(rsi_handle != INVALID_HANDLE)
{
IndicatorRelease(rsi_handle);
PrintFormat("HANDLE_RELEASE name=iRSI handle=%d reason=%d",
rsi_handle,
reason);
rsi_handle = INVALID_HANDLE;
}
}この例では、各ハンドルが有効な場合だけIndicatorReleaseを呼び出し、その後INVALID_HANDLEへ戻しています。ログにreasonを出しておくと、EA終了理由の確認にも役立ちます。
MTFでインジケーター値を取得する時の注意
MTFでは、現在チャートとは別の時間足のインジケーター値を取得します。たとえば、M5チャート上でEAを動かしながら、H1の移動平均線やRSIをフィルターに使うような構成です。
MTFで注意すべきなのは、現在チャートのバー更新と上位足のバー更新が一致しないことです。上位足のshift=0は形成中であり、値が変わります。上位足をフィルターに使う場合は、shift=1の確定足を使うか、現在足を使うかを仕様として明確にします。
int h1_ma_handle = INVALID_HANDLE;
bool CreateH1MAHandle()
{
h1_ma_handle = iMA(_Symbol, PERIOD_H1, 20, 0, MODE_SMA, PRICE_CLOSE);
if(h1_ma_handle == INVALID_HANDLE)
{
PrintFormat("HANDLE_FAIL name=H1_MA last_error=%d", GetLastError());
return false;
}
PrintFormat("HANDLE_OK name=H1_MA handle=%d", h1_ma_handle);
return true;
}上位足の値が取得できない場合は、時間足データ不足、バックテスト開始直後、BarsCalculated不足を確認します。MTFでは、CopyBuffer失敗をシグナル不成立と混同しないようにしてください。
マルチシンボルでインジケーター値を取得する時の注意
マルチシンボルEAでは、現在チャートとは別の銘柄のインジケーター値を取得することがあります。この場合、対象銘柄がMarket Watchに表示されているか、SymbolSelectで選択できているか、ヒストリーデータがあるかを確認する必要があります。
別銘柄のiMAやiRSIハンドルを作る場合は、_Symbolに固定せず、対象symbolを引数として渡す構成にします。銘柄ごとにハンドルを管理する必要があるため、単一ハンドルのEAよりも管理が複雑になります。
bool PrepareSymbol(string symbol)
{
ResetLastError();
if(!SymbolSelect(symbol, true))
{
PrintFormat("SYMBOL_SELECT_FAIL symbol=%s last_error=%d",
symbol,
GetLastError());
return false;
}
PrintFormat("SYMBOL_SELECT_OK symbol=%s", symbol);
return true;
}SymbolSelectに失敗した状態でインジケーターハンドルを作成すると、ハンドル作成やCopyBufferに失敗する可能性があります。マルチシンボルEAでは、対象銘柄ごとの準備ログを残してください。
CopyBuffer失敗時に確認すること
CopyBufferが失敗する場合は、ハンドル、バッファ番号、取得開始位置、取得本数、計算済み本数、配列方向、対象銘柄、対象時間足、GetLastErrorを確認します。値が取れない理由を1つに決めつけないことが重要です。
| 症状 | 確認する場所 | 主な原因候補 |
|---|---|---|
| copiedが要求本数より少ない | CopyBuffer戻り値、BarsCalculated。 | 計算本数不足、ヒストリー不足、起動直後。 |
| INVALID_HANDLEになる | iMA、iATR、iRSI、iCustomの戻り値。 | 引数不正、ファイル名違い、銘柄・時間足データ不足。 |
| 値が0.0に見える | CopyBuffer成功可否、値の意味。 | 取得失敗、値なし、初期値、実際に0。 |
| EMPTY_VALUEになる | 取得値、カスタムインジ仕様。 | サインなし、描画なし、条件不成立。 |
| MTFだけ失敗する | 上位足ハンドル、BarsCalculated。 | 上位足データ不足、バックテスト開始直後。 |
| 別銘柄だけ失敗する | SymbolSelect、銘柄名、Market Watch。 | 銘柄未選択、銘柄名違い、ヒストリー不足。 |
| バッファ番号が合わない | インジケーター仕様。 | 買いサイン・売りサインのバッファ番号違い。 |
バックテストでCopyBufferを確認する
バックテストでは、EAの損益結果だけでなく、インジケーター値が正しく取得できているかを確認します。特に、CopyBufferの取得本数、BarsCalculated、shift、現在足と確定足、MTF、iCustomのバッファ番号をログで確認してください。
バックテスト開始直後は、インジケーター計算に必要な本数が揃っていない場合があります。その状態でCopyBuffer結果を使ってしまうと、シグナル不成立や誤判定につながります。
- EA名とバージョンを記録する
- 使用したsetファイルを保存する
- 銘柄、時間足、期間を記録する
- インジケーターハンドル作成ログを確認する
- BarsCalculatedの値を確認する
- CopyBufferの戻り値を確認する
- 現在足と確定足を混同していないか確認する
- iCustom利用時はバッファ番号を確認する
- EMPTY_VALUEをシグナル不成立として扱うか確認する
- バックテスト結果を将来成績保証として扱わない
EAに取り込んだインジケーター値をどこで使うかを分ける
EAにインジケーター値を取り込む時は、値を取得できたかどうかだけでなく、その値をEA内部のどこで使うかを先に分ける必要があります。
同じMA値やRSI値でも、エントリー方向を決めるために使うのか、エントリーを見送るフィルターとして使うのか、決済条件として使うのか、通知やパネル表示だけに使うのかで、必要な確認内容が変わります。
たとえば、RSIが30未満になったことを通知するだけなら、値取得と通知重複防止が中心です。一方で、自動発注EAとして使う場合は、スプレッド、既存ポジション、Magic Number、証拠金、ロット、OrderCheck、取引時間、外部停止条件まで確認する必要があります。
| 責務 | インジケーター値の使い方 | 確認ポイント |
|---|---|---|
| signal | BUY / SELL / WAITの候補を作る | 値取得、shift、EMPTY_VALUE、判定方向を確認する |
| filter | エントリー許可・見送り条件に使う | トレンド方向、過熱感、ボラティリティ、時間帯と分ける |
| risk | SL幅、トレーリング幅、ロット補助に使う | ATR値、point、pips、銘柄仕様、証拠金を確認する |
| exit | 決済条件や撤退条件に使う | エントリー条件と決済条件を混同しない |
| notification | 通知だけに使う | 重複通知、通知対象足、通知タイミングを確認する |
| UI / panel | 画面表示や状態確認に使う | 取得失敗時の表示、更新頻度、表示値の説明を確認する |
EA設計では、インジケーター値を取得した時点で発注へ直結させないことが重要です。値取得、値の妥当性確認、signal判定、entry gate、risk確認、executionを分けることで、後からログを見た時にどの段階で止まったのかを確認できます。
この分離がないと、「CopyBufferは成功しているのにEAが入らない」という状態になった時、値が悪いのか、判定条件が悪いのか、発注前条件で止まったのか、注文処理で失敗したのかを確認できません。
標準インジケーター別のEA取り込み方
EAへ取り込む標準インジケーターは、それぞれ値の性質が異なります。すべてを同じ「数値」として扱うのではなく、値の単位、意味、使う責務を分けて確認してください。
| インジケーター | 値の性質 | EAでの主な使い方 | 注意点 |
|---|---|---|---|
| iMA | 価格に近いライン値 | 方向確認、クロス、価格との位置関係 | 価格とMAのshiftを合わせる |
| iRSI | 0〜100のオシレーター値 | 過熱感、押し目、逆張り候補の補助 | 価格やpipsとは単位が違う |
| iATR | 価格変動幅 | ボラティリティ確認、SL幅、トレーリング補助 | point、pips、価格単位への換算を確認する |
| iBands | 上限・中央・下限の価格値 | 価格位置、バンド内外、ボラティリティ確認 | buffer番号を必ず確認する |
| iMACD | MACD線やシグナル線など複数buffer | 方向、勢い、クロス補助 | どのbufferを読むか確認する |
| iStochastic | %K / %Dなどのオシレーター値 | 過熱感、クロス、レンジ補助 | 現在足の変動と確定足を分ける |
たとえば、ATRはエントリー方向を直接決めるインジケーターではありません。ATRが大きいからBUY、ATRが小さいからSELLという使い方ではなく、相場の値幅やボラティリティ環境を確認するために使う方が自然です。
一方で、MAは価格と同じような水準の値を返すため、価格との比較や短期線・長期線の比較に使いやすいです。ただし、現在足の終値と確定足のMAを比較するなど、参照足がズレると判定が不自然になります。
RSIやStochasticは、価格そのものではなく指標値です。0〜100の範囲で評価する値を、価格差やpipsと同じ単位で扱わないようにしてください。
CopyBufferで取得できた値を使える値として扱う前の確認
CopyBufferが成功したように見えても、その値をEA判定に使ってよいとは限りません。取得本数が足りているか、値がEMPTY_VALUEではないか、NaNではないか、意図したshiftの値か、buffer番号が正しいかを確認する必要があります。
| 状態 | 意味 | EA側の扱い |
|---|---|---|
| copiedが要求本数と一致 | CopyBufferの取得処理は成功 | 次に値の妥当性を確認する |
| copiedが0 | 取得できた値がない | BarsCalculated、履歴、buffer番号を確認する |
| copiedが-1 | CopyBuffer失敗 | GetLastErrorとhandle状態を確認する |
| EMPTY_VALUE | そのバーに値やサインがない可能性 | サインなしとして扱うか仕様で決める |
| 0.0 | 有効値の場合も初期値の場合もある | インジケーター仕様とログを照合する |
| 異常に大きい値 | EMPTY_VALUE相当や未初期化の可能性 | MathIsValidNumberなどで確認する |
| 想定と違う方向の値 | buffer番号違いの可能性 | SetIndexBufferやData Windowを確認する |
値取得の成功、値の有効性、signal成立は別の段階です。CopyBufferが成功しても、EMPTY_VALUEならサインなしの可能性があります。値が有効でも、EAのsignal条件を満たしていないならWAITです。
この段階を分けずに実装すると、正常なWAIT状態をエラー扱いしたり、取得失敗をシグナル不成立として見逃したりします。ログ上でも、COPYBUFFER_FAIL、VALUE_EMPTY、SIGNAL_WAIT、ENTRY_BLOCKを分けると確認しやすくなります。
リペイント確認が必要な理由
リペイントとは、過去に出ていたサインや値が後から変わることです。
リペイントするインジケーターをEA化する場合、バックテスト上ではきれいに見えても、リアルタイムでは同じように動かない可能性があります。
| 確認項目 | 理由 |
|---|---|
| 過去の矢印が変わるか | サイン履歴が後から変わる可能性を確認するため |
| shift=0だけ変わるのか | 未確定足だけの変動か確認するため |
| shift=1以降も変わるのか | 確定足後も変わるならEA化リスクが高いため |
| バックテストとリアルで差があるか | 検証再現性を確認するため |
| buffer値と表示Objectが一致するか | 見た目とEA取得値の差を確認するため |
リペイント自体が必ず悪いわけではありません。ただし、EA化する場合は、どのタイミングの値を使うのか、過去サインの変化をどう扱うのかを明確にする必要があります。
サイン表示とbuffer値は同じとは限らない
チャート上に矢印が表示されていても、その矢印がEAから直接読めるbuffer値とは限りません。
インジケーターによっては、矢印Objectをチャート上に描画しているだけで、bufferには値を出していない場合があります。また、BUYサインとSELLサインが別bufferで管理されている場合もあります。
| 確認対象 | 注意点 |
|---|---|
| bufferサイン | CopyBufferで取得しやすい |
| Objectサイン | Object名や座標から判断する必要があり、EA化では慎重に扱う |
| コメント表示 | 画面表示であり、判定の真実にしない方が安全 |
| 色変化 | buffer構造やDRAW設定により読み方が変わる |
| アラートだけのサイン | EA側で同じ条件を再現できるか確認が必要 |
EA化前には、サインがbufferで取れるのか、Objectでしか出ていないのか、アラートだけなのかを確認します。
iCustomの入力・バッファ・サインを照合する
以下のコード例は、個別の処理と確認項目を読むためのものです。複数の例をそのまま一つのEAへ連結する前提ではありません。初期化、値の取得、状態更新、終了処理をそれぞれの例で区別して確認してください。
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時など、目的に応じて出力条件を分けると扱いやすくなります。
現在足を使うEAと確定足を使うEAの違い
インジケーター値をEAへ取り込む時、現在足を使う設計と、確定足を使う設計では挙動が大きく変わります。
現在足は価格更新に応じて変化します。シグナルが早く出る一方で、同じバーの中で条件が成立したり不成立になったりすることがあります。確定足は反応が遅くなりますが、検証結果と実運用の照合がしやすくなります。
| 方式 | 特徴 | 向いている用途 | 注意点 |
|---|---|---|---|
| 現在足方式 | 価格更新中の値を使う | リアルタイム通知、早い反応を狙う判定 | サインが出たり消えたりする可能性がある |
| 確定足方式 | 1本前の確定した値を使う | バー確定型EA、再現性重視の検証 | 反応は現在足方式より遅くなる |
| 現在足通知+確定足発注 | 通知と発注で基準を分ける | 半裁量、確認用EA | ユーザーに基準差を明示する必要がある |
| 複数本確認方式 | 直近数本の値を比較する | クロス、傾き、連続条件 | 取得本数不足に注意する |
「チャートではサインが出ているのにEAが入らない」という相談では、現在足と確定足の違いが原因になっていることがあります。チャート上で見ているサインが現在足の変動中サインで、EAが確定足を見ている場合、見え方が違って当然です。
そのため、EA仕様書やinput説明では、判定basisを明記してください。たとえば、SignalShift=1なら確定足基準、SignalShift=0なら現在足基準というように、利用者が分かる形にしておくと問い合わせ対応もしやすくなります。
iCustomでサイン系インジケーターをEA化する時の確認順序
サイン系カスタムインジケーターをEA化する場合、標準インジケーターより確認項目が多くなります。
特に、買いサインと売りサインがどのbufferに入っているのか、サインがない時にEMPTY_VALUEを返すのか0を返すのか、現在足でサインが消えるのか、確定足で固定されるのかを確認する必要があります。
| 順番 | 確認項目 | 確認内容 |
|---|---|---|
| 1 | インジケーター名 | iCustomで指定するファイル名と配置場所を確認する |
| 2 | input引数 | インジ側inputの順序、型、初期値とEA側指定を照合する |
| 3 | handle作成 | INVALID_HANDLEでないか、GetLastErrorを確認する |
| 4 | buffer番号 | BUYサイン、SELLサイン、フィルター値のbufferを確認する |
| 5 | shift | 現在足で見るか、確定足で見るかを決める |
| 6 | EMPTY_VALUE | サインなしをどの値で表しているか確認する |
| 7 | リペイント | 未確定足や過去足の値が変わるか確認する |
| 8 | EA側signal化 | 値取得をBUY / SELL / WAITへ変換する |
カスタムインジケーターのソースコードがある場合は、SetIndexBuffer、OnCalculate、EMPTY_VALUEの入れ方を確認できます。ソースコードがない場合は、Data Window、チャート表示、CopyBufferログを使って推測することになります。
ex5のみのインジケーターをEA化する場合、内部bufferの意味が完全には分からないことがあります。この場合は、すぐに自動発注へつなげず、まず通知やログ出力でサイン検出の再現性を確認する方が安全です。
値取得できているのにEAが動かない時の確認
CopyBufferで値が取れているにもかかわらず、EAがエントリーしない場合があります。この場合、インジケーター値の取得は成功しているため、次にsignal判定、filter、entry gate、risk、executionを確認します。
| 段階 | 確認内容 | よくある停止理由 |
|---|---|---|
| value | CopyBufferで値が取得できているか | 取得失敗、EMPTY_VALUE、buffer番号違い |
| signal | 取得値からBUY / SELL / WAITへ変換できているか | 条件未達、現在足と確定足の違い |
| filter | 追加条件を通過しているか | MA方向、RSI範囲、ATR環境、時間帯 |
| entry gate | 発注前制限を通過しているか | スプレッド過大、既存ポジションあり、取引停止 |
| risk | ロットや証拠金条件が通っているか | 最小ロット未満、証拠金不足、StopLevel違反 |
| execution | 注文結果が成功しているか | OrderSend失敗、retcode異常、約定拒否 |
EAが動かない時に「インジケーター値が取れていない」と決めつけるのは危険です。実際には、インジケーター値は正常に取れているが、entry gateやrisk条件で止まっている場合があります。
そのため、ログでは、VALUE_OK、SIGNAL_EVAL、FILTER_BLOCK、ENTRY_BLOCK、RISK_BLOCK、ORDER_RESULT のように段階を分けると原因を追いやすくなります。
ATRをSL幅やトレーリングに使う時の注意点
ATRは、EAでSL幅、トレーリング幅、ボラティリティ判定に使われることがあります。ただし、ATR値をそのままpipsとして扱うと、銘柄や桁数によって誤った距離になる可能性があります。
| 確認項目 | 内容 | 注意点 |
|---|---|---|
| Digits | 銘柄の小数桁 | 5桁、3桁、2桁、XAUUSDなどで感覚が違う |
| Point | 最小価格単位 | pips換算と混同しない |
| ATR値 | 価格単位の変動幅 | そのままSL距離に使えるか確認する |
| StopLevel | 注文時の最小距離 | ATR由来のSL幅が近すぎる場合がある |
| Spread | 現在スプレッド | 低ATR環境ではスプレッド比率が大きくなる |
| 銘柄仕様 | 最小ロット、tick value、contract size | risk計算と組み合わせて確認する |
ATRを使う場合は、単に「ATRが大きいから広いSL」「ATRが小さいから狭いSL」とするだけでは不十分です。銘柄仕様、StopLevel、スプレッド、証拠金、ロット計算と合わせて確認する必要があります。
また、ATRは相場環境を示す値であり、売買方向そのものを決める値ではありません。EA内部では、signalではなく、risk補助、volatility filter、exit補助として扱う方が自然な場面が多くあります。
通知・半裁量・完全自動で確認項目を変える
同じインジケーター値取得でも、通知ツール、半裁量EA、完全自動EAでは、必要な確認項目が異なります。
通知ツールでは、値取得と重複通知防止が中心です。半裁量EAでは、サイン表示や手動実行ボタン、誤操作防止が重要です。完全自動EAでは、発注前のentry gate、risk、execution、注文結果確認まで必要になります。
| 用途 | インジケーター値の扱い | 追加確認 |
|---|---|---|
| 通知ツール | サイン検出や条件成立を通知する | 重複通知、通知先、通知文、ログ |
| 情報パネル | 現在値や状態を表示する | 更新頻度、取得失敗時の表示、色分け |
| 半裁量EA | サインを確認して手動操作する | ボタン、確認表示、誤操作防止 |
| 完全自動EA | signalから注文処理へ進める | entry gate、risk、OrderCheck、retcode確認 |
| 検証EA | 条件成立や値変化を記録する | CSVログ、検証期間、再現条件 |
インジケーター値を取得できるようになったからといって、すぐに完全自動発注へ接続する必要はありません。まず通知やログで検証し、サインの再現性、リペイント有無、確定足での残り方を確認してから、発注処理へ進める方が安全です。
EAファンクラブでは、インジケーター値取得を、通知、表示、判定、発注、決済、リスク管理へ分けて扱います。これにより、後から「通知だけ使う」「発注を止める」「ログ検証だけ行う」といった運用に切り替えやすくなります。
実コード例1:インジケーターハンドルを管理する
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したインジケーターハンドル管理例です。
// 実運用EAの完成ソースではありません。
int g_indicatorHandle = INVALID_HANDLE;
bool CreateIndicatorHandle()
{
// 実際のEAでは、インジケーター名やinput引数を仕様に合わせて設定します。
// この記事では実インジケーター名や中核条件は扱いません。
g_indicatorHandle = iCustom(_Symbol, PERIOD_CURRENT, "SampleIndicator");
if(g_indicatorHandle == INVALID_HANDLE)
{
Print("DIAG/INDICATOR: event=HANDLE_FAIL",
" reason=INVALID_HANDLE",
" symbol=", _Symbol);
return false;
}
Print("DIAG/INDICATOR: event=HANDLE_OK",
" reason=ICUSTOM_CREATED",
" symbol=", _Symbol);
return true;
}
void ReleaseIndicatorHandle()
{
if(g_indicatorHandle == INVALID_HANDLE)
return;
IndicatorRelease(g_indicatorHandle);
g_indicatorHandle = INVALID_HANDLE;
Print("DIAG/INDICATOR: event=HANDLE_RELEASE",
" reason=INDICATOR_RELEASE_DONE");
}この例では、iCustomでインジケーターハンドルを作り、失敗時はHANDLE_FAILとしてログに残します。handle作成失敗は、サインなしとは別の状態です。
実コード例2:CopyBuffer取得結果を分ける
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したCopyBuffer取得例です。
// 実運用EAの完成ソースではありません。
struct BufferReadResult
{
bool ok;
double value;
int copied;
int bufferIndex;
int shift;
string reason;
};
BufferReadResult ReadIndicatorBuffer(int bufferIndex, int shift)
{
BufferReadResult result;
result.ok = false;
result.value = EMPTY_VALUE;
result.copied = 0;
result.bufferIndex = bufferIndex;
result.shift = shift;
result.reason = "INIT";
if(g_indicatorHandle == INVALID_HANDLE)
{
result.reason = "INVALID_HANDLE";
return result;
}
double values[1];
ArraySetAsSeries(values, true);
ResetLastError();
int copied = CopyBuffer(g_indicatorHandle, bufferIndex, shift, 1, values);
result.copied = copied;
if(copied != 1)
{
result.reason = "COPYBUFFER_FAIL";
return result;
}
result.ok = true;
result.value = values[0];
result.reason = "COPYBUFFER_OK";
return result;
}この例では、CopyBufferの取得失敗と、取得できた値を分けています。copiedが1ではない場合は、サインなしではなく、取得失敗として扱います。
実コード例3:Signal Snapshotとして固定する
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したSignal Snapshot例です。
// 実運用EAの完成ソースではありません。
enum IndicatorSignalSide
{
IND_SIGNAL_NONE = 0,
IND_SIGNAL_BUY,
IND_SIGNAL_SELL
};
struct IndicatorSignalSnapshot
{
bool valid;
IndicatorSignalSide side;
int shift;
datetime barTime;
double rawValue;
string reason;
};
IndicatorSignalSnapshot BuildSignalSnapshot(int shift)
{
IndicatorSignalSnapshot snap;
snap.valid = false;
snap.side = IND_SIGNAL_NONE;
snap.shift = shift;
snap.barTime = iTime(_Symbol, PERIOD_CURRENT, shift);
snap.rawValue = EMPTY_VALUE;
snap.reason = "INIT";
BufferReadResult buyBuffer = ReadIndicatorBuffer(0, shift);
BufferReadResult sellBuffer = ReadIndicatorBuffer(1, shift);
if(!buyBuffer.ok && !sellBuffer.ok)
{
snap.reason = "BUFFER_READ_FAIL";
return snap;
}
if(buyBuffer.ok && buyBuffer.value != EMPTY_VALUE)
{
snap.valid = true;
snap.side = IND_SIGNAL_BUY;
snap.rawValue = buyBuffer.value;
snap.reason = "BUY_SIGNAL";
return snap;
}
if(sellBuffer.ok && sellBuffer.value != EMPTY_VALUE)
{
snap.valid = true;
snap.side = IND_SIGNAL_SELL;
snap.rawValue = sellBuffer.value;
snap.reason = "SELL_SIGNAL";
return snap;
}
snap.reason = "NO_SIGNAL";
return snap;
}この例では、取得値をその場で直接Entryへ使わず、IndicatorSignalSnapshotとして固定しています。
実際のEAでは、このSnapshotをGATE、risk、duplicate controlへ渡し、最終的にexecution層へ進むかを判断します。
実コード例4:確定足で判定する
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化した確定足判定例です。
// 実運用EAの完成ソースではありません。
int SignalShiftForConfirmedBar()
{
return 1;
}
void EvaluateConfirmedBarSignal()
{
int shift = SignalShiftForConfirmedBar();
IndicatorSignalSnapshot snap = BuildSignalSnapshot(shift);
Print("DIAG/INDICATOR_SIGNAL: event=SNAPSHOT",
" valid=", snap.valid,
" shift=", snap.shift,
" bar_time=", TimeToString(snap.barTime, TIME_DATE | TIME_MINUTES),
" reason=", snap.reason);
if(!snap.valid)
{
Print("DIAG/ENTRY: event=SKIP",
" reason=", snap.reason);
return;
}
Print("DIAG/ENTRY: event=CANDIDATE",
" reason=CONFIRMED_BAR_SIGNAL",
" side=", (int)snap.side);
// 実際のEAでは、この後にGATE / risk / executionへ分けて渡します。
// この記事では実Entry条件やOrderSend処理は扱いません。
}この例では、shift=1を使って確定足のサインを評価しています。未確定足を使う場合は、サイン変動やリペイントの扱いを別途仕様化する必要があります。
ログで確認するポイント
インジケーターEA化では、サインが出たかどうかだけでなく、どのbuffer、どのshift、どのbar timeを見たかが重要です。
| ログ区分 | 確認できること | 例 |
|---|---|---|
| INDICATOR | handle作成・解放 | HANDLE_OK、HANDLE_FAIL、HANDLE_RELEASE |
| BUFFER_READ | CopyBuffer取得結果 | COPYBUFFER_OK、COPYBUFFER_FAIL |
| INDICATOR_SIGNAL | サインSnapshot | BUY_SIGNAL、SELL_SIGNAL、NO_SIGNAL |
| SHIFT | どの足を見たか | shift=0、shift=1 |
| BAR_TIME | 対象バー時刻 | bar_time |
| REPAINT_CHECK | 過去サイン変化の確認 | VALUE_CHANGED、VALUE_STABLE |
| ENTRY | Entry候補へ渡したか | CANDIDATE、SKIP |
「サインが出ているのにEAが入らない」という相談では、まず表示サインとEA取得値が一致しているかを確認します。
バックテストとリアルで確認すること
インジケーターEA化では、バックテストだけでなく、リアルタイムでの挙動確認も重要です。
| 確認項目 | バックテスト | リアルタイム確認 |
|---|---|---|
| サイン発生位置 | 履歴上のサイン位置を確認 | リアルで同じタイミングに出るか確認 |
| shift | shift=0 / shift=1の違いを確認 | 未確定足で変動するか確認 |
| リペイント | 履歴だけでは見えにくい場合がある | 発生後にサインが変わるか観察 |
| CopyBuffer値 | ログで値取得を確認 | チャート表示とログ値を比較 |
| Entry候補 | 候補発生時刻を確認 | リアルで候補が固定されるか確認 |
バックテスト上で見えるサインが、リアルタイムで同じように使えるとは限りません。特に、未確定足サインやリペイント型インジケーターでは、リアルタイム確認が重要です。
よくある失敗例
失敗1:チャート上の矢印だけを見てEA化する
表示されている矢印と、EAがCopyBufferで取得できる値は同じとは限りません。buffer番号、shift、EMPTY_VALUE、Object表示かどうかを確認する必要があります。
失敗2:CopyBuffer失敗をサインなしとして扱う
CopyBufferが失敗した状態と、サインがない状態は違います。COPYBUFFER_FAILとNO_SIGNALはログ上も分けるべきです。
失敗3:shift=0を確定サインとして扱う
shift=0は現在足であり、値が変わる可能性があります。確定足で判断したい場合は、shift=1を使う方針を検討します。
失敗4:リペイント確認をしない
過去サインが変わるインジケーターをそのままEA化すると、バックテストとリアル稼働で差が出る可能性があります。
失敗5:handle作成失敗を見逃す
iCustomでhandleが作成できていない場合、EAは値を取得できません。INVALID_HANDLEを起動時に確認し、ログに残す必要があります。
失敗6:インジケーターinput条件がチャート表示とEA呼び出しで違う
チャートに表示したインジケーターとEAから呼び出したインジケーターでinputが違うと、サイン位置が変わる場合があります。
失敗7:サイン取得とEntry実行を直接つなぐ
サインがあっても、GATE、risk、duplicate control、execution precheckを通す必要があります。サイン取得はEntry候補の一部として扱います。
導入前・開発依頼前に整理する情報
インジケーターEA化を依頼する場合、次の情報を整理しておくと仕様確認がスムーズになります。
- 対象インジケーター名
- インジケーターのinput設定
- BUYサインとSELLサインの見た目
- BUY / SELLのbuffer番号
- サインなしの値が0か、EMPTY_VALUEか、別の値か
- shift=0で使うのか、shift=1で使うのか
- 確定足でEntryするのか、未確定足でEntryするのか
- リペイントするかどうか
- サイン発生後に消えることがあるか
- チャート表示とCopyBuffer値が一致するか
- Entryを1回だけにするか、同一方向の再Entryを許可するか
- サイン取得後に通すGATE / risk条件
- ログに出すbuffer、shift、bar time、reason
- バックテストとリアル確認の手順
インジケーターEA化では、見た目のサインだけでなく、EAが取得できる値、確定タイミング、リペイントの有無を整理することが重要です。
値取得・EA化の検証条件を記録する
よくある失敗パターン
| 失敗パターン | 起きる問題 | 確認すること |
|---|---|---|
| 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ログで検索しやすくしている
検証時に残すべき証跡
EAにインジケーター値を取り込む処理を検証する場合、単にバックテストを実行するだけでは不十分です。どの値を、どの足で、どのbufferから取得し、どの判定に使ったのかを確認できる証跡が必要です。
| 証跡 | 残す内容 | 目的 |
|---|---|---|
| setファイル | input設定、使用インジ、shift、期間 | 同じ条件を再現する |
| Expertsログ | handle、BarsCalculated、CopyBuffer、signal | 値取得から判定まで追跡する |
| バックテスト条件 | 銘柄、時間足、期間、モデル、スプレッド | 検証条件を明確にする |
| チャートスクリーンショット | サイン位置、インジ表示、EAログ | 表示と取得値を照合する |
| CSVログ | 時刻、値、signal、block reason | 大量データを後から分析する |
| バージョン情報 | EAバージョン、インジバージョン | 修正前後の差分を確認する |
不具合調査では、「動かない」という説明だけでは原因を特定できません。値取得に失敗しているのか、値は取れているがsignal条件を満たしていないのか、entry gateで止まっているのか、注文処理で失敗しているのかをログで分けてください。
特にCopyBuffer関連では、buffer、shift、copied、last_error、BarsCalculated、取得値、basisを同じ行に残すと、後から原因を確認しやすくなります。
CopyBufferのログ設計
CopyBufferを使うEAでは、ログ設計が重要です。EAが注文しない時、ロジック条件ではなく、インジケーター値が取得できていないことが原因の場合があります。
ログ名は、HANDLE_OK、HANDLE_FAIL、INDICATOR_READY、INDICATOR_NOT_READY、COPYBUFFER_OK、COPYBUFFER_FAIL、SIGNAL_EMPTYのように分けると検索しやすくなります。
| ログ名 | 残す内容 | 目的 |
|---|---|---|
HANDLE_OK | インジケーター名、handle、symbol、timeframe。 | ハンドル作成成功を確認します。 |
HANDLE_FAIL | インジケーター名、GetLastError。 | ハンドル作成失敗を確認します。 |
INDICATOR_READY | BarsCalculated、必要本数。 | 計算済み本数を確認します。 |
INDICATOR_NOT_READY | 計算済み本数不足。 | 起動直後やデータ不足を確認します。 |
COPYBUFFER_OK | buffer、shift、count、value。 | 値取得成功を確認します。 |
COPYBUFFER_FAIL | buffer、shift、要求本数、取得本数、GetLastError。 | 値取得失敗を確認します。 |
SIGNAL_EMPTY | EMPTY_VALUE、buffer、shift。 | サインなしと取得失敗を分けます。 |
開発依頼前に整理する情報
EAにインジケーター値を取り込みたい場合や、CopyBufferが失敗する不具合を相談する場合は、対象EA、対象インジケーター、setファイル、銘柄、時間足、ハンドル作成ログ、CopyBufferログ、バッファ番号、shift指定を整理してください。
特にカスタムインジケーターをEAに取り込む場合は、インジケーター側のバッファ仕様が重要です。買いサイン、売りサイン、ライン、フィルター値がどのバッファに入っているのか、値なしがEMPTY_VALUEなのか0なのかを確認してください。
| 整理する情報 | 内容 | 理由 |
|---|---|---|
| EA名・バージョン | 対象ファイル名、更新日、バージョン。 | 確認対象を特定するため。 |
| インジケーター名 | iMA、iATR、iRSI、iCustom対象。 | 取得対象を明確にするため。 |
| setファイル | input設定一式。 | 同じ条件を再現するため。 |
| 銘柄・時間足 | _Symbol、参照時間足、MTF。 | ハンドル作成条件に関係するため。 |
| バッファ番号 | CopyBufferで指定する番号。 | 取得する値を特定するため。 |
| shift | 現在足か確定足か。 | 判定再現性に関係するため。 |
| ログ | HANDLE、BarsCalculated、CopyBuffer。 | 取得失敗の確認に必要です。 |
| 送らない情報 | 口座番号、パスワード、APIキー、Webhook URL。 | 機密情報を保護するため。 |
CopyBuffer実務チェック表
- OnInitでインジケーターハンドルを作成している
- INVALID_HANDLEを確認している
- ハンドル作成失敗時にログを出している
- BarsCalculatedで計算済み本数を確認している
- CopyBufferの戻り値を確認している
- 要求本数に満たない場合は判定へ進んでいない
- shift=0とshift=1の意味を明確にしている
- 現在足と確定足を混同していない
- ArraySetAsSeriesの配列方向を確認している
- EMPTY_VALUEとCopyBuffer失敗を分けている
- iCustom利用時にバッファ番号を確認している
- OnDeinitでIndicatorReleaseを行っている
- バックテスト条件とsetファイルを保存している
- 口座情報や認証情報をログへ出していない
よくある失敗パターン
| 失敗パターン | 起きる問題 | 確認すること |
|---|---|---|
| インジ名を推測で指定する | 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の責務を分けるため |
実務チェックリスト
- 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を分けた
よくある質問
チャートに矢印が出ていればEAからも同じように読めますか?
必ずしもそうではありません。矢印がbufferに出ているのか、Objectとして描画されているのか、アラートだけなのかを確認する必要があります。
CopyBufferで値が取れない場合はサインなしですか?
違います。CopyBuffer失敗とサインなしは別です。取得失敗はCOPYBUFFER_FAIL、サインなしはNO_SIGNALとして分けるべきです。
shift=0とshift=1はどちらを使うべきですか?
仕様によります。早く反応したいならshift=0を使う場合がありますが、値が変わる可能性があります。確定足で安定して判定したい場合はshift=1を使う方針を検討します。
リペイントするインジケーターはEA化できませんか?
EA化自体は可能な場合があります。ただし、過去サインが変わる前提で、どのタイミングの値を使うか、検証とリアルの差をどう扱うかを明確にする必要があります。
インジケーターinputはEA側でも同じにする必要がありますか?
基本的には同じ条件で確認する必要があります。チャート表示時とEA呼び出し時でinputが違うと、サイン位置や値が変わる可能性があります。
サインが出たらすぐOrderSendしてよいですか?
避けた方が安全です。サインはEntry候補の一部です。GATE、risk、duplicate control、execution precheckを通してから実行する設計が必要です。
バックテストで良ければリアルでも同じになりますか?
必ずしも同じにはなりません。未確定足、リペイント、tick model、履歴計算、リアルタイム更新の違いにより差が出る場合があります。
実装コードがなくてもインジケーターEA化の相談はできますか?
可能です。インジケーター名、input設定、サイン表示、buffer番号、shift方針、リペイント有無、ログ確認項目が整理されていれば、相談を進めやすくなります。
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失敗とサインなしは分けて扱ってください。
MQL5でiMAを呼べば移動平均線の値が直接返りますか?
MQL5では、iMAは基本的にインジケーターハンドルを返します。移動平均線の値を取得するには、そのハンドルを使ってCopyBufferでバッファ値を取得します。
CopyBufferの戻り値は何を意味しますか?
CopyBufferの戻り値は、実際にコピーできた要素数です。要求本数と一致していない場合は、取得不足として扱い、ログを出して判定を止める方が安全です。
shift=0とshift=1はどちらを使えばよいですか?
shift=0は現在足、shift=1は1本前の確定足として扱うことが多いです。現在足は値が変化するため、再現性を重視する場合は確定足を使う設計が扱いやすいです。
BarsCalculatedは必ず確認すべきですか?
確認した方が安全です。ハンドルが有効でも、インジケーターの計算本数が不足していると、CopyBufferで必要本数を取得できないことがあります。
iCustomで値が取れない時は何を確認しますか?
インジケーター名、ファイルパス、input引数、バッファ番号、EMPTY_VALUE、ハンドル作成結果、BarsCalculated、CopyBufferの戻り値を確認します。カスタムインジケーター側の仕様確認も必要です。
CopyBufferで0.0が返った場合は取得成功ですか?
0.0が有効な値である場合もありますが、取得失敗や初期値の可能性もあります。CopyBufferの戻り値、GetLastError、BarsCalculated、EMPTY_VALUEを確認し、値の意味を分けて判断してください。
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の仕様によります。安定した判定を優先するなら確定足、リアルタイム反応を優先するなら現在足を使う場合があります。ただし、現在足を使う場合は値の変動、リペイント、同一バー重複発注防止を設計してください。
関連する確認ページ
確認したい内容に応じて、次のページを参照してください。本文中ですでに案内したページは、ここでは重ねて掲載していません。
あわせて確認したいページ
今回の確認内容に関連する補足ページです。必要な項目だけ確認してください。
| 確認目的 | 関連ページ |
|---|---|
| 「MQL5 iCustom インジケーター EA化」の読後に「サインインジケータ作成を依頼する前に整理すること」を補足確認 | サインインジケータ作成を依頼する前に整理すること |
| 「MQL5 iCustom インジケーター EA化」の読後に「MQL5 CopyBufferとインジケーターハンドル管理を実コードで解説」を補足確認 | MQL5 CopyBufferとインジケーターハンドル管理を実コードで解説 |
| 「MQL5 iCustom インジケーター EA化」の読後に「押し目・戻り・トレンドとは」を補足確認 | 押し目・戻り・トレンドとは |
| 「MQL5 iCustom インジケーター EA化」の読後に「サポートライン・レジスタンスラインとは」を補足確認 | サポートライン・レジスタンスラインとは |
まとめ
MQL5でインジケーターサインをEA化する時は、チャート上の見た目だけで判断せず、EAが取得できる値、buffer番号、shift、確定足、リペイントの有無を整理することが重要です。
特に、次の4点を分けると、サイン不一致、早すぎるEntry、バックテストとリアルの差、CopyBuffer失敗の原因を確認やすくなります。
- indicator handle:iCustomなどでインジケーターを正しく呼び出す
- buffer read:CopyBufferでどのbuffer・shiftの値を取得したか確認する
- Signal Snapshot:取得値を判定用に固定し、reason付きで残す
- confirmed bar / repaint check:確定足で使うか、未確定足やリペイントを許容するか決める
また、CopyBuffer失敗とNO_SIGNALを混同しないこと、サイン取得とOrderSendを直接つなげないことも重要です。
完成EAの中核Entry条件や独自インジケーターの内部ロジックを公開しなくても、iCustom、CopyBuffer、buffer番号、shift、確定足、リペイント確認の考え方を理解しておくことで、開発依頼、既存インジケーターのEA化、検証、不具合調査、販売後サポートの品質を上げやすくなります。
利用条件・相談窓口
| 確認内容 | 案内ページ |
|---|---|
| MT4/MT5 技術講座|EA・インジケーター・MQL開発を学ぶ | 技術講座ハブ |
| 不具合報告の方法 | 不具合報告・調査依頼について |
| 免責事項・リスク説明 | 免責事項・リスク説明 |
お問い合わせはEAFC公式LINEへ
EAFCサイト内の記事、商品、導入前確認、設定確認、ログ確認、開発依頼前のご相談など、各種お問い合わせはEAFC公式LINEより受け付けています。
※投資助言、売買指示、利益保証、推奨ロット、推奨銘柄の案内は行っておりません。
口座番号、認証情報、Webhook URL、GAS URL、APIキーなどの機密情報は送信しないでください。
