【超入門】MQL5 EA講座 第92回「待機注文クラスを使ってEAを作る」

MQL5でEA作ろう講座

前回待機注文を削除するメンバ関数DeletePending関数」をOriginalCTradeクラスに追加する模様を解説しました。

これで前回第91回も含め、第86回第87回第88回第89回第90回、のの6回に分けて待機注文クラス関数を作ってきました。今回はこれらのクラス関数を利用して待機注文を使ったEA(自動売買プログラム)を作っていきたいと思います。

スポンサーリンク
スポンサーリンク
  1. 今回作るEAの仕様
  2. MQL5ウィザードを起動し、EAのひな型ファイルを作成する
  3. ステップ2:デモ口座でしか機能しない回路を盛り込む
  4. 各種インクルードファイルをメインプログラムで使う為に、include命令を記述する
  5. OriginalCTradeクラスとOriginalCpendingクラスのインスタンスを生成する。
  6. 必要なパラメーターをinput変数で用意する
    1. input変数「AddPoints」は約定予定価格の調整値を設定する
    2. input変数「TradeVolume」はロット数を設定する
    3. input変数「StopLoss」はストップロスを設定する
    4. input変数「TakeProfit」はテイクプロフィットを設定する
  7. グローバル変数の定義
  8. OnTick関数内の記述
    1. 価格や時間などに関するデータを取得する配列を用意する
      1. MqlRates構造体について
        1. メンバ「time」は レート情報を取得したオープン時間を格納する
        2. メンバ「open」は 始値を格納する
        3. メンバ「high」は 高値を格納する
        4. メンバ「low」は 安値を格納する
        5. メンバ「close」は 終値を格納する
        6. メンバ「tick_volume」は ティック数を格納する
        7. メンバ「spread」は スプレッド情報を格納する
          1. スプレッドとは→買値(Ask)と売値(Bid)の差のこと
        8. メンバ「real_volume」は 取引高を格納する
    2. ArraySetAsSeries関数を使い、配列を時系列にセットする
    3. CopyRates関数を使い、構造体配列に価格情報をコピー
      1. CopyRates関数について
        1. 第1引数には取引銘柄を指定する
        2. 第2引数には時間軸を指定する
        3. 第3引数には、コピー開始位置を指定する
        4. 第4引数には複製データ数を指定する
        5. 第5引数には複製データをコピーする配列を指定する
    4. 新しい足が現れた時にだけ読み込む回路を記述する
      1. 新しい足が現れた時にtrueとなるbool型の変数を宣言する
      2. glLastBarTime と rates[0].timeの時間データをif文で比較する
      3. if(glLastBarTime != rates[0].time)がtrueだった時の{}内の処理を記述する
    5. 新しい足が現れた最初に待機注文を出す記述をする
      1. オーダー番号を格納する配列の準備と今までにあった待機注文の削除
      2. OriginalCpendingクラスのGetOrderTickets関数を使って、オーダー番号を取得し、配列に格納
      3. ArraySize関数を使って待機注文数を取得し、変数に格納する
      4. 今までにあった待機注文を一旦削除する
        1. if(Pending.TotalPendingNum(_Symbol) > 0)は待機注文の有無を確認している。
        2. for文を使って、待機注文プールをチェックし、待機注文削除を実行する
    6. 買いの待機注文(逆指値)を出す
      1. 約定予定価格の設定
      2. ストップレベルの調整を行う
      3. ストップロス値の設定
      4. テイクプロフィット値の設定
      5. 買いの待機注文を出す
    7. 売りの待機注文(逆指値)を出す
      1. 約定予定価格の設定
      2. ストップレベルの調整を行う
      3. ストップロス値の設定
      4. テイクプロフィット値の設定
      5. 売りの待機注文を出す
  9. まとめ

今回作るEAの仕様

今回作るEA(自動売買プログラム)の仕様は以下の通りです。

【買い注文・売り注文における共通の前提】
注文は待機注文で行うものとする。
注文のタイミングはEAを設定するチャート時間足において、新しい足が現れた時に行う。
新しい足が現れた際に、過去の約定しなかった待機注文は削除する。
<買いの注文条件>
EAを設定するチャート時間足における一本前の高値に、指定の値をプラスした価格を約定予定価格として待機注文を出す。
上記の約定予定価格に合わせて、ストップロスとテイクプロフィットの値も決定する。

<売りの注文条件>
EAを設定するチャート時間足における一本前の安値に、指定の値をマイナスした価格を約定予定価格として待機注文を出す。
上記の約定予定価格に合わせて、ストップロスとテイクプロフィットの値も決定する。

では早速作っていきましょう。

MQL5ウィザードを起動し、EAのひな型ファイルを作成する

まずは、メタエディターの「新規作成」ボタンを押して、MQL5ウィザードを立ち上げ、「エキスパートアドバイザー(テンプレート)」を選択→EA(自動売買プログラム)の名前を決めます。

EA(自動売買プログラム)の名前は「Pending Order EA」とします。イベントハンドラーの箇所はチェックをつけずに完了させます。

メタエディターについては第2回「メタエディターの使い方」をご覧ください。

ステップ2:デモ口座でしか機能しない回路を盛り込む

第68回「簡単な仕組みのMT5用EAを作るーその1ー」

でも解説した記述ですが、デモ口座でしか動かないような回路をOnInit関数に組み込みます。

