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

MQL5でEA作ろう講座

前回は、待機注文の情報を取得・管理するクラスである「OriginalCpendingクラス」に、以下の関数を追加し、その内容を解説しました。

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

今回は、待機注文を修正するメンバ関数OriginalCTradeクラスに追加する模様を解説していきます。今回の記事を読んで理解する事によって、状況に応じて当初の待機注文を修正するような類のEA(自動売買プログラム)も作れるようになります。

メンバ関数を使わない、ノーマルな待機注文の修正記述に関しては↓

MQL5 EA講座 第66回「待機注文の注文修正」で解説していますので、参考にして頂ければと思います。

スポンサーリンク
スポンサーリンク

待機注文を修正する関数「ChangePending」を宣言する

まずは、OriginalCTradeクラス内にを待機注文を修正する関数の宣言を行います。

関数名はChangePending関数とします。

EA(自動売買プログラム)のメインプログラムで呼び出す事を目的としているので、アクセスレベルpublicになります。

publicセクションであれば場所はどこでも良いのですが、第76回「SLとTPを設定する関数をクラスに追加する」で作った、SetSLTP関数の下あたりで宣言をします。

public:

bool SetSLTP(ulong parTicket, double parStop, double parProfit = 0);
  //待機注文を修正する
  bool ChangePending(ulong parTicket, double parPrice, double parStop, double parProfit, datetime parExpiration = 0);
  //ポジションをクローズする
  bool ClosePosition(ulong parTicket, double parVolume = 0, string parComment = NULL);

ChangePending関数の仮引数と戻り値について

ChangePending関数引数戻り値は以下の通りです。

//待機注文を修正する
 bool ChangePending(ulong parTicket, 
                    double parPrice, 
                    double parStop, 
                    double parProfit,
                    datetime parExpiration = 0);

parTicketには待機注文オーダー番号が記述される想定になっています。

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

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

parPriceには約定予定価格が記述される想定になっています。

parStopには損切り価格(ストップロス)が記述される想定になっています。

parProfitには利益確定価格が記述される想定になっています。

parExpirationには待機注文有効期限が記述される想定になっています。初期値を0に定めており、省略可能にしてあります。

ChangePending関数の処理実装記述についてはこの後解説していきますが、待機注文の修正が成功した場合はtrue、失敗した場合はfalseを返す仕様にするので、戻り値bool型になります。

ChangePending関数の処理実装記述について

ChangePending関数の処理実装記述は以下の通りです。

//待機注文の内容を修正する
bool OriginalCTrade::ChangePending(ulong parTicket,double parPrice,double parStop,double parProfit,datetime parExpiration=0)
{
   //インスタンスのリセット
	ZeroMemory(request);
	ZeroMemory(result);
	
	//各種メンバ変数への値代入
	request.action = TRADE_ACTION_MODIFY;
	request.order = parTicket;
	request.sl = parStop;
	request.tp = parProfit;
	
	////待機注文の選択
	bool select = OrderSelect(parTicket);
	
	//parPriceに値が入力されていれば、parPriceの値をrequest.priceにセット
	if(parPrice > 0) request.price = parPrice;
	else request.price = OrderGetDouble(ORDER_PRICE_OPEN);
	
	//parExpirationに値が入力されていれば、request.expirationにセット
	if(parExpiration > 0)
	{
		request.expiration = parExpiration;
		request.type_time = ORDER_TIME_SPECIFIED;
	}
	else request.type_time = ORDER_TIME_GTC;
	
	// リトライ回数とリターンコードを格納する変数を宣言
	int retryCount = 0;//再試行回数を格納する
	int checkCode = 0;//リターンコードを格納する
	
	do 
	{  //待機注文の修正注文を出す。
		bool sent = OrderSend(request,result);
		//注文結果を返す
		checkCode = ReturnCodeCheck(result.retcode);
		
		//約定成功の場合do-whileループを抜ける
		if(checkCode == CHECK_RETCODE_OK) break;
		//再試行しても解決しないエラーの場合
		else if(checkCode == CHECK_RETCODE_ERROR)
		{  //リターンコードを取得
			string errDesc = TradeServerReturnCodeDescription(result.retcode);
			//アラートを発出
			Alert("待機注文修正エラー: エラー内容 ",result.retcode," - ",errDesc);
			//ログの出力
			TradeLog();
			break;//ループを抜ける
		}
		
		//再試行すれば解決する可能性のあるエラーの場合
		else
		{
			Print("サーバーエラーを検知。 再試行中...");
			//次の処理までの待機
			Sleep(RETRY_MILLISECONDS);
			//再試行回数をカウントする変数をインクリメントする
			retryCount++;
		}
	}
	while(retryCount < RETRY_LIMIT);
	
	if(retryCount >= RETRY_LIMIT)
	{
		string errDesc = TradeServerReturnCodeDescription(result.retcode);
		Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
	}
	
	select = OrderSelect(parTicket);
	
	string errDesc = TradeServerReturnCodeDescription(result.retcode);
	Print("待機注文の修正 #",parTicket,": ",
	      result.retcode," - ",errDesc,", Price: ",
	      OrderGetDouble(ORDER_PRICE_OPEN),", SL: ",request.sl,
	      ", TP: ",request.tp,", Expiration: ",request.expiration);
	
	if(checkCode == CHECK_RETCODE_OK) 
	{
		Comment("待機注文 ",parTicket," 修正後の内容,",
		" Price: ",OrderGetDouble(ORDER_PRICE_OPEN),
		", SL: ",request.sl,", TP: ",request.tp);
		
		//注文が成功した場合、trueを返す
		return(true);
	}
	//注文が失敗した場合、falseを返す
	else return(false);
}

