MQL5でポジションを決済する方法|OrderCloseは無い

先に結論です。MQL4の OrderClose はMQL5に存在しません。MQL4から移ってきて最初に詰まるのがここです。

MQL5ではCTrade クラスの PositionClose を使うのがいちばん短く書けます。保有中のポジションは PositionsTotal と PositionGetTicket で列挙します。

(確認環境:MetaTrader 5 / Windows 11)

最小のコード

#include <Trade\Trade.mqh>

CTrade trade;

void CloseAll()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0)
         continue;
      trade.PositionClose(ticket);
     }
  }

#include <Trade\Trade.mqh> は標準ライブラリなので、追加のファイルを用意する必要はありません。

🔴 ループは必ず後ろから回す

上のコードで i-- にしているのには理由があります。

ポジションを閉じると、その分リストが詰まってインデックスがずれます。前から回すと、閉じた直後の1件を飛ばします。

回し方 結果
for(i=0; i<PositionsTotal(); i++) 🔴 1つ飛ばしになる
for(i=PositionsTotal()-1; i>=0; i--) 正しく全部閉じる

「全部閉じたはずなのに半分残る」という症状は、ほぼこれが原因です。ループ条件に PositionsTotal() を直接書いている場合は特に起きます。閉じるたびに上限が減るためです。

PositionGetTicket は選択も兼ねている

公式リファレンスによると、PositionGetTicket はチケットを返すと同時に、そのポジションを操作対象として選択します。

つまり、呼んだ直後であれば PositionGetString や PositionGetDouble がそのまま使えます。

ulong  ticket = PositionGetTicket(i);
string sym    = PositionGetString(POSITION_SYMBOL);
long   magic  = PositionGetInteger(POSITION_MAGIC);
double profit = PositionGetDouble(POSITION_PROFIT);

別途 PositionSelect を呼ぶ必要はありません。ただし時間が経ってから参照する場合は、最新の情報を得るために選択し直すことが推奨されています。

対象を絞って閉じる

全部閉じるより、条件で絞る方が実用的です。

for(int i = PositionsTotal() - 1; i >= 0; i--)
  {
   ulong ticket = PositionGetTicket(i);
   if(ticket == 0)
      continue;

   // 自分のEAが建てたものだけ
   if(PositionGetInteger(POSITION_MAGIC) != MyMagic)
      continue;

   // このチャートの銘柄だけ
   if(PositionGetString(POSITION_SYMBOL) != _Symbol)
      continue;

   trade.PositionClose(ticket);
  }

マジックナンバーで絞らないと、手動で建てたポジションや他のEAのポジションまで閉じます。複数のEAを同じ口座で動かしている場合、ここを抜かすと事故になります。

一部だけ閉じる

数量を指定すれば部分決済になります。

trade.PositionClosePartial(ticket, 0.05);

指定できる数量には最小取引単位と刻み幅の制限があります。銘柄仕様を超えた値を渡すと拒否されます。画面側からの操作はMT5の分割決済(部分利確)のやり方にまとめています。

🔴 口座タイプで前提が変わる

公式リファレンスによると、ネッティング口座では1つの銘柄につきポジションは常に1つだけです。ヘッジング口座では同じ銘柄で複数持てます。

口座タイプ 同一銘柄のポジション ループの意味
ネッティング 常に1つ 銘柄数だけ回る
ヘッジング 複数 建てた回数だけ回る

ヘッジング口座で書いたコードをネッティング口座で動かすと、想定した件数だけ回りません。口座タイプは ACCOUNT_MARGIN_MODE で確認できます。

失敗した時に原因を見る

PositionClose は成否を返します。失敗を黙って無視すると、閉じたつもりで残ります。

if(!trade.PositionClose(ticket))
   Print("決済失敗 ticket=", ticket,
         " retcode=", trade.ResultRetcode(),
         " ", trade.ResultRetcodeDescription());

よく出るのは取引時間外と価格の制限に引っかかっているケースです。後者はMQL5でinvalid stopsが出る理由で扱っています。出力の確認方法はMQL5のPrintとログの場所にまとめました。

何に使うか

決済のコードは、時間で区切って自動的に閉じる仕組みの中核になります。時刻の取り方と組み合わせる形はMQL5で一定時間ごとに処理する方法に、考え方はMT5で決済だけを自動化する方法にまとめています。

標準機能でまとめて閉じたいだけなら、コードを書く必要はありません。MT5で複数ポジションをまとめて決済する方法を参照してください。

まとめ

  1. MQL5に OrderClose は無い。CTrade::PositionClose を使う
  2. 🔴 ループは後ろから回す。前から回すと1つ飛ばしになる
  3. PositionGetTicket は選択も兼ねる
  4. マジックナンバーと銘柄で必ず絞る
  5. 🔴 ネッティング口座は1銘柄1ポジション。件数の前提が変わる

決済用のEAを入れたのに手動のポジションが閉じない場合は、マジックナンバーの扱いが原因のことがあります。MT5の決済用EAが手動ポジションに効かない理由にまとめました。

本記事はMQL5の仕様と実装方法を説明したものであり、特定の売買を推奨するものではありません。実口座で動かす前に必ずデモ口座で検証してください。投資判断はご自身の責任で行ってください。