int OnInit()
  {

   ENUM_ACCOUNT_TRADE_MODE tradeMode=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
   if(tradeMode != ACCOUNT_TRADE_MODE_DEMO)                 // デモ口座以外の場合
     {
     
      MessageBox("このEAはデモ口座でのみ稼働します","口座エラー");
      return INIT_FAILED;                            // 処理終了
     }

   return(INIT_SUCCEEDED);
  }

AccountInfoInteger関数引数ACCOUNT_TRADE_MODEを記述すると、口座がリアル口座かデモ口座かを調べることができます。取得した値がデモ口座ではなかった場合、処理を終了させます。

AccountInfoInteger関数については↓の記事をご参照ください。

OnInit関数については↓の記事をご覧ください。

MessageBox関数については↓の記事をご覧ください。

各種インクルードファイルをメインプログラムで使う為に、include命令を記述する

今回はOriginalTrade.mqhファイルOriginalCpending.mqhファイルをメインプログラムで利用します。

OriginalTrade.mqhファイルには待機注文を出すための関数を多数追加してきたので、今回はそれを利用するために用います。

OriginalCpending.mqhファイルには待機注文の数や、オーダー番号などを取得する為の関数が入っています。EA(自動売買プログラム)を作るにあたり、それらを利用することになります。

MQL5ではオーダー番号ポジション番号約定番号(ディール番号)という概念が存在します。

それぞれの違いについては各用語のアンカーテキストリンクから該当の記事をご参照ください。

これらのインクルードファイルをメインプログラムで使う為には、include命令を記述する必要があります。

//+------------------------------------------------------------------+
//|                                             Pending Order EA.mq5 |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"
#property version   "1.00"

#include <OriginalTrade.mqh>
#include <OriginalCpending.mqh>
//+------------------------------------

これで、メインプログラムでOriginalTrade.mqhファイルOriginalCpending.mqhファイルに収められているクラス関数が使えるようになりました。

include命令については↓の記事をご覧ください。

第56回「#include命令(#include directive)」

OriginalCTradeクラスとOriginalCpendingクラスのインスタンスを生成する。

include命令により、OriginalTrade.mqhファイルOriginalCpending.mqhファイル内にあるクラス関数が使えるようになりました。

次はOriginalCTradeクラスOriginalCpendingクラスそれぞれのインスタンスを生成します。OriginalCTradeクラスインスタンス名を「Trade」とします。OriginalCpendingクラスインスタンス名を「Pending」とします。

//インスタンスの生成
OriginalCTrade Trade;
OriginalCpending Pending;

インスタンス「Trade」「Pending」をそれぞれ生成したことにより、OriginalCTradeクラスOriginalCpendingクラス内の、アクセスレベルpublic関数が使えるようになりました。

インスタンスの生成方法については↓の記事をご覧ください。

第49回「クラスについて2 -クラスの使い方-」

第54回「インスタンス(オブジェクト)について」

クラスアクセスレベルについては↓の記事をご覧ください。

第50回「クラスについて3 -アクセス指定子-」

必要なパラメーターをinput変数で用意する

EA(自動売買プログラム)の仕様書に従って、パラメータ化するべき値をinput変数で定義していきます↓

//インプット変数の定義
input int AddPoints = 100;//約定予定価格の調整値
input double TradeVolume=0.1;//ロット数
input int StopLoss=1000;//ストップロスの値
input int TakeProfit=1000;//テイクプロフィットの値
//+--------------------------------------------

それぞれのinput変数について簡単に説明します。

input変数「AddPoints」は約定予定価格の調整値を設定する

input変数「AddPoints」は約定予定価格の調整値を設定するのに使います。

具体的な使い方はOnTick関数内で、実際に利用する際に再度触れますが、「今回作るEAの仕様」セクションで書いた通り、今回のEA(自動売買プログラム)の仕様では約定予定価格は

「一本前の高値/安値に、指定の値をプラス/マイナスした価格」

と定義しているので、現在足の一本前の高値にプラスしたり、安値にマイナスしたりして約定価格を調整する為の変数となります(値はポイントベースとなっています。)

input変数「TradeVolume」はロット数を設定する

input変数「TradeVolume」はロット数、すなわちエントリー時の取引数量を設定するのに使います。

input変数「StopLoss」はストップロスを設定する

input変数「StopLoss」はストップロス、すなわち損切り価格を設定するのに使います。

input変数「TakeProfit」はテイクプロフィットを設定する

input変数「TakeProfit」はテイクプロフィット、すなわち利益確定価格を設定するのに使います。

input変数については↓の記事をご覧ください

グローバル変数の定義

input変数の定義・設定が終わったので、今度はグローバル変数の定義・設定に移ります。

// グローバル変数の定義
datetime glLastBarTime;//最新の時間情報を格納する

今回は、データ型datetime型の、「glLastBarTime」というグローバル変数を定義しました。

この変数も具体的な使い方は、実際に使う時に解説していきますが、基本的に最新の時間情報を格納する目的で定義された変数になります。

グローバル変数については↓の記事をご覧ください

MQL5 EA講座 第27回「グローバル変数について」

datetime型については↓の記事をご覧ください

MQL5 EA講座 第16回「データ型その6 datetime型」

OnTick関数内の記述

続いてはOnTick関数内の記述に入ります。

価格や時間などに関するデータを取得する配列を用意する

まず最初に、価格や時間などに関するデータを取得する配列を用意していきます↓

