ホーム » MQL5でEA作ろう講座 » 【超入門】MQL5 EA講座 第96回「トレーリングストップクラスを作る1」【EAの作り方】
a

【超入門】MQL5 EA講座 第96回「トレーリングストップクラスを作る1」【EAの作り方】

MQL5でEA作ろう講座

前回トレーリングストップにおける「ステップ幅」の概念について説明しました。

改めて前回の内容を説明すると、

ステップ幅」とは、「一旦トレーリングストップが発動した後に、再度発動するタイミングを任意に調整できるようにしようという仕組み(MQL5においてはそのコード記述)になります。

一旦トレーリングストップが発動した後、さらに相場が想定通りの方向(=より利益を生む方向)に向かっても、input変数などを使って設定・調整した指定価格に到達するまではトレーリングストップを発動させない仕組みです。

前回95回に解説したサンプルコードの全体(ミニマムプロフィットとステップ幅を実装)はコチラ

第94回で解説したサンプルコードの全体(ミニマムプロフィットを実装)はコチラ

今回は、

第93回第94回第95回で解説してきたトレーリングストップの基本を踏まえた上で、メインプログラムで簡単にトレーリングストップの記述設定クラスができるようにトレーリングストップ専用クラスを作っていきたいと思います。

<過去に講座で解説してきた、オリジナルクラスに関係した内容の講座一覧↓>

第71回「トレード用のオリジナルクラスを作る」

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

第73回「Buy関数とSell関数を実装」

第74回「マジックナンバー・スリッページ・フィルポリシーを返す関数を実装」

第76回「SLとTPを設定する関数をクラスに追加する」

第77回「固定SLTPを計算する関数を実装する」

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

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

第82回「ポジション情報管理クラスを作る-その1」

第83回「ポジション情報管理クラスを作る-その2」

第84回「ポジション情報を取得する独立関数を作る」

第85回「ポジションを決済する関数を作る」

第86回「待機注文関数を作る-その1-」

第87回「待機注文関数を作る-その2-」

第88回「待機注文情報取得用のクラスを追加する」

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

第90回「待機注文を修正する関数を実装する」

第91回「待機注文を削除する関数を実装する」

スポンサーリンク

トレーリングストップ専用のクラスを実装していく

まずはトレーリングストップ専用クラスを記述するインクルードファイルを作っていきます。

//+------------------------------------------------------------------+
//|                                         OriginalTrailingStop.mqh |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"

#include <errordescription.mqh>
#include <OriginalTrade.mqh>

class OriginalCTrailing
{

};

ファイル名を「OriginalTrailingStop.mqh」とし、クラス名を「OriginalCTrailingクラス」としました。

ここから、トレーリングストップに必要なメンバ変数メンバ関数を追加していきます。

エラー内容を検知する為に、errordescription.mqhファイルを、

発注に関する関数を使うために、OriginalTrade.mqhファイルを、

それぞれOriginalTrailingStop.mqhファイル内で使えるようにinclude命令記述を行いました。

インクルードファイルについての概要、使い方などについては↓の記事をご参照ください。

スポンサーリンク

アクセスレベル=protectedのメンバを追加する

まずはアクセスレベル=protectedメンバを追加していきます。

private,protected,publicなどのアクセス指定子については↓

第50回「クラスについて3 -アクセス指定子-」をご参照ください。

class OriginalCTrailing
{
      protected:
		MqlTradeRequest request;
};

{}内に

アクセス指定子protectedにしたMqlTradeRequest構造体インスタンス、「request」

を宣言しました。

protectedキーワードで指定された変数関数は、その利用有効範囲が、宣言したクラスと、派生クラスに制限されます。

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

MQL5 EA講座 第54回「インスタンスについて」

スポンサーリンク

アクセスレベル=publicのメンバを追加する

続いては、アクセスレベル=publicメンバを追加していきます。

