Schnelle Werkzeuge für den manuellen Handel: Arbeiten mit offenen Positionen und Pending-Orders

28 Oktober 2020, 12:04
Alexander Fedosov
0
605

Inhalt

Einführung

Wir haben zuvor die Grundfunktionen geschaffen, die die Händler unterstützen soll, die den manuellen Handel bevorzugen. Wir konzentrierten uns hauptsächlich auf die bequeme Arbeit im Zusammenhang mit der Auftragserteilung, und daher bezogen sich die meisten Funktionen auf den Markteintritt. Jede Handelsstrategie, sei sie nun manuell oder automatisch, sollte jedoch drei Hauptstufen bei der Arbeit mit den Märkten haben. Dazu gehören die Regeln für den Markteintritt, die Verwaltung offener Positionen und die Bedingungen zum Schließen. Bis jetzt decken die Werkzeuge nur die erste Stufe ab. Daher können wir als Weiterentwicklung mehr Möglichkeiten für die Arbeit mit offenen Positionen oder Pending-Orders sowie die Bedingungen für das Schließen von Positionen erweitern. Alle Berechnungen sollten durch das Toolkit durchgeführt werden, während die Entscheidung vom Händler getroffen werden sollte. 


Formulierung des Problems

Beginnen wir mit der Bestimmung des Aufgabenspektrums, das zur Implementierung der neuen Funktionalität erforderlich ist. Bestimmen wir die Hauptphasen, nach denen wir die aktuelle Anwendung weiterentwickeln werden:

  • Überarbeitung der Struktur der Anwendung. Die Anwendung wurde ursprünglich als Hauptfenster mit einer Reihe von Aufgabenschaltflächen erstellt, von denen zwei modale Fenster mit Werkzeugen zur Festlegung von Marktpositionen und Pending-Orders öffneten. Die anderen drei Schaltflächen bieten einfache Funktionen zum Schließen von Positionen zum Marktpreis und zum Löschen aller Pending-Orders. Nun müssen wir eine Schnittstelle für die Verwaltung aktueller Positionen und für die Bearbeitung der Pending-Orders hinzufügen.
  • Erweiterung der Order-Betriebsmodi. Früher konnten wir alle Positionen im Gewinn oder im Verlust schließen. Jetzt benötigen wir flexiblere Funktionen, die nach Positionstypen unterteilt sind und die Möglichkeit bietet, verschiedene Bedingungen für die Schließung aller/ausgewählter Positionen festzulegen. Was die Pending-Orders betrifft, so konnten wir nur die zuvor durch das Toolkit erstellten Orders massenweise schließen. Dies ist nicht ausreichend. Wir sollten in der Lage sein, einen separaten Pending-Order zu löschen oder ihn zu ändern. Es wäre auch schön, wenn wir getrennte Listen von Marktaufträgen und Pending-Orders hätten.

Lassen Sie uns jede dieser Situationen einzeln näher betrachten. 


Überarbeitung der Anwendungsstruktur

Betrachten wir zunächst, was bereits umgesetzt wurde. Die folgende Abbildung 1 zeigt die Hauptblöcke, die Aufgaben in zwei Kategorien ausführen: Öffnen/Platzieren und Schließen/Löschen. Die dritte Kategorie beinhaltet die manuelle Verwaltung und Änderung.

Abb. 1 Die Hauptblöcke des Toolkits

Lassen Sie uns also drei Registerkarten erstellen. Das erste Register wird für die in Abbildung 1 gezeigten Funktionen verwendet. In den beiden anderen Registern werden wir Funktionen für die Arbeit mit Positionen und Pending-Order implementieren.

Abb.2 Neue Anwendungsstruktur

Alle Funktionen, die sich zuvor im Hauptfenster befanden, werden in der Registerkarte Trading implementiert. Die Kontrolle der offenen Positionen wird eine Tabelle der bestehenden Positionen enthalten, die über dieses Werkzeug eröffnet werden. Dort werden auch Befehle für die Arbeit mit diesen Positionen implementiert. "Pending Control" wird die über das Toolkit erstellten Orders sowie Kontrollen für das Schließen und Ändern von Pending-Orders enthalten. Betrachten wir die Registerkarten im Detail.

Abb.3 Registerkarte "Market Control"

Abbildung 3 zeigt die Registerkarte "Market Control". Dies Element enthält folgendes:

  • Registerkarte Market Orders. Es zeigt Informationen über alle aktuellen Positionen an, die von der Anwendung eröffnet wurden. Es ist ähnlich wie die Tabelle auf der Registerkarte Handel in MetaTrader 5. 
  • Drei Eingabefelder. Sie entsprechen und sind mit den Tabellenspalten verbunden: Volumen, Stop-Loss, Take-Profit.
  • Zwei Schaltflächen. Nach einem Klick auf eine Tabellenzeile werden editierbare Positionsparameter in den Eingabefeldern angezeigt. Durch Klicken auf Bearbeiten können wir Stop-Loss und Take-Profit für eine in der Liste ausgewählte Position ändern und sogar löschen. Mit der Schaltfläche "Close" wird eine Position zu einem aktuellen Kurs geschlossen. Das System überprüft zusätzlich das Eingabefeld Volume beim Schließen einer Position. Das bedeutet, dass Sie einen Wert für die Losgröße auswählen können, der niedriger als die Losgröße der aktuellen Position ist, um so die Position teilweise schließen zu können. 

Werfen wir nun einen Blick auf die Registerkarte "Pending Control" in Abbildung 4.

Fig. 4 "Pending Control".

Sie ist dem Vorhergehenden sehr ähnlich:

  • Eine Tabelle der Pending-Order. Sie enthält eine Liste der Pending-Order, die über dieses Toolkit platziert wurden.
  • Drei Eingabefelder. Wenn eine Pending-Order in der Tabelle ausgewählt ist, können Sie seinen aktuellen Ausführungspreis ändern sowie Stop-Loss und Take-Profit bearbeiten oder löschen.
  • Zwei Schaltflächen. Die Schaltfläche "Modify" greift im Gegensatz zur Tabelle der Marktpositionen auf alle drei Eingabefelder zu, um den ausgewählten Pending-Order zu bearbeiten. Die Schaltfläche Remove löscht den Auftrag.

Kehren wir zur Registerkarte Trading zurück und passen sie an die neuen Funktionen an. Lassen Sie uns zunächst die bestehenden Werkzeuge zum Schließen von Positionen im Gewinn oder Verlust überarbeiten. Es wird möglich sein, zusätzlich zu den Long-Positionen auch Short-Positionen zu schließen. Dies wird durch Umschalter für den Schließmodus umgesetzt.

Abb. 5 Erweiterung der Funktionen zum Schließen von Marktpositionen.