// 価格や時間などに関するデータを取得する配列を用意
	MqlRates rates[];

データ型にはMqlRates構造体を用います。MqlRates構造体インスタンス配列として宣言します。

インスタンス名は「rates」とします。

MqlRates構造体について

MqlRates構造体は価格や時間などに関するデータを格納する為に、予めMQL5側で用意された定義済み構造体となります。

MqlRates構造体で、定義されているメンバ変数は以下の通りです。

struct MqlRates 
  { 
   datetime time;         // 期間開始時間 
  double   open;         // 始値 
  double   high;         // 期間中の最高値 
  double   low;         // 期間中の最安値 
  double   close;       // 終値 
  long     tick_volume; // ティックボリューム 
  int     spread;       // スプレッド 
  long     real_volume; // 取引高 
 };
メンバ「time」は レート情報を取得したオープン時間を格納する

メンバ「time」は レート情報を取得したオープン時間を格納します。時間情報なので、データ型datetime型です。

メンバ「open」は 始値を格納する

メンバ「open」は 始値(オープン時間以降に、初めて取引が成立した価格)を格納します。価格情報なのでデータ型double型です。

メンバ「high」は 高値を格納する

メンバ「high」は 高値(オープン時間内でもっとも高い価格)を格納します。価格情報なのでデータ型double型です。

メンバ「low」は 安値を格納する

メンバ「low」は 安値(オープン時間内でもっとも安い価格)を格納します。価格情報なのでデータ型double型です。

メンバ「close」は 終値を格納する

メンバ「close」は 終値(オープン時間内に、最後に取引が成立した価格)を格納します。価格情報なのでデータ型double型です。

メンバ「tick_volume」は ティック数を格納する

メンバ「tick_volume」は ティック数を格納します。ティック数というのは、価格情報の変更があった数、ということです。データ型long型です。

メンバ「spread」は スプレッド情報を格納する

メンバ「spread」は スプレッド情報を格納を格納します。データ型int型です。

スプレッドとは→買値(Ask)と売値(Bid)の差のこと

スプレッドとは、金融市場において、異なる商品や金融商品の価格の差を表す用語でFXにおいては、通貨ペアの買値(Ask)と売値(Bid)の差のことを表します。端的に言えば、手数料です。

スプレッドが狭い場合は、取引手数料が低くなる一方で、スプレッドが広い場合は、取引手数料が高くなることを意味しています。ブローカーを選択する判断の基準としてはスプレッドは当然着目するべきポイントになります。

メンバ「real_volume」は 取引高を格納する

メンバ「real_volume」は 取引高を格納を格納します。取引高、すなわち特定の銘柄が市場において、どのくらいの取引量であったかを示す指標となります。データ型はint型です。

以上が、MqlRates構造体の各メンバとなります。

これを前提として、

MqlRates構造体インスタンス名「rates」に、バーデータを格納していくわけですが、格納していくプロセスについては実際に使う時に改めて説明します。

MqlRates構造体と似たような構造体に、MqlTick構造体があります。

MqlRates構造体が「指定された期間内における情報」を格納するのに対し、MqlTick構造体は「最新の価格関連情報」を格納するのに使います。詳しくは下記のリンクをご覧ください。

「MqlTick構造体について」

「MqlRates構造体とMqlTick構造体の違いについて」

※なお、この先の内容にはなりますが、講座記事第104回ではバーデータにアクセスするためのクラスを作ります。MqlRates構造体とこの後解説予定のCopyRates関数は、その時にも登場予定です。↓

配列については↓の記事をご覧ください

MQL5 EA講座 第18回「配列(Array)について」

構造体については↓の記事をご覧ください

MQL5 EA講座 第22回「構造体」

ArraySetAsSeries関数を使い、配列を時系列にセットする

先程解説した、MqlRates構造体インスタンス名「rates」は配列として定義しています。

次は、この配列「rates」をArraySetAsSeries関数を使い、時系列にセットします。

//時系列データをセット
	ArraySetAsSeries(rates,true);

ArraySetAsSeries関数は、第1引数に指定した動的配列に 時系列設定を付与することができる関数です。

第1引数には、時系列設定を付与する配列を記述し、第2引数には、時系列の設定フラグをtrueかfalseで記述します。時系列の設定フラグというのは、簡単に言えば

最新の時間→過去の時間 の順番で要素配列に格納していくのか?

それとも

過去の時間→最新の時間 の順番で要素配列に格納していくのか?

この第2引数をtrueに設定する事によって、配列「rates」には直近の時間のデータから、配列に格納されていきます。配列[0]には、取得したデータの中で最新の時間のデータが格納される、ということになります。↓のような感じです。

array[0](一番新しい時間)→・・・array[n](古い方の時間)

以上の前提を踏まえて、再度↓の記述を見ると・・・

ArraySetAsSeries(rates,true);

↑は配列「rates」を、新しい時間のデータから順番に格納できる設定にした

と読み解くことができます。

※ちなみに、第2引数をfalseにした場合は、配列は古い時間のデータから、配列に格納されていきます。配列[0]には取得されたデータ中で一番古い時間のデータが格納される、ということになります。

array[0](一番古い時間)→・・・array[n](新しい方の時間)

例えば5本分の足があったとすると↓のような感じです。

ArraySetAsSeries関数についての詳細は↓の記事をご覧ください

CopyRates関数を使い、構造体配列に価格情報をコピー

