前回は、ポジション情報を取得する為の独立関数についての解説をしました。
記事では、OriginalTrade.mqhファイル内、「OriginalCPositions」クラスの下の部分に以下の独立関数を追加しました。
- PositionComment関数(ポジションに付与されているコメントを取得するオリジナル関数)
- PositionType関数(ポジションタイプを取得するオリジナル関数)
- PositionOpenTime関数(注文が出された時刻を取得するオリジナル関数)
- PositionOpenPrice関数(ポジションを持った時の価格を取得するオリジナル関数)
- PositionVolume関数(ポジションのロット数を取得するオリジナル関数)
- PositionStopLoss関数(ポジションの損切り値を取得するオリジナル関数)
- PositionTakeProfit関数(ポジションの利益確定値を取得するオリジナル関数)
- PositionProfit関数(ポジションの損益情報を取得するオリジナル関数)
- PositionMagicNumber関数(ポジションのマジックナンバーを取得するオリジナル関数)
今回は、ポジションをクローズする関数を追加していきます。
ClosePosition関数について
今回紹介・解説するClosePosition関数は独立関数ではなく、クラスに所属するメンバ関数となります。所属関数はOriginalCTradeクラス、アクセスレベルはpublicになります。
<OriginalCTradeクラスに関連した講座記事↓>
- 第71回「トレード用のオリジナルクラスを作る」
- 第72回「ポジションオープン関数を実装する」
- 第73回「Buy関数とSell関数を実装」
- 第74回「マジックナンバー・スリッページ・フィルポリシーを返す関数を実装」
ClosePosition関数の戻り値と引数構成について
OriginalCTradeクラスのpublicセクション内にClosePosition関数の宣言を行います。
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);
ClosePosition関数の戻り値と引数構成は以下の通りです。
//ポジションをクローズする
bool ClosePosition(ulong parTicket, double parVolume = 0, string parComment = NULL);
仮引数「parTicket」にはポジション番号が記述される想定です。
仮引数「parVolume」にはロット数が記述される想定です。初期値を0と定めて省略可能にしてあります。
仮引数「parComment」 には、注文時のコメントが記述される想定です。こちらも初期値をNULLにして省略可能にしてあります。
後程、処理実装記述は解説していきますが、クローズ処理が成功したらtrue、失敗したらfalseを返す構成にするので、戻り値はbool型になっています。
※これまで作ってきたOriginalCTradeクラス内のpublic関数へのリンクは↓コチラ
Buy関数、Sell関数、SetMagicNumber関数、SetDeviation関数、SetSLTP関数
ClosePosition関数の処理実装記述について
ClosePosition関数の処理実装記述は以下のようになっています。
{
//構造体インスタンスのリセット
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;
// Order loop
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);
}
処理実装記述を一つ一つ見ていきましょう
処理実装記述その1-インスタンスのリセット~各種メンバ変数への値代入-
//構造体インスタンスのリセット
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;//取引の種類を設定する
request.position = parTicket;//ポジション番号を設定する
request.deviation = deviation;//スリッページを設定する
request.type_filling = fillType;//フィルポリシーを設定する
request.magic=magicNumber;//マジックナンバーを設定する
インスタンスのリセット→ポジション番号と銘柄の取得→各種メンバ変数への値代入
という、ここまで講座記事を読み進められた方にとっては見慣れた流れになっていると思います。
や
と構造はほとんど同じですね。
取引の種類の設定は、新規にポジションを持つ時と同じくTRADE_ACTION_DEALを設定します。
買いポジションの時は売りを、売りポジションの時は買いを行使する様に後程設定します。
処理実装記述手順2 ロット数、銘柄の設定を行う。
続いてはロット数、銘柄の設定を行います。
double closeVol = 0;
long openType = WRONG_VALUE;
string symbol;
変数「closeVol」には、決済するロット数を格納するようにします。初期値は0とします。
変数「openType」には、ポジションタイプをを格納するようにします。初期値はWRONG_VALUE とします。
※WRONG_VALUEについては→コチラをご覧ください。
変数「symbol」には、を銘柄情報を格納するようにします。
//ポジションを選択出来たら
if(PositionSelectByTicket(parTicket) == true)
{
//ポジションのロット数を代入
closeVol = PositionGetDouble(POSITION_VOLUME);
//ポジションタイプを代入
openType = PositionGetInteger(POSITION_TYPE);
//ポジションの銘柄情報を代入
symbol = PositionGetString(POSITION_SYMBOL);
}
//ポジション選択できなければfalseを返して終了
else return(false);
if(PositionSelectByTicket(parTicket) == true)の条件文がtrueの場合、ポジションが無事に選択できた事を意味しますから、
変数「closeVol」にはPositionGetDouble関数の引数に定数値(POSITION_VOLUME)の結果を代入します。これで現在のポジションのロット数が格納できました。
変数「openType」にはPositionGetInteger関数の引数に定数値(POSITION_TYPE)の結果を代入します。これでポジションタイプが格納できました。
変数「symbol」にはPositionGetString関数の引数に定数値(POSITION_SYMBOL)の結果を代入します。これで銘柄情報を格納できたことになります。
else文は→if(PositionSelectByTicket(parTicket) == false)、つまりポジション選択が失敗したことを意味しますから、returnで処理を終了します。
※PositionSelectByTicket関数については→コチラをご覧ください。
request.symbol = symbol;//銘柄情報を設定する
/*指定したクローズロット数がポジションのロット数より大きいか、指定したクローズロット数が0以下の時は、
ポジションのロット数をクローズするロット数とする*/
if(parVolume > closeVol || parVolume <= 0) request.volume = closeVol;
//それ以外の場合は、指定したクローズロット数をクローズする
else request.volume = parVolume;
取得したロット数、ポジションタイプ、銘柄の情報をMqlTradeRequest構造体のメンバ変数に渡していきます。
request.symbol に変数「symbol」を代入することによって銘柄情報の設定は完了です。
request.Volume には変数「 closeVol」を代入する訳ですが、その前に
if(parVolume > closeVol || parVolume <= 0)というif文による条件をつけています。これは和訳すると
「指定したクローズロット数が保有中ポジションのロット数より大きいか、指定したクローズロット数が0以下の時は」
ということになります。
仮引数である変数「parVolume 」に不適切な値が入っている場合、自動的にポジションのロット数分を決済ロットにするよう修正する記述になります。
if(parVolume > closeVol || parVolume <= 0)の条件に合致しない場合は、変数「parVolume 」の値で注文しても問題ないことを意味するので、request.Volume には「parVolume」の値を代入します。
※論理積・論理和に関しては下記↓の記事をご参照ください。
処理実装記述3-リトライ回数とリターンコードを格納する変数を宣言する-
続いては、注文のリトライ回数とリターンコードを格納する変数を宣言します
// リトライ回数とリターンコードを格納する変数を宣言する
int retryCount = 0;
int checkCode = 0;
変数「retryCount」は、注文を出して注文エラーが発生した時、エラー内容次第では再注文すれば約定する可能性があるので、指定回数はエラーが出たときに再注文を出す回路を組み込む為に使います。
変数「checkCode」は、注文後、インスタンス「result」が受け取るリターンコードを格納するためのものです。
注文が成功したのか?、失敗したのならばその理由は何か?を確認する為に使います。
※第72回、第76回でも取り上げた回路になりますので、宜しければそちらもご参照ください。
処理実装記述4-do-while文の中に発注回路を組み込んでいく。-
PositionOpen関数やSetSLTP関数と同様に、注文エラーが発生した時に、再注文を出す回路を組み込みます。そのため、do-while文の中に発注回路を記述していきます。
※do-while構文については、→第39回「do-while文について」の記事をご覧ください。
do-while文の中その1
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);
変数「openType」 の値がPOSITION_TYPE_BUYだった場合、決済に必要な命令はORDER_TYPE_SELLということになるので、
メンバ変数request.type には ORDER_TYPE_SELLが代入されます。(買ったものを売る)
ORDER_TYPE_SELL→売り注文ということになるので、注文価格はBid価格になります。
request.PriceにはSymbolInfoDouble関数の引数に定数値SYMBOL_BIDを記述した値を代入します。
逆に変数「openType」 の値がPOSITION_TYPE_SELLの場合、決済に必要な命令はORDER_TYPE_BUYです。(売りから入ったものを買い戻す)
メンバ変数request.type には ORDER_TYPE_BUYが代入されます。
ORDER_TYPE_BUY→買い注文ということになるので、注文価格はAsk価格になります。
request.PriceにはSymbolInfoDouble関数の引数に定数値SYMBOL_ASKを記述した値を代入します。
do-while文の中その2
これでメンバ変数 の設定が終わったので、OrderSend関数で決済注文を出します。
//注文を出す
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);
OrderSend関数での発注結果を、ReturnCodeCheck関数で確認し次の行動を規定します。
↓
「約定が成功した場合」「再試行しても解決しないエラーの場合」「再試行すれば解決する可能性のあるエラーの場合」というの3つをif文 else文で条件分岐させ処理を切り分けていきます
ここはPositionOpen関数(講座記事第72回)やSetSLTP関数(講座記事第76回)と同じです。
<参考リンク>
- 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 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);//約定価格
これも、出力するログの内容が変わっているだけで、やっていることはPositionOpen関数やSetSLTP関数と同じです。
処理実装記述7 -戻り値を返す-
最後に戻り値を返す記述をします。↓
if(checkCode == CHECK_RETCODE_OK)
{
Comment(posType," ポジションをクローズしました。 ",symbol," at ",result.price);
return(true);
}
else return(false);
}
変数「checkCode」にはReturnCodeCheck関数で取得したリターンコードが格納されています。
if(checkCode == CHECK_RETCODE_OK)の条件文がtrueだった場合、無事にクローズ処理が行われたことを意味するのでreturnにて、trueを返します。
checkCode == CHECK_RETCODE_OK以外だった場合、クローズ処理に問題が発生していることを意味するのでreturnにてfalseを返します。
記述は以上になります。
まとめ:コード全体の記述内容
現時点での講座記事で作ってきた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);
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::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関数
今回は以上とさせていただきます。
最後までお読みいただきありがとうございました。
コメント