Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 31): Der Zukunft entgegen (IV)

Daniel Jose | 9 Januar, 2023

Einführung

Nachdem wir im Artikel „Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 29)“ Chart Trade aus dem EA entfernt haben, haben wir das Paneel von Chart Trade in einen Indikator umgewandelt. Wie dies zu bewerkstelligen ist und wie die für die Funktionsfähigkeit des Anzeigers erforderlichen Funktionen eingestellt und gewartet werden, ist in Teil 30 beschrieben. Dies ist einer der möglichen Ansätze, obwohl es auch andere Wege gibt, mit ihren Vor- und Nachteilen, aber wir werden sie ein anderes Mal betrachten.

Es bleibt also noch etwas übrig, das wir aus dem EA entfernen müssen. Wir werden sie jetzt entfernen, und der Artikel wird der letzte in dieser Reihe sein. Das, was entfernt werden muss, ist das Soundsystem. Dies kann verwirrend sein, wenn Sie die vorherigen Artikel nicht verfolgt haben.

Um zu verstehen, wie der gesamte Prozess abläuft (da er viele Dinge umfasst, die einer Erklärung bedürfen), werden wir fast das gleiche Modell wie im vorherigen Artikel verwenden. Dadurch wird die Erklärung einfach und verständlich, auch für diejenigen, die keine professionellen Programmierer sind. Wir werden das System auch ein wenig verkomplizieren, um die Sache ein wenig aufzupeppen.

Diesmal ist das neue Teil das Soundsystem. Es wird aus dem EA entfernt. Aber das wird in Zukunft viele Vorteile bringen. Aber wir sollten nichts überstürzen, denn es ist wichtig zu verstehen, was hier geschehen wird. Der Artikel wird relativ kurz sein, aber interessant.


Einführung eines Sounddienstes

All diese Veränderungen können einen verrückt machen. Aber glauben Sie mir, die Idee ist nicht, Sie in den Wahnsinn zu treiben, vielleicht nur Sie ein wenig zu verwirren, sondern zu zeigen, wie manchmal kleine Änderungen einen großen Unterschied machen können und die Nutzung der MetaTrader 5-Plattform viel angenehmer machen. Gleichzeitig werden Sie sehen, wie all diese Aktionen die Modulation der Dinge ermöglichen.

Auf diese Weise können Sie auswählen, was Sie brauchen und was nicht. Wenn etwas wirklich genutzt wird, können Sie es später verbessern, um die Funktion noch nützlicher und angenehmer zu machen. Dies erfordert keine großen Änderungen oder Neuprogrammierung dessen, was vor einiger Zeit geschaffen wurde. Die Idee ist, das IMMER WIEDER ZU VERWENDEN.

Eines dieser Merkmale ist das Soundsystem. Es mag den Anschein haben, dass es eine gute Idee ist, dieses System innerhalb des EA zu belassen. In gewissem Sinne beeinträchtigt dieses System nicht das Funktionieren des EA als Ganzes. Aber wenn wir sie aus dem EA entfernen und eine Kommunikation zwischen ihnen implementieren, werden Sie sehen, dass es möglich ist, Sound Alerts auf sehr einfache Weise zu verwenden, als ob es sich um eine Sound-Bibliothek handelt. Diese Lösung wird sehr nützlich sein.

Es macht keinen Sinn, ein Warnsystem nur innerhalb eines EA zu platzieren. Es kann nützlich sein, ein Soundsystem in Indikatoren oder sogar in Skripten zu haben, die zu bestimmten Zeiten laufen. Dies wird bei der Analyse sehr hilfreich sein. Auf diese Weise kann die MetaTrader 5-Plattform zu einem wahren Analysemonster werden, mit dem Sie umfangreiche Berechnungen durchführen können, um den Markt zu ganz bestimmten Zeitpunkten besser analysieren zu können, sei es beim Eingehen oder Schließen von Positionen. All dies kann mit einem Minimum an Aufwand erreicht werden.

Man könnte sagen: „Aber ich kann alle Sounds in eine MQH-Datei (Header-Datei) einfügen, sie in ausführbare Dateien einbetten und das gewünschte Verhalten erzielen.“ Ja, das können Sie. Aber stellen Sie sich folgendes Szenario vor: Im Laufe der Zeit wird diese MQH-Datei wachsen, und einige ältere Programme können dann mit dieser Header-Datei (MQH) inkompatibel werden. Wenn Sie solche alten Dateien neu kompilieren müssen, werden Sie auf Probleme stoßen. Und wenn Sie ein modulares System schaffen, in dem es ein Kommunikationsprotokoll zwischen den Prozessen gibt, können Sie die Funktionalität der Plattform erweitern und gleichzeitig die Kompatibilität mit älteren Programmen wahren.