次はデータ型MqlRates構造体配列「rates」に、価格情報をコピーしていきます。

int copy = CopyRates(_Symbol,_Period,0,3,rates);

価格情報をコピーするにはCopyRates関数を使います

CopyRates関数について

CopyRates関数は各種価格情報をMqlRates構造体配列にコピーする為の関数です。

複製された要素数を戻り値として返すので、戻り値データ型int型になります。

第1引数には取引銘柄を指定する

第1引数には銘柄を指定します定義済み変数_Symbol」あるいはSymbol関数を記述すると、プログラムを挿入したチャートの銘柄を指定したことになります。

第2引数には時間軸を指定する

第2引数には時間軸を指定します。定数ENUM_TIMEFRAMESで定められた値を設定します。0、もしくはPERIOD_CURRENTと記述すると現在のチャートの時間軸が適用されます。

第3引数には、コピー開始位置を指定する

第3引数には、コピー開始位置を指定します。第3引数に0を記述する事によって最新のデータがインデックス0番に格納され、時間軸が古い方に向かって、1.2.3・・・n番目と並んでいます。

第4引数には複製データ数を指定する

第4引数には複製データ数を指定します。第3引数で指定したコピー開始位置から指定した数だけデータをコピーします。今回のように第4引数に「3」と記述し、第3引数に「0」と記述した場合、最新足から3期間分のデータが配列にコピーされます。

第5引数には複製データをコピーする配列を指定する

第5引数には複製データ数をコピーする配列を指定します。受け取る配列データ型MqlRates構造体になります。

※第1引数から第4引数までは入力データを記述していましたが、第5引数は出力データを記述している点に注意してください。↓のようなイメージです。

第1引数から第4引数までが注文伝票であり、注文した商品を第5引数で受け取るイメージです。

今回CopyRates関数の第5引数には、「価格や時間などに関するデータを取得する配列を用意する」セクションでも説明した、データ型MqlRates構造体配列「rates」を記述します。

※ちなみに、

CopyRates関数は複数の書式が設定されているオーバーロード関数ですが、今回はよく使う第1書式のみ解説しています。

オーバーロード関数については、以下の記事をご覧ください。

MQL5 EA講座 第45回「関数のオーバーロード」

CopyRates関数以外にもCopy~関数はいくつか種類があります。

↓の記事で解説予定なのでもうしばらくお待ちください。

MQL5 EA講座 第105回「CopyRates関数以外のCopy関数」

新しい足が現れた時にだけ読み込む回路を記述する

「新しい足が現れた時にだけ読み込む回路」というのは、簡単に言えば

例えば5分足でEA(自動売買プログラム)を運用しているのだとしたら、5分に1回だけ、

4時間足でEA(自動売買プログラム)を運用しているのだとしたら、4時間に1回だけ、処理を行うようにする

記述回路です。

OnTick関数というのは、通常価格の値動きがあるたびに処理を行うイベントハンドラー関数です。

ただそれだと、内容によっては、処理が不必要な頻度で行使されてしまう可能性があります。

注文処理もそのうちの1つです。価格が動くたびに、発注が行われてしまうのは困りものです。

「新しい足が現れた時にだけ読み込む回路」は、そういった不都合を避けるための記述と言えます。

「新しい足が現れた時にだけ読み込む回路」も、実現するには色々な記述方法がありますが、今回は、以下のような記述によって実現させます↓

// 確定足のチェック
	bool newBar = false;
	//glLastBarTimeに格納されている日時データが現在足のデータでなければ処理を行う(同じなら処理をしない)
	if(glLastBarTime != rates[0].time) 
	{//glLastBarTimeに0より大きいデータが格納されていればnewBarにtrueを代入
		if(glLastBarTime > 0) newBar = true;
		//glLastBarTimeに現在足のデータを代入
		glLastBarTime = rates[0].time;
	}

1つ1つ順を追って見ていきましょう

新しい足が現れた時にtrueとなるbool型の変数を宣言する

bool newBar = false;

新しい足が現れた時だけに1回だけtrueとなり、それ以外の時はfalseとするようなbool型ローカル変数「newBar」を宣言します。

ローカル変数については以下の記事をご覧ください↓

glLastBarTime と rates[0].timeの時間データをif文で比較する

if(glLastBarTime != rates[0].time) 

「glLastBarTime」は、この記事内の「グローバル変数の定義」セクションで、説明したグローバル変数です。最新の時間情報を格納する事を目的に設けられた変数となります。

一方rates[0].timeというのは、この記事内の「価格や時間などに関するデータを取得する配列を用意する」セクションで、説明したMqlRates構造体データ型とした配列になります。

rates[0].timeは最新の時間情報を格納しています。

ArraySetAsSeries関数で時系列をtrueにセットしているので、インデックス[0]には最新の情報が格納されています。

構造体変数をひとまとめにしたものであり、MqlRates構造体メンバ変数.timeにはCopyRates関数によって時間情報がコピーされています。

従って、rates[0].timeには最新の時間情報が格納されている・・・という理屈です。

このあたりの部分にいまいちピンとこない場合、この記事内の

MqlRates構造体について」 セクションや

ArraySetAsSeries関数を使い、配列を時系列にセットする」 セクションを読み直してみると良いかもしれません。

あるいは構造体自体の使い方に、おぼろげな所があってわからない、という可能性もあります。

