トレードシグナルの多通貨監視(その4)。機能強化とシグナル検索システムの改善
目次
- イントロダクション
- トレーディングシグナルを生成するためのカスタムインジケータ
- トレードシグナル検索ルールの仕組みを拡充
- シンボルのリストを表形式に変換
- モニターからの検索ルールの素早い編集
- アプリケーションローカライゼーション
- その他の機能
- 結論
イントロダクション
第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つのルール設定タイプを持つ新しいドロップダウンリストを追加する必要があります。CompareとIntervalです。
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
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索