Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen

3 Juli 2020, 12:46
Alexander Fedosov
0
453

Inhalt

Einführung

In früheren Artikeln haben wir die Struktur der Anwendung für die Überwachung von Handelssignalen geschaffen. Wir haben auch die Anwendungsschnittstelle mit grundlegenden Interaktionsmöglichkeiten implementiert. Nun ist es an der Zeit, den visuellen Teil des Algorithmus zur Konfiguration und Suche von Symbolen zu programmieren. Wir werden das Projekt aus dem vorhergehenden Artikel als Grundlage verwenden und es Schritt für Schritt um neue Funktionen erweitern.

System zum Speichern von Symbolsätzen

Letztes Mal haben wir im ersten Schritt der Anwendungseinrichtung Werkzeuge zur Auswahl von Symbolen aus der Market Watch erstellt. Die Symbole konnten auf drei verschiedene Arten ausgewählt werden:

  • Manuell. Markieren Sie die gewünschten Symbole und klicken Sie auf Next.
  • Ein vordefinierter Gruppe (Set). Ein Klick auf All, Major oder Crosses wählte automatisch eine bestimmte vordefinierte Gruppe von Symbolen aus.
  • Gespeicherte Gruppe. Eine zuvor vorbereitete Gruppe von Symbolen, die mit den ersten beiden Methoden konfiguriert und unter einem bestimmten Namen in einer Datei gespeichert wurde.

Abb.1 Anwendungs-Setup Schritt 1 und Einstellungen des Saved Set.

Die ersten beiden Möglichkeiten sind recht einfach, und sie wurden bereits früher erstellt. Der dritte Weg muss neu erstellt werden. Nun wollen wir genauer definieren, was wir tun werden. Wir werden uns eingehender mit der Interaktion der Elemente im roten Rahmen in Abb.1 befassen, was Folgendes impliziert:

  • Die gewünschten Symbole sind mit Häkchen markiert, der Nutzer gibt den Namen in das Feld 'Template name' ein und klickt dann auf Speichern oder drückt die Schnelltaste S. Bei erfolgreichem Speichern wird eine entsprechende Meldung angezeigt.
  • Um auf eine zuvor konfigurierte und gespeicherte Vorlage zuzugreifen, muss der Vorlagenname in das Feld eingegeben und Laden oder die Tastenkombination L gedrückt werden.

Öffnen Sie das Projekt, suchen Sie die Basisklasse Programm und fügen Sie zwei Methoden in seinem 'private' Abschnitt hinzu. Die Methoden werden für das Laden und Speichern der Symbolvorlage verantwortlich sein.

   bool              SaveSymbolSet(string file_name);
   bool              LoadSymbolSet(string file_name);

Im Folgenden wird beschrieben, wie diese Methoden umgesetzt werden. 

//+------------------------------------------------------------------+
//| 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);
}

Wenn Sie jedoch versuchen, das Projekt jetzt zu kompilieren, löst dies einen Fehler in Verbindung mit der Variable m_save aus. Diese Struktur hat einen Parameter vom Typ bool mit dem Namen tf. Sie merkt sich die Wahl des Nutzers für eine Datei. Erstellen wir also diese Struktur in der Anwendungsklasse und fügen Sie ihre Instanz zur Basisklasse hinzu.

//+------------------------------------------------------------------+
//| Class for creating the application                               |
//+------------------------------------------------------------------+
struct SAVE
{
   bool     tf[100];
};
class CProgram : public CWndEvents
{
...
        SAVE            m_save;

Gehen Sie zu OnEvent(), geben Sie den Abschnitt ein, der sich auf das Button-Klick-Ereignis bezieht, und fügen Sie den folgenden Code in der Schritt 1-Bedingung hinzu:

         //--- 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());
         }

Implementieren Sie auch die Verwendung von Tastenkombinationen für die oben genannten Tasten. Fügen Sie in derselben Methode eine Prüfung für das Tastendruckereignis hinzu und fügen Sie den Code für die verwendeten Tasten hinzu.

//--- 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());
         }
      }
   }

Kompilieren wir das Projekt. Ein erfolgreiches Kompilieren wird zu folgendem Ergebnis führen.

Abb.2 Speichern und Laden einer Nutzervorlage

Hinzufügen und Bearbeiten eines Handelssignals

