MQL5

MQL5 でポジション決済する方法とは?ソースコードを大公開

MQL5 におけるポジションの決済(クローズ)は複雑なので、初見では戸惑うことも多いのではないでしょうか。

ここでは、一つずつ手順を追って
わかりやすくポジションを決済する方法を解説します。

これを読み終えたころには、すぐに手を動かして MT5 でポジションを決済するためのコードが書けるようになっているはずです

1.ポジション決済(クローズ)するには OrderSend 関数を使う

MQL5 では、注文に関するあらゆる操作は OrderSend() 関数を使います。「ポジションの決済(クローズ)」に関しても、もちろん使うのは  OrderSend() 関数です。

なお、下記の記事では 目的とする操作別に、OrderSend 関数の各パラメータをそれぞれどう設定すればよいかをすべて詳細に解説しているので、もし本記事にてわかりにくい場所があったら合わせて参照してみてください。

⇒ MQL5の OrderSend() 関数の使い方を完璧に解説!

2.【警告】「必ず注意すべき重大ミス」とは?

「トレードに関するあらゆる操作を1つの関数で完結できること」は、OrderSend() 関数のメリットであり、かつデメリットでもあります。

実際、たった1つの関数で複数のトレード操作ができるので、どの操作を行いたいかの区別はどのようにすればいいのか迷うのではないでしょうか?

以下では絶対に注意するべき「重大なミス」とその回避方法に関して簡単に説明します。

エントリー・エグジットを分けるのは position パラメータを設定するか、のみ

「エントリーとエグジットの区別」に関して述べると、OrderSend() 関数においてエントリー・エグジットのどちらが行われるかは、

“position パラメータに(決済したいポジションの)チケット番号を設定するかしないか”

 

という点のみで決まります。

したがって positionパラメータを設定し忘れただけで、「ポジションを決済したいのに誤ってポジションをエントリーしてしまう」ということが起き得るのです。

あらゆる場合を想定して、誤エントリーを回避するようなコーディングを行う必要があります。

どういう場合にMT5 ターミナル側は「エグジット」を「エントリー」として “誤認識” してしまうか?

※ 現時点で、この問題に関して言及している人は日本語のサイトではもちろん外国のサイトでもほとんどいませんが、非常に危険な問題なのでしっかりと理解してください。

MT5 側は position パラメータが設定されていないと、エントリーであると認識します。
今回は「ポジションを決済する方法」を行うので、position パラメータの設定は必須です

ただ、position パラメータを設定したはずなのに、MT5 側に「設定していない」と認識されてしまう場合があります。

position パラメータに指定するチケット番号が何らかの不具合で 0 になってしまった場合

 

です。

position パラメータが(意図せずして)0 になってしまうのは例えば次の場合が考えられます。

  • (position パラメータに入力するための)チケット番号の取得に失敗し、0 になってしまうケース
  • MT5 ターミナルが再起動され、変数の値を紛失してしまうケース(勝手に起きてしまうこともあります)

などです。もちろん他にも多くのケースが考えられると思いますが。

※ なお、エラーを回避するための具体的なプログラミング方法に関しては、positionパラメータの箇所をご覧ください。

3.プログラミングの際の手順

では、実際にコードを書いていきましょう。

ここでは、(現在保有中の)ポジションを成り行きで決済する状況を想定して、コードを書いていきます。
なお凡庸性を持たせるために、「ポジションを成り行きで決済するための “関数“」を作ってみましょう。

ステップとしては以下のような 4つのステップになります。

STEP0: 関数名と引数を決める!

STEP1: MqlTradeRequest, MqlTradeResult 構造体を宣言する!

STEP2: MqlTradeRequest 構造体のメンバー変数を設定する!

STEP3: OrderSend 関数を実行する!

STEP4: MqlTradeResult 構造体をで結果を確認する!

 

STEP0:関数名と引数を決める!

これから作っていく関数名は、わかりやすく ”CloseMarketPosition()“ という名前にしてみます。

引数は以下のようにしてみました。

  • MagicNumber:    EAのマジックナンバー
  • OrderAndPositionTicket:  決済するポジションのチケット番号
  • ExitSymbol:  トレードする銘柄名
  • MarketPosition:  現在保有ポジション(0: ポジションなし、1: ロングポジション、-1: ショートポジション)
  • SlippagePoints:  許容スリッページ
  • FillingMode:  Filling mode(約定方式)は FX業者によって異なるので外部から設定できるようにしておく
