【超入門】MQL5 EA講座 第80回「ストップレベルに違反していた場合に、自動修正する関数」【MT5用EAの作り方】

MQL5でEA作ろう講座


前回は、OriginalTrade.mqhファイルに以下の関数を追加・実装する過程を解説しました。↓

CheckUpperStopLevel関数CheckLowerStopLevel関数を作る過程を見ていく中で、

どのようにストップレベルに関する情報を取得し、チェックする記述を書いていけばよいか

を学びました。

今回は、それを踏まえて注文価格がストップレベルに違反していた場合に、自動修正する関数を実装していきます。

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

ストップレベルを自動修正する関数実装のロードマップ

CheckUpperStopLevel関数CheckLowerStopLevel関数の実装記述内容が理解できていれば、ストップレベルを自動修正する関数についても理解する事は容易です。

  • 買い注文のTP値
  • 売り注文のSL値
  • 買い逆指値注文の約定希望価格
  • 売り指値注文の約定希望価格

に関しては、ストップレベルの最小値 を求める必要があり、

  • 買い注文のSL値
  • 売り注文のTP値
  • 売り逆指値注文の約定希望価格
  • 買い指値注文の約定希望価格

に関しては、ストップレベルの最大値 を求める必要があります。

今回も2種類の関数を作っていく事になります。

※買い指値注文については→のORDER_TYPE_BUY_LIMITリンクをご覧ください。

※買い逆指値注文については→のORDER_TYPE_BUY_STOPリンクをご覧ください。

※売り指値注文については→のORDER_TYPE_SELL_LIMITリンクをご覧ください。

※売り逆指値注文については→のORDER_TYPE_SELL_STOPリンクをご覧ください。

ストップレベルの最小値要件を満たすよう修正する関数

ストップレベルの最小値要件を満たすよう修正する関数の概要は以下の通りです

// ストップレベルを修正する(最小値)
double ModifyUpperStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{
	//現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最小値を算出
	double stopPrice = currentPrice + stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice > stopPrice + addPoints) return(parPrice);
	else
	{//注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す
		double newPrice = stopPrice + addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}

関数名は「ModifyUpperStopLevel」としました。戻り値double型です。

前回作ったCheckUpperStopLevel関数は、ストップレベルをチェックするだけでしたから、戻り値bool型だったのですが、今回作るModifyUpperStopLevel関数実際に注文に出す価格を返す必要がありますから、必然的にdouble型になります。

<参照>

DoubleToString関数

ModifyUpperStopLevel関数の引数構成

ModifyUpperStopLevel関数引数構成は↓の通りです。引数は3つです。

double ModifyUpperStopLevel(string parSymbol, double parPrice, int parPoints = 10)

parSymbolには、通貨ペア・取引銘柄、parPriceには注文する価格が入ります。第3引数のparPointsには初期値を設けてあり、省略が可能です

引数parPointsは算出されたストップレベルの最小値に、値を追加する為に用います。

詳しくは前回

等のリンクをご覧ください。

ModifyUpperStopLevel関数の処理実装記述

ModifyUpperStopLevel関数の処理実装記述についても、CheckUpperStopLevel関数とほとんど同じです。

ストップレベルの算出に必要な各種値を取得

まずは、ストップレベルの算出に必要な、

現在値・ポイント・価格ベースに変換したストップレベル情報

という3つの情報を取得します。

//現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;

現在値は、SymbolInfoDouble関数の第2引数にSYMBOL_ASKを記述して取得します。

ポイントは、SymbolInfoDouble関数の第2引数にSYMBOL_POINTを記述して取得します。

価格ベースに変換したストップレベル情報は、SymbolInfoInteger関数の第2引数SYMBOL_TRADE_STOPS_LEVELを記述して取得したものに、ポイントをかけて算出します。

ストップレベル最小値の算出と任意に設定・加算するポイントの取得

//ストップレベル最小値を算出
	double stopPrice = currentPrice + stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;

ストップレベル最小値は、現在値Ask値+価格ベースに変換されたストップレベル情報になります。これが変数「stopPrice」に格納されます。

変数「addPoints」には、ストップレベル最小値に追加する価格ベースのポイントが格納されます。