Gehen Sie nun zum Hauptanwendungsteil über, der für die Erstellung und Bearbeitung von Handelssignalen sowie für deren weitere Verfolgung im Monitor zuständig ist. So sieht die Erstellung und Bearbeitung der Signale aus. 

Abb.3 Fenster zur Erzeugung und Bearbeitung von Signalen.

In der aktuellen Phase erscheint das Fenster als ein Satz von GUI-Elementen, die verschiedene Parameter steuern. Diese Einstellungen werden jedoch nirgendwo verwendet. Beginnen wir mit dem Hinzufügen von zwei Schaltflächen zur Schnittstelle für das Hinzufügen/Speichern eines Handelssignals. Eine weitere Schaltfläche bricht die Erstellung/Bearbeitung ab. Öffnen Sie Program.mqh und fügen Sie der Basisklasse eine Implementierungsmethode für diese beiden Schaltflächen hinzu:

bool              CreateButton3(CButton &button,string text,const int x_gap,const int y_gap);

Zwei CButton Schaltflächen-Instanzen:

   CButton           m_new_signal;
   CButton           m_cancel_button;

Gehen Sie nun zu SetWindow.mqh und implementieren Sie diese Methode.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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);
}

Damit diese beiden Schaltflächen in der Oberfläche des Handelsfensters erscheinen und ein Signal hinzufügen, fügen Sie die folgenden Zeilen am Ende des Methodenrumpfes CreateSetWindow() ein:

//--- 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);

Nach dem Kompilieren erscheinen zwei Schaltflächen am unteren Rand des Fensters zur Erstellung von Handelssignalen. 

Abb.4 Schaltflächen zum Hinzufügen und Löschen von Signalen.

Jetzt müssen wir die Ereignisse hinzufügen, die bei Klicks auf die Schaltflächen eintreten werden. Die Wirkung der Schaltfläche Cancel (Abbrechen) ist offensichtlich: sie speichert keine Aktionen und Einstellungen im gegebenen Fenster und schließt das Fenster, ohne ein Signal hinzuzufügen. Betrachten wir die Schaltfläche Add (Hinzufügen) genauer.

Bestimmen wir zunächst die Reihenfolge der Aktionen, die ausgeführt werden, wenn auf Hinzufügen geklickt wird.

  1. Ein Klick auf die Schaltfläche speichert die Parameter, die unter Verwendung der UI-Elemente im Fenster zur Erstellung von Handelssignalen ausgewählt wurden.
  2. Nach erfolgreicher Speicherung wird das Fenster geschlossen, und der erste Datensatz mit dem Signalnamen erscheint in der Liste der Signale im Hauptfenster.
  3. Nach einem Klick auf den Datensatz wird der zuvor gespeicherte Satz auf die UI-Elemente der Signaleinrichtung angewendet, und die Schaltfläche Hinzufügen wird in Speichern umgewandelt.

Um das Speichern von Einstellungen in einer Datei zu ermöglichen, müssen wir einen universellen Satz von Einstellungen erstellen, der sowohl für die visuelle Anzeige im Editierfenster als auch für die nachfolgende Signalsuche verwendet wird. Lassen Sie uns daher eine Struktur erstellen und diese SIGNAL nennen. Die Konfiguration der Einstellungen im Erstellungs- und Editierfenster wird in diese Struktur geschrieben.

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];
};

