15.シンプルなEAを作成してみる

今回は本カテゴリのこれまでの内容を総動員して、MT4用のシンプルなEA作成を行います。
ざっくりと実際のEA作成作業の流れを掴んでもらえればと思います。

今回作成するEAのロジックは次の通りです。

  1. 稼働させる通貨ペア・タイムフレームは不問とする
  2. 200SMAが上昇しているか下降しているか調べる
    – 200SMAの現在値と1つ前のバー時点での値を比較し、値が大きくなっていれば上昇、逆なら下降とする(完全に同値だった場合、これ以降の処理は行わない)
  3. 20SMAが200SMAの上下どちらに位置しているか調べる
  4. 200SMAが上向きかつ20SMAが200SMAより上に位置する時、バーが20SMAを下から上に抜けたらロングエントリーする
    逆の場合にショートエントリーする
  5. 期間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をテストしてみます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

コメントする