自動売買04

ここでは、MetaTrader4のEAの作り方について説明します。

もし「自作EAを作ってみたい!」とか「EA作成方法を知っておきたい!」と思っていたら、ぜひこの記事を参考にしてみてください。

EAのバックテスト方法について確認したい方は、前回の記事を参考にしてみてください。

前回記事【MetaTrader4のテスター機能でEAの良し悪しを判断する】へ

自作EA新規作成から完成までの流れ

この記事では、次の手順でEAを作成していきます。

  1. ステップ1:取引ルールとEA名を決める
  2. ステップ2:メタエディタで新規EAファイルを生成
  3. ステップ3:メタエディタでコードを入力していく
  4. ステップ4:テスターでEAの動作チェック

取引ルールとEA名を決める

EA作成に慣れないうちは、すごく簡単な取引ルールにしてEAを作る練習をしましょう。
この記事では、次の取引ルールのEAを作成しながら説明をしていきます。

  1. ポジション保有最大数は1つ
  2. 移動平均線を終値が上回ったら次のローソク足で買いエントリー
  3. 移動平均線を終値が下回ったら次のローソク足で買いポジション決済
  4. 売りはその逆

EA名は自分で分かりやすい名前を付けると良いです。日本語文字に一部対応していない場合がありますので、半角のアルファベット・記号・数字を使うようにします。

今回はEA名を【MA_Doten_EAv1.00】にしたいと思います。

メタエディタで新規EAファイルを生成

EAやインジケーターをプログラミングするエディタをメタエディタといいます。

メタエディタを開くには、MT4画面のメニューの「ツール」→「メタエディタ」をクリックします。メタクォーツ言語エディタボタンをクリックでも開くことができます。

メタエディタ画面の左上の新規作成ボタンを押すと、MQLウィザードが開きます。
最初の画面で「エキスパートアドバイザ(テンプレート)」にチェックを入れて、次へボタンをクリックします。
名前のボックスの「Experts\」の右側に、EA名を入力します。その後は完了ボタンが出るまで次へボタンをクリックして完了ボタンをクリックします。

メタエディタのタイトルバーのタイトルが「MetaEditor – [EA名.mq4]」となり、次のコードが表示されます。

//+------------------------------------------------------------------+
//|                                             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
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

にゃんたにゃんた

なんじゃこりゃー

Dr.EADr.EA

安心しなさい。一つ一つ説明するぞい

EAテンプレートのコード解説

コード入力の前に、表示されているEAのテンプレートのコードを順に見ていきます。

左に // (スラッシュ2つ)がある行はコメントです。コードを見やすくしたりコードの説明をする場合に使用します。コメントはEAの動作に影響しません。

#property で始まる行は、プログラムの特性などを入力します。EA作成の練習段階では、この部分は特に気にしなくても良いです。

下記のコード部分は、OnInit()関数といい、EA稼働開始時に実行される関数です。

int OnInit()
  {
   // ここにEA開始時の処理を入力
   return(INIT_SUCCEEDED);
  }

つぎのコード部分は、OnDeinit()関数といい、EA稼働終了時に実行される関数です。

void OnDeinit(const int reason)
  {
   // ここにEA終了時の処理を入力
  }

最後の関数が、OnTick()関数といい、ティック(値動き)毎に実行される関数です。

void OnTick()
  {
   // ここにティック毎の処理を入力
  }

MQLウィザードで作成したEAのテンプレートは、コメント・#property・OnInit()関数・OnDeinit()関数・OnTick()関数で構成されていることがわかります。
※ちなみに旧MQL4では、OnInit()関数・OnDeinit()関数・OnTick()関数は、それぞれinit()関数・deinit()関数・start()関数でした。

メタエディタでコードを入力

Dr.EADr.EA

それでは、コードを入力していくぞい!

にゃんたにゃんた

はい!(ワクワク♪)

Dr.EADr.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.");
      }
   }
  }
//+------------------------------------------------------------------+
にゃんたにゃんた

はぅ。。

EAのパラメーターを設置

EAのパラメーターを設置するには、OnInit()関数の上に次の形式でコードを追加します。

extern データ型 パラメーター名 = 初期値;

データ型には、「int」整数型、「double」実数型、「bool」真偽型、「string」文字列型などがあります。

ここでは、取引数量を設定する実数型のLotsと、マジックナンバーを設定する整数型のMagicNumberをパラメーターとして設置するため、次のコードを追加します。

extern double  Lots        = 0.1;
extern int     MagicNumber = 20191030;

この段階でコンパイルボタンをクリックして、MT4画面のテスターでエキスパート設定のパラメーターの入力タブを見ると、パラメーターの項目にLotsとMagicNumberが反映されていることがわかります。

グローバル変数を宣言

パラメーターのコードの下で、グローバル変数を宣言します。変数の宣言方法は次の通りです。

データ型 変数名 = 初期値;

どの関数内でもないこの位置で宣言する変数は、どの関数からも参照できて格納された値を記憶する特別な変数「グローバル変数」となります。

今回は、エントリーした時にチャート全体のローソク足の数を記憶させておく変数として次のように宣言します。

int entry_bar; // エントリーした時のバーの数

初期値が何であってもかまわない場合は初期値設定を省略することもできます。

OnTick()関数内の構成

OnTick()関数内の処理を次のように4つのセクションに分けた基本的な構成にします。

  1. 保有ポジションを確認
  2. 取引シグナルを判定
  3. 決済処理
  4. エントリー処理

ドテン取引を1ティック内で実行できるように、「決済処理」→「エントリー処理」の順にしています。

ローカル変数を宣言

ローカル変数とは、関数などのブロック内でのみ使用される変数のことで、そのブロックの処理が終われば格納されている値もリセットされます。
OnTick()関数内で初期値を気にせず複数回使いそうな変数は、関数内の最初に宣言しておくと楽です。

今回は、次のように整数変数「i」と「ticket」をカンマ区切りで宣言します。「i」はfor()文などの繰り返し分でよく使い、「ticket」は新規注文の注文番号を受け取る際によく使います。

   int i, ticket;

保有ポジションを確認

1つ目のセクションでは、保有ポジションの有無やその情報を確認します。
EA稼働口座内で保有中のポジションや予約注文を一つ一つ見ていき、このEAでのポジションがある場合、そのポジションの情報を変数に格納しておきます。

今回欲しいポジション情報は、注文番号とロット数です。(なぜこの2つの情報が欲しいかは後編で説明します)

ポジション情報を格納する変数として、次のように宣言します。

   int ticket_buy = 0;  // 買いポジションの注文番号
   double lots_buy = 0; // 買いポジションのロット数
   int ticket_sell = 0; // 売りポジションの注文番号
   double lots_sell = 0;// 売りポジションのロット数

さらに次のコードで、このEAでのポジション情報を取得します。

   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();
      }
   }

このコードに関しては、繰り返しのfor()文とOrderSelect()関数について理解するまでは、丸暗記でかまいません。

コード内のforを選択してキーボードのF1キーを押すとfor()文の解説が見られます。
コード内のOrderSelectを選択してキーボードのF1キーを押すとOrderSelect()関数の解説が見られます。
MQL4リファレンスを上手に活用してMQL4を覚えていくのもEA作成スキル上達にとって大切なことです。


続きは、後編で解説します。お楽しみに!