細かく一つ一つ見ていきましょう

処理実装記述その1-インスタンスのリセット~各種メンバ変数への値代入-

インスタンスのリセット~各種メンバ変数への値代入という流れは、これまでに何度も出てきているので、大分慣れてきているのではないかな、と思います。

<参考リンク>

第72回「ポジションオープン関数を実装する」第76回「SLとTPを設定する関数をクラスに追加する」第85回「ポジションを決済する関数を作る」

//インスタンスのリセット
	ZeroMemory(request);
	ZeroMemory(result);
	
	//各種メンバ変数への値代入
	request.action = TRADE_ACTION_MODIFY;
	request.order = parTicket;
	request.sl = parStop;
	request.tp = parProfit;
	
	//待機注文の選択
	bool select = OrderSelect(parTicket);
	
	//parPriceに値が入力されていれば、parPriceの値をrequest.priceにセット
	if(parPrice > 0) request.price = parPrice;
	else request.price = OrderGetDouble(ORDER_PRICE_OPEN);
	
	//parExpirationに値が入力されていれば、request.expirationにセット
	if(parExpiration > 0)
	{
		request.expiration = parExpiration;
		request.type_time = ORDER_TIME_SPECIFIED;
	}
	else request.type_time = ORDER_TIME_GTC;

インスタンス「request」の各種メンバ変数代入する値についてですが、

メンバ変数 .actionに設定する定数値はTRADE_ACTION_MODIFY

待機注文の修正を行うので、メンバ変数 .actionに設定する定数値はTRADE_ACTION_MODIFYになります。

メンバ変数 .orderには「parTicket」の値を代入

request.orderオーダー番号を格納するメンバ変数なので、オーダー番号が記述される想定の引数「parTicket」の値を代入します。

メンバ変数 .slと.tpにはparStop、parProfitの値をそれぞれ代入

request.sl は損切り価格(ストップロス)を格納するメンバ変数なので、parStopの値を、
request.tp は利益確定価格を格納するメンバ変数なので、 parProfitの値をそれぞれ代入します。

OrderSelect関数を使った待機注文の選択

OrderSelect関数は、()内にオーダー番号を記述する事によって待機注文を選択することができます。メンバ変数「parTicket」を引数として記述し、ここで選択処理を行います。

メンバ変数.priceの設定

約定予定価格が記述される想定の引数「parPrice」に0より大きい値が入っていれば、引数「parPrice」に記述されている値をrequest.Price に設定します。

request.Priceに0より小さい値が入っている場合(=異常値)、OrderGetDouble関数引数定数値(ORDER_PRICE_OPEN)を記述する事によって、現在の待機注文に設定されている約定予定価格を取得できるので、その値をrequest.Priceに設定します。

メンバ変数.expiration の設定

待機注文有効期限情報が記述される想定の引数「parExpiration」に、0より大きい値が格納されている場合、具体的な有効期限を明示していることになりますから、引数「parExpiration」の値をメンバ変数request.expiration代入し、メンバ変数request.type_time には定数値「ORDER_TIME_SPECIFIED」を代入します。

メンバ変数.type_time の設定

引数「parExpiration」に、0より大きい値が格納されていない場合、待機注文有効期限が示されていないことを意味しますから、メンバ変数request.type_time には、待機注文有効期限を設定しないことを意味する定数値「ORDER_TIME_GTC」を代入します。