Lassen Sie uns jedes der Elemente in der Struktur betrachten:

  • ind_type — enthält den Typ des Indikators, der als Basis für die Signalsuche gewählt wurde. Er wird in der Schnittstelle als Indikatortyp angezeigt.
  • ind_period — enthält die Periodenlänge des gewählten Indikators.
  • app_price — der für die Indikatorberechnung verwendete Preis. Dieser Wert ist nicht für alle Indikatoren verfügbar und wird daher nur geschrieben, wenn er anwendbar ist. Beispielsweise wird er für RSI verwendet, aber nicht für WPR.
  • rule_type — legt den Typ der Regel fest, die bei der Suche nach einem Handelssignal verwendet werden soll. Sie erscheint in der Schnittstelle als Dropdown-Menü, das Zeichen wie ==,>=,<= und andere enthält.
  • rule_value — ein Schwellenwert des ausgewählten Indikators, auf den die Suchregel angewendet werden soll.
  • label_type — dieses Element speichert den Anzeigetyp des Textlabels. Es handelt sich entweder um den aktuellen Indikatorwert oder ein nutzerdefiniertes Label mit bis zu 3 Zeichen Länge.
  • label_value — wenn der zweite Textlabel-Anzeigetyp gewählt wird, speichert dieser Parameter den vom Nutzer angegebenen nutzerdefinierten Labeltext.
  • label_color — speichert die Farbe des Textlabels.
  • back_color — speichert die Hintergrundfarbe des Signalblocks auf dem Monitor, wenn diese Option gewählt ist.
  • Randfarbe — speichert die Randfarbe des Signalblocks im Monitor, wenn diese Option ausgewählt ist.
  • tooltip — enthält einen Hinweis darauf, ob ein Tooltip verwendet wird.
  • tooltip_text — wenn der Tooltip verwendet wird, enthält dieser Parameter den Text.
  • image — enthält einen Hinweis auf die Verwendung eines Bildes.
  • img_index — speichert die Sequenznummer des Bildes, wenn es verwendet wird.
  • timeframes — Array mit Informationen über die im zweiten Schritt gewählten Einstellungen für den Arbeitszeitraum.
  • tf_name — speichert die Zeitrahmen, in denen nach dem Handelssignal gesucht wird. 

Nun deklarieren wir in der Basisklasse ein Array von Strukturen zum Speichern der Einstellungen der erzeugten Signale.

SIGNAL            m_signal_set[5];

Erstellen Sie außerdem zwei Methoden im 'private' Bereich der Klasse CProgram, um einen Satz von Parametern in einer Datei zu speichern und einen aus der Datei in die Struktur zu laden.

   bool              SaveSignalSet(int index);
   bool              LoadSignalSet(int index);

Hier ist die Implementierung:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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);
}

Damit ist die erste Aktion mit dem Algorithmus für das Speichern und Laden abgeschlossen. Jetzt müssen wir das Objekt erstellen, das als Aufzeichnung der erzeugten Signale dienen soll. Wenn wir auf diese Objekte klicken, können wir die Parameter des zuvor erstellten Handelssignals bearbeiten. Um diese Objekte zu implementieren, erstellen Sie ein Array mit Klasseninstanzen von CButton.

CButton           m_signal_editor[5];

Fügen Sie außerdem eine Methode zur Erzeugung der Objekte hinzu.

bool              CreateSignalEditor(CButton &button,string text,const int x_gap,const int y_gap);

Implementieren Sie diese Methode in der Datei StepWindow.mqh, da diese Objekte zum Hauptfenster gehören.

//+------------------------------------------------------------------+
//| 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);
}

Fügen Sie mit dieser Methode fünf Objekte zum Körper CreateStepWindow() hinzu, die die Objekte in der Liste der Signale sein werden.

//---
   for(int i=0; i<5; i++)
   {
      if(!CreateSignalEditor(m_signal_editor[i],"Signal_"+string(i),10,40*i+90))
         return(false);
   }

Um die Darstellung dieser Elemente nach dem Start der Anwendung zu deaktivieren, blenden Sie sie in der Methode CreateGUI() aus.

//+------------------------------------------------------------------+
//| 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);
}

Als Nächstes muss vor der Projektzusammenstellung eine Methode erstellt werden, die während der anfänglichen Einrichtung alle zuvor gespeicherten Daten löscht. Dazu erstellen Sie die Methode ClearSaves() und rufen sie im Klassenkonstruktor CProgramm auf.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::ClearSaves(void)
{
   for(int i=0; i<5; i++)
      FileDelete("Signal Monitor\\signal_"+string(i)+".bin");
   m_total_signals=0;
   return(true);
}

Fügen Sie nun Folgendes zum Ereignis des Klicks auf die Schaltfläche Add Signal hinzu:

//--- 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);
      }

Der Mechanismus zum Hinzufügen eines neuen Handelssignals ist nach dem Kompilieren des Projekts bereit. Der nächste Schritt ist die Implementierung der Bearbeitung eines früher erstellten Signals.

Abb.5 Hinzufügen eines neuen Handelssignals.

