English Русский 中文 Español Deutsch Português
トレードシグナルの多通貨監視(その4)。機能強化とシグナル検索システムの改善

トレードシグナルの多通貨監視(その4)。機能強化とシグナル検索システムの改善

MetaTrader 5トレーディング | 17 7月 2020, 11:40
848 0
Alexander Fedosov
Alexander Fedosov

目次

イントロダクション

第3部では、シグナルを検索するための基本的なシステムを作成しましたが、小さなインジケータとシンプルな検索ルールのセットに基づいていました。 また、トレードモニターの視覚的なパートでできるユーザビリティの改善提案もしました。 それは、今回のパートで実施します。

トレーディングシグナルを生成するためのカスタムインジケータ

トレードシグナルの作成と編集にロジカルに追加することで、利用可能なインジケータのセットを拡大することができます。 以前は、標準的なMetaTrader5のセットのインジケータのみを使用することができました。 これで、カスタムインジケータの計算パートを使用する可能性が追加されました。 前のパートのプロジェクトを元にしてみましょう。 記事の添付ファイルからダウンロードできます。 このパートでは、第3部で考えた基底クラスメソッドの動作アルゴリズムを変更する必要があります。 訂正・追加はすべて適切な説明を行います。

シグナルの追加と編集ウィンドウでカスタムインジケータを選択する可能性から始めましょう。 このウィンドウの実装は、プロジェクトのSetWindow.mqhファイルで提供されています。 このファイルを開き、CreateIndicatorType()メソッドを見つけます。 変更はこのファイルに正確に実装してください。

//+------------------------------------------------------------------+
//| Creates a drop-down menu with indicator types                    |
//+------------------------------------------------------------------+
bool CProgram::CreateIndicatorType(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_indicator_type.MainPointer(m_set_window);
//---
#define SIZE 10
//--- Array of the item values in the list view
   string pattern_names[SIZE]=
   {
      "ATR","CCI","DeMarker","Force Ind","WPR","RSI","Momentum","ADX","ADX Wilder","Custom"
   };
//--- Set up properties before creation
   m_indicator_type.XSize(200);
   m_indicator_type.YSize(26);
   m_indicator_type.LabelYGap(4);
   m_indicator_type.ItemsTotal(SIZE);
   m_indicator_type.Font(m_base_font);
   m_indicator_type.FontSize(m_base_font_size);
   m_indicator_type.BackColor(m_background);
   m_indicator_type.GetButtonPointer().Font(m_base_font);
   m_indicator_type.GetButtonPointer().FontSize(m_base_font_size);
   m_indicator_type.GetButtonPointer().BackColor(clrWhite);
   m_indicator_type.GetButtonPointer().XGap(100);
   m_indicator_type.GetButtonPointer().XSize(100);
   m_indicator_type.GetListViewPointer().Font(m_base_font);
   m_indicator_type.GetListViewPointer().FontSize(m_base_font_size);
   m_indicator_type.GetListViewPointer().ItemYSize(25);
   m_indicator_type.GetListViewPointer().YSize(200);
//--- Save the item values in the combobox list view
   for(int i=0; i<SIZE; i++)
      m_indicator_type.SetValue(i,pattern_names[i]);
//--- Get the list view pointer
   CListView *lv=m_indicator_type.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_indicator_type.SelectItem(1);
//--- Create the control
   if(!m_indicator_type.CreateComboBox("Indicator Type",x_gap,y_gap))
      return(false);
//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(1,m_indicator_type);
   return(true);
}

では、前バージョンと比較して何が変わったのかを考えてみましょう。 まず、SIZEマクロ置換を追加していますが、これはドロップダウンリストの要素数を意味します。 このように、すべてのコードパートで置換を行わなくても、リストの長さを一箇所から変更することができます。 そして、最後に新しいリスト項目を追加します。Customです。 その変更点を以下の図1に示します。

図1 カスタムインジケータを選択するための項目を追加  

では、インジケータを設定して使用するための新しいインターフェース要素を追加してみましょう。 iCustom()関数の引数に応じて変更を加えて、独自のインジケータの計算パートを使用する必要があります。 これらは、シンボル名、ピリオド、コンパイルされた*.ex5インジケータファイルへのパス、およびインジケータパラメータのコンマ区切りリストが含まれます。

int  iCustom(
   string           symbol,     // symbol name
   ENUM_TIMEFRAMES  period,     // period
   string           name        // folder/custom indicator_name
   ...                          // the list of indicator input parameters
   );

シンボル名と時間枠は、最初のアプリケーション設定の最初の2つのステップで選択された値から置き換えられます。 ただし、ユーザーはインジケータのパスとパラメータのリストを自分で設定しなければなりません。 ここには、2つのフィールドを追加する必要があります。 CProgram基底クラスに2つの新しい変数とメソッドを追加します。

   CTextEdit         m_custom_path;
   CTextEdit         m_custom_param;

   bool              CreateCustomEdit(CTextEdit &text_edit,const int x_gap,const int y_gap,const string default_text);

トレードシグナル作成・編集画面で適用されますので、SetWindow.mqhファイルに実装してください。

//+------------------------------------------------------------------+
//| Input field for a custom indicator                               |
//+------------------------------------------------------------------+
bool CProgram::CreateCustomEdit(CTextEdit &text_edit,const int x_gap,const int y_gap,const string default_text)
{
//--- Save the pointer to the main control
   text_edit.MainPointer(m_set_window);
//--- Properties
   text_edit.XSize(100);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.GetTextBoxPointer().AutoSelectionMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(325);
   text_edit.GetTextBoxPointer().DefaultTextColor(clrSilver);
   text_edit.GetTextBoxPointer().DefaultText(default_text);
   text_edit.GetTextBoxPointer().BorderColor(clrBlack);
//--- Create the control
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.IsLocked(true);
//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(1,text_edit);
   return(true);
}

CreateCustomEdit()メソッドを使用して2つのインプットフィールドを作成します。 同じファイル内のCreateSetWindow()メソッドのボディで、選択されたインジケータの設定セクションを見つけて、以下のコードを追加します。

   if(!CreateCustomEdit(m_custom_path,240,22+10+2*(25+10),"Enter the indicator path"))
      return(false);
   if(!CreateCustomEdit(m_custom_param,240,22+10+3*(25+10),"Enter indicator parameters separated by commas"))
      return(false);

 その結果、図2のように設定画面に2つのインプット欄が表示されます。

図2 カスタムインジケータ設定のインプットフィールドの追加

この開発段階では機能していません。 利用可能性が選択されたインジケータタイプに厳密に依存するためです。つまり、ドロップダウンリストでCustomが選択された場合にのみ利用可能になります。 このタスクを実装するために、RebuildParameters()メソッドを修正してみましょう。 しかし、まず、OnEvent()メソッドのドロップダウンリストの項目選択イベントのセクションに移動し、インジケータタイプの選択で目的のリストのイベントのチェックを追加します。

//--- Selecting an item in the combobox drop-down list
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
   {
      //--- Indicator type
      if(lparam==m_indicator_type.Id())
         RebuildParameters(m_indicator_type.GetListViewPointer().SelectedItemIndex());
   }

RebuildParameters()メソッドを変更して、利用可能なインジケータのそれぞれが選択されたときに、関連する設定が表示されるようにしました。 さらに、カスタムインジケータについては、パスとパラメータのインプットフィールドをアクティブにします

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::RebuildParameters(int index)
{
   switch(index)
   {
   case  0:
      m_period_edit.LabelText("ATR Period");
      m_applied_price.Hide();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  1:
      m_period_edit.LabelText("CCI Period");
      m_applied_price.Show();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  2:
      m_period_edit.LabelText("DeMarker Period");
      m_applied_price.Hide();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  3:
      m_period_edit.LabelText("Force Index Period");
      m_applied_price.Hide();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  4:
      m_period_edit.LabelText("WPR Period");
      m_applied_price.Hide();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  5:
      m_period_edit.LabelText("RSI Period");
      m_applied_price.Show();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  6:
      m_period_edit.LabelText("Momentum Period");
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  7:
      m_period_edit.LabelText("ADX Period");
      m_applied_price.Hide();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  8:
      m_period_edit.LabelText("ADXW Period");
      m_applied_price.Hide();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   case  9:
      m_period_edit.LabelText("Buffer Number");
      m_applied_price.Hide();
      m_custom_param.IsLocked(false);
      m_custom_path.IsLocked(false);
      break;
   default:
      m_period_edit.LabelText("Ind Period");
      m_applied_price.Hide();
      m_custom_param.IsLocked(true);
      m_custom_path.IsLocked(true);
      break;
   }
   m_period_edit.Update(true);
}