Wie Sie in Abbildung 5 sehen können, haben wir 4 neue Schaltflächen: "Close BUY profit" (Schließe KAUFPOSITION im Gewinn), "Close SELL profit" (Schließe VERKAUFSPOSITION im Gewinn), "Close BUY loss" (Schließe KAUFPOSITION im Verlust), "Close SELL loss" (Schließe VERKAUFSPOSITION im Verlust). Auf der rechten Seite der Schaltflächen befinden sich zusätzliche Umschalter; lassen Sie uns diese näher betrachten. Die Umschalter sind ähnlich, daher gelten die folgenden Beschreibungen für sie alle.

  • Alle. Standardwert. Es setzt keine Werte für Schaltflächen und schließt alle ausgewählten Elemente.
  • >Point. Schließt alle Positionen des gewählten Typs, in denen der Gewinn/Verlust höher als die angegebene Punktzahl ist.
  • >Currency. Das schließt alle Positionen des ausgewählten Typs, in denen der Gewinn/Verlust höher ist als der angegebene Betrag in der Kontowährung.
  • Sum>Points. Schließt alle Positionen des ausgewählten Typs, bei denen der Gesamtgewinn/-verlust höher ist als die angegebene Anzahl von Punkten.
  • Sum>Currency. Schließt alle Positionen des ausgewählten Typs, bei denen der Gesamtgewinn/-verlust höher ist als der angegebene Betrag in der Kontowährung.
Wichtiger Hinweis: Wenn einer der letzten beiden Punkte ausgewählt wird, wird der Gesamtbetrag nur für Positionen berechnet, die von dieser Anwendung eröffnet wurden, mit der angegebenen Magicnummer. Der Betrag wird für alle Positionen geprüft, die zwei Kriterien erfüllen: Positionstyp und die Magicnummer.

Betrachten wir ein Beispiel für die in Abbildung 5 gezeigten Einstellungen: Alle Positionen im Verlust schließen und der Option sum>currency. In diesem Fall findet das Toolkit alle Positionen, die es zuvor eröffnet hat, fasst ihren Gewinn zusammen, und wenn er mehr als 10 Einheiten in der Kontowährung beträgt, schließt es alle.


Implementierung der Toolkit-Ergänzungen

Als Grundlage werden wir das früher erstellte Projekt aus Artikel Schnelle Werkzeuge für den manuellen Handel: Grundlegende Funktionsweise. Zuerst müssen wir das Hauptfenster umstrukturieren, wie in Abb.2 dargestellt. Dazu fügen wir das Tab-Interface-Element hinzu, indem wir die Methode CreateTabs() in der Basisklasse CProgram erstellen und in MainWindow.mqh implementieren.

//+------------------------------------------------------------------+
//| Create a group with tabs                                         |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTabs(const int x_gap,const int y_gap)
{
//--- Store the pointer to the main control
   m_tab.MainPointer(m_main_window);
//--- Properties
   m_tab.Font(m_base_font);
   m_tab.FontSize(m_base_font_size);
   m_tab.LabelColor(clrWhite);
   m_tab.LabelColorHover(clrWhite);
   m_tab.IsCenterText(true);
   m_tab.AutoXResizeMode(true);
   m_tab.AutoYResizeMode(true);
   m_tab.AutoXResizeRightOffset(5);
   m_tab.AutoYResizeBottomOffset(5);
   m_tab.TabsYSize(27);
   m_tab.GetButtonsGroupPointer().Font(m_base_font);
   m_tab.GetButtonsGroupPointer().FontSize(m_base_font_size);
//--- Add tabs with the specified properties
   string tabs_names[3];
   tabs_names[0]=TRADING;
   tabs_names[1]=CAPTION_M_CONTROL_NAME;
   tabs_names[2]=CAPTION_P_CONTROL_NAME;
   for(int i=0; i<3; i++)
      m_tab.AddTab(tabs_names[i],180);
//--- Create a control element
   if(!m_tab.CreateTabs(x_gap,y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_tab);
   return(true);
}

Der erstellte Methodenrumpf hat drei neue Makro-Substitutionen, die als Tabulatorüberschriften dienen - sie müssen in zwei Sprachen in die Datei Defines.mqh eingefügt werden:

#define TRADING                        (m_language==RUSSIAN ? "Трейдинг" : "Trading")
#define CAPTION_M_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль рыночных позиций" : "Market Control")
#define CAPTION_P_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль отложенных ордеров" : "Pending Control")

Bevor wir die neu erstellte Methode anwenden, müssen wir die Methoden, die die Schaltflächen erstellen, neu erstellen und diese Methoden mit der ersten Registerkarte verknüpfen. Suchen wir die gängige Methode CreateButton() und bearbeiten sie wie folgt:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateButton(CButton &button,string text,color baseclr,int x_gap,int y_gap)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,button);
//--- Set properties before creation
   button.XSize(180);
   button.YSize(40);
   button.Font(m_base_font);
   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(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

Wenden Sie nun die Änderungen in der Erstellungsmethode des Hauptfensters an.

//+------------------------------------------------------------------+
//| Creates a form for orders                                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_main_window);
//--- Properties
   m_main_window.XSize(600);
   m_main_window.YSize(375);
//--- Coordinates
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
//--- Create the form
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//--- Tabs
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10))
      return(false);
   if(!CreateButton(m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60))
      return(false);
   if(!CreateButton(m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110))
      return(false);
   if(!CreateButton(m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60))
      return(false);
//---
   return(true);
}

Wie hier zu sehen ist, haben wir die als erstes hinzugefügten Registerkarten, dann die aktualisierten Aufrufe von Methoden, die die Schaltflächen erzeugen und die veränderte Größe des Hauptfensters. Nach der Projektkompilierung werden alle Schaltflächen auf die erste Registerkarte verschoben:

Abb. 6 Erstellen der Registerkarten und die verschobenen Schaltflächen auf der ersten Registerkarte

Lassen Sie uns gemäß Abbildung 5 zusätzliche Schaltflächen und Eingabefelder erstellen, um die erforderliche Funktionalität zu implementieren. Für große Schaltflächen werden wir die aktualisierte Methode CreateButton() verwenden. Um jedoch Eingabefelder und Umschalter zu erstellen, müssen wir zusätzliche Methoden einführen:CreateModeButton() — Modusumschalter, CreateModeEdit() — Eingabefelder. Ihre vollständige Implementierung ist wie folgt:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeButton(CButton &button,string text,int x_gap,int y_gap)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,button);
   color baseclr=clrDarkViolet;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(20);
   button.Font(m_base_font);
   button.FontSize(9);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeEdit(CTextEdit &text_edit,int x_gap,int y_gap)
{
//--- Store the window pointer
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(20);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.SetDigits(2);
   text_edit.MaxValue(99999);
   text_edit.StepValue(0.01);
   text_edit.MinValue(0.01);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0.01));
   text_edit.IsLocked(true);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

