【MT4】ポジション一括決済パネルEA 損益合計・BUY/SELL別・全決済ボタン付き

MT4のポジション一括決済パネルEAのアイキャッチ画像。損益合計やBUY・SELL別決済、全決済ボタンを表示した管理ツールのイメージ
EAサンプル・ノウハウ

「MT4で複数ポジションを持っていると、損益確認や決済操作が少し面倒…」
「BUYだけ閉じたい」「SELLだけ閉じたい」「一定利益や一定損失でまとめて決済したい」
そんな場面はありませんか?

この記事では、MT4チャート上にポジション管理パネルを表示する補助EAを紹介します。

このEAは新規エントリーは行わず、保有中のポジションを確認・決済しやすくするための管理ツールです。
裁量トレード中の一括決済や、EA運用時の安全確認用パネルとして使いやすい構成になっています。

この記事で紹介するEAの特徴

  • MT4チャート上にポジション管理パネルを表示
  • 合計損益・ポジション数・BUY/SELL数を確認できる
  • 利確・損切ボタンをONにしている間だけ、設定金額到達で自動全決済
  • BUYのみ・SELLのみの個別決済に対応
  • 全決済ボタンは誤クリック防止のため2回クリック式

このEAでできること・できないこと

今回のEAは、売買ロジックで新規注文を出すタイプではありません。
あくまで、すでに保有しているポジションを管理・決済するための補助EAです。

できることできないこと
保有ポジションの合計損益を表示新規エントリー
BUYのみ・SELLのみ決済売買サインの判定
利確・損切ボタンON時のみ、設定金額到達で自動全決済相場分析
表示チャートの通貨ペアだけを対象にする複雑な資金管理ロジック
マジックナンバーで対象を絞るバックテスト用の売買検証

簡単に言うと、「MT4上でポジションをまとめて見やすく・閉じやすくするためのEA」です。

ポジション管理パネルの画面イメージ

EAをチャートに設置すると、チャート上に以下のような管理パネルを表示します。

MT4チャート上に表示されるポジション一括決済パネルEAの画面

パネル上では、現在の合計損益、ポジション数、BUY/SELLの保有数を確認できます。
また、ボタン操作で利確条件・損切条件の監視ON/OFF、BUYのみ決済、SELLのみ決済、全決済ができます。

パラメータ設定について

主なパラメータは以下の通りです。

表示名内容
表示チャートの通貨ペアだけ対象 UseCurrentSymbolOnly trueの場合、EAを設置しているチャートの通貨ペアだけを対象にします。 設定例:true推奨
指定MagicNumberだけ対象 UseMagicFilter trueの場合、指定したマジックナンバーのポジションだけを対象にします。 設定例:false
対象MagicNumber TargetMagic 管理対象にするマジックナンバーを指定します。裁量ポジションを対象にしたい場合は、0にします。 設定例:0
利益条件ボタンを使用 UseProfitTargetClose trueの場合、「+○円で利確」ボタンを使えます。falseの場合は、ボタンを押しても条件監視ONになりません。 設定例:true
この金額以上で自動全決済 ProfitTarget 利確ボタンが押下状態のとき、合計損益がこの金額以上になったら対象ポジションを全決済します。 設定例:5000
損切条件ボタンを使用 UseLossCutClose trueの場合、「-○円で損切」ボタンを使えます。falseの場合は、ボタンを押しても条件監視ONになりません。 設定例:true
この金額以下で自動全決済 LossCut 損切ボタンが押下状態のとき、合計損益がこの金額以下になったら対象ポジションを全決済します。 設定例:-3000
許容スリッページ Slippage 決済時に許容するスリッページを指定します。数値を大きくすると約定しやすくなりますが、想定より不利な価格で決済される可能性もあります。 設定例:5
全決済ボタンの再クリック有効秒数 ConfirmSeconds 全決済ボタンを1回押した後、何秒以内に再クリックすれば全決済を実行するかを指定します。 設定例:5
パネルX PanelX パネルの横方向の表示位置を調整します。右上表示のため、数値を変えると左右位置が変わります。 設定例:250
パネルY PanelY パネルの縦方向の表示位置を調整します。チャート上で他の表示と重なる場合に調整します。 設定例:20
フォントサイズ FontSize パネル内の文字サイズを調整します。見づらい場合は少し大きくできます。 設定例:10
通貨ラベル CurrencyLabel パネル上に表示する通貨単位です。円口座の場合は「円」のままでOKです。 設定例:円
MT4ポジション一括決済パネルEAのパラメータ設定画面