さて、プロジェクトをコンパイルすると、以下のような結果が得られるはずです。

図3 カスタムインジケータのインプットフィールドの追加

次のステップは、シグナル追加ボタンクリックのイベントを補足します。 押すと、インジケータの選択と設定をデフォルトに設定します。

      //--- Add Signal button click event
      if(lparam==m_add_signal.Id())
      {
         if(m_total_signals>4)
         {
            MessageBox("Maximum number of signals is 5","Signal Monitor");
            return;
         }
         m_set_window.OpenWindow();
         RebuildParameters(1);
         m_number_signal=-1;
         RebuildTimeframes();
         m_new_signal.LabelText("Add");
         m_new_signal.Update(true);
         m_indicator_type.SelectItem(1);
         m_indicator_type.GetButtonPointer().Update(true);
      }

シグナル設定のセットを保存する既存のアルゴリズムに新しいコントロール(インプットフィールド)を適応させる前に、トレードシグナルの検索ルールのシステムを展開してみましょう。 これより、新しいインターフェース要素が追加されます。 しかし、設定のシステムを拡張していくと、その都度セット保存のアルゴリズムを変更するのは現実的ではありません。 より論理的な解決策は、すべての新しいセットアップと制御要素を追加し、シグナル検索のパラメータのセットを保存する方法を修正することです。


トレードシグナル検索ルールの仕組みを拡充

現時点では、トレードモニターは不等号に基づいてシグナルを作成することができます。 これはある数以上、ある数以下の状態を意味します。 しかし、このような選択は、必ずしも希望のシグナルを正確に反映するとは限りません。 例えば、オシレータインジケータは、特定の値幅で使用するのがより適切な場合があります。 今から実施することになります。 まず、以前のルール設定システムと新しいルール設定システムの間にスイッチを追加する必要があります。 2つのルール設定タイプを持つ新しいドロップダウンリストを追加する必要があります。CompareIntervalです。 

CProgram基底クラスに移動し、CСomboboxクラスのインスタンスである新しい変数を追加し、UI要素を実装するメソッドを作成します。

CComboBox         m_rule_interval;

bool              CreateRuleInterval(const int x_gap,const int y_gap);

このドロップダウンリストは設定画面に属しているので、メソッドの実装はSetWindow.mqhファイルに追加する必要があります。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateRuleInterval(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_rule_interval.MainPointer(m_set_window);
//--- Array of the item values in the list view
   string pattern_names[2]=
   {
      "Compare","Interval",
   };
//--- Set up properties before creation
   m_rule_interval.XSize(160);
   m_rule_interval.YSize(26);
   m_rule_interval.LabelYGap(4);
   m_rule_interval.ItemsTotal(2);
   m_rule_interval.Font(m_base_font);
   m_rule_interval.FontSize(m_base_font_size);
   m_rule_interval.BackColor(m_background);
   m_rule_interval.GetButtonPointer().Font(m_base_font);
   m_rule_interval.GetButtonPointer().FontSize(m_base_font_size);
   m_rule_interval.GetButtonPointer().BackColor(clrWhite);
   m_rule_interval.GetButtonPointer().XGap(90);
   m_rule_interval.GetButtonPointer().XSize(80);
   m_rule_interval.GetListViewPointer().Font(m_base_font);
   m_rule_interval.GetListViewPointer().FontSize(m_base_font_size);
   m_rule_interval.GetListViewPointer().ItemYSize(26);
//--- Save the item values in the combobox list view
   for(int i=0; i<2; i++)
      m_rule_interval.SetValue(i,pattern_names[i]);
//--- Get the list view pointer
   CListView *lv=m_rule_interval.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_rule_interval.SelectItem(0);
//--- Create the control
   if(!m_rule_interval.CreateComboBox("Rule",x_gap,y_gap))
      return(false);
//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(1,m_rule_interval);
   return(true);
}

新しい間隔ルールには下限と上限があるはずなので、数値をインプットするためのフィールドを追加します。 上限値には前のフィールドが使用され、下限値には新しいフィールドが使用します。 また、例えばWPRのようなインジケータに負の値を指定できるようにすることも必要です。 この場合、上限値と下限値が切り替わります。 下期のインプットフィールドの実装に別のメソッドを作成する必要性を避けるために、既存のインプットフィールドとCreateRule()メソッドを担当する現在の変数を修正するだけでよい。 変数は配列になります。

CTextEdit         m_rule_value[2];

メソッドでは、CTextEditクラスインスタンスへの参照を受け取った新しい引数を追加します。

bool              CreateRuleValue(CTextEdit &text_edit,const int x_gap,const int y_gap);

応じてメソッドの実装を変更します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateRuleValue(CTextEdit &text_edit,const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   text_edit.MainPointer(m_set_window);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.LabelColor(C'0,100,255');
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(999);
   text_edit.StepValue(0.1);
   text_edit.MinValue(-999);
   text_edit.SetDigits(3);
   text_edit.SpinEditMode(true); 
//--- Create the control
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(5));
   text_edit.GetTextBoxPointer().AutoSelectionMode(true);
//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(1,text_edit);
   return(true);
}

また、既存のCreateRule()メソッドの一部の値を変更します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateRule(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_rule_type.MainPointer(m_set_window);
//--- Array of the item values in the list view
   string pattern_names[5]=
   {
      ">",">=","==","<","<="
   };
//--- Set up properties before creation
   m_rule_type.XSize(80);
   m_rule_type.YSize(26);
   m_rule_type.LabelYGap(4);
   m_rule_type.ItemsTotal(5);
   m_rule_type.Font(m_base_font);
   m_rule_type.FontSize(m_base_font_size);
   m_rule_type.BackColor(m_background);
   m_rule_type.GetButtonPointer().Font(m_base_font);
   m_rule_type.GetButtonPointer().FontSize(m_base_font_size);
   m_rule_type.GetButtonPointer().BackColor(clrWhite);
   m_rule_type.GetButtonPointer().XGap(1);
   m_rule_type.GetButtonPointer().XSize(80);
   m_rule_type.GetListViewPointer().Font(m_base_font);
   m_rule_type.GetListViewPointer().FontSize(m_base_font_size);
   m_rule_type.GetListViewPointer().ItemYSize(26);
//--- Save the item values in the combobox list view
   for(int i=0; i<5; i++)
      m_rule_type.SetValue(i,pattern_names[i]);
//--- Get the list view pointer
   CListView *lv=m_rule_type.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_rule_type.SelectItem(0);
//--- Create the control
   if(!m_rule_type.CreateComboBox("",x_gap,y_gap))
      return(false);
//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(1,m_rule_type);
   return(true);
}

さて、CreateSetWindow()メソッドの条件設定セクションを見つけ、以下のようにコードを変更します。

//--- Condition settings
   if(!CreateRuleValue(m_rule_value[0],200,22+10+5*(25+10)))
      return(false);
   if(!CreateRuleValue(m_rule_value[1],300,22+10+5*(25+10)))
      return(false);
   if(!CreateRule(200,22+10+5*(25+10)))
      return(false);
   if(!CreateRuleInterval(10,22+10+5*(25+10)))
      return(false);

この変更により、既存のインターフェース要素のポジションを再構成したり、新しい要素を追加したりすることができるようになります。 その結果は、図4のようになるはずです。 しかし、ルールモードをCompareからIntervalに切り替えようとしても、今のところ何も動きません。 修正しましょう。

図4 シグナル探索ルールのモード選択の追加

これを行うには、OnEvent()メソッドを開き、ドロップダウンリストからアイテム選択イベントを担当するセクションを見つけ、選択されたモードに応じて正しいインターフェース要素を表示するためのコードを追加します。

//--- Selecting an item in the combobox drop-down list
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
   {
      ...
      //--- Rule type
      if(lparam==m_rule_interval.Id())
      {
         switch(m_rule_interval.GetListViewPointer().SelectedItemIndex())
         {
         case  0:
            m_rule_value[0].Hide();
            m_rule_type.Show();
            break;
         case  1:
            m_rule_value[0].Show();
            m_rule_type.Hide();
            break;
         default:
            break;
         }
      }
   }

次に、インターフェイスの読み込みに関連するイベントのいくつかを、OnEvent()メソッドの別のセクションに移動させてみましょう。 これを行うには、インターフェース作成完了イベントを作成し、CreateGUI()メソッドのコードをそこに移動します。 CreateGUIには以下のコードが残ります。

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Loading the language
   ChangeLanguage();