構造体については↓の記事をご覧ください。

if文で、「glLastBarTime」 と「 rates[0].time」の時間情報を比較し、時間情報が同じでない場合には{}内の記述を実行します。

「glLastBarTime」 と「 rates[0].time」がなぜ、等しくならない場合があるのか?、という点については現時点ではコード上わかりません。次の{}記述にて判明します。

※「!=」は右辺と左辺が等しくない、という意味を表す関係演算子です。

詳細は↓の記事をご覧ください

if(glLastBarTime != rates[0].time)がtrueだった時の{}内の処理を記述する

次はif(glLastBarTime != rates[0].time)がtrueだった場合の{}内の処理記述となります。

{//glLastBarTimeに0より大きいデータが格納されていればnewBarにtrueを代入
		if(glLastBarTime > 0) newBar = true;
		//glLastBarTimeに現在足のデータを代入
		glLastBarTime = rates[0].time;
	}

{}内の最初の文、

if(glLastBarTime > 0) newBar = true;

についてですが、

glLastBarTime には宣言した時点ではなにも値を入れていない状態です。

glLastBarTime > 0、すなわち0よりも大きい値が入っている場合は、bool型ローカル変数「newBar 」をtrueにします。

if(glLastBarTime != rates[0].time)の条件を満たしていても、glLastBarTime の値が0の場合、EA(自動売買プログラム)を設定した後の最初のティックの可能性があります。その時は新しい足が現れた時間帯ではない可能性があるので、このglLastBarTime > 0という記述が入っています。

ローカル変数「newBar 」は、今解説中の記述より上の部分で初期値をfalseにした状態で宣言しています(※詳細はこの記事内の 「新しい足が現れた時にtrueとなるbool型の変数を宣言する」セクションをご覧ください)

従って、OnTick関数が値動きを検知して読込を行う度に、順次進行の原則によって、false→trueとなりますが、次のティックで再度falseに戻ります。

その下の、

glLastBarTime = rates[0].time;

という表記は記述自体は単純で、変数「glLastBarTime 」に 「rates[0].time」の時間情報を代入する、という意味ですね。

つまり、基本的には「glLastBarTime 」と「rates[0].time」の値はいつも等しくなっています。

ただ一つの例外があります。

それが新しい足が現れる瞬間の時間帯に、初めてOnTick関数が処理を行う時(=値動きがあった時)です。

仮に4時間足でEA(自動売買プログラム)を運用していた場合、例えば1/21/15時59分までは、「glLastBarTime 」も「rates[0].time」も格納されている値は、1/21 12:00となっているはずです。

それが、1/21/16時00分という新しい足ができたタイミングでの最初のティック(値動きがあった時)の時だけ、

「glLastBarTime 」=1/21 12:00

「rates[0].time 」=1/21 16:00

という相違が出ます。この時だけ

if(glLastBarTime != rates[0].time)の条件文が成立し、{}の記述を執行します。

{}内の処理では

glLastBarTime = rates[0].time;の代入処理がすぐにされるので、「glLastBarTime 」と「rates[0].time」の値相違はすぐ是正され、結果次のティックからは{}内の処理を読み込まなくなります。

以上のような理屈で、新しい足が現れた時の1回だけ処理を行う仕組みが出来上がる訳です。

新しい足が現れた最初に待機注文を出す記述をする

次は新しい足が現れた最初に、待機注文を出す記述です。

if(newBar == true)、すなわち新しい足が現れた1回だけ通る処理をこれから記述していきます。

ここもいくつかのセクションに分けて解説していきたいと思います。

オーダー番号を格納する配列の準備と今までにあった待機注文の削除