public:
		MqlTradeResult result;
		bool TrailingStop(ulong parTicket, int parTrailPoints, int parMinProfit = 0, int parStep = 10);
		bool TrailingStop(ulong parTicket, double parTrailPrice, int parMinProfit = 0, int parStep = 10);

MqlTradeResult構造体インスタンス「result」を宣言します。インスタンス「result」はOrderSend関数による発注結果を受け取るためのものです。もう何度も出てきている記述ですが、もし理解があやふやな方は↓

MQL5 EA講座 第61回「MqlTradeResult構造体について」をご覧ください。

続いては戻り値bool型TrailingStop関数というものを宣言しています。

「あれ、TrailingStopという同じ名前の関数が2つ出てきていない?」

と思われた方はいらっしゃいますでしょうか?

これはオーバーロード関数をこれから作ろうとしている事を意味しています。

関数のオーバーロードについては↓の記事をご参照ください。

一応復習の為に、軽く触れると

関数のオーバーロードとは、

関数に複数の書式(バリアントとも言います)を付与すること

を言います。

MQL5では、同じ名前の関数でも、複数の異なる処理を行うことができるような仕組みを設けています。

それがオーバーロードという訳です。

今回は、「トレーリングストップを発動させる」という目的は同じなのだけれども、その処理を微妙に変えたいので、関数のオーバーロードの仕組みを利用して関数を2つ作ることにしました。

どのような異なる処理を行うオーバーロード関数を作るかはこの後解説していくので、今はわからなくて大丈夫です。

今回はTrailingStop関数書式1について解説をします。書式2については次回97回にて解説をする予定です。

TrailingStop関数(書式1)の仮引数について

TrailingStop関数書式1)の引数は以下のようになっています。

bool TrailingStop(ulong parTicket, int parTrailPoints, int parMinProfit = 0, int parStep = 10);

第1引数「parTicket」にはポジション番号が記述される想定です。

第2引数「parTrailPoints」にはポイントベースでのトレーリングストップ値が記述される想定です。

この第2引数が書式2と唯一異なっている部分です(詳しい違いについては次回説明します)

第3引数「parMinProfit」にはミニマムプロフィットの値が記述される想定です。

初期値を0に設定しているので省略可能です。

ミニマムプロフィットの概念については第94回「トレーリングストップにおけるミニマムプロフィットについて」をご覧ください。

第4引数「parStep」にはステップ幅の値が記述される想定です。

ステップ幅の概念については第95回「トレーリングストップにおけるステップ幅について」をご覧ください。

こちらも初期値を0に設定しているので省略可能です。

引数の省略や初期値については第46回「関数の初期値について」をご覧ください。

TrailingStop関数(書式1)の処理実装記述について

書式1引数について説明をしたところで、次は処理実装記述を見ていきましょう。まずはTrailingStop関数書式1ですが、処理実装記述は以下のようになります。

