前回は、ポジションをクローズする為のクラス関数を追加・実装する過程を解説しました。
OriginalCTradeクラスに、ポジションを決済する用の関数であるClosePosition関数というアクセスレベル=publicのメンバ関数を作る過程を解説しました。
今回からしばらくは、待機注文(Pending Order)に関する内容が続くかと思います。 待機注文を基軸とした売買ロジックを構築するのに必要なクラス/関数を実装・解説していく予定ですが、今回はその第1弾です。
今回の記事を読んで理解を深めることによって、EA(自動売買プログラム)開発において待機注文処理を実装しようとする際に、容易に待機注文を実装できるようになります。
待機注文(Pending Order)とは?
待機注文は、相場の現在値でポジションをオープンさせる成行注文(Market Order)とは異なり、
特定の価格に到達する事を想定し、現在値から一定数離れた価格で注文を出す方式となります。
※待機注文を使ったコード記述については、講座記事とは別に以下のような記事もありますので、参考にしてください↓
また、講座記事内でも待機注文に関しては今回初めて言及する、という訳ではありません。
今回は待機注文専用のクラスを作っていくわけですが、クラスを使わない、待機注文のプレーンな記述については↓の記事群で解説をしていますので参考にしていただければと思います。
また講座記事第60回内の以下↓のセクションも待機注文のコード記述をする際の手助けになります。
- TRADE_ACTION_PENDING→新規の待機注文を出す
- TRADE_ACTION_MODIFY→待機注文のパラメータを修正
- TRADE_ACTION_REMOVE→待機注文を削除する
- .order →待機注文のチケット番号を入力・設定する。
- ORDER_TYPE_BUY_LIMITは買い指値注文のこと。
- ORDER_TYPE_SELL_LIMITは売り指値注文のこと。
- ORDER_TYPE_BUY_STOPは買い逆指値注文のこと。
- ORDER_TYPE_SELL_LIMITは売り指値注文のこと。
- ORDER_TYPE_BUY_STOP_LIMITは買いのストップリミット待機注文のこと。
- ORDER_TYPE_SELL_STOP_LIMITは売りのストップリミット待機注文のこと。
待機注文にも、リミットオーダー、ストップオーダー、ストップリミットオーダーの3種類があります。
これも、今回の記事で初めて言及したわけではありませんが、軽くおさらいしておきましょう。
リミットオーダーとは?→現在値より有利な価格を指定する待機注文方式
リミットオーダーは、現在値より有利な価格を指定する待機注文方式です。
リミットオーダーはポジションがオープンした後、相場が反転することを想定した戦略に用いられます。
ストップオーダーとは?→現在値より不利な価格を指定する待機注文方式
ストップオーダーとは、現在値より不利な価格を指定する待機注文方式です。
ストップオーダーはポジションがオープンした後、相場がそのままトレンドを継続することを想定した戦略に用いられます。
ストップリミットオーダーとは?
ストップリミットオーダーはMT4/MQL4にはない、MT5/MQL5から実装された待機注文方式です。
ストップリミットオーダーの場合、ストップとリミットの両方の価格を指定します。相場がブレイクアウトする形でストッププライスに到達した後、価格がいったん押し目を作ることを想定して最終的にリミット価格でポジションを持ちます。
詳しくは↓
.stoplimit →stoplimit待機注文のリミット価格を設定する
をご覧ください。
待機注文専用の関数を追加する
待機注文に関する復習が終わったところで、早速待機注文専用関数をいくつか作っていきます。
既に書いてきたように、待機注文にも、リミットオーダー、ストップオーダー、ストップリミットオーダーがあり、それぞれに売りと買いの種類が存在します。
それを踏まえて、protectedセクションに待機注文自体を行う関数を、publicセクションに待機注文の種類に合わせて注文を行える関数を作っていきたいと思います。
ちょうど
第72回「ポジションオープン関数を実装する」で紹介したPositionOpen関数と
第73回「Buy関数とSell関数を実装」で紹介したBuy関数&Sell関数の関係と同じですね。
PositionOpen関数とBuy関数&Sell関数は成行注文の時に利用する関数ですが、待機注文の時も同じような考えで関数を作っていきます。
追加するのは、今まで関数やクラスを追加してきたOriginalTrade.mqhファイルの、OriginalCTradeクラスです。
PendingOpen関数について
まずはアクセスレベル=protectedセクションに待機注文自体を行う関数の宣言を行います。
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);
//待機注文関数の宣言
bool PendingOpen(string parSymbol, ENUM_ORDER_TYPE parType, double parVolume,
double parPrice, double parStop = 0, double parProfit = 0,
double parStopLimit = 0, datetime parExpiration = 0, string parComment = NULL);
講座記事 第72回で作ったPositionOpen関数の宣言箇所の下に、新たな関数の宣言を行います。
関数名をPendingOpen関数としました。
PendingOpen関数の戻り値と引数構成について
PendingOpen関数の戻り値と引数構成については以下の通りです。
bool PendingOpen(string parSymbol, ENUM_ORDER_TYPE parType, double parVolume,
double parPrice, double parStop = 0, double parProfit = 0,
double parStopLimit = 0, datetime parExpiration = 0, string parComment = NULL);
仮引数parSymbol
仮引数parSymbolには、取引銘柄が記述される想定です。
仮引数parType
仮引数parTypeには、取引タイプが記述される想定です。
仮引数parVolume
仮引数parVolumeには、取引ロットが記述される想定です。
仮引数parPrice
仮引数parPriceには、待機注文における約定価格 が記述される想定です。(ただし、ストップリミットオーダーの場合、希望するストップ注文価格が記述される想定です。詳しいことは↓
・「.stoplimit →stoplimit待機注文のリミット価格を設定する」
・「ORDER_TYPE_BUY_STOP_LIMITは買いのストップリミット待機注文のこと」
をご参照ください)
仮引数parStop
仮引数parStopには、損切り価格値が記述される想定です。初期値を0にして省略可能にしてあります。
仮引数parProfit
仮引数parProfitには、利益確定価格値が記述される想定です。初期値を0にして省略可能にしてあります。
仮引数parStopLimit
仮引数parStopLimitには、ストップリミットオーダーの約定予定価格値が記述される想定です。初期値を0にして省略可能にしてあります。ストップリミットオーダーについては、「.stoplimit →stoplimit待機注文のリミット価格を設定する」などの関連リンクでご確認ください)
仮引数parExpiration
仮引数parExpirationには、待機注文の有効期限が記述される想定です。初期値を0にして省略可能にしてあります。
※関連リンク→第16回「データ型その6 datetime型」、「.type_time→注文の有効期限を設定する」、「.expiration→有効期限の時刻を設定する」)
仮引数parComment
仮引数parCommentには、注文時のコメントが記述される想定です。初期値をNULLにして省略可能にしてあります。
PendingOpen関数の戻り値はbool型
PendingOpen関数の処理実装記述についてはこの後解説していきますが、最終的に注文のリターンコードをがCHECK_RETCODE_OKならtrue、それ以外ならfalseを返すよう設計していくので、戻り値のデータ型はbool型になります。
PendingOpen関数の処理実装記述について
PendingOpen関数の処理実装記述については以下の通りです。(実装記述の場所は、どこでも良いのですが、関数の役割からして、PositionOpen関数とClosePosition関数の間ぐらいが良いかと思います)
//+------------------------------------------------------------------+
//待機注文を実行する関数
bool OriginalCTrade::PendingOpen(string parSymbol, ENUM_ORDER_TYPE parType, double parVolume,
double parPrice, double parStop = 0, double parProfit = 0,
double parStopLimit = 0, datetime parExpiration = 0, string parComment = NULL)
{ //構造体インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_PENDING;//取引の種類を設定する
request.symbol = parSymbol;//取引銘柄を設定する
request.type = parType;//取引タイプを設定する
request.sl = parStop;//損切り価格を設定する
request.tp = parProfit;//利益確定価格を設定する
request.comment = parComment;//コメントを設定する
request.price = parPrice;//約定予定価格を設定する
request.volume = parVolume;//ロット数を設定する
request.stoplimit = parStopLimit;//ストップリミットを設定する
request.deviation = deviation;//スリッページを設定する
request.type_filling = fillType;//フィルポリシーを設定する
request.magic = magicNumber;//マジックナンバーを設定する
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 に格納する
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++;
}
}
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,", Price: ",request.price,
", Bid: ",SymbolInfoDouble(parSymbol,SYMBOL_BID),
", Ask: ",SymbolInfoDouble(parSymbol,SYMBOL_ASK),
", SL: ",request.sl,", TP: ",request.tp,
", Stop Limit: ",request.stoplimit,
", 有効期限: ",request.expiration);
if(checkCode == CHECK_RETCODE_OK)
{
Comment(orderType," 待機注文を出しました ",request.price," on ",parSymbol);
return(true);
}
else return(false);
}//PendingOpen
処理実装記述を1つ1つ見ていきましょう
処理実装記述その1-インスタンスのリセット~各種メンバ変数への値代入-
//構造体インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_PENDING;//取引の種類を設定する
request.symbol = parSymbol;//取引銘柄を設定する
request.type = parType;//取引タイプを設定する
request.sl = parStop;//損切り価格を設定する
request.tp = parProfit;//利益確定価格を設定する
request.comment = parComment;//コメントを設定する
request.price = parPrice;//約定予定価格を設定する
request.volume = parVolume;//ロット数を設定する
request.stoplimit = parStopLimit;//ストップリミットを設定する
request.deviation = deviation;//スリッページを設定する
request.type_filling = fillType;//フィルポリシーを設定する
request.magic = magicNumber;//マジックナンバーを設定する
インスタンスのリセット→MqlTradeRequest構造体の各種メンバ変数への値代入という流れは、
第72回「ポジションオープン関数を実装する」、第76回「SLとTPを設定する関数をクラスに追加する」、第85回「ポジションを決済する関数を作る」 で作った関数群(PositionOpen関数、SetSLTP関数、ClosePosition関数)と同じ流れの記述となっています。
if(parExpiration > 0)//有効期限が設定されている時
{
request.expiration = parExpiration;
request.type_time = ORDER_TIME_SPECIFIED;
}
//有効期限が設定されていない時
else request.type_time = ORDER_TIME_GTC;
処理実装記述その2-待機注文の有効期限に関する記述を行う-
if(parExpiration > 0)
↑の記述は仮引数「parExpiration」に有効期限が明示されていることを意味しますから、メンバ変数.expirationに仮引数「parExpiration」の値を代入します。
メンバ変数.expirationに有効期限の設定がされているので、メンバ変数 .type_timeには定数値「ORDER_TIME_SPECIFIED」を代入します。
elseの場合、仮引数「parExpiration」に有効期限が明示されていないことを意味しますから、メンバ変数.type_timeには定数値「ORDER_TIME_GTC」を代入します。
<有効期限の関連リンク>
.type_time、.type 、.expiration、ORDER_TIME_GTC、ORDER_TIME_DAY、ORDER_TIME_SPECIFIED
処理実装記述3-リトライ回数とリターンコードを格納する変数を宣言する-
↓の流れも、もう見慣れてきたかもしれませんが、注文のリトライ回数とリターンコードを格納する変数を宣言します。※第72回、第76回、第85回でも取り上げた回路となっています。
// リトライ回数とリターンコード用の変数を宣言
int retryCount = 0;
int checkCode = 0;
変数「retryCount」は、注文を出して注文エラーが発生した時、エラー内容次第では再注文すれば約定する可能性があるので、指定回数はエラーが出たときに再注文を出す回路を組み込む為に使います。
変数「checkCode」は、注文後、MqlTradeResult構造体のインスタンス「result」が受け取るリターンコードを格納するためのものです。注文が成功したのか?、失敗したのならばその理由は何か? を確認する為に使います。
処理実装記述4-do-while文の中に発注回路を組み込んでいく-
ここも、
ositionOpen関数やSetSLTP関数、ClosePosition関数と同様に、注文エラーが発生した時に、再注文を出す回路を組み込みます。そのため、do-while文の中に発注回路を記述していきます。
※do-while構文については、→第39回「do-while文について」の記事をご覧ください。
do
{ //注文を出す
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++;
}
}
while(retryCount < RETRY_LIMIT);
今回の記事の「処理実装記述その2」 にてメンバ変数 の設定が終わっているので、OrderSend関数で待機注文を出します。
その後は、これもお決まりの流れですが、OrderSend関数での発注結果を、ReturnCodeCheck関数で確認し次の行動を規定します。発注結果は
1「約定が成功した場合」
2「再試行しても解決しないエラーの場合」
3「再試行すれば解決する可能性のあるエラーの場合」というの3つの場合を想定し、
<参考リンク>
- define命令で、再注文の上限回数を設定する。
- ReturnCodeCheck関数
- ErrorDescription.mqhファイルをインクルードする
- リターンコードのチェックをする関数を作成する
- TradeServerReturnCodeDescription関数
- 定数「RETRY_MILLISECONDS」
- 定数「RETRY_LIMIT」
- 「ENUM_CHECK_RETCODE」
- インクリメントとデクリメント
処理実装記述5-再注文試行が上限回数に達した場合の記述-
//再注文上限回数に達した時
if(retryCount >= RETRY_LIMIT)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
}
if(retryCount >= RETRY_LIMIT)
という記述は↑決済注文の試行回数が#define命令で設定した定数「 RETRY_LIMIT」の数を上回っている事を意味します。
TradeServerReturnCodeDescription関数でリターンコードを獲得後、変数「errDesc」に格納→Alert関数で変数に格納された内容を発出します。
処理実装記述6 -待機注文の結果をPrint関数でログ出力する-
待機注文の発注結果が、成功なのか失敗なのかを含めた注文結果をPrint関数でログ出力します。
//ログ出力用のオーダータイプ格納変数
string orderType = ConfirmOrderType(parType);
//エラーコードの格納
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print("注文種類 ",orderType,
" オーダー番号 #",result.order,": ",result.retcode," - ",errDesc,
", ロット数: ",result.volume,", Price: ",request.price,
", Bid: ",SymbolInfoDouble(parSymbol,SYMBOL_BID),
", Ask: ",SymbolInfoDouble(parSymbol,SYMBOL_ASK),
", SL: ",request.sl,", TP: ",request.tp,
", Stop Limit: ",request.stoplimit,
", 有効期限: ",request.expiration);
string型の変数「orderType」 には、第72回で作ったオリジナル関数のConfirmOrderType関数を用いて値を代入します。ログ出力する内容は待機注文に合わせたものになっています。
オーダー番号や、Stop Limit、有効期限などの情報をログ出力しているのが特徴的と言えるでしょうか?
<参考リンク>
処理実装記述7 -戻り値を返す-
最後に戻り値を返す記述を行います。↓
if(checkCode == CHECK_RETCODE_OK)
{
Comment(orderType," 待機注文を出しました ",request.price," on ",parSymbol);
return(true);
}
else return(false);
}//PendingOpen
変数「checkCode」にはReturnCodeCheck関数で取得したリターンコードが格納されています。
if(checkCode == CHECK_RETCODE_OK)の条件文がtrueだった場合、無事に待機注文処理が行われたことを意味するので、returnにて、trueを返します。
checkCode == CHECK_RETCODE_OK以外だった場合、待機注文処理に何らかの問題が発生していることを意味するのでelse文の後に、returnにてfalseを返します。
PendingOpen関数の処理実装記述は以上になります。
まとめ:コード全体の記述内容
今回は、OriginalCTradeクラスにアクセスレベル=protectedのPendingOpen関数を追加し、そのコード内容を解説しました。PendingOpen関数は次回以降の講座記事で作る、アクセスレベル=publicの待機注文系関数群からアクセスを受けて、最終的な待機注文処理を担う関数となっています。
現時点での講座記事で作ってきたOriginalTrade.mqhファイル内のコード全体の記述は↓の通りです。
※OriginalTrade.mqhファイルには、現在OriginalCTradeクラスとOriginalCPositionsクラス、および独立関数が記述されています。
//+------------------------------------------------------------------+
//| 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);
//待機注文関数
bool PendingOpen(string parSymbol, ENUM_ORDER_TYPE parType, double parVolume,
double parPrice, double parStop = 0, double parProfit = 0,
double parStopLimit = 0, datetime parExpiration = 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 SetMagicNumber(ulong parMagic);
// スリッページを設定する
void SetDeviation(ulong parDeviation);
// フィルポリシーを設定する
void SetFillType(ENUM_ORDER_TYPE_FILLING parFill);
//ストップロスとテイクプロフィットを設定する
bool SetSLTP(ulong parTicket, double parStop, double parProfit = 0);
//ポジションをクローズする
bool ClosePosition(ulong parTicket, double parVolume = 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);
//再注文上限回数に達した時
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
//+------------------------------------------------------------------+
//待機注文を実行する関数
bool OriginalCTrade::PendingOpen(string parSymbol, ENUM_ORDER_TYPE parType, double parVolume,
double parPrice, double parStop = 0, double parProfit = 0,
double parStopLimit = 0, datetime parExpiration = 0, string parComment = NULL)
{ //構造体インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_PENDING;//取引の種類を設定する
request.symbol = parSymbol;//取引銘柄を設定する
request.type = parType;//取引タイプを設定する
request.sl = parStop;//損切り価格を設定する
request.tp = parProfit;//利益確定価格を設定する
request.comment = parComment;//コメントを設定する
request.price = parPrice;//約定予定価格を設定する
request.volume = parVolume;//ロット数を設定する
request.stoplimit = parStopLimit;//ストップリミットを設定する
request.deviation = deviation;//スリッページを設定する
request.type_filling = fillType;//フィルポリシーを設定する
request.magic = magicNumber;//マジックナンバーを設定する
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 に格納する
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++;
}
}
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,", Price: ",request.price,
", Bid: ",SymbolInfoDouble(parSymbol,SYMBOL_BID),
", Ask: ",SymbolInfoDouble(parSymbol,SYMBOL_ASK),
", SL: ",request.sl,", TP: ",request.tp,
", Stop Limit: ",request.stoplimit,
", 有効期限: ",request.expiration);
if(checkCode == CHECK_RETCODE_OK)
{
Comment(orderType," 待機注文を出しました ",request.price," on ",parSymbol);
return(true);
}
else return(false);
}//PendingOpen
//+------------------------------------------------------------------+
// ポジションをクローズする関数
bool OriginalCTrade::ClosePosition(ulong parTicket, double parVolume=0.000000, string parPComment=NULL)
{
//構造体インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;//取引の種類を設定する
request.position = parTicket;//ポジション番号を設定する
request.deviation = deviation;//スリッページを設定する
request.type_filling = fillType;//フィルポリシーを設定する
request.magic=magicNumber;//マジックナンバーを設定する
double closeVol = 0;
long openType = WRONG_VALUE;
string symbol;
//ポジションを選択出来たら
if(PositionSelectByTicket(parTicket) == true)
{
//ポジションのロット数を代入
closeVol = PositionGetDouble(POSITION_VOLUME);
//ポジションタイプを代入
openType = PositionGetInteger(POSITION_TYPE);
//ポジションの銘柄情報を代入
symbol = PositionGetString(POSITION_SYMBOL);
}
//ポジション選択できなければfalseを返して終了
else return(false);
request.symbol = symbol;//銘柄情報を設定する
/*指定したクローズロット数がポジションのロット数より大きいか、指定したクローズロット数が0以下の時は、
ポジションのロット数をクローズするロット数とする*/
if(parVolume > closeVol || parVolume <= 0) request.volume = closeVol;
//それ以外の場合は、指定したクローズロット数をクローズする
else request.volume = parVolume;
// リトライ回数とリターンコードを格納する変数を宣言する
int retryCount = 0;
int checkCode = 0;
do
{ //ポジションが買いの場合の注文価格と注文タイプの設定
if(openType == POSITION_TYPE_BUY)
{
request.type = ORDER_TYPE_SELL;
request.price = SymbolInfoDouble(symbol,SYMBOL_BID);
}
//ポジションが売りの場合の注文価格と注文タイプの設定
else if(openType == POSITION_TYPE_SELL)
{
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(symbol,SYMBOL_ASK);
}
//注文を出す
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++;
}
}
while(retryCount < RETRY_LIMIT);
//再注文上限回数に達した時
if(retryCount >= RETRY_LIMIT)
{ //エラーコードの格納
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert("再注文上限回数に達しました: エラー内容 ",result.retcode," - ",errDesc);
}
//ログ出力用のポジションタイプ格納変数
string posType;
if(openType == POSITION_TYPE_BUY) posType = "Buy";
else if(openType == POSITION_TYPE_SELL) posType = "Sell";
//エラーコードの格納
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print("Close ",posType,//ポジションタイプ
" position #",parTicket,//ポジション番号
": ",result.retcode," - ",errDesc,//リターンコードとエラーコード
", Volume: ",result.volume,//約定ロット数
", Price: ",result.price);//約定価格
if(checkCode == CHECK_RETCODE_OK)
{
Comment(posType," ポジションをクローズしました。 ",symbol," at ",result.price);
return(true);
}
else return(false);
}
//-------------------------------------------------------------------------------
// リターンコードによって、次の行動を決めるための列挙型
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);
}
}
//ポジション情報を管理するクラス------------------------------
class OriginalCPositions
{
protected:
ulong BuyPosNum[];
ulong SellPosNum[];
ulong PosNum[];
int BuyPosCount;
int SellPosCount;
int TotalPosCount;
void GetPosInfo(ulong parMagicNum = 0);
int ChangeArraySize(ulong &parArray[]);
public:
int GetBuyPosCount(ulong parMagicNum);
int GetSellPosCount(ulong parMagicNum);
int GetTotalPosCount(ulong parMagicNum);
void GetBuyPosNum(ulong parMagicNum,ulong &parTickets[]);
void GetSellPosNum(ulong parMagicNum,ulong &parTickets[]);
void GetTotalPosNum(ulong parMagicNum,ulong &parTickets[]);
};
// ポジション情報を取得・振り分けする関数
void OriginalCPositions::GetPosInfo(ulong parMagicNumber = 0)
{
//ポジション数を格納する変数の初期化
BuyPosCount = 0;
SellPosCount = 0;
TotalPosCount = 0;
//ポジション番号格納配列の初期化とリサイズ
ArrayResize(BuyPosNum, 1);
ArrayInitialize(BuyPosNum, 0);
ArrayResize(SellPosNum, 1);
ArrayInitialize(SellPosNum, 0);
ArrayResize(PosNum, 1);
ArrayInitialize(PosNum, 0);
for(int i = 0; i < PositionsTotal(); i++)//未決済ポジション内を一番古いポジからチェック
{
ulong posNumber = PositionGetTicket(i);//ポジションプール内からインデックス順にポジション番号を取得
PositionSelectByTicket(posNumber);//ポジション選択
if(PositionGetInteger(POSITION_MAGIC) != parMagicNumber && parMagicNumber > 0) continue;
//当該EAからの注文でなければスキップ
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{ //買いポジションの数を1増加させる
BuyPosCount++;
int arrayIndex = ChangeArraySize(BuyPosNum);//配列をリサイズし、インデックスを戻り値として返す
BuyPosNum[arrayIndex] = PositionGetInteger(POSITION_TICKET);//Buy ポジション番号情報を配列に格納
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{ ////売りポジションの数を1増加させる
SellPosCount++;
int arrayIndex = ChangeArraySize(SellPosNum);//配列をリサイズし、インデックスを戻り値として返す
SellPosNum[arrayIndex] = PositionGetInteger(POSITION_TICKET);//Sell ポジション番号情報を配列に格納
}
//全体のポジション数を1増加させる
TotalPosCount++;
int arrayIndex = ChangeArraySize(PosNum);//配列をリサイズし、インデックスを戻り値として返す
PosNum[arrayIndex] = PositionGetInteger(POSITION_TICKET);//全てのポジションの ポジション番号情報を配列に格納
}// for(int i = 0; i < PositionsTotal(); i++)の終端
//各種ポジカウント変数とポジション番号格納配列に必要な情報を振り分けて作業を終える
}
//配列サイズを変更し、配列インデックスを返す関数
int OriginalCPositions::ChangeArraySize(ulong &parArray[])
{
//配列の新しいサイズを計算
int newSize = ArraySize(parArray) + 1;
//リサイズに失敗した場合に、ログ出力する
if(ArrayResize(parArray, newSize) == -1)
{
//リサイズに失敗した場合のエラーハンドリング
Print("配列のリサイズに失敗しました.");
return -1; // 適切なエラーコードを返す
}
//配列の最後のインデックスを計算
int arrayIndex = newSize - 1;
//変数「arrayIndex」の値を戻り値として返す
return arrayIndex;
}
//買いポジションの数を取得する関数
int OriginalCPositions::GetBuyPosCount(ulong parMagicNum)
{
GetPosInfo(parMagicNum);//各種ポジカウント変数とポジション番号格納配列に、必要な情報を振り分けるprotected関数
//protected変数の「BuyPosCount」を呼び出し、戻り値として返す
return BuyPosCount;
}
//売りポジションの数を取得する関数
int OriginalCPositions::GetSellPosCount(ulong parMagicNum)
{
GetPosInfo(parMagicNum);//各種ポジカウント変数とポジション番号格納配列に、必要な情報を振り分けるprotected関数
//protected変数の「BuyPosCount」を呼び出し、戻り値として返す
return SellPosCount;
}
//買いと売りの合計ポジションの数を取得する関数
int OriginalCPositions::GetTotalPosCount(ulong parMagicNum)
{
GetPosInfo(parMagicNum);//各種ポジカウント変数とポジション番号格納配列に、必要な情報を振り分けるprotected関数
//protected変数の「BuyPosCount」を呼び出し、戻り値として返す
return TotalPosCount;
}
void OriginalCPositions::GetBuyPosNum(ulong parMagicNum,ulong &parTickets[])
{
GetPosInfo(parMagicNum);//各種ポジカウント変数とポジション番号格納配列に、必要な情報を振り分けるprotected関数
ArrayCopy(parTickets,BuyPosNum);
return;
}
void OriginalCPositions::GetSellPosNum(ulong parMagicNum,ulong &parTickets[])
{
GetPosInfo(parMagicNum);//各種ポジカウント変数とポジション番号格納配列に、必要な情報を振り分けるprotected関数
ArrayCopy(parTickets,SellPosNum);
return;
}
void OriginalCPositions::GetTotalPosNum(ulong parMagicNum,ulong &parTickets[])
{
GetPosInfo(parMagicNum);//各種ポジカウント変数とポジション番号格納配列に、必要な情報を振り分けるprotected関数
ArrayCopy(parTickets,PosNum);
return;
}
//+------------------------------------------------------------------+
//|ポジション情報を取得する関数群 |
//+------------------------------------------------------------------+
string PositionComment(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、コメント情報を取得して戻り値として返す
if(select == true) return(PositionGetString(POSITION_COMMENT));
//選択ができなかった場合は、事前定義された値を返す
else return(NULL);
}
long PositionType(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、ポジションタイプを取得して戻り値として返す
if(select == true) return(PositionGetInteger(POSITION_TYPE));
else return(WRONG_VALUE);
}
long PositionOpenTime(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、ポジションタイプを取得して戻り値として返す
if(select == true) return(PositionGetInteger(POSITION_TIME));
else return(WRONG_VALUE);
}
double PositionOpenPrice(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、ポジションを持った時の価格を取得して戻り値として返す
if(select == true) return(PositionGetDouble(POSITION_PRICE_OPEN));
else return(WRONG_VALUE);
}
double PositionVolume(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、ロット数を取得して戻り値として返す
if(select == true) return(PositionGetDouble(POSITION_VOLUME));
else return(WRONG_VALUE);
}
double PositionStopLoss(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、損切り値を取得して戻り値として返す
if(select == true) return(PositionGetDouble(POSITION_SL));
else return(WRONG_VALUE);
}
double PositionTakeProfit(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、利益確定値を取得して戻り値として返す
if(select == true) return(PositionGetDouble(POSITION_TP));
else return(WRONG_VALUE);
}
double PositionProfit(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、損益情報を取得して戻り値として返す
if(select == true) return(PositionGetDouble(POSITION_PROFIT));
else return(WRONG_VALUE);
}
long PositionMagicNumber(ulong parTicket = 0)
{//ポジション選択可否を格納する変数
bool select = false;
//ポジション番号を選択する
if(parTicket > 0) select = PositionSelectByTicket(parTicket);
//ポジション選択ができていれば、マジックナンバーを取得して戻り値として返す
if(select == true) return(PositionGetInteger(POSITION_MAGIC));
else return(NULL);
}
<参照>
DoubleToString関数
今回は以上とさせていただきます。
最後までお読みいただきありがとうございました<m(__)m>
コメント