対象ポジションの絞り込み

このEAでは、対象にするポジションを以下の2つで絞り込めます。

  • 表示チャートの通貨ペアだけにする
  • 指定したマジックナンバーだけにする

たとえば、USDJPYのチャートに設置して UseCurrentSymbolOnly=true にしておけば、USDJPYのポジションだけを対象にできます。

さらに、EAごとにマジックナンバーを分けている場合は、UseMagicFilter=true にして TargetMagic に対象の番号を入れることで、特定EAのポジションだけを管理できます。

裁量ポジションだけを管理したい場合:一般的に裁量ポジションはマジックナンバー0で扱われることが多いため、UseMagicFilter=true、TargetMagic=0 にすると裁量ポジションを対象にしやすくなります。

利確・損切ボタンは押下状態のときだけ条件監視

このEAでは、対象ポジションの合計損益を計算し、設定した利益目標または損失上限に到達した場合に自動で全決済できます。

ただし、「+5000円で利確」「-3000円で損切」ボタンを押した瞬間に決済するわけではありません。
それぞれのボタンが押下状態になっている間だけ、条件監視がONになります。

ボタン状態条件動作
利確ボタンON合計損益が ProfitTarget 以上利益目標として全決済
損切ボタンON合計損益が LossCut 以下損失上限として全決済
利確・損切ボタンOFF設定金額に到達しても監視しない自動決済しない

ポイント:利確・損切ボタンは、通常の即時決済ボタンではなく「条件監視のON/OFFボタン」です。BUYのみ決済・SELLのみ決済・全決済ボタンとは役割が違います。

複数ポジションを持つEAや、ナンピン系のポジションを管理する場合、1ポジションごとの損益ではなく、対象ポジション全体の合計損益で見られるのがポイントです。

BUYのみ・SELLのみ決済

パネルには、BUYのみ決済SELLのみ決済のボタンを用意しています。

たとえば、両建て状態になっている場合や、片側のポジションだけ整理したい場合に使いやすいです。

MT4ポジション一括決済パネルEAのBUYのみ決済とSELLのみ決済ボタン

全決済ボタンは2回クリック式

全決済ボタンは便利ですが、誤クリックすると危険です。
そのため、このEAでは1回クリックしただけでは全決済せず、5秒以内にもう一度クリックした場合だけ実行する仕組みにしています。

安全設計:全決済ボタンを押すと、まず「5秒以内に再クリックで全決済」と表示されます。5秒を過ぎると確認状態は解除されます。

このEAを使う場面

今回のポジション管理パネルEAは、以下のような場面で使いやすいです。

  • 裁量トレード中に複数ポジションをまとめて確認したい
  • BUYだけ、SELLだけをまとめて決済したい
  • 利確ボタンをONにして、一定利益に到達したらポジションをまとめて閉じたい
  • 損切ボタンをONにして、損失が一定額を超えたら自動で止めたい
  • EA運用中に対象ポジションの合計損益を見やすくしたい
  • MT4の注文一覧を開かずにチャート上で操作したい

特に、複数ポジションを扱うEAや、裁量トレードで分割エントリーをする場合は、チャート上で状態を確認できるだけでもかなり便利です。

指定ポジションをTP・SLで見張りたい場合

今回のEAは、複数ポジションをまとめて管理するパネル型のEAです。

一方で、指定した注文番号に対してTP・SLのように決済ラインを設定したい場合は、以下の記事で紹介している自動決済EAの方が向いています。

個別ポジションを細かく見張るなら自動決済EA、複数ポジションをまとめて操作するなら今回のポジション管理パネルEA、という使い分けが分かりやすいです。

使用前の注意点

このツールはEAとして使用します。
チャート上に操作パネルを表示するため見た目はインジケーターに近いですが、実際にはポジション決済を行うため、MT4のExpertsフォルダに入れて使用します。