Lassen Sie uns alles zusammenfassen. Wie in Abbildung 5 dargestellt, haben wir die Möglichkeit implementiert, Signale durch einen Klick auf die Schaltfläche Add Signal hinzuzufügen. Außerdem wird eine Schaltfläche mit dem neuen Signalnamen zu der Schaltfläche Signal List hinzugefügt. Derzeit ist dies ein voreingestellter Wert ohne die Möglichkeit, ihn zu bearbeiten. Bei einem Klick auf Signal_0 passiert jedoch nichts, also lassen Sie uns dies korrigieren. Lassen Sie uns das erneute Öffnen des Einstellungsfensters mit dem Laden genau jener Einstellungen in die Schnittstelle ermöglichen, die zuvor für das gewählte Signal gespeichert wurden. Eine weitere Idee ist die Implementierung der Möglichkeit, die geladenen Einstellungen zu bearbeiten und zu speichern.

Öffnen Sie den Methodenrumpf OnEvent() in der Basisklasse CProgram und suchen Sie das Teil mit dem Schaltflächen-Klick-Ereignis. Fügen Sie den folgenden Code hinzu:

//---
      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;
         }
      }

Stellen Sie hier fest, welcher erzeugte Signalknopf gedrückt wurde. Dies wissend laden wir die zuvor gespeicherten Daten mit der Methode LoadSignalSet() in die Schnittstelle des Einstellungsfensters, ändern den Schaltflächennamen von Add zu Save und öffnen das Einstellungsfenster.


Nun, da die Werkzeuge zur Erstellung und Bearbeitung von Handelssignalen fertig sind, ist es an der Zeit, sie mit dem Anwendungsteil zu verbinden, der für die Suche und Anzeige von Signalen zuständig ist. Wir haben bereits eine Grundlage für die Signalüberwachung geschaffen. Sie verfügt über eine tabellarische Ansicht mit den Namen der Zeilen (im ersten Setup-Schritt ausgewählte Symbole) und Spalten (im zweiten Setup-Schritt ausgewählte Zeitrahmen).

Abb.6 Schaltfläche zum Erstellen eines Monitors für Handelssignale.

Die Abfolge der Aktionen, nachdem mindestens ein Handelssignal erstellt wurde, ist einfach. Ein Klick auf Create (Erstellen) löst die Bildung eines Handelssignalmonitors aus, der auf dem gesamten Bereich der zuvor festgelegten Einstellungen basiert. Bevor wir mit der Programmierung dieses Systems fortfahren, müssen wir die Methode ToMonitor() ergänzen, die nach Drücken von Create aufgerufen wird.

//--- 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();

Da wir über Schaltflächen-Objekte verfügen, die die Anzeige und Bearbeitung aktuell erzeugter Handelssignale ermöglichen, sollten diese Schaltflächen auch ausgeblendet werden, wenn zum Monitorfenster gesprungen wird, ähnlich wie alle Bedienelemente aus dem vorherigen Schritt 3.

Als wir die Anwendungsstruktur entwickelt haben, war eines der Monitorelemente im ersten Artikel der in Abbildung 5 des ersten Artikels gezeigte Indikationsblock. Ihr Zweck ist es, in Echtzeit das Vorhandensein eines der früher erzeugten Handelssignale anzuzeigen. Daher besteht der erste Schritt darin, ein Objekt zu erstellen, das als Indikationsblock verwendet wird. Dies kann durch die Implementierung der Methode CreateSignalButton() in der Klasse CProgram erfolgen. 

bool              CreateSignalButton(CButton &button,const int x_gap,const int y_gap);

Fügen wir außerdem ein Array mit Klasseninstanzen von CButton hinzu, die für die Erstellung des vollständigen Satzes von Indikationsblöcken erforderlich sind.

CButton           m_signal_button[];

Öffnen Sie nun StepWindow.mqh und fügen Sie am Ende der Datei die Implementierung der erstellten Methode hinzu:

//+------------------------------------------------------------------+
//| 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);
}

Wenden Sie es nun in der Erstellungsmethode ToMonitor() an. Suchen Sie dazu den Abschnitt Timeframes im Methodenrumpf und fügen Sie Code zur Methode hinzu, wie unten gezeigt:

//--- 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);

Kompilieren Sie das Projekt und Sie erhalten ein fertiges Layout für die zukünftige Darstellung von Handelssignalen.


Abb.7 Ein fertiges Layout von Handelssignalen.

Erinnern wir sich nun, welche Elemente des Indikationsblocks so konfiguriert werden können, dass sie bestimmte Signale anzeigen.

  • Hintergrundfarbe.
  • Das Vorhandensein und die Farbe der Umrandung des Indikationsblocks.
  • Die Farbe und der Wert des Textlabels.
  • Das Vorhandensein eines Icons.
  • Das Vorhandensein eines Tooltips.