最終的には変数「stopPrice」 + 変数「addPoints」の値と、第2引数parPriceの注文価格と比較して、ストップレベルに違反していないかを確認します。

ストップレベルに違反している場合は、修正した価格を戻り値として返す

ここからが、前回79回で作った関数と異なってくる部分です。

//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice > stopPrice + addPoints) return(parPrice);
	else
	{//注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す
		double newPrice = stopPrice + addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}

if文でparPriceの値がstopPrice + addPoints以上なら、ストップレベルに違反していないのでparPriceの値を注文価格にするべく戻り値として返します。

注文価格がストップレベルに違反している場合は、else文でstopPrice + addPointsの値を変数「newPrice」に格納し、それを修正した価格を戻り値として返します。

ストップレベルの最小値を修正する関数については以上です。

続いてはストップレベルの最大値を修正する関数についてです

ストップレベルの最大値要件を満たすよう修正する関数

ストップレベルの最大値を満たすよう修正する関数の概要は以下の通りです。

// ストップレベルを修正する(最大値)
double ModifyLowerStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	double stopPrice = currentPrice - stopLevel;
	double addPoints = parPoints * point;
	
	if(parPrice < stopPrice - addPoints) return(parPrice);
	else
	{
		double newPrice = stopPrice - addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}
}

関数名は「ModifyLowerStopLevel」としました。戻り値ModifyUpperStopLevel関数と同じくdouble型です。

ModifyLowerStopLevel関数の引数構成

ModifyLowerStopLevel関数引数構成は以下の通りです。

// ストップレベルを修正する(最大値)
double ModifyLowerStopLevel(string parSymbol, double parPrice, int parPoints = 10)

ModifyLowerStopLevel関数の処理実装記述

{  //現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最大値を算出
	double stopPrice = currentPrice - stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	
	//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice < stopPrice - addPoints) return(parPrice);
	else
	{ //注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す 
		double newPrice = stopPrice - addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}

の処理実装記述は細かい部分をModifyUpperStopLevel関数と逆にするだけです。

ストップレベルの算出に必要な各種値を取得

ModifyUpperStopLevel関数と同様に、ストップレベルの算出に必要なのは、

現在値・ポイント・価格ベースに変換したストップレベル情報

の3つです

//現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;

現在値は、SymbolInfoDouble関数の第2引数にSYMBOL_BIDを記述して取得します。

ポイントは、SymbolInfoDouble関数の第2引数にSYMBOL_POINTを記述して取得します。

価格ベースに変換したストップレベル情報は、SymbolInfoInteger関数の第2引数SYMBOL_TRADE_STOPS_LEVELを記述して取得したものに、ポイントをかけて算出します。

ストップレベル最大値の算出と任意に設定・加算するポイントの取得

//ストップレベル最大値を算出
	double stopPrice = currentPrice - stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;

ストップレベル最大値は、現在値Bid値 – 価格ベースに変換されたストップレベル情報になります。これが変数「stopPrice」に格納されます。

変数「addPoints」には、ストップレベル最大値に追加する価格ベースのポイントが格納されます。

変数「stopPrice」 - 変数「addPoints」の値と、第2引数parPriceの注文価格と比較して、ストップレベルに違反していないかを確認します。

ストップレベルに違反している場合は、修正した価格を戻り値として返す

//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice < stopPrice - addPoints) return(parPrice);
	else
	{ //注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す 
		double newPrice = stopPrice - addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}

if文でparPriceの値がstopPrice – addPoints以下なら、ストップレベルに違反していないのでparPriceの値を注文価格にするべく戻り値として返します

注文価格がストップレベルを違反している場合は、stopPrice – addPointsの値を変数「newPrice」に格納し、それを修正した価格を戻り値として返します。

ストップレベルの最大値を修正する関数については以上です。

今回作った関数の全体記述

今回作った関数の全体記述は以下の通りです↓

// ストップレベルを修正する(最小値)
double ModifyUpperStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{
	//現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最小値を算出
	double stopPrice = currentPrice + stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice > stopPrice + addPoints) return(parPrice);
	else
	{//注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す
		double newPrice = stopPrice + addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}
}