//TrailingStop関数の書式1
bool OriginalCTrailing::TrailingStop(ulong parTicket,int parTrailPoints,int parMinProfit=0,int parStep=10)
{

	if(PositionSelectByTicket(parTicket) == true && parTrailPoints > 0)//トレイルポイントが0以上なら
	{
		request.action = TRADE_ACTION_SLTP;//取引の種類
		request.position = parTicket;//ポジション番号
		
		long posType = PositionGetInteger(POSITION_TYPE);//ポジションタイプ
		double currentStop = PositionGetDouble(POSITION_SL);//未決済ポジションのSL価格
		
		double currentLimit = PositionGetDouble(POSITION_TP);//未決済ポジションのTP価格
		
		
		double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);//ポジションの約定価格
		string symbol = PositionGetString(POSITION_SYMBOL);//ポジションの銘柄情報
		
		double point = SymbolInfoDouble(symbol,SYMBOL_POINT);//ポイント情報
		int digits = (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);//銘柄の価格桁数を取得
		
		if(parStep < 10) parStep = 10;//ステップ幅の修正
		double step = parStep * point;//ステップ幅を価格ベースに変換
		
		double minProfit = parMinProfit * point;//ミニマムプロフィットを価格ベースに変換
		double trailStop = parTrailPoints * point;//トレイルストップ値を価格ベースに変換
		
		
		currentStop = NormalizeDouble(currentStop,digits);//現在のSLを正規化
		currentLimit = NormalizeDouble(currentLimit,digits);//現在のTPを正規化 
		
		double trailStopPrice;//トレイリングストップの値を格納する変数を宣言
		double currentProfit;//ポジションの損益情報を格納する変数を宣言
		
		// リトライ回数とリターンコードを格納する変数を宣言する
		int retryCount = 0;
		int checkRes = 0;
		
		do 
		{
			if(posType == POSITION_TYPE_BUY)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_BID) - trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				
				currentProfit = SymbolInfoDouble(symbol,SYMBOL_BID) - openPrice;//ポジションの損益情報を格納
				
				if(trailStopPrice > currentStop + step /*現在のSL値+ステップ幅*/&& currentProfit >= minProfit)
				{//現在値-トレールPが現在のSL値+ステップ幅より大きく、現在値-ポジションの始値がminProfitより大きい 
					
					request.sl = trailStopPrice;//現在値-トレールPを新しいSL値に設定 
					request.tp=currentLimit;
					
					//トレーリングストップの発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);
			}
			else if(posType == POSITION_TYPE_SELL)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				currentProfit = openPrice - SymbolInfoDouble(symbol,SYMBOL_ASK);//ポジションの損益情報を格納
				
				if((trailStopPrice < currentStop - step || currentStop == 0) && currentProfit >= minProfit)
				{	/*(現在値+自分で決めたトレイルポイントが現在のSL-ステップ幅より小さい、または現時点で損切りが設定されていない<0だといつまでたっても小なり、にならない)
				かつ、ポジションの始値-現在値 が minProfitより大きい*/
					request.sl = trailStopPrice;
					request.tp=currentLimit;
					
					//トレーリングストップ発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);
			}
			
			//リターンコードを変数checkRes に格納する
			checkRes = ReturnCodeCheck(result.retcode);
		   //注文が成功したらループを抜ける
			if(checkRes == CHECK_RETCODE_OK) break;
			//注文が失敗した時
			else if(checkRes == CHECK_RETCODE_ERROR)
			{
				string errDesc = TradeServerReturnCodeDescription(result.retcode);
				Alert("トレイリングストップエラー: エラー内容",result.retcode," - ",errDesc);
				break;
			}
			else
			{
				Print("サーバーエラーを検知。 再試行中...");
				Sleep(RETRY_MILLISECONDS);
				retryCount++;
			}
		}
		while(retryCount < RETRY_LIMIT);
	
		if(retryCount >= RETRY_LIMIT)
		{
			string errDesc = TradeServerReturnCodeDescription(result.retcode);
			Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
		}
		
		string errDesc = TradeServerReturnCodeDescription(result.retcode);
		Print("トレイリングストップ: ",result.retcode," - ",errDesc,", #",parTicket,", 前回のSL: ",
		      currentStop,", 新しいSL: ",request.sl,", Bid: ",SymbolInfoDouble(symbol,SYMBOL_BID),
		      ", Ask: ",SymbolInfoDouble(symbol,SYMBOL_ASK),
		      ", ストップレベル: ",SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL));
		
		if(checkRes == CHECK_RETCODE_OK) return(true);
		else return(false);
	}
	
	else return(false);
}//TrailingStop

基本的に記述されている回路については、第93回第94回第95回の内容に沿ったものなので、そちらを確認しつつ見て頂ければ、記述内容で分からない所はないかとは思うのですが、一応一つ一つ見ていきます。

ポジション選択とトレーリングストップ値の設定有無を確認する

if(PositionSelectByTicket(parTicket) == true && parTrailPoints > 0)

関数内の一番最初の記述文↑は