//--- Step 1-3. Symbol selection window.
   if(!CreateStepWindow(m_lang[0]))
      return(false);
//---
   if(!CreateSetWindow(m_lang[17]))
      return(false);
//--- Creating form 2 for the color picker
   if(!CreateColorWindow("Color Picker"))
      return(false);
//--- Finishing the creation of GUI
   CWndEvents::CompletedGUI();
   return(true);
}

新しいセクションは以下のようになります。

// --- GUI creation completion
   if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
   {
      m_back_button.Hide();
      m_add_signal.Hide();
      m_signal_header.Hide();
      m_label_button[1].IsPressed(true);
      m_label_button[1].Update(true);
      for(int i=0; i<5; i++)
         m_signal_editor[i].Hide();
      m_rule_value[0].Hide();
   }

アプリケーション読み込み時の新規アクションに注意してください。下限インターバル制限をインプットするために新規作成したフィールドを非表示にします。

新しいUI要素やパラメータを作成した後は、ローディングアルゴリズムの修正やトレードシグナルの設定のセット保存などを進めていきます。 SaveSignalSet()メソッド本体に移動し、最新の変更点に合わせて調整します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::SaveSignalSet(int index)
{
//---
   int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_WRITE|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Не удалось создать файл конфигурации","Монитор сигналов");
      else
         MessageBox("Failed to create configuration file","Signal Monitor");
      return(false);
   }
   if(index>4)
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Максимальное число сигналов не должно быть больше 5","Монитор сигналов");
      else
         MessageBox("Maximum number of signals is 5","Signal Monitor");
      return(false);
   }
//--- Save the selection
//--- Indicator type
   m_signal_set[index].ind_type=m_indicator_type.GetListViewPointer().SelectedItemIndex();
//--- Indicator period
   if(m_signal_set[index].ind_type!=9)
   {
      m_signal_set[index].ind_period=(int)m_period_edit.GetValue();
      //--- Type of applied price
      m_signal_set[index].app_price=m_applied_price.GetListViewPointer().SelectedItemIndex();
   }
   else
   {
      string path=m_custom_path.GetValue();
      string param=m_custom_param.GetValue();
      if(path=="")
      {
         if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
            MessageBox("Введите путь к индикатору","Монитор сигналов");
         else
            MessageBox("Enter the indicator path","Signal Monitor");
         FileClose(h);
         return(false);
      }
      if(param=="")
      {
         if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
            MessageBox("Введите параметры индикатора через запятую","Монитор сигналов");
         else
            MessageBox("Enter indicator parameters separated by commas","Signal Monitor");
         FileClose(h);
         return(false);
      }
      StringToCharArray(path,m_signal_set[index].custom_path);
      StringToCharArray(param,m_signal_set[index].custom_val);
      m_signal_set[index].ind_period=(int)m_period_edit.GetValue();
   }
//--- Rule type
   m_signal_set[index].rule_int=m_rule_interval.GetListViewPointer().SelectedItemIndex();
//--- Comparison type
   m_signal_set[index].rule_type=m_rule_type.GetListViewPointer().SelectedItemIndex();
//--- Rule value
   m_signal_set[index].rule_value1=(double)m_rule_value[0].GetValue();
   m_signal_set[index].rule_value2=(double)m_rule_value[1].GetValue();
//--- Text label display type
   m_signal_set[index].label_type=m_label_button[0].IsPressed()?0:1;
//--- Save the value of the text field for the second type
   if(m_label_button[1].IsPressed())
      StringToCharArray(StringSubstr(m_text_box.GetValue(),0,3),m_signal_set[index].label_value);
//--- Color of the text label
   m_signal_set[index].label_color=m_color_button[0].CurrentColor();
//--- Background color
   if(m_set_param[0].IsPressed())
      m_signal_set[index].back_color=m_color_button[1].CurrentColor();
   else
      m_signal_set[index].back_color=clrNONE;
//--- Border color
   if(m_set_param[1].IsPressed())
      m_signal_set[index].border_color=m_color_button[2].CurrentColor();
   else
      m_signal_set[index].border_color=clrNONE;
//--- Tooltip value
   m_signal_set[index].tooltip=m_set_param[2].IsPressed();
   if(m_signal_set[index].tooltip)
      StringToCharArray(m_tooltip_text.GetValue(),m_signal_set[index].tooltip_text);
//--- Selected image
   m_signal_set[index].image=m_set_param[3].IsPressed();
   if(m_signal_set[index].image)
      m_signal_set[index].img_index=m_pictures_slider.GetRadioButtonsPointer().SelectedButtonIndex();
//--- Selected timegrames
   int tf=0;
   for(int i=0; i<21; i++)
   {
      if(!m_tf_button[i].IsLocked() && m_tf_button[i].IsPressed())
      {
         m_signal_set[index].timeframes[i]=true;
         StringToCharArray(m_tf_button[i].LabelText(),m_signal_set[index].tf_name[i].tf);
         tf++;
      }
      else
         m_signal_set[index].timeframes[i]=false;
   }
//---
   if(tf<1)
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Не выбран ни один таймфрейм","Монитор сигналов");
      else
         MessageBox("No timeframes selected","Signal Monitor");
      FileClose(h);
      return(false);
   }
//---
   FileWriteStruct(h,m_signal_set[index]);
   FileClose(h);
   Print("Configuration signal_"+string(index)+" has been successfully saved");
//---
   return(true);
}

上記のコードには多くの変更点があります。 主な変更点を詳しく考えてみましょう。 まず、標準インジケータとカスタムインジケータのどちらが選択されているかのチェックです。 カスタム インジケータが選択されている場合、期間インプットフィールドからの値と同様に、インジケータ パス保存アルゴリズムとそのパラメータを追加します。

保存されているパラメータの数を変更しました。 したがって、すべてをバイナリファイルに保存するSIGNALの構造を変更する必要があります。 新しい変数を追加します

struct SIGNAL
{
   int               ind_type;
   int               ind_period;
   int               app_price;
   int               rule_int;
   int               rule_type;
   double            rule_value1;
   double            rule_value2;
   int               label_type;
   uchar             label_value[10];
   color             label_color;
   color             back_color;
   color             border_color;
   bool              tooltip;
   uchar             tooltip_text[100];
   bool              image;
   int               img_index;
   bool              timeframes[21];
   TFNAME            tf_name[21];
   uchar             custom_path[100];
   uchar             custom_val[100];
};

閾値インプットフィールドは、比較用のrule_valueに変更され、インターバルモードの上限値と下限値のrule_value1とrule_value2に変更されました。 カスタム・インジケータ・パスとそのパラメータに関するデータを格納するための変数custom_pathとcustom_valが追加されました。 また、トレードシグナルのパラメータセットをファイル LoadSignalSet() から読み込む方法を変更しました。.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::LoadSignalSet(int index)
{
   int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_READ|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      MessageBox("Configuration not found","Signal Monitor");
      return(false);
   }
   ZeroMemory(m_signal_set[index]);
   FileReadStruct(h,m_signal_set[index]);
//--- Loading indicator type
   m_indicator_type.SelectItem(m_signal_set[index].ind_type);
   RebuildParameters(m_signal_set[index].ind_type);
   m_indicator_type.GetButtonPointer().Update(true);
   if(m_signal_set[index].ind_type!=9)
   {
      //--- Loading indicator period
      m_period_edit.SetValue((string)m_signal_set[index].ind_period);
      m_period_edit.GetTextBoxPointer().Update(true);
      //--- Loading applied price
      if(!m_applied_price.IsLocked())
      {
         m_applied_price.SelectItem(m_signal_set[index].app_price);
         m_applied_price.GetButtonPointer().Update(true);
      }
   }
   else
   {
      m_period_edit.SetValue((string)m_signal_set[index].ind_period);
      m_custom_path.SetValue(CharArrayToString(m_signal_set[index].custom_path));
      m_custom_param.SetValue(CharArrayToString(m_signal_set[index].custom_val));
      m_custom_path.GetTextBoxPointer().Update(true);
      m_custom_param.GetTextBoxPointer().Update(true);
   }
