ネットで見つけたカスタムインジケーター連携EAのサンプルコードを、
そのままMetaEditorに貼り付けてコンパイルしたら、構文エラーで先に進めなかった、
あるいはコンパイルは通ったのにポジションが増え続けた——
iCustom関数でカスタムインジケーターをEA化するときによくあるつまずきです。
iCustom関数を使えば、自分で作ったカスタムインジケーターのシグナルを、
そのままEAのエントリー条件として使えます。
この記事では、ゴールデンクロス・デッドクロスのシグナルを表示する、
カスタムインジケーター「GCDC-SIGNAL」を例に、
パラメーターの設定からOnTick()関数の中身、
そして見落としがちな「ポジションの重複防止」と「損切り設定」まで、
実際にMetaEditorでコンパイルしてMT4で動かせる状態のコードを解説します。
iCustom関数を活用してカスタムインジケーターをEA化する
iCustom関数を活用してカスタムインジケーターをEA化させていきましょう。
iCustom関数とは指定したカスタムインジケーターの値を返す関数です。

iCustom関数を活用して、以前紹介したゴールデンクロス・デッドクロスの、
インジケーターを自動売買させましょう。

このEAは「GCDC-SIGNAL」という名前のカスタムインジケーターが、
MT4の「MQL4/Indicators」フォルダに入っていることが前提です。
上のリンク先の記事でこのインジケーターを先に作成・コンパイルしておいてください。
メタエディタ(MetaEditor)を立ち上げる
MT4を起動し、
ツールバーの黄色いボタン、
(またはツール→MetaQuotes Language Editor)から、
メタエディタ(MetaEditor)を立ち上げましょう。
メタエディタの「ファイル」→「新規作成」を選び、
ウィザードで「Expert Advisor(テンプレートを使用)」を選択して、
「次へ」を押します。
名前を入力する画面が出るので、今回は「iCustom-EA」と入力します。
あとはイベントハンドラのチェックを何も入れず「次へ」を数回押して「完了」まで進めると、
OnInit()・OnDeinit()・OnTick()の3つの関数だけが、
書かれた空のひな形が開きます。
パラメーターを記述する
パラメーターを記述しましょう。
今回は利確・損切り・ロット数・マジックナンバー・スリッページの5つを、
あとから値を変更しやすいようにinput変数として用意します。
input int TakeProfit = 5;//利確(pips) input int StopLoss = 10;//損切り(pips) input double Lots = 0.01;//ロット数 input int MagicNumber = 12345;//マジックナンバー input int Slippage = 3;//スリッページ
損切り(StopLoss)とスリッページ(Slippage)は、
後述する理由でこの記事から新しく追加したパラメーターです。
OnTick()関数を記述する
OnTick()関数を記述しましょう。
まず最初に、iCustom関数を活用してカスタムインジケーターの、
矢印の配列を変数に代入します。
double UPArrow = iCustom(NULL,0,"GCDC-SIGNAL",2,1); double DWNArrow = iCustom(NULL,0,"GCDC-SIGNAL",3,1);
iCustom関数の第4引数(2または3)が読み取るバッファ番号、
最後の引数(1)が「1つ前の確定足」を指すシフトです。
GCDC-SIGNALインジケーター側で、上矢印をバッファ2、下矢印をバッファ3に、
描画している前提のため、この番号は使うインジケーターの実装に合わせてください。
バッファ番号は、参照するインジケーター自身のコード内で、
`SetIndexBuffer()`が呼ばれている順番、
(0番目に呼んだものがバッファ0、次がバッファ1…)で確認できます。
そしてUPArrowがEMPTY_VALUEではなく、
DWNArrowがEMPTY_VALUEの時にロングのポジションを持つようにします。
if(UPArrow != EMPTY_VALUE && DWNArrow == EMPTY_VALUE)
OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask-StopLoss*Point*10,Ask+TakeProfit*Point*10,"Order Long",MagicNumber,0,Red);
今度は逆で、UPArrowがEMPTY_VALUEで、
DWNArrowがEMPTY_VALUEではない時に、
ショートのポジションを持つようにします。
if(UPArrow == EMPTY_VALUE && DWNArrow != EMPTY_VALUE)
OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid+StopLoss*Point*10,Bid-TakeProfit*Point*10,"Order Short",MagicNumber,0,Blue);
これでエントリーのロジック自体はできました。
見落としがちな2つの問題を直す
このまま動かすと2つの問題が残っているので、順番に直していきます。
ポジションを重複して持たないようにする
シグナルが出ているローソク足が確定するまでの間、
そのローソク足の中で価格が更新されるたびに(つまり数十〜数百ティックの間)、
上のコードは新規注文を送り続けてしまいます。
1本のローソク足の間に大量のポジションが積み上がる結果になるため、
既にポジションを持っているかどうかを判定してから注文する処理を追加します。
bool HasPosition()
{
for(int i=OrdersTotal()-1; i>=0; i--)
{
if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
return(true);
}
return(false);
}
OnTick()の先頭に以下の1行を追加するだけで、
ポジションを持っている間は新規エントリーの判定自体をスキップできます。
if(HasPosition()) return;//既にポジションがあれば新規エントリーしない
損切り(ストップロス)を設定する
元のOrderSend()は、6番目の引数(損切り価格)が0になっていました。
0を指定すると損切りは設定されず、
シグナルと逆方向に相場が動いても決済されないまま含み損が膨らみ続けます。
利確だけでなく損切りも必ず設定しましょう。
パラメーターに追加したStopLossを使って、
エントリー方向と逆側に損切りラインを計算します、
(買いはAsk-StopLoss*Point*10、
売りはBid+StopLoss*Point*10)。
※Pointは通貨ペアの最小価格単位です。
多くのFX業者が採用している5桁表示のブローカーでは1pip=10Pointになるため、
pips単位で指定したTakeProfit・StopLossに10を掛けています。
4桁表示のブローカーを使う場合は、この「×10」を外してください。
エラーコード130(Invalid stops)が出た場合は、
StopLoss・TakeProfitの値を大きくしてみてください、
(ブローカーごとの最小ストップ距離未満の値を指定すると発生します)。
コンパイル
コードを保存したら、メタエディタの「コンパイル」(Ctrl+F7)で、
「0 error(s), 0 warning(s)」になることを確認しましょう。
エラーが出た場合は、括弧の対応やセミコロンの付け忘れがないか、
コード全体と見比べてください。
iCustom-EAのソースコード全体
ここまでの修正をすべて反映した、
実際にMetaEditorでコンパイルしてそのまま動かせる状態のコードです。
//+------------------------------------------------------------------+
//| iCustom-EA.mq4 |
//| Copyright 2020, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, FX-EA System Project Creator"
#property link "https://creator.fx-ea-system-project.com/"
#property version "1.01"
#property strict
/*パラメーター*/
input int TakeProfit = 5;//利確(pips)
input int StopLoss = 10;//損切り(pips)
input double Lots = 0.01;//ロット数
input int MagicNumber = 12345;//マジックナンバー
input int Slippage = 3;//スリッページ
//+------------------------------------------------------------------+
//| このEA(マジックナンバー)のポジションを保有しているか判定する |
//+------------------------------------------------------------------+
bool HasPosition()
{
for(int i=OrdersTotal()-1; i>=0; i--)
{
if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
//既にポジションを持っている場合は新規エントリーしない
if(HasPosition())
return;
double UPArrow = iCustom(NULL,0,"GCDC-SIGNAL",2,1);//上矢印をUPArrowに入れる
double DWNArrow = iCustom(NULL,0,"GCDC-SIGNAL",3,1);//下矢印をDWNArrowに入れる
int ticket;
if(UPArrow != EMPTY_VALUE && DWNArrow == EMPTY_VALUE)//UPArrowが空ではなくDWNArrowが空の場合
{
ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask-StopLoss*Point*10,Ask+TakeProfit*Point*10,"Order Long",MagicNumber,0,Red);//ロングエントリー
if(ticket < 0)
Print("買い注文に失敗しました。エラーコード=",GetLastError());
}
if(UPArrow == EMPTY_VALUE && DWNArrow != EMPTY_VALUE)//UPArrowが空でDWNArrowが空ではない場合
{
ticket = OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid+StopLoss*Point*10,Bid-TakeProfit*Point*10,"Order Short",MagicNumber,0,Blue);//ショートエントリー
if(ticket < 0)
Print("売り注文に失敗しました。エラーコード=",GetLastError());
}
}
//+------------------------------------------------------------------+
バックテストで動作を確認する
バックテストする前に、参照先の「GCDC-SIGNAL」インジケーターが、
MT4の「MQL4/Indicators」フォルダに入って、
コンパイル済みであることを確認してください。
入っていないと、iCustom関数が値を取得できずEAが正しく動作しません。
そのうえでMT4の「表示」→「ストラテジーテスター」を開き、
Expert Advisorに作成したiCustom-EAを選択、
通貨ペアと期間を指定して、
モデルを「全ティック(最も正確な値)」にしてスタートすると、
過去データでの動作を確認できます。
実運用する前に確認しておきたいこと
このEAは、カスタムインジケーターのシグナルをそのままエントリー条件にする、
学習用にシンプルへ作った例です。
実際の口座で稼働させる前に、次の点を確認してください。
- 参照するカスタムインジケーター(GCDC-SIGNAL)自体のロジックの精度が、このEA全体の成績を左右する
- ロット数(Lots)は口座残高に対して過大にならないよう調整する
- バックテストの結果は過去データに基づくものであり、将来の相場でも同じ成績になるとは限らない
まずはデモ口座、または少額のロットでの、
フォワードテスト(リアルタイムでの動作確認)を行ってから、
本番運用を検討してください。
あわせて読みたい: 【MQL】カスタムインジケーターを作成の流れについてわかりやすく説明してみた
【MQL】iCustom関数を活用してカスタムインジケーターをEA化する【中級編】:まとめ
iCustom関数を活用すれば、
どのようなカスタムインジケーターでも自動売買化させることができます。
エントリーのロジックだけでなく、ポジションの重複防止と損切り設定まで含めて初めて、
実運用を検討できる形になります。
ソースコードを応用して、ぜひiCustom関数をマスターしてみてください。
コメント