「ポジションの選択ができて、トレーリングストップの値が指定されている」という意味です。

このif文がtrueであれば、トレーリングストップの設定を執行する{}内の記述に移ります。

トレーリングストップ算出に必要な値を取得する1

request.action = TRADE_ACTION_SLTP;//取引の種類
		request.position = parTicket;//ポジション番号
		
		long posType = PositionGetInteger(POSITION_TYPE);//ポジションタイプ
		double currentStop = PositionGetDouble(POSITION_SL);//未決済ポジションのSL価格
		
		double currentLimit = PositionGetDouble(POSITION_TP);//未決済ポジションのTP価格

MqlTradeRequest構造体の各メンバ変数に値を設定していきます

ストップロスに修正を加えるので.actionには「TRADE_ACTION_SLTP」が入ります。

メンバ変数.position には ポジション番号代入されます。引数のparTicketにポジション番号が格納される設計になっているので、引数parTicketを代入します。

ポジション種類が買いか、売りかを取得するにはPositionGetInteger関数引数定数POSITION_TYPEを記述します。取得出来た値を変数「posType」に格納します。

現在のストップロス価格を取得するにはPositionGetDouble関数引数定数値「POSITION_SL」を記述します。取得出来た値を変数「currentStop」に格納します。

現在のテイクプロフィット価格を取得するにはPositionGetDouble関数引数定数値「POSITION_TP」を記述します。取得出来た値を変数「currentLimit」に格納します。

トレーリングストップ算出に必要な値を取得する2

引き続きトレーリングストップ算出に必要な値を取得する記述を行います。

double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);//ポジションの約定価格
		string symbol = PositionGetString(POSITION_SYMBOL);//ポジションの銘柄情報
		
		double point = SymbolInfoDouble(symbol,SYMBOL_POINT);//ポイント情報
		int digits = (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);//銘柄の桁数を取得

double型変数「openPrice」にはポジションの約定価格を代入します。ポジションの約定価格はPositionGetDouble関数引数定数値「POSITION_PRICE_OPEN」を記述して取得します。

string型変数「symbol」には、ポジションの銘柄情報を代入します。ポジションの銘柄情報は、

PositionGetString関数引数定数値「POSITION_SYMBOL」を記述して取得します。

double型変数「point」にはポイント情報を代入します。ポイント情報についてはSymbolInfoDouble関数の第2引数定数値「SYMBOL_POINT」を記述して取得します。

int型変数「digits」には銘柄の桁数を代入します。銘柄の桁数についてはSymbolInfoInteger関数の第2引数定数値「SYMBOL_DIGITS」を記述して取得します。

SymbolInfoInteger関数の前についている(int)という記述は型変換(タイプキャスト)と呼ばれるものです。

型変換(タイプキャスト)については、下記の記述をご参照ください。

ステップ幅に関する記述を行う

続いてはステップ幅に関する記述を行います。

if(parStep < 10) parStep = 10;//ステップ幅の修正
		double step = parStep * point;//ステップ幅を価格ベースに変換

if(parStep < 10) parStep = 10;

という記述はステップ幅が極端に小さくなると、ステップ幅の機能が意味を為さなくなってしまうので、引数「parStep 」に極端に小さい数値が入っている場合、それを修正する(今回のサンプルコードで言えば10ポイント)為の記述です。

ステップ幅が10ポイントより小さければステップ幅を10ポイントに設定する」

↑という意味の記述となります。10ポイントより小さい値が「parStep 」に入っていた場合は、強制的に「parStep 」の値を10ポイントに設定します。

その下に宣言した、double型ローカル変数「step」には、引数「parStep 」に定義済み変数_Point」を掛け算した値が入ります。

これによりステップ幅を価格ベースに変換する情報をローカル変数「step」に格納する事ができました。

ステップ幅という概念について確認されたい方は、前回第95回「トレーリングストップにおけるステップ幅について」をご参照ください。

ミニマムプロフィットとトレイルストップ値を価格ベースに変換した値を格納する

