ホーム » FX自動売買基礎と応用 » MT5版のボタン操作で全決済するツールを作成する【MQLプログラミングの基礎】

MT5版のボタン操作で全決済するツールを作成する【MQLプログラミングの基礎】

1.決済用ボタン追加

この記事では、チャート上に設置したボタン操作で、保有している全てのポジションを決済するツールを作成します。

※この記事はMQL5の内容です。

ファイルは新規作成せず、以前の記事で作成した、ボタン操作でエントリーするEA「TradePanel.mq5」を流用します。

「TradePanel.mq5」を改修して機能を追加します。

ファイル「TradePanel.mq5」をMetaEditorで開き、OnInit関数内の下記の行


   ButtonCreate(0, PREFIX + "Buy", 0, 80, 20, 80, 40, CORNER_LEFT_UPPER, "Buy", "Arial Black", 20, clrWhite, clrRed, clrNONE);

の下に次のコードを追記し、決済用の「Exit」ボタンを追加します。


   ButtonCreate(0, PREFIX + "Exit", 0, 0, 60, 160, 40, CORNER_LEFT_UPPER, "Exit", "Arial Black", 20, clrBlack, clrGold, clrNONE);

決済ボタン追加

2.ボタン戻しの回路を追加

OnTimer関数内の下記の行


   static bool stateBuy = false, stateSell = false;

に変数stateExitを追加して次のようにします。


   static bool stateBuy = false, stateSell = false, stateExit = false;

また、下記の行


   stateSell = ObjectGetInteger(0, PREFIX + "Sell", OBJPROP_STATE);

の下に次のコードを追記します。


   if (stateExit) ObjectSetInteger(0, PREFIX + "Exit", OBJPROP_STATE, false);
   stateExit = ObjectGetInteger(0, PREFIX + "Exit", OBJPROP_STATE);

上記を追記することで、Exitボタンを押しても自動で戻るようになります。

3.決済処理用の関数を作成

決済用の関数を作成するために、MQLリファレンス内にある例文を利用します。

MetaEditorメニューの「ヘルプ」から「MQL5リファレンス」を開いて、「目次」タブを選択し、「標準的な定数、列挙と構造体」→「取引定数」→「取引操作の種類」を開きます。

取引操作の種類

下へスクロールし、「ポジションを決済する 取引操作TRADE_ACTION_DEALの例:」の内容を表示します。

ポジションを決済する

プログラム最下部(Entry関数の下)に、上記の例のOnStart関数をコピーして貼り付けます。

――――― コピーして貼り付けするコード(ここから) ―――――


//+------------------------------------------------------------------+
//| 全てのポジションを決済                                                  |
//+------------------------------------------------------------------+
void OnStart()
 {
//--- 結果とリクエストの宣言
  MqlTradeRequest request;
  MqlTradeResult  result;
  int total=PositionsTotal(); // 保有ポジション数
//--- 全ての保有ポジションの取捨
  for(int i=total-1; i>=0; i--)
    {
    //--- 注文のパラメータ
    ulong  position_ticket=PositionGetTicket(i);                                     // ポジションチケット
    string position_symbol=PositionGetString(POSITION_SYMBOL);                       // シンボル 
    int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);             // 小数点以下の桁数
    ulong  magic=PositionGetInteger(POSITION_MAGIC);                                 // ポジションのMagicNumber
    double volume=PositionGetDouble(POSITION_VOLUME);                                 // ポジションボリューム
    ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);   // ポジションタイプ
    //--- ポジション情報の出力
    PrintFormat("#%I64u %s  %s  %.2f  %s [%I64d]",
                 position_ticket,
                 position_symbol,
                EnumToString(type),
                 volume,
                DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                 magic);
    //--- MagicNumberが一致している場合
    if(magic==EXPERT_MAGIC)
       {
        //--- リクエストと結果の値のゼロ化
        ZeroMemory(request);
        ZeroMemory(result);
        //--- 操作パラメータの設定
        request.action   =TRADE_ACTION_DEAL;       // 取引操作タイプ
        request.position =position_ticket;         // ポジションチケット
        request.symbol   =position_symbol;         // シンボル 
        request.volume   =volume;                   // ポジションボリューム
        request.deviation=5;                       // 価格からの許容偏差
        request.magic    =EXPERT_MAGIC;             // ポジションのMagicNumber
        //--- ポジションタイプによる注文タイプと価格の設定
        if(type==POSITION_TYPE_BUY)
          {
           request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
           request.type =ORDER_TYPE_SELL;
          }
        else
          {
           request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
           request.type =ORDER_TYPE_BUY;
          }
        //--- 決済情報の出力
        PrintFormat("Close #%I64d %s %s",position_ticket,position_symbol,EnumToString(type));
        //--- リクエストの送信
        if(!OrderSend(request,result))
          PrintFormat("OrderSend error %d",GetLastError()); // リクエストの送信に失敗した場合、エラーコードを出力
        //--- 操作情報  
        PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
        //---
       }
    }
 }
