English Русский Español 日本語 Português
preview
Entwicklung eines Replay System (Teil 34): Auftragssystem (III)

Entwicklung eines Replay System (Teil 34): Auftragssystem (III)

MetaTrader 5Beispiele | 14 Mai 2024, 09:31
56 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel Entwicklung eines Wiedergabesystems (Teil 33): Ordnungssystem (II), habe ich erklärt, wie wir unser Ordnungssystem aufbauen werden. In diesem Artikel haben wir uns den Großteil des Codes angesehen und die Komplexität und die Probleme angesprochen, die wir lösen müssen. Auch wenn der Code einfach und leicht zu handhaben zu sein scheint, ist dies bei weitem nicht der Fall. In diesem Artikel werden wir also ein wenig tiefer in das eintauchen, was wir in Bezug auf die Umsetzung tatsächlich haben und noch tun müssen. Wir müssen noch die letzte Methode in der Klasse C_Manager diskutieren und erklären sowie den EA-Code kommentieren. Im Falle des EA-Codes werden wir uns hauptsächlich auf die geänderten Teile konzentrieren. Auf diese Weise können Sie sich ein Bild davon machen, wie es sich verhalten wird, ohne auf die MetaTrader 5-Plattform zurückgreifen zu müssen. Nun, Sie können es so viel testen, wie Sie wollen, da der Code im Anhang zu diesem Artikel enthalten sein wird.

Viele mögen es für unnötig halten, das System in der gezeigten Form zu testen, in der Hoffnung, ein kompatibleres System zu haben, bevor sie es tatsächlich ausprobieren. Das ist keine gute Idee, denn wenn Sie nicht verstehen, wie das System jetzt funktioniert, werden Sie große Probleme haben zu verstehen, wie es in Zukunft funktionieren wird. Schlimmer noch, Sie werden nicht in der Lage sein, es so anzupassen, dass es etwas erzeugt, was Sie gerne sehen würden, wenn dies hier aus dem einen oder anderen Grund nicht gezeigt wird und Sie nicht an der Entwicklung interessiert sind. Während sich das System allmählich entwickelt, können Sie einigen Details mehr Aufmerksamkeit schenken, andere testen oder warten, bis das System so weit ausgereift ist, dass Sie es tatsächlich auf die Plattform stellen und sein Verhalten analysieren möchten.

Ich weiß, dass viele Menschen gerne ein ausgefeiltes System in die Hand nehmen und nutzen, während andere es gerne wachsen und sich entwickeln sehen. Nun, wir beginnen mit einer Methode, die bisher fehlte. Da dies ein recht umfangreiches Thema ist, werden wir ihm einen eigenen Abschnitt widmen.


Hauptfunktion der Klasse C_Manager: DispatchMessage

Diese Funktion ist zweifelsohne das Herzstück der gesamten Klasse, die wir erstellen. Es verarbeitet Ereignisse, die von der MetaTrader 5-Plattform erzeugt und an unser Programm gesendet werden. Dies sind die Ereignisse, die wir von der Plattform erhalten möchten. Zum Beispiel: CHARTEVENT_MOUSE_MOVE. Es gibt auch andere gesendete Ereignisse, die unser Programm ignorieren kann, da sie für das Projekt, das wir erstellen, nicht sehr nützlich sind. Ein Beispiel ist CHARTEVENT_OBJECT_CLICK.

Die Tatsache, dass die gesamte Ereignisbehandlung in den Klassen konzentriert ist, macht es viel einfacher, das Projekt in Modulen auszuführen. Das mag zwar nach viel Arbeit klingen, aber Sie werden bald sehen, dass sich der Code viel einfacher von einem Projekt auf ein anderes übertragen lässt, wodurch die Entwicklung neuer Projekte beschleunigt wird.

Hier gibt es zwei Momente:

  • Erstens: Durch die Konzentration der Ereignisverarbeitung an einer Stelle und die Erleichterung der Portierung und des Verschiebens von Code zwischen Projekten reduzieren wir die Menge an Code, die wir im Hauptcode, in diesem Fall dem EA, unterbringen müssen. Dies erleichtert die Fehlersuche erheblich, da die Anzahl der Fehler, die sowohl bei der Wiederverwendung von Klassen als auch bei der Behandlung von Ereignissen auftreten können, reduziert wird.
  • Der zweite Moment ist ein wenig komplexer. Es geht darum, wie jedes Programm funktionieren wird. Einige Arten von Code sollten in einer bestimmten Reihenfolge ablaufen. Sehr oft wird Code geschrieben, der in einer bestimmten Reihenfolge ausgeführt werden muss, da er sonst nicht funktioniert oder fehlerhafte Ergebnisse liefert.