//--- Loading signal rule
   m_rule_interval.SelectItem(m_signal_set[index].rule_int);
   m_rule_interval.GetButtonPointer().Update(true);
   m_rule_type.SelectItem(m_signal_set[index].rule_type);
   m_rule_type.GetButtonPointer().Update(true);
   m_rule_value[0].SetValue((string)m_signal_set[index].rule_value1);
   m_rule_value[0].GetTextBoxPointer().Update(true);
   m_rule_value[1].SetValue((string)m_signal_set[index].rule_value2);
   m_rule_value[1].GetTextBoxPointer().Update(true);
//--- Loading a text label
   if(m_signal_set[index].label_type==0)
   {
      m_label_button[0].IsPressed(true);
      m_label_button[0].Update(true);
      m_label_button[1].IsPressed(false);
      m_label_button[1].Update(true);
      m_text_box.IsLocked(true);
   }
   else
   {
      m_label_button[0].IsPressed(false);
      m_label_button[0].Update(true);
      m_label_button[1].IsPressed(true);
      m_label_button[1].Update(true);
      m_text_box.IsLocked(false);
      m_text_box.ClearTextBox();
      m_text_box.AddText(0,CharArrayToString(m_signal_set[index].label_value));
      m_text_box.Update(true);
   }
//--- Loading the color of the text label
   m_color_button[0].CurrentColor(m_signal_set[index].label_color);
   m_color_button[0].Update(true);
//--- Loading the background color
   if(m_signal_set[index].back_color==clrNONE)
   {
      m_set_param[0].IsPressed(false);
      m_set_param[0].Update(true);
      m_color_button[1].IsLocked(true);
      m_color_button[1].GetButtonPointer().Update(true);
   }
   else
   {
      m_set_param[0].IsPressed(true);
      m_set_param[0].Update(true);
      m_color_button[1].IsLocked(false);
      m_color_button[1].CurrentColor(m_signal_set[index].back_color);
      m_color_button[1].GetButtonPointer().Update(true);
   }
//--- Loading the border color
   if(m_signal_set[index].border_color==clrNONE)
   {
      m_set_param[1].IsPressed(false);
      m_set_param[1].Update(true);
      m_color_button[2].IsLocked(true);
      m_color_button[2].GetButtonPointer().Update(true);
   }
   else
   {
      m_set_param[1].IsPressed(true);
      m_set_param[1].Update(true);
      m_color_button[2].IsLocked(false);
      m_color_button[2].CurrentColor(m_signal_set[index].border_color);
      m_color_button[2].GetButtonPointer().Update(true);
   }
//--- Loading the tooltip value
   if(!m_signal_set[index].tooltip)
   {
      m_set_param[2].IsPressed(false);
      m_set_param[2].Update(true);
      m_tooltip_text.IsLocked(true);
      m_tooltip_text.Update(true);
   }
   else
   {
      m_set_param[2].IsPressed(true);
      m_set_param[2].Update(true);
      m_tooltip_text.IsLocked(false);
      m_tooltip_text.ClearTextBox();
      m_tooltip_text.AddText(0,CharArrayToString(m_signal_set[index].tooltip_text));
      m_tooltip_text.Update(true);
   }
//--- Loading the image
   if(!m_signal_set[index].image)
   {
      m_set_param[3].IsPressed(false);
      m_set_param[3].Update(true);
      m_pictures_slider.IsLocked(true);
      m_pictures_slider.GetRadioButtonsPointer().Update(true);
   }
   else
   {
      m_set_param[3].IsPressed(true);
      m_set_param[3].Update(true);
      m_pictures_slider.IsLocked(false);
      m_pictures_slider.GetRadioButtonsPointer().SelectButton(m_signal_set[index].img_index);
      m_pictures_slider.GetRadioButtonsPointer().Update(true);
   }
//--- Loading selected timeframes
   for(int i=0; i<21; i++)
   {
      if(!m_tf_button[i].IsLocked())
      {
         m_tf_button[i].IsPressed(m_signal_set[index].timeframes[i]);
         m_tf_button[i].Update(true);
      }
   }
//---
   FileClose(h);
   return(true);
}

ファイルの標準インジケータが選択されているか、カスタムインジケータが使用されているかのチェックを実装しました。 したがって、必要なデータは、さらに編集するために設定ウィンドウのインターフェイスにロードされます。

これでトレードシグナルパラメータのセットの保存とロードが準備ができています。 さて、シグナル探索アルゴリズムを改良しましょう。 GetSignal()メソッドを開き、条件を調べるのパートを探します。 置き換えは以下のようになります。

//--- Check the condition
   int s=0;
   if(signal_set.rule_int==0)
   {
      double r_value=signal_set.rule_value2;
      double c_value=val[0];
      m_ind_value=c_value;
      switch(signal_set.rule_type)
      {
      case  0:
         if(c_value>r_value)
            s=1;
         break;
      case  1:
         if(c_value>=r_value)
            s=1;
         break;
      case  2:
         if(c_value==r_value)
            s=1;
         break;
      case  3:
         if(c_value<r_value)
            s=1;
         break;
      case  4:
         if(c_value<=r_value)
            s=1;
         break;
      default:
         s=0;
         break;
      }
   }
   else if(signal_set.rule_int==1)
   {
      double r_value_min=signal_set.rule_value1;
      double r_value_max=signal_set.rule_value2;
      double c_value=val[0];
      m_ind_value=c_value;
      if(c_value>=r_value_min && c_value<=r_value_max)
         s=1;
   }

また、追加されたインジケータをセクション選択されたインジケータのハンドルをインクルードします。

//--- Get the handle of the selected indicator
   string str[],name;
   double arr[];
   switch(signal_set.ind_type)
   {
   case  0:
      h=iATR(sy,tf,signal_set.ind_period);
      break;
   case  1:
      h=iCCI(sy,tf,signal_set.ind_period,app_price);
      break;
   case  2:
      h=iDeMarker(sy,tf,signal_set.ind_period);
      break;
   case  3:
      h=iForce(sy,tf,signal_set.ind_period,MODE_SMA,VOLUME_TICK);
      break;
   case  4:
      h=iWPR(sy,tf,signal_set.ind_period);
      break;
   case  5:
      h=iRSI(sy,tf,signal_set.ind_period,app_price);
      break;
   case  6:
      h=iMomentum(sy,tf,signal_set.ind_period,app_price);
      break;
   case  7:
      h=iADX(sy,tf,signal_set.ind_period);
      break;
   case  8:
      h=iADXWilder(sy,tf,signal_set.ind_period);
      break;
   case  9:
      StringSplit(m_custom_param.GetValue(),StringGetCharacter(",",0),str);
      ArrayResize(arr,ArraySize(str));
      for(int i=0; i<ArraySize(str); i++)
         arr[i]=StringToDouble(str[i]);
      name=m_custom_path.GetValue();
      h=GetCustomValue(tf,name,arr);
      break;
   default:
      break;
   }

このブロックには、検索モード、比較またはインターバルのチェックが含まれるようになりました。 条件のチェックを適宜行っています。


シンボルのリストを表形式に変換

トレード口座のシンボル数が少ない場合は、名前として実装されているものを選択できる可能性があり、その横にチェックボックスがあるだけでもかなりのものになります。 しかし、何百ものシンボルを扱う場合、アプリケーションウィンドウの高さが大幅に増加します(シンボルのある行の数に応じてスケーリングされるため)。 そのため、このビューは表形式に置き換えられました。 さらに、シンボルが多すぎると一部が隠れてしまい、右にスクロールバーが追加されてしまいます。 しかし、タスク時間枠を選択するためのチェックボックスはまだ必要です。 そのため、問題を解決する必要があります。

  • 同じシンボル名をチェックボックスで表示するテーブルを作成します。
  • 既存のチェックボックスを簡素化し、タスク時間枠のみを選択できるようにしました。
  • 既存のアルゴリズムに合わせてシンボルを選択したり、トレードシグナルをさらに検索するためのタスク時間枠に合わせて、すべてのUIの変更を適応させています。

まずは、古いチェックボックスの表示を外してみましょう。 これを行うには、GUI作成完了イベントで非表示にします。 これで、チェックボックスの数が一定であることがわかります:21、ターミナル内の可能なタイムフレームの合計数に等しいです。 そのため、動的なm_checkbox[]配列をサイズ21の静的な配列にします。

      //--- Hide timeframe checkboxes
      for(int i=0; i<21; i++)
         m_checkbox[i].Hide();

また、目的が明確なため、チェックボックスの作成方法を調整する必要があります。 CreateStepWindow()メソッドボディに移動し、チェックボックスセクションを以下のように置き換えます。