Unten sehen wir die aktualisierte Methode, die das Hauptfenster mit den obigen Methoden erstellt und Schaltflächen aus Abbildung 5 hinzufügt:

//+------------------------------------------------------------------+
//| Creates a form for orders                                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_main_window);
//--- Properties
   m_main_window.XSize(600);
   m_main_window.YSize(375);
//--- Coordinates
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
//--- Create the form
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//--- Tabs
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT,C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT,C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS,C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS,C'87,128,255',10,10+45*6))
      return(false);
//---
   for(int i=0; i<6; i++)
   {
      if(!CreateModeButton(m_mode_button[i],"all",205-10,10+45*(i+1)))
         return(false);
      if(!CreateModeEdit(m_mode_edit[i],204-10,30+45*(i+1)))
         return(false);
   }
//---
   return(true);
}

Neue Makrosubstitutionen werden hier verwendet, daher sollten die entsprechenden Werte zu Definiert.mqh hinzugefügt werden:

#define CLOSE_BUY_PROFIT               (m_language==RUSSIAN ? "Закрыть BUY прибыльные" : "Close BUY Profit")
#define CLOSE_SELL_PROFIT              (m_language==RUSSIAN ? "Закрыть SELL прибыльные" : "Close SELL Profit")
#define CLOSE_ALL_PROFIT               (m_language==RUSSIAN ? "Закрыть ВСЕ прибыльные" : "Close ALL Profit")
#define CLOSE_BUY_LOSS                 (m_language==RUSSIAN ? "Закрыть BUY убыточные" : "Close BUY Losing")
#define CLOSE_SELL_LOSS                (m_language==RUSSIAN ? "Закрыть SELL убыточные" : "Close SELL Losing")
#define CLOSE_ALL_LOSS                 (m_language==RUSSIAN ? "Закрыть ВСЕ убыточные" : "Close ALL Losing")

Kompilieren Sie das Projekt und schauen Sie sich das Zwischenergebnis an:

Abb. 7 Hinzufügen von Tasten und Modusschaltern

Aber dies ist nur eine visuelle Umsetzung. Der nächste Schritt ist die Zuweisung einer logischen Aufgabe für jedes der hinzugefügten Elemente. Richten wir den Schaltmechanismus ein, denn alle nachfolgenden Elemente werden sich auf ihre Werte und Zustände beziehen. Erstellen wir eine neue Methode ModeButtonSwitch() für die Schaltflächenobjekte. Sie schaltet den Modus um, wenn eine Taste gedrückt wird.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeButtonSwitch(CButton &button,long lparam,string &states[])
{
   if(lparam==button.Id())
   {
      int size=ArraySize(states);
      for(int i=0; i<size; i++)
      {
         if(button.LabelText()==states[i])
         {
            if(i==size-1)
            {
               SetButtonParam(button,states[0]);
               break;
            }
            else
            {
               SetButtonParam(button,states[i+1]);
               break;
            }
         }
      }
   }
}

Eine weitere neue Methode ModeEditSwitch() ermöglicht die Übereinstimmung der Eingabefeldeinstellungen mit dem gewählten Modus. Zum Beispiel sind Punkte ganze Zahlen, und wenn wir die Kontowährung verwenden, sollten Werte 2 Dezimalstellen haben.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeEditSwitch(long lparam,string &states[])
{
   for(int i=0; i<6; i++)
   {
      if(lparam==m_mode_button[i].Id())
      {
         if(m_mode_button[i].LabelText()==states[1])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=1;
         }
         else if(m_mode_button[i].LabelText()==states[2])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=2;
         }
         else if(m_mode_button[i].LabelText()==states[3])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=3;
         }
         else if(m_mode_button[i].LabelText()==states[4])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=4;
         }
         else
         {
            m_mode_edit[i].IsLocked(true);
            m_current_mode[i]=0;
         }
      }
   }
}

Die aktuelle Implementierung hat ein statisches Array m_current_mode — seine Größe entspricht der Anzahl der Modus-Umschalter, d.h. 6. Die Modi jedes Schließknopfes, die vom Nutzer ausgewählt wurden, werden in dieses Array geschrieben. Um die neu hinzugefügten Methoden zu aktivieren, öffnen Sie die Ereignisbehandlung durch OnEvent() und fügen Sie den folgenden Code in das Ereignis eines Knopfdrucks ein:

      //---
      string states[5]= {"all",">points",">currency","sum>points","sum>currency"};
      for(int i=0; i<6; i++)
         ModeButtonSwitch(m_mode_button[i],lparam,states);
      //---
      ModeEditSwitch(lparam,states);

Kompilieren wir das Projekt. Jetzt können Sie sehen, dass die Modusumschaltung die Eigenschaften von Eingabefeldern ändert. Dies ist in Abb. 8 dargestellt.


Abb. 8 Umschalten der Modi für das Schließen der Marktpositionen

Der nächste Schritt ist die Implementierung der Aktionslogik gemäß den Schaltflächenbeschreibungen, die auch mit den früher hinzugefügten Modi verknüpft werden sollten. Wir haben bereits zwei Aktionen: "Close all profitable" (Alle Profitablen schließen) und "Close all losing" (Alle Verlierenden schließen). Nun sollten sie in Übereinstimmung mit den neuen Schließmodi erweitert werden. Diese Aktionen werden mit den Methoden AlleMarktProfit schließen() und AlleMarktVerlust schließen() durchgeführt.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_C))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[0]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            int profit_pp;
            if(type==POSITION_TYPE_BUY)
               profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[0])
                  {
                  case  3:
                     sum_pp+=profit_pp;
                     break;
                  case  4:
                     sum_cur+=profit_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int profit_pp;
         //---
         if(type==POSITION_TYPE_BUY)
            profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[0]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                     (m_current_mode[0]==1 && profit_pp>int(m_mode_edit[0].GetValue())) ||            // Close all positions having profit more than N points
                     (m_current_mode[0]==2 && profit_cur+swap>double(m_mode_edit[0].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                     (m_current_mode[0]==3 && sum_pp>int(m_mode_edit[0].GetValue())) ||               // Close all positions with the total profit more than N points
                     (m_current_mode[0]==4 && sum_cur>double(m_mode_edit[0].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                 )
               {
                  //--- zeroing the request and result values
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- set the operation parameters
                  request.action   =TRADE_ACTION_DEAL;        // trading operation type
                  request.position =position_ticket;          // position ticket
                  request.symbol   =position_symbol;          // symbol
                  request.volume   =volume;                   // position volume
                  request.deviation=5;                        // allowable price deviation
                  request.magic    =m_magic_number;           // position MagicNumber
                  //--- Set order price and type depending on the position type
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- sending a request
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                  }
               }
            }
      }
   }
}