//+------------------------------------------------------------------+

――――― コピーして貼り付けするコード(ここまで) ―――――

関数名を変更します。下記の行の


void OnStart()

OnStartExitへ変更します。


void Exit()

マジックナンバーの指定を変更します。

下記の行の


    if(magic==EXPERT_MAGIC)

EXPERT_MAGICをパラメーターのMAGICへ変更します。


    if(magic==MAGIC)

同様に、下記の行を


        request.magic    =EXPERT_MAGIC;             // ポジションのMagicNumber

次のように変更します。


        request.magic    =MAGIC;                   // ポジションのMagicNumber

パラメーターの許容スリッページを反映するため、下記の行を


        request.deviation=5;                       // 価格からの許容偏差

次のように変更します。


        request.deviation=int(SLIP * 10);          // 価格からの許容偏差

ボリューム実行ポリシー(request.type_filling)を指定するために、関数内の先頭にある下記の行の下へ


void Exit()
  {

次のコードを追記して変数fillingを定義します。


   ENUM_ORDER_TYPE_FILLING filling;
   long mode = SymbolInfoInteger(NULL, SYMBOL_FILLING_MODE);
   if ((mode & SYMBOL_FILLING_IOC) != 0) filling = ORDER_FILLING_IOC;
   else if ((mode & SYMBOL_FILLING_FOK) != 0) filling = ORDER_FILLING_FOK;
   else filling = ORDER_FILLING_RETURN;

注文リクエストへ実行ポリシーを指定するため、下記の行の下へ


        request.deviation=int(SLIP * 10);          // 価格からの許容偏差

次のコードを追記します。


        request.type_filling = filling;            // 注文充填

決済時に音を鳴らすため、下記の行の下へ


           PrintFormat("OrderSend error %d",GetLastError()); // リクエストの送信に失敗した場合、エラーコードを出力

次のコードを追記します。


        else PlaySound("ok.wav");

上記のExit関数で実行されている処理のおおまかな流れは、下記の通りです。

  • ① for文で全ての保有ポジションの情報を取得
  • ② マジックナンバーが一致しているポジションのみに処理を実行
  • ③ ポジションのタイプ(売買方向)に合わせて、反対の売買を実行し、ポジションを決済(買いポジションの場合、売り取引を送信して決済する)

4.ボタン操作で決済

Exitボタンクリック時に、Exit関数を実行させるため、OnChartEvent関数の「if (id == CHARTEVENT_OBJECT_CLICK) { }」内の下記コード(売りエントリーのif文)


         Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
      }

の下へ次のコードを追記します。


      if (sparam == PREFIX + "Exit") {
         Exit();
      }

上記までの改修で今回の作業は完了です。

コンパイル後、「Sell」「Buy」ボタンを押すとポジションを持ち、「Exit」ボタンを押すと全決済されることが確認できます。

5.ソースコード

今回、作成したソースコードは下記の通りです。


//+------------------------------------------------------------------+
//|                                                   TradePanel.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#define PREFIX MQLInfoString(MQL_PROGRAM_NAME) + "_"

input int MAGIC = 1;       // マジックナンバー
input double LOT = 0.1;    // ロット
input double TP = 50;      // TP [pips]
input double SL = 50;      // SL [pips]
input double SLIP = 1;     // 許容スリッページ [pips]
input string COMMENT = ""; // コメント

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   ButtonCreate(0, PREFIX + "Sell", 0, 0, 20, 80, 40, CORNER_LEFT_UPPER, "Sell", "Arial Black", 20, clrWhite, clrDodgerBlue, clrNONE);
   ButtonCreate(0, PREFIX + "Buy", 0, 80, 20, 80, 40, CORNER_LEFT_UPPER, "Buy", "Arial Black", 20, clrWhite, clrRed, clrNONE);
   ButtonCreate(0, PREFIX + "Exit", 0, 0, 60, 160, 40, CORNER_LEFT_UPPER, "Exit", "Arial Black", 20, clrBlack, clrGold, clrNONE);