if(newBar == true)
	{
		
		// 待機注文のオーダー番号情報を取得する配列を用意
		ulong tickets[];
		// 待機注文のオーダー番号情報を配列に格納
		Pending.GetOrderTickets(_Symbol,tickets);
		//配列の要素数を取得
		int numTickets = ArraySize(tickets);
		
		//今までにあった待機注文を削除する
		if(Pending.TotalPendingNum(_Symbol) > 0)
		{
			for(int i = 0; i < numTickets; i++)
			{
				Trade.DeletePending(tickets[i]);
			}//for(int i = 0; i < numTickets; i++)
			
	   }//if(Pending.TotalPending(_Symbol) > 0)

まずは待機注文オーダー番号情報を取得する配列を用意します。

配列名は「tickets」とします。オーダー番号を格納するので、データ型ulong型です。

OriginalCpendingクラスのGetOrderTickets関数を使って、オーダー番号を取得し、配列に格納

配列名「tickets」にオーダー番号を格納するには、OriginalCpendingクラスGetOrderTickets関数を使います。OriginalCpendingクラスインスタンス「Pending」からGetOrderTickets関数を呼び出しましょう。

// 待機注文のオーダー番号情報を配列に格納
		Pending.GetOrderTickets(_Symbol,tickets);

↑の記述によって、配列「tickets」にオーダー番号を格納することができました。

GetOrderTickets関数の詳細・使い方については第91回「待機注文を削除する関数を実装する」をご覧ください。

ArraySize関数を使って待機注文数を取得し、変数に格納する

OriginalCpendingクラスGetOrderTickets関数を使い、配列「tickets」にオーダー番号を格納した後は、ArraySize関数を使って待機注文数を取得し、変数「numTickets」に格納します。

変数「numTickets」は、この後待機注文数を削除するプロセスで用います。

//配列の要素数を取得
		int numTickets = ArraySize(tickets);

<参照リンク>

ArraySize関数

今までにあった待機注文を一旦削除する

新しい待機注文を出す前に、現在待機注文が残っているかどうかを確認し、もし残っているようなら、一旦残っている待機注文を削除します。

//今までにあった待機注文を削除する
		if(Pending.TotalPendingNum(_Symbol) > 0)
		{
			for(int i = 0; i < numTickets; i++)
			{
				Trade.DeletePending(tickets[i]);
			}//for(int i = 0; i < numTickets; i++)
			
	   }//if(Pending.TotalPending(_Symbol) > 0)

↑のコードを解説する前に、なぜ待機注文を削除しなくてはいけないか?を簡単に説明します。

今回作るEAの仕様では、

EA(自動売買プログラム)を設定するチャート時間足における一本前の高値・安値に、指定の値をプラス・マイナスした価格を約定予定価格として待機注文を出す。

としています。つまり、毎回新しい足が現れる度に、新しい待機注文を出す仕様になっています。

そうなると、もし約定しなかった待機注文を削除せずにそのままにしておくと、エントリー根拠を失った待機注文がどんどん増える一方になってしまいます。

そうならないように、新しい足が現れた際に、目標価格に到達せず、約定しなかった待機注文は、削除する必要がある訳です。

それを踏まえて、コードを見ていきましょう。

if(Pending.TotalPendingNum(_Symbol) > 0)は待機注文の有無を確認している。
if(Pending.TotalPendingNum(_Symbol) > 0)

TotalPendingNum関数待機注文の総数を取得するメンバ関数です。

従って、

if(Pending.TotalPendingNum(_Symbol) > 0)の条件文がtrueの場合、約定しなかった待機注文が残っている事を意味します。

for文を使って、待機注文プールをチェックし、待機注文削除を実行する
{
			for(int i = 0; i < numTickets; i++)
			{
				Trade.DeletePending(tickets[i]);
			}//for(int i = 0; i < numTickets; i++)
			
}//if(Pending.TotalPending(_Symbol) > 0)

変数「numTickets」には、待機注文の数が格納されているので、for文をつかって0から、変数「numTickets」より数が小さい条件を満たしている間、繰り返し処理を行います。

待機注文の削除には第91回で作ったオリジナル関数である、DeletePending関数を用います。

これで待機注文の削除が完了しました。晴れて、新規の待機注文を出す記述に移ります。

買いの待機注文(逆指値)を出す

買いの待機注文を出すまでの記述は以下の通りです。

//買いの待機注文(逆指値)を出す
		
		//約定予定価格の設定
		double orderPrice = rates[1].high + (AddPoints * _Point);
		//ストップレベルの調整
		orderPrice = ModifyUpperStopLevel(_Symbol,orderPrice);
		
		//ストップロス値の設定
		double stopLoss = CalBuySL(_Symbol,StopLoss,orderPrice);
		//テイクプロフィットの設定
		double takeProfit = CalBuyTP(_Symbol,TakeProfit,orderPrice);
		
		//買いの待機注文
		Trade.BuyStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);

例によって、一つ一つ順に見ていきましょう

約定予定価格の設定

まずは待機注文の約定予定価格の設定です。

	//約定予定価格の設定
		double orderPrice = rates[1].high + (AddPoints * _Point);

MqlRates構造体データ型配列「rates」からメンバ変数.highを呼び出すことで指定されたインデックスの高値情報を呼び出すことができます。rates[1].highは最新足から一本前の高値情報を呼び出したことを意味します。

あとはinput変数「AddPoints」に定義済み変数_Point」をかけ合わせた物を足すことによって、約定予定価格を算出できます。算出された値は、変数「orderPrice」に格納しておきます。

ストップレベルの調整を行う

続いてストップレベルの調整を行います。

ストップレベルについては覚えていますでしょうか?

あまり覚えていない・・・、という人は以下↓の講座記事を確認頂ければと思います。

第78回「ストップレベルについて」

第79回「注文価格がストップレベルに違反していないかをチェックする関数」

第80回「ストップレベルに違反していた場合に、自動修正する関数」

//ストップレベルの調整
		orderPrice = ModifyUpperStopLevel(_Symbol,orderPrice);

買いの逆指値待機注文におけるストップレベルの調整には、OriginalTrade.mqhファイルに作った独立関数である、ModifyUpperStopLevel関数を使います。

約定予定価格を格納した変数「orderPrice」をModifyUpperStopLevel関数の第2引数に記述する事によって、万が一約定予定価格を格納した変数「orderPrice」の値がストップレベルに違反していたとしても、自動的に調整されるようになっています。

ModifyUpperStopLevel関数ストップレベルを調整した値を、変数「orderPrice」に改めて代入します。

ModifyUpperStopLevel関数の詳しい使い方についてはコチラのリンクをご覧ください

ストップロス値の設定

続いてストップロス値の設定の行います。ストップロスの設定には、OriginalTrade.mqhファイルに作った独立関数である、CalBuySL関数を使います。

double stopLoss = CalBuySL(_Symbol,StopLoss,orderPrice);

CalBuySL関数の第2引数に記述した変数「StopLoss」はinput変数です。自身で任意にストップロスを設定できるようになっています。

第3引数には約定予定価格を格納した変数「orderPrice」を記述します。

これで、ストップロス価格を取得出来たので、ローカル変数「stopLoss」に値を代入します。

CalBuySL関数の詳しいコード記述についてはコチラのリンクをご覧ください。

テイクプロフィット値の設定

ストップロスの次はテイクプロフィットの設定の行います。

テイクプロフィットの設定には、OriginalTrade.mqhファイルに作った独立関数である、CalBuyTP関数を使います。

//テイクプロフィットの設定
		double takeProfit = CalBuyTP(_Symbol,TakeProfit,orderPrice);

CalBuyTP関数の第2引数に記述した変数「TakeProfit」はinput変数です。自身で任意にテイクプロフィットを設定できるようになっています。

第3引数には約定予定価格を格納した変数「orderPrice」を記述します。

これで、テイクプロフィット価格を取得出来たので、ローカル変数「takeProfit」に値を代入します。

CalBuyTP関数の詳しいコード記述についてはコチラのリンクをご覧ください。

買いの待機注文を出す

約定予定価格、ストップレベルの調整、ストップロスとテイクプロフィットの設定が終わったので、ここで買いの待機注文を出します。

買いの待機注文を出すにはOriginalCTradeクラスメンバ関数であるBuyStop関数を使います。

//買いの待機注文
		Trade.BuyStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);

第2引数に、ロット数を任意に設定できるinput変数「TradeVolume」を、

第3引数に、約定予定価格を格納した変数「orderPrice」を、

第4引数に、ストップロス価格を格納したローカル変数「stopLoss」を、

第5引数に、テイクプロフィット価格を格納したローカル変数「takeProfit」を、

それぞれ記述します。

買いの待機注文発注はこれで完了です。

BuyStop関数の詳しいコード記述についてはコチラのリンクをご覧ください。

売りの待機注文(逆指値)を出す

続いては売り待機注文についてです。

売りの待機注文を出すまでの記述は以下の通りです。

//売りの待機注文(逆指値)を出す
		
		//約定予定価格の設定
		orderPrice = rates[1].low - (AddPoints * _Point);
		
		//ストップレベルの調整
		orderPrice = ModifyLowerStopLevel(_Symbol,orderPrice);
		
		//ストップロス値の設定
		stopLoss = CalSellSL(_Symbol,StopLoss,orderPrice);
		//テイクプロフィットの設定
		takeProfit = CalSellTP(_Symbol,TakeProfit,orderPrice);
		
		//売りの待機注文
		Trade.SellStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);

やっていることは買いの時とほとんど同じです。

約定予定価格の設定

まずは待機注文の約定予定価格の設定です。

//約定予定価格の設定
		orderPrice = rates[1].low - (AddPoints * _Point);

MqlRates構造体データ型配列「rates」からメンバ変数.lowを呼び出すことで指定されたインデックスの安値情報を呼び出すことができます。rates[1].lowは最新足から一本前の安値情報を呼び出したことを意味します。

あとはinput変数「AddPoints」に定義済み変数_Point」をかけ合わせた物をマイナスすることによって、約定予定価格を算出できます。算出された値は、変数「orderPrice」に格納しておきます。

ストップレベルの調整を行う

続いてストップレベルの調整を行います。やることは買いの時とほぼ同じです。

//ストップレベルの調整
		orderPrice = ModifyLowerStopLevel(_Symbol,orderPrice);

売りの逆指値待機注文におけるストップレベルの調整には、OriginalTrade.mqhファイルに作った独立関数である、ModifyLowerStopLevel関数を使います。

約定予定価格を格納した変数「orderPrice」をModifyLowerStopLevel関数の第2引数に記述する事によって、万が一約定予定価格を格納した変数「orderPrice」の値がストップレベルに違反していたとしても、自動的に調整されるようになっています。

ModifyLowerStopLevel関数ストップレベルを調整した値を、変数「orderPrice」に改めて代入します。

ModifyLowerStopLevel関数の詳しい使い方についてはコチラのリンクをご覧ください

ストップロス値の設定

続いてストップロス値の設定の行います。ストップロスの設定には、OriginalTrade.mqhファイルに作った独立関数である、CalSellSL関数を使います。

//ストップロス値の設定
		stopLoss = CalSellSL(_Symbol,StopLoss,orderPrice);

CalSellSL関数の第2引数に記述した変数「StopLoss」はinput変数です。自身で任意にストップロスを設定できるようになっています。

第3引数には約定予定価格を格納した変数「orderPrice」を記述します。

これで、ストップロス価格を取得出来たので、ローカル変数「stopLoss」に値を代入します。

CalSellSL関数の詳しいコード記述についてはコチラのリンクをご覧ください。

テイクプロフィット値の設定

ストップロスの次はテイクプロフィットの設定の行います。テイクプロフィットの設定には、OriginalTrade.mqhファイルに作った独立関数である、CalSellTP関数を使います。

	//テイクプロフィットの設定
		takeProfit = CalSellTP(_Symbol,TakeProfit,orderPrice);

CalSellTP関数の第2引数に記述した変数「TakeProfit」はinput変数です。自身で任意にテイクプロフィットを設定できるようになっています。

第3引数には約定予定価格を格納した変数「orderPrice」を記述します。

これで、テイクプロフィット価格を取得出来たので、ローカル変数「takeProfit」に値を代入します。

CalSellTP関数の詳しいコード記述についてはコチラのリンクをご覧ください。

売りの待機注文を出す