//--- Checkboxes
   int k=0;
   string timeframe_names[21]=
   {
      "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30",
      "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
   };
   for(int j=0; j<=3; j++)
   {
      for(int i=0; i<7; i++)
      {
         if(k<21)
            if(!CreateCheckBox(m_checkbox[k],10+80*i,m_step_window.CaptionHeight()+70+j*25,timeframe_names[k]))
               return(false);
         k++;
      }
   }

また、ウィンドウの高さ計算ウィンドウも削除(マーケットウォッチのシンボル数に合わせて高さを計算する必要がありました)。

m_step_window.ChangeWindowHeight(m_checkbox[m_all_symbols-1].YGap()+30+30);

ウィンドウの高さを静的にします。

m_step_window.YSize(500);

ここでは、Market Watchのデータで埋められるテーブルの基本的なオブジェクトを作成する必要があります。 CTableクラスのインスタンスとテーブルの実装メソッドを作成します。

//--- Rendered table
   CTable            m_table;

   bool              CreateTable(const int x_gap,const int y_gap);

StepWindow.mqh、メインウィンドウファイルに実装します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateTable(const int x_gap,const int y_gap)
{
#define COLUMNS1_TOTAL 7
#define ROWS1_TOTAL int(MathCeil(m_all_symbols/7))
//--- Save the pointer to the main control
   m_table.MainPointer(m_step_window);
//--- Array of column widths
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,80);
//--- Array of text offset along the X axis in the columns
   int text_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(text_x_offset,25);
//--- Array of text alignment in columns
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_LEFT);
//--- Array of column image offsets along the X axis
   int image_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_x_offset,5);
//--- Array of column image offsets along the Y axis
   int image_y_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_y_offset,4);
//--- Properties
   m_table.XSize(560);
   m_table.YSize(190);
   m_table.Font(m_base_font);
   m_table.FontSize(m_base_font_size);
   m_table.CellYSize(20);
   m_table.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_table.TextAlign(align);
   m_table.ColumnsWidth(width);
   m_table.TextXOffset(text_x_offset);
   m_table.ImageXOffset(image_x_offset);
   m_table.ImageYOffset(image_y_offset);
   m_table.LabelXGap(5);
   m_table.LabelYGap(4);
   m_table.IconXGap(7);
   m_table.IconYGap(4);
   m_table.MinColumnWidth(0);
   m_table.LightsHover(true);
   m_table.SelectableRow(false);
   m_table.IsWithoutDeselect(false);
   m_table.ColumnResizeMode(true);
   m_table.IsZebraFormatRows(clrWhiteSmoke);
   m_table.AutoXResizeMode(true);
   m_table.AutoXResizeRightOffset(10);
   m_table.AutoYResizeMode(true);
   m_table.AutoYResizeBottomOffset(50);
//--- Populate the table with data
   InitializingTable();
//--- Create the control
   if(!m_table.CreateTable(x_gap,y_gap))
      return(false);
//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(0,m_table);
   return(true);
}

テーブルを作成するためのこのメソッドを使用する前に、次のデータで満たされるべきです:現在のトレード口座のMarket Watchを形成するすべてのシンボルのリスト。 InitializingTable() メソッドによって行われます: 基底クラスのプライベートセクションに追加します。

//+------------------------------------------------------------------+
//| Initialize the table                                             |
//+------------------------------------------------------------------+
void CProgram::InitializingTable(void)
{
//--- Array of icons 1
   string image_array1[2]=
   {
      "Images\\EasyAndFastGUI\\Controls\\checkbox_off.bmp",
      "Images\\EasyAndFastGUI\\Controls\\checkbox_on_g.bmp"
   };
//---
   int k=0;
   for(int c=0; c<COLUMNS1_TOTAL; c++)
   {
      //---
      for(int r=0; r<ROWS1_TOTAL; r++)
      {
         if(k<m_all_symbols)
         {
            //--- Set the cell type to Checkbox
            m_table.CellType(c,r,CELL_CHECKBOX);
            m_table.SetImages(c,r,image_array1);
            //--- Set the text
            m_table.SetValue(c,r,SymbolName(k,false));
         }
         k++;
      }
   }
}

さて、CreateStepWindow()メソッドのボディで上記の準備をして、テーブルを作成して埋めるようにします。 その結果、マーケットウォッチ(図5)から、同じくチェックボックス選択型であるが、表形式で提示されている全シンボルのリストを得ることになる。

図5 シンボルのリストを表形式に変換した結果

次に、新しく作成されたテーブルを、以前のチェックボックスのセットで利用可能だったインタラクションとリンクさせます。 以下のような可能性があります。

  • 定義済みのセットを選択します。ALL、メジャー、クロスを選択します。
  • シンボルのカスタムセットのテンプレートを保存して読み込みます。

最初の可能性を実装するには、セクションをOnEvent()のテンプレート名に置き換えると、以下のコードになります。

//--- All
         if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed())
         {
            m_currency_set[1].IsPressed(false);
            m_currency_set[2].IsPressed(false);
            m_currency_set[1].Update(true);
            m_currency_set[2].Update(true);
            //---
            int k=0;
            for(int c=0; c<7; c++)
            {
               //---
               for(int r=0; r<MathCeil(m_all_symbols/7); r++)
               {
                  if(k<m_all_symbols)
                     m_table.ChangeImage(c,r,1);
                  k++;
               }
            }
            m_table.Update(true);
         }
         //--- Majors
         else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed())
         {
            m_currency_set[0].IsPressed(false);
            m_currency_set[2].IsPressed(false);
            m_currency_set[0].Update(true);
            m_currency_set[2].Update(true);
            //---
            string pairs[4]= {"EURUSD","GBPUSD","USDCHF","USDJPY"};
            //--- Clear the selection
            int k=0;
            for(int c=0; c<7; c++)
            {
               //---
               for(int r=0; r<MathCeil(m_all_symbols/7); r++)
               {
                  if(k<m_all_symbols)
                     m_table.ChangeImage(c,r,0);
                  k++;
               }
            }
            //---
            k=0;
            for(int c=0; c<7; c++)
            {
               //---
               for(int r=0; r<MathCeil(m_all_symbols/7); r++)
               {
                  if(k<m_all_symbols)
                  {
                     for(int j=0; j<4; j++)
                     {
                        if(m_table.GetValue(c,r)==pairs[j])
                           m_table.ChangeImage(c,r,1);
                     }
                  }
                  k++;
               }
            }
            m_table.Update(true);
         }
         //--- Crosses
         else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed())
         {
            m_currency_set[0].IsPressed(false);
            m_currency_set[1].IsPressed(false);
            m_currency_set[0].Update(true);
            m_currency_set[1].Update(true);
            //---
            string pairs[20]=
            {
               "EURUSD","GBPUSD","USDCHF","USDJPY","USDCAD","AUDUSD","AUDNZD","AUDCAD","AUDCHF","AUDJPY",
               "CHFJPY","EURGBP","EURAUD","EURCHF","EURJPY","EURCAD","EURNZD","GBPCHF","GBPJPY","CADCHF"
            };
            //--- Clear the selection
            int k=0;
            for(int c=0; c<7; c++)
            {
               //---
               for(int r=0; r<MathCeil(m_all_symbols/7); r++)
               {
                  if(k<m_all_symbols)
                     m_table.ChangeImage(c,r,0);
                  k++;
               }
            }
            //---
            k=0;
            for(int c=0; c<7; c++)
            {
               //---
               for(int r=0; r<MathCeil(m_all_symbols/7); r++)
               {
                  if(k<m_all_symbols)
                  {
                     for(int j=0; j<20; j++)
                     {
                        if(m_table.GetValue(c,r)==pairs[j])
                           m_table.ChangeImage(c,r,1);
                     }
                  }
                  k++;
               }
            }
            m_table.Update(true);
         }
         //---
         if((lparam==m_currency_set[0].Id() && !m_currency_set[0].IsPressed())      ||
               (lparam==m_currency_set[1].Id() && !m_currency_set[1].IsPressed())   ||
               (lparam==m_currency_set[2].Id() && !m_currency_set[2].IsPressed())
           )
         {
            //--- Clear the selection
            int k=0;
            for(int c=0; c<7; c++)
            {
               //---
               for(int r=0; r<MathCeil(m_all_symbols/7); r++)
               {
                  if(k<m_all_symbols)
                     m_table.ChangeImage(c,r,0);
                  k++;
               }
            }
            m_table.Update(true);
         }