ミニマムプロフィットとトレイルストップ値を価格ベースに変換した値を変数「minProfit」「trailStop」にそれぞれ格納します

double minProfit = parMinProfit * point;//ミニマムプロフィットを価格ベースに変換
double trailStop = parTrailPoints * point;//トレイルストップ値を価格ベースに変換

ミニマムプロフィットについては第94回「トレーリングストップにおけるミニマムプロフィットについて」を、トレイルストップ値については第93回「トレーリングストップについて」をそれぞれご確認ください。

ストップロスとテイクプロフィットの値を正規化する

ストップロスとテイクプロフィットの値を格納している変数「currentStop」「currentLimit」の値を

NormalizeDouble関数を使って正規化します。

currentStop = NormalizeDouble(currentStop,digits);//現在のSLを正規化
currentLimit = NormalizeDouble(currentLimit,digits);//現在のTPを正規化 

double trailStopPrice;//トレイリングストップの値を格納する変数を宣言
double currentProfit;//ポジションの損益情報を格納する変数を宣言

正規化というのはプログラムが取り扱うルールに則って値を整える作業のことを言います。

変数「currentStop」や「currentLimit」に格納されている値は価格データです。プログラムエラーを防ぐ為に3桁通貨なら小数点以下3桁に、5桁通貨なら小数点以下5桁に数値を整形しています。

正規化については詳しくは下記の記事をご覧ください。↓

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

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

第72回第76回第85回第86回でも同じ記述が出てきますのでご参照ください。

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

変数「retryCount」は、注文を出して注文エラーが発生した時、エラー内容次第では再注文すればエラーが解消する可能性があるので、指定回数はエラーが出たときに再注文を出す回路を組み込む為に使います。

変数「checkRes」は、注文後、MqlTradeResult構造体インスタンス「result」が受け取るリターンコードを格納するためのものです。注文が成功したのか?、失敗したのならばその理由は何か? を確認する為に使います。

do-while文の中に発注回路を組み込んでいく

続いては発注回路の記述になります。

発注回路はdo-while文の中に記述していきます。PositionOpen関数SetSLTP関数ClosePosition関数PendingOpen関数等でも共通して使われている回路です。

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

do 
		{
			if(posType == POSITION_TYPE_BUY)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_BID) - trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				
				currentProfit = SymbolInfoDouble(symbol,SYMBOL_BID) - openPrice;//ポジションの損益情報を格納
				
				if(trailStopPrice > currentStop + step /*現在のSL値+ステップ幅*/&& currentProfit >= minProfit)
				{//現在値-トレールPが現在のSL値+ステップ幅より大きく、現在値-ポジションの始値がminProfitより大きい 
					
					request.sl = trailStopPrice;//現在値-トレールPを新しいSL値に設定 
					request.tp=currentLimit;
					
					//トレーリングストップの発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);
			}
			else if(posType == POSITION_TYPE_SELL)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				currentProfit = openPrice - SymbolInfoDouble(symbol,SYMBOL_ASK);//ポジションの損益情報を格納
				
				if((trailStopPrice < currentStop - step || currentStop == 0) && currentProfit >= minProfit)
				{	/*(現在値+自分で決めたトレイルポイントが現在のSL-ステップ幅より大きい、または現時点で損切りが設定されていない<0だといつまでたっても小なり、にならない)
				かつ、ポジションの始値-現在値 が minProfitより大きい*/
					request.sl = trailStopPrice;
					request.tp=currentLimit;
					
					//トレーリングストップ発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);
			}
			
			//リターンコードを変数checkRes に格納する
			checkRes = ReturnCodeCheck(result.retcode);
		   //注文が成功したらループを抜ける
			if(checkRes == CHECK_RETCODE_OK) break;
			//注文が失敗した時
			else if(checkRes == CHECK_RETCODE_ERROR)
			{
				string errDesc = TradeServerReturnCodeDescription(result.retcode);
				Alert("トレイリングストップエラー: エラー内容",result.retcode," - ",errDesc);
				break;
			}
			else
			{
				Print("サーバーエラーを検知。 再試行中...");
				Sleep(RETRY_MILLISECONDS);
				retryCount++;
			}
		}
		while(retryCount < RETRY_LIMIT);

