【超入門】MQL5 EA講座 第73回「Buy関数とSell関数を実装」【MT5用EAの作り方】

MQL5でEA作ろう講座


前回は トレード用のオリジナルクラスにポジションオープン関数を実装しました。

そして、PositionOpen関数を実装させるために、

を併せて、同じインクルードファイル内に作りました。

しかしながら、前回完成させたPositionOpen関数は、アクセスレベルprotectedで、MT5EA(自動売買プログラム)を開発する際、イベントハンドラーなどのメインプログラムから呼び出そうとしても呼び出すことができません。

今の状態は、駅の切符券売機で言うなら、中身の機械は完成しているけど、肝心の切符を買うタッチパネルディスプレイが実装されていない状態です。

そこで、今回からは切符券売機のタッチパネルディスプレイにあたる部分、即ちメインプログラムからPositionOpen関数アクセスレベルprotected)に注文を伝えるアクセスレベルpublic関数を実装していきます。

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

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

アクセスレベルpublicのBuy関数とSell関数を宣言

まずはPositionOpen関数アクセスレベルprotected)に連結させる、アクセスレベルpublic関数を作る必要があります。

publicキーワードの下に、2つの関数宣言を行います。

関数名は「Buy」と「Sell」にしました。

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

引数の名前を見ると、役割もほぼ類推できると思いますが、一応コメントアウトした説明をつけると


//買い注文を出す関数
   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//コメントを設定する
);

こうなります。前回作ったPositionOpen関数アクセスレベルprotected)と引数構成はほぼ同じなのですが、1点だけ違うのは、

PositionOpen関数アクセスレベルprotected)にはあった、parTypeという引数がなくなっている、ということです。

parTypeはPositionOpen関数アクセスレベルprotected)では取引の種類を設定する為に使われる引数なのですが、なぜBuy関数Sell関数ではなくなっているのでしょう?

この点については、実際の処理実装記述を見ていくとわかると思います。

宣言に続いて実装記述を見ていきます。

クラスの処理実装記述について復習されたい方は↓の記事をご覧ください。

Buy関数の処理実装記述

Buy関数の処理は極めてシンプルです。

{}内でPositionOpen関数を呼び出して、注文を出し(PositionOpen関数の処理実装記述にはOrderSend関数が含まれています)、その注文結果を変数「ticket」に格納して戻り値として返す(PositionOpen関数戻り値はresult.orderに格納されているオーダー番号になっています)

これだけです。↓

// 買い注文を出す
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);
}

Buy関数は、初めから「買い注文を出す為に使う関数」と決めているので、PositionOpen関数の第2引数ORDER_TYPE_BUYとなっています。それ以外のPositionOpen関数引数は、Buy関数引数の値が使われる構造になっています。

Sell関数の処理実装記述

Sell関数は、Buy関数と細かい部分が逆になっているだけです。↓

// 売り注文を出す
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);
}

Sell関数は初めから「売り注文を出す為に使う関数」と決めているので、PositionOpen関数の第2引数ORDER_TYPE_SELLとなっています。それ以外のPositionOpen関数引数は、Sell関数引数の値が使われる構造になっています。

Buy関数Sell関数引数に、取引種類を設定するparTypeがなかった理由は、{}内で呼び出したPositionOpen関数ORDER_TYPE_BUYORDER_TYPE_SELLとオーダー種類を明示していたからです。

これで、メインプログラムからからOriginalCTradeのインスタンスを生成し、Buy関数Sell関数を呼び出せば、晴れて注文ができます。それだけではなく、もう煩わしいMqlTradeRequest構造体メンバ変数に値を埋める必要がなくなるわけです。

<復習用リンク>

コード全体の記述内容

コード全体の記述内容は以下の通りです。

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

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

//戻り値を返す
   return(result.order);
  }//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()

// 買い注文を出す
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);
}

今回は以上になります。

次回は、マジックナンバースリッページフィルポリシーを返す関数を実装していきます。

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

<m(__)m>

【超入門】MQL5 EA講座 第72回「ポジションオープン関数を実装する」

【超入門】MQL5 EA講座 第74回「マジックナンバー・スリッページ・フィルポリシーを返す関数を実装」【MT5用EAの作り方】

コメント

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