実運用前に必ず確認してください

  • このEAはポジションを決済する機能を持っています。
  • 最初は必ずデモ口座や少額環境で動作確認してください。
  • 対象通貨ペアやマジックナンバーの設定ミスに注意してください。
  • 相場急変時や通信環境によっては、想定価格で決済できない場合があります。
  • 使用による損益について、当サイトでは責任を負いかねます。

ポジション一括決済パネルEAのダウンロード

EAファイルのダウンロードはこちら👇

📥 ポジション一括決済パネルEA をダウンロード

\ 国内最大級のEAプラットフォーム /
GogoJungle(ゴゴジャン)でEAの評価・レビューをチェック!

▶ GogoJungleでEAを探す 【MT4】ポジション一括決済パネルEA 損益合計・BUY/SELL別・全決済ボタン付き

サンプルソースコード

以下が今回のサンプルソースコードです。
MT4のエディタに貼り付けて使う場合は、必ずデモ口座で動作確認してから利用してください。

//+------------------------------------------------------------------+
//|                                    33_Position_Control_Panel.mq4 |
//|                                    Copyright © 2020-2026 ぷろぐらむFX |
//|                                             https://fx-prog.com/ |
//+------------------------------------------------------------------+

#property copyright "Copyright © 2020-2026 ぷろぐらむFX"
#property link      "https://fx-prog.com/"
#property version   "1.20"
#property strict

// ==============================
// MT4 Position Control Panel EA
// 表示チャートの通貨・MagicNumberを対象にした
// 安全設計の一括決済パネルEA
// ==============================

// ---- ステータス色
#define STATUS_WAIT   clrDimGray
#define STATUS_OK     clrSeaGreen
#define STATUS_ALERT  clrCrimson

// ---- 対象ポジション設定
input bool   UseCurrentSymbolOnly = true;    // true: 表示チャートの通貨ペアだけを対象
input bool   UseMagicFilter       = false;   // true: 指定MagicNumberだけを対象
input int    TargetMagic          = 0;       // 対象MagicNumber

// ---- 条件決済設定
input bool   UseProfitTargetClose = true;    // true: 利益条件ボタンを使用
input double ProfitTarget         = 5000;    // この金額以上で自動全決済

input bool   UseLossCutClose      = true;    // true: 損切条件ボタンを使用
input double LossCut              = -3000;   // この金額以下で自動全決済

// ---- 決済設定
input int    Slippage             = 5;       // 許容スリッページ
input int    ConfirmSeconds       = 5;       // 全決済ボタンの再クリック有効秒数

// ---- 表示設定
input int    PanelX               = 250;
input int    PanelY               = 20;
input int    FontSize             = 10;
input string CurrencyLabel        = "円";

input color  PanelTextColor       = clrWhite;
input color  LabelBgColor         = clrDimGray;
input color  ProfitButtonColor    = clrSteelBlue;
input color  LossButtonColor      = clrDarkOrange;
input color  BuyButtonColor       = clrDodgerBlue;
input color  SellButtonColor      = clrTomato;
input color  DangerButtonColor    = clrCrimson;

// ---- オブジェクト名
string PREFIX = "PCP_";

// ---- 状態管理
bool     ConfirmCloseAll   = false;
datetime ConfirmExpireTime = 0;

// ---- 情報保持
double g_totalProfit = 0;
int    g_buyCount    = 0;
int    g_sellCount   = 0;
int    g_totalCount  = 0;

//+------------------------------------------------------------------+
//| 初期化
//+------------------------------------------------------------------+
int OnInit()
{
   DeletePanelObjects();
   CreatePanel();

   UpdateTradeInfo();
   UpdatePanel();
   SetStatus("待機中", STATUS_WAIT);

   EventSetTimer(1);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 終了処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   EventKillTimer();
   DeletePanelObjects();
   Comment("");
}

//+------------------------------------------------------------------+
//| Tick処理
//+------------------------------------------------------------------+
void OnTick()
{
   RefreshPanelAndRules();
}