Dies ist eine Modifikation der Methode, die alle Marktpositionen schließt. Zuvor haben wir das statische Array m_current_mode eingeführt, das die Modusauswahl für jede der Aktionen auf der Schaltfläche kontrolliert. Daher wird die Berechnung für die Modi 4 und 5 durchgeführt, in denen alle Positionen durch den Gesamtgewinn in Punkten oder in Depotwährung geschlossen werden. Danach wählen wir Positionen aus, die zu unserem Toolkit gehören und, abhängig vom gewählten Schließmodus, wählen wir Bedingungen, woraufhin alle durch das Toolkit geschaffenen Marktpositionen geschlossen werden sollten.

Ändern wir in ähnlicher Weise die zweite Methode, mit der alle Positionen im Verlust geschlossen werden:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[6].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_D))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      ZeroMemory(request);
      ZeroMemory(result);
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate losses
      if(m_current_mode[3]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double loss_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            int loss_pp;
            //---
            if(type==POSITION_TYPE_BUY)
               loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[3])
                  {
                  case  3:
                     sum_pp+=loss_pp;
                     break;
                  case  4:
                     sum_cur+=loss_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double loss_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int loss_pp;
         if(type==POSITION_TYPE_BUY)
            loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[3]==0 && loss_cur+swap<0) ||
                     (m_current_mode[3]==1 && loss_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==2 && loss_cur+swap<-double(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==3 && sum_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==4 && sum_cur<-double(m_mode_edit[3].GetValue()))
                 )
               {
                  //--- zeroing the request and result values
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- set the operation parameters
                  request.action=TRADE_ACTION_DEAL;         // trading operation type
                  request.position=position_ticket;         // position ticket
                  request.symbol=position_symbol;           // symbol
                  request.volume=volume;                    // position volume
                  request.deviation=5;                      // allowable price deviation
                  request.magic=m_magic_number;             // position MagicNumber
                  //--- Set order price and type depending on the position type
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- sending a request
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                  }
               }
            }
      }
   }
}

Außerdem wird die Berechnung für Modi durchgeführt, bei denen der Gesamtverlust aller offenen Positionen in Punkten oder in Kontowährung überprüft wird und alle durch das Toolkit geschaffenen Marktpositionen geschlossen werden, wenn die entsprechenden Verlustbedingungen erfüllt sind.