Anhand dieser beiden Punkte können wir uns den Code der Methode ansehen, um herauszufinden und zu verstehen, warum sie ausgeführt wird:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      static double price = 0;
      bool bBuy, bSell;
                                
      def_AcessTerminal.DispatchMessage(id, lparam, dparam, sparam);
      def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam);
      switch (id)
      {
         case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL))
            {
               if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP))  ToMarket(ORDER_TYPE_BUY);
               if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN))ToMarket(ORDER_TYPE_SELL);
            }
            break;
         case CHARTEVENT_MOUSE_MOVE:
            bBuy = def_AcessMouse.CheckClick(C_Mouse::eSHIFT_Press);
            bSell = def_AcessMouse.CheckClick(C_Mouse::eCTRL_Press);
            if (bBuy != bSell)
            {
               if (!m_Objects.bCreate)
               {
                  def_AcessTerminal.CreateObjectGraphics(def_LINE_PRICE, OBJ_HLINE, m_Objects.corPrice, 0);
                  def_AcessTerminal.CreateObjectGraphics(def_LINE_STOP, OBJ_HLINE, m_Objects.corStop, 0);
                  def_AcessTerminal.CreateObjectGraphics(def_LINE_TAKE, OBJ_HLINE, m_Objects.corTake, 0);
                  EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, "");
                  m_Objects.bCreate = true;
               }
               ObjectMove(def_InfoTerminal.ID, def_LINE_PRICE, 0, 0, def_InfoMouse.Position.Price);
               ObjectMove(def_InfoTerminal.ID, def_LINE_TAKE, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceTake, m_Infos.Leverage) * (bBuy ? 1 : -1)));
               ObjectMove(def_InfoTerminal.ID, def_LINE_STOP, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceStop, m_Infos.Leverage) * (bSell ? 1 : -1)));
               if ((def_AcessMouse.CheckClick(C_Mouse::eClickLeft)) && (price == 0)) CreateOrder((bBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL), price = def_InfoMouse.Position.Price);
            }else if (m_Objects.bCreate)
            {
               EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, "");
               ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix);
               m_Objects.bCreate = false;
               price = 0;
            }
            break;
         }
      }

    Haben Sie keine Angst vor dem obigen Code. Auch wenn dieser Code auf den ersten Blick kompliziert erscheinen mag, sollten Sie sich nicht davor fürchten. Alles, was wir tun, ist recht einfach und relativ alltäglich. Das Aussehen des Codes erweckt den ersten Eindruck, dass er sehr komplex und schwer zu verstehen ist. Lassen Sie uns also Schritt für Schritt vorgehen. Schauen wir uns zunächst die ersten Anrufe an. Um sie besser zu erklären, werde ich sie in mehrere Teile unterteilen. Ich glaube, eine solche Erklärung ist leichter zu verstehen. Wir werden uns auf einige wenige Punkte konzentrieren, damit Sie nicht die ganze Seite durchblättern müssen, um zu finden, was wir besprechen.

    def_AcessTerminal.DispatchMessage(id, lparam, dparam, sparam);
    def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam);
    
    

    Erinnern Sie sich daran, dass wir an manchen Stellen alles in einer bestimmten Reihenfolge ablaufen lassen müssen? Genau darum geht es in den beiden obigen Zeilen. Sie verhindern, dass Sie die Ereignisverarbeitung vergessen oder (noch schlimmer) in der falschen Reihenfolge im EA-Code platzieren. Es ist nicht so, dass dies jetzt, in diesem Stadium der Erstellung des Codes, irgendeine Auswirkung hat, aber je weniger Code wir in den EA einfügen müssen, desto besser wird er in Zukunft sein. Wenn wir ein bestimmtes Ereignis übersehen, kann dies dazu führen, dass das gesamte Projekt auf völlig unerwartete Weise oder nicht so funktioniert, wie wir es uns wünschen.

    Das ist alles klar. Nun können wir uns das Ereignis CHARTEVENT_KEYDOWN ansehen. Es kümmert sich um die Auslöser, die auftreten, wenn Sie eine Taste drücken.

    case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL))
            {
                    if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP))  ToMarket(ORDER_TYPE_BUY);
                    if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN))ToMarket(ORDER_TYPE_SELL);
            }
            break;
    
    

    Hier haben wir eine Situation, die ein wenig verwirrend erscheinen mag: Laut der Dokumentation enthält die Variable lparam den Code der gedrückten Taste. Das passiert auch, aber das Problem ist, dass wir die Dinge etwas anders angehen müssen. Wenn eine Taste gedrückt wird, erzeugt das Betriebssystem ein bestimmtes Ereignis. Wenn MetaTrader 5 den Fokus des Betriebssystems erhält, wird das erzeugte Ereignis weitergegeben. Da unser Code einen Handler für die Tastendruck-Aktion hat, trennt er die Behandlung des Tastendrucks von anderen Arten von Ereignissen.

    Anmerkung: Der oben gezeigte Code muss nicht unbedingt in das CHARTEVENT_KEYDOWN-Ereignis eingefügt werden. Sie könnte außerhalb dieses Ereignisses stattfinden. Indem wir ihn jedoch in CHARTEVENT_KEYDOWN unterbringen, verhindern wir eine peinliche Situation. Diese Situation tritt ein, wenn wir die Analyse einer Schlüsselbedingung, d. h. des CHARTEVENT_KEYDOWN-Ereignisses, durchführen, während die Plattform uns auf eine andere Art von Ereignis aufmerksam macht, das aus irgendeinem Grund ausgelöst wurde.

    Denken Sie an die Behandlung des Tastaturstatus, wenn ein Ereignis wie CHARTEVENT_CHART_CHANGE ausgelöst wird, das tatsächlich aktiviert wird, wenn Änderungen im Chart auftreten. Und gleichzeitig prüft unser Programm den Zustand der Tastatur. Solche Dinge haben keine praktische Bedeutung und sind zudem sehr zeitaufwendig. Aus diesem Grund isoliere ich das Parsen des Tastaturstatus im Ereignis CHARTEVENT_KEYDOWN.

    Aber kommen wir zurück zum Code. Sie werden feststellen, dass ich die Funktion TerminalInfoInteger verwende, um bestimmten Tastaturcode zu erkennen und zu isolieren. Wenn dies nicht der Fall ist, müssten wir zusätzlich prüfen, ob die STRG-Taste gleichzeitig mit einer anderen Taste, in diesem Fall PFEIL AUF oder PFEIL AB, gedrückt wurde. Das ist genau das, was wir tun. Wir brauchen ein Tastaturkürzel, damit unser Programm, in diesem Fall der EA, weiß, was es programmtechnisch zu tun hat. Wenn Sie die Kombination STRG + PFEIL OBEN drücken, sollte der EA verstehen, dass wir zum Marktpreis kaufen wollen. Wenn die Kombination STRG + PFEIL RUNTER gedrückt wird, sollte der EA zum Marktpreis verkaufen. Beachten Sie, dass die Variable lparam zwar die einzelnen Tastenwerte angibt, dies aber bei der Arbeit mit Tastaturkürzeln nicht hilfreich ist. Wenn Sie aber so vorgehen, wie Sie es jetzt tun, indem Sie nur eine der Tastenkombinationen drücken, erhält der EA keine Anweisungen zum Handel zum Marktpreis.

    Wenn Sie der Meinung sind, dass diese Kombination mit dem, was Sie verwenden, in Konflikt geraten könnte, ändern Sie sie einfach. Seien Sie jedoch vorsichtig und halten Sie den Code innerhalb des Ereignisses CHARTEVENT_KEYDOWN, um die Tatsache auszunutzen, dass er nur dann ausgeführt wird, wenn MetaTrader 5 ein Schlüsselereignis auslöst, wodurch eine unnötige Codeausführung verhindert wird. Ein weiterer Punkt ist, dass der in der Variablen lparam angezeigte Schlüsselcode einer Tabelle folgt, die von Region zu Region unterschiedlich ist, was die Sache noch viel komplizierter macht. In der hier gezeigten Weise wird eine solche Tabelle eigentlich nicht verwendet.

    Schauen wir uns nun die nächste Ereignisbehandlung an, CHARTEVENT_MOUSE_MOVE. Der Einfachheit halber werde ich es in kleine Teile aufteilen, in der Reihenfolge, in der sie im Klassencode erscheinen.

    case CHARTEVENT_MOUSE_MOVE:
            bBuy = def_AcessMouse.CheckClick(C_Mouse::eSHIFT_Press);
            bSell = def_AcessMouse.CheckClick(C_Mouse::eCTRL_Press);
    
    

    Achten Sie auf eine Sache. Hier verwenden wir die Klasse C_Study für den Zugriff auf die Klasse C_Mouse. Vergessen Sie das nicht, und beachten Sie, dass wir im Gegensatz zu der oben beschriebenen Ereignisbehandlung von CHARTEVENT_KEYDOWN hier den Zustand der Schaltflächen erfassen. Dies bezieht sich jetzt auf die Maus. Beunruhigt Sie das nicht? Diese Tasten gehören eigentlich zur Maus, nicht zur alphanumerischen Tastatur. Warum? Versuchen ich, Sie zu verwirren? Nichts dergleichen, mein lieber Leser. Die Tatsache, dass wir SHIFT und CTRL auf einer alphanumerischen Tastatur drücken können und es trotzdem schaffen, dies innerhalb der C_Mouse-Klasse zu tun, liegt daran, dass es nicht ganz so funktioniert. Die Tasten SHIFT und CTRL gehören eigentlich zur Maus. Aber nicht irgendeine Maus. Ich spreche von einer ganz bestimmten Art von Maus, die mehr oder weniger der in Abbildung 01 gezeigten ähnelt:

    Abbildung 01

    Abbildung 01:

    Dieser Maustyp hat zusätzliche Tasten am Gehäuse. Für das Betriebssystem und somit für die Plattform und unser Programm sind die SHIFT- und STRG-Tasten, von denen wir sprechen, eigentlich Teil der Maus. Da eine Maus jedoch nicht über solche zusätzlichen Tasten verfügen darf, erlaubt das Betriebssystem die Verwendung der Tastatur, wodurch die Plattform und das Programm sicherstellen, dass der Code richtig interpretiert wird. Die Tasten SHIFT und CTRL aus dem Ereignis CHARTEVENT_KEYDOWN sind daher nicht zu verwechseln mit denen, die hier im Ereignis CHARTEVENT_MOUSE_MOVE verwendet werden.

    Da wir nun den Zustand der SHIFT- und STRG-Tasten kennen, können wir uns den Rest des Ereigniscodes ansehen. Dies lässt sich anhand des folgenden Fragments beurteilen.

            if (bBuy != bSell)
            {
                    if (!m_Objects.bCreate)
                    {
                            def_AcessTerminal.CreateObjectGraphics(def_LINE_PRICE, OBJ_HLINE, m_Objects.corPrice, 0);
                            def_AcessTerminal.CreateObjectGraphics(def_LINE_STOP, OBJ_HLINE, m_Objects.corStop, 0);
                            def_AcessTerminal.CreateObjectGraphics(def_LINE_TAKE, OBJ_HLINE, m_Objects.corTake, 0);
                            EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, "");
                            m_Objects.bCreate = true;
                    }
                    ObjectMove(def_InfoTerminal.ID, def_LINE_PRICE, 0, 0, def_InfoMouse.Position.Price);
                    ObjectMove(def_InfoTerminal.ID, def_LINE_TAKE, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceTake, m_Infos.Leverage) * (bBuy ? 1 : -1)));
                    ObjectMove(def_InfoTerminal.ID, def_LINE_STOP, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceStop, m_Infos.Leverage) * (bSell ? 1 : -1)));
                    if ((def_AcessMouse.CheckClick(C_Mouse::eClickLeft)) && (price == 0)) CreateOrder((bBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL), price = def_InfoMouse.Position.Price);
            }else if (m_Objects.bCreate)
            {
                    EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, "");
                    ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix);
                    m_Objects.bCreate = false;
                    price = 0;
            }
    
    

    Dieser Code sieht zwar kompliziert aus, erfüllt aber eigentlich drei recht einfache Aufgaben. Sie können in einer separaten Funktion oder Methode innerhalb einer Klasse getrennt werden. Aber für den Moment werden sie hier bleiben, um die Dinge ein wenig zu erleichtern, zumindest was die Anrufe betrifft. Als Erstes müssen wir der Plattform über den EA mitteilen, dass wir einen schwebenden Auftrag erteilen wollen. Aber bevor wir das tun, müssen wir sehen, wo die Grenzwerte und der Auftrag platziert werden. Zu diesem Zweck verwenden wir drei Objekte, die an dieser Stelle des Codes erstellt werden. Beachten Sie, dass wir auch ein Ereignis an das System der Klasse C_Mouse senden, um der Klasse C_Mouse mitzuteilen, dass die Maus in den nächsten Momenten ausgeblendet werden soll. Dies ist die erste Phase.

    Sobald wir die gewünschten Objekte auf dem Chart haben, verschieben wir sie auf eine bestimmte Weise. Dazu verwenden wir diese Reihe von Funktionen. Wenn der Nutzer uns jedoch mitteilt, dass der gewünschte Zeitpunkt für die Übermittlung des Auftrags derjenige ist, der im Chart durch die erstellten Objekte angezeigt wird, erfüllen wir die Anforderung, eine schwebende Bestellung aufzugeben. Beachten Sie, wie die Prüfungen durchgeführt werden, damit wir wissen, was mit der Maus und im Chart passiert.

    Was den dritten und letzten Punkt betrifft, so entwickeln sich die Ereignisse wie folgt. Zunächst wird ein Ereignis an das Klassensystem C_Mouse gesendet, damit die Maus im Chart wieder sichtbar wird. Unmittelbar danach werden wir die erstellten Objekte löschen.

    In diesem Code steckt etwas Wichtiges. Dies sollten Sie bei der Programmierung Ihrer eigenen Codes immer beachten. Wenn Sie gerade erst mit dem Programmieren beginnen, haben Sie vielleicht einen sehr interessanten und gleichzeitig gefährlichen Punkt im obigen Code nicht bemerkt. Es kann gefährlich sein, wenn man es nicht richtig macht. Ich spreche von der RECURSION. In dem obigen Code verwenden wir eine Rekursion. Wenn dies nicht richtig geplant wird, landen wir in einer Endlosschleife. Sobald ein System in den Code-Teil eintritt, der die Rekursion verwendet, kann es ihn nie wieder verlassen.

    Um zu verstehen, wie diese Rekursion abläuft, werfen Sie einen Blick auf Abbildung 02, die sich direkt darunter befindet:

    Abbildung 02

    Abbildung 02: Interner Nachrichtenfluss.

    Der grüne Pfeil in Abbildung 02 zeigt genau, wo die Rekursion stattfindet. Aber wie geschieht das im Code? Um dies zu sehen, sehen Sie sich den nachstehenden Code an, der die DispatchMessage-Methode in der Klasse C_Manager zeigt.

    void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
       {
    
    // ...                          
    
          def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam);
          switch (id)
          {
    
    // ...
    
             case CHARTEVENT_MOUSE_MOVE:
    // ...
                if (bBuy != bSell)
                {
                   if (!m_Objects.bCreate)
                   {
    // ...
                      EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, "");
                   }
    
    // ...
    
                }else if (m_Objects.bCreate)
                {
                   EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, "");
    
    // ...
                }
                break;
             }
          }
    
    

    Die Rekursion ist aus Abbildung 02 vielleicht nicht ganz klar ersichtlich. Wenn Sie sich nur den obigen Code ansehen, ist er vielleicht auch nicht klar genug. Aber wenn Sie Abbildung 02 mit dem obigen Code kombinieren, können Sie sehen, wie die Rekursion funktioniert. Wenn dies nicht richtig geplant wird, werden wir ernsthafte Probleme bekommen. Lassen Sie uns das genauer erklären, damit Sie als neuer Programmierer sowohl die Macht der Rekursion als auch ihre Gefahren verstehen können. Wenn wir etwas sehr Komplexes tun müssen, egal was es ist, müssen wir den Prozess in kleinere oder einfachere Aufgaben unterteilen. Diese Aufgaben können eine bestimmte Anzahl von Malen wiederholt werden, sodass am Ende etwas viel Komplexeres entsteht, das jedoch einem einfachen Konzept folgt. Das ist die Macht der Rekursion. Sie wird jedoch nicht nur in diesem Szenario verwendet. Obwohl sie meist damit in Verbindung gebracht wird, können wir die Rekursion auch zur Lösung anderer Fälle verwenden.

    Das obige Fragment ist einer dieser Fälle. Um die Erklärungen zu verstehen, werfen Sie bitte noch einmal einen Blick auf Abbildung 02. Sie beginnt, wenn der Nutzer ein Ereignis erzeugt, das die MetaTrader 5-Plattform veranlasst, die Funktion OnChartEvent aufzurufen. In diesem Fall ruft diese Funktion die Methode DispatchMessage in der Klasse C_Manager auf. An dieser Stelle rufen wir die Ereignisbehandlung auf, die durch Vererbung in der Klasse C_Mouse vorhanden ist, und zwar in der Methode DispatchMessage, die in dieser Klasse vorhanden ist. Wenn die Methode zurückkehrt, macht das Programm dort weiter, wo es aufgehört hat. Wir treten in die Ereignisbehandlung ein, um zu prüfen, ob der Nutzer die Hilfslinien erstellen oder löschen möchte. An einem bestimmten Punkt haben wir also den Aufruf EventChartCustom. An diesem Punkt wird die Rekursion aktiviert.

    Was tatsächlich passiert, ist, dass MetaTrader 5 einen neuen Aufruf der OnChartEvent-Funktion macht, wodurch die Funktion erneut ausgeführt und die DispatchMessage-Methode aus der C_Manager-Klasse aufgerufen wird. Diese wiederum ruft durch Vererbung die Klasse C_Mouse auf, um eine nutzerdefinierte Methode auszuführen, die den Mauszeiger je nach Situation erscheinen oder verschwinden lässt. Wegen der Rekursion wird der Code jedoch nicht so viel zurückgeben, wie viele vielleicht denken. Sie kehrt zwar zurück, aber dies löst erneut die Ausführung von Code in der DispatchMessage-Methode der Klasse C_Manager aus. Darin liegt die Gefahr: Wenn Sie den Aufruf so platzieren, dass ein nutzerdefiniertes Ereignis, das in der Klasse C_Mouse behandelt wurde, in der Klasse C_Manager erscheint, wird es auch in der Klasse C_Manager verarbeitet. Und wenn wir dieses Ereignis in der Klasse C_Mouse mit der Funktion EventChartCustom versehentlich erneut behandeln, befinden wir uns in einer Endlosschleife.

    Da die Klasse C_Manager jedoch nicht über ein solches Ereignis verfügt, das von der Klasse C_Mouse behandelt wird, greifen wir auf die Funktion OnChartEvent zurück, die vollständig ausgeführt wird. Wenn die Funktion OnChartEvent ausgeführt wird, kehrt sie zu dem Punkt zurück, an dem der Aufruf von EventChartCustom erfolgte. Dies ist im obigen Codeteil dargestellt. Dadurch wird der gesamte verbleibende Code in der DispatchMessage-Methode in der Klasse C_Manager ausgeführt. Wenn sie abgeschlossen ist, kehren wir zur Funktion OnChartEvent zurück, wo sie vollständig ausgeführt wird, sodass die Plattform für die Ausführung anderer Arten von Ereignissen frei wird.

    So wird beim Aufruf von EventChartCustom aufgrund der Rekursion mindestens doppelt so viel Code ausgeführt wie in der Funktion OnChartEvent. Dies scheint ineffizient, aber der Punkt ist, dass der Code einfach ist und die Gesamtleistung der Plattform nicht stark beeinträchtigt. Aber es ist gut, dass wir uns immer bewusst sind, was wirklich passiert. Die Kosten der Rekursion sind in diesem Fall recht gering im Vergleich zu modularerem Code. In manchen Situationen können diese Kosten jedoch nicht gedeckt werden und den Code zu langsam machen. In solchen Fällen müssten wir andere Maßnahmen ergreifen, aber das ist im Moment nicht der Fall.

    Ich denke, ich habe die Methode DispatchMessage, die in der Klasse C_Manager verwendet wird, im Detail erklärt. Obwohl dies recht kompliziert erscheinen mag, sind wir in Wirklichkeit weit von etwas wirklich Komplexem entfernt, da das System noch nicht weiß, wie es mit dem auftragsübergreifenden Modell arbeiten soll. Zu diesem Zweck musste die Methode DispatchMessage erheblich geändert werden. Aber das werden wir für die Zukunft aufheben.

    Schauen wir uns nun weitere Änderungen im EA-Code an.


    Analyse der Aktualisierungen im Expert Advisor

    Obwohl der EA jetzt Aufträge erteilen und innerhalb der konfigurierten Handelszeit handeln kann, hat sich sein Code nicht wesentlich geändert. Es gibt einen Teil des Codes, der besondere Aufmerksamkeit verdient. Ich werde erklären, was dort vor sich geht. Dieser wichtige Punkt betrifft die Nutzerinteraktion und den Code im OnInit-Ereignis. Beginnen wir mit der Nutzerinteraktion. Das ist im folgenden Code dargestellt:

    input group "Mouse";
    input color     user00 = clrBlack;      //Price Line
    input color     user01 = clrPaleGreen;  //Positive Study
    input color     user02 = clrLightCoral; //Negative Study
    input group "Trade";
    input uint      user10 = 1;             //Leverage
    input double    user11 = 100;           //Take Profit ( Finance )
    input double    user12 = 75;            //Stop Loss ( Finance )
    input bool      user13 = true;          //Is Day Trade
    //+------------------------------------------------------------------+
    input group "Control of Time"
    input string    user20  = "00:00 - 00:00";      //Sunday
    input string    user21  = "09:05 - 17:35";      //Monday
    input string    user22  = "10:05 - 16:50";      //Tuesday
    input string    user23  = "09:45 - 13:38";      //Wednesday
    input string    user24  = "11:07 - 15:00";      //Thursday
    input string    user25  = "12:55 - 18:25";      //Friday
    input string    user26  = "00:00 - 00:00";      //Saturday
    
    

    Der Code ist für die Nutzerinteraktion verantwortlich. Hier gibt es zwei neue Gruppen von Informationen, auf die der Nutzer zugreifen und die er konfigurieren kann. In der ersten Gruppe wählt der Nutzer aus, wie die Handelsoperation ausgeführt werden soll - als Marktauftrag oder als schwebender Auftrag. Die Einstellungen sind recht einfach. Der Wert, der die Hebelwirkung darstellt (user 10), sollte den Multiplikator darstellen, mit dem Sie das Mindestvolumen für den Handel mit dem Vermögenswert verwenden wollen. Im Falle von Forex werden Sie höchstwahrscheinlich einen Wert von 100 oder etwas Ähnlichem verwenden, um eine gute Marge zu finden, mit der Sie arbeiten können. Andernfalls arbeiten Sie mit der Größenordnung von Cents, was dazu führt, dass die Grenzlinien nicht dort liegen, wo Sie sie erwarten würden. Wenn Sie an der Börse handeln, müssen Sie die Anzahl der zu verwendenden Aktien melden. Andernfalls geben Sie die Anzahl der Lose an. Bei Termingeschäften geben Sie die Anzahl der Kontrakte an. Das ist also alles ziemlich einfach. Für den Take-Profit (user 11) und den Stop-Loss (user 12) sollten Sie nicht die Anzahl der Punkte angeben, sondern den finanziellen Wert, der verwendet werden soll. Dieser Wert muss durch den Code entsprechend angepasst werden, um die korrekte Position im Vermögenspreis widerzuspiegeln. Die letzte Variable (user 13) gibt nur an, ob wir eine Kauf- oder Verkaufsposition eingehen.

    Wichtiger Hinweis: Dieser Mechanismus sollte mit Vorsicht getestet werden, da die Broker sehr spezifische Handelsbedingungen haben können. Bitte klären Sie dies vorab mit Ihrem Broker.

    Bei der zweiten Gruppe gibt es einige Dinge zu prüfen, bevor sie richtig eingestellt werden. Das liegt nicht daran, dass sie kompliziert oder schwer zu verstehen sind, sondern daran, dass Sie verstehen sollten, dass diese Variablen bestimmen, wann der EA uns erlaubt, Aufträge zu senden oder schwebende Aufträge zu platzieren. Die Frage der Verwaltung, Beendigung oder sogar Änderung von Aufträgen wird nicht mehr von der EA abhängen. Die Plattform MetaTrader 5 wird dafür zuständig sein, zumindest vorläufig.

    Sie können dann ein 1-Stunden-Fenster festlegen, in dem der EA mit den ihm zur Verfügung stehenden Ressourcen arbeiten darf. Diese Konfiguration wird für eine Woche und nicht für einen bestimmten Tag oder ein spezielles Datum vorgenommen.

    Um dies zu verstehen, sehen Sie sich den Code von OnInit unten an:

    int OnInit()
    {
            string szInfo;
            
            terminal = new C_Terminal();
            study    = new C_Study(terminal, user00, user01, user02);
            manager  = new C_Manager(terminal, study, user00, user02, user01, def_MagicNumber, user12, user11, user10, user13);
            
            if (_LastError != ERR_SUCCESS) return INIT_FAILED;
            
            for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++)
            {
                    switch (c0)
                    {
                            case SUNDAY     : szInfo = user20; break;
                            case MONDAY     : szInfo = user21; break;
                            case TUESDAY    : szInfo = user22; break;
                            case WEDNESDAY  : szInfo = user23; break;
                            case THURSDAY   : szInfo = user24; break;
                            case FRIDAY     : szInfo = user25; break;
                            case SATURDAY   : szInfo = user26; break;
                    }
                    (*manager).SetInfoCtrl(c0, szInfo);
            }
    
            MarketBookAdd(def_InfoTerminal.szSymbol);
            OnBookEvent(def_InfoTerminal.szSymbol);
            EventSetMillisecondTimer(500);
    
            return INIT_SUCCEEDED;
    }
    
    

    Achten Sie auf den obigen Code von OnInit. Er stellt ein vollständiges Bild davon dar, wie sich der EA während der Woche verhalten sollte, nicht nur an einem bestimmten Tag. Während der gesamten Woche. Es gibt Vermögenswerte oder Märkte, in diesem Fall den Devisenmarkt, auf denen der Handel fast ununterbrochen läuft und zu keinem Zeitpunkt des Tages aufhört. Wenn wir unseren individuellen Handelsplan für den EA konfigurieren müssten, der 24 Stunden am Tag laufen kann, würden wir während der Tageswechselperiode Probleme bekommen. Das heißt, sobald es 23:59:59 ist, müssten wir den EA stoppen und ihn die nächste Sekunde zurücksetzen, um das neue Handelsintervall herauszufinden. Wenn Sie jedoch die oben beschriebene Methode anwenden, kann der EA 24 Stunden am Tag, 7 Tage die Woche und 52 Wochen im Jahr laufen, ohne sich zu verlaufen oder zu wissen, welchen Zeitplan Sie verwenden sollen. Ich weiß, dass viele, die sich diesen Code ansehen, nicht verstehen, wie das eigentlich passiert. Daher sollten Sie den EA testen, um zu verstehen, wie dieses System funktioniert. Aber dieses System ist nicht neu. Wir haben dies bereits in einem der früheren Artikel besprochen: „Erstellen eines automatisch arbeitenden EA (Teil 10): Automatisierung (II)“.


    Schlussfolgerung

    Obwohl das System recht stabil und vielseitig zu sein scheint, begann es unter einem Fehler zu leiden. Dies ist ziemlich seltsam und rätselhaft, da das Auftreten dieses Fehlers keinen Sinn ergibt. Der Grund dafür ist, dass der Fehler an einer Stelle auftrat, die in keiner Weise verändert wurde. Wie auch immer, wir werden auf diesen Fehler im nächsten Artikel zurückkommen. Der Anhang enthält den gesamten Code im aktuellen Entwicklungsstadium, den Sie im Detail studieren und analysieren können. Im vorherigen Artikel habe ich keinen Code beigefügt. 

    Bitte beachten Sie, dass ich im letzten Artikel über das System der Auswahl von Objekten mit einem Klick gesprochen habe, im Gegensatz zum plattformüblichen Doppelklick-Modus. Am besten testen Sie das System auf Ihrer Plattform und ziehen Ihre eigenen Schlussfolgerungen, nachdem Sie es in Aktion gesehen haben. Laden Sie es herunter und führen Sie es aus, um zu sehen, wie das System funktioniert.



    Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
    Originalartikel: https://www.mql5.com/pt/articles/11484

    Beigefügte Dateien |
    Files_-_BOLSA.zip (1358.24 KB)
    Files_-_FOREX.zip (3743.96 KB)
    Files_-_FUTUROS.zip (11397.51 KB)
    Entwicklung eines Replay Systems (Teil 35): Anpassungen vornehmen (I) Entwicklung eines Replay Systems (Teil 35): Anpassungen vornehmen (I)
    Bevor wir weitermachen können, müssen wir einige Dinge in Ordnung bringen. Dabei handelt es sich nicht um die notwendigen Korrekturen, sondern vielmehr um Verbesserungen bei der Verwaltung und Verwendung der Klasse. Der Grund dafür ist, dass die Fehler durch eine Interaktion innerhalb des Systems entstanden sind. Trotz der Versuche, die Ursache für diese Ausfälle herauszufinden, um sie zu beseitigen, blieben alle Versuche erfolglos. Einige dieser Fälle machen keinen Sinn, z. B. wenn wir Zeiger oder Rekursion in C/C++ verwenden, stürzt das Programm ab.
    Algorithmen zur Optimierung mit Populationen: Mikro-Künstliches Immunsystem (Mikro-AIS) Algorithmen zur Optimierung mit Populationen: Mikro-Künstliches Immunsystem (Mikro-AIS)
    Der Artikel befasst sich mit einer Optimierungsmethode, die auf den Prinzipien des körpereigenen Immunsystems basiert - Mikro-Künstliches Immunsystem (Micro Artificial Immune System, Micro-AIS) - eine Modifikation von AIS. Micro-AIS verwendet ein einfacheres Modell des Immunsystems und einfache Informationsverarbeitungsprozesse des Immunsystems. In dem Artikel werden auch die Vor- und Nachteile von Mikro-AIS im Vergleich zu herkömmlichen AIS erörtert.
    Alternative Risiko-Ertrags-Metriken in MQL5 Alternative Risiko-Ertrags-Metriken in MQL5
    In diesem Artikel stellen wir die Umsetzung mehrere Risikorenditekennzahlen vor, die als Alternativen zur Sharpe-Ratio angepriesen werden, und untersuchen hypothetische Aktienkurven, um ihre Eigenschaften zu analysieren.
    Entwicklung eines Replay Systems (Teil 33): Auftragssystem (II) Entwicklung eines Replay Systems (Teil 33): Auftragssystem (II)
    Heute werden wir das Auftragssystem weiterentwickeln. Wie Sie sehen werden, werden wir in großem Umfang wiederverwenden, was bereits in anderen Artikeln gezeigt wurde. Dennoch werden Sie in diesem Artikel eine kleine Belohnung erhalten. Zunächst werden wir ein System entwickeln, das mit einem echten Handelsserver verwendet werden kann, sowohl von einem Demokonto als auch von einem echten Konto. Wir werden die Plattform MetaTrader 5 ausgiebig nutzen, die uns von Anfang an alle notwendige Unterstützung bietet.