//+------------------------------------------------------------------+
//| Timer処理
//+------------------------------------------------------------------+
void OnTimer()
{
   RefreshPanelAndRules();

   // 全決済確認の有効期限
   if(ConfirmCloseAll && TimeCurrent() > ConfirmExpireTime)
   {
      ConfirmCloseAll = false;
      SetStatus("待機中", STATUS_WAIT);
   }
}

//+------------------------------------------------------------------+
//| パネル更新・条件決済判定
//+------------------------------------------------------------------+
void RefreshPanelAndRules()
{
   UpdateTradeInfo();
   CheckAutoClose();

   // 決済後の表示更新用
   UpdateTradeInfo();
   UpdatePanel();
}

//+------------------------------------------------------------------+
//| クリックイベント
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   if(id != CHARTEVENT_OBJECT_CLICK)
      return;

   // 利確条件ボタン
   // 押した瞬間に決済するのではなく、押下状態の間だけ条件監視ON
   if(sparam == PREFIX + "btn_profit")
   {
      ConfirmCloseAll = false;

      if(!UseProfitTargetClose)
      {
         ResetButtonState(PREFIX + "btn_profit");
         SetStatus("利益条件は設定でOFFです", STATUS_WAIT);
         UpdatePanel();
         return;
      }

      if(IsButtonPressed(PREFIX + "btn_profit"))
      {
         SetStatus("利益条件ON:" + DoubleToString(ProfitTarget, 0) + " " + CurrencyLabel + "以上で決済", STATUS_OK);
      }
      else
      {
         SetStatus("利益条件OFF", STATUS_WAIT);
      }

      UpdatePanel();
      return;
   }

   // 損切条件ボタン
   // 押した瞬間に決済するのではなく、押下状態の間だけ条件監視ON
   if(sparam == PREFIX + "btn_loss")
   {
      ConfirmCloseAll = false;

      if(!UseLossCutClose)
      {
         ResetButtonState(PREFIX + "btn_loss");
         SetStatus("損切条件は設定でOFFです", STATUS_WAIT);
         UpdatePanel();
         return;
      }

      if(IsButtonPressed(PREFIX + "btn_loss"))
      {
         SetStatus("損切条件ON:" + DoubleToString(LossCut, 0) + " " + CurrencyLabel + "以下で決済", STATUS_ALERT);
      }
      else
      {
         SetStatus("損切条件OFF", STATUS_WAIT);
      }

      UpdatePanel();
      return;
   }

   // BUYのみ決済
   // 押した瞬間に実行し、押下状態は解除
   if(sparam == PREFIX + "btn_buy")
   {
      ResetButtonState(PREFIX + "btn_buy");

      bool result = CloseOrdersByFilter(OP_BUY);

      if(result)
         SetStatus("BUYのみ決済", STATUS_OK);
      else
         SetStatus("対象BUYポジションなし / 決済失敗", STATUS_ALERT);

      ConfirmCloseAll = false;
      UpdateTradeInfo();
      UpdatePanel();
      return;
   }

   // SELLのみ決済
   // 押した瞬間に実行し、押下状態は解除
   if(sparam == PREFIX + "btn_sell")
   {
      ResetButtonState(PREFIX + "btn_sell");

      bool result = CloseOrdersByFilter(OP_SELL);

      if(result)
         SetStatus("SELLのみ決済", STATUS_OK);
      else
         SetStatus("対象SELLポジションなし / 決済失敗", STATUS_ALERT);

      ConfirmCloseAll = false;
      UpdateTradeInfo();
      UpdatePanel();
      return;
   }

   // 全決済
   // 誤クリック防止のため2回クリック式
   if(sparam == PREFIX + "btn_all")
   {
      ResetButtonState(PREFIX + "btn_all");

      if(!ConfirmCloseAll)
      {
         ConfirmCloseAll   = true;
         ConfirmExpireTime = TimeCurrent() + ConfirmSeconds;

         SetStatus("! " + IntegerToString(ConfirmSeconds) + "秒以内に再クリックで全決済", STATUS_ALERT);
         return;
      }

      bool result = CloseOrdersByFilter(-1);

      if(result)
         SetStatus("全決済を実行", STATUS_OK);
      else
         SetStatus("対象ポジションなし / 決済失敗", STATUS_ALERT);

      ConfirmCloseAll = false;
      UpdateTradeInfo();
      UpdatePanel();
      return;
   }
}

