前回は OriginalCTradeクラスにBuy関数とSell関数 というアクセスレベルがpublicの注文関数を追加しました。
これらの関数の実装により、メインプログラムでMqlTradeRequest構造体のインスタンスを作ったり、メンバ変数に値を代入したりせずに注文ができるようになりました。(MQL4を経験している人にはMQL4のOrderSend関数に近い印象を受けるのではないでしょうか)
// 買い注文を出す
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);
}
今回も引き続き、OriginalCTradeクラスに関数を追加していきます。
今回の記事読んで理解する事によって、OriginalCTradeクラスに、実戦に使える関数群を追加することができるようになります。
今回実装するのは、マジックナンバー・スリッページ・フィルポリシーを返す関数です。
※マジックナンバー・スリッページ・フィルポリシーを返す関数は、メインプログラムのOnInit関数で使う事を想定したアクセスレベルがpublicの関数になります。
第71回「トレード用のオリジナルクラスを作る」 記事内でPositionOpen関数を作る過程を解説していく中で、
「アクセスレベルprotectedに変数をいくつか追加する」 という単元がありました。
そこでアクセスレベルが protectedの変数を3つ作りました。↓
ulong magicNumber;//マジックナンバーを格納する
ulong deviation;//スリッページを格納する
ENUM_ORDER_TYPE_FILLING fillType;//フィルポリシーを格納する
今回作る3つの関数は、いずれも上記のメンバ変数に値を代入する処理を担う関数になります。
上記のメンバ変数は、PositionOpen関数の中で、MqlTradeRequest構造体のメンバ変数である.magic、.deviation、.type_fillingへそれぞれ代入されるよう記述されています(詳しくは前回の記事の、「構造体インスタンスのリセットとメンバ変数への設定」の部分をご参照ください)
今回実装する関数が完成すれば、メインプログラムにて今回作成した関数を呼び出し、引数にマジックナンバー・スリッページ・フィルポリシーを入力するだけで、MqlTradeRequest構造体のメンバ変数である.magic、.deviation、.type_fillingへの設定も完了します。
※MqlTradeRequest構造体については↓の記事をご覧ください。
・第60回「OrderSend関数とMqlTradeRequest構造体」
※OnInit関数については↓の記事をご覧ください↓
マジックナンバーを返す関数
そもそも、マジックナンバーとは何か?というと、EA(自動売買プログラム)固有の識別番号です。
1つのMT5で複数のEA(自動売買プログラム)を運用する時、保有しているポジションやオーダーがどのEA(自動売買プログラム)に由来するものかをえり分けるための番号となります。
まずはアクセス指定子 publicのセクションに関数の定義を行います。
関数名は「SetMagicNumber」としました。データ型はvoid型とします。仮引数parMagicには、指定するマジックナンバーが入る想定です。※何度か言及していますが、マジックナンバーはinput変数でパラメータ化しておくと便利です。
※input変数については↓の記事をご覧ください。
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 SetMagicNumber(ulong parMagic);
宣言したら、処理実装記述を行いますが、処理実装記述自体はシンプルなものです。
// マジックナンバーを設定する
void OriginalCTrade::SetMagicNumber(ulong parMagic)
{
magicNumber = parMagic;
}
仮引数「parMagic」に入力したマジックナンバーをアクセスレベルがprotectedのメンバ変数「magicNumber」が受け取ります。
メンバ変数「magicNumber」は、同じくアクセスレベルがprotectedのPositionOpen関数内で、MqlTradeRequest構造体のメンバ変数である.magicに値を代入します。
即ちSetMagicNumber関数をメインプログラムで呼び出すだけで、MqlTradeRequest構造体のメンバ変数である.magicへの値代入が完了する、という事になります。
スリッページを返す関数
執行方式がinstant執行方式、またはRequest執行方式の場合、成行注文時にメンバ変数 .deviation への値入力が必須となります。現在は、多くのブローカーがmarket執行方式やinstant執行方式を採用しているので、スリッページを気にする必然性は薄れている気はしますが、一応どの執行方式でも対応できるように関数化しておきます。
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);
まずはアクセス指定子 publicのセクションに関数の定義を行います。
関数名は「SetDeviation」、データ型はvoid型です。仮引数parDeviationには、指定するスリッページが入る想定です。※マジックナンバーと同様にinput変数でパラメータ化しておくのが一般的です。
// スリッページを設定する
void OriginalCTrade::SetDeviation(ulong parDeviation)
{
deviation = parDeviation;
}
記述構造はSetMagicNumber関数同じですね。
仮引数「parDeviation」に入力したスリッページの値をアクセスレベルがprotectedのメンバ変数「deviation」が受け取ります。
メンバ変数「deviation」は、同じくアクセスレベルがprotectedのPositionOpen関数内で、MqlTradeRequest構造体のメンバ変数である.deviation に値を代入します。
即ちSetDeviation関数をメインプログラムで呼び出すだけで、MqlTradeRequest構造体のメンバ変数である.deviationへの値代入が完了する、という事になります。
※執行方式については↓の記事をご覧ください。
フィルポリシーを返す関数
フィルポリシーとは、流動性が極端に低下する等の事情で、発注した量のロット数では約定できないときの対応方法を指します。
※フィルポリシーについての詳細は↓の記事をご覧ください。
例によって、アクセス指定子 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);
↑関数名は「SetFillType」、データ型はvoid型です。仮引数parFillには、口座記事、第68回「簡単な仕組みのMT5用EAを作るーその1ー」の中で作った適切なフィルポリシーを返す関数で取得した値が入ることになります。
※「適切なフィルポリシーを返す関数」についてはくわしくはこの回の、「フィルポリシーを設定する関数を作り、メンバ変数.type_fillingに代入する」という項目をご参照ください。
関数の処理実装記述についてはもう想像がつくと思いますが、↓
// フィルポリシーを設定する
void OriginalCTrade::SetFillType(ENUM_ORDER_TYPE_FILLING parFill)
{
fillType = parFill;
}
仮引数「parFill」に入力したフィルポリシーの値をアクセスレベルがprotectedのメンバ変数「fillType」が受け取ります。
メンバ変数「fillType」は、同じくアクセスレベルがprotectedのPositionOpen関数内で、MqlTradeRequest構造体のメンバ変数である.type_filling に値を代入します。
即ちSetFillType関数をメインプログラムで呼び出すだけで、MqlTradeRequest構造体のメンバ変数である.type_fillingへの値代入が完了する、という事になります。
これで、OriginalCTradeクラスにマジックナンバー・スリッページ・フィルポリシーを返す関数が実装されました。
コード全体の記述内容
コード全体の記述内容は以下の通りです。
//+------------------------------------------------------------------+
//| 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);
};//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);
}
// マジックナンバーを設定する
void OriginalCTrade::SetMagicNumber(ulong parMagic)
{
magicNumber = parMagic;
}
// スリッページを設定する
void OriginalCTrade::SetDeviation(ulong parDeviation)
{
deviation = parDeviation;
}
// フィルポリシーを設定する
void OriginalCTrade::SetFillType(ENUM_ORDER_TYPE_FILLING parFill)
{
fillType = parFill;
}
今回は以上となります。最後までお読みいただきありがとうございました。
<m(__)m>
コメント