Lassen Sie uns nun zu neuen Aktionen mit offenen Positionen übergehen: Schließen von Kauf-/Verkaufspositionen, entweder mit Gewinn oder Verlust. Tatsächlich handelt es sich hierbei um Sonderfälle der beiden oben beschriebenen Methoden. Daher werden wir eine Filterung nach Positionstyp hinzufügen. Erstellen wir zunächst Methoden, die die angegebenen Aktionen ausführen:

  • CloseBuyMarketProfit() — schließt alle profitablen Kaufpositionen.
  • CloseSellMarketProfit() — schließt alle profitablen Verkaufspositionen.
  • CloseBuyMarketLoss() — schließt alle verlierenden Kaufpositionen.  
  • CloseSellMarketLoss() — schließt alle verlierenden Verkaufspositionen.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_U))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[1]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[1]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                        (m_current_mode[1]==1 && profit_pp>int(m_mode_edit[1].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[1]==2 && profit_cur+swap>double(m_mode_edit[1].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[1]==3 && sum_pp>int(m_mode_edit[1].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[1]==4 && sum_cur>double(m_mode_edit[1].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[7].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_H))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[4]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[4]==0 && profit_cur+swap<0) ||                                   // Close all profitable positions
                        (m_current_mode[4]==1 && profit_pp<-int(m_mode_edit[4].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[4]==2 && profit_cur+swap<-double(m_mode_edit[4].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[4]==3 && sum_pp<-int(m_mode_edit[4].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[4]==4 && sum_cur<-double(m_mode_edit[4].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[5].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_J))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[2]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[2])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[2]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                        (m_current_mode[2]==1 && profit_pp>int(m_mode_edit[2].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[2]==2 && profit_cur+swap>double(m_mode_edit[2].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[2]==3 && sum_pp>int(m_mode_edit[2].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[2]==4 && sum_cur>double(m_mode_edit[2].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[8].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_L))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[5]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[5]==0 && profit_cur+swap<0) ||                                   // Close all profitable positions
                        (m_current_mode[5]==1 && profit_pp<-int(m_mode_edit[5].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[5]==2 && profit_cur+swap<-double(m_mode_edit[5].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[5]==3 && sum_pp<-int(m_mode_edit[5].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[5]==4 && sum_cur<-double(m_mode_edit[5].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}

Rufen Sie nach dem Erstellen und Implementieren die Methoden im Hauptteil der Ereignisbehandlung OnEvent() auf, ohne irgendwelche Abschnitte.

//---
   CloseBuyMarketProfit(id,lparam);
   CloseSellMarketProfit(id,lparam);
   CloseBuyMarketLoss(id,lparam);
   CloseSellMarketLoss(id,lparam);

Jede der Aktionen kann nicht nur durch einen Tastenklick, sondern auch durch Tastendruck-Ereignisse ausgeführt werden. Sie können Hotkeys im Code neu zuweisen. Der Einfachheit halber zeigen wir deren Werte neben den Aktionsnamen in Schaltflächen an.

//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT+"(U)",C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT+"(J)",C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS+"(H)",C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS+"(L)",C'87,128,255',10,10+45*6))
      return(false);

Daraus ergeben sich folgende Werte:

Abb. 9 Zuweisen von Hotkeys zu den neuen Aktionen

Wir haben alle Funktionen der Registerkarte Handel implementiert. Lassen Sie uns nun zum nächsten Schritt übergehen: Erstellung einer Tabelle mit den durch das Toolkit eröffneten Positionen und Hinzufügen der Möglichkeit, Positionen auf der Registerkarte "Market Control" zu verwalten. Abbildung 3 am Anfang des Artikels enthält ein visuelles Schema zur Erstellung von Oberflächenelementen, das aus drei Eingabefeldern, zwei Schaltflächen und der Tabelle besteht. Beginnen wir mit dem Erstel1len der Registerkarte. Zunächst werden wir drei Eingabefelder für die Bearbeitung des Lots, Stop-Loss und Take-Profit von offenen Positionen erstellen. Dies geschieht in den Methoden CreateLotControl(), CreateStopLossControl() und CreateTakeProfitControl().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateLotControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTakeProfitControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateStopLossControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.MinValue(0);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

Fügen wir die neuen Methoden dem Hauptteil von CreateMainWindow() hinzu.

//--- Input field for editing open positions
   if(!CreateLotControl(m_lot_edit[6],375,3,1))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[6],375+80,3,1))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[6],375+83*2,3,1))
      return(false);

Für die Schaltflächen "Edit" (Bearbeiten) und "Close" (Schließen) müssen wir zwei neue Methoden implementieren, die diese erstellen: CreateModifyButton() und CreateCloseButton().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModifyButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrDarkOrange;
   color pressclr=clrOrange;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(24);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateCloseButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrCrimson;
   color pressclr=clrFireBrick;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(24);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

Fügen wir sie der Methode zum Erstellen des Hauptfensters hinzu:

//--- Position editing/closing buttons
   if(!CreateModifyButton(m_small_button[0],MODIFY,622,3,1))
      return(false);
   if(!CreateCloseButton(m_small_button[1],CLOSE,622+80,3,1))
      return(false);

Hier haben wir zwei neue Makro-Substitutionen für die UI-Lokalisierung, öffnen Sie also Defines.mqh und fügen Sie die entsprechenden Werte hinzu:

#define MODIFY                         (m_language==RUSSIAN ? "Изменить" : "Modify")
#define CLOSE                          (m_language==RUSSIAN ? "Закрыть" : "Close")

Kommen wir nun zur Tabelle. Erstellen Sie zunächst die Methode CreatePositionsTable(), implementieren Sie sie und fügen Sie sie zur Methode des Hauptfensters hinzu.

//+------------------------------------------------------------------+
//| Create a table of positions                                      |
//+------------------------------------------------------------------+
bool CFastTrading::CreatePositionsTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS2_TOTAL 9
//--- Store the pointer to the main control
   table.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(1,table);
//--- Array of column widths
   int width[COLUMNS2_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[1]=110;
   width[2]=100;
   width[3]=60;
   width[6]=90;
//--- Array of text alignment in columns
   ENUM_ALIGN_MODE align[COLUMNS2_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
//--- Array of text offset along the X axis in the columns
   int text_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Array of column image offsets along the X axis
   int image_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Array of column image offsets along the Y axis
   int image_y_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Properties
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(782);
   table.CellYSize(24);
   table.TableSize(COLUMNS2_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_y_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.DataType(0,TYPE_LONG);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
//--- Create a control element
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Set the header titles
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,PRICE);
   table.SetHeaderText(4,VOLUME);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
   table.SetHeaderText(7,SWAP);
   table.SetHeaderText(8,PROFIT);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

Die Tabelle wird 9 Spalten haben. Lassen Sie uns die Spaltenbreite anpassen, da die Spaltennamen unterschiedlich lang sind. Setzen Sie die Spaltennamen für die beiden verfügbaren Sprachen mit Hilfe von Makrosubstitutionen in der Datei Definiert.mqh..

#define SYMBOL                         (m_language==RUSSIAN ? "Символ" : "Symbol")
#define VOLUME                         (m_language==RUSSIAN ? "Объем" : "Volume")
#define TYPE_POS                       (m_language==RUSSIAN ? "Тип позиции" : "Position Type")
#define SWAP                           (m_language==RUSSIAN ? "Своп" : "Swap")
#define PROFIT                         (m_language==RUSSIAN ? "Прибыль" : "Profit")

Wenn Sie jedoch jetzt versuchen, das Projekt zu kompilieren, werden Sie feststellen, dass die Tabelle, die Schaltflächen und Felder über den rechten Rand des Fensters hinausgehen. Daher müssen wir einen Mechanismus hinzufügen, der die Breite des Hauptfensters in Übereinstimmung mit seinem Inhalt anpasst. Dies kann durch die Methode WindowRezise() erfolgen.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::WindowResize(int x_size,int y_size)
{
   m_main_window.GetCloseButtonPointer().Hide();
   m_main_window.ChangeWindowWidth(x_size);
   m_main_window.ChangeWindowHeight(y_size);
   m_main_window.GetCloseButtonPointer().Show();
}

In der Ereignisbehandlung erstellen Sie ein neues Ereignis für jeden Klick auf die Schaltfläche meiner Registerkarte. Fügen Sie in diesem Ereignis die Breite des Hauptfensters für jede Registerkarte hinzu.

//--- Tab switching event
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_TAB)
   {
      if(m_tab.SelectedTab()==0)
         WindowResize(600,m_main_window.YSize());
      if(m_tab.SelectedTab()==1)
         WindowResize(782+10,m_main_window.YSize());
      if(m_tab.SelectedTab()==2)
         WindowResize(682+10,m_main_window.YSize());
   }

Nun werden die Informationen korrekt angezeigt, wie in Abbildung 3 dargestellt. Der nächste Schritt besteht darin, Daten zu den von der Anwendung eröffneten Positionen zu erhalten und diese Informationen in der erstellten Tabelle anzuzeigen. Wir haben drei Schritte zur Vorbereitung und Anzeige der Daten in der Tabelle:

  • Initialisierung. Hier bestimmen wir die Anzahl der Positionen, die zu unserem Toolkit gehören, und bauen die Tabelle für diese Daten neu auf. 
  • Hinzufügen von Daten. Wir fügen die Parameter jeder offenen Position zur Tabelle hinzu, entsprechend der Beschreibung in den Spaltenüberschriften.
  • Aktualisieren wir die Tabelle mit den ausgefüllten Daten.

Erstellen wir für den ersten Initialisierungsschritt die Funktion InitializePositionsTable() und treffen eine Auswahl aller offenen Positionen nach dem aktuellen Symbol und der Magicnummer. So erhalten wir die Anzahl der Positionen, die unsere Bedingungen erfüllen. Fügen Sie die gleiche Anzahl von Zeilen in die Tabelle ein.

//+------------------------------------------------------------------+
//| Initializing the table of positions                              |
//+------------------------------------------------------------------+
void CFastTrading::InitializePositionsTable(void)
{
//--- Get symbols of open positions
   int total=PositionsTotal(); // the number of open positions
   int cnt=0;
//--- Delete all rows
   m_table_positions.DeleteAllRows();
//--- Set the number of rows equal to the number of positions
   for(int i=0; i<total; i++)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.AddRow(cnt);
            cnt++;
         }
   }
//--- If there are positions
   if(cnt>0)
   {
      //--- Set the values in the table
      SetValuesToPositionsTable();
      //--- Update the table
      UpdatePositionsTable();
   }
}

Prüfen wir jetzt, ob es mindestens eine Position gibt, die durch das Toolkit eröffnet wurde. Wenn es solche Positionen gibt, setzen wir die Informationen über offene Positionen in den neu erstellten Zeilen mit der Methode SetValuePositionTable().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToPositionsTable(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
      double stoploss=PositionGetDouble(POSITION_SL);
      double takeprofit=PositionGetDouble(POSITION_TP);
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      double openprice=PositionGetDouble(POSITION_PRICE_OPEN);
      string pos=(type==POSITION_TYPE_BUY)?"BUY":"SELL";
      profit+=swap;
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(0,i,string(position_ticket));
            m_table_positions.SetValue(1,i,string(position_symbol));
            m_table_positions.SetValue(2,i,pos);
            m_table_positions.SetValue(3,i,string(openprice));
            m_table_positions.SetValue(4,i,string(volume));
            m_table_positions.SetValue(5,i,string(stoploss));
            m_table_positions.SetValue(6,i,string(takeprofit));
            m_table_positions.SetValue(7,i,string(swap));
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(2,i,(pos=="BUY")? clrForestGreen : clrCrimson);
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
}

Nachdem wir die Daten eingetragen haben, aktualisieren wir die Tabelle mit UpdatePositionsTable():

//+------------------------------------------------------------------+
//| Update the table of positions                                    |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionsTable(void)
{
//--- Update the table
   m_table_positions.Update(true);
   m_table_positions.GetScrollVPointer().Update(true);
}

Um die Änderungen in unserem Produkt zu aktivieren, sollten wir sie richtig konfigurieren. Wir öffnen die Datei SimpleTrading.mq5, suchen die Funktion OnInit() und fügen den Aufruf der Methode zur Initialisierung der Anwendungsklasse hinzu:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   tick_counter=GetTickCount();
//--- Initialize class variables
   program.FontName("Trebuchet MS");
   program.FontSize(Inp_BaseFont);
   program.BackgroundColor(Background);
   program.CaptionColor(Caption);
   program.SetLanguage(Language);
   program.SetMagicNumber(MagicNumber);
//--- Set up the trading panel
   if(!program.CreateGUI())
   {
      Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
   }
   program.OnInitEvent();
//---
   return(INIT_SUCCEEDED);
}

Dies muss unbedingt nach dem Erstellen der GUI der Anwendungs durch CreateGUI() erfolgen. Gehen wir nun zum Hauptteil von OnInitEvent() und rufen darin die Tabelleninitialisierung auf. 

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializePositionsTable();
}

Jetzt zeigt die Tabelle Informationen über offene Positionen korrekt an. Diese Daten sind jedoch nur zum Zeitpunkt des Anwendungsstarts relevant und sollten daher ständig aktualisiert werden. Dies sollte in der Ereignisbehandlung durch OnTrade() und in der Funktion OnTick() erfolgen. Bei Handelsereignissen werden wir die Anzahl der aktuell offenen Positionen und deren Parameter verfolgen. Informationen über den aktuellen Gewinn jeder Order werden in OnTick aktualisiert.

Erstellen wir die Methode OnTradeEvent() im 'public' Bereich der Basisklasse und rufen die Tabelleninitialisierung in ihrem Hauptteil auf. 

//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- If a new trade
      InitializePositionsTable();
}

Die neue Methode wird in der Behandlung der Handelsereignisse aufgerufen:

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade(void)
{
   program.OnTradeEvent();
}

Die oben genannten Aktionen legen die Relevanz der angezeigten offenen Positionen fest. Um den Gewinn zu aktualisieren, erstellen wir die Methode UpdatePositionProfit() im 'public' Bereich der Klasse CFastTrading:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionProfit(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      profit+=swap;
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
//---
   if(cnt>0)
      UpdatePositionsTable();
}

Rufen wir sie in OnTick() auf:

void OnTick()
{
   program.UpdatePositionProfit();
}

Damit ist die Umsetzung der Tabelle abgeschlossen. Nun wollen wir die Möglichkeit schaffen, offene Positionen, die in der aktuellen Liste verfügbar sind, zu bearbeiten und zu schließen. Wir sollten dafür sorgen, dass bei einem Klick auf eine Tabellenzeile die Eingabefelder für die Losgröße, Take-Profit und Stop-Loss einer ausgewählten Position angezeigt werden.

//--- Event of clicking on a table row
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Check the element ID
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
   }

Kompilieren wir das Projekt und erhalten ein Ergebnis, wie es Abbildung 10 dargestellt: Werte aus einer ausgewählten Marktposition, die in Eingabefeldern angezeigt werden.

Abb.10 Auswahl einer offenen Position zur weiteren Bearbeitung.

Erstellen wir nun die zwei Methoden ModifyPosition() und ClosePosition(). Nach einem Klick auf die Schaltflächen "Modify" und "Close" wendet die Methode die entsprechenden Aktionen auf die ausgewählte offene Position an. 

//+------------------------------------------------------------------+
//| Modifying a selected open position                               |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyPosition(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[0].Id())
   {
//--- Get index and symbol
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- calculation and rounding of the Stop Loss and Take Profit values
         double sl=NormalizeDouble((double)m_sl_edit[6].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[6].GetValue(),_Digits);
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action  =TRADE_ACTION_SLTP; // trading operation type
         request.position=ticket;            // position ticket
         request.symbol=Symbol();            // symbol
         request.sl      =sl;                // position Stop Loss
         request.tp      =tp;                // position Take Profit
         request.magic=m_magic_number;       // position MagicNumber
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ClosePosition(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[1].Id())
   {
      //--- Get index and symbol
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- Set order price and type depending on the position type
         if(type==POSITION_TYPE_BUY)
         {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
            request.type =ORDER_TYPE_SELL;
         }
         else if(type==POSITION_TYPE_SELL)
         {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
            request.type =ORDER_TYPE_BUY;
         }
         //--- check volume
         double position_volume=PositionGetDouble(POSITION_VOLUME);
         double closing_volume=(double)m_lot_edit[6].GetValue();
         if(closing_volume>position_volume)
            closing_volume=position_volume;
         //--- setting request
         request.action   =TRADE_ACTION_DEAL;
         request.position =ticket;
         request.symbol   =Symbol();
         request.volume   =NormalizeLot(Symbol(),closing_volume);
         request.magic    =m_magic_number;
         request.deviation=5;
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Damit ist die Implementierung der Aufgaben auf der Registerkarte Steuerung der Marktpositionen abgeschlossen. Kommen wir nun zur Entwicklung der Registerkarte "Pending Control". Wir fügen am Ende des Hauptteils der Methode CreateMainWindow() den Code hinzu, der die gleichen Funktionen hinzufügt, die wir in der vorherigen Registerkarte implementiert haben: eine Tabelle mit den Pending-Orders, den Schaltflächen und Eingabefelder zum Bearbeiten der Orders.

//--- Create a table of pending orders
   if(!CreateOrdersTable(m_table_orders,0,22+5))
      return(false);
//--- Input fields for editing pending orders
   if(!CreateLotControl(m_pr_edit[4],360,3,2))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[7],360+80,3,2))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[7],360+80*2,3,2))
      return(false);
