前回は トレード用のオリジナルクラスにポジションオープン関数を実装しました。
そして、PositionOpen関数を実装させるために、
を併せて、同じインクルードファイル内に作りました。
しかしながら、前回完成させたPositionOpen関数は、アクセスレベルがprotectedで、MT5用EA(自動売買プログラム)を開発する際、イベントハンドラーなどのメインプログラムから呼び出そうとしても呼び出すことができません。
今の状態は、駅の切符券売機で言うなら、中身の機械は完成しているけど、肝心の切符を買うタッチパネルディスプレイが実装されていない状態です。
そこで、今回からは切符券売機のタッチパネルディスプレイにあたる部分、即ちメインプログラムからPositionOpen関数(アクセスレベル=protected)に注文を伝えるアクセスレベルがpublicの関数を実装していきます。
アクセスレベルpublicのBuy関数とSell関数を宣言
まずはPositionOpen関数(アクセスレベル=protected)に連結させる、アクセスレベルがpublicの関数を作る必要があります。
関数名は「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_BUY、ORDER_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の作り方】
コメント