do-while構文の中をもう少し細かく見ていきます。

do-while文の中1:トレーリングストップ注文(買いの場合)

do-while構文の最初には、トレーリングストップ買いの発注記述が来ます。

if(posType == POSITION_TYPE_BUY)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_BID) - trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				
				currentProfit = SymbolInfoDouble(symbol,SYMBOL_BID) - openPrice;//ポジションの損益情報を格納
				
				if(trailStopPrice > currentStop + step /*現在のSL値+ステップ幅*/&& currentProfit >= minProfit)
				{//現在値-トレールPが現在のSL値+ステップ幅より大きく、現在値-ポジションの始値がminProfitより大きい 
					
					request.sl = trailStopPrice;//現在値-トレールPを新しいSL値に設定 
					request.tp=currentLimit;
					
					//トレーリングストップの発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);

記述されている内容については、前回95回の「トレーリングストップの注文を出す(買いの場合)

セクションで解説している内容なので、詳しくはそちらをご参照ください。

do-while文の中2:トレーリングストップ注文(売りの場合)

続いては売りの場合です。

else if(posType == POSITION_TYPE_SELL)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				currentProfit = openPrice - SymbolInfoDouble(symbol,SYMBOL_ASK);//ポジションの損益情報を格納
				
				if((trailStopPrice < currentStop - step || currentStop == 0) && currentProfit >= minProfit)
				{	/*(現在値+自分で決めたトレイルポイントが現在のSL-ステップ幅より大きい、または現時点で損切りが設定されていない<0だといつまでたっても小なり、にならない)
				かつ、ポジションの始値-現在値 が minProfitより大きい*/
					request.sl = trailStopPrice;
					request.tp=currentLimit;
					
					//トレーリングストップ発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);
			}

これも、前回の「トレーリングストップの注文を出す(売りの場合)」セクションで解説している記述と基本的に同じとなっています。詳しくはそちらをご参照ください。

do-whileの中3:注文成功・失敗・再試行の条件分岐を行う

買いと売りそれぞれの発注記述が終わったので、その注文結果に応じた条件分岐の記述を行います。

//リターンコードを変数checkRes に格納する
			checkRes = ReturnCodeCheck(result.retcode);
		   //注文が成功したらループを抜ける
			if(checkRes == CHECK_RETCODE_OK) break;
			//注文が失敗した時
			else if(checkRes == CHECK_RETCODE_ERROR)
			{
				string errDesc = TradeServerReturnCodeDescription(result.retcode);
				Alert("トレイリングストップエラー: エラー内容",result.retcode," - ",errDesc);
				break;
			}
			else
			{
				Print("サーバーエラーを検知。 再試行中...");
				Sleep(RETRY_MILLISECONDS);
				retryCount++;
			}
		}
		while(retryCount < RETRY_LIMIT);

OrderSend関数での発注結果を、ReturnCodeCheck関数で確認し次の行動を規定します。発注結果は

「発注が成功した場合」

2「再試行しても解決しないエラーの場合」

3「再試行すれば解決する可能性のあるエラーの場合」というの3つの場合を想定し、

if文 else文で条件分岐させ処理を切り分けていきます

<参考リンク>

再注文試行が上限回数に達した場合の記述

do-while構文の中の記述が終わったので、次は再注文試行が上限回数に達した場合の記述に移ります。

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

if(retryCount >= RETRY_LIMIT)

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

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

トレーリングストップ注文の結果をPrint関数でログ出力する

トレーリングストップ注文の結果が、成功なのか失敗なのかを含めた結果をPrint関数でログ出力します。

