未決済注文 P157
未決注文は指定された価格で取引を開始するリクエストです。要求された取引価格は、ストップレベルによって決定されるように、現在のビッドまたはアスク価格から最小距離、離れている必要があります。指値注文の始値に達すると取引が成立し、取引は現在のポジションの一部になります。
注文価格と出来高に加えて、オプションのストップロス価格、テイクプロフィット価格、注文の有効期限を指定できます。指値注文にはいくつかの有効期限タイプがあります。GTC(Good Til Canceled)は、注文が期限切れにならないことを意味します。「Today」というのは、注文は取引日の終わりまで有効期限があることを意味します。指定では、トレーダーは有効期限を指定する必要があります。指定された日時に、注文は自動的に期限切れになります。
指値注文には、ストップ、リミット、ストップリミットの3種類があります。買い注文タイプと売り注文タイプを合わせると、計6種類の指値注文があります。ストップ注文とリミット注文は、注文価格が現在の価格に対してどこにあるかが異なります。ストップリミット注文タイプは、この2つを組み合わせたものです。
買いストップ注文は、現在の価格より上に出されます。アスク価格が注文開始価格以上の場合、約定が成立します。売りストップ注文は、現在の価格より下に配置されます。取引は、ビッド価格が注文開始価格以下の場合に成立します。ストップ注文は、価格が利益の方向に働き続けることを期待して配置されます。
買い指値注文は、現在の価格より下に配置されます。アスク価格が注文開始価格以下の場合、約定が成立します。売り注文は、現在の価格より上に出されます。取引は、ビッド価格が注文開始価格以上の場合に成立します。指値注文は、価格が指値注文価格の近くで反転し、利益の方向に続くことを期待して配置されます。
ストップリミット注文は、ストップ注文価格に達した時に指値注文を出します。これには、トレーダーがストップ注文価格と指値注文価格の2つの価格を入力する必要があります。買いストップリミット注文は、アスク価格がストップ注文価格以上の場合、現在の価格を下回る買いリミット注文を出します。売りストップリミット注文は、ビッド価格がストップ注文価格以下の場合に、現在の価格を上回る売りリミット注文を出します。
売り注文は買いポジションを決済するために使用でき、逆もまた同様であるため、未決注文をストップロスとして使用し、利益注文を受けることが可能です。ポジションに置かれたストップロスまたはテイクプロフィット価格は、価格に達するとポジション全体を決済します。ただし、未決注文は、未決注文がトリガーされたときに、ポジションの一部を決済したり、ポジションを反転されたりするために使用できます。
たとえば、1.34500でEURUSDの1ロットの買いポジションがオープンしています。500ポイント下の1.34000で売りストップ注文を出すと、この価格でポジションの全部または一部を決済できます。1.34000で2ロットの売りストップ注文を出すと、ポジションは実質的に1ロットのネットロングポジションから1ロットのネットショートポジションに反転します。
指値注文を使用して、収益性の高いポジションからスケールアウトできます。上記の例を使用すると、1.35000と1.35500でそれぞれ0.5ロットの売り指値注文を出すことができ、それぞれ500ポイントと1000ポイントでポジションからスケールアウトできます。ポジションのスケールアウトについては、この章の後半で詳しく説明します。
OpenPending()関数 P158
成行注文の場合と同じように、指値注文を出す関数を作成します。第10章で説明したのと同じ原則がここにも適用されます。CTradeクラス宣言のOpenPending()関数宣言は次のとおりです。
Class CTrade
{
private:
MqlTradeRequest request;
bool OpenPrice(string pSymbol, ENUM_ORDER_TYPE pType, double pVolume,
double pPrice, double pStop = 0, double pProfit = 0, double pStopLimit = 0,
datetime pExpiration = 0, string pComment = NULL);
public:
MqlTradeResult result;
};
OpenPending()関数には、2つの追加パラメータがあります。pStopLimitは注文のストップリミット価格で、pExpirationは注文の有効期限です。どちらのパラメータもオプションで、デフォルト値は0です。他のパラメータは、98ページで説明したOpenPendingパラメータと同じです。アクセスするパブリック関数を作成するため、OpenPending()関数をprivateとして宣言しました。
OpenPending()関数の本体を見てみましょう。まず、リクエストオブジェクト変数に関数パラメータを入力します。
bool CTrade::OpenPending(string pSymbol, ENUM_ORDER_TYPE pType, double pVolme,
double pPrice, double pStop = 0, double pProfit = 0, double pStopLimit – 0,
datetime pExpriation = 0, string pComment = NULL)
{
request.action = TRADE_ACTION_PENDING;
request.symbol = pSymbol;
request.type = pType;
request.sl = pStop;
request.tp = pProfit;
request.comment = pComment;
request.price = pPrice;
request.volume = pVolume;
request.stopLimit = pStopLimit;
TRADE_ACTION_PENDING定数は、未決注文を出していることを示します。残りのリクエスト変数には、関数に渡されるパラメータが割り当てられます。次に、有効期限がある場合はそれを決定する必要があります。
if(pExpirtion > 0)
{
request.expiration = pExpiration;
request.type_time = ORDER_TIME_SPECIFIED;
}
else request.type_time = ORDER_TIME_GTC;
pExpirationパラメータが指定されている場合、pExperationの値がrequest.expiration変数に割り当てられ、request.type_time変数がORDER_TIME_SPECIFIEDに設定されます。これは有効期限が指定されていることを示します。pExpirationが指定されていないか、値が0の場合、request.type_timeはORDER_TIME_GTCに設定され、注文が期限切れにならないことを示します。
次に、指値注文を出します。エラー処理コードとエラー時の再試行を含む発注コードは次のとおりです。
//注文ループ
int retryCount = 0;
int checkCode = 0;
do
{
OrderSend(request, result);
checkCode = CheckReturnCode(result.retcode);
if(checkCode == CHECK_RETCODE_OK)break;
else if(checkCode == CHECK_RETCODE_ERROR)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert(“Open pending order: Error ”,result.retcode,” – “, errDesc);
break;
}
else
{
Print(“サーバーエラーが検出されました。再試行しています…。”);
Sleep(RETRY_DELAY);
retryCount++;
}
}
while(retryCount < MAX_RETRIES);
これは、OpenPosition()関数のコードと似ていますが、現在の売値を確認する必要がない点が異なります。OpenPending()関数の残りのコードは次のとおりです。
if(retryCount >= MAX_RETRIES)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert(“Max retries exceeded: Error ”,result.retcode,” – “, errDesc);
}
string orderType = CheckOrderType(pType);
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print(“Open ”, orderType,” order #”,result.order,”: “,result.retcode,” – “, errDesc,
“,Volume: “,result.volume,”, Price: “,request.price,
“, Bid: “,SymbolInfoDouble(pSymbol, SYMBOL_BID),
“, Ask: “,SymbolInfoDouble(pSymbol, SYMBOL_ASK),
“,SL: “, request.sl,”, TP: “,request.tp,”, Stop Limit: “,request.stopLimit,
“, Expiration: “,request.expiration);
if(checkCode == CHECK_RETCODE_OK)
{
Comment(orderType,” order opened at “, request.price,” on “,pSymbol);
return(true);
}
else return(false);
以前と同様に、retryCountがMAX_RETRIESを超えた場合、関数はエラーを表示します。注文タイプの文字列の説明はCheckOrderType()関数を使用して取得され、戻りコードの説明はTradeServerReturnCodeDescription()関数を使用して取得されます。
次に、注文に関する情報をログに出力します。結果変数には指値注文に関する多くの情報が含まれていないため、リクエスト変数と結果変数の組み合わせを使用します。SymbolInfoDouble()関数は、現在の買値と売値を取得するために使用されます。最後に、注文が成功した場合はtureの値を返し、失敗した場合はfalseを返します。
\MQL5\Include\Mql5Book\Trade.mqhファイルでOpenPending()関数全体を表示できます。指値注文は、アカウントの種類に関係なく同じように機能することに注意してください。前の章とは異なり、ヘッジアカウントの保留中の注文を配置および管理するための個別の関数を記述する必要はありません。
OpenPending()関数の使用 P160
OpenPosition()関数で行ったように、いくつかのヘルパー関数を作成して、プライベートOpenPending()関数にアクセスします。各関数は、指定されたタイプの注文を開きます。
class CTrade
{
public:
bool BuyStop(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, datetime pExpiration=0, string pComment=NULL);
bool SellStop(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, datetime pExpiration=0, string pComment=NULL);
bool BuyLimitStop(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, datetime pExpiration=0, string pComment=NULL);
bool SellLimitStop(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, datetime pExpiration=0, string pComment=NULL);
bool BuyStopLimit(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, datetime pExpiration=0, string pComment=NULL);
bool SellStopLimit(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, datetime pExpiration=0, string pComment=NULL);
};
ストップリミット注文関数にはpStopLimitパラメータが含まれていますが、ほかの関数には含まれていないことに注意してください。これがBuyStop()関数の本体です。この関数は単純にOpenPending()関数を呼び出し、関数パラメータをOpenPending()に渡し、2番目のパラメータとしてORDER_TYPE_BUY_STOP定数を指定します。次に、OpenPending()の戻り値がプログラムに返されます。
bool CTrade::BuyStop(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, datetime pExpiration=0, string pComment=NULL)
{
bool success=OpenPending(pSymbol, ORDER_TYPE_BUY_STOP, pVolume, pPrice, pStop,
pProfit, 0, pExpiration, pComment);
return(success);
}
他のストップ注文関数と指値注文関数は、OpenPending()関数に渡される注文タイプと同じです。以下は、pStopLimitパラメータを含む売りストップリミット注文関数です。
bool CTrade::SellStopLimit(string pSymbol, double pVolume, double pPrice, double pStop =0,
double pProfit=0, double pStopLimit=0, datetime pExpiration=0, string pComment=NULL)
{
bool success=OpenPending(pSymbol, ORDER_TYPE_SELL_STOP_LIMIT, pVolume, pPrice, pStop,
pProfit, pStopLimit, , pExpiration, pComment);
return(success);
}
これらの関数は、\MQL5\Include\Mql5Book\Trade.mqhファイルで表示できます。これらの関数の使用法については、章の後半で説明します。
指値注文の処理 P162
保留中の注文を処理する場合、現在の注文に関する情報を取得する必要があります。シンボルごとに複数の未決注文が開かれている可能性があるため、現在開いている未決注文の数を正確にカウントする必要があります。
未決注文プールを処理するには、forループとOrderGetTicket()関数を使用して、注文プール内の各注文を選択します。
for(int i = 0; i < OrderTotal(); i++)
{
ulong ticket = OrderGetTiket( i );
Print(ticket);
}
forループは整数iをインクリメントとして使用します。注文プールインデックスは0から始まるため、0に初期化されます。OrdersTotal()関数は、現在開かれている未決注文の数を返します。注文プールのインデックスは0から始まるため、iの最大値はOrdersTotal()よりも1小さい必要があります。インデックス0は、プール内の最も古い注文です。0から開始し、iがOrderTotal()より小さい限り1づつ増加します。
OrderGetTicket()関数は、i変数によって示される注文プールインデックスを持つ注文を選択します。その後、OrderGetDouble()、OrderGetIntegerまたはOrderGetString()関数を使用して、現在の注文に関する情報を取得できます。OrderGetTicket()関数は、選択した注文のチケット番号も返します。上記の例では、注文チケットをticket変数に割り当てます。チケット番号がログに出力され、ループが繰り返されます。
現在の注文プールをループする方法がわかったので、現在開いている注文の数をタイプ別に正確にカウントできます。注文カウント関数と、注文数とチケット番号を保持する変数を保持するクラスを作成します。このクラスをCPendingと呼びます。CPendingクラスと、この章のすべての関連関数は、\MQL5\Include\Mql5Book\Pending.mqhファイルに保存されます。
class CPending
{
private:
void OrderCount(string pSymbol);
int BuyLimitCount, SellLimitcount, BuyStopCount, SellStopCount,
BuyStopLimitCount, SelllStopLimitCount, TotalPendingCount;
ulong PendingTickets[];
};
CpendingクラスのOrderCount()関数は、注文プールを反復処理し、現在の注文数をカウントします。整数変数BuyLimitCount、SellLimitCountなどは注文数を格納し、PendingTickets[]配列は現在の注文のチケット番号を保持します。これらのメンバーはすべてプライベートです。この情報にアクセスするためのパブリック関数を作成します。
OrderCount()関数の本体は次のとおりです。
void CPending::OrderCount(string pSymbol)
{
BuyLimitCount=0; SellLimitCount=0; BuyStopCount=0; SellStopCount=0;
BuyStopLimitCount=0; SellStopLimitCount=0; TotalPendingCount=0;
ArrayFree(PendingTickets);
for(int i = 0; i < OrdersTotal(); i++)
{
ulong ticket = OrderGetTicket(i);
if(OrderGetString(ORDER_SYMBOL)==pSymbol)
{
long type=OrderGetInteger(ORDER_TYPE);
switch((int)type)
{
case ORDER_TYPE_BUY_STOP:
BuyStopCount++; break;
case ORDER_TYPE_SELL_STOP:
SellStopCount++; break;
case ORDER_TYPE_BUY_LIMIT:
BuyLimitCount++; break;
case ORDER_TYPE_SELL_LIMIT:
SellLimitCount++; break;
case ORDER_TYPE_BUY_STOP_LIMIT:
BuyStopLimitCount++; break;
case ORDER_TYPE_SELL_STOP_LIMIT:
SellStopLimitCount++; break;
}
TotalPendingCount++;
ArrayResize(PendingTickets, TotalPendingCount);
PendingTickets[ArraySize(PendingTickets)-1] = ticket;
}
}
}
OrderCount()関数で最初に行うことは、すべての注文カウント変数をゼロに初期化することです。各式がセミコロンで終了する限り、1行に複数の式を配置できます。ArrayFree()関数はPendingTickets[]配列をリセットし、その内容をクリアします。
forループの初期化とOrderGetTicket()関数についてはすでに説明しました。ORDER_SYMBOLパラメータを指定したOrderGetString()関数は、現在選択されている注文のシンボルを取得し、それをpSymbolパラメータと比較します。シンボルが一致しない場合、プール内の次の注文に進みます。それ以外の場合は、現在選択されている注文を処理します。
ORDER_TYPEパラメータを指定したOrderGetInteger()関数は、現在選択されている注文の注文タイプを取得し、type変数に格納します。swich演算子はtype変数をcase演算子で指定された注文タイプのリストと比較します。switch演算子内の(int)に注意してください。これにより、型変数の値が整数にキャストされ、型変換の警告が回避されます。正しい注文タイプが一致すると、関連するカウント変数がインクリメントされ、breakオペレータがswitchブロックを終了します。
TotalPendingCount変数はタイプに関係なく、現在のシンボルの注文ごとに増加します。ArrayResize()関数は、PendingTickets[]配列のサイズを変更して、TotalPendingCountで示される現在の注文数に一致させます。最後に、現在選択されている注文チケットが、配列の最上位のインデックスに格納されます。
OrderCount()関数の結果として、関連するカウント変数が、選択されたシンボルの各タイプの未決注文の数で埋められます。TotalPendingCount変数は、選択したシンボルのすべての未決済注文を集計します。最後に、PendingTickets[]配列には、選択したシンボルのすべての未決注文のチケット番号が含まれています。
ここで、カウンター変数とPendingTickets[]配列にアクセスするためのpublic関数を製作する必要があります。CPendingクラスのパブリックメンバーは次の通りです。
class CPending
{
public:
int BuyLimit(string pSymbol);
int SellLimit(string pSymbol);
int BuyStop(string pSymbol);
int SellStop(string pSymbol);
int BuyStopLimit(string pSymbol);
int SellStopLimit(string pSymbol);
int TotalPending(string pSymbol);
void GetTickets(string pSymbol, ulong &pTickets);
};
戻り値の型がintの7つの関数は、指定された注文タイプのカウントを返します。GetTickets()関数は、PendingTickets[]配列をプログラマが処理する2番目の配列にコピーします。
BuyLimit()関数の関数本体を見てみましょう。これら7つの関数はすべて、PrivateOrderCount()関数を呼び出します。関数の実行後、適切なカウント変数がプログラムに返されます。
int CPending::BuyLimit(string pSymbol)
{
OrderCount(pSymbol);
return(BuyLimitCount);
}
BuyLimit()関数は、pSymbolで指定された銘柄で現在開いている買い指値注文の数を返します。残りの6つのカウント関数も同様です。
唯一の違いは、関数によって返されるカウント変数です。\MQL5\Include\Mql5Book\Pending.mqhファイルでこれらの関数を表示できます。
それでは、GetTickets()関数を調べてみましょう。これはOrderCount()関数を呼び出し、PendingTickets[]配列を参照によって関数に渡される2番目の配列にコピーします。
void CPending::GetTickets(string pSymbol, ulong &Tickets[])
{
OrderCount(pSymbol);
ArrayCopy(pTickets, PendingTickets);
return;
}
pTickets[]パラメータの前のアンパサンド(&)は、これが参照によって渡される配列であることを示します。配列は関数によって変更され、それらの変更はプログラムの残りの部分全体に存在します。ArrayCopy()関数は、PendingTickets[]配列をpTicketsパラメータで指定された配列にコピーします。プログラムでGetTickets()関数を使用する方法は次のとおりです。
//グローバル変数
CPending Pending;
//OnTick()イベントハンドラ
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
for(int i = 0; i < ArraySize(tickets); i++)
{
ulong tickets = tickets[i];
OrderSelect(ticket);
long orderType = OrderGetInteger(ORDER_TYPE);
double orderVolume = OrderGetDouble(ORDER_VOLUME_CURRENT);
double orderOpenPrice = OrderGetDouble(ORDER_PRICE_OPEN);
double orderSL = OrderGetDouble(ORDER_SL);
double orderTP = OrderGetDouble(ORDER_TP);
Print(“Ticket:”+ticket+”, Type:”+orderType+”, Volume:”+orderVolume+”,
Price:”+orderOpenPrice+”, SL:”+orderSL+”, TP:”+orderTP+”);
}
まず、プログラムの先頭にあるCPendingクラスに基づいて、Pendingという名前のオブジェクトを宣言します。コードの残り部分は、OnTick()イベントハンドラ、またはそこから呼び出される関数に属します。
ticket[]配列は、保留中のチケット番号を保持します。GetTickets()関数は、現在のチャートシンボルの未約定注文全てのチケット番号を含むticket[]配列を埋めます。forループを使用して、チケット[]配列内のチケット番号を反復処理します。イテレータ変数iは0から始まります。iの最大値は、ArraySize()関数によって決定されます。この関数は、ticket[]配列の要素数を返します。
forループ内で、チケット[i]の値をticket変数に代入します。ticket変数はOrderSelect()関数に渡され、さらに処理する注文が選択されます。OrderGetInteger()、OrderGetDouble()、およびOrderGetString()関数を使用して、注文に関する情報を取得できるようになりました。
OrderGetInteger()およびOrderGetDouble()関数を使用して、現在選択されている注文に関する情報(注文タイプ、出来高、始値、ストップロス、テイクプロフィットなど)を取得します。最後に、この情報をログに出力します。
この方法で未決注文プールを反復処理する場合、各注文に関する情報を取得し、その情報を使用して、注文を変更するか閉じるか、などの決定を下します。
注文情報関数 P166
前の章の位置情報関数と同様に、注文情報を取得するための覚えやすい一連の関数を作成します。OrderGet…()関数を使用する代わりに、短い関数を作成して、チケット番号のみを使用してこの情報を取得します。
注文タイプから始めましょう。ORDER_TYPEパラメータを指定したOrderGetInteger()関数は、ENUM_ORDER_TYPE列挙から注文タイプ定数を返します。OrderSelect()関数を使用して、最初に注文を選択する必要があります。以下の関数は、チケット番号のみを使用して注文タイプを取得します。
long OrderType(ulong pTicket)
{
bool select = OrderSelect(pTicket);
if(select == true) return(OrderGetInteger(ORDER_TYPE));
else return(WRONG_VALUE);
}
OrderSelect()関数は、pTicketパラメータに渡されたチケット番号によって示される注文を選択します。注文が存在しない場合、関数はWRONG_VALUEまたは-1を返します。それ以外の場合、注文タイプはOrderGetInterger()関数で取得され、プログラムに返されます。
OrderGetDouble()関数を使用する別の関数を見てみましょう。この関数は、注文の現在のボリュームを返します。
double OrderVolume(ulong pTicket)
{
bool select = OrderSelect(pTicket);
if(select == true) return(OrderGetDouble(ORDER_VOLUME_CURRENT));
else return(WRONG_VALUE);
}
OrderGetDouble()関数は、OrderSelect()によって選択されたチケット番号が有効な場合、注文の現在のボリュームを返します。最後に、OrderGetString()を使用する関数を見てみましょう。
string OrderComment(ulong pTicket)
{
bool select = OrderSelect(pTicket);
if(select == true) return(OrderGetString(ORDER_VOLUME_CURRENT));
else return(NULL);
}
この関数は注文コメントを返します。OrderSelect()関数に渡された注文チケットが有効でない場合、NULL値が返されます。
OrderSelect()およびOrderGet…()関数を使用する関数がほかにもいくつかあります。\MQL5\Include\Mql5Book\Pending.mqhファイルでそれらを表示できます。OrderGet…()関数のパラメータは、MQL5リファレンスの標準定数…>取引定数>注文プロパティで確認できます。
165ページのコード例のPending.mqhファイルで定義した注文情報関数を使用してみましょう。
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
for(int i = 0, < ArraySize(tickets); i++)
{
ulong ticket = tickets[i];
long orderType = Order(tickets);
double orderVolume = OrderVolume(tickets);
double orderOpenPrice = OrderOpenPrice(tickets);
double orderSL = OrderStopLoss(tickets);
double orderTP = OrderTakeProfit(tickets);
Print(“Ticket:”+ticket+”, Type:”+orderType+”, Volume:”+orderVolume+”,
Price:”+orderOpenPrice+”, SL:”+orderSL+”, TP:“+orderTP+);
}
OrderGet…()関数を、定義した注文情報関数に置き換えました。OrderSelect()関数も不要になったため削除しました。
要約すると、7つの公開注文カウント関数(BuyLimit()、SellLimit()など)を持つCPendingクラスを作成しました。これは、指定されたシンボルのそのタイプの未決注文の数を返します。GetTickets()関数は、指定された銘柄で現在オープンしているすべての未決注文のチケット番号を含む配列を取得できます。チケット番号を使用すると、OrderType()、OrderOpenPrice()、OrderStopLoss()などの注文情報関数を使用して、注文に関する情報を取得できます。
指値注文の変更 P168
未決注文の始値、ストップロス、テイクプロフィット、または有効期限を変更する必要がある場合があります。これを行うには、OrderSend()関数でTRADE_ACTION_MODIFYアクションを使用します。チケット番号、注文開始価格、ストップロス、テイクプロフィット、有効期限とタイプを指定する必要があります。
指値注文を変更する場合、変更されない価格の現在の値を指定する必要があります。たとえば、指値注文の始値を変更していて、注文に現在ストップロスとテイクプロフィットの価格がある場合、現在のストップロスとテイクプロフィットの価格を取得し、それらをOrderSend()関数に渡す必要があります。
簡単な指値注文の変更を示しましょう。この例では、指値注文の始値を変更します。ストップロスとテイクプロフィットは変更されません。注文に有効期限は設定されていません。変数newPriceが新しい未決注文の価格を保持していると仮定します。
double newPrice;
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
for(int i = 0; i < ArraySize(tickets); i++)
{
ulong ticket = tickets[1];
request.action = TRADE_ACTION_MODIFY;
request.order = ticket;
request.price = newPrice;
request.sl = OrderStopLoss(ticket);
request.tp = OrderTakeProfit(ticket);
request.expiration = 0;
request.type_time = ORDER_TIME_GTC;
OrderSend(request, result);
}
前のセクションのGetTickets()関数とforループに気付くでしょう。現在のチャートシンボルに未決注文が1つあると仮定します。このコードは、始値をnewPriceに保存されている値に変更します。
TRADE_ACTION_MODIFY定数は、これを指値注文の変更として識別します。注文を変更するときは、request.order変数を使用して注文チケットを指定する必要があります。価格、ストップロス、テイクプロフィット、または有効期限の変更は、適切な変数に割り当てられます。変更がない場合は、現在の値をそれらの変数に割り当てる必要があります。
注文の開始価格が変更されているため、newPrice変数の値をrequesut.priceに割り当てます。OrderStopLoss()関数とOrderTkakeProfit()関数を使用して、現在のストップロス価格とテイクプロフィット価格を取得し、それらの価格をrequest.sl変数とrequest.tp変数に割り当てます。これにより、ストップロスとテイクプロフィットの価格が変更されないことが保証されます。完全を期すために、request.expirationに値0を指定し、request.type_timeをORDER_TIME_GTCに設定して、注文が期限切れにならないようにします。OrderSend()関数は、更新された未決注文価格を変更のためにサーバーに送信します。ストップロス、テイクプロフィット、有効期限は変更されません。
これは、指値注文のストップロスとテイクプロフィット価格を変更する別の例です。注文の開始価格は変更されません。ストップロスは注文開始価格より500ポイント下に調整され、テイクプロフィットはゼロに設定されます。
//グローバル変数
input int StopLoss = 500;
//OnTick()イベントハンドラ
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
for(int i = 0; i < ArraySize(tickets); i++)
{
ulong ticket = tickets[1];
request.action = TRADE_ACTION_MODIFY;
request.order = ticket;
request.price = OrderOpenPrice(ticket);
request.sl = OrderOpenPrice(ticket) – (StopLoss * _Point);
request.tp = 0;
request.expiration = 0;
request.type_time = ORDER_TIME_GTC;
OrderSend(request, result);
}
太字の行は、前の例からの変更を示しています。request.price変数は、現在の注文開始価格に設定されます。request.sl変数は、注文の始値からStopLoss変数に示されているポイント数を差し引いた値に設定されます。result.tp変数は0に設定され、変更された注文にテイクプロフィットが無いことを示します。現在、注文にテイクプロフィットがある場合、それは0にリセットされます。
ModifyPending()関数 P170
ModifyPending()関数は、注文の始値、ストップロス、テイクプロフィット、または有効期限を変更します。注文チケット、価格、ストップロス、テイクプロフィットは必須パラメータです。価格が変更されない場合、値0がpPriceパラメータに渡されます。ストップロス、テイクプロフィット、または有効期限が設定されていて変更されない場合、現在の値をModifyPending()関数に渡す必要があります。
CTradeクラス宣言内のModifyPending()関数宣言は次のとおりです。
class CTrade
{
public:
bool ModifyPending(ulong pTicket, double pPrice, double pStop, double pProfit,
datetime pExpiration = 0);
};
関数はpublicです。つまり、直接呼び出すことになります。以下は、リクエスト変数を設定するModifyPending()関数の最初の部分です。
bool CTrad::ModifyPending(ulong pTicket, double pPrice, pStop, double pProfit,
datetime pExpiration = 0)
{
request.action = TRADE_ACTION_MODIFY;
request.order = pTicket;
request.sl = pStop;
request.tp = pProfit;
OrderSelect(pTicket);
if(pPrice > 0) request.price =pPrice;
else request.price = OrderGetDouble(ORDER_PRICE_OPEN);
if(pExpiration > 0)
{
request.expiration = pExpiration;
request.type_time = ORDER_TIME_SPECIFIED;
}
else request.type_time = ORDER_TIME_GTC;
取引アクションはTRADE_ACTION_MODIFYに設定され、pTicket、pStopおよびpProfitパラメータ値が適切な要求変数に割り当てられます。次に、OrderSelect()関数を使用して、pTicketが示す注文チケットを選択します。pPriceパラメータがゼロより大きい場合、pPriceの値をrequest.priceに割り当てます。それ以外の場合は、現在の始値を取得し、その値をrequest.priceに割り当てます。
pExpirationの値がゼロより大きい場合、新しい注文の有効期限をrequest.expirationに割り当て、request.type_timeをORDER_TIME_SPECIFIEDに設定します。それ以外の場合は、request.type_timeをORDER_TIME_GTCに設定します。これは、注文に有効期限が無いことを示します。
関数の残りの部分には、注文の変更とエラー処理のコードが含まれています。
int retryCount = 0;
int checkCode = 0;
do
{
OrderSend(request, result);
checkCode = CheckReturnCode(result.retcode);
if(checkCode == CHECK__RETCODE_OK) break;
else if(checkCode == CHECK_RETCODE_ERROR)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert(“Modify pending order: Error”, result.retcode,” – “, errDesc);
break;
}
else
{
Print(“サーバーエラーが検出されました。再試行しています…。”);
Sleep(RETRY_DELAY);
retryCount++;
}
}
while(retryCount < MAX_RETRIES);
if(retryCount >= MAX_RETRIES)
{
string errDesc = TradeServerCodeDescription(result.retcode);
Alert(“Max retries exceeded: Error”, result.retcode,” – “, errDesc);
}
OrderSelect(pTicket;)
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print(“Modify pending order #”, pTicket,”: ”, result.retcode,” - ”, errDesc,
”, Price:”, OrderGetDouble(ORDER_PRICE_OPEN),”, SL: “, request.sl,
“, TP:”, request.tp,”, Expiration: “, request.expiration);
if(checkCode == CHECK_RETCODE_OK)
{
Comment(“Pending order”, pTicket,” modified,”,
” Price: ”, OrderGetDouble(ORDER_PRICE_OPEN),”, SL: ”, request.sl,
”, TP: ”, request.tp”);
return(true);
}
else return(false);
}
以下は、EAプログラムでModifyPending()関数を使用する方法です。この例では、注文の開始価格を変更しますが、ストップロスとテイクプロフィットはそのままにします。
double newPrice;
ulong ticket;
double curSL = OrderStopLoss(ticket);
double curTP = OrderTakeProfit(ticket);
Trade.ModifyPending(ticket, newPrice, curSL, curTP);
newPrice変数には新しい注文の開始価格が保持され、ticket変数には注文チケット番号が含まれます。どちらにも有効な値が含まれていると仮定します。OrderStopLoss()関数とOrderTakeProfit()関数を使用して、指定された注文チケットの現在のストップロスとテイクプロフィットの値を取得し、それらの値をそれぞれcurSLとcurTPに保存します。パラメータはModifyPending()関数に渡され、注文は新しい未決注文価格で更新されます。
ここでは、ストップロスとテイクプロフィットを変更して、注文価格を変更せずに2番目の例を示します。
double newSL, newTP;
ulong ticket;
Trade.ModifyPending(ticket, 0, newSL, newTP);
newSL変数とnewTP変数が有効なストップロスとテイクプロフィット価格を保持していると仮定します。ModifyPending()関数呼び出しでは、pPriceパラメータに0を使用して、注文の始値が変更されないことを示します。
上記の両方の例で、pExpirationパラメータはディフォルト値の0を使用しています。注文で有効期限が指定されている場合は、注文を変更するときに有効期限をModifyPending()関数に渡す必要があります。
未決注文の削除 P173
TRADE_ACTION_REMOVE定数をrequest.action変数に割り当てることにより、約定されていない未決注文を削除できます。唯一の必須パラメータは、削除する注文のチケット番号です:
request,action = TRADE_ACTION_MODIFY;
request.order = ticket;
OrderSend(request. result);
上記のコードは、チケット変数に一致する未決注文を削除します。未決注文が約定すると、それはポジションの一部となり、前の章のポジション決済方法を使用して決済する必要があることに注意してください。
前の関数と同じエラー処理と再試行コードを使用して、指値注文を削除する関数を作成します。CTradeクラス宣言の関数宣言は次のとおりです。
class CTrade
{
public:
bool Delete(ulong pTicket);
}
そして、これがその関数です。Delete()関数に必要なパラメータは1つだけです。削除する指値注文のチケット番号です。
bool CTrade::Delete(ulong pTicket)
{
request,action = TRADE_ACTION_REMOVE;
request.order = pTicket;
//注文ループ
int retryCount = 0;
int checkCode = 0;
do
{
OrderSend(request, result);
checkCode = CheckReturnCode(result.retcode);
if(checkCode == CHECK_RETCODE_OK) break;
else if(checkCode == CHECK_RETCODE_ERROR)
{
string errDesc = TradeSeverReturnCodeDescription(result.retcode);
Alert(“Delete order: Error”, result.retcode,” - ”, errDesc);
break;
}
else
{
Print(“サーバーエラーを検出しました。再試行しています…。”);
Sleep(RETRY_DELAY);
retryCount++;
}
}
while(retryCount < MAX_RETRIES);
if(retryCount >= MAX_RETRIES)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert(“Max retries exceeded: Error ”, result.retcode,” – “, errDesc);
}
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Print(“Delete order #”, pTicket,”: “, result.retcode,” – “, errDesc);
if(checkCode == CHECK_RETCODE_OK)
{
Comment(“Pending order”, pTicket,” deleted”);
return(true);
}
else return(false);
}
Delete()関数を使用するには、削除する注文のチケット番号をpTicketパラメータに渡すだけです。
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
for(int i = 0; i < ArraySize(tickets); i++)
{
ulong ticket = tickets[1];
Trade.Delete(ticket);
}
この例では、現在のチャートで開かれている未決注文をすべて削除します。
EAで指値注文を作る P175
保留中の注文を出す簡単なEAを作成します。この戦略はH4以上のチャートで使用することを意図しています。新しいバーが開くと、2つの保留中のストップ注文が、前のバーの高値と安値の上下に指定された数のポイントで配置されます。次のバーの開始時に、未約定の未決注文が削除され、新しい未決注文が配置されます。オープンポジションはストップロスまたはテイクプロフィット価格に達するまで継続します。
#include <Mql5Book\Trade.mqh>
CTrade Trade;
#include <Mql5Book\Trade.mqh>
CPending Pending;
//入力変数
input int AddPoints = 100;
input double TradeVolume = 0.1;
input int StopLoss = 1000;
input int TakeProfit = 1000;
//グローバル変数
bool glBuyPlaced, glSellPlaced;
datetime glLastBarTime;
//OnTick()イベントハンドラ
void OnTick()
{
//時間と価格のデータ
MqlRates rates[];
ArraySetSeries(rates, true);
int copy = CopyRates(_Symbol, _Period, 0, 3, rates);
//新しいバーをチェック
bool newBar = false;
if(glLastBarTime ! = rates[0].time)
{
if((glLastBarTime > 0) newBar = true;
glLastBarTime = rates[0].time;
}
//新しい足のオープン時に指値注文を出す
if(newBar == true)
{
//未決注文チケットを取得
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
int numTickets = ArraySize(tickets);
//未決注文を閉じる
if(Pending.TotalPending(_Symbol) > 0)
{
for(int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets[i]);
}
}
//保留中の買いストップ注文を開く
double orderPrice = rates[1].high + (AddPoints * Point);
orderPrice = AdjustAboveStopLevel(_Symbol, orderPrice);
double stopLoss = BuyStopLoss(_Symbol, StopLoss, orderPrice);
double takeProfit = BuyTakeProfit(_Symbol, TakeProfit, orderPrice);
Trade.BuyStop(_Symbol, TradeVolume, orderPrice, stopLoss, takeProfit);
//保留中の売りストップ注文を開く
orderPrice = rates[1].low – (AddPoints * _Point);
orderPrice = AdjustBelowStopLevel(_Symbol, orderPrice);
stopLoss = SellStopLoss(_Symbol, StopLoss, orderPrice);
takeProfit = SellTakeProfit(_Symbol, TakeProfit, orderPrice);
Trade.SellStop(_Symbol, TradeVolume, orderPrice, stopLoss, takeProfit);
}
}
プログラムを一歩ずつ進めましょう。
#include <Mql5Book\Trade.mqh>
CTrade Trade;
#include <Mql5Book\Trade.mqh>
CPending Pending;
//入力変数
input int AddPoints =100;
input double TradeVolume = 0.1;
input int StopLoss = 1000;
input int TakeProfit = 1000;
//グローバル変数
bool glBuyPlaced, glSellPlaced;
datetime glLastBarTime;
まず、\MQL5\Include\Mql5BookフォルダーからTrade.mqhおよびPending.mqhファイルを含めます。CTradeおよびCPendingクラスに基づいてTradeおよびPendingオブジェクトを作成します。AddPointsという名前の入力変数を追加します。これは、前のバーの高値または安値から指定されたポイント数を加算または減算します。取引量、ストップロス、テイクプロフィットを設定するための入力変数も含まれています。グローバル日時変数glLastBarTimeは、最新のバーのタイムスタンプを保持します。
void OnTick()
{
//時間と価格のデータ
MqlRates rates[];
ArraySetAsSeries(rates, true);
int copy = CopyRates(_Symbol, _Period, 0, 3, rates);
//新しいバーをチェック
bool newBar = false;
if(glLastTime ! = rates[0].time)
{
if(glLastBarTime > 0) newBar = true;
glLastBarTime = rates[0].time;
}
MqlRates構造体型のrates[]配列を使用して、各バーの高値、安値、および時間値を保持します。第15章でMqlRates構造体について説明します。newBar変数は、新しいバーが開いたかどうか(つまり、現在のバーのタイムスタンプが変更されたかどうか)を決定するブール値を保持します。新しいバー機能については、第18章で説明します。
if(newBar == true)
{
//未決注文チケットを取得
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
int numTickets = ArraySize(tickets);
//未決注文を閉じる
if(pending.TotalPending(_Symbol) > 0)
{
for(int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets);
}
}
newBar変数の値がtrueの場合、新しいバーが開いたことを示し、発注コードから始めます。まず、CPendingクラスのGetTickets()関数を使用して、オープンオーダーチケットを取得します。これらは、チケット[]配列に格納されます。ArraySize()関数は、ticket[]配列の要素数を返し、その値をnumTickets変数に格納します。未決注文の数が0より大きい場合は、numTickets変数により決定され、forループを使用して注文して注文プールを反復処理し、Trade.Delete()関数を使用してすべての未決済注文を削除します。
//保留中の買いストップ注文を開く
double orderPrice = rates[1].high + (AddPoints * _Point);
orderPrice = AdjustAboveStopLevel(_Symbol, orderPrice);
double stopLoss = BuyStopLoss(_Symbol, StopLoss, orderPrice);
double takeProfit = BuyTakeProfit(_Symbol, TakeProfit, orderPrice);
Trade.BuyStop(_Symbol, TradeVolume, orderPrice, stopLoss, takeProfit);
//保留中の売りストップ注文を開く
orderPrice = rates[1].low - (AddPoints * _Point);
orderPrice = AdjustBelowStopLevel(_Symbol, orderPrice);
stopLoss = SellStopLoss(_Symbol, StopLoss, orderPrice);
takeProfit = SellTakeProfit(_Symbol, TakeProfit, orderPrice);
Trade.SellStop(_Symbol, TradeVolume, orderPrice, stopLoss, takeProfit);
orderPrice変数は未決注文の始値を保持します。前のバーの高値または安値(rates[1].highおよびrate[1].low)にAddPoints値(最初にシンボルのPoint値を乗算)を加算または減算します。AdjustAboveStopLevel()およびAdjustBelowStopLevel()関数を使用して、必要に応じて注文価格を確認及び調整します。ストップロスを計算し、注文の始値に対して利益を確定します。最後に、CTradeクラスのBuyStop()およびSellStop()関数を使用して保留中の逆指値注文を出します。
\Experts\Mql5Book\Pending Expert Advisor.mq5ファイルで、このEAのコードを表示できます。
指値注文をしてポジションをスケールアウトする P179
一定の利益または損失レベルでポジションをスケールアウトしたい場合、最良の方法は指値注文を使用して、指定された価格でポジションの一部を決済することです。ポジションは指値注文を使用して利益で閉じることができますが、損失のあるポジションは逆指値注文を使用して閉じることができます。利益のある買いポジションを閉じるには、売り指値注文を使用します。損失で閉じるには、売りストップ注文を使用します。利益のある売りポジションを閉じるには、買い指値注文を使用します。損失で決済するには、買いストップ注文を使用します。
例えば、500ポイントの利益で買いポジションの半分を決済し、残りを1000ポイントで決済したい場合、ポジションのロットサイズの半分に対して、ポジションの始値より500ポイント高い売り指値注文を出します。ポジションのテイクプロフィット価格が残りを処理します。次に例を示します。
//入力変数
input double TradeVolume = 0.2;
input int StopLoss = 500
input int TakeProfit1 = 500
input int TakeProfit2 = 1000
//OnTick()イベントハンドラ
//成り行き買い注文
glBuyPlaced = Trade.Buy(_Symbol, TradeVolume);
//SL/TPを変更
if(glBuyPlaced == true)
{
do Sleep(100); while(PositionSelect(_Symbol) == false);
double positionOpenPrice = PositionOpenPrice();
double buyStopLoss = BuyStopLoss(_Symbol, StopLoss, positionOpenPrice);
if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol, buyStopLoss);
double buyTakeProfit = BuyTakeProfit(_Symbol, TakeProfit2, positionOpenPrice);
if(buyTakeProfit > 0) buyTakeProfit = AdjustAboveStopLevel(_Symbol, buyTakeProfit);
if(buyStopLoss > 0 || buyTakeProfit > 0)
Trade.ModifyPosition(_Symbol, buyStopLoss, buyTakeProfit);
glSellPlaced = false;
//部分決済注文を開く
double partialClose = positionOpenPrice + (TakeProfit1 * _Point);
double partialVolume = TradeVolume / 2;
Trade.sellLimit(_Symbol, partialVolume, partialClose);
}
上記の例では、買いポジションをオープンし、1000ポイントのストップロスとテイクプロフィットを設定してから、買いポジションの開始価格の500ポイント上に、買いポジションのロットサイズの半分に等しい売り指値注文を出します。テイクプロフィット値に2つの入力変数を使用します。TakeProfit1は売り指値注文の価格を決定し、TakeProfit2は買いポジションのテイクプロフィット価格です。TakeProfit2変数がBuyTakeProfit()関数に渡され、買いポジションのテイクプロフィット価格が計算されます。
売り指値注文価格を計算するには、TakeProfit1の値(_Point変数を乗算)をポジションの開始価格(positionOpenPrice)に追加し、その結果をpartialClose変数に格納します。TradeVolume入力変数を2で割り、その結果をVolume変数に格納することで、売り指値注文の出来高を計算します。SellLimit()関数を使用して、ポジションの始値より500ポイント高い0.1ロットの売り指値注文を開きます。
売り指値注文がトリガーされると、現在のポジションの半分が決済されます。残りのポジションは、ポジションのテイクプロフィット価格で決済されます。このメソッドを使用して、任意の数の部分クローズレベルをポジションに追加できます。未決注文のボリュームをポジションのボリュームより高く設定しないでください。そうしないと、反対方向にポジションをオープンすることになります!