// ストップレベルを修正する(最大値)
double ModifyLowerStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{  //現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最大値を算出
	double stopPrice = currentPrice - stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	
	//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice < stopPrice - addPoints) return(parPrice);
	else
	{ //注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す 
		double newPrice = stopPrice - addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}

これまでのOriginalTrade.mqhファイル全体の記述内容

これまでのOriginalTrade.mqhファイル全体の記述内容は以下の通りです↓

//+------------------------------------------------------------------+
//|                                                OriginalTrade.mqh |
//|                                                         MQL5ssei |
//|                                    https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"
#define RETRY_LIMIT 3//エラー時に再注文を出す上限回数
#define RETRY_MILLISECONDS 3000//再注文処理に移るまでの待機時間

#include <errordescription.mqh>

//トレード用のクラス「OriginalCTrade」を定義する
class OriginalCTrade
  {
protected:
   MqlTradeRequest   request;
   ulong             magicNumber;//マジックナンバーを格納する
   ulong             deviation;//スリッページを格納する
   ENUM_ORDER_TYPE_FILLING fillType;//フィルポリシーを格納する
   void TradeLog();//注文内容と結果をエキスパートログに残す

   //ポジションオープンクラスの実装
   ulong PositionOpen(string parSymbol, ENUM_ORDER_TYPE parType, double parVolume, double parStop = 0, double parProfit = 0, string parComment = NULL);


public:
   MqlTradeResult    result;
   //買い注文を出す関数
   ulong Buy(string parSymbol, double parVolume, double parStop = 0, double parProfit = 0, string parComment = NULL);
   //売り注文を出す関数
   ulong Sell(string parSymbol, double parVolume, double parStop = 0, double parProfit = 0, string parComment = NULL);
   //マジックナンバーを設定する関数
   void OriginalCTrade::SetMagicNumber(ulong parMagic);
   // スリッページを設定する
   void SetDeviation(ulong parDeviation);
   // フィルポリシーを設定する
  void SetFillType(ENUM_ORDER_TYPE_FILLING parFill);
  //ストップロスとテイクプロフィットを設定する
  bool SetSLTP(ulong parTicket, double parStop, double parProfit = 0);

  };//class OriginalCTrade
  


//PositionOpen関数の処理内容を実装
ulong OriginalCTrade::PositionOpen(string parSymbol, ENUM_ORDER_TYPE parType, double parVolume, double parStop = 0, double parProfit = 0, string parComment = NULL)
  {

//構造体インスタンスのリセット
   ZeroMemory(request);
   ZeroMemory(result);

   request.action = TRADE_ACTION_DEAL;//取引の種類を設定する
   request.symbol = parSymbol;//取引銘柄を設定する
   request.type = parType;//注文タイプを設定する
   request.sl = parStop;//ストップロスを設定する
   request.tp = parProfit;//テイクプロフィットを設定する
   request.comment = parComment;//コメントを設定する
   request.volume = parVolume;//ロット数を設定する
   request.deviation = deviation;//スリッページを設定する
   request.type_filling = fillType;//フィルポリシーを設定する
   request.magic = magicNumber;////マジックナンバーを設定する

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

//発注回路の記述
   do
     {
      //買いの場合の注文価格
      if(parType == ORDER_TYPE_BUY)
         request.price = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
      //売りの場合の注文価格
      else
         if(parType == ORDER_TYPE_SELL)
            request.price = SymbolInfoDouble(parSymbol,SYMBOL_BID);

      //注文を出す
      bool sent = OrderSend(request,result);
      
      //リターンコードを変数checkCode に格納する
      checkCode = ReturnCodeCheck(result.retcode);
     
      if(checkCode == CHECK_RETCODE_OK) break;
		else if(checkCode == CHECK_RETCODE_ERROR)
		{
			string errDesc = TradeServerReturnCodeDescription(result.retcode);
			Alert("注文エラー: エラー内容 ",result.retcode," - ",errDesc);
			TradeLog();
			break;
		}
		else//CHECK_RETCODE_RETRYの場合
		{
			Print("サーバーエラーを検知。 再試行中...");
			Sleep(RETRY_MILLISECONDS);
			retryCount++;
		}//else
      
      
     }//do
   while(retryCount < RETRY_LIMIT);
   
   //再注文上限回数に達した時
   if(retryCount >= RETRY_LIMIT)
   {
		string errDesc = TradeServerReturnCodeDescription(result.retcode);
		Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
	}
	
	//注文種類を確認する関数を呼び出し戻り値を変数に格納
	string orderType = ConfirmOrderType(parType);
	string errDesc = TradeServerReturnCodeDescription(result.retcode);
	
	//ログ出力
	Print("注文種類",orderType,
	      "オーダー番号 #",result.order,
	      " リターンコード: ",result.retcode,
	      " - ",errDesc,
	      ", ロット数: ",result.volume,
	      ", 約定価格: ",result.price);

//戻り値を返す
if(checkCode == CHECK_RETCODE_OK) 
	{
		Comment("注文種類: ",orderType," オーダー番号 #",result.order," 約定価格 ",result.price);
		return(result.order);
	}
	else return(0);
   
  }//OriginalCTrade::PositionOpen

//+------------------------------------------------------------------+



// リターンコードによって、次の行動を決めるための列挙型
enum ENUM_CHECK_RETCODE
{
	CHECK_RETCODE_OK,
	CHECK_RETCODE_ERROR,
	CHECK_RETCODE_RETRY
};


// リターンコードのチェックをする関数
int ReturnCodeCheck(uint parRetCode)
{
	int status;
	switch(parRetCode)
	{
		case TRADE_RETCODE_REQUOTE:
		case TRADE_RETCODE_CONNECTION:
		case TRADE_RETCODE_PRICE_CHANGED:
		case TRADE_RETCODE_TIMEOUT:
		case TRADE_RETCODE_PRICE_OFF:
		case TRADE_RETCODE_REJECT:
		case TRADE_RETCODE_ERROR:
		
			status = CHECK_RETCODE_RETRY;
			break;
			
		case TRADE_RETCODE_DONE:
		case TRADE_RETCODE_DONE_PARTIAL:
		case TRADE_RETCODE_PLACED:
		case TRADE_RETCODE_NO_CHANGES:
		
			status = CHECK_RETCODE_OK;
			break;
			
		default: status = CHECK_RETCODE_ERROR;
	}
	
	return(status);
}

//注文内容と結果をエキスパートログに残す
void OriginalCTrade::TradeLog()
{
   Print("MqlTradeRequest - 取引の種類:",request.action,
         ", コメント:",request.comment,
         ",スリッページ:",request.deviation,
         ", 注文有効期限:",request.expiration,
         ", マジックナンバー:",request.magic,
         ", オーダー番号:",request.order,
         ", ポジション番号:",request.position,
         ", 反対ポジションの番号:",request.position_by,
         ", ポジション価格:",request.price,
         ", ストップロス:",request.sl,
         ", テイクプロフィット:",request.tp,
         ", ストップリミット:",request.stoplimit,
         ", 取引銘柄:",request.symbol,
         ", オーダー種類:",request.type,
         ", フィルポリシー:",request.type_filling,
         ", 有効期限の種類:",request.type_time,
         ", ロット数:",request.volume);
         
   Print("MqlTradeResult - ask:",result.ask,
         ", bid:",result.bid,
         ", コメント:",result.comment,
         ", 約定番号:",result.deal,
         ", オーダー番号:",result.order,
         ", ポジション価格:",result.price,
         ", リクエストID:",result.request_id,
         ", リターンコード:",result.retcode,
         ", リターンコード(外部):",result.retcode_external,
         ", ロット数:",result.volume);

}//void OriginalCTrade::TradeLog()

//注文種類を確認する関数
string ConfirmOrderType(ENUM_ORDER_TYPE parType)
{
	string orderType;
	if(parType == ORDER_TYPE_BUY) orderType = "buy";
	else if(parType == ORDER_TYPE_SELL) orderType = "sell";
	else if(parType == ORDER_TYPE_BUY_STOP) orderType = "buy stop";
	else if(parType == ORDER_TYPE_BUY_LIMIT) orderType = "buy limit";
	else if(parType == ORDER_TYPE_SELL_STOP) orderType = "sell stop";
	else if(parType == ORDER_TYPE_SELL_LIMIT) orderType = "sell limit";
	else if(parType == ORDER_TYPE_BUY_STOP_LIMIT) orderType = "buy stop limit";
	else if(parType == ORDER_TYPE_SELL_STOP_LIMIT) orderType = "sell stop limit";
	else orderType = "不適切な注文種類です。";
	return(orderType);
}

// 買い注文を出す
ulong OriginalCTrade::Buy(string parSymbol, double parVolume, double parStop = 0, double parProfit = 0, string parComment = NULL)
{
	ulong ticket = PositionOpen(parSymbol,ORDER_TYPE_BUY,parVolume,parStop,parProfit,parComment);
	return(ticket);
}

// 売り注文を出す
ulong OriginalCTrade::Sell(string parSymbol, double parVolume, double parStop = 0, double parProfit = 0, string parComment = NULL)
{
	ulong ticket = PositionOpen(parSymbol,ORDER_TYPE_SELL,parVolume,parStop,parProfit,parComment);
	return(ticket);
}

// マジックナンバーを設定する
void OriginalCTrade::SetMagicNumber(ulong parMagic)
{
	magicNumber = parMagic;
}

// スリッページを設定する
void OriginalCTrade::SetDeviation(ulong parDeviation)
{
	deviation = parDeviation;
}

// フィルポリシーを設定する
void OriginalCTrade::SetFillType(ENUM_ORDER_TYPE_FILLING parFill)
{
	fillType = parFill;
}

//+------------------------------------------------------------------+
//口座の適切なフィルポリシーを返す関数
ENUM_ORDER_TYPE_FILLING FillPolicy()
{
   long fillType = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
   if(fillType==SYMBOL_FILLING_IOC)return ORDER_FILLING_IOC;
   else if(fillType==SYMBOL_FILLING_FOK)return ORDER_FILLING_FOK;
   else return ORDER_FILLING_RETURN;

}

//ストップロスとテイクプロフィットを設定する
bool OriginalCTrade::SetSLTP(ulong parTicket,double parStop,double parProfit=0.000000)
{
	//インスタンスのリセット
	ZeroMemory(request);
	ZeroMemory(result);
	
	//ポジション番号を取得し、変数に格納
	bool select = PositionSelectByTicket(parTicket);
	//銘柄を取得し、変数に格納
	string symbol = PositionGetString(POSITION_SYMBOL);
	
	request.action = TRADE_ACTION_SLTP;//取引種別の設定
	request.sl = parStop;//ストップロスの設定
	request.tp = parProfit;//テイクプロフィットの設定
	request.position = parTicket;//ポジション番号の設定
	request.symbol = symbol;//銘柄情報の設定
	request.type_filling = fillType;//フィルポリシーを設定
   request.magic = magicNumber;////マジックナンバーを設定
   
	// リトライ回数とリターンコードを格納する変数を宣言
	int retryCount = 0;//再試行回数を格納する
	int checkCode = 0;//リターンコードを格納する
	
	do 
	{  //SLTP設定注文を出す。
		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("SLTP設定エラー:エラー内容 ",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);
	}

	string errDesc = TradeServerReturnCodeDescription(result.retcode);
	Print("SLTP設定ポジション番号 #",parTicket,
	      ": ",result.retcode," - ",errDesc,
	      ", SL: ",request.sl,", TP: ",request.tp,
	      ", Bid: ",SymbolInfoDouble(symbol,SYMBOL_BID),
	      ", Ask: ",SymbolInfoDouble(symbol,SYMBOL_ASK),
	      ", ストップレベル: ",SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL));
	
	//注文が成功した場合、trueを返す
	if(checkCode == CHECK_RETCODE_OK) 
	{
		Comment("ポジション番号 #",parTicket," SLTP設定した銘柄 ",symbol,", SL: ",request.sl,", TP: ",request.tp);
		return(true);
	}
	//注文が失敗した場合、falseを返す
	else return(false);
}

