【MQL4 : MT4】GUI で 全決済、個別決済 を実装した EA を作る。【Dialog】

23/09/22追記
note購入者はDMして下されば続きが読めます！
https://note.com/iceseeds/n/nfdae2c5ac2ef


# はじめに
ここでは、GUIで全決済、個別決済を実装します。
前回までの記事を理解しているのが前提です。
[【MQL4 : MT4】GUI で 売買機能を実装した EA を作る。③]
作成手順としては、以下の通りです。
1. ComboBoxの作成
2. 全決済、個別決済処理
3. Event.mqhに処理を追加
**※前回とは画面サイズを変えています。**
`if( !AppWindow.Create( ChartID(), "SampleTitle", 0, 0, 0, 250, 100 ) )`
            　　　　　　　　　　　　　　　　　　　↓↓↓↓
`if( !AppWindow.Create( ChartID(), "SampleTitle", 0, 0, 0, 250, 120 ) )`
.
**※ネーミングセンスはありません。なるだけ分かりやすくしているつもりです！** 
いろいろと初心者な為、至らない部分もありますが、宜しくお願いします。
フォルダ構造のアドバイスなどを頂けると助かります。
- Experts/
    - Sample/
        - Plugin/　　　　　　　　←これから機能を追加していくフォルダを作りました。
          - ExitCurrency.mqh　　←今回のメインファイルです。
        - guiwindow.mq4
        - AppWindow.mqh
        - Event.mqh