//--- Pending order modifying/deleting orders
   if(!CreateModifyButton(m_small_button[2],MODIFY,361+80*3,3,2))
      return(false);
   if(!CreateCloseButton(m_small_button[3],REMOVE,361+80*3,3+24,2))
      return(false);

Dies Hinzugefügte umfasst eine neue Methode, die eine Tabelle für Pending-Orders und eine neue Makro-Ersetzung für die Schaltfläche zum Löschen einer Order. Sehen wir uns genauer an, wie eine Tabelle erstellt wird:

//+------------------------------------------------------------------+
//| Creates a table of pending orders                                |
//+------------------------------------------------------------------+
//---
bool CFastTrading::CreateOrdersTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS1_TOTAL 7
//--- Store the pointer to the main control
   table.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(2,table);
//--- Array of column widths
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[2]=100;
//--- Array of text offset along the X axis in the columns
   int text_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Array of column image offsets along the X axis
   int image_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Array of column image offsets along the Y axis
   int image_y_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Array of text alignment in columns
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[6]=ALIGN_LEFT;
//--- Properties
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(602);
   table.CellYSize(24);
   table.TableSize(COLUMNS1_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_x_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
   table.DataType(0,TYPE_LONG);
   table.DataType(1,TYPE_STRING);
   table.DataType(2,TYPE_STRING);
   table.DataType(3,TYPE_DOUBLE);
   table.DataType(4,TYPE_DOUBLE);
   table.DataType(5,TYPE_DOUBLE);
//--- Create a control element
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Set the header titles
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,VOLUME);
   table.SetHeaderText(4,PRICE);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