//+------------------------------------------------------------------+
//| 取引情報更新
//+------------------------------------------------------------------+
void UpdateTradeInfo()
{
   g_totalProfit = 0;
   g_buyCount    = 0;
   g_sellCount   = 0;
   g_totalCount  = 0;

   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      if(!IsTargetOrder())
         continue;

      int type = OrderType();

      if(type != OP_BUY && type != OP_SELL)
         continue;

      g_totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
      g_totalCount++;

      if(type == OP_BUY)
         g_buyCount++;

      if(type == OP_SELL)
         g_sellCount++;
   }
}

//+------------------------------------------------------------------+
//| 対象ポジション判定
//+------------------------------------------------------------------+
bool IsTargetOrder()
{
   if(UseCurrentSymbolOnly && OrderSymbol() != Symbol())
      return(false);

   if(UseMagicFilter && OrderMagicNumber() != TargetMagic)
      return(false);

   return(true);
}

//+------------------------------------------------------------------+
//| 自動決済判定
//+------------------------------------------------------------------+
void CheckAutoClose()
{
   if(g_totalCount <= 0)
      return;

   bool profitButtonOn = IsButtonPressed(PREFIX + "btn_profit");
   bool lossButtonOn   = IsButtonPressed(PREFIX + "btn_loss");

   // 利確条件ボタンが押下状態のときだけ判定
   if(UseProfitTargetClose &&
      profitButtonOn &&
      ProfitTarget > 0 &&
      g_totalProfit >= ProfitTarget)
   {
      bool result = CloseOrdersByFilter(-1);

      if(result)
         SetStatus("利益目標で自動全決済", STATUS_OK);
      else
         SetStatus("利益目標到達 / 決済失敗", STATUS_ALERT);

      ConfirmCloseAll = false;

      // 自動決済後は利確条件ボタンをOFFにする
      ResetButtonState(PREFIX + "btn_profit");

      return;
   }

   // 損切条件ボタンが押下状態のときだけ判定
   if(UseLossCutClose &&
      lossButtonOn &&
      g_totalProfit <= LossCut)
   {
      bool result = CloseOrdersByFilter(-1);

      if(result)
         SetStatus("損失上限で自動全決済", STATUS_ALERT);
      else
         SetStatus("損失上限到達 / 決済失敗", STATUS_ALERT);

      ConfirmCloseAll = false;

      // 自動決済後は損切条件ボタンをOFFにする
      ResetButtonState(PREFIX + "btn_loss");

      return;
   }
}

//+------------------------------------------------------------------+
//| 決済処理
//| typeFilter: -1=全決済, OP_BUY, OP_SELL
//+------------------------------------------------------------------+
bool CloseOrdersByFilter(int typeFilter)
{
   bool closedAny = false;

   if(!IsTradeAllowed())
   {
      Print("決済できません。自動売買が許可されていない可能性があります。");
      return(false);
   }

   RefreshRates();

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      if(!IsTargetOrder())
         continue;

      int type = OrderType();

      if(type != OP_BUY && type != OP_SELL)
         continue;

      if(typeFilter != -1 && type != typeFilter)
         continue;

      int    ticket = OrderTicket();
      string symbol = OrderSymbol();
      double lots   = OrderLots();

      bool result = CloseSelectedOrder(ticket, symbol, type, lots);

      if(result)
      {
         closedAny = true;
      }
      else
      {
         Print("OrderClose失敗 Ticket=", ticket, " Error=", GetLastError());
      }

      Sleep(100);
      RefreshRates();
   }

   return(closedAny);
}

//+------------------------------------------------------------------+
//| 1ポジション決済
//+------------------------------------------------------------------+
bool CloseSelectedOrder(int ticket, string symbol, int type, double lots)
{
   for(int retry = 0; retry < 3; retry++)
   {
      RefreshRates();

      double closePrice = 0;

      if(type == OP_BUY)
         closePrice = MarketInfo(symbol, MODE_BID);

      if(type == OP_SELL)
         closePrice = MarketInfo(symbol, MODE_ASK);

      int digits = (int)MarketInfo(symbol, MODE_DIGITS);
      closePrice = NormalizeDouble(closePrice, digits);

      ResetLastError();

      bool result = OrderClose(ticket, lots, closePrice, Slippage, clrNONE);

      if(result)
         return(true);

      int err = GetLastError();

      Print("OrderClose失敗 Ticket=", ticket,
            " Symbol=", symbol,
            " Retry=", retry + 1,
            " Error=", err);

      Sleep(300);
   }

   return(false);
}