//+------------------------------------------------------------------+
//| ストップロスとテイクプロフィットを計算する関数                                                       
//+------------------------------------------------------------------+
//買いのSLを設定する関数
double CalBuySL(string parSymbol,int parStopPoints, double parOpenPrice = 0)
{
	if(parStopPoints <= 0) return(0);
	
	double openPrice;
	if(parOpenPrice > 0) openPrice = parOpenPrice;
	else openPrice = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
	//指定があればその値、なければASKを基準値として採用
	
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	double stopLoss = openPrice - (parStopPoints * point);
	//現在値-pStopPoints * pointを損切りプライスとして代入
	
	long digits = SymbolInfoInteger(parSymbol,SYMBOL_DIGITS);
	stopLoss = NormalizeDouble(stopLoss,(int)digits);
	
	return(stopLoss);
}


//買いのTPを設定する関数
double CalBuyTP(string parSymbol,int parProfitPoints, double parOpenPrice = 0)
{
	if(parProfitPoints <= 0) return(0);
	
	double openPrice;
	if(parOpenPrice > 0) openPrice = parOpenPrice;
	else openPrice = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
	
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	double takeProfit = openPrice + (parProfitPoints * point);
	
	long digits = SymbolInfoInteger(parSymbol,SYMBOL_DIGITS);
	takeProfit = NormalizeDouble(takeProfit,(int)digits);
	return(takeProfit);
}