Um diese Eigenschaften zu verwalten, stellen Sie die folgenden Methoden im privaten Bereich von unserer Basisklasse CProgram ein:

   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");

Deren Implementation:

//+------------------------------------------------------------------+
//| 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);
}

Dann müssen wir einige Hilfsmethoden erstellen, die für die weiteren Berechnungen, die korrekte Darstellung und vor allem für die Übereinstimmung jedes der erstellten Indikationsblöcke mit einer bestimmten Zeile (ausgewähltes Symbol) und Spalte (Zeitrahmen) notwendig sind. Zuerst sind Methoden zur Bestimmung der Zeile und der Spalte des Indikationsblocks auf der Grundlage des Index in der Tabelle zu erstellen.

   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));
}

Wir müssen auch in der Lage sein, die erforderlichen Daten von der Schnittstelle zu erhalten. Tatsächlich müssen wir die Textanzeige der Zeitrahmen in das Enumerationselement des Zeitrahmens umwandeln. Wir müssen auch in der Lage sein, anhand des Indikationsblockindex herauszufinden, welchem Symbol und Zeitrahmen er in der Tabelle entspricht.

//+------------------------------------------------------------------+
//| 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());
}

Die nächste Methode steht in direktem Zusammenhang mit dem Signalsuchalgorithmus: Sie sucht nach dem Parametersatz der früher erzeugten Signale auf dem angegebenen Symbol und Zeitrahmen.

bool              GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set);

Die Einstellungen werden durch die Struktur SIGNAL aus einer Menge von Parametern übergeben. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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);
}

Die Methode GetSignal() erhält von der Struktur SIGNAL die Information, welcher der verfügbaren Indikatoren für die Erzeugung von Handelssignalen ausgewählt wurde, welche Einstellungen für den Indikator gewählt wurden und welche Suchregel gesetzt wurde. Vergessen Sie nicht, dass das Filtern nach Zeitrahmen für jedes Signal zweimal durchgeführt werden kann. Das erste Mal wird es im zweiten Einrichtungsschritt durchgeführt, und dann können wir ausgewählte Werte im Fenster zur Erzeugung von Handelssignalen filtern, wie es in Abbildung 8 unten dargestellt ist.

Abb.8 Auswahl der Zeitrahmen für das erzeugte Signal.

Damit unser Algorithmus diesen Filter berücksichtigt und nicht nach Signalen außerhalb der vorgegebenen Zeitrahmen sucht, sollte die Spezifikation der Zeitrahmen für jedes der erzeugten Handelssignale überprüft werden. Erstellen wir also die Methode CheckTimeframe() in der Basisklasse. Die Methode wird als Filter dienen.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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);
}

Jetzt ist es an der Zeit, den Suchmechanismus für Handelssignale zu schaffen. Dazu fügen wir eine Methode zum Abschnitt public der Klasse CProgram hinzu: SearchSignal().

bool              SearchSignals(void);

Lassen Sie uns ihre schrittweise Implementierung genauer analysieren und den Zweck der früher geschaffenen Hilfsmethoden sehen.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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);
}

Im ersten Operationsschritt sammelt die Suchmethode Daten über die Gesamtzahl der erzeugten und konfigurierten Handelssignale. Dann durchläuft die Methode Dateien, die Informationen über Signaleinstellungen verbinden, und liest diese Daten in eine Struktur ein. Der Mechanismus bestimmt für jeden Indikationsblock das entsprechende Symbol und den Zeitrahmen, dem der Block in tabellarischer Form entspricht. Auf der Grundlage dieser Daten prüft, ob ein Handelssignal in dem gewählten Zeitrahmen gesucht werden muss. Wenn der Zeitrahmen übereinstimmt, suchen wir nach einem Signal. Wenn ein Signal gefunden wird, färben wir den Indikationsblock entsprechend der Signalkonfiguration ein. Nun kann die erstellte Methode angewendet werden. Die Methode sollte am Ende des Körpers der Methode ToMonitor() aufgerufen werden.

...
//--- 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();
}

Versuchen wir nun, nach einem bestimmten Zeitintervall eine wiederholte Suche zu ermöglichen. Öffnen Sie die Datei SignalMonitor.mq5 und erstellen Sie eine Enumeration am Anfang der Datei:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum UPDATE
{
   MINUTE,        // 1 Minute
   MINUTE_15,     // 15 Minutes
   MINUTE_30,     // 30 Minutes
   HOUR,          // 1 Hour
   HOUR_4         // 4 Hours
};