選択されたシンボルセットの保存と読み込みは、使用したメソッドを使用して行いました。 選択されたシンボルセットの保存と読み込みには、それぞれSaveSymbolSet()メソッドとLoadSymbolSet()メソッドを使用しました。 ここでは、新たに作成したテーブルからデータを取得する必要があるため、チェックボックスからデータを取得するコードパートを変更する必要があります。 したがって、データは同じテーブルにロードする必要があります。

//+------------------------------------------------------------------+
//| Save template to a file                                          |
//+------------------------------------------------------------------+
bool CProgram::SaveSymbolSet(string file_name)
{
   if(file_name=="")
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Выберите имя шаблона для записи","Монитор сигналов");
      else
         MessageBox("Choose a name for the template to save","Signal Monitor");
      return(false);
   }
   int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_WRITE|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Не удалось создать файл конфигурации","Монитор сигналов");
      else
         MessageBox("Failed to create configuration file","Signal Mo
nitor");
      return(false);
   }
   else
      MessageBox("The "+file_name+" configuration has been successfully saved","Signal Monitor");
//--- Save symbol selection
   int k=0;
   for(int c=0; c<7; c++)
   {
      //---
      for(int r=0; r<MathCeil(m_all_symbols/7); r++)
      {
         if(k<m_all_symbols)
            m_save.tf[k]=m_table.SelectedImageIndex(c,r)>0?true:false;
         k++;
      }
   }
//---
   FileWriteStruct(h,m_save);
   FileClose(h);
//---
   return(true);
}
//+------------------------------------------------------------------+
//| Load data to a panel                                             |
//+------------------------------------------------------------------+
bool CProgram::LoadSymbolSet(string file_name)
{
   if(file_name=="")
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Выберите имя шаблона для загрузки","Монитор сигналов");
      else
         MessageBox("Choose a name for the template to load","Signal Monitor");
      return(false);
   }
   int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_READ|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      MessageBox("Configuration "+file_name+" not found","Signal Monitor");
      return(false);
   }
   ZeroMemory(m_save);
   FileReadStruct(h,m_save);
//--- Load symbol selection
   int k=0;
   for(int c=0; c<7; c++)
   {
      //---
      for(int r=0; r<MathCeil(m_all_symbols/7); r++)
      {
         if(k<m_all_symbols)
         {
            if(m_save.tf[k])
               m_table.ChangeImage(c,r,1);
            else
               m_table.ChangeImage(c,r,0);
         }
         k++;
      }
   }
   m_table.Update(true);
//---
   FileClose(h);
//---
   return(true);
}

では、データを集めて記憶しているブロックをファイルに保存する構造体にマークしてみましょう。 また、ここでは、ファイルから構造体にデータが読み込まれ、そのデータがテーブルに追加されます。

すべての変更と追加の視覚的な結果を図6に示しますが、主な目的は、ウィンドウに収まりきらない多数のシンボルで便利な操作ができるようにすることでした。

図.6 テーブルを追加した結果とUI要素との相互作用。

さらに、選択されたシンボルに関する情報の取得方法を変更したため、ステップ1とステップ2の間の遷移の方法を編集する必要があります。 構成ステップ間の遷移は、変更が必要な2つのメソッドを介して実行されます。 To_Step1()は、ステップ2からステップ1にジャンプする際に、タイムフレームを選択する可能性を非表示にし、テーブルを表示するように修正する必要があります。

//+------------------------------------------------------------------+
//| Go to Step 1                                                     |
//+------------------------------------------------------------------+
void CProgram::ToStep_1(void)
{
//--- Change header
   m_step_window.LabelText("Signal Monitor Step 1: Choose Symbols");
   m_step_window.Update(true);
//--- Hide the Back button
   m_back_button.Hide();
//--- Show the table
   m_table.Show();
//--- Hide timeframes
   for(int i=0; i<21; i++)
      m_checkbox[i].Hide();
   string names[3]= {"All","Majors","Crosses"};
//--- Change names of selection buttons
   for(int i=0; i<3; i++)
   {
      m_currency_set[i].IsPressed(false);
      m_currency_set[i].LabelText(names[i]);
      m_currency_set[i].Update(true);
   }
//--- Show block for working with templates
   m_text_edit.Show();
   m_load_button.Show();
   m_save_button.Show();
//--- Set the current setup step
   m_current_step=1;
}

To_Step2()メソッドでは、テーブルを非表示にし、タイムフレーム選択を表示し、最初のステップで選択したシンボルを記憶します。 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::ToStep_2(void)
{
//--- Check whether at least one symbol is selected
   int cnt=0;
//---
   for(int c=0; c<7; c++)
   {
      for(int r=0; r<MathCeil(m_all_symbols/7); r++)
      {
         if(m_table.SelectedImageIndex(c,r)>0)
            cnt++;
      }
   }
//---
   if(cnt<1)
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Не выбран ни один символ!","Внимание");
      else
         MessageBox("No symbols selected!","Warning");
      return;
   }
//--- Hide the table
   m_table.Hide();
//--- Display timeframes
   for(int i=0; i<21; i++)
      m_checkbox[i].Show();
//--- Count the number of selected symbols
   ArrayResize(m_symbols,cnt);
   cnt=0;
//--- Remember the selected symbols in the array
   for(int c=0; c<7; c++)
   {
      for(int r=0; r<MathCeil(m_all_symbols/7); r++)
      {
         if(m_table.SelectedImageIndex(c,r)>0)
         {
            m_symbols[cnt]=m_table.GetValue(c,r);
            cnt++;
         }
      }
   }
//--- Set selected symbols in Market Watch
   for(int c=0; c<7; c++)
   {
      for(int r=0; r<MathCeil(m_all_symbols/7); r++)
      {
         if(m_table.SelectedImageIndex(c,r)>0)
            SymbolSelect(m_table.GetValue(c,r),true);
         else
            SymbolSelect(m_table.GetValue(c,r),false);
      }
   }
//---
   if(m_current_step==3)
   {
      m_add_signal.Hide();
      m_signal_header.Hide();
      m_next_button.LabelText("Next");
      m_next_button.Update(true);
      for(int i=0; i<5; i++)
         m_signal_editor[i].Hide();
      ClearSaves();
   }
//--- Change header
   m_step_window.LabelText("Signal Monitor Step 2: Choose Timeframes");
   m_step_window.Update(true);
   string names[3]= {"All","Junior","Senior"};
//--- Change names of selection buttons
   for(int i=0; i<3; i++)
   {
      m_currency_set[i].LabelText(names[i]);
      m_currency_set[i].IsPressed(false);
      if(m_current_step==3)
         m_currency_set[i].Show();
      m_currency_set[i].Update(true);
   }
//--- Hide block for working with templates
   m_text_edit.Hide();
   m_load_button.Hide();
   m_save_button.Hide();
//--- Show Back button
   m_back_button.Show();
//---
   m_current_step=2;
}

さて、チェックボックスのリストでプリセットのタイムフレームセットを選択するボタンの相互作用を調整する必要があります。 現在はリストが一定なので、コードに適切な変更を加える必要があります

//--- All
         if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed())
         {
            m_currency_set[1].IsPressed(false);
            m_currency_set[2].IsPressed(false);
            m_currency_set[1].Update(true);
            m_currency_set[2].Update(true);
            //---
            for(int i=0; i<21; i++)
            {
               m_checkbox[i].IsPressed(true);
               m_checkbox[i].Update(true);
            }
         }
         //--- Junior Timeframes
         else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed())
         {
            m_currency_set[0].IsPressed(false);
            m_currency_set[2].IsPressed(false);
            m_currency_set[0].Update(true);
            m_currency_set[2].Update(true);
            //---
            string pairs[11]=
            {
               "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30"
            };
            //--- Clear the selection
            for(int i=0; i<21; i++)
            {
               m_checkbox[i].IsPressed(false);
               m_checkbox[i].Update(true);
            }
            //---
            for(int i=0; i<21; i++)
            {
               for(int j=0; j<11; j++)
                  if(m_checkbox[i].LabelText()==pairs[j])
                  {
                     m_checkbox[i].IsPressed(true);
                     m_checkbox[i].Update(true);
                  }
            }
         }
         //--- Senior Timeframes
         else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed())
         {
            m_currency_set[0].IsPressed(false);
            m_currency_set[1].IsPressed(false);
            m_currency_set[0].Update(true);
            m_currency_set[1].Update(true);
            //---
            string pairs[10]=
            {
               "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
            };
            //--- Clear the selection
            for(int i=0; i<21; i++)
            {
               m_checkbox[i].IsPressed(false);
               m_checkbox[i].Update(true);
            }
            //---
            for(int i=0; i<21; i++)
            {
               for(int j=0; j<10; j++)
                  if(m_checkbox[i].LabelText()==pairs[j])
                  {
                     m_checkbox[i].IsPressed(true);
                     m_checkbox[i].Update(true);
                  }
            }
         }
         //---
         if((lparam==m_currency_set[0].Id() && !m_currency_set[0].IsPressed())      ||
               (lparam==m_currency_set[1].Id() && !m_currency_set[1].IsPressed())   ||
               (lparam==m_currency_set[2].Id() && !m_currency_set[2].IsPressed())
           )
         {
            //--- Clear the selection
            for(int i=0; i<21; i++)
            {
               m_checkbox[i].IsPressed(false);
               m_checkbox[i].Update(true);
            }
         }