Das ist der Grund für diese Änderungen: Sie sollen zeigen, wie Sie jeden der möglichen Pfade erstellen und nutzen können. Und ich zeige das, indem ich Dinge aus dem EA herausnehme, während ich die Dinge so nah wie möglich am ursprünglichen Verhalten halte.

Im vorigen Artikel habe ich gezeigt, wie man Chart Trade neu erstellt, sodass er sich genauso verhält, als wäre er Teil des EAs. Nachdem er jedoch aus dem EA entfernt worden war, musste eine Möglichkeit geschaffen werden, damit er im gleichen Modus weiterarbeiten konnte. Der Weg, den ich Ihnen gezeigt habe, ist einer von vielen möglichen, und obwohl er nicht der beste ist, funktioniert er. Jede Lösung erfordert ein angemessenes Verständnis dafür, wie die Dinge im Allgemeinen funktionieren. Manchmal hilft die Beschränkung auf ein einziges Ideenmodell nicht bei der Lösung bestimmter Situationen, ganz im Gegenteil. Gerade aufgrund mangelnder Kenntnisse denken viele, dass es nicht möglich ist, etwas zu tun, oder sie sagen, dass das System begrenzt ist, während in Wirklichkeit die Begrenzung in den mangelnden Kenntnissen der Person liegt, die für die Planung und Umsetzung der Lösung verantwortlich ist.

Das haben wir bei der Implementierung des Auftragssystems gesehen, bei der keine Struktur zur Speicherung der Daten verwendet wurde. Viele dachten, dass es unmöglich sei, so etwas zu tun, dass es keine Möglichkeit gäbe, so etwas zu tun. Aber ich habe gezeigt, dass es möglich ist. Das Wichtigste ist, dass Sie wissen und verstehen, was Sie tun. Der erste Schritt besteht darin, die Grenzen der einzelnen Lösungsarten zu kennen.

Lassen Sie uns also lernen, wie wir das Soundsystem so modular wie möglich gestalten und dabei berücksichtigen, dass wir seine Funktionalität mit dem weiteren Wachstum des Systems erweitern werden.

Zunächst einmal werden wir die Klasse C_Sound nicht anfassen, außer in den Fällen, in denen wir die Funktionalität erweitern müssen. In dieser Klasse wird es also keine großen Veränderungen geben. In der Tat wird diese Klasse in diesem Stadium unverändert bleiben, wir müssen jedoch kleine Ergänzungen an dem System vornehmen. Die erste davon ist die unten abgebildete Header-Datei:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableAlert "Sound Alert"
//+------------------------------------------------------------------+


Sie könnten denken, dass wir diese Datei im EA verwenden werden, aber nein... der EA wird diese Datei nicht verwenden, zumindest noch nicht. Es wird eine andere Datei verwendet, die wir später sehen werden.

Danach können wir eine Datei erstellen, die den Sounddienst darstellt. Das ist im folgenden Code dargestellt:

#property service
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Auxiliar\C_Sounds.mqh>
#include <NanoEA-SIMD\Interprocess\Sound.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        union u00
        {
                double value;
                struct s00
                {
                        uint    i0,
                                i1;
                }info;
        }u_local;
        C_Sounds Sound;
        
        while (!IsStopped())
        {
                Sleep(500);
                if (GlobalVariableGet(def_GlobalVariableAlert, u_local.value))
                {
                        GlobalVariableDel(def_GlobalVariableAlert);
                        if (u_local.info.i1 == 0) Sound.PlayAlert((C_Sounds::eTypeSound)u_local.info.i0);
                        else Sound.PlayAlert(u_local.info.i1);
                }
        }
}
//+------------------------------------------------------------------+


Dieser Dienst überwacht die globalen Variablen des MetaTrader 5. Es wird der angegebene Ton abgespielt, sobald die Variable, deren Name in der Header-Datei deklariert ist, von einem beliebigen Skript, EA oder Indikator gestartet wird, unabhängig davon, was es ist und wann es passiert.