void CloseMarketPosition(
   ulong MagicNumber,
   ulong &OrderAndPositionTicket,
   string ExitSymbol, 
   int &MarketPosition, 
   int SlippagePoints, 
   ENUM_ORDER_TYPE_FILLING FillingMode
   ) 
{
   ……………
   ……………
}

引数が決まったので、これから …… の部分に関数の中身を書いていきます。

STEP1:MqlTradeRequest, MqlTradeResult 構造体を宣言する!

ポジションを成り行き決済するには OrderSend 関数を使いますが、
そのためにはまず MqlTradeRequest構造体と MqlTradeResult 構造体を用意しないといけません

(※ MqlTradeRequest 構造体・MqlTradeResult 構造体について理解していない方は、MqlTradeRequest , MqlTradeResult 構造体について解説しているこちらのページをまずはご覧ください。)

名前はわかりやすく CloseRequest、CloseResult などとして以下のように宣言します。

void CloseMarketPosition(...)
{
   MqlTradeRequest CloseRequest; // MqlTradeRequest構造体を宣言
   MqlTradeResult CloseResult;   // MqlTradeResult構造体を宣言

   ZeroMemory(CloseRequest);
   ZeroMemory(CloseResult);

   ………

なお、前に使ったときの情報が残っていると意図しないトレード操作をする可能性があるので
構造体は2つとも ZeroMemory()  関数によってメモリーをクリアしておきます。

STEP2: MqlTradeRequest 構造体のメンバー変数を設定する!

STEP1 で MqlTradeRequest構造体と MqlTradeResult 構造体 が準備できたので、
次は行うトレード操作を決めるために MqlTradeRequest構造体 のメンバー変数を設定する段階です。

以下に1つ1つ慎重に行っていきます。

(1) action パラメータの設定

トレード操作の種類を設定するパラメータです。成り行き注文なので、’ TRADE_ACTION_DEAL ‘ になります。

以下のようにして、設定します。

CloseRequest.action = TRADE_ACTION_DEAL;

(2) magic パラメータの設定

マジックナンバーです。もともと関数の引数になっているので、そのまま渡します。

CloseRequest.magic = MagicNumber;

(3) deviation パラメータの設定

許容スリッページ(もちろんポイント単位)。こちらも関数の引数になっているのでそのまま渡しましょう。

CloseRequest.deviation = SlippagePoints;

(4) type_filling パラメータの設定

フィリングタイプ (= 約定方式)も同様。

CloseRequest.type_filling = FillingMode;

(5) type パラメータと price パラメータの設定

これらは関連するので、一緒にやります。

  • type パラメータ … 買いの成り行き・売りの逆指値 など、「注文の種類」の指定です
  • price パラメータ … 注文価格

なので、決済したいポジションが

  • 買いポジションを決済する場合 ⇒ 売り注文を行うので type は ORDER_TYPE_SELL、price は Bid を指定
  • 売りポジションを決済する場合 ⇒ 買い注文を行うので type は ORDER_TYPE_BUY、price は Ask を指定

する流れになります。

現在保有ポジションの管理は先ほど MarketPosition という引数で行ったので、次のようにコーディングできます。

if (MarketPosition == 1) // 買いポジションを決済する場合
{
   CloseRequest.type = ORDER_TYPE_SELL;
   CloseRequest.price = SymbolInfoDouble(ExitSymbol,SYMBOL_BID); 
}

if (MarketPosition == -1) // 売りポジションを決済する場合
{
   CloseRequest.type = ORDER_TYPE_BUY;
   CloseRequest.price = SymbolInfoDouble(ExitSymbol,SYMBOL_ASK); 
}

Ask や Bid を取得する際に SymbolInfoDouble() 関数を用いました。

一応公式サイトでは Ask や Bid の取得には SymbolInfoDouble() ではなく MqlTick 構造体による取得が推奨されているので、EA内に記述する際は MqlTick 構造体を使った方がよいかもしれません。あまり動作上の大差はないはずですが。

(関数にする分には SymbolInfoDouble() の方が使い勝手がよいので今回は SymbolInfoDouble を使いました)

(6) position パラメータ、symbol パラメータ、volume パラメータの設定

position パラメータ、symbol パラメータ、volume パラメータの 3つは「決済したいポジション」に関する情報なので、まとめて解説します。

 

まずは決済するポジションを選択します。選択すると情報の取得が可能になります。

if ( PositionSelectByTicket(OrderAndPositionTicket)==false ) return;

(今作っている関数では)チケット番号を引数としたので、(PositionSelectではなく)PositionSelectByTicket() を使えばいいでしょう。

( ⇒ 詳しくはこちらの記事をご覧ください)

もし Select に失敗したら関数を終了させます

Select が完了したので、ロット数 と 銘柄に関する情報を取得して、それぞれ volume と symbol パラメータに入れてあげます

CloseRequest.symbol = PositionGetString(POSITION_SYMBOL);
CloseRequest.volume = PositionGetDouble(POSITION_VOLUME);

volume に関しては「実際の保有中ポジションに関する情報を参照する」ことをせずに、引数などで直接設定しようとする人もいるようですがやめたほうがいいでしょう。

マーケットの状況によっては、予定していたロット数をすべて約定できない場合もあるからです。ロット数でエラーが出るとポジションの決済自体ができない可能性もあります。

 

最後に position パラメータです。
‘position’ には決済したいポジションのチケット番号を指定する必要があります。

今回は引数になっているので、そのまま指定するだけです。

CloseRequest.position = OrderAndPositionTicket; // 決済したいポジションのチケット番号を指定

となります。

(7) comment パラメータ

コメントは任意です。今回は設定しません。

私は普段、どのくらいのスリッページがあったかを把握するために活用したりしています。

(8) 設定が不要なパラメータに関して

以下のパラメータは、「成り行きでポジションを決済する」際には設定が不要です。

  • stoplimit パラメータ
  • sl パラメータ
  • tp パラメータ
  • order パラメータ
  • type_time パラメータ
  • expiration パラメータ
  • position_by パラメータ

したがって何も記述する必要がありません。

STEP3: OrderSend 関数を実行する!

準備ができたので、やっと OrderSend 関数が実行できます。

一応、OrderCheck() にて 「MqlTradeRequest 構造体の入力に不備がないか」の確認をここに入れることも可能ですが、今回はパスします。

string ExitName;
if (MarketPosition == 1) ExitName = "LONG";
if (MarketPosition == -1) ExitName = "SHORT";

if(!OrderSend(CloseRequest,CloseResult)) // OrderSend 関数の実行!!
{ 
   int error = GetLastError();

   printf ("OrderSend Error : [%s EXIT] for %s failed @(Magic: %d) // OrderSend Error : %d",ExitName,ExitSymbol,MagicNumber,error);
   SendNotification("OrderSend Error : [" + ExitName + " EXIT] for " + ExitSymbol + " failed @(Magic: " + (string)MagicNumber + ") // OrderSend Error : " + (string)error );
}

OrderSend 関数の戻り値が false の場合に、ログへ出力するコードもついでに書いてみました。
(上3段もログ出力のためのコードになります。無視しても構いません)

STEP4: MqlTradeResult 構造体を確認する!

OrderSend 関数の実行後は、エラーがなかったかを確認する必要があります。

OrderSend の戻り値としてもある程度までは確認が可能ですが、詳細は MqlTradeResult 構造体をチェックしなければなりません

(戻り値が決まる段階では、MT5 側がまだエラー自体を把握できていない場合もあります。いずれにしろ MqlTradeResult 構造体のチェックは必須です。)

OrderSend のエラーに関しては MqlTradeResult 構造体の retcode メンバーをチェックすればわかります。

成功した場合、
retcode は TRADE_RETCODE_DONE あるいは TRADE_RETCODE_PLACED になるので以下のような「エラー処理のコード」が書けます。

if(CloseResult.retcode != TRADE_RETCODE_DONE && CloseResult.retcode != TRADE_RETCODE_PLACED) 
{ 
   printf ("Trade Result Error : [%s EXIT] for %s failed @(Magic: %d) // TradeResult Error : %d",ExitName,ExitSymbol,MagicNumber,CloseResult.retcode);
   SendNotification("Trade Result Error : [" + ExitName + " EXIT] for " + ExitSymbol + " failed @(Magic: " + (string)MagicNumber + ") // TradeResult Error : " + (string)CloseResult.retcode);
}

成功していた場合には現在保有ポジションは0になるので、以下のようなコードも追加しておきます。(ハイライトされている範囲です)

if(CloseResult.retcode != TRADE_RETCODE_DONE && CloseResult.retcode != TRADE_RETCODE_PLACED) 
{ 
   printf ("Trade Result Error : [%s EXIT] for %s failed @(Magic: %d) // TradeResult Error : %d",ExitName,ExitSymbol,MagicNumber,CloseResult.retcode);
   SendNotification("Trade Result Error : [" + ExitName + " EXIT] for " + ExitSymbol + " failed @(Magic: " + (string)MagicNumber + ") // TradeResult Error : " + (string)CloseResult.retcode);
}

else // set MarketPosition
{
   MarketPosition = 0;
   OrderAndPositionTicket = 0;
}

これで終了です!

【まとめ】できあがったコードは次の通り

では今まで書きてきたコードを全てまとめます!

現在保有中のポジションを成り行きで決済する関数 CloseMarketPosition() がやっと完成しました。

void CloseMarketPosition(
   ulong MagicNumber,
   ulong &OrderAndPositionTicket,
   string ExitSymbol, 
   int &MarketPosition, 
   int SlippagePoints, 
   ENUM_ORDER_TYPE_FILLING FillingMode
   ) 
{
   MqlTradeRequest CloseRequest; // MqlTradeRequest構造体を宣言
   MqlTradeResult CloseResult;   // MqlTradeResult構造体を宣言

   ZeroMemory(CloseRequest);
   ZeroMemory(CloseResult);

   CloseRequest.action = TRADE_ACTION_DEAL;   // 取引リクエストの種類
   CloseRequest.magic = MagicNumber;   // マジックナンバー
   CloseRequest.deviation = SlippagePoints;  // 許容スリッページ
   CloseRequest.type_filling = FillingMode;   // 約定方式

   if (MarketPosition == 1) // 買いポジションを決済する場合
   {
      CloseRequest.type = ORDER_TYPE_SELL;
      CloseRequest.price = SymbolInfoDouble(ExitSymbol,SYMBOL_BID); 
   }

   if (MarketPosition == -1) // 売りポジションを決済する場合
   {
      CloseRequest.type = ORDER_TYPE_BUY;
      CloseRequest.price = SymbolInfoDouble(ExitSymbol,SYMBOL_ASK); 
   }

   // Select positions

   if ( PositionSelectByTicket(OrderAndPositionTicket)==false ) return;
   CloseRequest.symbol = PositionGetString(POSITION_SYMBOL);
   CloseRequest.volume = PositionGetDouble(POSITION_VOLUME);
   
   CloseRequest.position = OrderAndPositionTicket; // position ticket

   // OrderCheck can be written here (optional) -----------------

   string ExitName;
   if (MarketPosition == 1) ExitName = "LONG";
   if (MarketPosition == -1) ExitName = "SHORT";
   
   if(!OrderSend(CloseRequest,CloseResult)) // OrderSend 関数を実行!!
   { 
      int error = GetLastError();

      printf ("OrderSend Error : [%s EXIT] for %s failed @(Magic: %d) // OrderSend Error : %d",ExitName,ExitSymbol,MagicNumber,error);
      SendNotification("OrderSend Error : [" + ExitName + " EXIT] for " + ExitSymbol + " failed @(Magic: " + (string)MagicNumber + ") // OrderSend Error : " + (string)error );
   }

   // check Order Result ---------------------------------------- & check for errors

   if(CloseResult.retcode != TRADE_RETCODE_DONE && CloseResult.retcode != TRADE_RETCODE_PLACED) 
   { 
      printf ("Trade Result Error : [%s EXIT] for %s failed @(Magic: %d) // TradeResult Error : %d",ExitName,ExitSymbol,MagicNumber,CloseResult.retcode);
      SendNotification("Trade Result Error : [" + ExitName + " EXIT] for " + ExitSymbol + " failed @(Magic: " + (string)MagicNumber + ") // TradeResult Error : " + (string)CloseResult.retcode);
   }

   else // set MarketPosition
   {
      MarketPosition = 0;
      OrderAndPositionTicket = 0;
   }
}

以上です!!

   

コメント

  1. とまと より:

    お忙しいところ失礼いたします。
    mql5を勉強しはじめたばかりで、
    ポジションを決済するところで挫折しかかっています。
    Auto close.ex5 という感じのものを作りたいと思い、
    色々と調べていたら、
    ルイス様のこちらのサイトに辿りつきました。
    こちらのコードをそのまま実行しようと思い
    コンパイルしたのですが、
    エラーが出てコンパイルできず立ち止まっています。。
    18行~24行にエラーがあるようで、
    24 erros, 1 warnings
    と、赤文字で表示されています。
    お忙しい中、大変恐縮ですが
    ご教示願えましたら助かります。

コメントを残す

*

CAPTCHA