前回は、待機注文の情報を取得・管理するクラスである「OriginalCpendingクラス」に、以下の関数を追加し、その内容を解説しました。
- BuyLimitNum関数:買いリミット待機注文の数を取得するアクセスレベル=publicの関数
- SellLimitNum関数:売りリミット待機注文の数を取得するアクセスレベル=publicの関数
- BuyStopNum関数:買いストップ待機注文の数を取得するアクセスレベル=publicの関数
- SellStopNum関数:売りのストップ待機注文の数を取得するアクセスレベル=publicの関数
- BuyStopLimitNum関数:買いのストップリミット待機注文の数を取得するアクセスレベル=publicの関数
- SellStopLimitNum関数:売りのストップリミット待機注文の数を取得するアクセスレベル=publicの関数
- TotalPendingNum関数:待機注文全体の数を取得するアクセスレベル=publicの関数
- GetOrderTickets関数:待機注文のオーダー番号を取得するアクセスレベル=publicの関数
※クラスのアクセスレベルについては↓の記事をご参照ください。
今回は、待機注文を修正するメンバ関数をOriginalCTradeクラスに追加する模様を解説していきます。今回の記事を読んで理解する事によって、状況に応じて当初の待機注文を修正するような類のEA(自動売買プログラム)も作れるようになります。
※メンバ関数を使わない、ノーマルな待機注文の修正記述に関しては↓
MQL5 EA講座 第66回「待機注文の注文修正」で解説していますので、参考にして頂ければと思います。
待機注文を修正する関数「ChangePending」を宣言する
まずは、OriginalCTradeクラス内にを待機注文を修正する関数の宣言を行います。
関数名はChangePending関数とします。
EA(自動売買プログラム)のメインプログラムで呼び出す事を目的としているので、アクセスレベルはpublicになります。
publicセクションであれば場所はどこでも良いのですが、第76回「SLとTPを設定する関数をクラスに追加する」で作った、SetSLTP関数の下あたりで宣言をします。
public:
bool SetSLTP(ulong parTicket, double parStop, double parProfit = 0);
//待機注文を修正する
bool ChangePending(ulong parTicket, double parPrice, double parStop, double parProfit, datetime parExpiration = 0);
//ポジションをクローズする
bool ClosePosition(ulong parTicket, double parVolume = 0, string parComment = NULL);
ChangePending関数の仮引数と戻り値について
ChangePending関数の仮引数と戻り値は以下の通りです。
//待機注文を修正する
bool ChangePending(ulong parTicket,
double parPrice,
double parStop,
double parProfit,
datetime parExpiration = 0);
parTicketには待機注文のオーダー番号が記述される想定になっています。
※MQL5ではオーダー番号、ポジション番号、約定番号(ディール番号)という概念が存在します。
それぞれの違いについては各用語のアンカーテキストリンクから該当の記事をご参照ください。
parPriceには約定予定価格が記述される想定になっています。
parStopには損切り価格(ストップロス)が記述される想定になっています。
parProfitには利益確定価格が記述される想定になっています。
parExpirationには待機注文の有効期限が記述される想定になっています。初期値を0に定めており、省略可能にしてあります。
ChangePending関数の処理実装記述についてはこの後解説していきますが、待機注文の修正が成功した場合はtrue、失敗した場合はfalseを返す仕様にするので、戻り値はbool型になります。
ChangePending関数の処理実装記述について
ChangePending関数の処理実装記述は以下の通りです。
//待機注文の内容を修正する
bool OriginalCTrade::ChangePending(ulong parTicket,double parPrice,double parStop,double parProfit,datetime parExpiration=0)
{
//インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
//各種メンバ変数への値代入
request.action = TRADE_ACTION_MODIFY;
request.order = parTicket;
request.sl = parStop;
request.tp = parProfit;
////待機注文の選択
bool select = OrderSelect(parTicket);
//parPriceに値が入力されていれば、parPriceの値をrequest.priceにセット
if(parPrice > 0) request.price = parPrice;
else request.price = OrderGetDouble(ORDER_PRICE_OPEN);
//parExpirationに値が入力されていれば、request.expirationにセット
if(parExpiration > 0)
{
request.expiration = parExpiration;
request.type_time = ORDER_TIME_SPECIFIED;
}
else request.type_time = ORDER_TIME_GTC;
// リトライ回数とリターンコードを格納する変数を宣言
int retryCount = 0;//再試行回数を格納する
int checkCode = 0;//リターンコードを格納する
do
{ //待機注文の修正注文を出す。
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("待機注文修正エラー: エラー内容 ",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);
}
select = OrderSelect(parTicket);
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print("待機注文の修正 #",parTicket,": ",
result.retcode," - ",errDesc,", Price: ",
OrderGetDouble(ORDER_PRICE_OPEN),", SL: ",request.sl,
", TP: ",request.tp,", Expiration: ",request.expiration);
if(checkCode == CHECK_RETCODE_OK)
{
Comment("待機注文 ",parTicket," 修正後の内容,",
" Price: ",OrderGetDouble(ORDER_PRICE_OPEN),
", SL: ",request.sl,", TP: ",request.tp);
//注文が成功した場合、trueを返す
return(true);
}
//注文が失敗した場合、falseを返す
else return(false);
}
細かく一つ一つ見ていきましょう
処理実装記述その1-インスタンスのリセット~各種メンバ変数への値代入-
インスタンスのリセット~各種メンバ変数への値代入という流れは、これまでに何度も出てきているので、大分慣れてきているのではないかな、と思います。
<参考リンク>
第72回「ポジションオープン関数を実装する」、第76回「SLとTPを設定する関数をクラスに追加する」、第85回「ポジションを決済する関数を作る」
//インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
//各種メンバ変数への値代入
request.action = TRADE_ACTION_MODIFY;
request.order = parTicket;
request.sl = parStop;
request.tp = parProfit;
//待機注文の選択
bool select = OrderSelect(parTicket);
//parPriceに値が入力されていれば、parPriceの値をrequest.priceにセット
if(parPrice > 0) request.price = parPrice;
else request.price = OrderGetDouble(ORDER_PRICE_OPEN);
//parExpirationに値が入力されていれば、request.expirationにセット
if(parExpiration > 0)
{
request.expiration = parExpiration;
request.type_time = ORDER_TIME_SPECIFIED;
}
else request.type_time = ORDER_TIME_GTC;
インスタンス「request」の各種メンバ変数に代入する値についてですが、
メンバ変数 .actionに設定する定数値はTRADE_ACTION_MODIFY
待機注文の修正を行うので、メンバ変数 .actionに設定する定数値はTRADE_ACTION_MODIFYになります。
メンバ変数 .orderには「parTicket」の値を代入
request.order はオーダー番号を格納するメンバ変数なので、オーダー番号が記述される想定の仮引数「parTicket」の値を代入します。
メンバ変数 .slと.tpにはparStop、parProfitの値をそれぞれ代入
request.sl は損切り価格(ストップロス)を格納するメンバ変数なので、parStopの値を、
request.tp は利益確定価格を格納するメンバ変数なので、 parProfitの値をそれぞれ代入します。
OrderSelect関数を使った待機注文の選択
OrderSelect関数は、()内にオーダー番号を記述する事によって待機注文を選択することができます。メンバ変数「parTicket」を引数として記述し、ここで選択処理を行います。
メンバ変数.priceの設定
約定予定価格が記述される想定の仮引数「parPrice」に0より大きい値が入っていれば、仮引数「parPrice」に記述されている値をrequest.Price に設定します。
request.Priceに0より小さい値が入っている場合(=異常値)、OrderGetDouble関数の引数に定数値(ORDER_PRICE_OPEN)を記述する事によって、現在の待機注文に設定されている約定予定価格を取得できるので、その値をrequest.Priceに設定します。
メンバ変数.expiration の設定
待機注文の有効期限情報が記述される想定の仮引数「parExpiration」に、0より大きい値が格納されている場合、具体的な有効期限を明示していることになりますから、仮引数「parExpiration」の値をメンバ変数request.expiration に代入し、メンバ変数request.type_time には定数値「ORDER_TIME_SPECIFIED」を代入します。
メンバ変数.type_time の設定
仮引数「parExpiration」に、0より大きい値が格納されていない場合、待機注文の有効期限が示されていないことを意味しますから、メンバ変数request.type_time には、待機注文に有効期限を設定しないことを意味する定数値「ORDER_TIME_GTC」を代入します。
処理実装記述その2-リトライ回数とリターンコードを格納する変数を宣言する-
<参照リンク>
// リトライ回数とリターンコードを格納する変数を宣言
int retryCount = 0;//再試行回数を格納する
int checkCode = 0;//リターンコードを格納する
処理実装記述その3-do-while文の中に発注回路を組み込んでいく-
do-while文を使って、発注回路を記述していきます。
と言っても、PositionOpen関数やSetSLTP関数の時とやっていることはほぼ同じです。
do
{ //待機注文の修正注文を出す。
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("待機注文修正エラー: エラー内容 ",result.retcode," - ",errDesc);
//ログの出力
TradeLog();
break;//ループを抜ける
}
//再試行すれば解決する可能性のあるエラーの場合
else
{
Print("サーバーエラーを検知。 再試行中...");
//次の処理までの待機
Sleep(RETRY_MILLISECONDS);
//再試行回数をカウントする変数をインクリメントする
retryCount++;
}
}
while(retryCount < RETRY_LIMIT);
OrderSend関数で待機注文の修正注文を出す
↓
ReturnCodeCheck関数で注文結果の次を行動を規定する。
↓
「約定が成功した場合」「再試行しても解決しないエラーの場合」「再試行すれば解決する可能性のあるエラーの場合」というの3つをif文 else文で条件分岐させ処理を切り分けていきます。
※TradeServerReturnCodeDescription関数については→コチラをご覧ください。
※ReturnCodeCheck関数の戻り値であるデータ型「ENUM_CHECK_RETCODE」については→コチラをご覧ください。
※do-while構文については、→の記事をご覧ください。第39回「do-while文について」
※インクリメントについては↓をご覧ください。
インクリメントとデクリメント(Increment,Decrement)
処理実装記述その4-再注文試行が上限回数に達した場合の記述-
if(retryCount >= RETRY_LIMIT)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
}
if(retryCount >= RETRY_LIMIT)
という記述は↑再注文試行回数が#define命令で設定した定数RETRY_LIMITの数を上回っている事を意味します。
TradeServerReturnCodeDescription関数でリターンコードを獲得後、変数「errDesc」に格納→Alert関数で変数に格納された内容を発出します。
この箇所も、PositionOpen関数やSetSLTP関数とやっていることは変わりません。
処理実装記述その5:結果をPrint関数でログ出力
待機注文の修正の結果をPrint関数でログ出力します。この処理は、修正注文が成功した場合でも、失敗した場合でも行います。
select = OrderSelect(parTicket);
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print("待機注文の修正 #",parTicket,": ",
result.retcode," - ",errDesc,", Price: ",
OrderGetDouble(ORDER_PRICE_OPEN),", SL: ",request.sl,
", TP: ",request.tp,", Expiration: ",request.expiration);
処理実装記述その6:戻り値を返して終了
ChangePending関数の戻り値はbool型です。待機注文の修正に成功した場合はtrue、失敗した場合はfalseを返すよう設計しています。
if(checkCode == CHECK_RETCODE_OK)
{
Comment("待機注文 ",parTicket," 修正後の内容,",
" Price: ",OrderGetDouble(ORDER_PRICE_OPEN),
", SL: ",request.sl,", TP: ",request.tp);
//注文が成功した場合、trueを返す
return(true);
}
//注文が失敗した場合、falseを返す
else return(false);
}
まとめ
今回の記事では、待機注文を修正するメンバ関数「ChangePending関数」をOriginalCTradeクラスに追加する模様を解説しました。
ChangePending関数の宣言部分↓は以下の通りです。
//待機注文を修正する
bool ChangePending(ulong parTicket, //待機注文のオーダー番号が記述される想定
double parPrice,//約定予定価格が記述される想定
double parStop,//損切り価格(ストップロス)が記述される想定
double parProfit,//利益確定価格が記述される想定
datetime parExpiration = 0);//待機注文の有効期限が記述される想定
ChangePending関数の処理実装記述部分↓は以下の通りです。
//待機注文の内容を修正する
bool OriginalCTrade::ChangePending(ulong parTicket,double parPrice,double parStop,double parProfit,datetime parExpiration=0)
{
//インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
//各種メンバ変数への値代入
request.action = TRADE_ACTION_MODIFY;
request.order = parTicket;
request.sl = parStop;
request.tp = parProfit;
//待機注文の選択
bool select = OrderSelect(parTicket);
//parPriceに値が入力されていれば、parPriceの値をrequest.priceにセット
if(parPrice > 0) request.price = parPrice;
else request.price = OrderGetDouble(ORDER_PRICE_OPEN);
//parExpirationに値が入力されていれば、request.expirationにセット
if(parExpiration > 0)
{
request.expiration = parExpiration;
request.type_time = ORDER_TIME_SPECIFIED;
}
else request.type_time = ORDER_TIME_GTC;
// リトライ回数とリターンコードを格納する変数を宣言
int retryCount = 0;//再試行回数を格納する
int checkCode = 0;//リターンコードを格納する
do
{ //待機注文の修正注文を出す。
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("待機注文修正エラー: エラー内容 ",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);
}
select = OrderSelect(parTicket);
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print("待機注文の修正 #",parTicket,": ",
result.retcode," - ",errDesc,", Price: ",
OrderGetDouble(ORDER_PRICE_OPEN),", SL: ",request.sl,
", TP: ",request.tp,", Expiration: ",request.expiration);
if(checkCode == CHECK_RETCODE_OK)
{
Comment("待機注文 ",parTicket," 修正後の内容,",
" Price: ",OrderGetDouble(ORDER_PRICE_OPEN),
", SL: ",request.sl,", TP: ",request.tp);
//注文が成功した場合、trueを返す
return(true);
}
//注文が失敗した場合、falseを返す
else return(false);
}
今回紹介した現時点(講座記事第90回時点)でのOriginalTrade.mqhファイル全体のコード記述は↓の記事からご確認ください。
講座記事第90回時点でのOriginalTrade.mqhファイル全体のコード記述について
※講座記事第87回時点でのOriginalTrade.mqhファイル全体のコード記述は↓コチラ
講座記事第87回時点でのOriginalTrade.mqhファイル全体のコード記述について
今回は以上とさせていただきます。
最後までお読みいただきありがとうございました。
コメント