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

MQL5でEA作ろう講座

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

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

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

今回はTrailingStop関数書式2についての解説をしていきたいと思います。

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

講座記事96回時点でのOriginalCTrailingクラスのコード記述全体はコチラ

スポンサーリンク

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

まずは書式2引数から見ていきます。

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

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

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

第2引数「parTrailPrice」には価格ベースでのトレーリングストップ値が記述されます。

書式1引数「parTrailPoints」はデータ型int型でしたが、書式2ではdouble型になっています。この第2引数書式1との違いであり、これにより後程説明する処理実装記述も変わってくることになります。

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

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

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

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

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

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

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

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

TrailingStop関数書式2)の処理実装記述は以下のようになっています。

//TrailingStop関数の書式2
bool OriginalCTrailing::TrailingStop(ulong parTicket,double parTrailPrice,int parMinProfit=0,int parStep=10)
{
	if(PositionSelectByTicket(parTicket) == true && parTrailPrice > 0)//96回との違い
	{
		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;//ミニマムプロフィットを価格ベースに変換
		
				
		currentStop = NormalizeDouble(currentStop,digits);//現在のSLを正規化
		parTrailPrice = NormalizeDouble(parTrailPrice,digits);//トレイリングストップの値を正規化(96回との違い)
		//↑現在のSLとトレールプライスをそれぞれ正規化
		
		double currentProfit;//ポジションの損益情報を格納する変数を宣言
		
		// リトライ回数とリターンコードを格納する変数を宣言する
		int retryCount = 0;
		int checkRes = 0;
		
		//bid値とask値を格納する変数を宣言
		double bid = 0, ask = 0;
		
		do 
		{
			if(posType == POSITION_TYPE_BUY)//買いポジションの場合
			{
				bid = SymbolInfoDouble(symbol,SYMBOL_BID);//bid値を変数に格納
				currentProfit = bid - openPrice;//損益情報を格納
				if(parTrailPrice> currentStop + step && currentProfit >= minProfit) 
				{//現在値-トレールPが現在のSL値+ステップ幅より大きく、損益情報がminProfitより大きい
					request.sl = parTrailPrice;//新しいSL値に設定
					request.tp=currentLimit;
					
					//トレーリングストップの発注
					bool sent = OrderSend(request,result);
				}
				else return(false);
			}
			else if(posType == POSITION_TYPE_SELL)//売りポジションの場合
			{
				ask = SymbolInfoDouble(symbol,SYMBOL_ASK);//ask値を変数に格納
				currentProfit = openPrice - ask;
				if((parTrailPrice < currentStop - step || currentStop == 0) && currentProfit >= minProfit)
				{/*トレイルストップ値が現在のSL-ステップ幅より小さい、または現時点で損切りが設定されていない
				かつ、ポジションの損益 が minProfitより大きい*/
					request.sl = parTrailPrice;//新しいSL値に設定
					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関数(書式2)の仮引数について」セクションにおいて、書式1書式2では、第2引数データ型が異なっている事を書きました(書式1int型書式2double型)。

書式1では、トレーリングストップ値を

買いの場合であれば、SymbolInfoDouble(symbol,SYMBOL_BID) – trailStop

売りの場合であれば、SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStop

といったように、画一的に算出して求めていました。

書式2では、トレーリングストップ値の設定に柔軟性を持たせる為に、TrailingStop関数内ではトレーリングストップ値の算出は行いません。

代わりにトレーリングストップ値の算出はメインプログラム内で行い、その値を第2引数に記述します。

第2引数書式1の時はint型だったのに対し、書式2ではdouble型になっています。メインプログラムで計算した価格ベースのストップロス値を直接第2引数に記述する為です。

※今は、何を言っているのかピンとこない人がいるかもしれません。次回具体例を挙げて解説していきますので現時点ではわからなくても大丈夫です。

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

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

↑のサンプルコードのif文を読み解くと、「ポジションの選択ができて、トレーリングストップの値が指定されている」という意味になります。

ポジション選択の後の&&条件で用いられている変数が、書式1の時はint型のparTrailPointでしたが、

書式2の時のparTrailPriceはdouble型になっています。(理由は「TrailingStop関数(書式2)の処理実装記述について」セクションの冒頭に書いた通りです。

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

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

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);//銘柄の価格桁数を取得

書かれている内容・理屈は前回96回

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

トレーリングストップ算出に必要な値を取得する2 セクションで書かれている事と基本的に同じです。

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

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

ここも前回96回ステップ幅に関する記述を行うセクションと同じ内容です。

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

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

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

ミニマムプロフィットを価格ベースミニマムプロフィットに変換した値を変数「minProfit」に格納します。

書式1ではinput変数でパラメーター化したint型トレーリングストップ値を価格ベース(double型)に変換し、変数「trailStop」に格納する記述がありましたが、今回の書式2ではそれがなくなっています。

記述がなくなっている理由は「TrailingStop関数(書式2)の処理実装記述について」セクションの冒頭で書いたように、書式2では最終的なトレーリングストップ値の計算はメインプログラムで行うためです。

SLとTPの正規化・ポジション損益情報とトレーリングストップ値を格納する変数を宣言

currentStop = NormalizeDouble(currentStop,digits);//現在のSLを正規化
		currentLimit = NormalizeDouble(currentLimit,digits);//現在のTPを正規化
		parTrailPrice = NormalizeDouble(parTrailPrice,digits);//トレイリングストップの値を正規化
		double currentProfit;//ポジションの損益情報を格納する変数を宣言

ストップロスとテイクプロフィット、トレーリングストップ値を格納している変数「currentStop」「currentLimit」「parTrailPrice」の値をNormalizeDouble関数を使って正規化します。

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

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

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

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

前回96回とここは同じです。その他第72回第76回第85回第86回でも同じ記述が出てきますのでご参照ください。

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

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

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

あとはお馴染みのdo-while文を利用した発注回路です。

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

do 
		{
			if(posType == POSITION_TYPE_BUY)//買いポジションの場合
			{
				bid = SymbolInfoDouble(symbol,SYMBOL_BID);//bid値を変数に格納
				currentProfit = bid - openPrice;//損益情報を格納
				if(parTrailPrice> currentStop + step && currentProfit >= minProfit) 
				{//現在値-トレールPが現在のSL値+ステップ幅より大きく、損益情報がminProfitより大きい
					request.sl = parTrailPrice;//新しいSL値に設定
					request.tp=currentLimit;
					
					//トレーリングストップの発注
					bool sent = OrderSend(request,result);
				}
				else return(false);
			}
			else if(posType == POSITION_TYPE_SELL)//売りポジションの場合
			{
				ask = SymbolInfoDouble(symbol,SYMBOL_ASK);//ask値を変数に格納
				currentProfit = openPrice - ask;
				if((parTrailPrice < currentStop - step || currentStop == 0) && currentProfit >= minProfit)
				{/*トレイルストップ値が現在のSL-ステップ幅より小さい、または現時点で損切りが設定されていない
				かつ、ポジションの損益 が minProfitより大きい*/
					request.sl = parTrailPrice;//新しいSL値に設定
					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);

記述内容については96回と同じです。記述の詳細については96回

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

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

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

セクションをそれぞれご覧ください。

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

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

出力する内容は書式1と同じ内容です。

<参考リンク>

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を返します。

書式2の記述は以上になります。

まとめ

今回は、トレーリングストップ専用クラスであるOriginalCTrailingクラスにおけるメンバ関数であるTrailingStop関数、その書式2について解説しました。

書式1書式2の違いは、まず

第2引数書式1int型なのに対し、書式2double型になっている点です。

これは書式1TrailingStop関数内でトレーリングストップ値を画一的に計算して算出するのに対し、書式2ではメインプログラムで計算・用意した値をトレーリングストップ値として用いることが理由となっています。

従って、処理実装記述においても書式1トレーリングストップ値を算出する記述があるのに対し、書式2にはトレーリングストップ値を算出する記述がないのが違いとなっています。

※「TrailingStop関数(書式2)の処理実装記述について」セクションでも書いたことですが、現時点では、なぜ書式を2つ用意するのか、あまりピンと来ていない方もいらっしゃるかもしれません。

次回第98回「動的トレーリングストップについて」にて、書式2を使った具体例を挙げて解説していくので今はわからなくても大丈夫です。

今回(講座記事97回時点)での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

//TrailingStop関数の書式2
bool OriginalCTrailing::TrailingStop(ulong parTicket,double parTrailPrice,int parMinProfit=0,int parStep=10)
{
	if(PositionSelectByTicket(parTicket) == true && parTrailPrice > 0)//96回との違い
	{
		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;//ミニマムプロフィットを価格ベースに変換
		
				
		currentStop = NormalizeDouble(currentStop,digits);//現在のSLを正規化
		currentLimit = NormalizeDouble(currentLimit,digits);//現在のTPを正規化
		parTrailPrice = NormalizeDouble(parTrailPrice,digits);//トレイリングストップの値を正規化
		//↑現在のSLとトレールプライスをそれぞれ正規化
		
		double currentProfit;//ポジションの損益情報を格納する変数を宣言
		
		// リトライ回数とリターンコードを格納する変数を宣言する
		int retryCount = 0;
		int checkRes = 0;
		
		//bid値とask値を格納する変数を宣言
		double bid = 0, ask = 0;
		
		do 
		{
			if(posType == POSITION_TYPE_BUY)//買いポジションの場合
			{
				bid = SymbolInfoDouble(symbol,SYMBOL_BID);//bid値を変数に格納
				currentProfit = bid - openPrice;//損益情報を格納
				if(parTrailPrice> currentStop + step && currentProfit >= minProfit) 
				{//現在値-トレールPが現在のSL値+ステップ幅より大きく、損益情報がminProfitより大きい
					request.sl = parTrailPrice;//新しいSL値に設定
					request.tp=currentLimit;
					
					//トレーリングストップの発注
					bool sent = OrderSend(request,result);
				}
				else return(false);
			}
			else if(posType == POSITION_TYPE_SELL)//売りポジションの場合
			{
				ask = SymbolInfoDouble(symbol,SYMBOL_ASK);//ask値を変数に格納
				currentProfit = openPrice - ask;
				if((parTrailPrice < currentStop - step || currentStop == 0) && currentProfit >= minProfit)
				{/*トレイルストップ値が現在のSL-ステップ幅より小さい、または現時点で損切りが設定されていない
				かつ、ポジションの損益 が minProfitより大きい*/
					request.sl = parTrailPrice;//新しいSL値に設定
					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);
}

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

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

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

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

MQL5 EA講座 第96回「トレーリングストップクラスを作る1」 ←

      →【超入門】MQL5 EA講座 第98回「動的トレーリングストップについて」

コメント

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