モニターからの検索ルールの素早い編集

シグナルモニタリング中に、ユーザーは以前に作成されたトレードシグナルの条件を変更する必要があるかもしれません。 現在は、アプリケーションを再起動して、必要なものに加えてモニターのすべてのシグナルを再設定することで行うことができます。 この解決策は便利ではありません。 したがって、モニター自体から準備ができているトレードシグナルを編集する可能性を提供してみましょう。 図7に示すように、小さなダイアログボックスを開くためのボタンをモニターインターフェースに追加してみましょう。 このボックスには、作成されたすべてのトレードシグナルのリストが表示されます。 シグナルをクリックすると編集用に開きます。

図7 モニターから先に作成されたシグナルを編集

では実装を進めていきましょう。 トレードシグナルの一覧でウィンドウを開くボタンを表示するには、CreateStepWindow()メソッドのボディに以下のプロパティを追加します。

m_step_window.TooltipsButtonIsUsed(true);

 そして、GUI作成完了イベントで無効にします - このようにして、ボタンは最初のアプリケーションセットアップステップでは表示されず、すべてのシグナルが作成されてモニターが起動した後にのみ表示されます。

// --- GUI creation completion
   if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
   {
      ...
      m_step_window.GetTooltipButtonPointer().Hide();
   }

モニターの読み込み時に有効にします。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AutoResize(const int x_size,const int y_size)
{
   ...
   m_step_window.GetTooltipButtonPointer().Show();
}

ここで、作成されたシグナルのリストが表示される新しいダイアログボックスを作成します。 CWindow クラスのインスタンス変数と、ウィンドウの作成を実装するCreateFastEdit()メソッド、ボタンを作成するCreateFastEditor()メソッドを作成します(ボタンをクリックすることでシグナル編集が行われます)。

   CWindow           m_fast_edit;

   bool              CreateFastEdit(const string caption_text);
   bool              CreateFastEditor(CButton &button,string text,const int x_gap,const int y_gap);

メソッドを実施します。

//+------------------------------------------------------------------+
//| Creates a window for creating and editing trading signals        |
//+------------------------------------------------------------------+
bool CProgram::CreateFastEdit(const string caption_text)
{
//--- Add the window pointer to the window array
   CWndContainer::AddWindow(m_fast_edit);
//--- Properties
   m_fast_edit.XSize(180);
   m_fast_edit.YSize(280);
//--- Coordinates
   int x=m_step_window.XGap()+m_step_window.XSize()+10;
   int y=m_step_window.YGap();
//---
   m_fast_edit.CaptionHeight(22);
   m_fast_edit.IsMovable(true);
   m_fast_edit.CaptionColor(m_caption);
   m_fast_edit.CaptionColorLocked(m_caption);
   m_fast_edit.CaptionColorHover(m_caption);
   m_fast_edit.BackColor(m_background);
   m_fast_edit.FontSize(m_base_font_size);
   m_fast_edit.Font(m_base_font);
   m_fast_edit.WindowType(W_DIALOG);
//--- Creating the form
   if(!m_fast_edit.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//---
   for(int i=0; i<5; i++)
   {
      if(!CreateFastEditor(m_fast_editor[i],"Signal_"+string(i),10,40*i+40))
         return(false);
   }
   return(true);
}
//+------------------------------------------------------------------+
//| Creates a button with an image                                   |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_light.bmp"
bool CProgram::CreateFastEditor(CButton &button,string text,const int x_gap,const int y_gap)
{
//---
   color baseclr=C'70,180,70';
   color pressed=C'70,170,70';
//--- Save the window pointer
   button.MainPointer(m_fast_edit);
//--- Set up properties before creation
   button.XSize(110);
   button.YSize(30);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.IconXGap(3);
   button.IconYGap(7);
   button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_light.bmp");
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressed);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressed);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create the control
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add the element pointer to the base
   CWndContainer::AddToElementsArray(3,button);
   return(true);
}

CreateGUI()メソッド本体のCreateFastEdit()メソッドを呼び出します。

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Step 1-3. Symbol selection window.
   if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols"))
      return(false);
//---
   if(!CreateSetWindow("Signal Monitor Edit Signal"))
      return(false);
//--- Creating form 2 for the color picker
   if(!CreateColorWindow("Color Picker"))
      return(false);
//--- Creating a quick edit form
   if(!CreateFastEdit("Fast Signal Editor"))
      return(false);
//--- Finishing the creation of GUI
   CWndEvents::CompletedGUI();
   return(true);
}

さて、モニターの設定ボタンをクリックすると、シグナルのダイアログボックスが開きます。 これを行うには、OnEvent()メソッドのボタンクリックイベントセクションに以下のコードを追加します。

      //--- OPEN THE SETTING WINDOW
      if(lparam==m_step_window.GetTooltipButtonPointer().Id())
      {
         //--- Coordinates
         int x=m_step_window.X()+m_step_window.XSize()+10;
         int y=m_step_window.Y();
         m_fast_edit.X(x);
         m_fast_edit.Y(y);
         m_fast_edit.OpenWindow();
      }

今、プロジェクトをコンパイルすると以下のような結果が得られます。

図8 トレードシグナルを素早く編集するためのウィンドウの追加

このボックスにはすべてのシグナル編集ボタンが表示されますが、作成されたシグナルのみを表示することができます。 そこで、現在の利用可能なシグナル数のチェックを追加してみましょう。 これは、ダイアログボックスを開くイベントによって行うことができます。

//--- Opening a dialog window
   if(id==CHARTEVENT_CUSTOM+ON_OPEN_DIALOG_BOX)
   {
      if(m_current_step<4)
         return;
      for(int i=0; i<5; i++)
      {
         if(!FileIsExist("Signal Monitor\\signal_"+string(i)+".bin"))
            m_fast_editor[i].Hide();
      }
   }

ここでは、トレーディングシグナルを持つファイルが存在するかどうかのチェックが行われます。 これより、以前に作成されたシグナルのみが表示されます。 ここで、作成されたシグナルのボタンをクリックすると、そのシグナルの編集ウィンドウが開きます。 ボタンクリックイベントセクションで行われます。

      //--- Trading signal editing
      for(int i=0; i<5; i++)
      {
         if(lparam==m_fast_editor[i].Id())
         {
            m_fast_edit.CloseDialogBox();
            LoadSignalSet(i);
            m_new_signal.LabelText("Save");
            m_new_signal.Update(true);
            RebuildParameters(m_indicator_type.GetListViewPointer().SelectedItemIndex());
            m_set_window.OpenWindow();
            m_number_signal=i;
         }
      }

クイックエディットウィンドウでシグナルをクリックすると、設定ウィンドウが開き、このトレードシグナルの以前に保存されたデータがロードされます。 そして、必要なデータを変更したら、新しい設定をファイルに書き込みます。 この場合は、モニターの設定ステップをすべて完了させる必要はありません。

アプリケーションローカライゼーション

ローカリゼーションの課題を解決するためには、翻訳可能なすべてのGUI要素を決定する必要がありますが、一部の要素は一般的に受け入れられている名前のままにしておく必要があります。 選択した言語に応じてUI要素の置換に使用するデータを持つ文字列配列を作成します。 2つの言語を用意します。ロシア語と英語です。 まず、SignalMonitor.mq5ファイルに起動時に任意のUI言語を選択できるようにする列挙を作成しましょう。 一部の要素の名称は、英語の基準に沿って設定されます。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum UPDATE
{
   MINUTE,        // 1 minute
   MINUTE_15,     // 15 minutes
   MINUTE_30,     // 30 minutes
   HOUR,          // 1 hour
   HOUR_4         // 4 hour
};
enum LANG
{
   RUSSIAN,       // Russian
   ENGLISH        // English
};
//+------------------------------------------------------------------+
//| Expert Advisor input parameters                                  |
//+------------------------------------------------------------------+
input int                  Inp_BaseFont      =  10;                  // Base Font
input color                Caption           =  C'0,130,225';        // Caption Color
input color                Background        =  clrWhiteSmoke;       // Back color
input LANG                 Language          =  ENGLISH;             // Interface language
input UPDATE               Update            =  MINUTE;              // Update interval