//売りのSLを設定する関数
double CalSellSL(string parSymbol,int parStopPoints, double parOpenPrice = 0)
{
	if(parStopPoints <= 0) return(0);
	
	double openPrice;
	if(parOpenPrice > 0) openPrice = parOpenPrice;
	else openPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	double stopLoss = openPrice + (parStopPoints * point);
	
	long digits = SymbolInfoInteger(parSymbol,SYMBOL_DIGITS);
	stopLoss = NormalizeDouble(stopLoss,(int)digits);
	
	return(stopLoss);
}

//売りのTPを設定する関数
double CalSellTP(string parSymbol,int parProfitPoints, double parOpenPrice = 0)
{
	if(parProfitPoints <= 0) return(0);
	
	double openPrice;
	if(parOpenPrice > 0) openPrice = parOpenPrice;
	else openPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	double takeProfit = openPrice - (parProfitPoints * point);
	
	long digits = SymbolInfoInteger(parSymbol,SYMBOL_DIGITS);
	takeProfit = NormalizeDouble(takeProfit,(int)digits);
	return(takeProfit);
}


// ストップレベルをチェックする関数(最小値)
bool CheckUpperStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{  //現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最小値を算出
	double stopPrice = currentPrice + stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	//注文価格がストップレベルに違反していないかをチェックし、戻り値を返す
	if(parPrice >= stopPrice + addPoints) return(true);
	else return(false);
}