---
## 実行結果
![Screenshot_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542117/9a31efde-d179-b9db-f3b1-8fc5d8750fc1.png)
All Exitを選択し、Pushすると、**仕掛けているポジションを全て決済**します。
This Exitを選択し、Pushすると、**Pushした通貨のポジションを全て決済**します。
（ この場合は、USDJPYのポジションを全て決済します )
## 全決済、個別決済機能
前回までが理解できていれば、GUI部分は簡単です。
### AppWindow.mqh
```AppWindow.mqh
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Controls\Edit.mqh>
#include <Controls\ComboBox.mqh>  // --- 1
class CPanelDialog : public CAppDialog
{
   public:
       CPanelDialog();
      ~CPanelDialog();
   
      /* create */
      virtual bool      Create( const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2 );
      /* chart event handler */
      virtual bool      OnEvent( const int id, const long &lparam, const double &dparam, const string &sparam );
      
      CEdit             m_editLots;
      CButton           m_btnBuy;
      CButton           m_btnSell;
      CButton           m_btnUp;
      CButton           m_btnDown;
      CComboBox         m_combCreate;
      CButton           m_btnComb;
      
   protected:
      bool              CreateEditLots();
      bool              CreateBtnBuy();
      bool              CreateBtnSell();
      bool              CreateBtnUp();
      bool              CreateBtnDown();
      bool              CreateCombCreate();
      bool              CreateBtnComb();
      
      void              OnClickBtnUp();
      void              OnClickBtnDown();
};
EVENT_MAP_BEGIN( CPanelDialog )
ON_EVENT( ON_CLICK, m_btnUp,     OnClickBtnUp )
ON_EVENT( ON_CLICK, m_btnDown,   OnClickBtnDown )
EVENT_MAP_END( CAppDialog )
CPanelDialog::CPanelDialog(){}
CPanelDialog::~CPanelDialog(){}
bool CPanelDialog::Create( const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2 )
{
   if( !CAppDialog::Create( chart, name, subwin, x1, y1, x2, y2 ) )
      return false;
   
   if( !CreateEditLots() )    return false;
   if( !CreateBtnUp() )       return false;
   if( !CreateBtnDown() )     return false;
   if( !CreateBtnBuy() )      return false;
   if( !CreateBtnSell() )     return false;
   if( !CreateCombCreate() )  return false;
   if( !CreateBtnComb() )     return false;
   
   return true;
}
bool CPanelDialog::CreateEditLots()
{
   int widths = ClientAreaWidth() / 5;
   int x1 = widths;
   int y1 = 0;
   int x2 = widths * 4;
   int y2 = 20;
   
   if( !m_editLots.Create( m_chart_id, m_name + "Edit", m_subwin, x1, y1, x2, y2 ) ) return false;
   if( !m_editLots.ReadOnly( false ) ) return false;
   if( !Add( m_editLots ) ) return false;
   m_editLots.TextAlign( ALIGN_CENTER );
   m_editLots.Text( "0.10" );
   
   return true;
}
~
中略
~
bool CPanelDialog::CreateCombCreate()             // --- 2
{
   int widths = ClientAreaWidth() / 5;
   int x1 = 0;
   int y1 = ClientAreaHeight() - 20;
   int x2 = widths * 4;
   int y2 = ClientAreaHeight();
   
   if( !m_combCreate.Create( m_chart_id, m_name + "comboCreate", m_subwin, x1, y1, x2, y2 ) ) return false;
   m_combCreate.AddItem( "All Exit", 0 );
   m_combCreate.AddItem( "This Exit", 1 );
   
   if( !Add( m_combCreate ) ) return false;
   
   return true;
}
bool CPanelDialog::CreateBtnComb()
{
   int widths = ClientAreaWidth() / 5;
   int x1 = widths * 4;
   int y1 = ClientAreaHeight() - 20;
   int x2 = ClientAreaWidth();
   int y2 = ClientAreaHeight();
   
   if( !m_btnComb.Create( m_chart_id, m_name + "btnComb", m_subwin, x1, y1, x2, y2 ) ) return false;
   if( !m_btnComb.Text( "Push" ) ) return false;
   if( !Add( m_btnComb ) ) return false;
   
   return true;
}
/* Event Handle */
void CPanelDialog::OnClickBtnUp()
{
   m_btnUp.Pressed( false );
   
   string str_editText = m_editLots.Text();
   double d_lots = StringToDouble( str_editText );
   d_lots += 0.01;
   m_editLots.Text( DoubleToStr( d_lots, 2 ) );
}
void CPanelDialog::OnClickBtnDown()
{
   m_btnDown.Pressed( false );
   
   string str_editText = m_editLots.Text();
   double d_lots = StringToDouble( str_editText );
   if( d_lots > 0.01 )
   {
      d_lots -= 0.01;
      m_editLots.Text( DoubleToStr( d_lots, 2 ) );
   }
}
```
---
### AppWindo.mqh の ソースコード詳細
#### 1. [ AppWindow.mqh ] include文
今回は、プルダウンを使うので、ComboBoxをインクルードします。
```AppWindow.mqh
#include <Controls\ComboBox.mqh>
```
#### 2. [ AppWindow.mqh ] CreateCombCreate()関数
~~Createって２回言っとるよ...~~
AddItemでプルダウンメニューの中身を追加します。
`m_combCreate.AddItem( "All Exit", 0 );`
`m_combCreate.AddItem( "This Exit", 1 );`
メンバ変数 . AddItem( 表示文字列, 番号 );
```AppWindow.mqh
bool CPanelDialog::CreateCombCreate()
{
   int widths = ClientAreaWidth() / 5;
   int x1 = 0;
   int y1 = ClientAreaHeight() - 20;
   int x2 = widths * 4;
   int y2 = ClientAreaHeight();
   
   if( !m_combCreate.Create( m_chart_id, m_name + "comboCreate", m_subwin, x1, y1, x2, y2 ) ) return false;
   m_combCreate.AddItem( "All Exit", 0 );
   m_combCreate.AddItem( "This Exit", 1 );
   
   if( !Add( m_combCreate ) ) return false;
   
   return true;
}
```
---
### ExitCurrency.mqh ( New )
```ExitCurrency.mqh
class CExitCurrency
{
   public:
      bool exit();
      bool exit( string str_symbol );
};
bool CExitCurrency::exit()     // --- 3
{
   if( OrdersTotal() == 0 )
      Alert( "not Order" );
   else
   {
      for( int i = OrdersTotal() - 1; i >= 0; i-- )
      {
         if( !OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) continue;
         if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;
         Sleep( 10 );
      }
   }   
   
   return true;
}
bool CExitCurrency::exit( string str_symbol )     // --- 4
{
   for( int i = OrdersTotal() - 1; i >= 0; i-- )
   {
      if( !OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) continue;
      if( OrderSymbol() != str_symbol ) continue;
      if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;
      Sleep( 10 );
   }
   
   return true;
}
```
### ExitCurrency.mqh の ソースコード詳細
#### 3. [ ExitCurrency.mqh ] exit関数 ( 全決済 ）
ポジションがあれば、全決済をします。
```ExitCurrency.mqh
bool CExitCurrency::exit()     // --- 3
{
   if( OrdersTotal() == 0 )
      Alert( "not Order" );
   else
   {
      for( int i = OrdersTotal() - 1; i >= 0; i-- )
      {
         if( !OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) continue;
         if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;
         Sleep( 10 );
      }
   }   
   
   return true;
}
```
`if( OrdersTotal() == 0 )`
OrdersTotal()を使い、ポジションがあるかどうかを判別してます。
`for( int i = OrdersTotal() - 1; i >= 0; i-- )`
MT4では、いったんポジションが決済されると、
各ポジションに付されたインデックスが再び古いものから順に『０』から振り直されるので、
( ポジション合計数 - 1 ) になります。
なので、**新しい → → → 古い** と検索します。
`if( !OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) continue;`
OrderSelect関数を使って、**エントリー中の注文を選択します。**
OrderSelectで選択できなかったら、continue; で戻ります。
`if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;`
上記を全てクリアしたら、いよいよ決済です。
OrderClose関数を使用して、決済します。
**OrderSelectでポジションを選択済みなので、( チケット、ロット、価格、スリップページ ) を使って決済します。**
`Sleep( 100 );`
OrderCloseは、負荷が掛かるので、0.1秒 休止させます。
**※Sleepしないと、次のポジション決済に影響がでる可能性があります。**
#### 4. [ ExitCurrency.mqh ] exit関数 ( 個別決済 ）
通貨ペアの比較で、個別決済を実装します。
他は、全決済と同じです。
```ExitCurrency.mqh
bool CExitCurrency::exit( string str_symbol )
{
   for( int i = OrdersTotal() - 1; i >= 0; i-- )
   {
      if( !OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) continue;
      if( OrderSymbol() != str_symbol ) continue;
      if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;
      Sleep( 10 );
   }
   
   return true;
}
```
`bool CExitCurrency::exit( string str_symbol )`
**引数で、Pushをした通貨ペアを取得します**
`if( OrderSymbol() != str_symbol ) continue;`
全ポジションを比較し、**通貨ペアが同じなら**、決済します。