処理実装記述その2-リトライ回数とリターンコードを格納する変数を宣言する-

リトライ回数とリターンコードを格納する変数を宣言します。

<参照リンク>

第72回「ポジションオープン関数を実装する」

	// リトライ回数とリターンコードを格納する変数を宣言
	int retryCount = 0;//再試行回数を格納する
	int checkCode = 0;//リターンコードを格納する

処理実装記述その3-do-while文の中に発注回路を組み込んでいく-

do-while文を使って、発注回路を記述していきます。

と言っても、PositionOpen関数SetSLTP関数の時とやっていることはほぼ同じです。

do 
	{  //待機注文の修正注文を出す。
		bool sent = OrderSend(request,result);
		//注文結果を返す
		checkCode = ReturnCodeCheck(result.retcode);
		
		//約定成功の場合do-whileループを抜ける
		if(checkCode == CHECK_RETCODE_OK) break;
		//再試行しても解決しないエラーの場合
		else if(checkCode == CHECK_RETCODE_ERROR)
		{  //リターンコードを取得
			string errDesc = TradeServerReturnCodeDescription(result.retcode);
			//アラートを発出
			Alert("待機注文修正エラー: エラー内容 ",result.retcode," - ",errDesc);
			//ログの出力
			TradeLog();
			break;//ループを抜ける
		}
		
		//再試行すれば解決する可能性のあるエラーの場合
		else
		{
			Print("サーバーエラーを検知。 再試行中...");
			//次の処理までの待機
			Sleep(RETRY_MILLISECONDS);
			//再試行回数をカウントする変数をインクリメントする
			retryCount++;
		}
	}
	while(retryCount < RETRY_LIMIT);

OrderSend関数待機注文の修正注文を出す

ReturnCodeCheck関数で注文結果の次を行動を規定する。

「約定が成功した場合」「再試行しても解決しないエラーの場合」「再試行すれば解決する可能性のあるエラーの場合」というの3つをif文 else文で条件分岐させ処理を切り分けていきます。

TradeServerReturnCodeDescription関数については→コチラをご覧ください。

ReturnCodeCheck関数戻り値であるデータ型ENUM_CHECK_RETCODE」については→コチラをご覧ください。

do-while構文については、→の記事をご覧ください。第39回「do-while文について」

インクリメントについては↓をご覧ください。

インクリメントとデクリメント(Increment,Decrement)

breakについては→コチラをご覧ください。

Alert関数については→コチラをご覧ください。

処理実装記述その4-再注文試行が上限回数に達した場合の記述-

if(retryCount >= RETRY_LIMIT)
	{
		string errDesc = TradeServerReturnCodeDescription(result.retcode);
		Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
	}

if(retryCount >= RETRY_LIMIT)

という記述は↑再注文試行回数が#define命令で設定した定数RETRY_LIMITの数を上回っている事を意味します。

TradeServerReturnCodeDescription関数リターンコードを獲得後、変数「errDesc」に格納→Alert関数変数に格納された内容を発出します。

この箇所も、PositionOpen関数SetSLTP関数とやっていることは変わりません。

処理実装記述その5:結果をPrint関数でログ出力

待機注文の修正の結果をPrint関数でログ出力します。この処理は、修正注文が成功した場合でも、失敗した場合でも行います。

select = OrderSelect(parTicket);
	
	string errDesc = TradeServerReturnCodeDescription(result.retcode);
	Print("待機注文の修正 #",parTicket,": ",
	      result.retcode," - ",errDesc,", Price: ",
	      OrderGetDouble(ORDER_PRICE_OPEN),", SL: ",request.sl,
	      ", TP: ",request.tp,", Expiration: ",request.expiration);

処理実装記述その6:戻り値を返して終了

ChangePending関数戻り値bool型です。待機注文の修正に成功した場合はtrue、失敗した場合はfalseを返すよう設計しています。

Comment関数については→コチラをご覧ください。

if(checkCode == CHECK_RETCODE_OK) 
	{
		Comment("待機注文 ",parTicket," 修正後の内容,",
		" Price: ",OrderGetDouble(ORDER_PRICE_OPEN),
		", SL: ",request.sl,", TP: ",request.tp);
		
		//注文が成功した場合、trueを返す
		return(true);
	}
	//注文が失敗した場合、falseを返す
	else return(false);
}

まとめ