//--- create timer
   EventSetMillisecondTimer(100);
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, PREFIX);
//--- destroy timer
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   static bool stateBuy = false, stateSell = false, stateExit = false;
   if (stateBuy) ObjectSetInteger(0, PREFIX + "Buy", OBJPROP_STATE, false);
   stateBuy = ObjectGetInteger(0, PREFIX + "Buy", OBJPROP_STATE);
   if (stateSell) ObjectSetInteger(0, PREFIX + "Sell", OBJPROP_STATE, false);
   stateSell = ObjectGetInteger(0, PREFIX + "Sell", OBJPROP_STATE);
   if (stateExit) ObjectSetInteger(0, PREFIX + "Exit", OBJPROP_STATE, false);
   stateExit = ObjectGetInteger(0, PREFIX + "Exit", OBJPROP_STATE);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int32_t id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if (id == CHARTEVENT_OBJECT_CLICK) {
      if (sparam == PREFIX + "Buy") {
         double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
         double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
      }
      if (sparam == PREFIX + "Sell") {
         double price = SymbolInfoDouble(NULL, SYMBOL_BID);
         double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
         double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
         Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
      }
      if (sparam == PREFIX + "Exit") {
         Exit();
      }
   }
  }
//+------------------------------------------------------------------+ 
//| ボタンを作成する                                                      | 
//+------------------------------------------------------------------+ 
bool ButtonCreate(const long              chart_ID=0,               // チャート識別子 
                const string            name="Button",           // ボタン名 
                const int               sub_window=0,             // サブウィンドウ番号 
                const int               x=0,                     // X 座標 
                const int               y=0,                     // Y 座標 
                const int               width=50,                 // ボタンの幅 
                const int               height=18,               // ボタンの高さ 
                const ENUM_BASE_CORNER  corner=CORNER_LEFT_UPPER, // アンカーに使用されるチャートのコーナー 
                const string            text="Button",           // テキスト 
                const string            font="Arial",             // フォント 
                const int               font_size=10,             // フォントサイズ 
                const color             clr=clrBlack,             // テキストの色 
                const color             back_clr=C'236,233,216', // 背景色 
                const color             border_clr=clrNONE,       // 境界線の色 
                const bool              state=false,             // 押される/放される 
                const bool              back=false,               // 背景で表示する 
                const bool              selection=false,         // 強調表示して移動 
                const bool              hidden=true,             // オブジェクトリストに隠す 
                const long              z_order=0)               // マウスクリックの優先順位 
 { 
//--- エラー値をリセットする 
  ResetLastError(); 
//--- ボタンを作成する 
  if(!ObjectCreate(chart_ID,name,OBJ_BUTTON,sub_window,0,0)) 
    { 
    Print(__FUNCTION__, 
          ": failed to create the button! Error code = ",GetLastError()); 
    return(false); 
    } 
//--- ボタン座標を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
  ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
//--- ボタンサイズを設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); 
  ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height); 
//--- ポイント座標が相対的に定義されているチャートのコーナーを設定 
  ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); 
//--- テキストを設定する 
  ObjectSetString(chart_ID,name,OBJPROP_TEXT,text); 
//--- テキストフォントを設定する 
  ObjectSetString(chart_ID,name,OBJPROP_FONT,font); 
//--- フォントサイズを設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size); 
//--- テキストの色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
//--- 背景色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr); 
//--- 境界線の色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_COLOR,border_clr); 
//--- 前景(false)または背景(true)に表示 
  ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
//--- ボタンの状態を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_STATE,state); 
//--- マウスでのボタンを移動させるモードを有効(true)か無効(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
  ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
//--- オブジェクトリストのグラフィックオブジェクトを非表示(true)か表示(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
//--- チャートのマウスクリックのイベントを受信するための優先順位を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); 
//--- 実行成功 
  return(true); 
 } 