// ストップレベルをチェックする関数(最大値)
bool CheckLowerStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{  //現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最大値を算出
	double stopPrice = currentPrice - stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	//注文価格がストップレベルに違反していないかをチェックし、戻り値を返す
	if(parPrice <= stopPrice - addPoints) return(true);
	else return(false);
}

// ストップレベルを修正する(最小値)
double ModifyUpperStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{
	//現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_ASK);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最小値を算出
	double stopPrice = currentPrice + stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice > stopPrice + addPoints) return(parPrice);
	else
	{//注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す
		double newPrice = stopPrice + addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}
}

// ストップレベルを修正する(最大値)
double ModifyLowerStopLevel(string parSymbol, double parPrice, int parPoints = 10)
{  //現在値の取得
	double currentPrice = SymbolInfoDouble(parSymbol,SYMBOL_BID);
	//ポイントの取得
	double point = SymbolInfoDouble(parSymbol,SYMBOL_POINT);
	//ストップレベル情報を取得し、価格ベースに変換
	double stopLevel = SymbolInfoInteger(parSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point;
	//ストップレベル最大値を算出
	double stopPrice = currentPrice - stopLevel;
	//任意に設定・加算するポイント
	double addPoints = parPoints * point;
	
	
	//注文価格がストップレベルを違反していない場合は注文価格を戻り値として返す
	if(parPrice < stopPrice - addPoints) return(parPrice);
	else
	{ //注文価格がストップレベルを違反している場合は修正した価格を戻り値として返す 
		double newPrice = stopPrice - addPoints;
		Print("注文価格はストップレベルに適合した価格に修正されました→ "+DoubleToString(newPrice));
		return(newPrice);
	}
}

※これまでにOriginalTrade.mqhファイルに、講座記事を通して追加してきた関数の解説ページへのリンクを↓に貼っておきますので、参考にしてください。

本日は以上になります。最後までお読みいただきありがとうございました<m(__)m>

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

    →【超入門】MQL5 EA講座 第81回「動的ストップロスの一例」【MT5用EAの作り方】

コメント

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