Dazu müssen Sie lediglich den Index der abzuspielenden Datei angeben. Basierend auf der obigen Struktur können Sie insgesamt 4.294.967.295 verschiedene Töne abspielen, was nur die Zahl für externe Dateien ist. Man kann die gleiche Anzahl interner Klänge haben, sodass man eine Menge Dinge tun kann.

Damit das System weiß, welcher Soundtyp abgespielt werden soll, prüft es den Wert der Variablen u_lokal.info.i1 überprüft: Wenn der Wert 0 ist, wird der abzuspielende Sound in die Servicedatei eingebettet, und der Index des Sounds wird durch die Variable u_local.info.i0 angegeben, aber dieser Wert stellt den Enumerator innerhalb der Klasse C_Sound dar.

Jetzt können wir den Dienst kompilieren und ausführen. Sobald die oben genannten Bedingungen erfüllt sind, führt der Dienst seine Arbeit aus, wobei zu beachten ist, dass die globale Variable, wenn sie vom Dienst erfasst wird, entfernt wird, damit sie zu einem anderen Zeitpunkt verwendet werden kann.

Bevor wir weitermachen, sollten wir ein wenig nachdenken. Im Gegensatz zum Indikator mit Chart Trade, der nur mit dem EA kommuniziert, kann das Soundsystem mit jeder Art von Programm auf der MetaTrader 5-Plattform kommunizieren. Um den gewünschten Ton abzuspielen, müssen Sie der Variablen einen Wert vom Typ double zuweisen.

Sie denken vielleicht, dass es einfach ist, aber versuchen Sie es und Sie werden sehen, dass dem nicht so ist. Außerdem müssen Sie die globale Variable jedes Mal mit dem richtigen Namen anlegen. Sie müssen also jedes Mal viel Arbeit leisten, wenn Sie einen zuvor gespeicherten Ton abspielen wollen.

Aber es gibt eine praktische Lösung, die all diesen Ärger vermeidet. Weil es ganz nett ist, werden wir diese Lösung in diesem frühen Stadium in ihrer einfachsten Form verwenden. Um zu sehen, wie es gemacht wird, gehen wir zum nächsten Thema über.


Erstellen einer Bibliothek für den Zugriff auf den Sounddienst

Der Grund für die Einrichtung einer Bibliothek ist, dass sie unser Leben in irgendeiner Weise erleichtern wird. Egal wie, aber es wird unser Leben einfacher machen. Im vorigen Thema habe ich erwähnt, dass wir, wenn ein Programm auf den Sounddienst zugreift, den Namen der globalen Variablen, die den Zugriff auf den Dienst ermöglicht, nicht kennen müssen. So seltsam es auch klingen mag, aber die beste Möglichkeit, Informationen zwischen Prozessen weiterzugeben, ist das Hinzufügen einer Schicht zum System. Diese Ebene ist die Bibliothek.

Diese Bibliotheken werden die Komplexität der Datenmodellierung zwischen den Prozessen „verstecken“, sodass Sie sich keine Gedanken mehr darüber machen müssen, in welcher Form die Modellierung erfolgen soll. Sie kümmern sich nur um die Anrufe selbst und um die erwarteten Ergebnisse.

Bei der Erstellung einer Bibliothek gibt es nur 2 Probleme:

  1. Klares und deutliches deklarieren der Funktionen, die exportiert werden sollen.
  2. Verstecken der Komplexität der internen Modellierung so weit wie möglich, sodass der Bibliotheksnutzer nicht wissen muss, was vor sich geht. Der Nutzer sollte nur die eingehenden Daten und das ausgegebene Ergebnis sehen.

Daher ist jede Prozedur oder Funktion innerhalb einer Bibliothek so konzipiert, dass sie aus Sicht des Nutzers ein sehr einfaches Verhalten aufweist. Aber intern kann es eine äußerst komplexe Ebene von Vorgängen geben, die zu den Endergebnissen führen. Aber der Programmierer, der die Bibliothek verwendet, muss nicht wissen, was in ihr vor sich geht. Es ist wichtig zu wissen, dass die Ergebnisse korrekt übermittelt werden.

Werfen wir also einen Blick auf unsere Bibliothek, die die Datenmodellierung des Sounddienstes verbergen wird. Jedes Programm sollte zwei Dinge melden: Erstens, ob es sich um ein internes oder externes Lautbild handelt, und zweitens den Index des Lautbildes. Klingt kompliziert? Schauen wir uns den Code dieser Aufrufe innerhalb der Bibliothek an:

void Sound_WAV(uint index) export { Sound(0, index); }
void Sound_Alert(uint index) export { Sound(index, 0); }


Diese beiden Funktionen verbergen jegliche Komplexität in der Datenmodellierung. Beachten Sie, dass wir das Schlüsselwort export verwenden, das den Compiler anweist, einen symbolischen Link zu diesen Funktionen zu erstellen. Sie sind eigentlich Prozeduren, weil sie keinen Wert zurückgeben. Auf diese Weise sind sie außerhalb der Datei sichtbar, als ob diese Datei eine DLL wäre.

Aber wenn Sie sich den Code ansehen, werden Sie keine Funktion namens Sound. Wo ist sie? Sie befindet sich in der Bibliothek selbst, ist aber außerhalb der Bibliothek nicht sichtbar. Siehe den vollständigen Bibliothekscode unten:

//+------------------------------------------------------------------+
#property library
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Interprocess\Sound.mqh>
//+------------------------------------------------------------------+
void Sound_WAV(uint index) export { Sound(0, index); }
//+------------------------------------------------------------------+
void Sound_Alert(uint index) export { Sound(index, 0); }
//+------------------------------------------------------------------+
void Sound(uint value00, uint value01)
{
        union u00
        {
                double value;
                struct s00
                {
                        uint    i0,
                                i1;
                }info;
        }u_local;
        
        u_local.info.i0 = value00;
        u_local.info.i1 = value01;
        GlobalVariableTemp(def_GlobalVariableAlert);
        GlobalVariableSet(def_GlobalVariableAlert, u_local.value);
}
//+------------------------------------------------------------------+


Beachten Sie, dass die Sound-Prozedur die gesamte notwendige Komplexität enthält, die erforderlich ist, um den entsprechenden Wert zusammenzustellen, damit der Dienst die Aufgabe ausführen kann, die ein Skript, Indikator oder EA anfordert. Anstatt diesen Code jedoch in das Programm einzubauen, das auf den Dienst zugreift, werden wir nur vereinfachte Aufrufe verwenden, was das Debuggen des Programms bequemer und weniger anstrengend macht.

Um zu verstehen, wie das funktioniert, sehen wir uns ein Beispielskript an:

#property copyright "Daniel Jose"
#property script_show_inputs
#import "Service_Sound.ex5"
        void Sound_WAV(uint);
        void Sound_Alert(uint);
#import
//+------------------------------------------------------------------+
input uint value00 = 1;         //Internal sound service...
input uint value01 = 10016;     //Sound in WAV file...
//+------------------------------------------------------------------+
void OnStart()
{
        Sound_WAV(value01);
        Sound_Alert(value00);
}
//+------------------------------------------------------------------+


Sehen Sie sich den obigen Code an. Es ist nicht notwendig zu wissen, welche Art von Kommunikation implementiert ist, wo und wann das Schallereignis stattfindet — es kann überall passieren, innerhalb der Plattform, innerhalb des Betriebssystems oder sogar aus der Ferne, es spielt keine Rolle. Das Einzige, was wir wissen müssen, ist, ob es sich um ein internes oder externes Tonbild handelt und welchen Index es hat.

Bevor Sie fortfahren, möchte ich Sie bitten, ein Experiment durchzuführen. Funktionen tauschen. In diesem Fall führen wir Sound_WAV und dann Sound_Alert aus. Führen Sie es aus und sehen Sie sich das Ergebnis an. Als Nächstes ändern Sie die Reihenfolge: Führen Sie Sound_Alert aus und dann Sound_WAV, um das Ergebnis zu sehen. Für diejenigen, die das nicht verstehen, würde der Code innerhalb des OnStart-Ereignisses in der ersten Situation wie folgt aussehen:

void OnStart()
{
        Sound_WAV(value01);
        Sound_Alert(value00);
}


Und im zweiten Fall so:

void OnStart()
{
        Sound_Alert(value00);
        Sound_WAV(value01);
}


Auch wenn es albern erscheinen mag, ist dieses Experiment notwendig, um einige Dinge zu verstehen. Ignorieren Sie es nicht, die Ergebnisse sind interessant.