Diese Tabelle hat eine unterschiedliche Anzahl von Spalten, die in einer anderen Reihenfolge angeordnet sind. Wenn wir offene Marktpositionen bearbeiten, können wir die Losgröße sowie die Werte von Take-Profit und Stop-Loss ändern. Wie bei den Pending-Orders ändern wir hier den Eröffnungspreis anstelle der Losgröße. Kompilieren wir das Projekt und überprüfen das Ergebnis auf der Registerkarte Pending Control:

Abb.11 Schnittstelle für die Arbeit mit Pending-Orders.

Die weitere Entwicklung ist ähnlich der, die wir mit der vorherigen Registerkarte vorgenommen haben. Suchen wir zunächst alle zum Toolkit gehörenden Orders mit InitializeOrdersTable():

//+------------------------------------------------------------------+
//| Initializing the table of positions                              |
//+------------------------------------------------------------------+
void CFastTrading::InitializeOrdersTable(void)
{
//---
   int total=OrdersTotal();
   int cnt=0;
//--- Delete all rows
   m_table_orders.DeleteAllRows();
//--- Set the number of rows equal to the number of positions
   for(int i=0; i<total; i++)
   {
      //--- order parameters
      ulong  order_ticket=OrderGetTicket(i);                                   // order ticket
      string order_symbol=OrderGetString(ORDER_SYMBOL);                        // symbol
      ulong  magic=OrderGetInteger(ORDER_MAGIC);                               // position MagicNumber
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(order_symbol==Symbol())
         {
            m_table_orders.AddRow(cnt);
            cnt++;
         }
   }
//--- If there are positions
   if(cnt>0)
   {
      //--- Set the values in the table
      SetValuesToOrderTable();
      //--- Update the table
      UpdateOrdersTable();
   }
}

Wenn Pending-Orders gefunden werden, tragen wir sie relevanten Informationen in die Tabelle mit der Methode SetValuesToOrderTable() ein:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToOrderTable(void)
{
//---
   int cnt=0;
   ulong    ticket;
   for(int i=0; i<OrdersTotal(); i++)
   {
      //--- order parameters
      if((ticket=OrderGetTicket(i))>0)
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         double volume=OrderGetDouble(ORDER_VOLUME_INITIAL);                           // order volume
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         double price=OrderGetDouble(ORDER_PRICE_OPEN);
         double stoploss=OrderGetDouble(ORDER_SL);
         double takeprofit=OrderGetDouble(ORDER_TP);
         string pos="";
         if(type==ORDER_TYPE_BUY_LIMIT)
            pos="Buy Limit";
         else if(type==ORDER_TYPE_SELL_LIMIT)
            pos="Sell Limit";
         else if(type==ORDER_TYPE_BUY_STOP)
            pos="Buy Stop";
         else if(type==ORDER_TYPE_SELL_STOP)
            pos="Sell Stop";
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               m_table_orders.SetValue(0,i,string(ticket));
               m_table_orders.SetValue(1,i,string(position_symbol));
               m_table_orders.SetValue(2,i,pos);
               m_table_orders.SetValue(3,i,string(volume));
               m_table_orders.SetValue(4,i,string(price));
               m_table_orders.SetValue(5,i,string(stoploss));
               m_table_orders.SetValue(6,i,string(takeprofit));
               cnt++;
            }
      }
   }
}

Update der hinzugefügten Daten mit der Methode UpdateOrdersTable():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdateOrdersTable(void)
{
//--- Update the table
   m_table_orders.Update(true);
   m_table_orders.GetScrollVPointer().Update(true);
}

Um diese Funktionen mit der Anwendung zu verbinden, machen wir dasselbe, was wir für die vorherige Registerkarte gemacht haben. Nämlich, wir initialisieren die Tabelle mit Pending-Orders:

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializeOrdersTable();
   InitializePositionsTable();
}

Wiederholen wir diese Aktion für die Ereignisbehandlung:

//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- 
   InitializePositionsTable();
   InitializeOrdersTable();
}

Um die Anzeige relevanter Daten in Eingabefeldern beim Anklicken einer Tabellenzeile zu aktivieren, fügen wir den folgenden Code in den entsprechenden Abschnitt der Ereignisbehandlung ein:

//--- Event of clicking on a table row
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Check the element ID
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
      //--- Check the element ID
      if(lparam==m_table_orders.Id())
      {
         //---
         int row=m_table_orders.SelectedItem();
         m_pr_edit[4].SetValue(string(m_table_orders.GetValue(4,row)));
         m_sl_edit[7].SetValue(string(m_table_orders.GetValue(5,row)));
         m_tp_edit[7].SetValue(string(m_table_orders.GetValue(6,row)));
         m_pr_edit[4].GetTextBoxPointer().Update(true);
         m_sl_edit[7].GetTextBoxPointer().Update(true);
         m_tp_edit[7].GetTextBoxPointer().Update(true);
      }
   }

Weisen wir nun den Schaltflächen "Modify" (Ändern) und "Delete" (Löschen) die entsprechenden Aktionen zu. Wir erstellen die Methoden ModifyOrder() und RemoveOrder()

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[2].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         //--- calculation and rounding of the Stop Loss and Take Profit values
         double sl=NormalizeDouble((double)m_sl_edit[7].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[7].GetValue(),_Digits);
         double price=NormalizeDouble((double)m_pr_edit[4].GetValue(),_Digits);
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_MODIFY; // trading operation type
         request.order = ticket;             // order ticket
         request.symbol=Symbol();            // symbol
         request.sl      =sl;                // position Stop Loss
         request.tp      =tp;                // position Take Profit
         request.price=price;                // new price
         request.magic=m_magic_number;       // position MagicNumber
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::RemoveOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[3].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_REMOVE;             // trading operation type
         request.order = ticket;                         // order ticket
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Wir rufen sie in der Ereignisbehandlung außerhalb von allen Abschnitten auf:

      //---
      if(ModifyOrder(lparam))
         return;
      if(RemoveOrder(lparam))
         return;

