ここでは、前回までの記事で作成したMetaTrader4のEAをカスタマイズする方法を紹介します。
もし「前回までの記事、まだ見ていないよー」という方は【MetaTrader4のEAの作り方(前編)】から読み進めてください。
前々回の記事【MetaTrader4のEAの作り方(前編)】へ
前回までの記事で作成したEAのおさらい
前回までの記事で作成したEAがこちら
//+------------------------------------------------------------------+ //| MA_Doten_EAv1.00.mq4 | //| Dr.EA Keiji | //| https://www.dr-ea.com/meta-blog/ | //+------------------------------------------------------------------+ #property copyright "Dr.EA Keiji" #property link "https://www.dr-ea.com/meta-blog/" #property version "1.00" #property strict // パラメーター extern double Lots = 0.1; extern int MagicNumber = 20191030; // グローバル変数 int entry_bar; // エントリーした時のバーの数 //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- // 使い回ししそうな変数を宣言 int i, ticket; // 保有ポジションを確認 ------------------------------- int ticket_buy = 0; // 買いポジションの注文番号 double lots_buy = 0; // 買いポジションのロット数 int ticket_sell = 0; // 売りポジションの注文番号 double lots_sell = 0;// 売りポジションのロット数 for (i=0; i<OrdersTotal(); i++) { // ポジション選択 if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) return; // 通貨ペアまたはマジックナンバーが違う場合はスキップ if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue; // 保有ポジションの注文番号とロット数を変数に代入 if (OrderType() == OP_BUY) { ticket_buy = OrderTicket(); lots_buy = OrderLots(); } if (OrderType() == OP_SELL) { ticket_sell = OrderTicket(); lots_sell = OrderLots(); } } // 取引シグナルを判定 --------------------------------- int entry_sign = 0; // エントリー用シグナル int exit_sign = 0; // 決済用シグナル // MAの値を取得 double ma_1 = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 1); double ma_2 = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 2); // 条件を満たしたらシグナル発生 (1: 買いシグナル, -1: 売りシグナル) if (Close[2] <= ma_2 && Close[1] > ma_1) { entry_sign = 1; exit_sign = 1; } if (Close[2] >= ma_2 && Close[1] < ma_1) { entry_sign = -1; exit_sign = -1; } // 決済 ----------------------------------------------- // 買いポジション決済 if (ticket_buy > 0 && exit_sign == -1) { if (OrderClose(ticket_buy, lots_buy, Bid, 0, clrYellow) == true) ticket_buy = 0; else Print("OrderClose error."); } // 売りポジション決済 if (ticket_sell > 0 && exit_sign == 1) { if (OrderClose(ticket_sell, lots_sell, Ask, 0, clrYellow) == true) ticket_sell = 0; else Print("OrderClose error."); } // エントリー------------------------------------------- if (ticket_buy == 0 && ticket_sell == 0 && entry_bar != Bars) { // 買いエントリー if (entry_sign == 1) { ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, 0, 0, 0, NULL, MagicNumber, 0, clrBlue); if (ticket > 0) entry_bar = Bars; else Print("OrderSend error."); } // 売りエントリー if (entry_sign == -1) { ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, 0, 0, 0, NULL, MagicNumber, 0, clrRed); if (ticket > 0) entry_bar = Bars; else Print("OrderSend error."); } } } //+------------------------------------------------------------------+
これこれー♪
単純移動平均線(期間20)を終値が超えて終えたら、次のローソク足の始めでドテン取引するEAです。
今回のEAの改良ポイント
次の4つの改良を行います。
- SL幅とTP幅を設定するパラメーターを追加する
- エントリーフィルタを追加する
- MAによる決済をするか否かのパラメーターを追加する
- 移動平均のパラメーターを追加する
なんだか良くなりそうな気がするにゃ!
では早速。(カタカタ・・・)っほい!
//+------------------------------------------------------------------+ //| MA_Doten_EAv1.01.mq4 | //+------------------------------------------------------------------+ #property copyright "Dr.EA Keiji" #property link "https://www.dr-ea.com/meta-blog/" #property version "1.01" #property strict // パラメーター extern int StopLoss = 500; extern int TakeProfit = 500; extern double Lots = 0.1; extern int MagicNumber = 20191030; extern int MA_Period = 20; extern int MA_Shift = 0; extern ENUM_MA_METHOD MA_Method = MODE_SMA; extern ENUM_APPLIED_PRICE MA_Applied = PRICE_CLOSE; extern bool ExitByMA = false; // グローバル変数 int entry_bar; // エントリーした時のバーの数 //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // 使い回ししそうな変数を宣言 int i, ticket; double sl, tp; // 保有ポジションを確認 ------------------------------- int ticket_buy = 0; // 買いポジションの注文番号 double lots_buy = 0; // 買いポジションのロット数 int ticket_sell = 0; // 売りポジションの注文番号 double lots_sell = 0;// 売りポジションのロット数 for (i=0; i<OrdersTotal(); i++) { // ポジション選択 if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) return; // 通貨ペアまたはマジックナンバーが違う場合はスキップ if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue; // 保有ポジションの注文番号とロット数を変数に代入 if (OrderType() == OP_BUY) { ticket_buy = OrderTicket(); lots_buy = OrderLots(); } if (OrderType() == OP_SELL) { ticket_sell = OrderTicket(); lots_sell = OrderLots(); } } // 取引シグナルを判定 --------------------------------- int entry_sign = 0; // エントリー用シグナル int exit_sign = 0; // 決済用シグナル // MAの値を取得 double ma_1 = iMA(NULL, 0, MA_Period, MA_Shift, MA_Method, MA_Applied, 1); double ma_2 = iMA(NULL, 0, MA_Period, MA_Shift, MA_Method, MA_Applied, 2); // 条件を満たしたらシグナル発生 (1: 買いシグナル, -1: 売りシグナル) if (Close[2] <= ma_2 && Close[1] > ma_1) { if (ma_2 < ma_1) entry_sign = 1; // MAが上昇なら買いエントリーシグナル発生 if (ExitByMA == true) exit_sign = 1; // ExitByMAがtrueなら売りポジ決済シグナル発生 } if (Close[2] >= ma_2 && Close[1] < ma_1) { if (ma_2 > ma_1) entry_sign = -1; // MAが下降なら売りエントリーシグナル発生 if (ExitByMA == true) exit_sign = -1; // ExitByMAがtrueなら買いポジ決済シグナル発生 } // 決済 ----------------------------------------------- // 買いポジション決済 if (ticket_buy > 0 && exit_sign == -1) { if (OrderClose(ticket_buy, lots_buy, Bid, 0, clrYellow) == true) ticket_buy = 0; else Print("OrderClose error."); } // 売りポジション決済 if (ticket_sell > 0 && exit_sign == 1) { if (OrderClose(ticket_sell, lots_sell, Ask, 0, clrYellow) == true) ticket_sell = 0; else Print("OrderClose error."); } // エントリー------------------------------------------- if (ticket_buy == 0 && ticket_sell == 0 && entry_bar != Bars) { // 買いエントリー if (entry_sign == 1) { sl = 0; tp = 0; if (StopLoss > 0) sl = Ask - StopLoss * Point; if (TakeProfit > 0) tp = Ask + TakeProfit * Point; ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, 0, sl, tp, NULL, MagicNumber, 0, clrBlue); if(ticket > 0) entry_bar = Bars; else Print("OrderSend error."); } // 売りエントリー if (entry_sign == -1) { sl = 0; tp = 0; if (StopLoss > 0) sl = Bid + StopLoss * Point; if (TakeProfit > 0) tp = Bid - TakeProfit * Point; ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, 0, sl, tp, NULL, MagicNumber, 0, clrRed); if(ticket > 0) entry_bar = Bars; else Print("OrderSend error."); } } }
はぅぅぅ。。
SL幅とTP幅を設定するパラメーターを追加
SL幅とTP幅をポイント単位で設定するパラメーターStopLossとTakeProfitを次のように追加します。
extern int StopLoss = 500; extern int TakeProfit = 500;
OnTick()関数内で、実数変数slとtpを宣言しておきます。
double sl, tp;
買いエントリーのコードを次のように変更します。
sl = 0; tp = 0; if (StopLoss > 0) sl = Ask - StopLoss * Point; if (TakeProfit > 0) tp = Ask + TakeProfit * Point; ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, 0, sl, tp, NULL, MagicNumber, 0, clrBlue);
slとtpに0を代入しておいて、StopLossが0より大きい場合AskからStopLossポイント下の価格をslに代入。 TakeProfitが0より大きい場合AskからTakeProfitポイント上の価格をtpに代入。 slとtpをOrderSend()関数のSL/TP設定箇所に入力。
売りエントリーのコードも同じように変更します。
ここで、例えばStopLossが0より大きい場合ではない場合、sl は 0 のままで、SL設定無しということになります。
エントリーフィルタを追加する
MAが上昇時のみ買いエントリーシグナルを発生させて、MAが下降時のみ売りエントリーシグナルを発生させるようにします。
買いエントリーシグナル発生部分を次のようにします。
if (ma_2 < ma_1) entry_sign = 1; // MAが上昇なら買いエントリーシグナル発生
売りエントリーシグナル発生部分を次のようにします。
if (ma_2 > ma_1) entry_sign = -1; // MAが下降なら売りエントリーシグナル発生
MAによる決済をするか否かのパラメーターを追加する
次のように、MAによる決済をするか否かを真偽型パラメーター変数ExitByMAを追加します。
extern bool ExitByMA = false;
決済用シグナルを判定する部分で、次のようにExitByMAがtrueの場合のみ決済シグナルが発生するようにします。
売りポジション決済シグナル発生部分を次のようにします。
if (ExitByMA == true) exit_sign = 1; // ExitByMAがtrueなら売りポジ決済シグナル発生
買いポジション決済シグナル発生部分を次のようにします。
if (ExitByMA == true) exit_sign = -1; // ExitByMAがtrueなら買いポジ決済シグナル発生
移動平均のパラメーターを追加する
移動平均の期間、表示移動、MAの種類、適用価格をそれぞれ設定するパラメーターを次のように追加します。
extern int MA_Period = 20; extern int MA_Shift = 0; extern ENUM_MA_METHOD MA_Method = MODE_SMA; extern ENUM_APPLIED_PRICE MA_Applied = PRICE_CLOSE;
MAの種類のデータ型をENUM_MA_METHODにすることで、パラメーター入力画面で直接入力ではなく、リストから選択する形になります。適用価格のデータ型はENUM_APPLIED_PRICEとします。
iMA()関数の括弧内を次のように変更します。
double ma_1 = iMA(NULL, 0, MA_Period, MA_Shift, MA_Method, MA_Applied, 1); double ma_2 = iMA(NULL, 0, MA_Period, MA_Shift, MA_Method, MA_Applied, 2);
あとがき
EAのパラメーターを増やすことで、取引ロジック設定の幅が広がりました。今回の記事ではEA改良の第一歩という感じでしたが、ぜひご自身のアイデアを取り込んだEAにカスタマイズしてみてください。
次回は、EAのパラメーターを最適化する方法を紹介します。
初めまして
シストレを始めようと思い、ネット上で情報を検索してたら、このHPに辿り着きました。
簡単に自己紹介しますと、プログラム経験はありますが(一応、SEです)、MQLはまったくの素人です。
サンプルで、ポジション無しの場合、nowbar != Bars の比較を行ってますが、nowbarの値が確定するのはOrderSend()の後です。nowbarは初期化されてませんので、この比較の結果はどうなるのでしょうか?
宜しくお願いします。
こんにちわ^^
>MT4初心者さん
初期値がない整数変数は、0が割り当てられますので、
OrderSend()実行前のnowbar != Barsはtrueとなります。
グローバル変数を初期化する場合は、init()関数内で初期値として値を代入するようにしたほうがよいです。
(パラメータ変更や稼動時間足変更した際、init()関数は実行されますがグローバル変数は初期化されないため。)
慶事さん
ご回答ありがとうございました。初期値なし変数が自動で0に初期化されるなんて、MQLは便利ですね。
Start()について質問があります。前の章で、Start()は「そのチャートに動きがあったときに、読み取られるプログラムです」とありました。これは、Tick単位で呼び出されると理解してます。値動きが激しいときは、Start()は頻繁に呼び出されると思います。Tickの変化が発生してStart()が呼び出され、最初のStart()の処理が完了しないうちにTickが変化した場合は、2回目のStart()は呼び出されてしまうのでしょうか?
例:Tick変化 → 1回目のStart開始 → Tick変化 → 2回目のStart開始 → 2回目のStart終了 → 1回目のStartの残り部分が実行 となり、予期せぬ結果を招く恐れがあります。
それとも、1回目のStart()処理中に、Tickの変化があっも、1回目のStart()が完了するまでは、2回目のStart()は呼び出されない仕組み(排他制御)になってるのでしょうか?
宜しく御願いします。
こんにちわ^^
start()関数についてですが、後者のほうですね。
1回目のstart()関数処理中は次のティックがあっても無視されます^^;
いつもMqlいじる時は、参考にさせて頂いております。
ありがとうございます。
こんにちわ!
Ayaさん>
コメントありがとうございます^^
お役にたててよかったです^^
はじめまして
自分でEAを作成したくて調べていたらここをみつけました。
非常にわかりやすくてたすかりました。
質問なのですが、最初に買いで損切り、利益確定を10ピップでエントリーし、
その後、損切りされればすぐに反対の売りでエントリー
利益確定されれば、また買いでエントリーする
というのを延々と繰り返していきたいのですが、
どういうEAにしたらよいか教えていただけませんか
よろしくお願いします。
こんにちわ!
egamiさん>
TPやSLにせず、EA内部で決済するようにすると編集しやすいかと思います。
TP・SLで決済する場合は、ポジションの有無を調べ、保有ポジションがなければ、取引履歴から前回の決済がTPかSLを調べるといった手順がよいかと思います。TPかSLかはOrderComment()で判別可能です。
あと、保有中の損益を記録しておいて、決済前の損益がプラスならTP決済・マイナスならSL決済と判断してもよいかもしれません。
いろいろなサイトがありますが、貴サイトは分かりやすいです。
初めてなのにいきなり(駄)質問で申し訳ないのですが
とあるインジケータを改造してEAを作ったのですが、処理がint start()
に行く前に終了してしまいます。
MT4のバックテスターでテストしていますが、プログラムにミスがあるのか
テスターの使い方に問題があるのか分かりません。
お時間がありましたら、なにかしらご教示いただけたら嬉しいです。
よろしくお願いいたします。
先ほど質問したものですが、解決しましたのでご報告いたします。
単純移動平均線(期間20)を終値が超えて終えたら、次のローソク足の始めでドテン取引するEAこれは
損切やリカクの設定はできないですか
コメントありがとうございます。
前回記事のEAでは、まだ損切りや利確の設定はできません。
今回記事の「SL幅とTP幅を設定するパラメーターを追加する」改良で、損切りや利確の設定ができるようになります^^