Eine neue Einstellung kann jetzt einfach zu den Eingaben hinzugefügt werden:

input UPDATE               Update            =  HOUR;                // Update interval

Erstellen wir zwei Variablen für Berechnungen.

int cnts=0;
datetime update;

Fügen Sie die folgenden Zeilen in der Initialisierung des Experten hinzu:

//---
   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;

Damit haben wir das Aktualisierungsintervall bestimmt und den nächsten Aktualisierungszeitpunkt festgelegt. Fügen wir im Funktionskörper von OnTick() eine Zeitprüfung hinzu: Wenn das angegebene Zeitintervall verstrichen ist, suchen wir erneut nach Handelssignalen.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(TimeLocal()>update)
   {
      program.SearchSignals();
      update=TimeLocal()+cnts;
   }
}

Stellen Sie das Projekt zusammen und erstellen Sie einen Satz Ihrer eigenen Symbole. Wir können ein Signal hinzufügen, um die Funktionsweise des Monitors zu demonstrieren.

 

Im nächsten Teil dieser Artikelserie werden wir mit der Erweiterung der derzeitigen Funktionalität in Richtung einer flexibleren Einrichtung von Handelssignalen fortfahren, und wir werden auch einige der bestehenden Funktionen verbessern.


Schlussfolgerung

Das unten angehängte Archiv enthält alle beschriebenen Dateien in Ordnern. Für einen korrekten Betrieb sollten Sie den Ordner MQL5 im Stammverzeichnis des Terminals speichern. Um das Stammverzeichnis des Terminals zu öffnen, in dem sich der Ordner MQL5 befindet, drücken Sie die Tastenkombination Strg+Umschalt+D im Terminal MetaTrader 5 oder verwenden Sie das Kontextmenü wie in Abb. 9 unten dargestellt.


Abb. 9. Öffnen des Ordners MQL5 im Stammordner von MetaTrader 5


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/7600

Beigefügte Dateien |
MQL5.zip (1706.81 KB)
Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen

Der Artikel befasst sich mit der Entwicklung der Zeitreihenkollektion spezifizierter Zeitrahmen für alle im Programm verwendeten Symbole. Wir werden die Zeitreihenkollektion, die Methoden zur Parametereinstellung der Zeitreihenkollektion und das anfängliche Ausfüllen der entwickelten Zeitreihen mit historischen Daten erstellen.

Anwendung von OLAP im Handel (Teil 4): Quantitative und visuelle Analyse der Testberichte Anwendung von OLAP im Handel (Teil 4): Quantitative und visuelle Analyse der Testberichte

Der Artikel bietet grundlegende Werkzeuge für die OLAP-Analyse von Testberichten in Bezug auf einzelne Durchläufe und Optimierungsergebnisse. Das Werkzeug kann mit Dateien im Standardformat (tst und opt) arbeiten und bietet auch eine grafische Schnittstelle. MQL-Quellcodes sind unten angefügt.

Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm

Der Artikel befasst sich mit der Echtzeit-Aktualisierung von Zeitreihendaten und dem Senden von Meldungen über das Ereignis "New bar" an die Kontrollprogramm auf dem Chart aus allen Zeitreihen aller Symbole, um diese Ereignisse in benutzerdefinierten Programmen handhaben zu können. Die Klasse "New tick" wird verwendet, um die Notwendigkeit der Aktualisierung der Zeitreihen von Symbolen und Perioden zu bestimmen, die nicht dem aktuellen Chart entsprechen.

MQL als Darstellungsmittel für graphische Schnittstellen von MQL-Programmen. Teil 1 MQL als Darstellungsmittel für graphische Schnittstellen von MQL-Programmen. Teil 1

Dieser Artikel schlägt ein neues Konzept zur Beschreibung der Fenster-Schnittstelle von MQL-Programmen vor, wobei die Strukturen von MQL verwendet werden. Spezielle Klassen transformieren das sichtbare MQL-Markup in die GUI-Elemente und erlauben es, diese zu verwalten, ihre Eigenschaften einzustellen und die Ereignisse in einer einheitlichen Weise zu verarbeiten. Es enthält auch einige Beispiele für die Verwendung des Markups für die Dialoge und Elemente einer Standardbibliothek.