今回の記事では、待機注文を修正するメンバ関数ChangePending関数」をOriginalCTradeクラスに追加する模様を解説しました。

ChangePending関数の宣言部分↓は以下の通りです。

 //待機注文を修正する
  bool ChangePending(ulong parTicket, //待機注文のオーダー番号が記述される想定
                     double parPrice,//約定予定価格が記述される想定
                     double parStop,//損切り価格(ストップロス)が記述される想定
                     double parProfit,//利益確定価格が記述される想定
                    datetime parExpiration = 0);//待機注文の有効期限が記述される想定

ChangePending関数の処理実装記述部分↓は以下の通りです。

//待機注文の内容を修正する
bool OriginalCTrade::ChangePending(ulong parTicket,double parPrice,double parStop,double parProfit,datetime parExpiration=0)
{
   //インスタンスのリセット
	ZeroMemory(request);
	ZeroMemory(result);
	
	//各種メンバ変数への値代入
	request.action = TRADE_ACTION_MODIFY;
	request.order = parTicket;
	request.sl = parStop;
	request.tp = parProfit;
	
	//待機注文の選択
	bool select = OrderSelect(parTicket);
	
	//parPriceに値が入力されていれば、parPriceの値をrequest.priceにセット
	if(parPrice > 0) request.price = parPrice;
	else request.price = OrderGetDouble(ORDER_PRICE_OPEN);
	
	//parExpirationに値が入力されていれば、request.expirationにセット
	if(parExpiration > 0)
	{
		request.expiration = parExpiration;
		request.type_time = ORDER_TIME_SPECIFIED;
	}
	else request.type_time = ORDER_TIME_GTC;
	
	// リトライ回数とリターンコードを格納する変数を宣言
	int retryCount = 0;//再試行回数を格納する
	int checkCode = 0;//リターンコードを格納する
	
	do 
	{  //待機注文の修正注文を出す。
		bool sent = OrderSend(request,result);
		//注文結果を返す
		checkCode = ReturnCodeCheck(result.retcode);
		
		//約定成功の場合do-whileループを抜ける
		if(checkCode == CHECK_RETCODE_OK) break;
		//再試行しても解決しないエラーの場合
		else if(checkCode == CHECK_RETCODE_ERROR)
		{  //リターンコードを取得
			string errDesc = TradeServerReturnCodeDescription(result.retcode);
			//アラートを発出
			Alert("待機注文修正エラー: エラー内容 ",result.retcode," - ",errDesc);
			//ログの出力
			TradeLog();
			break;//ループを抜ける
		}
		
		//再試行すれば解決する可能性のあるエラーの場合
		else
		{
			Print("サーバーエラーを検知。 再試行中...");
			//次の処理までの待機
			Sleep(RETRY_MILLISECONDS);
			//再試行回数をカウントする変数をインクリメントする
			retryCount++;
		}
	}
	while(retryCount < RETRY_LIMIT);
	
	if(retryCount >= RETRY_LIMIT)
	{
		string errDesc = TradeServerReturnCodeDescription(result.retcode);
		Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
	}
	
	select = OrderSelect(parTicket);
	
	string errDesc = TradeServerReturnCodeDescription(result.retcode);
	Print("待機注文の修正 #",parTicket,": ",
	      result.retcode," - ",errDesc,", Price: ",
	      OrderGetDouble(ORDER_PRICE_OPEN),", SL: ",request.sl,
	      ", TP: ",request.tp,", Expiration: ",request.expiration);
	
	if(checkCode == CHECK_RETCODE_OK) 
	{
		Comment("待機注文 ",parTicket," 修正後の内容,",
		" Price: ",OrderGetDouble(ORDER_PRICE_OPEN),
		", SL: ",request.sl,", TP: ",request.tp);
		
		//注文が成功した場合、trueを返す
		return(true);
	}
	//注文が失敗した場合、falseを返す
	else return(false);
}

今回紹介した現時点(講座記事第90回時点)でのOriginalTrade.mqhファイル全体のコード記述は↓の記事からご確認ください。

講座記事第90回時点でのOriginalTrade.mqhファイル全体のコード記述について

講座記事第87回時点でのOriginalTrade.mqhファイル全体のコード記述は↓コチラ

講座記事第87回時点でのOriginalTrade.mqhファイル全体のコード記述について

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

最後までお読みいただきありがとうございました。

MQL5 EA講座 第89回「待機注文情報取得用クラスにpublic関数を実装する」

         →MQL5 EA講座 第91回「待機注文を削除する関数を実装する」

コメント

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