string errDesc = TradeServerReturnCodeDescription(result.retcode);
		Print("トレイリングストップ: ",result.retcode," - ",errDesc,", #",parTicket,", 前回のSL: ",
		      currentStop,", 新しいSL: ",request.sl,", Bid: ",SymbolInfoDouble(symbol,SYMBOL_BID),
		      ", Ask: ",SymbolInfoDouble(symbol,SYMBOL_ASK),
		      ", ストップレベル: ",SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL));

出力する内容は言うまでもなく、トレーリングストップに関連した内容になっています。

<参考リンク>

TradeServerReturnCodeDescription関数

戻り値を返す

最後に戻り値を返す記述を行います。↓


		if(checkRes == CHECK_RETCODE_OK) return(true);
		else return(false);
	}//if(PositionSelectByTicket(parTicket) == true && parTrailPoints > 0)
	
	else return(false);
}//TrailingStop

変数「checkRes」にはReturnCodeCheck関数で取得したリターンコードが格納されています。

if(checkRes == CHECK_RETCODE_OK)の条件文がtrueだった場合、無事にトレーリングストップ処理が行われたことを意味するので、returnにて、trueを返します。

checkRes == CHECK_RETCODE_OK以外だった場合、トレーリングストップ処理に何らかの問題が発生していることを意味するのでelse文の後に、returnにてfalseを返します。

TrailingStop関数(書式1)の記述は以上になります。

スポンサーリンク

まとめ

今回は、トレーリングストップ専用クラスであるOriginalCTrailingクラスを宣言し、まずはその中のメンバ関数であるTrailingStop関数アクセスレベルpublic)を作りました。

TrailingStop関数オーバーロード関数として宣言したので書式が2つあります。

今回はそのうち書式1に関する処理実装記述について解説していきました。

今回(講座記事第96)回時点でのOriginalTrailingStop.mqhファイルのコード全体記述は以下の通りです↓

//+------------------------------------------------------------------+
//|                                         OriginalTrailingStop.mqh |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"

#include <errordescription.mqh>
#include <OriginalTrade.mqh>

class OriginalCTrailing
{
      protected:
		MqlTradeRequest request;
		
		public:
		MqlTradeResult result;
		bool TrailingStop(ulong parTicket, int parTrailPoints, int parMinProfit = 0, int parStep = 10);
		bool TrailingStop(ulong parTicket, double parTrailPrice, int parMinProfit = 0, int parStep = 10);

};