//+------------------------------------------------------------------+
//| エントリー関数                                                   | 
//+------------------------------------------------------------------+ 
void Entry(ENUM_ORDER_TYPE type, double lot, double price, double sl, double tp, int slip, int magic, string comment)
{
   ENUM_ORDER_TYPE_FILLING filling;
   long mode = SymbolInfoInteger(NULL, SYMBOL_FILLING_MODE);
   if ((mode & SYMBOL_FILLING_IOC) != 0) filling = ORDER_FILLING_IOC;
   else if ((mode & SYMBOL_FILLING_FOK) != 0) filling = ORDER_FILLING_FOK;
   else filling = ORDER_FILLING_RETURN;

//--- リクエストと結果の宣言と初期化
   MqlTradeRequest request = {};
   MqlTradeResult  result = {};
//--- リクエストのパラメータ
   request.action       = TRADE_ACTION_DEAL; // 取引操作タイプ
   request.symbol       = Symbol();          // シンボル
   request.volume       = lot;               // ロットのボリューム
   request.type         = type;              // 注文タイプ
   request.price        = price;             // 発注価格
   request.sl           = sl;                // SL (指定する為に行を追加)
   request.tp           = tp;                // TP (指定する為に行を追加)
   request.deviation    = slip;              // 価格からの許容偏差
   request.type_filling = filling;           // 注文充填
   request.magic        = magic;             // 注文のMagicNumber
//--- リクエストの送信
   if(!OrderSend(request,result))
      PrintFormat("OrderSend error %d",GetLastError());     // リクエストの送信が失敗した場合、エラーコードを出力する
   else PlaySound("ok.wav");
}

//+------------------------------------------------------------------+
//| 全てのポジションを決済                                                  |
//+------------------------------------------------------------------+
void Exit()
  {
   ENUM_ORDER_TYPE_FILLING filling;
   long mode = SymbolInfoInteger(NULL, SYMBOL_FILLING_MODE);
   if ((mode & SYMBOL_FILLING_IOC) != 0) filling = ORDER_FILLING_IOC;
   else if ((mode & SYMBOL_FILLING_FOK) != 0) filling = ORDER_FILLING_FOK;
   else filling = ORDER_FILLING_RETURN;

//--- 結果とリクエストの宣言
  MqlTradeRequest request;
   MqlTradeResult  result;
   int total=PositionsTotal(); // 保有ポジション数   
//--- 全ての保有ポジションの取捨
  for(int i=total-1; i>=0; i--)
     {
     //--- 注文のパラメータ
    ulong  position_ticket=PositionGetTicket(i);                                     // ポジションチケット
    string position_symbol=PositionGetString(POSITION_SYMBOL);                       // シンボル 
    int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);             // 小数点以下の桁数
    ulong  magic=PositionGetInteger(POSITION_MAGIC);                                 // ポジションのMagicNumber
     double volume=PositionGetDouble(POSITION_VOLUME);                                 // ポジションボリューム
    ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);   // ポジションタイプ
    //--- ポジション情報の出力
    PrintFormat("#%I64u %s  %s  %.2f  %s [%I64d]",
                  position_ticket,
                  position_symbol,
                 EnumToString(type),
                  volume,
                 DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  magic);
     //--- MagicNumberが一致している場合
    if(magic==MAGIC)
        {
         //--- リクエストと結果の値のゼロ化
        ZeroMemory(request);
         ZeroMemory(result);
         //--- 操作パラメータの設定
        request.action   =TRADE_ACTION_DEAL;       // 取引操作タイプ
        request.position =position_ticket;         // ポジションチケット
        request.symbol   =position_symbol;         // シンボル 
        request.volume   =volume;                   // ポジションボリューム
        request.deviation=int(SLIP * 10);          // 価格からの許容偏差
        request.type_filling = filling;            // 注文充填
        request.magic    =MAGIC;                   // ポジションのMagicNumber
         //--- ポジションタイプによる注文タイプと価格の設定
        if(type==POSITION_TYPE_BUY)
           {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
            request.type =ORDER_TYPE_SELL;
           }
         else
           {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
            request.type =ORDER_TYPE_BUY;
           }
         //--- 決済情報の出力
        PrintFormat("Close #%I64d %s %s",position_ticket,position_symbol,EnumToString(type));
         //--- リクエストの送信
        if(!OrderSend(request,result))
           PrintFormat("OrderSend error %d",GetLastError()); // リクエストの送信に失敗した場合、エラーコードを出力
        else PlaySound("ok.wav");
        //--- 操作情報  
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
         //---
        }
     }
  }
//+------------------------------------------------------------------+

本記事の監修者・HT FX

2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。

EA(自動売買)を学びたい方へオススメコンテンツ

EA運用の注意点

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。


本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。

この記事をシェアする