
取引シグナルの多通貨監視(その3):検索アルゴリズムの紹介
目次
イントロダクション
前回の記事では、取引シグナルを監視するためのアプリの構成を作成しました。 また、基本的なインタラクション機能を備えたアプリケーションインターフェイスを実装しました。 さて、シンボルの設定と検索のアルゴリズムの視覚的な部分を埋める時が来ました。 前回の記事にあったプロジェクトをベースに、少しずつ新機能を追加していく予定です。
シンボルセット保存システム
前回は、最初のアプリケーション設定ステップで、マーケットウォッチからシンボルを選択するためのツールを作成しました。 シンボルは3つの方法で選択することができました。
- マニュアル 必要なシンボルをマークし、[次へ] をクリックします。
- あらかじめ定義されたセット。 「すべて」、「メジャー」、または「クロス」をクリックすると、事前に定義された特定のシンボルセットが自動的に選択されます。
- 保存されたセット。 最初の2つの方法で設定され、特定の名前でファイルに保存された、あらかじめ用意されたシンボルのセット。
図1 アプリケーション設定ステップ1とセーブドセットの設定
最初の2つの方法は非常にシンプルで、先に作成されています。 第三の方法を模索する必要があります。 では、これから何をしようとしているのか、より具体的に定義してみましょう。 図1の赤枠内の要素の相互作用をさらに深く掘り下げていくと、次のようなことが暗示されます。
- 必要なシンボルはチェックマークで表示され、ユーザーは「テンプレート名」フィールドに名前を入力し、「保存」をクリックするか、Sホットキーを押します。 保存に成功すると、適切なメッセージが表示されます。
- 以前に設定して保存したテンプレートにアクセスするには、フィールドにテンプレート名を入力し、Load または L ホットキーを押します。
プロジェクトを開き、CProgramの基底クラスを見つけ、そのプライベートセクションに2つのメソッドを追加します。 このメソッドは、シンボルテンプレートの読み込みと保存を担当します。
bool SaveSymbolSet(string file_name); bool LoadSymbolSet(string file_name);
以下、これらのメソッドがどのように実装されているかを説明します。
//+------------------------------------------------------------------+ //| Save template to a file | //+------------------------------------------------------------------+ bool CProgram::SaveSymbolSet(string file_name) { if(file_name=="") { MessageBox("Select the template name to record","Signal Monitor"); return(false); } int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_WRITE|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Failed to create a configuration file","Signal Monitor"); return(false); } else MessageBox("The "+file_name+" configuration has been successfully saved","Signal Monitor"); //--- Save the selection of timeframes and patterns for(int i=0; i<m_all_symbols; i++) m_save.tf[i]=m_checkbox[i].IsPressed(); //--- FileWriteStruct(h,m_save); FileClose(h); //--- return(true); } //+------------------------------------------------------------------+ //| Load data to a panel | //+------------------------------------------------------------------+ bool CProgram::LoadSymbolSet(string file_name) { if(file_name=="") { MessageBox("Select the template name 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); //--- Loading timeframes for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(m_save.tf[i]); m_checkbox[i].Update(true); } //--- FileClose(h); //--- return(true); }
しかし、今プロジェクトをコンパイルしようとすると、m_save変数に関連したエラーが発生します。 この構造体には、tf という名前の bool 型のパラメータが 1 つあります。 ユーザーが選択したファイルを記憶します。 そこで、アプリケーションクラスにこの構造体を作成し、そのインスタンスを基底クラスに追加します。
//+------------------------------------------------------------------+ //| Class for creating the application | //+------------------------------------------------------------------+ struct SAVE { bool tf[100]; }; class CProgram : public CWndEvents { ... SAVE m_save;
OnEvent() に移動し、ボタンクリックイベントに関連する部分を入力し、ステップ1の条件に以下のコードを追加します。
//--- Save the template if(lparam==m_save_button.Id()) { SaveSymbolSet(m_text_edit.GetValue()); } //--- Load the template if(lparam==m_load_button.Id()) { LoadSymbolSet(m_text_edit.GetValue()); }
また、上記ボタンのホットキーの使い方を実装してください。 同じメソッドで、キープレスイベントのチェックを追加し、使用するキーのコードを追加します。
//--- Key press if(id==CHARTEVENT_KEYDOWN) { if(m_current_step==1) { short sym=TranslateKey((int)lparam); //--- if the entered character is successfully converted to Unicode if(sym>0) { if(ShortToString(sym)=="l" || ShortToString(sym)=="д") LoadSymbolSet(m_text_edit.GetValue()); if(ShortToString(sym)=="s" || ShortToString(sym)=="ы") SaveSymbolSet(m_text_edit.GetValue()); } } }
プロジェクトをコンパイルします。 コンパイルに成功すると、以下のような結果が得られます。
図2 ユーザーテンプレートの保存と読み込み
トレーディングシグナルの追加と編集
さて、取引シグナルの作成と編集だけでなく、モニターでのさらなる追跡のためのメインアプリケーションの部分に移ります。 シグナルの作成・編集はこのような具合です。
図3 シグナル作成・編集画面
現段階では、ウィンドウは様々なパラメータを制御するGUI要素のセットとして表示されます。 しかし、これらの設定はどこでも使われるものではありません。 インターフェイスに2つのボタンを追加することから始めます。 取引シグナルを追加/保存します。 別のボタンは、作成/編集をキャンセルします。 Program.mqh を開き、この2つのボタンの実装メソッドを基底クラスに追加します。
bool CreateButton3(CButton &button,string text,const int x_gap,const int y_gap);
2つのCButtonボタンインスタンス。
CButton m_new_signal; CButton m_cancel_button;
SetWindow.mqhに移動して、このメソッドを実装します。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateButton3(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_set_window); //--- Set up properties before creation button.XSize(60); button.YSize(30); button.Font(m_base_font); button.FontSize(m_base_font_size); 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(1,button); return(true); }
これら2つのボタンをシグナルを追加する取引ウィンドウのインターフェースに表示させるには、CreateSetWindow()メソッドのボディの最後に以下の行を挿入します。
//--- Add/Cancel Buttons if(!CreateButton3(m_new_signal,"Add",m_set_window.XSize()-2*(60+10),m_set_window.YSize()-(30+10))) return(false); if(!CreateButton3(m_cancel_button,"Cancel",m_set_window.XSize()-(60+10),m_set_window.YSize()-(30+10))) return(false);
プロジェクトのコンパイル後、トレーディングシグナル作成ウィンドウの下部に2つのボタンが表示されます。
図4 シグナル作成ボタンとキャンセルボタンの追加
次に、ボタンをクリックしたときに発生するイベントを追加する必要があります。 キャンセルボタンの効果は明らかです:それは与えられたウィンドウ内のアクションや設定を保存せず、シグナルを追加せずにウィンドウを閉じます。 追加ボタンについて詳しく考えてみましょう。
まず、「追加」がクリックされたときに実行されるアクションの順序を決めてみましょう。
- ボタンをクリックすると、取引シグナル作成ウィンドウのUI要素を使用して選択したパラメータが保存されます。
- 保存に成功すると、ウィンドウが閉じられ、メインウィンドウのシグナルリストにシグナル名の最初のレコードが表示されます。
- レコードをクリックすると、以前に保存されたセットがシグナル設定UI要素に適用され、追加ボタンが保存に変換されます。
設定をファイルに保存できるようにするには、編集画面での視覚的な表示や、その後のシグナル検索に使用されるユニバーサルな設定を作成する必要があります。 そこで、構造体を作ってSIGNALと名づけましょう。 作成・編集画面での設定の設定は、この構造に書き込まれます。
struct SIGNAL { int ind_type; int ind_period; int app_price; int rule_type; double rule_value; 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]; };
構造体の各要素を見てみましょう。
- ind_type - には、シグナル検索基準として選択された指標の種類が含まれています。 インターフェイスでは、Indicator Typeとして表示されます。
- ind_period - 選択した指標の期間を指定します。
- app_price - 指標算出に使用した価格 この値はすべての表示に対して利用できるわけではないので、該当する場合にのみ記述します。 例えば、RSIには使用されているが、WPRには使用されていない。
- rule_type - トレーディングシグナルを検索する際に使用するルールの種類を設定します。 インターフェイスでは、==, >=, <= などの文字を含むドロップダウンメニューとして表示されます。
- rule_value - 検索規則を適用する選択された指標の閾値。
- label_type - この要素は、テキストラベルの表示タイプを格納します。 現在のインジケータ値、または3文字以内のカスタムラベルです。
- label_value - 2番目のテキストラベル表示タイプが選択されている場合、このパラメータはユーザーによって指定されたカスタムラベルテキストを保存します。
- label_color - はテキストラベルの色を格納します。
- back_color - このオプションが選択されている場合、シグナルブロックの背景色をモニタに保存します。
- border_color - このオプションが選択されている場合、シグナルブロックの境界色をモニタに保存します。
- tooltip - には、ツールチップが使用されているかどうかの表示が含まれています。
- tooltip_text - ツールチップを使用する場合、このパラメータにはテキストが含まれます。
- image - 画像の使用状況の表示。
- img_index - 使用されている場合は、画像のシーケンス番号を保存します。
- timeframes - 2番目のステップで選択された作業時間枠の設定に関する情報を含む配列。
- tf_name - 取引シグナルが検索される時間帯を保存します。
ここで、作成したシグナルの設定を保存するための構造体の配列を基底クラスに宣言します。
SIGNAL m_signal_set[5];
また、CProgramクラスのプライベート領域に、パラメータのセットをファイルに保存するためのメソッドと、ファイルから構造体にロードするためのメソッドを2つ作成します。
bool SaveSignalSet(int index); bool LoadSignalSet(int index);
こちらが実装です。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::SaveSignalSet(int index) { //--- int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_WRITE|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Failed to create a configuration file","Signal Monitor"); return(false); } //--- Save the selection //--- Indicator type m_signal_set[index].ind_type=m_indicator_type.GetListViewPointer().SelectedItemIndex(); //--- Indicator period 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(); //--- Rule type m_signal_set[index].rule_type=m_rule_type.GetListViewPointer().SelectedItemIndex(); //--- Rule value m_signal_set[index].rule_value=(double)m_rule_value.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) { 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); //--- 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); } //--- Loading signal rule m_rule_type.SelectItem(m_signal_set[index].rule_type); m_rule_type.GetButtonPointer().Update(true); m_rule_value.SetValue((string)m_signal_set[index].rule_value); m_rule_value.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); }
このようにして、セーブ/ロードアルゴリズムによる第1の動作が完了します。 さて、作成されたシグナルのレコードとして機能するオブジェクトを作成する必要があります。 これらのオブジェクトをクリックすることにより、我々は以前の]作成された取引シグナルのパラメータを編集することができるようになります。 これらのオブジェクトを実装するために、CButtonクラスインスタンスの配列を作成します。
CButton m_signal_editor[5];
また、オブジェクトを作成するメソッドを追加します。
bool CreateSignalEditor(CButton &button,string text,const int x_gap,const int y_gap);
これらのオブジェクトはメインウィンドウに属しているので、このメソッドをStepWindow.mqhファイルに実装してください。
//+------------------------------------------------------------------+ //| Creates a button with an image | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_light.bmp" bool CProgram::CreateSignalEditor(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_step_window); //--- 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(0,button); return(true); }
このメソッドを使用して、CreateStepWindow()ボディに5つのオブジェクトを追加し、これがシグナルのリストのオブジェクトとなります。
//--- for(int i=0; i<5; i++) { if(!CreateSignalEditor(m_signal_editor[i],"Signal_"+string(i),10,40*i+90)) return(false); }
アプリケーションの起動後にこれらの要素の表示を無効にするには、 CreateGUI() メソッドで非表示にします。
//+------------------------------------------------------------------+ //| 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); //--- Finishing the creation of GUI CWndEvents::CompletedGUI(); 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(); return(true); }
プロジェクトのコンパイルの前に行うべき次のことは、初期設定中に以前に保存したデータをすべて削除するメソッドを作成することです。 そのためには、ClearSaves()メソッドを作成し、CProgramクラスのコンストラクタで呼び出します。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::ClearSaves(void) { for(int i=0; i<5; i++) FileDelete("Signal Monitor\\signal_"+string(i)+".bin"); m_total_signals=0; return(true); }
さて、Add Signalボタンクリックイベントに以下を追加します。
//--- Add Signal button click event if(lparam==m_add_signal.Id()) { m_set_window.OpenWindow(); m_number_signal=-1; RebuildTimeframes(); m_new_signal.LabelText("Add"); m_new_signal.Update(true); }
プロジェクトのコンパイル後、新しい取引シグナルを追加する仕組みが完成しました。 次のステップは、以前に作成されたシグナルの編集の実装です。
図.5 新規取引シグナルの追加
全てをまとめてみましょう。 図5に示すように、Add Signalボタンをクリックすることでシグナルを追加することができるように実装しました。 また、シグナルリストに新しいシグナル名のボタンが追加されます。 現在は、編集することができないプリセット値となっています。 しかし、Signal_0をクリックしても何も起こらないので、これを修正しましょう。 選択したシグナルに対して以前に保存されていた設定をそのままインターフェイスにロードして、設定ウィンドウを再度開くことができるようにしてみましょう。 もう一つのアイデアは、ロードされた設定を編集して保存する可能性を実装することです。
CProgram基底クラスのOnEvent()メソッド本体を開き、ボタンクリックイベントのある部分を探します。 そこに以下のコードを追加します。
//--- for(int i=0; i<5; i++) { if(lparam==m_signal_editor[i].Id()) { LoadSignalSet(i); m_new_signal.LabelText("Save"); m_new_signal.Update(true); m_set_window.OpenWindow(); m_number_signal=i; } }
ここでは、作成されたシグナルボタンが押されたかどうかを判定します。 これを知った上で、LoadSignalSet()メソッドを使用して、以前に保存したデータを設定ウィンドウのインターフェースにロードし、ボタン名をAddからSaveに変更して、設定ウィンドウを開きます。
取引シグナル検索アルゴリズム
取引シグナルを作成・編集するためのツールの準備ができたので、シグナルの検索・表示を担当するアプリケーション部分にリンクさせてみましょう。 すでにシグナル監視の基礎はできています。 これには、行(最初の設定ステップで選択されたシンボル)と列(2番目の設定ステップで選択された時間枠)の名前を持つテーブル形式の外観があります。
図.6 取引シグナルモニター作成ボタン
少なくとも1つの取引シグナルが作成された後のアクションのシーケンスは単純です。 Createをクリックすると、それまでに設定されていた設定の配列全体に基づいて、取引シグナルモニターが形成されます。 このシステムのプログラミングを進める前に、Createを押した後に呼び出されるToMonitor()メソッドを補足する必要があります。
//--- Hide Step 3 m_add_signal.Hide(); m_signal_header.Hide(); m_back_button.Hide(); m_next_button.Hide(); for(int i=0; i<5; i++) m_signal_editor[i].Hide();
現在作成されている取引シグナルの表示と編集を可能にするボタンオブジェクトを持っているので、これらのボタンは、前のステップ3からのすべてのコントロールと同様に、モニターウィンドウにジャンプするときにも非表示にする必要があります。
第1回目の記事では、アプリケーションの構造を開発した際に、モニター要素の一つとして、第1回目の記事の図5に示すような表示ブロックがありました。 その目的は、以前に作成された取引シグナルの一つの存在をリアルタイムで表示することです。 したがって、まず、指示ブロックとして使用されるオブジェクトを作成します。 これは、CProgramクラスのCreateSignalButton()メソッドを実装することで行うことができます。
bool CreateSignalButton(CButton &button,const int x_gap,const int y_gap);
また、表示ブロックのフルセットを作成するために必要なCButtonクラスインスタンスの配列を追加します。
CButton m_signal_button[];
今度はStepWindow.mqhを開いて、作成したメソッドの実装をファイルの最後に追加します。
//+------------------------------------------------------------------+ //| Creates an indication block | //+------------------------------------------------------------------+ bool CProgram::CreateSignalButton(CButton &button,const int x_gap,const int y_gap) { //--- color baseclr=C'220,225,235'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set up properties before creation button.TwoState(false); button.XSize(40); button.YSize(20); button.IconXGap(2); button.IconYGap(button.YSize()/2-8); button.LabelXGap(19); button.LabelYGap(2); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrBlack); button.LabelColorPressed(clrSlateGray); button.IconFile(""); button.IconFileLocked(""); button.IsDoubleBorder(true); //--- Create the control if(!button.CreateButton("",x_gap-button.XSize()/2,y_gap)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(0,button); return(true); }
今度はToMonitor()の作成方法で適用します。 これを行うには、以下のように、メソッド本体のTimeframesセクションを見つけて、メソッドにコードを追加します。
//--- 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+50*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()+25+j*25)) return; m_signal_button[k].Update(true); k++; } } //--- Resize window AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+5,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+5);
プロジェクトをコンパイルして、将来の取引シグナルの表示のための既製のレイアウトを取得します。
図.7 取引シグナルの用意されたレイアウト
さて、表示ブロックのどの要素が特定のシグナルを表示するように構成することができるかを心にとめておいてください。
- 背景色。
- 表示ブロックのボーダーの有無と色。
- テキストラベルの色と値。
- アイコンの存在。
- ツールチップの存在。
これらのプロパティを管理するために、CProgram基底クラスのプライベートセクションに以下のメソッドを設定します。
void SetBorderColor(int index, color clr); void SetLabel(int index, string text,color clr=clrBlack); void SetIcon(int index,int number); void SetBackground(int index,color clr); void SetTooltip(int index,string text="\n");
実装:
//+------------------------------------------------------------------+ //| Set the border color | //+------------------------------------------------------------------+ void CProgram::SetBorderColor(int index, color clr) { m_signal_button[index].BorderColor(clr); m_signal_button[index].BorderColorHover(clr); m_signal_button[index].BorderColorPressed(clr); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the label text | //+------------------------------------------------------------------+ void CProgram::SetLabel(int index, string text,color clr=clrBlack) { m_signal_button[index].LabelColor(clr); m_signal_button[index].LabelColorHover(clr); m_signal_button[index].LabelColorPressed(clr); m_signal_button[index].LabelText(text); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the background | //+------------------------------------------------------------------+ void CProgram::SetBackground(int index,color clr) { m_signal_button[index].BackColor(clr); m_signal_button[index].BackColorHover(clr); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the icon | //+------------------------------------------------------------------+ void CProgram::SetIcon(int index,int number) { //--- string image[]= { "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_up.bmp", "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_down.bmp" }; string path=(number>=0)?image[number]:""; if(number<0) m_signal_button[index].IsCenterText(true); else m_signal_button[index].IsCenterText(false); m_signal_button[index].IconFile(path); m_signal_button[index].IconFilePressed(path); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the tooltip | //+------------------------------------------------------------------+ void CProgram::SetTooltip(int index,string text="\n") { m_signal_button[index].Tooltip(text); m_signal_button[index].ShowTooltip(true); }
次に、計算、正しい表示、そして最も重要なことに、作成された指示ブロックのそれぞれを特定の行(選択されたシンボル)と列(時間枠)に対応させるために必要ないくつかの補助的なメソッドを作成する必要があります。 まず、テーブル内のインデックスに基づいて、指示ブロックの行と列を決定するための方法を作成します。
int GetRow(int index,int row_size); int GetCol(int index,int row_size); //+------------------------------------------------------------------+ //| Determining a row by the index | //+------------------------------------------------------------------+ int CProgram::GetRow(int index,int row_size) { return(int(MathFloor(index/row_size)+1)); } //+------------------------------------------------------------------+ //| Determining a column by the index | //+------------------------------------------------------------------+ int CProgram::GetCol(int index,int row_size) { return(int(MathMod(index,row_size)+1)); }
また、インターフェースから必要なデータを取得できるようにする必要があります。 つまり、タイムフレームのテキスト表示をタイムフレーム列挙型に変換する必要があります。 また、表示ブロック・インデックスに基づいて、それが表のどのシンボルと時間枠に対応しているかを見つけることができる必要があります。
//+------------------------------------------------------------------+ //| Return timeframe by row | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES CProgram::StringToTimeframe(const string timeframe) { if(timeframe=="M1") return(PERIOD_M1); if(timeframe=="M2") return(PERIOD_M2); if(timeframe=="M3") return(PERIOD_M3); if(timeframe=="M4") return(PERIOD_M4); if(timeframe=="M5") return(PERIOD_M5); if(timeframe=="M6") return(PERIOD_M6); if(timeframe=="M10") return(PERIOD_M10); if(timeframe=="M12") return(PERIOD_M12); if(timeframe=="M15") return(PERIOD_M15); if(timeframe=="M20") return(PERIOD_M20); if(timeframe=="M30") return(PERIOD_M30); if(timeframe=="H1") return(PERIOD_H1); if(timeframe=="H2") return(PERIOD_H2); if(timeframe=="H3") return(PERIOD_H3); if(timeframe=="H4") return(PERIOD_H4); if(timeframe=="H6") return(PERIOD_H6); if(timeframe=="H8") return(PERIOD_H8); if(timeframe=="H12") return(PERIOD_H12); if(timeframe=="D1") return(PERIOD_D1); if(timeframe=="W1") return(PERIOD_W1); if(timeframe=="MN") return(PERIOD_MN1); //--- The default value return(::Period()); } //+------------------------------------------------------------------+ //| Determine the timeframe | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES CProgram::GetTimeframe(int index) { int tf=ArraySize(m_timeframes); return(StringToTimeframe((m_timeframe_label[GetCol(index,tf)-1].LabelText()))); } //+------------------------------------------------------------------+ //| Determine the symbol | //+------------------------------------------------------------------+ string CProgram::GetSymbol(int index) { int tf=ArraySize(m_timeframes); return(m_symbol_label[GetRow(index,tf)-1].LabelText()); }
次の方法は、シグナル検索アルゴリズムに直接関連しています:これは、指定されたシンボルと時間枠上で以前に作成されたシグナルのパラメータのセットを検索します。
bool GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set);
設定は、パラメータの集合からSIGNAL構造体で渡されます。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set) { //--- Getting the indicator handle int h=INVALID_HANDLE; ENUM_APPLIED_PRICE app_price; switch(signal_set.app_price) { case 0: app_price=PRICE_CLOSE; break; case 1: app_price=PRICE_OPEN; break; case 2: app_price=PRICE_HIGH; break; case 3: app_price=PRICE_LOW; break; case 4: app_price=PRICE_MEDIAN; break; case 5: app_price=PRICE_TYPICAL; break; case 6: app_price=PRICE_WEIGHTED; break; default: app_price=PRICE_CLOSE; break; } //--- 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; default: break; } if(h==INVALID_HANDLE) { Print(sy+". Failed to get handle"); Print("Handle = ",h," error = ",GetLastError()); return(false); } //--- double arr[1]; if(CopyBuffer(h,0, 0,1,arr)!=1) { Print("sy= ",sy,"tf= ",EnumToString(tf)," Failed to get handle data ",GetLastError()); return(false); } IndicatorRelease(h); //--- Check the condition double r_value=signal_set.rule_value; double c_value=arr[0]; m_ind_value=c_value; int s=0; 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; } //--- if(s>0) return(true); return(false); }
GetSignal()メソッドは、SIGNAL構造体から、利用可能なインジケーターのうち、どのインジケーターが取引シグナルを生成するために選択されたか、どの設定がインジケーターに選択されたか、どの検索ルールが設定されたかの情報を受け取ります。 時間枠によるフィルタリングは、各シグナルに対して2回実行できることを忘れないでください。 最初は第2のセットアップステップで実行され、その後、下の図8に示すように、取引シグナル作成ウィンドウで選択された値をフィルタリングすることができます。
図.8 作成したシグナルのタイムフレームの選択
私たちのアルゴリズムがこのフィルターを考慮し、与えられた時間枠外のシグナルを探さないようにするためには、作成された取引シグナルのそれぞれについて時間枠の指定をチェックする必要があります。 そこで、基底クラスにCheckTimeframe()メソッドを作成します。 メソッドがフィルターとして機能します。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CheckTimeframe(ENUM_TIMEFRAMES tf,SIGNAL &signal_set) { for(int i=0; i<21; i++) { if(StringToTimeframe(CharArrayToString(signal_set.tf_name[i].tf))==tf) return(true); } return(false); }
さて、いよいよ売買シグナル検索の仕組みを作る時が来ました。 これを行うには、CProgramクラスのpublicセクションにメソッドを追加します。SearchSignal() のセクションにメソッドを追加します。
bool SearchSignals(void);
ステップバイステップの実装をより詳細に分析し、先に作成された補助メソッドの目的を見てみましょう。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::SearchSignals(void) { //--- Search for set signals SIGNAL signal_set[]; int cnt=0; for(int i=0; i<5; i++) { if(FileIsExist("Signal Monitor\\signal_"+string(i)+".bin")) cnt++; } //--- ArrayResize(signal_set,cnt); ZeroMemory(signal_set); //--- for(int i=0; i<cnt; i++) { int h=FileOpen("Signal Monitor\\signal_"+string(i)+".bin",FILE_READ|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Configuration not found","Signal Monitor"); return(false); } FileReadStruct(h,signal_set[i]); FileClose(h); for(int j=0; j<ArraySize(m_signal_button); j++) { //--- string sy=GetSymbol(j); ENUM_TIMEFRAMES tf=GetTimeframe(j); //--- if(!CheckTimeframe(tf,signal_set[i])) continue; //--- if(GetSignal(sy,tf,signal_set[i])) { //--- if(signal_set[i].label_type==1) SetLabel(j,CharArrayToString(signal_set[i].label_value),signal_set[i].label_color); else SetLabel(j,DoubleToString(m_ind_value,3),signal_set[i].label_color); //--- if(signal_set[i].back_color!=clrNONE) SetBackground(j,signal_set[i].back_color); //--- if(signal_set[i].border_color!=clrNONE) SetBorderColor(j,signal_set[i].border_color); else SetBorderColor(j,signal_set[i].back_color); //--- if(signal_set[i].tooltip) SetTooltip(j,CharArrayToString(signal_set[i].tooltip_text)); //--- if(signal_set[i].image) SetIcon(j,signal_set[i].img_index); else SetIcon(j,-1); } } } return(true); }
最初の操作ステップでは、検索方法は、作成され、設定された取引シグナルの合計数に関するデータを収集します。 その後、このメソッドは、シグナル設定に関する情報を接続するファイルをループし、このデータを構造体に読み込みます。 この機構は、各指示ブロックに対して、表形式 でブロックが対応する適切なシンボルと時間枠を決定します。 このデータに基づいて、選択した時間枠で取引シグナルを検索する必要があるかどうかをチェックします。 タイムフレームが一致した場合は、シグナルを検索します。 シグナルが検出された場合は、シグナルの構成に応じて表示ブロックを色付けします。 これで、作成したメソッドを適用することができます。 このメソッドは、ToMonitor()メソッド本体の最後に呼び出されなければなりません。
... //--- Resize window AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+5,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+5); //--- SearchSignals(); }
では、一定時間経過後に繰り返し検索を有効にしてみましょう。 SignalMonitor.mq5ファイルを開き、ファイルの先頭に列挙を作成します。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum UPDATE { MINUTE, // 1 Minute MINUTE_15, // 15 Minutes MINUTE_30, // 30 Minutes HOUR, // 1 Hour HOUR_4 // 4 Hours };
入力に簡単に設定を追加できるようになりました。
input UPDATE Update = HOUR; // Update interval
計算用の2つの9つの変数を作成します。
int cnts=0; datetime update;
エキスパートの初期化で以下の行を追加します。
//--- switch(Update) { case MINUTE: cnts=60; break; case MINUTE_15: cnts=60*15; break; case MINUTE_30: cnts=60*30; break; case HOUR: cnts=3600; break; case HOUR_4: cnts=3600*4; break; default: cnts=1; break; } update=TimeLocal()+cnts;
このように、更新間隔を決定し、次回の更新時刻を設定している。 OnTick()関数本体に、指定された時間間隔が経過した場合、再度取引シグナルを検索するタイムチェックを追加します。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(TimeLocal()>update) { program.SearchSignals(); update=TimeLocal()+cnts; } }
プロジェクトをコンパイルして、独自のシンボルのセットを作成します。 モニター動作を実演するためのシグナルを1つ追加することができます。
このシリーズの次の部分では、現在の機能をより柔軟な取引シグナルの設定に向けて拡張し、既存の機能を改善していきます。
結論
以下に添付されているアーカイブには、説明されているすべてのファイルがフォルダに適切に配置されています。 正しく操作するためには、端末のルートディレクトリのMQL5フォルダに保存する必要があります。 MQL5フォルダがあるターミナル・ルート・ディレクトリを開くには、MetaTrader 5ターミナルでCtrl+Shift+Dキーの組み合わせを押すか、以下の図9に示すようにコンテキスト・メニューを使用します。
図9 MetaTrader 5のターミナルルートでMQL5フォルダを開く
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/7600





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索