//+------------------------------------------------------------------+
//| パネル生成
//+------------------------------------------------------------------+
void CreatePanel()
{
   int x = PanelX;
   int y = PanelY;

   CreateLabel(PREFIX + "title",
               "EA CONTROL PANEL",
               x,
               y,
               150,
               20,
               LabelBgColor,
               PanelTextColor,
               true);

   y += 24;
   CreateLabel(PREFIX + "profit",
               "損益:0",
               x,
               y,
               220,
               20,
               LabelBgColor,
               PanelTextColor,
               false);

   y += 22;
   CreateLabel(PREFIX + "count",
               "ポジ:0 (B0/S0)",
               x,
               y,
               220,
               20,
               LabelBgColor,
               PanelTextColor,
               false);

   y += 22;
   CreateLabel(PREFIX + "target",
               "対象:確認中",
               x,
               y,
               220,
               20,
               LabelBgColor,
               PanelTextColor,
               false);

   y += 28;
   CreateButton(PREFIX + "btn_profit",
                "+" + DoubleToString(ProfitTarget, 0) + CurrencyLabel + "で利確",
                x,
                y,
                140,
                24,
                ProfitButtonColor);

   y += 28;
   CreateButton(PREFIX + "btn_loss",
                DoubleToString(LossCut, 0) + CurrencyLabel + "で損切",
                x,
                y,
                140,
                24,
                LossButtonColor);

   y += 34;
   CreateButton(PREFIX + "btn_buy",
                "BUYのみ決済",
                x - 115,
                y,
                105,
                24,
                BuyButtonColor);

   CreateButton(PREFIX + "btn_sell",
                "SELLのみ決済",
                x,
                y,
                105,
                24,
                SellButtonColor);

   y += 34;
   CreateButton(PREFIX + "btn_all",
                "▲ 全決済",
                x,
                y,
                220,
                28,
                DangerButtonColor);

   y += 36;
   CreateLabel(PREFIX + "status",
               "待機中",
               x,
               y,
               220,
               20,
               LabelBgColor,
               PanelTextColor,
               false);

   ChartRedraw();
}

//+------------------------------------------------------------------+
//| パネル更新
//+------------------------------------------------------------------+
void UpdatePanel()
{
   string profitText = "損益:" + DoubleToString(g_totalProfit, 2) + " " + CurrencyLabel;

   string countText = "ポジ:" + IntegerToString(g_totalCount)
                    + " (B" + IntegerToString(g_buyCount)
                    + "/S" + IntegerToString(g_sellCount) + ")";

   string targetText = BuildTargetText();

   SetLabelText(PREFIX + "profit", profitText);
   SetLabelText(PREFIX + "count",  countText);
   SetLabelText(PREFIX + "target", targetText);

   SetButtonText(PREFIX + "btn_profit",
                 "+" + DoubleToString(ProfitTarget, 0) + CurrencyLabel + "で利確");

   SetButtonText(PREFIX + "btn_loss",
                 DoubleToString(LossCut, 0) + CurrencyLabel + "で損切");

   ChartRedraw();
}

//+------------------------------------------------------------------+
//| 対象表示テキスト作成
//+------------------------------------------------------------------+
string BuildTargetText()
{
   string targetText = "";

   if(UseCurrentSymbolOnly)
      targetText = "対象:" + Symbol();
   else
      targetText = "対象:全通貨";

   if(UseMagicFilter)
      targetText += " / Magic " + IntegerToString(TargetMagic);
   else
      targetText += " / 全Magic";

   return(targetText);
}

//+------------------------------------------------------------------+
//| ボタン押下状態取得
//+------------------------------------------------------------------+
bool IsButtonPressed(string name)
{
   if(ObjectFind(0, name) < 0)
      return(false);

   return((bool)ObjectGetInteger(0, name, OBJPROP_STATE));
}