---
### Event.mqh
```Event.mqh
#include "AppWindow.mqh"
#include "Plugin/ExitCurrency.mqh"
CPanelDialog   AppWindow;
CExitCurrency  ExitCurrency;
class CEvent
{
   public:
      bool  OnEvent( const int id, const long lparam, const double dparam, const string sparam );
      
   private:
      bool  btnOrder( const int id, const long lparam, const double dparam, const string sparam );
      bool  Order( int i_type, double d_lots );
      bool  combSelect( const int id, const long lparam, const double dparam, const string sparam );
};
bool CEvent::OnEvent( const int id, const long lparam, const double dparam, const string sparam )
{
   if( !btnOrder( id, lparam, dparam, sparam ) )
      Alert( "Error : " + (string)GetLastError() );
   if( !combSelect( id, lparam, dparam, sparam ) )
      Alert( "Error : " + (string)GetLastError() );   
   
   return true;
}
bool CEvent::btnOrder( const int id, const long lparam, const double dparam, const string sparam )
{
   if( ( StringFind( sparam, "btnBuy", 4 ) != -1 ) && id == CHARTEVENT_OBJECT_CLICK ) 
   {
      if( !Order( 0, (double)AppWindow.m_editLots.Text() ) )
         Alert( "Error : " + (string)GetLastError() );
   }else if( ( StringFind( sparam, "btnSell", 4 ) != -1 ) && id == CHARTEVENT_OBJECT_CLICK ) 
   {
      if( !Order( 1, (double)AppWindow.m_editLots.Text() ) )
         Alert( "Error : " + (string)GetLastError() );
   }
   
   return true;
}
bool CEvent::Order( int i_type, double d_lots )
{
   if( OrderSend( Symbol(), i_type, d_lots, Close[0], 0, 0, 0, "GUISample", 999, 0, clrRed ) == -1 )
      return false;
   else
      Alert( "OrderSend Success!!\n Lots = " + (string)d_lots + "\n type = " + (string)i_type );
   
   return true;
}
bool CEvent::combSelect( const int id, const long lparam, const double dparam, const string sparam )
{
   if( ( StringFind( sparam, "btnComb", 4 ) != -1 ) && id == CHARTEVENT_OBJECT_CLICK ) 
   {
      switch( (int)AppWindow.m_combCreate.Value() )       // --- 5
      {
         case 0: // to All Exit
            if( !ExitCurrency.exit() )
               Alert( "Error : " + (string)GetLastError() );
            break;
         case 1: // to This Exit
            if( !ExitCurrency.exit( Symbol() ) )
               Alert( "Error : " + (string)GetLastError() );
            break;
         default:
            Alert( "not select" );
      }
   }
   
   return true;
}
```
### Event.mqh の ソースコード詳細
#### 5. [ Exit.mqh ] combSelect関数
Pushのボタンを押した際に、ComboBoxの番号を取得します。
switch文で、番号ごとの命令を追加。
```Event.mqh
bool CEvent::combSelect( const int id, const long lparam, const double dparam, const string sparam )
{
   if( ( StringFind( sparam, "btnComb", 4 ) != -1 ) && id == CHARTEVENT_OBJECT_CLICK ) 
   {
      switch( (int)AppWindow.m_combCreate.Value() )
      {
         case 0: // to All Exit
            if( !ExitCurrency.exit() )
               Alert( "Error : " + (string)GetLastError() );
            break;
         case 1: // to This Exit
            if( !ExitCurrency.exit( Symbol() ) )
               Alert( "Error : " + (string)GetLastError() );
            break;
         default:
            Alert( "not select" );
      }
   }
   
   return true;
}
```
`switch( (int)AppWindow.m_combCreate.Value() )`
Editのロットを取得したのと同じ様に、ComboBoxの値を取得します。
**AppWindow. メンバ変数 . Value()**
`if( !ExitCurrency.exit( Symbol() ) )`
( case 1 )個別決済だったら、exitの引数に、通貨ペアを渡します。
## さいごに
今回は、全決済、個別決済を実装しました。
コードが長そうに見えますが、凄く単純です。
次回は、水平ラインタッチで、決済(ストップ狩りの対処かも？） を実装します。
*コードをGithubに up した方がいいのかな。。。*
YoutubeでLive配信しながら作ってます。
https://www.youtube.com/channel/UCcTw_iVgpLfrep9f94KxwLg?sub_confirmation=1 
チャンネル登録お願いします:relaxed:
Twitterでは毎日呟いています。
https://twitter.com/IceSeed_bz
フォローお願いします:relaxed:
お疲れ様。:eye::eye: