ポジションの処理・変更・クローズ P135
位置情報機能 P135
多くの場合、現在開いているポジションのステータスを知る必要があります。ポジションの種類(買いまたは売り)、ポジションが現在利益を上げているかどうか、現在のストップロス、テイクプロフィット、ボリュームが何であるかを知る必要がある場合があります。
ネッティングアカウントの場合、PositionGetDouble()…PositionGetInteger()およびPositionGetString()関数とともに、PositionSelect()関数を使用して、現在のポジションに関する情報を取得します。以下は、現在の位置タイプを取得するために使用したコードの例です。
bool openPosition = PositionSelect(_Symbol);
long PositionType = PositionGetInteger(POSITION_TYPE);
PositionSelect()関数は、現在のチャートシンボルの位置を選択します。位置が選択されると、PositionGet…()関数を使用して位置に関する情報を取得できます。上記の例では、Position_TYPEパラメータを指定してPositionGetInteger()を使用して、位置のタイプを返しています。標準定数… > 取引定数 > ポジションプロパティの下のMQL5リファレンスで、PositionGet…()関数の3つすべてのポジションプロパティ定数をすべて表示できます。
MQL5で位置情報を取得する際の問題は、正しい位置プロパティ定数とともにどのPositionGet…()関数を使用するかを覚えていることです。1回の関数呼び出しで現在位置に関する情報を取得する、覚えやすい一連の関数を作成します。
ポジションのタイプを取得する関数を使って説明しましょう。PositionType()関数は、指定されたシンボルの現在の位置を選択します。次に、位置タイプの定数を返します。
long PositionType(string pSymbol = NULL)
{
if(pSymbol == NULL) pSymbol = _Symbol;
bool select = PositionSelect(pSymbol);
if(select == true) return(PositionGetInteger(POSITION_TYPE));
else return(WRONG_VALUE);
}
pSymbolパラメータはオプションです。指定しない場合、関数は現在のチャートシンボルを使用します。PositionSelect()関数は、指定されたシンボルの位置を選択し、選択変数にブール値を返します。Selectがtrueの場合、PositionGetInteger()関数は位置タイプを取得し、それをプログラムに返します。開いているポジションがない場合、関数はWRONG_VALUE定数または-1を返します。
double PositionOpenPrice(string pSymbol = NULL)
{
if(pSymbol == NULL) pSymbol = _Symbol;
bool select = PositionSelect(pSymbol);
if(select == true) return(PositionGetDuble(POSITION_PRICE_OPEN));
else return(WRONG_VALUE);
}
出来高、開始時間、ストップロス、テイクプロフィット、コメント、現在の利益など、現在のポジションに関する情報を返す合計10個の関数があります。これらの関数は\MQL5\Include\Mql5Book\Trade.mqhファイルで表示できます。
位置情報関数を使用して、単純なEAを変更してみましょう。
//現在位置情報
long poshitionType = PositionType();
//買い成り行き注文を開く
if(close[0] > ma[0] && glBuyPlaced == false && positionType != POSITION_TYPE_BUY)
{
glBuyPlaced = Trade.Buy(_Symbol, TradeVolume);
//SL/TPを変更
if(glBuyPlaced == true)
{
request.action = TRADE_ACTION_SLTP;
do Sleep(100); while(PositionSelect(_Symbol) == false);
double positionOpenPrice = PositionOpenPrice();
double buyStopLoss = BuyStopLoss(_Symbol, StopLoss, positionOpenPrice);
if(buyStopLoss > 0) request.sl = AdjustBelowStopLevel(_Symbol, buyStopLoss);
double buyTakeProfit = BuyTakeProfit(_Symbol, TakeProfit, positionOpenPrice);
if(buyTakeProfit > 0) request.tp = AdjustAboveStopLevel(_Symbol, buyTakeProfit);
if(request.sl > 0 && request.tp > 0) OrderSend(request, result);
glSellPlaced = false;
}
}
現在の位置タイプを返すために、単一の関数PositionType()を使用しています。開いているポジションが無い場合、関数はWRONG_VALUEを返します。このため、PositionSelect()関数の出力を確認する必要がなくなったため、注文開始条件を編集しました。
注文が確定したら、glBuyPlaced変数の値を確認します。glBuyPlacedがtrueの場合、注文の変更に進みます。PositionOpenPrice()関数は、ポジションの始値を取得するためのPositionGetDouble()関数にとって代わりました。現在のチャートシンボルの情報を取得しているため、これらの関数にパラメータを渡す必要はありません。
ヘッジ口座 P137
ヘッジ口座の場合、チケット番号を使用して位置情報を取得する必要があります。PositionSelectTicket()関数は、ヘッジ口座のオープン成行注文のポジション情報を選択するために使用されます。以下は、ヘッジ注文のポジションタイプを取得する方法の例です。ticket変数が有効な注文チケット番号を保持していると仮定します。
bool openPosition = PositionSelectByTicket(ticket);
long positionType = PositionGetInteger(POSITION_TYPE);
PositionSelectByTicket()関数は、有効な注文チケットをパラメータとして受け取ります。チケット番号に一致するオープンポジションが正常に見つかった場合、PositionSelectByTicket()はtrueを返します。その時点で、PositionGet…()関数を使用して、ヘッジポジションに関する情報を取得できます。
ヘッジアカウントの単純なEAでは、forループを使用して注文プールをループし、注文チケットを取得したことを思い出してください。すべてのオープンな買い注文と売り注文の数と、それらのチケット番号を含む配列を返すクラスを作成します。
オープンポジションのリストを取得する P137
CPositionsという名前のクラスを作成します。これにより、オープンな売買成行注文の数と、チケット番号を含む配列を取得できます。CPositionsクラスは、TradeHedge.mqhインクルードファイルに配置されます。
注文タイプのカウントとチケット番号を保持する、クラスの保護されたメンバーをいくつか宣言することから始めましょう。
class CPositions
{
protected :
ulong BuyTickets[];
ulong SellTickets[];
ulong Tickets[];
int BuyCount;
int SellCount;
int TotalCount;
void GetOpenPositions(ulong pMagicNumber = 0);
int ResizeArray(ulong &array[]);
};
ulong型の配列はチケット番号を保持し、int変数は注文数を保持します。GetOpenPositions()関数は、注文プールをループし、提供されたマジックナンバーに一致する注文を選択する内部関数です。ResizeArray()は、注文チケットの配列のサイズ変更プロセスを簡素化する別の内部関数です。
注文数とチケットを取得するために使用されるパブリック関数を宣言しましょう。
class CPositions
{
protected :
ulong BuyTickets[];
ulong SellTickets[];
ulong Tickets[];
int BuyCount;
int SellCount;
int TotalCount;
void GetOpenPositions(ulong pMagicNumber = 0);
int ResizeArray(ulong &array[]);
public :
int Buy(ulong pMagicNumber);
int Sell(ulong pMagicNumber);
int TotalPositions(ulong pMagicNumber);
void GetBuyTickets(ulong pMagicNumber, ulong &pTickets[]);
void GetSellTickets(ulong pMagicNumber, ulong &pTickets[]);
void GetTickets(ulong pMagicNumber, ulong &pTickets[]);
};
Buy()およびSell()関数は、未決済の成り行き注文または未決済注文の合計数を返しますが、TotalPositions()はすべての未決済成行注文の数を返します。pMagicNumberパラメータは、このEAに設定されたマジックナンバーです。第10章でマジックナンバーの使用法について説明しました。何らかの理由でEAでマジックナンバーを使用しない場合は、このパラメータをゼロに設定できます。
Buy()、Sell()、TotalPositions()関数の関数本体は次のとおりです。
int CPositions : : Buy(ulong pMagicNumber)
{
GetOpenPositions(pMagicNumber);
return(BuyCount);
}
int CPositions : : Sell(ulong pMagicNumber)
{
GetOpenPositions(pMagicNumber);
return(SellCount);
}
int CPositions : : TotalPositions(ulong pMagicNumber)
{
GetOpenPositions(pMagicNumber);
return(TotalCount);
}
3つの関数はすべて、単にpMagicNumber値をGetOpenPositions()関数に渡し、適切なフィールドをCPositionsクラスに返します。GetOpenPosition()関数をすぐに確認します。
GetBuyTickets()、GetSellTickes()、およびGetTickets()関数は、買い、売り、またはすべての成り行き注文のチケット番号を含む(参照によって渡される)配列を返します。
void CPositions : : GetBuyTickets(ulong pMagicNumber, ulong &pTickets[])
{
GetOpenPositions(pMagicNumber);
ArrayCopy(pTickets, BuyTickets)
return;
}
void CPositions : : GetSellTickets(ulong pMagicNumber, ulong &pTickets[])
{
GetOpenPositions(pMagicNumber);
ArrayCopy(pTickets, SellTickets)
return;
}
void CPositions : : GetTickets(ulong pMagicNumber, ulong &pTickets[])
{
GetOpenPositions(pMagicNumber);
ArrayCopy(pTickets, Tickets)
return;
}
3つの関数はすべてGetOpenPositions()関数を呼び出し、参照によって渡されるpTickets[]配列に適切なクラス配列をコピーします。更新された注文数またはチケット番号のリストを要求するたびにGetOpenPositions()関数を呼び出すことで、常に最新の情報を返すようにします。
GetOpenPositions()関数を詳しく見てみましょう。
void CPosition : : GetOpenPoshitions(ulong pMagicNumber = 0)
{
BuyCount = 0;
SellCount = 0;
TotalCount = 0;
ArrayResize(BuyTickets, 1);
ArrayInitialize(BuyTickets, 0);
ArrayResize(SellTickets, 1);
ArrayInitialize(SellTickets, 0);
ArrayResize(Tickets, 1);
ArrayInitialize(Tickets, 0);
for(int i = 0; i < PositionTotal(); I++)
{
ulong ticket = PositionsGetTicket(i);
PositionSelectByTicket(ticket);
if(PositionGetInteger(POSITION_MAGIC) != pMagicNuber && pMagicNumber > 0)continue;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
BuyCount++;
int arrayIndex = ResizeArray(BuyTickets);
BuyTickets[arrayIndex] = PositionGetInteger(POSITION_TICKET);
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
SellCount++;
int arrayIndex = ResizeArray(SellTickets);
SellTickets[arrayIndex] = PositionGetInteger(POSITION_TICKET);
}
TotalCount++;
int arrayIndex = ResizeArray(Tickets);
Tickets[arrayIndex] = PositionGetInteger(POSITION_TICKET);
}
}
最初に、BuyCount、SellCount、およびTotalCountの値をゼロに初期化し、BuyTickets[]、SellTickets[]、およびTickets[]配列のサイズを1つの要素に変更します。これらの配列は動的配列であり、価格データを保持するためにMQL5で最も一般的に使用されます。動的配列はサイズを変更できます。エラーを防ぐために、使用する前にサイズを変更して初期化する必要があります。
第10章で説明したように、注文プールをループして、開いている注文ごとに注文チケットを取得する必要があります。PositionGetTicket()をゼロベースのインデックス変数とともに使用して、各チケット番号を取得します。PositionSelectByTicket()を使用して、情報取得の順序を選択します。POSITION_MAGICパラメータを指定したPositionGetInteger()関数は、注文のマジックナンバーを取得します。(pMagicNumberパラメータを使用して)関数に渡したマジックナンバーと一致しない場合は、それをスキップして次の順序に進みます。
次に、ポジションタイプを確認します。タイプ(買いまたは売り)に応じて、BuyCountまたはSellCount変数をインクリメントし、チケット番号を配列に追加します。配列のサイズを変更する必要があります(必要に応じて、関数の開始時にすでにサイズを変更しています)。次に、配列の最大インデックスをチケット番号に設定します。ループの最後で、処理されるすべての注文がTotalCountフィールドとTickets[]配列にも追加されます。
ResizeArray()関数は、参照によって配列を受け取り、新しい配列要素を最後に追加し、サイズ変更された配列と、その最大要素のインデックスを返す内部関数です。
int CPositions : : ResizeArray(ulong &array[])
{
int arrayIndex = 0;
if(ArraySize(array) > 1)
{
int newSize = ArrayResize(array, ArraySize(array) + 1);
arrayIndex = newSize – 1;
}
return arrayIndex;
}
ResizeArray()関数が戻ると、現在選択されている注文のチケット番号を、サイズ変更された配列の最大要素に割り当てるだけです。
CPositionクラスを使用してオープン注文に関する情報にアクセスする方法を示しましょう。以下のコードは、ファイル\Mql5\Experts\Mql5Book\Simple ExpertAdvisor with Functions(Hedging).mq5からのものです。まず、プログラムの上部にある宣言セクションでCPositionsクラスに基づいてオブジェクトを作成する必要があります。
#include <Mql5Book\TradeHedg.mqh>
CTradeHedg Trade;
CPositions Positions;
次に、ユーザーがマジックナンバーを設定するための入力変数を追加します。
input int MagicNumber = 12345;
OnInit()イベントハンドラでマジックナンバーを設定する必要があります。
//OnInit()イベントハンドラ
void OnInit()
{
Trade.MagicNumber(MagicNumber);
}
以下のコードは、OnTick()イベントハンドラの上部付近、売買注文コードの前に配置されます。
ulong buyTicket = 0, sellTicket = 0;
if(Positions.Buy(MagicNumvber) == 1)
{
ulong buyTickets[];
position.GetBuyTickets(MagicNumber, buyTickets);
buyTicket = buyTickets[0]
glBuyPlaced = true;
}
if(Positions.Sell(MagicNumvber) == 1)
{
ulong sellTickets[];
position.GetSellTickets(MagicNumber, sellTickets);
sellTicket = sellTickets[0]
glSellPlaced = true;
}
この例では、上で定義したMagicNumber入力変数を使用して、position.Buy()またはPosition.Sall()関数を呼び出します。これらの関数が買い注文または売り注文がオープンであることを示している場合、ulong型の配列を宣言し、それをPositionGetBuyTicket()またはPositionGetSellTicket()関数に渡します。返された配列には、配列の最初の要素に有効な注文チケットが含まれている必要があります。これはbuyTicketまたはSellTicket変数に割り当てられます。
全てのオープンポジションのチケット番号が利用可能になったので、TradeHedge.mqlで作成するポジション情報関数を使用して、オープンポジションに関する詳細情報を取得できます。この章の前半でネッティングアカウントで使用するためにTrade.mqhファイルにポジション情報関数を作成したことを思い出してください。次のセクションでは、ヘッジアカウントで使用するオーバーロードされた関数を作成します。
ヘッジ口座のポジション情報関数 P143
チケット番号を取得したら、PositionSelectByTicket()を使用して注文に関する情報を取得し、PositionGetDouble()、PositionGetInteger()、およびPositionGetString()関数を適切なパラメータとともに使用して取得できます。ネッティング口座の場合と同様に、ヘッジ口座で使用するポジション情報関数を作成しました。
例えば、この関数はチケット番号を入力として取り、注文タイプを返します。
long PositionType(ulong pTicket = 0)
{
bool select = false;
if(pTicket > 0) select = PositionSelectByTicket(pTicket);
if(select == true) return(PositionGetInteger(POSITION_TYPE));
else return(WRONG_VALUE);
}
PositionSelectByTicket()の戻り値を確認します。falseの場合は、WRONG_VALUE定数(-1)を返します。それ以外の場合は、POSITION_TYPEパラメータを指定して、PositionGetInteger()の値を返します。
ヘッジ口座のポジション情報関数はTradeHedge.mqhファイルにあり、Trade.mqhファイルで同様の名前の関数をオーバーロードします。
ポジション修正関数の作成 P143
前の章では、OrderSend()関数を使用してストップロスを追加し、ポジションに利益を確定しました。他の関数と同じエラー処理と再試行機能を使用して、位置を変更する関数を簡単に作成できます。ModifyPosition()関数は、CTradeクラスのパブリック関数になります。これには、3つのパラメータがあります。pSymbolは変更するポジションのシンボル、pStopはストップロス価格、pProfitはテイクプロフィットの価格です。
Class CTrade
{
public :
bool ModifyPosition(string pSymbol, double pStop, double pProfit = 0);
}
pProfitパラメータはオプションです。ほとんどの場合、注文のストップロスを変更しますが、常にテイクプロフィットを変更するとは限りません。これが、ModifyPosition()関数の本体です。この関数のほぼすべてのコードは、すでにおなじみのはずです。
bool CTrade::ModifyPosition(string pSymbol, double pStop, double pProfit = 0)
{
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_SLTP;
request.symbol = pSymbol;
request.sl = pStop;
request.tp = pProfit;
//注文ループ
int retryCount = 0;
int checkCode = 0;
do
{
bool sent = 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 position: Error”, result.retcode,” - “, errDesc);
LogTradeRequest();
break;
}
else
{
Print(“Server error detected, retrying…”);
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 = TradeSeverReturnCodeDescription(result.retcode);
Print(“Modify position:”, result.retcode,” – “, errDesc,”, SL:”, request.sl,”, TP:”,
request.tp,”, Bid:”, SymbolInfoDouble(pSymbol, SYMBOL_BID),”, ASK:”,
SymbolInfoDouble(pSymbol, SYMBOL_ASK),”, Stop Level:”,
SymbolInfoInteger(pSymbol, SYMBOL_TRADE_STOPS_LEVEL));
if(checkCode == CHECK_RETCODE_OK)
{
Comment(“Position modified on”, pSymbol,”, SL:”, request/sl,”, TP:”, request/tp);
return(true);
}
else return(false);
}
以下は、EAプログラムでModifyPosition()関数を使用する方法です。成行注文を行った後、ストップロスとテイクプロフィットの価格を計算して検証します。次に、ModifyPosition()関数を呼び出してオープンポジションを変更します。
//買い成り行き注文を開く
if(close[0] > ma[0] && glBuyPlaced == false && positionType != POSITION_TYPE_BUY)
{
glBuyPlaced = Trade.Buy(_Symbol, TradeVolume);
//SL/TPを変更
if(glBuyPlaced == true)
{
PositionSelect(_Symbol);
double positionOpenPrice = PositionOpenPrice();
double buyStopLoss = BuyStopLoss(_Symbol, StopLoss, positionOpenPrice);
if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol, buyStopLoss);
double buyTakeProfit = BuyTakeProfit(_Symbol, TakeProfit, positionOpenPrice);
if(buyTakeProfit > 0) buyTakeProfit = AdjustBelowStopLevel(_Symbol, buyTakeProfit);
if(buyStopLoss > 0 || buyTakeProfit > 0)
Trade.ModifyPosition(_Symbol, buyStopLoss, buyTakeProfit);
glSellPlaced = false;
}
}
request.sl変数とrequset.tp変数をbuyStopLoss変数とbuYTakeProfit変数に置き換えました。ポジションを変更する前に、buyStopLossまたはbuyTakeProfit変数がゼロより大きいかどうかを確認します。その場合、TCradeクラスのModifyPosition()関数が呼び出されて、ストップロスが変更され、現在のポジションの利益が確定します。上記の変更は、\Experts\Mql5BookフォルダーにあるファイルSimpleExpertAdvisor with Funciton.mq5で確認できます。
ヘッジ口座 P146
ModifyPosition()関数は、TradeHedge.mqhファイルでオーバーロードされています。記号の代わりにチケット番号を取ります。
bool CTradeHedge::ModifyPosition(ulong pTicket, double pStop, double pProfit = 0.000000)
{
ZeroMemory(request);
ZeroMemory(result);
bool select = PositionSelectByTicket(pTicket);
string symbol = PositionGetString(POSITION_SYMBOL);
request.action = TRADE_ACTION_SLTP;
request.sl = pStop;
request.tp = pProfit;
request.position = pTicket;
request.symbol = symbol;
//注文ループ
int retryCount = 0;
int checkCode = 0;
do
{
bool sent = 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 position: Error”, result.retcode,” - “, errDesc);
LogTradeRequest();
break;
}
else
{
Print(“Server error detected, retrying…”);
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 = TradeSeverReturnCodeDescription(result.retcode);
Print(“Modify position #”,pTicket,”:”, result.retcode,” – “, errDesc,”, SL:”, request.sl,”, TP:”,
Request.tp,”, Bid:”, SymbolInfoDouble(symbol, SYMBOL_BID),”, ASK:”,
SymbolInfoDouble(symbol, SYMBOL_ASK),”, Stop Level:”,
SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL));
if(checkCode == CHECK_RETCODE_OK)
{
Comment(“Position #”,pTicket,”modified on”, symbol,”, SL:”, request/sl,
”, TP:”, request/tp);
return(true);
}
else return(false);
}
}
pTicketパラメータは変更する成行注文チケット番号です。まず、PositionSelectByTicket()関数を使用して注文を選択します。次に、POSITION_SYMBOLパラメータを指定したPositionGetString()を使用して注文記号を取得し、それを記号変数に割り当てます。symbol変数は(request.symbolを使用して)変更する注文のシンボルを設定し、ロギング目的で現在の価格情報を取得するために使用されます。request.position変数もpTicketの値に設定されます。
CTradeHedge::ModifyPosition()関数の使用方法は、CTradeクラスに同名の関数とほぼ同じです。ファイル\MQL5\Experts\Mql5Book\Simple Expert Advisor with Functions(Hedgeing).mq5から取得した買い成り行き買い注文を変更する方法の例を以下に示します。相違点は太字で強調されています。
//SL/TPを変更
if(buyTicket == 0)
{
PositionSelectByTicket(_buyTicket);
double positionOpenPrice = PositionOpenPrice(buyTicket);
double buyStopLoss = BuyStopLoss(_Symbol, StopLoss, positionOpenPrice);
if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol, buyStopLoss);
double buyTakeProfit = BuyTakeProfit(_Symbol, TakeProfit, positionOpenPrice);
if(buyTakeProfit > 0) buyTakeProfit = AdjustBelowStopLevel(_Symbol, buyTakeProfit);
if(buyStopLoss > 0 || buyTakeProfit > 0)
Trade.ModifyPosition(_buyTicket, buyStopLoss, buyTakeProfit);
glSellPlaced = false;
}
}
ポジションを閉じる P148
第10章を思い出すかもしれませんが、成行注文がネッティング口座で開かれると、まず、反対方向に現在開いているポジションがあるかどうかを確認します。その場合、注文のロットサイズを増やして現在のポジションを決済し、逆方向のネットポジションになります。ただし、反対方向の注文をオープンする前に、ポジションをクローズしたい場合もあります。オープンポジションをクローズする関数を作成します。ポジションの一部または全部を決済できますが、オープンポジションの方向を反転させることはありません。
ポジションを閉じるプロセスは、ポジションを開くプロセスと同じです。TRADE_ACTION_DEALをトレードアクションとして指定し、MqlTradeRequsetオブジェクトのシンボル、タイプ、ボリューム、および価格変数を入力します。ポジション決済関数では、現在のポジションのボリュームと方向を決定する必要があります。要求されたクローズボリュームが現在のポジションボリュームより大きくないことを確認する必要があります。ポジションを閉じるには、現在のポジションと反対方向に注文する必要があります。
Close()関数は、CTradeクラスのパブリック関数です。これには、決済するポジションのシンボル、決済ボリューム(オプション)、注文コメント(オプション)の3つのパラメータがあります。
class CTrade
{
public :
bool Close(string pSymbol, doble pVolume = 0, string pComment = NULL);
}
Close()関数の本体を見てみましょう。
bool CTrade::Close(string pSymbol, double pVolume=0.000000, string pComment=NULL)
{
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;
request.symbol = pSymbol;
request.deviation= deviation;
request.type_filling = fillType;
request.magic = magicNumber;
double closeVol = 0;
long openType = WRONG_VALUE;
if(PositionSelect(pSymbol) == true)
{
closeVol = PositionGetDouble(POSITION_VOLUME);
openType = PositionGetInteger(POSITION_TYPE);
}
else return(false);
いつものように、リクエストオブジェクトの関連フィールドに入力します。現在のポジションを選択し、closeVol変数とopenType変数をポジションのボリュームとトレードタイプにそれぞれ設定します。
次に、ポジションのクローズボリュームを決定する必要があります。pVolumeパラメータに値が指定されているかどうか、およびそれが有効かどうかを確認します。
if(pVolume > closeVol || pVolume <= 0) request.volume = closeVol;
else request.volume = pVolume;
pVolumeの値が現在の位置のボリューム(closeVol)より大きい場合、またはpVolumeが指定されていない(ゼロ以下)場合、request.volumeにはcloseVolの値が割り当てられます。これにより、ポジション全体が決済されます。それ以外の場合、クローズボリュームがpVolumeで指定されている場合、request.volumeにはpVolumeの値が割り当てられます。これにより、ポジションが部分的に決済されます。
次に、注文ループに入ります。ポジションを決済する前に、注文タイプと終値を決定する必要があります。
int retryCount = 0;
int checkCode = 0;
do
{
if(openType == POSITION_TYPE_BUY)
{
request.type = ORDER_TYPE_SELL;
request.price |= SymbolInfoDouble(pSymbol, SYMBOL_BID);
}
else if(openType == POSITION_TYPE_SELL)
{
request.type = ORDER_TYPE_BUY
request.price |= SymbolInfoDouble(pSymbol, SYMBOL_ASK);
}
bool sent = OrderSend(request, result);
太文字で強調表示されたコードは、新しい注文タイプと価格を決定します。現在オープンしているポジションが買いポジション(POSITION_TYPE_BUY)の場合、注文タイプはORDER_TYPE_SELLに設定され、注文価格は現在のビッド価格に設定されます。現在のポジションが売りポジションの場合、タイプはORDER_TYPE_BUYに設定され、価格は現在のアスク価格に設定されます。OrderSend()関数は、現在のポジションの一部または全部を決済します。
関数内の残りのコードは、エラー処理と注文の再試行機能を処理します。
checkCode= CheckReturnCode(result.retcode);
if(checkCode == CHECK_RETCODE_OK) break;
else if(checkCode == CHECK_RETCODE_ERROR)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert(“Close position: Error”, result.retcode,” – “, errDesc);
LogTradeRequest();
break;
}
else
{
Print(“Server error detected, retrying…”);
Sleep(RETRY_DELAY);
tryryCount++;
}
}
while(retryCount < MAX_RETRIES);
if(retryCount < MAX_RETRIES)
{
string errDesc = TradeServerReturnCodeDescription(result.retcode);
Alert(“Max retries exceeded: Error”, 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 #”, result.order,”: “, result.retcode,” – “, errDesc,”,
Volume: “, result.volume,”, Price:”, result.price,”, Bid:”, result.bid,”,
Ask: “, result.ask);
if(checkCode == CHECK_RETCODE_OK)
{
Comment(posType,”position closed on”, pSymbol,” at “, result.price);
return(true);
}
else return(false);
以下は、EAでClose()関数を使用する方法です。たとえば、単純なEAでは、現在の終値が移動平均線を超えた場合に注文をクローズします。注文をクローズするコードは、現在のポジションタイプを取得した直後、注文開始条件の前にEAに送信されます。
long positionType = PositionType();
if(closs[0] < && positionType == POSITION_TYPE_BUY)
{
Trade.Close(_Symbol);
}
else if(closs[0] > ma[0] && positionType == POSITION_TYPE_SELL)
{
Trade.Close(_Symbol);
}
簡単にするために、売買制約条件を分けました。現在の終値が現在の移動平均値よりも低く、買いポジションが開いている場合、現在のポジションをクローズします。終値が移動平均線を上回り、売りポジションが現在開いている場合も同様です。
Close()関数を使用して、ポジションの一部を決済できます。以下は、現在のポジションの半分を決済する方法の簡単な例です。
double posVolume = PositionVolume();
double closeVolume = PosVolume / 2;
Trade.Close(_Symbol, closeVolume);
PositionVolume()関数は、現在の位置ボリュームを取得し、結果をposVolumeに割り当てます。posVolumeを2つで割り、ポジションの半分を計算し結果をcloseVolumeに割り当てます。closeVolumeをClose()関数の2番目のパラメータとして使用し、ポジションの半分を効果的に決済します。
この方法を使用してポジションの一部を決済する場合は、決済条件が非常に具体的であることを確認してください。そうしないと、プログラムはポジションの半分を何度も決済し続けます。この本の後半で、未決注文を使用してポジションをスケールアウトする方法を検討します。
ヘッジ口座のポジション決済 P151
ヘッジ口座のポジションを決済するには、成行注文のチケット番号が必要です。この章のほかのヘッジ関数で行ったように、Trade.mqhファイルの関数を、シンボルの代わりにチケット番号を取るTradeHedge.mqhの同じ名前の関数でオーバーロードします。
bool CTradeHedge::Close(ulong pTicket, double pVolume=0.000000, string pComment=NULL)
{
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;
request.position = pTicket;
request.deviation= deviation;
request.type_filling = fillType;
double closeVol = 0;
long openType = WRONG_VALUE;
string symbol;
if(PositionSelectByTicket(pTicket) == true)
{
closeVol = PositionGetDouble(POSITION_VOLUME);
openType = PositionGetInteger(POSITION_TYPE);
symbol = PositionGetString(POSITION_SYMBOL);
}
else return(false);
//…
}
簡潔にするために、変更された関数の部分のみを示しています。注文リクエストとロギングコードは同じです。Request.positionをpTicketに含まれるチケット番号に設定します。次に、PositionSelectByTicket()を使用してポジションを選択し、さまざまなPositionGet…()関数を使用して、注文ボリューム、注文タイプ、シンボルなど、このポジションに関する情報を取得します。
CTradeHedge::Close()の使用法はCTrade::Close()と似ていますが、シンボルの代わりにチケット番号を渡す点が異なります。
位置情報・修正・決済関数の使い方 P152
前の章で作成した単純なEAをもう一度見てみましょう。発注、変更、注文のクローズを処理できる関数ができたので、このファイルのコードの多くを新しい関数に置き換えることができます。これにより、読みやすく、書きやすく、変更しやすいEAが作成されます。
変更したSimpleExpertAdvisor with Functions.mq5を見てみましょう。
void OnTick()
{
//移動平均
double ma[ ];
ArraySetAsSeries(ma, true);
int maHandle=iMA(_Symbol, 0, MAPeriod, MODE_SMA, 0, PRICE_CLOSE);
CopyBuffer(maHandle, 0, 0, 1, ma);
//終値
double close[ ];
ArraySetSeries(close, true);
CopyClose(_Symbol, 0, 0, 1, close);
//現在の位置情報
long positionType = PositionType();
//買い成り行き注文を開く
if(close[0] > ma[0] && glBuyPlaced == false && positionType ! = POSITION_TYPE_BUY)
{
glBuyPlaced = Trade.Buy(_Symbol, TradeVolume);
//SL/TPを変更
if(glBuyPlaced == true)
{
PositionSelect(_Symbol);
double positionOpenPrice = PositionOpenPrice();
double buyStopLoss = BuyStopLoss(_Symbol, StopLoss, positionOpenPrice);
if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol, buyStopLoss);
double buyTakeProfit = BuyTakeProfit(_Symbol, TakeProfit, positionOpenPrice);
if(buyTakeProfit > 0) buyTakeProfit = AdjustBelowStopLevel(_Symbol, buyTakeProfit);
if(buyStopLoss > 0 || buyTakeProfit > 0)
Trade.ModifyPosition(_Symbol, buyStopLoss, buyTakeProfit);
glSellPlaced = false;
}
}
//売り成り行き注文を開く
// …
}
オープンポジションタイプと始値を取得するコードを、ポジション情報関数、PositionType()およびPositionOpenPrice()に置き換えました。Trade.Buy()およびTrade.Sell()関数は必要に応じてポジションを反転させるため、このEAではTrade.Close()関数を使用しないことに注意してください。次に、TradeModifyPosition()関数を使用して、注文にストップロスとテイクプロフィットを追加します。
ヘッジ口座 P154
関数(Hedgeing).mq5を使用した単純なEAへの変更は次のとおりです。
#include <Mql5Book\TradeHedg.mqh>
CTradeHedg Trade;
CPositions Positions;
//入力変数
input int MagicNumber = 12345;
void OnInit()
{
Trade.MagicNumber(MagicNumber);
}
void OnTicke()
{
//移動平均
double ma[];
ArraySetAsSeries(ma, true);
int manHandle = iMA(_Symbol, 0, MAPeriod, MODE_LWMA, 0, PRICE_CLOSE);
CopyBuffer(manHandle, 0, 0, 1, ma);
//終値
double close[];
ArraySetAsSeries(close, true);
CopyClose(_Symbol, 0, 0, 1, close);
//現在の成り行き注文を取得
ulong buyTicket = 0, sellTicket = 0;
if(Positions.Buy(MagicNumber) == 1)
{
ulong buyTickets[];
Positions.GetBuyTickets(MagicNumber, buyTikets);
buyTicket = buyTickets[0]
glBuyPlaced = true;
}
if(Psitions.Sell(MagicNumber) == 1)
{
ulong sellTickets[];
Positions.GetSellTickets(MagicNumber, sellTickets);
sellTicket = sellTickets[0];
glSellPlaced = true;
}
//買い成り行き注文を開く
if(close[0] > ma[0] && glBuyPlaced == fales)
{
//売り注文を閉じる
if(sellTicket > 0)
{
Trade.Close(sellTicket);
}
//買い注文を開く
buyTicket = Trade.Buy(_Symbol, TradeVolume);
//SL/TPを変更
if(buyTicket > 0)
{
PositionSelectByTicket(buyTicket);
double positionOpenPrice = PositionOpenPrice(buyTicket);
double buyStopLoss = BuyStopLoss(_Symbol, StopLoss, positionOpenPrice);
if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol, buyStopLoss);
double buyTakePrifit = BuyTakeProfit(_Symbol, TakeProfit, positionOpenPrice);
if(buyTakeProfit > 0) buyTakeProfit = AdjustAboveStopLevel(_Symbol, buyTakeProfit);
if(buyStopLoss > 0 || buyTakeProfit > 0)
Trade.ModifyPosition(buyTicket, buyStopLoss, buyTakeProfit);
glSellPlaced = false;
}
}
//売り成行注文を開ける
//…
}
ファイルの先頭に、Positionsという名前のCPositionsクラスオブジェクトを追加しました。また、マジックナンバーの入力変数も追加しました。マジックナンバーは、Trade.MagicNumber()関数を使用して、OnInit()イベントハンドラですべての新しい注文に割り当てられます。
OnTick()イベントハンドラで内で、CPositionsクラス関数(Positionsオブジェクトで表される)を使用して、現在開いているポジションの注文チケットを取得します。新しいポジションを開くときに、既存の反対側のポジションを閉じるためのTrade.Close()関数を追加しました。PositionOpenPrice()関数は、指定されたチケット番号の注文開始価格を取得します。最後に、Trade.ModifyPosition()関数は、指定されたチケット番号のストップロスとテイクプロフィットの価格を設定します。