//+------------------------------------------------------------------+
//| ボタン押下状態解除
//+------------------------------------------------------------------+
void ResetButtonState(string name)
{
   if(ObjectFind(0, name) >= 0)
      ObjectSetInteger(0, name, OBJPROP_STATE, false);
}

//+------------------------------------------------------------------+
//| ラベル作成
//+------------------------------------------------------------------+
void CreateLabel(string name,
                 string text,
                 int x,
                 int y,
                 int w,
                 int h,
                 color bg,
                 color txt,
                 bool bold)
{
   if(ObjectFind(0, name) >= 0)
      ObjectDelete(0, name);

   string bgName = name + "_bg";

   if(ObjectFind(0, bgName) >= 0)
      ObjectDelete(0, bgName);

   // 背景
   ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(0, bgName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, x + 4);
   ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, y - 2);
   ObjectSetInteger(0, bgName, OBJPROP_XSIZE, w);
   ObjectSetInteger(0, bgName, OBJPROP_YSIZE, h);
   ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, bgName, OBJPROP_BORDER_COLOR, clrBlack);
   ObjectSetInteger(0, bgName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, bgName, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, bgName, OBJPROP_BACK, false);

   // 文字
   ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, txt);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, FontSize);
   ObjectSetString(0, name, OBJPROP_FONT, bold ? "Meiryo UI Bold" : "Meiryo UI");
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
}

//+------------------------------------------------------------------+
//| ラベル文字更新
//+------------------------------------------------------------------+
void SetLabelText(string name, string text)
{
   if(ObjectFind(0, name) >= 0)
      ObjectSetString(0, name, OBJPROP_TEXT, text);
}

//+------------------------------------------------------------------+
//| ボタン作成
//+------------------------------------------------------------------+
void CreateButton(string name,
                  string text,
                  int x,
                  int y,
                  int w,
                  int h,
                  color bg)
{
   if(ObjectFind(0, name) >= 0)
      ObjectDelete(0, name);

   ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrBlack);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, FontSize);
   ObjectSetString(0, name, OBJPROP_FONT, "Meiryo UI");
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
}

//+------------------------------------------------------------------+
//| ボタン文字更新
//+------------------------------------------------------------------+
void SetButtonText(string name, string text)
{
   if(ObjectFind(0, name) >= 0)
      ObjectSetString(0, name, OBJPROP_TEXT, text);
}

//+------------------------------------------------------------------+
//| パネル関連オブジェクト削除
//+------------------------------------------------------------------+
void DeletePanelObjects()
{
   int total = ObjectsTotal(0, -1, -1);

   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, -1, -1);

      if(StringFind(name, PREFIX, 0) == 0)
         ObjectDelete(0, name);
   }

   ChartRedraw();
}

//+------------------------------------------------------------------+
//| ステータス表示
//+------------------------------------------------------------------+
void SetStatus(string text, color bgColor)
{
   string name   = PREFIX + "status";
   string bgName = name + "_bg";

   if(ObjectFind(0, name) >= 0)
      ObjectSetString(0, name, OBJPROP_TEXT, text);

   if(ObjectFind(0, bgName) >= 0)
      ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, bgColor);

   ChartRedraw();
}

ソースコードの設計ポイント解説

今回のソースコードでは、主に以下のような処理を入れています。

  • チャート上にラベルとボタンを作成する処理
  • OnChartEventでボタンクリックを判定する処理
  • 対象ポジションを通貨ペア・マジックナンバーで絞り込む処理
  • OrderProfit、OrderSwap、OrderCommissionを合算して損益表示する処理
  • 利確・損切ボタンの押下状態を使った条件監視処理
  • BUYのみ・SELLのみ・全決済を切り替える処理
  • 全決済ボタンの2回クリック確認処理
  • EA削除時にチャート上のオブジェクトを削除する処理

対象ポジションの判定

対象ポジションは、通貨ペアマジックナンバーで判定しています。

表示チャートの通貨ペアだけを対象にする場合は、UseCurrentSymbolOnlyをtrueにします。
また、特定のEAだけを対象にしたい場合は、UseMagicFilterをtrueにしてTargetMagicを指定します。