Ich möchte eine weitere praktische Funktion für die Arbeit mit ausstehenden Aufträgen hinzufügen. Sie bietet die Möglichkeit, durch Klicken auf den Chart einen Eröffnungspreis des zuvor ausgewählten Endauftrags festzulegen. Dies ist in Abbildung 12 dargestellt:

Abb.12 Festlegen eines Eröffnungspreises für eine Pending-Order.

Das funktioniert wie folgt: 

  • Ein Klick auf das Eingabefeld aktiviert die Bearbeitung des Wertes.
  • Dann bewegen wir den Mauszeiger auf eine beliebige Stelle im Chart — die entsprechenden Werte werden entlang der Achse angezeigt. Der Einfachheit halber können wir auf das Mausrad klicken und den gewünschten Preis mit Hilfe eines Fadenkreuzes auswählen.
  • Halten Sie den Mauszeiger bei dem gewünschten Preis an und klicken Sie auf den Chart. Dieser Preiswert wird im Eingabefeld festgelegt.

Mit dieser Methode können Sie schnell und bequem den Preis für eine Pending-Order festlegen, vor allem dann, wenn dies anhand einer visuellen Analyse einfacher zu bewerkstelligen ist. Wenn Sie den Preis präziser einstellen müssen, geben Sie ihn über die Tastatur in das Eingabefeld ein. Die Implementierung ist sehr einfach. Erstellen wir in der Funktion der Basisklasse einen Abschnitt mit dem Ereignis eines Klicks auf den Chart und fügen den folgenden Code hinzu:

//--- The event of clicking on the chart
   if(id==CHARTEVENT_CLICK)
   {
      for(int i=0; i<4; i++)
      {
         if(m_pr_edit[i].GetTextBoxPointer().TextEditState())
         {
            m_last_index=i;
            break;
         }
         else
         {
            if(m_last_index>=0)
            {
               //---
               datetime dt    =0;
               int      window=0;
               //--- convert X and Y coordinates to date/time
               if(ChartXYToTimePrice(0,(int)lparam,(int)dparam,window,dt,m_xy_price))
               {
                  m_pr_edit[m_last_index].SetValue(DoubleToString(m_xy_price));
                  m_pr_edit[m_last_index].GetTextBoxPointer().Update(true);
                  m_last_index=-1;
               }
            }
         }
      }
   }

Bestimmen Sie hier, welches der Felder bearbeitet wird, merken Sie es sich, erhalten Sie den Wert durch Anklicken des Diagramms und fügen Sie diesen Wert in das Eingabefeld ein. Die Hauptmerkmale und Neuerungen werden im Video unten gezeigt.




Schlussfolgerung

Das angehängte Archiv enthält alle besprochenen Dateien, die sich in den entsprechenden Ordnern befinden. Für ihren ordnungsgemäßen Betrieb brauchen Sie nur den MQL5-Ordner im Terminalordner zu speichern. Um das Stammverzeichnis des Terminals, in dem sich der MQL5-Ordner befindet, zu öffnen, drücken Sie die Tastenkombination Strg+Umschalt+D im MetaTrader 5 Terminal oder verwenden Sie das Kontextmenü, wie in Abb. 13 unten dargestellt.


Abb.13 Öffnen des MQL5-Ordners im Terminal-Stamm von MetaTrader 5

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

Beigefügte Dateien |
MQL5.zip (5952.86 KB)
Zeitreihen in der Bibliothek DoEasy (Teil 46): Mehrperioden-Multisymbol-Indikatorpuffer Zeitreihen in der Bibliothek DoEasy (Teil 46): Mehrperioden-Multisymbol-Indikatorpuffer

In diesem Artikel werde ich die Klassen der Objekte der Indikatorpuffer verbessern, um im Multisymbolmodus arbeiten zu können. Dies wird den Weg für die Erstellung von Multisymbol- und Mehrperioden-Indikatoren in benutzerdefinierten Programmen ebnen. Ich werde den berechneten Pufferobjekten die fehlende Funktionalität hinzufügen, die es uns ermöglicht, multisymbol- und mehrperiodische Standardindikatoren zu erstellen.

Berechnung mathematischer Ausdrücke (Teil 2). Parser nach Pratt und dem Shunting-yard-Algorithmus Berechnung mathematischer Ausdrücke (Teil 2). Parser nach Pratt und dem Shunting-yard-Algorithmus

In diesem Artikel betrachten wir die Prinzipien der Analyse und Auswertung mathematischer Ausdrücke unter Verwendung von Parsern, die auf der Operator-Priorität basieren. Wir werden Parser nach Pratt und dem Shunting-yard-Algorithmus, Bytecode-Generierung und Auswertungen mit diesem Code implementieren und uns ansehen, wie Indikatoren als Funktionen in Ausdrücken verwendet und wie Handelssignale in Expert Advisors auf der Grundlage dieser Indikatoren eingerichtet werden können.

Wahrscheinlichkeitstheorie und mathematische Statistik mit Beispielen (Teil I): Grundlagen und elementare Theorie Wahrscheinlichkeitstheorie und mathematische Statistik mit Beispielen (Teil I): Grundlagen und elementare Theorie

Beim Handel geht es immer darum, im Angesicht von Unsicherheit Entscheidungen zu treffen. Das bedeutet, dass die Ergebnisse der Entscheidungen zu dem Zeitpunkt, zu dem diese Entscheidungen getroffen werden, nicht ganz eindeutig sind. Daraus ergibt sich die Bedeutung theoretischer Ansätze für die Konstruktion mathematischer Modelle, die es uns ermöglichen, solche Fälle sinnvoll zu beschreiben.

Über Methoden zum Erkennen überkaufter/überverkaufter Zonen. Teil I Über Methoden zum Erkennen überkaufter/überverkaufter Zonen. Teil I

Überkaufte/überverkaufte Zonen kennzeichnen einen bestimmten Zustand des Marktes, der sich durch schwächere Veränderungen der Wertpapierpreise von anderen unterscheidet. Diese nachteilige Veränderung der Dynamik ist in der letzten Phase der Entwicklung von Trends jeglicher Größenordnung am stärksten ausgeprägt. Da der Gewinn beim Handel direkt von der Fähigkeit abhängt, eine möglichst große Trendamplitude abzudecken, ist die Genauigkeit der Erkennung solcher Zonen eine Schlüsselaufgabe beim Handel mit irgendwelchen Wertpapieren.