約定予定価格、ストップレベルの調整、ストップロスとテイクプロフィットの設定が終わったので、ここで売りの待機注文を出します。

売りの待機注文を出すにはOriginalCTradeクラスメンバ関数であるSellStop関数を使います。

//売りの待機注文
		Trade.SellStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);

第2引数に、ロット数を任意に設定できるinput変数「TradeVolume」を、

第3引数に、約定予定価格を格納した変数「orderPrice」を、

第4引数に、ストップロス価格を格納したローカル変数「stopLoss」を、

第5引数に、テイクプロフィット価格を格納したローカル変数「takeProfit」を、

それぞれ記述します。

売りの待機注文発注はこれで完了です。

SellStop関数の詳しいコード記述についてはコチラのリンクをご覧ください。

そして、売りの待機注文発注記述をもって、コード全体の記述も終了となります。

まとめ

今回はこれまでに作ってきた、待機注文 を効率的に行うためのクラス関数を利用して待機注文を使ったEA(自動売買プログラム)を作り、その解説を行ってきました。今回解説したEA(自動売買プログラム)のコード全体の記述は以下のようになります。

//+------------------------------------------------------------------+
//|                                             Pending Order EA.mq5 |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"
#property version   "1.00"

#include <OriginalTrade.mqh>
#include <OriginalCpending.mqh>

//インスタンスの生成
OriginalCTrade Trade;
OriginalCpending Pending;

//インプット変数の定義
input int AddPoints = 100;//約定予定価格の調整値
input double TradeVolume=0.1;//ロット数
input int StopLoss=1000;//ストップロスの値
input int TakeProfit=1000;//テイクプロフィットの値

// グローバル変数の定義
datetime glLastBarTime;//最新の時間情報を格納する

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

int OnInit()
  {

   ENUM_ACCOUNT_TRADE_MODE tradeMode=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
   if(tradeMode != ACCOUNT_TRADE_MODE_DEMO)                 // デモ口座以外の場合
     {
     
      MessageBox("このEAはデモ口座でのみ稼働します","口座エラー");
      return INIT_FAILED;                            // 処理終了
     }


//---
   return(INIT_SUCCEEDED);
  }
  
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
      // 価格や時間などに関するデータを取得する配列を用意
	MqlRates rates[];
	//時系列データをセット
	ArraySetAsSeries(rates,true);
	//構造体配列に価格情報をコピー
	int copy = CopyRates(_Symbol,_Period,0,3,rates);
	
	// 確定足のチェック
	bool newBar = false;
	//glLastBarTimeに格納されている日時データが現在足のデータでなければ処理を行う(同じなら処理をしない)
	if(glLastBarTime != rates[0].time) 
	{//glLastBarTimeに0より大きいデータが格納されていればnewBarにtrueを代入
		if(glLastBarTime > 0) newBar = true;//glLastBarTime=0の時で、glLastBarTime != rates[0].timeの時は確定足とは言い難い
		//glLastBarTimeに現在足のデータを代入
		glLastBarTime = rates[0].time;
	}
	
	//新しい足が現れた最初に待機注文を出す
	if(newBar == true)
	{
		
		// 待機注文のオーダー番号情報を取得する配列を用意
		ulong tickets[];
		// 待機注文のオーダー番号情報を配列に格納
		Pending.GetOrderTickets(_Symbol,tickets);
		//配列の要素数を取得
		int numTickets = ArraySize(tickets);
		
		//今までにあった待機注文を削除する
		if(Pending.TotalPendingNum(_Symbol) > 0)
		{
			for(int i = 0; i < numTickets; i++)
			{
				Trade.DeletePending(tickets[i]);
			}//for(int i = 0; i < numTickets; i++)
			
	   }//if(Pending.TotalPending(_Symbol) > 0)
		
  
	
   //買いの待機注文(逆指値)を出す
		
		//約定予定価格の設定
		double orderPrice = rates[1].high + (AddPoints * _Point);
		//ストップレベルの調整
		orderPrice = ModifyUpperStopLevel(_Symbol,orderPrice);
		
		//ストップロス値の設定
		double stopLoss = CalBuySL(_Symbol,StopLoss,orderPrice);
		//テイクプロフィットの設定
		double takeProfit = CalBuyTP(_Symbol,TakeProfit,orderPrice);
		
		//買いの待機注文
		Trade.BuyStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);
		
		
		//売りの待機注文(逆指値)を出す
		
		//約定予定価格の設定
		orderPrice = rates[1].low - (AddPoints * _Point);
		
		//ストップレベルの調整
		orderPrice = ModifyLowerStopLevel(_Symbol,orderPrice);
		
		//ストップロス値の設定
		stopLoss = CalSellSL(_Symbol,StopLoss,orderPrice);
		//テイクプロフィットの設定
		takeProfit = CalSellTP(_Symbol,TakeProfit,orderPrice);
		
		//売りの待機注文
		Trade.SellStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);
    }//if(newBar == true)
   
  }//OnTick
//+------------------------------------------------------------------+

今回作ったEAの挙動は以下のようになっています。

今回は以上とさせていただきます。

最後までお読みいただきありがとうございました<m(__)m>

【超入門】MQL5 EA講座 第91回「待機注文を削除する関数を実装する」

MQL5 EA講座 第93回「トレーリングストップについて」【MT5用EAの作り方】

コメント

タイトルとURLをコピーしました