合計損益の計算

合計損益は、OrderProfitだけでなく、スワップと手数料も含めて計算しています。

そのため、ポジション単体の含み益だけでなく、実際の合計損益に近い数字を見ながら判断できます。

利確・損切ボタンの押下状態

今回のEAでは、「+5000円で利確」「-3000円で損切」ボタンを押した瞬間に決済するのではなく、ボタンが押下状態になっている間だけ条件監視ONになります。

利確ボタンがONのときは、合計損益がProfitTarget以上になった場合に自動で全決済します。
損切ボタンがONのときは、合計損益がLossCut以下になった場合に自動で全決済します。

全決済の安全確認

全決済ボタンは、1回押しただけでは実行されません。
1回目で確認メッセージを表示し、5秒以内に再クリックした場合のみ全決済します。

チャート上のボタンは便利ですが、誤クリックのリスクもあるため、この確認処理はかなり重要です。

FAQ

このEAは自動でエントリーしますか?

いいえ。今回のEAは新規エントリーは行いません。保有中のポジションを確認・決済するための補助EAです。

利確ボタンや損切ボタンを押すとすぐ決済されますか?

いいえ。「+5000円で利確」「-3000円で損切」ボタンは、押した瞬間に決済するボタンではありません。ボタンが押下状態になっている間だけ条件監視がONになり、合計損益が設定金額に到達した場合に自動で全決済します。

裁量トレードのポジションにも使えますか?

使えます。裁量ポジションを対象にしたい場合は、マジックナンバー0を対象にする設定にすると管理しやすいです。ただし、ブローカーや注文方法によって扱いが変わる場合があるため、必ずデモ口座で確認してください。

BUYだけ、SELLだけを決済できますか?

できます。パネル上の「BUYのみ決済」「SELLのみ決済」ボタンから、対象ポジションのうち片側だけを決済できます。

全決済ボタンを間違えて押したらすぐ決済されますか?

すぐには決済されません。全決済ボタンは誤クリック防止のため、1回目で確認表示、5秒以内の2回目クリックで実行する仕組みにしています。

複数通貨ペアのポジションをまとめて決済できますか?

UseCurrentSymbolOnlyをfalseにすると、表示チャートの通貨ペア以外も対象にできます。ただし、誤決済のリスクが高くなるため、基本的にはUseCurrentSymbolOnly=trueで使うことをおすすめします。

さいごに

以上が、MT4チャート上で使えるポジション一括決済パネルEAです。

今回のEAは、相場を分析して売買するタイプではありませんが、裁量トレードやEA運用中のポジション管理をかなり楽にしてくれる補助ツールです。

特に、複数ポジションを持つ場面では、合計損益・BUY/SELL数・決済ボタンがチャート上にまとまっているだけでも使いやすいと思います。

MT4のチャートオブジェクト操作、ボタン処理、ポジション検索、決済処理のサンプルとしても使えるので、EA作成の学習用としても参考にしてみてください。

 

💡 このサンプルをベースにしたカスタマイズ相談も可能です。
仕様相談・EA作成の詳細はこちら


✅ 今回のロジックをベースにしたEAサンプルも多数公開中

今回紹介したようなEAの実装例・考え方をベースに、
当サイトではさまざまなFX自動売買EAのサンプルコードを公開しています。

ロジックの違いや設計の考え方を比較しながら、
自分に合ったEA構成を探したい方はぜひチェックしてみてください。


📊 EA運用・検証フェーズに進みたい方へ

今回のような仕組みを理解したうえで、
「実際にどのEAが安定しているのか」「検証データではどんな差が出ているのか」
を確認したい方は、以下の記事も参考になります。


⚙️ 同じ条件でバックテストしたい方へ(検証環境)

当サイトのバックテストは、同一の検証環境で比較できるように統一しています。
EAの再現テストやパラメータ検証を行う場合は、
検証環境の作り方もあわせて確認しておくと理解が深まります。


EA開発初心者向けに、今後も実践的に使えるMQL4関数や実装例を紹介していきます。
気になる機能やロジックがあれば、用途別に整理した関連記事もぜひあわせてご覧ください。

コメント

タイトルとURLをコピーしました