Nachdem wir nun gesehen haben, was wir in unsere Programme einfügen müssen, um Töne abspielen zu können, müssen wir nur noch den folgenden Code hinzufügen:

#import "Service_Sound.ex5"
        void Sound_WAV(uint);
        void Sound_Alert(uint);
#import


Wann immer Sie ein Tonbild abspielen wollen, verwenden Sie einfach die richtige Funktion mit dem richtigen Wert, ohne sich Gedanken darüber zu machen, wie das geschehen soll. Das System selbst wird dafür sorgen, dass alles perfekt funktioniert. In unserem EA wird der Code wie folgt aussehen:

// ...

#import "Service_Sound.ex5"
        void Sound_WAV(uint);
        void Sound_Alert(uint);
#import
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh>
#include <NanoEA-SIMD\Interprocess\Sound.mqh>

// ...

Hier stellt sich die Frage: Was bewirkt der hervorgehobene Code dort? Können wir nicht einfach die Bibliothek nutzen? Ja, aber wir können eine Enumeration verwenden, um die numerischen Codes der Geräusche zu identifizieren, so wie es früher gemacht wurde, und wenn Sie nicht eine sehr geringe Anzahl von Tonbildern oder Alarmen verwenden, kann es sehr schwierig sein, zu verstehen, was jeder einzelne repräsentiert, wenn Sie nur den Code betrachten. Aus diesem Grund wurde die Header-Datei Sound.mqh um einen Zusatz erweitert, der im folgenden Code hervorgehoben wird:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableAlert "Sound Alert"
//+------------------------------------------------------------------+
enum eTypeSound {TRADE_ALLOWED, OPERATION_BEGIN, OPERATION_END};
//+------------------------------------------------------------------+


Wir könnten also mit einem Code wie diesem enden:

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound_Alert(TRADE_ALLOWED);
                return INIT_FAILED;
        }

// ... The rest of the function

Er ist viel repräsentativer als derselbe Code, der Indizes anstelle von Enumerationen verwendet:

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound_Alert(0);
                return INIT_FAILED;
        }

// ... Rest of the code


Welche ist leichter zu verstehen?

Nach all dieser Arbeit haben Sie den Informationsfluss in der Plattform wie in der folgenden Abbildung dargestellt:

Wie Sie sehen, haben wir unabhängig davon, wer das Signal liefert, immer das gleiche Ziel.


Schlussfolgerung

Auch wenn es auf den ersten Blick keine große Sache zu sein scheint, trägt das, was in diesem Artikel gezeigt wurde, erheblich zur Verbesserung der Nutzerfreundlichkeit Ihres Codes, Ihrer Programme und Ihrer Informationen bei. Je weniger Sie programmieren und je produktiver Sie werden, desto sicherer und stabiler wird Ihr Code, da Wiederverwendung und Tests in vielen verschiedenen Szenarien wiederholt werden.

Hier haben wir einen anderen Weg gesehen, der sich von dem im vorigen Artikel beschriebenen unterscheidet, der aber noch sehr verbessert werden kann und eine Fülle von neuen Möglichkeiten bietet. Aber das werden wir in einer anderen Serie sehen, in der Sie lernen werden, wie Sie Ihre Programme und Projekte in MetaTrader 5 viel modularer gestalten können, mit einem viel höheren Maß an Sicherheit, Nutzerfreundlichkeit und Stabilität als jede der hier vorgestellten Methoden.

Aber das Wichtigste ist, dass man weiß, wie man verschiedene Lösungen entwirft und einsetzt, denn es gibt Fälle, in denen eine Lösung aus dem einen oder anderen Grund besser ist als eine andere.

Alle Codes sind in der beigefügten Datei enthalten. Für diejenigen, die mit dieser Art der Programmierung, der Verwendung von Bibliotheken, nicht sehr vertraut sind, empfehle ich, diese Phase der EA-Entwicklung gut zu studieren. Wie heißt es so schön, „Verschiebe nicht auf morgen, was Du heute kannst besorgen“, denn der morgige Tag kommt vielleicht nicht so, wie erwartet.

Dieser Artikel vervollständigt diese EA-Entwicklungsphase. In Kürze werde ich eine andere Art von Material vorstellen, das sich auf eine andere Art von Situation konzentriert, bei der der Grad der Komplexität viel höher, aber dennoch wesentlich interessanter ist. Eine herzliche Umarmung an alle und bis später.