15.シンプルなEAを作成してみる
今回は本カテゴリのこれまでの内容を総動員して、MT4用のシンプルなEA作成を行います。
ざっくりと実際のEA作成作業の流れを掴んでもらえればと思います。
今回作成するEAのロジックは次の通りです。
- 稼働させる通貨ペア・タイムフレームは不問とする
- 200SMAが上昇しているか下降しているか調べる
– 200SMAの現在値と1つ前のバー時点での値を比較し、値が大きくなっていれば上昇、逆なら下降とする(完全に同値だった場合、これ以降の処理は行わない) - 20SMAが200SMAの上下どちらに位置しているか調べる
- 200SMAが上向きかつ20SMAが200SMAより上に位置する時、バーが20SMAを下から上に抜けたらロングエントリーする
逆の場合にショートエントリーする - 期間20のボリンジャーバンド±2σラインを使い、エントリーと逆方向のラインにタッチしたら決済する
まとめると、200SMAと20SMAで値動きの方向を確認して順張り、強い逆方向の動き(±2σタッチ)を確認したら決済するロジックでしょうか。
まず、初めてのEAを作成してみると同様に、MetaEditorでEAを新規作成します。
ファイル名は「simple」にしておきます。
新規作成されたファイルから、邪魔なのでコメントを省いて、以下の状態にします。
#property copyright "" #property link "" #property version "1.00" #property strict int OnInit() { return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { } void OnTick() { }
コードは全てOnTick内に記述していきます。
200SMAを2期間分取得し、上昇しているか下降しているかの条件式をif文で書きます。
void OnTick() { double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る } else { //200SMAが水平移動しているなら、ここに処理が入る } }
20SMAの値も取得します。
200SMAのif文の中に、20SMAの位置関係を調べるif文も追加します。
void OnTick() { double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る } } else { //200SMAが水平移動しているなら、ここに処理が入る } }
次にバーが20SMAを上抜け/下抜けしたことを判定するif文を書きます。
20SMAに対して、始値が下/終値が上になっていたら上抜け、逆なら下抜けとします。
確定足でのみ判定が可能になるため、現在のバーではなく1本前のバーと1本前の時点でのSMAの値で判定します。
よって、1本前の20SMAも取得しておきます。
void OnTick() { double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA double sma20_prev = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る if(Open[1] < sma20_prev && Close[1] >= sma20_prev) { //バーが20SMAを上抜けたら、ここに処理が入る } } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る if(Open[1] > sma20_prev && Close[1] <= sma20_prev) { //バーが20SMAを下抜けたら、ここに処理が入る } } } else { //200SMAが水平移動しているなら、ここに処理が入る } }
次に注文処理を書きます。
上抜け時はロングエントリー、下抜け時はショートエントリーを成行注文で行います。
上抜け/下抜けは、1本前のバーで判定しているため、最初に判定が下されるタイミングは、新しいバーが出現した直後になります。
(エントリー実行は、バーの始値で行われると想定できます。)
ロットは0.1ロットとし、ストップロス/テイクプロフィットは設定しません。
また、注文が成功したかどうかの判定は特に行いません。
void OnTick() { double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA double sma20_prev = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る if(Open[1] < sma20_prev && Close[1] >= sma20_prev) { //バーが20SMAを上抜けたら、ここに処理が入る OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, NULL, 12345); //買い注文・マジックナンバー12345 } } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る if(Open[1] > sma20_prev && Close[1] <= sma20_prev) { //バーが20SMAを下抜けたら、ここに処理が入る OrderSend(Symbol(), OP_SELL, 0.1, Bid, 3, 0, 0, NULL, 12345); //売り注文・マジックナンバー12345 } } } else { //200SMAが水平移動しているなら、ここに処理が入る } }
このままだと、1本前のバーが上抜け/下抜けを満たしている状態でティックが発生しつづける限り(&証拠金が許す限り)何度も続けてエントリーを行ってしまうため、処理全体を「保有しているポジションが無い時に限り実行する」条件でかこみます。
OrderSendの実行直前に条件式を入れても良いですが、ポジションを持っている時にはSMAの取得や判定処理は全て不要になるので、一番最初に判定させます。
void OnTick() { if(OrdersTotal() == 0) { //ポジションを持っていない時は、ここに処理が入る double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA double sma20_prev = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る if(Open[1] < sma20_prev && Close[1] >= sma20_prev) { //バーが20SMAを上抜けたら、ここに処理が入る OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, NULL, 12345); //買い注文・マジックナンバー12345 } } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る if(Open[1] > sma20_prev && Close[1] <= sma20_prev) { //バーが20SMAを下抜けたら、ここに処理が入る OrderSend(Symbol(), OP_SELL, 0.1, Bid, 3, 0, 0, NULL, 12345); //売り注文・マジックナンバー12345 } } } else { //200SMAが水平移動しているなら、ここに処理が入る } } }
決済後はポジション保有数が0になるため、エントリーできることになります。
仮にエントリーしたバーですぐ決済されてしまった場合、1本目のバーはエントリー条件を満たしたままなので、再度エントリーしてしまうことになります。
これを避けるため、OrderSend直前のif文に、「現在レートと20SMAの位置関係」の条件を追加します。
void OnTick() { if(OrdersTotal() == 0) { //ポジションを持っていない時は、ここに処理が入る double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA double sma20_prev = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る if(Open[1] < sma20_prev && Close[1] >= sma20_prev && Bid > sma20) { //バーが20SMAを上抜けたら、ここに処理が入る OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, NULL, 12345); //買い注文・マジックナンバー12345 } } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る if(Open[1] > sma20_prev && Close[1] <= sma20_prev && Bid < sma20) { //バーが20SMAを下抜けたら、ここに処理が入る OrderSend(Symbol(), OP_SELL, 0.1, Bid, 3, 0, 0, NULL, 12345); //売り注文・マジックナンバー12345 } } } else { //200SMAが水平移動しているなら、ここに処理が入る } } }
逆に、ポジションを保有している時は、ボリンジャーバンド±2σタッチで決済を行いますので、今追加したif文にelseを追加して、ボリンジャーバンドの上下のラインの値を取得します。
void OnTick() { if(OrdersTotal() == 0) { //ポジションを持っていない時は、ここに処理が入る double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA double sma20_prev = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る if(Open[1] < sma20_prev && Close[1] >= sma20_prev && Bid > sma20) { //バーが20SMAを上抜けたら、ここに処理が入る OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, NULL, 12345); //買い注文・マジックナンバー12345 } } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る if(Open[1] > sma20_prev && Close[1] <= sma20_prev && Bid < sma20) { //バーが20SMAを下抜けたら、ここに処理が入る OrderSend(Symbol(), OP_SELL, 0.1, Bid, 3, 0, 0, NULL, 12345); //売り注文・マジックナンバー12345 } } } else { //200SMAが水平移動しているなら、ここに処理が入る } } else { //ポジションを持っている時は、ここに処理が入る double bb_upper = iBands(Symbol(), Period(), 20, 2.0, 0, PRICE_CLOSE, MODE_UPPER, 0); //ボリンジャーバンド+2σ double bb_lower = iBands(Symbol(), Period(), 20, 2.0, 0, PRICE_CLOSE, MODE_LOWER, 0); //ボリンジャーバンド-2σ } }
注文番号を保持していないので、注文プールの中をループさせて、このEAによるポジションがあるかどうか判断させます。
この判断は、マジックナンバーの照合のみで行わせます。
void OnTick() { if(OrdersTotal() == 0) { //ポジションを持っていない時は、ここに処理が入る double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA double sma20_prev = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る if(Open[1] < sma20_prev && Close[1] >= sma20_prev && Bid > sma20) { //バーが20SMAを上抜けたら、ここに処理が入る OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, NULL, 12345); //買い注文・マジックナンバー12345 } } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る if(Open[1] > sma20_prev && Close[1] <= sma20_prev && Bid < sma20) { //バーが20SMAを下抜けたら、ここに処理が入る OrderSend(Symbol(), OP_SELL, 0.1, Bid, 3, 0, 0, NULL, 12345); //売り注文・マジックナンバー12345 } } } else { //200SMAが水平移動しているなら、ここに処理が入る } } else { //ポジションを持っている時は、ここに処理が入る double bb_upper = iBands(Symbol(), Period(), 20, 2.0, 0, PRICE_CLOSE, MODE_UPPER, 0); //ボリンジャーバンド+2σ double bb_lower = iBands(Symbol(), Period(), 20, 2.0, 0, PRICE_CLOSE, MODE_LOWER, 0); //ボリンジャーバンド-2σ for(int i=OrdersTotal()-1; i>=0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == 12345) { //このEAによるポジションがあればここに処理が入る } } } } }
ロングポジションの場合はレートが-2σタッチ、ショートポジションの場合はレートが+2σタッチ、という条件で決済を行います。
決済が成功したかどうかの判定は特に行いません。
void OnTick() { if(OrdersTotal() == 0) { //ポジションを持っていない時は、ここに処理が入る double sma200 = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での200SMA double sma200_prev = iMA(Symbol(), Period(), 200, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での200SMA double sma20 = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); //現在時点での20SMA double sma20_prev = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); //1本前のバー時点での20SMA if(sma200 > sma200_prev) { //200SMAが上昇なら、ここに処理が入る if(sma20 > sma200) { //20SMAが200SMAより上に位置するなら、ここに処理が入る if(Open[1] < sma20_prev && Close[1] >= sma20_prev && Bid > sma20) { //バーが20SMAを上抜けたら、ここに処理が入る OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, NULL, 12345); //買い注文・マジックナンバー12345 } } } else if(sma200 < sma200_prev) { //200SMAが下降なら、ここに処理が入る if(sma20 < sma200) { //20SMAが200SMAより下に位置するなら、ここに処理が入る if(Open[1] > sma20_prev && Close[1] <= sma20_prev && Bid < sma20) { //バーが20SMAを下抜けたら、ここに処理が入る OrderSend(Symbol(), OP_SELL, 0.1, Bid, 3, 0, 0, NULL, 12345); //売り注文・マジックナンバー12345 } } } else { //200SMAが水平移動しているなら、ここに処理が入る } } else { //ポジションを持っている時は、ここに処理が入る double bb_upper = iBands(Symbol(), Period(), 20, 2.0, 0, PRICE_CLOSE, MODE_UPPER, 0); //ボリンジャーバンド+2σ double bb_lower = iBands(Symbol(), Period(), 20, 2.0, 0, PRICE_CLOSE, MODE_LOWER, 0); //ボリンジャーバンド-2σ for(int i=OrdersTotal()-1; i>=0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == 12345) { //このEAによるポジションがあればここに処理が入る if(OrderType() == OP_BUY && Bid <= bb_lower) { //ロングポジションは-2σタッチで決済 OrderClose(OrderTicket(), OrderLots(), Bid, 3); } else if(OrderType() == OP_SELL && Bid >= bb_upper) { //ショートポジションは+2σタッチで決済 OrderClose(OrderTicket(), OrderLots(), Ask, 3); } } } } } }
ここまで書いたら、コンパイルを行います。
タイプミスが無ければコンパイルは成功するはずです。
次回は、今回作成したEAをテストしてみます。