選択した言語に関する情報をインターフェイスに渡すには、CProgram基底クラスのパブリックセクションに変数を作成します。

   //---
   int               m_language;

選択された言語のインデックスは、アプリケーションの初期化時に変数に代入されます。

   program.m_language=Language;

そして、基底クラスのプライベートセクションに配列を作成し、選択された言語に応じてインターフェイスで置換されるデータのレシーバとして機能します。 また、インターフェースにデータをロードするメソッドを作成します。

   string            m_lang[];

   void              ChangeLanguage(void);

さて、Program.mqhファイルに宣言されたメソッドを実装し、各GUI要素の適切なフィールドに言語の値を設定します。

//+------------------------------------------------------------------+
//| Changing the interface language                                  |
//+------------------------------------------------------------------+
void CProgram::ChangeLanguage(void)
{
//---
#define ITEMS 40
   ArrayResize(m_lang,ITEMS);
   string rus[ITEMS]=
   {
      "Монитор Сигналов Шаг 1: Выбор Символов","Все","Мажоры","Кроссы",
      "Назад","Далее","Загрузка(L)","Сохранить(S)","Имя шаблона","Монитор сигналов Шаг 2: Выбор таймфреймов",
      "Все","Младшие","Старшие",
      "Монитор Сигналов Шаг 3: Создание торговых сигналов","Создать","Добавить сигнал","Список сигналов",
      "Редактор торговых сигналов","Тип индикатора","1.Настройки индикатора","Примен. цена",
      "Введите путь индикатора","Введите параметры индикатора через запятую",
      "2.Настройка сигнала","Правило","Метка","Значение","Текст","Цвет метки","Фон","Кант","Подсказка",
      "Изображение","Таймфреймы","Добавить","Отмена","Монитор торговых сигналов","Номер буфера","Сохранить"
   };
   string eng[ITEMS]=
   {
      "Signal Monitor Step 1: Choose Symbols","ALL","Major","Crosses",
      "Back","Next","Load(L)","Save(S)","Template name","Signal Monitor Step 2: Choose Timeframes",
      "ALL","Junior","Senior",
      "Signal Monitor Step 3: Creating Trading Signals","Create","Add Signal","Signal List",
      "Signal Monitor Edit Signal","Indicator Type","1.Indicator Settings","Applied Price",
      "Enter the indicator path","Enter indicator parameters separated by commas",
      "2.Signal Settings","Rule","Label","Value","Text","Label Color","Use Background","Use Border","Use Tooltip",
      "Use Image","Timeframes","Add","Cancel","Signal Monitor","Buffer number","Save"
   };
//--- Russian
   if(m_language==0)
      ArrayCopy(m_lang,rus);
//--- English
   else
      ArrayCopy(m_lang,eng);
}

そこで、ロシア語を追加実装しました(図9)。 同様に、お好みの言語を追加することができます。

図9 GUIローカリゼーションの結果。


その他の機能

追加関数により、モニターの視覚的なパートが改善され、シグナルが出現したシンボルチャートに素早く切り替えることが可能になります。 現在使用されている形が小さいようなので、視覚的なパートはシグナルブロックの延長線上にあります。 CreateSignalButton()メソッドを見つけて、シグナル・ブロックのサイズを大きくするとともに、ブロック内の要素のポジションを調整してくださいTo_Monitor()メソッドで、ブロックの相対的な配置を調整してください

   button.XSize(60);
   button.YSize(30);
   button.IconXGap(2);
   button.IconYGap(11);
   button.LabelXGap(19);
   button.LabelYGap(10);

//--- Symbols
   int sy=ArraySize(m_symbols);
   ArrayResize(m_symbol_label,sy);
   for(int i=0; i<sy; i++)
   {
      if(!CreateSymbolLabel(m_symbol_label[i],5,m_step_window.CaptionHeight()+40+i*35,m_symbols[i]))
         return;
      m_symbol_label[i].Update(true);
   }
//--- Timeframes
   int tf=ArraySize(m_timeframes);
   ArrayResize(m_timeframe_label,tf);
//---
   for(int i=0; i<tf; i++)
   {
      if(!CreateTimeframeLabel(m_timeframe_label[i],110+65*i,m_step_window.CaptionHeight()+3,m_timeframes[i]))
         return;
      m_timeframe_label[i].Update(true);
   }
//-- Signal blocks
   int k=0;
   ArrayResize(m_signal_button,sy*tf);
   for(int j=0; j<sy; j++)
   {
      for(int i=0; i<tf; i++)
      {
         if(!CreateSignalButton(m_signal_button[k],m_timeframe_label[i].XGap()+m_timeframe_label[i].XSize()/2,m_step_window.CaptionHeight()+35+j*35))
            return;
         m_signal_button[k].Update(true);
         k++;
      }
   }
//---
   m_current_step=4;
//--- Resize window
   AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+15,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+10);

このモニターの実装は、トラッキングに際しはるかに便利です。

図10 シグナルブロックのリサイズとモニタインタフェースの調整

では、シグナルが出たシンボルと時間帯のチャートの開き方を実装してみましょう。 チャートは、対応するブロックをクリックして開く必要があります。 OnEvent()メソッドのボタンクリックイベントのパートに以下のように追加します(シグナルブロックがボタンなので)。

      //--- CLICKING ON THE SIGNAL BLOCK
      for(int i=0; i<ArraySize(m_signal_button); i++)
      {
         if(lparam==m_signal_button[i].Id())
            ChartOpen(GetSymbol(i),GetTimeframe(i));
      }

何もかもがシンプルです。 この時点で現在の開発フェーズは終了します。 次のパートでは、シグナル探索システムの改良を続け、コンポジットシグナルの概念を紹介し、モニター制御の関数を拡張していきます。


結論

以下に添付されているアーカイブには、説明されているすべてのファイルがフォルダに適切に配置されています。 正しく操作するためには、ターミナルのルートディレクトリにMQL5フォルダを保存する必要があります。 MQL5フォルダがあるターミナル・ルート・ディレクトリを開くには、MetaTrader5ターミナルでCtrl+Shift+Dキーの組み合わせを押すか、以下の図11に示すようにコンテキスト・メニューを使用します。


図11 MetaTrader5のターミナルルートでMQL5フォルダを開く


MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/7678

添付されたファイル |
MQL5.zip (501.31 KB)
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト
本稿は、プログラムで使用されるすべての銘柄の指定された時間枠の時系列コレクションの開発についてです。時系列コレクション、コレクションの時系列パラメータを設定するメソッド、および開発された時系列に履歴データを最初に入力するメソッドを開発します。
時系列の予測(第2部):最小二乗サポートベクターマシン(LS-SVM) 時系列の予測(第2部):最小二乗サポートベクターマシン(LS-SVM)
この記事では、サポートベクター法に基づいて時系列を予測するアルゴリズムの理論と実際の使用法について説明します。また、このメソッドのMQL実装を提案し、テスト指標とエキスパートアドバイザーを提示します。このテクノロジーはまだMQLに実装されていません。まず、そのための数学を理解する必要があります。
DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス
本稿では、時系列データのリアルタイム更新と、すべての銘柄のすべての時系列から「新しいバー」イベントに関するメッセージを制御プログラムチャートに送信し、カスタムプログラムでこれらのイベントを処理する機能について検討します。「新しいティック」クラスは、現在以外のチャート銘柄と期間の時系列を更新する必要性を判断するために使用されます。
MQLプログラムのグラフィカルインターフェイスのマークアップツールとしてのMQL 第1部 MQLプログラムのグラフィカルインターフェイスのマークアップツールとしてのMQL 第1部
この論文では、MQLの構造体を用いて、MQLプログラムのウィンドウインタフェースを記述するための新しい概念を提案します。 特別なクラスは、表示可能なMQLマークアップをGUI要素に変換し、管理し、プロパティを設定し、イベントを統一的に処理することができます。 また、標準ライブラリのダイアログや要素にマークアップを使用する例をいくつか紹介します。