//TrailingStop関数の書式1
bool OriginalCTrailing::TrailingStop(ulong parTicket,int parTrailPoints,int parMinProfit=0,int parStep=10)
{

	if(PositionSelectByTicket(parTicket) == true && parTrailPoints > 0)//トレイルポイントが0以上なら
	{
		request.action = TRADE_ACTION_SLTP;//取引の種類
		request.position = parTicket;//ポジション番号
		
		long posType = PositionGetInteger(POSITION_TYPE);//ポジションタイプ
		double currentStop = PositionGetDouble(POSITION_SL);//未決済ポジションのSL価格
		
		double currentLimit = PositionGetDouble(POSITION_TP);//未決済ポジションのTP価格
		
		
		double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);//ポジションの約定価格
		string symbol = PositionGetString(POSITION_SYMBOL);//ポジションの銘柄情報
		
		double point = SymbolInfoDouble(symbol,SYMBOL_POINT);//ポイント情報
		int digits = (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);//銘柄の桁数を取得
		
		if(parStep < 10) parStep = 10;//ステップ幅の修正
		double step = parStep * point;//ステップ幅を価格ベースに変換
		
		double minProfit = parMinProfit * point;//ミニマムプロフィットを価格ベースに変換
		double trailStop = parTrailPoints * point;//トレイルストップ値を価格ベースに変換
		
		
		currentStop = NormalizeDouble(currentStop,digits);//現在のSLを正規化
		currentLimit = NormalizeDouble(currentLimit,digits);//現在のTPを正規化 
		
		double trailStopPrice;//トレイリングストップの値を格納する変数を宣言
		double currentProfit;//ポジションの損益情報を格納する変数を宣言
		
		// リトライ回数とリターンコードを格納する変数を宣言する
		int retryCount = 0;
		int checkRes = 0;
		
		do 
		{
			if(posType == POSITION_TYPE_BUY)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_BID) - trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				
				currentProfit = SymbolInfoDouble(symbol,SYMBOL_BID) - openPrice;//ポジションの損益情報を格納
				
				if(trailStopPrice > currentStop + step /*現在のSL値+ステップ幅*/&& currentProfit >= minProfit)
				{//現在値-トレールPが現在のSL値+ステップ幅より大きく、現在値-ポジションの始値がminProfitより大きい 
					
					request.sl = trailStopPrice;//現在値-トレールPを新しいSL値に設定 
					request.tp=currentLimit;
					
					//トレーリングストップの発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);
			}
			else if(posType == POSITION_TYPE_SELL)
			{
				trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStop;//トレイリングストップの値を格納
				trailStopPrice = NormalizeDouble(trailStopPrice,digits);//トレイリングストップの値をを正規化
				currentProfit = openPrice - SymbolInfoDouble(symbol,SYMBOL_ASK);//ポジションの損益情報を格納
				
				if((trailStopPrice < currentStop - step || currentStop == 0) && currentProfit >= minProfit)
				{	/*(現在値+自分で決めたトレイルポイントが現在のSL-ステップ幅より大きい、または現時点で損切りが設定されていない<0だといつまでたっても小なり、にならない)
				かつ、ポジションの始値-現在値 が minProfitより大きい*/
					request.sl = trailStopPrice;
					request.tp=currentLimit;
					
					//トレーリングストップ発注
					bool sent = OrderSend(request,result);
					
				}
				else return(false);
			}
			
			//リターンコードを変数checkRes に格納する
			checkRes = ReturnCodeCheck(result.retcode);
		   //注文が成功したらループを抜ける
			if(checkRes == CHECK_RETCODE_OK) break;
			//注文が失敗した時
			else if(checkRes == CHECK_RETCODE_ERROR)
			{
				string errDesc = TradeServerReturnCodeDescription(result.retcode);
				Alert("トレイリングストップエラー: エラー内容",result.retcode," - ",errDesc);
				break;
			}
			else
			{
				Print("サーバーエラーを検知。 再試行中...");
				Sleep(RETRY_MILLISECONDS);
				retryCount++;
			}
		}
		while(retryCount < RETRY_LIMIT);
	
		if(retryCount >= RETRY_LIMIT)
		{
			string errDesc = TradeServerReturnCodeDescription(result.retcode);
			Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
		}
		
		string errDesc = TradeServerReturnCodeDescription(result.retcode);
		Print("トレイリングストップ: ",result.retcode," - ",errDesc,", #",parTicket,", 前回のSL: ",
		      currentStop,", 新しいSL: ",request.sl,", Bid: ",SymbolInfoDouble(symbol,SYMBOL_BID),
		      ", Ask: ",SymbolInfoDouble(symbol,SYMBOL_ASK),
		      ", ストップレベル: ",SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL));
		
		if(checkRes == CHECK_RETCODE_OK) return(true);
		else return(false);
	}//if(PositionSelectByTicket(parTicket) == true && parTrailPoints > 0)
	
	else return(false);
}//TrailingStop

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

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

※今回の内容がよくわからなかったという方は、ひょっとするとクラスに関しての基本的な理解が追い付いていない可能性があります。クラスについては以下のリンクをご参照ください。↓

※↓の記事でもトレーリングストップを実装している様子を解説しているので、よろしければ参考にしていただければと思います。

MQL5 EA講座 第95回「トレーリングストップにおけるステップ幅について」

           →MQL5 EA講座 第97回「トレーリングストップクラスを作る2」

コメント

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