English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
preview
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 22): Neues Auftragssystems (V)

Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 22): Neues Auftragssystems (V)

MetaTrader 5Handel | 10 Oktober 2022, 16:05
352 0
Daniel Jose
Daniel Jose

Einführung

Es ist nicht einfach, ein neues System einzuführen, da wir häufig auf Probleme stoßen, die den Prozess erheblich erschweren. Wenn diese Probleme auftauchen, müssen wir innehalten und die Richtung, in die wir uns bewegen, neu analysieren und entscheiden, ob wir die Dinge so lassen können, wie sie sind, oder ob wir einen neuen Blick darauf werfen sollten.

Solche Entscheidungen können bei der Entwicklung eines Systems recht häufig vorkommen, vor allem, wenn wir keine klare Frist oder kein Budget haben: Wenn wir nicht unter Druck stehen, können wir die Dinge testen und anpassen, um unser Bestes zu tun, um Stabilität in Bezug auf Entwicklung und Verbesserung zu gewährleisten.

Seriöse Programme, insbesondere kommerzielle, die im Rahmen bestimmter Budgets oder Fristen entwickelt werden, enthalten in der Regel viele Fehler, die später korrigiert werden müssen. Diejenigen, die das System entwickeln, haben oft keine Zeit, einige Lösungen zu implementieren oder zu lernen, die für das System sehr nützlich sein könnten. Auf diese Weise erhalten wir Programme, die in vielen Fällen hinter dem zurückbleiben, was Programmierer schaffen könnten. Manchmal bringen Unternehmen eine Version nach der anderen heraus, mit kleineren Verbesserungen oder Fehlerbehebungen. Das liegt nicht daran, dass die Fehler erst nach der Freigabe des Programms entdeckt wurden, sondern daran, dass die Programmierer vor der Freigabe unter Druck standen.

Dies geschieht tatsächlich in der gesamten Produktionskette. Während wir jedoch an der Lösung arbeiten, suchen wir nach dem besten und möglichst einfachsten Weg. Außerdem können wir es uns leisten, alle möglichen und machbaren Lösungen zu untersuchen. Wenn nötig, können wir anhalten und ein wenig zurückgehen, um den Systementwicklungsprozess zu ändern und zu verbessern. Sehr oft kann ein kleiner Stopp und ein Richtungswechsel bei der Entwicklung des gewünschten Systems sehr hilfreich sein.

In dem Artikel Entwicklung eines Handelssystems von Grund auf (Teil 21) war das System fast fertig. Es fehlte nur der Teil, der für das Verschieben von Aufträgen direkt auf dem Chart zuständig ist. Um diesen Teil zu implementieren, überprüfte ich den Code und stellte einige Merkwürdigkeiten fest: Er enthielt eine Menge sich wiederholender Teile. Allerdings waren wir sehr darauf bedacht, unnötige Wiederholungen zu vermeiden. Schlimmer noch, einige Dinge funktionierten nicht so, wie sie sollten, z. B. bei der Verwendung sehr unterschiedlicher Assets. Als wir anfingen, den EA zu entwickeln und als ich begann, ihn in den hier veröffentlichten Artikeln zu dokumentieren, dachte ich daran, ihn hauptsächlich für den Handel mit Futures an der B3-Börse einzusetzen. Ich habe es schließlich entwickelt, um mit Dollar-Futures oder dem als WDO und WIN bekannten Index zu handeln. Aber als das System dem System, das ich ursprünglich verwenden wollte, immer näher kam, wurde mir klar, dass es auf andere Märkte oder Vermögenswerte ausgedehnt werden kann. Genau hier liegt das Problem.


1.0. Zurück zur Zwischenablage

Um das Problem zu verstehen, sollten Sie auf ein wichtiges Detail achten, das viele Menschen bei der Entwicklung eines Expert Advisors übersehen. Wie werden die Kontrakte im Handelssystem definiert? MetaTrader 5 stellt diese Informationen zur Verfügung, während MQL5 den Zugriff auf diese Informationen ermöglicht. Wir müssen also die Daten verstehen, um die Berechnungen mathematisch zu verallgemeinern und ein möglichst vollständiges System zu erhalten.

Das Auftragssystem, das wir entwickeln, ist so konzipiert, dass es direkt aus dem Chart heraus handelt, ohne dass andere externe Ressourcen oder etwas, das in der Zukunft erstellt werden könnte, erforderlich sind. Diese Entwicklung ist ein Versuch, ein Handelssystem zu schaffen, das den Plattformen sehr ähnlich ist, aber vollständig quelloffen ist, so dass Sie die Informationen, die Sie benötigen, anpassen und hinzufügen können.

Die Idee ist, dem Nutzer die Möglichkeit zu geben, sich über eine Position zu informieren, indem er sie einfach ansieht. Obwohl die Idee sehr gut zu sein scheint, macht das Handelssystem denjenigen das Leben nicht leicht, die versuchen, ein System für FOREX und Aktienmärkte wie B3 zu erstellen. Der Umfang der verfügbaren Informationen reicht aus, um Aufträge für den einen oder anderen Markt anzupassen, aber etwas Allgemeines zu erstellen, ist zu einem Problem, fast zu einer persönlichen Ehrverletzung geworden. Ich beschloss jedoch, mich dem Problem zu stellen und zu versuchen, ein universelles System zu schaffen.

Ich bin mir zwar nicht sicher, ob ich das wirklich kann, aber ich kann Ihnen zumindest zeigen, wie Sie die erforderlichen Informationen finden, sodass Sie, wenn Sie den EA an Ihr lokales System anpassen müssen, über die Erfahrung und das Wissen verfügen, wie Sie das tun können. Selbst wenn das System die Modellierung ursprünglich nicht bewältigen kann, können Sie es an Ihre Bedürfnisse anpassen.

Eine Bemerkung: Die Daten können mit MQL5-Code erhalten werden, aber um die Arbeit zu vereinfachen, werde ich MetaTrader 5 verwenden, um zu zeigen, wo die Daten sind.


1.0.1. Die Unternehmensvermögen (Aktien), die auf B3 gehandelt werden

Schauen Sie sich die beiden folgenden Bilder an:

     

Die hervorgehobenen Teile zeigen die Daten, die wir benötigen, um zu berechnen, wie die Position platziert werden soll.

Wenn Sie eine Position auf der Grundlage eines finanziellen Wertes erstellen, benötigen Sie diese Werte, um Stop-Loss und Take-Profit zu berechnen. Wenn Sie keine OCO-Aufträge verwenden, ist es noch einfacher, aber hier gehen wir davon aus, dass alle Aufträge OCO-Aufträge oder Positionen sind.

Wichtig: Wenn ein Vermögenswert in Form von Bruchteilen gehandelt wird (es gibt Unterschiede zwischen den Märkten auf B3), beträgt das Mindestvolumen 1 % des angegebenen Wertes, d.h. anstelle von 100 zu 100 wird 1 zu 1 gehandelt. Die Berechnung wird anders ausfallen, also vergessen Sie es nicht.


1.0.2. Terminkontrakte (Futures) in B3

Die Regeln für Futures-Kontrakte unterscheiden sich von den Regeln für den Aktienhandel, das Volumen ändert sich, je nachdem, ob Sie mit einem vollen Kontrakt oder einem Minikontrakt handeln, und sogar von einem Vermögenswert zum anderen. Zum Beispiel, um BOI zu handeln, sollten wir schauen, wie der Hebel (Multiplikator) gefüllt ist, da es von dem ist, was wir hier tun werden. Ich werde mich auf die Verträge konzentrieren. In manchen Fällen entspricht ein voller Kontrakt 25 Mini-Kontrakten, aber das kann variieren, so dass Sie immer die Volumenregeln der Börse überprüfen sollten.

Werfen wir nun einen Blick auf die folgenden Bilder des Mini-Dollars:

     

Diese Kontrakte haben ein Verfallsdatum, aber für diesen Artikel ist das nicht wichtig, denn im Artikel Entwicklung eines Trading Expert Advisors von Grund auf (Teil 11) haben wir uns überlegt, wie man ein Cross-Order-System erstellt, um Futures-Kontrakte direkt aus der Historie zu handeln, und wenn Sie diese Methode verwenden, müssen Sie sich nicht darum kümmern, welcher Kontrakt gerade gehandelt wird, da der EA das für Sie übernimmt. Achten Sie auf die markierten Punkte; sie unterscheiden sich von dem serienmäßigen System. Dies kann manchmal zu Problemen führen, aber der EA hat dieses Problem in den Griff bekommen, obwohl dieser Teil nicht sehr intuitiv ist. Im Laufe der Zeit werden Sie sich jedoch an die richtigen Werte gewöhnen, die für die Hebelwirkung beim Handel mit bestimmten Finanzvolumen verwendet werden sollten.

Ich habe jedoch beschlossen, mir ein höheres Ziel zu setzen. Hier beginnen die Schwierigkeiten.


1.0.3. FOREX

Mein Problem hing mit dem Forex-System zusammen, und mein EA konnte es nicht lösen. Als ich es schaffte, Forex Leverage zu handhaben, fand ich heraus, dass der gleiche Code aus dem B3 EA nicht verwendet werden konnte. Das hat mich gestört, denn ich sehe nicht ein, warum man zwei EAs haben sollte, die sich nur in diesem Teil unterscheiden. Um zu verstehen, was hier vor sich geht, sehen Sie sich die folgenden Bilder an:

     

In B3 haben wir vier Werte, während wir in Forex nur zwei Werte haben. Die Berechnungen zwischen den beiden Märkten stimmten aufgrund der beiden fehlenden Werte nicht überein.

Dies führte dazu, dass die vom EA durchgeführten Berechnungen nur schwer nachvollziehbar waren: Manchmal waren die berechneten Werte zu hoch, manchmal führte der Multiplikator dazu, dass das Handelssystem den Auftrag ablehnte, weil das Volumen im Vergleich zum erwarteten Mindestvolumen zu gering war, und manchmal wurden OCO-Limitpunkte aufgrund falscher Positionen nicht akzeptiert. Es herrschte also eine endlose Verwirrung.

Nachdem ich dies verstanden hatte, beschloss ich, den EA zu ändern. Wir werden den Wert also nicht auf die bisherige Weise berechnen, sondern eine andere Methode anwenden. In der Tat wird dieser Wert nun in Abhängigkeit von dem Vermögenswert, mit dem wir arbeiten, angepasst, unabhängig davon, ob wir es auf dem Aktienmarkt oder auf dem Devisenmarkt tun. So passt sich der EA an die Daten an und führt die Berechnungen korrekt durch. Wir müssen jedoch wissen, womit wir handeln, um den Multiplikator richtig einzustellen. Und obwohl wir jetzt aus rechnerischen Gründen die Verwendung von Fließkommazahlen im Volumen erlauben, sollten Sie dies vermeiden. Vielmehr sollten Sie ganzzahlige Werte verwenden, die die verwendete Hebelwirkung angeben.

Wir sagen dem EA also nicht, wie viel wir handeln wollen. Stattdessen geben wir den Multiplikator an, der auf das zulässige Mindestvolumen anzuwenden ist. So habe ich das Problem gelöst. Sie können es selbst ausprobieren und sich davon überzeugen, wie es funktioniert.

Um das Verständnis zu erleichtern und die Arbeit zu reduzieren, stelle ich im nächsten Abschnitt die Ergebnisse kurz vor.


2.0. Visualisierung von Daten

Betrachten wir zunächst den Fall des Aktienmarktes, insbesondere des brasilianischen Aktienmarktes (B3). So werden die Daten in der EA unter 5. dargestellt.


2.0.1. B3 Vermögenswerte

Im Falle einer an der brasilianischen Börse notierten Unternehmensaktie beträgt das Mindesthandelsvolumen 100. Durch die Angabe von 1 stellen wir den EA so ein, dass er das minimal zulässige Volumen handelt. Auf diese Weise ist es viel einfacher, mit dem Volumen zu arbeiten - es besteht keine Notwendigkeit, das Mindestlos genau zu kennen, wir verwenden einfach den Multiplikator, und der EA wird die notwendigen Berechnungen durchführen, um einen korrekten Auftrag zu erstellen.

Wenn Sie Bruchzahlen verwenden, geben Sie einfach die Anzahl der zu verwendenden Bruchzahl an, d. h. 50 bedeutet, dass Sie 50 mal die Bruchzahl verwenden, wenn Sie 15 angeben, werden 15 mal die Bruchzahl verwendet usw.

Das Ergebnis wird unten im Fenster der MetaTrader 5 Toolbox angezeigt. Sie zeigt, wie ein Auftrag mit dem Mindestlosgröße erstellt wurde. Wenn Sie die Werte von Take-Profit und Stopp-Loss analysieren, werden Sie feststellen, dass sie mit den im Chart angezeigten Werten übereinstimmen, was bedeutet, dass der EA hier funktioniert hat.


2.0.2. Mini-Dollar

Hier ist das Volumen 1 zu 1, aber im Falle von Vollverträgen ist der Wert anders. Werfen wir einen Blick auf den Chart Trade: Die als Take-Profit und Stopp-Loss angegebenen Werte sind die gleichen wie die oben genannten. Aber der EA passt die Werte korrekt an. Die Werte, die berücksichtigt werden sollten, sind also die Werte des Chart-Indikators, die Handelswerte des Chart sollten ignoriert werden, aber sie werden nahe an den im Chart angezeigten Werten liegen.


Die folgenden Daten werden im Bereich Werkzeuge (Toolbox) angezeigt:

Genau wie beim vorherigen Vermögenswert, wenn wir die Berechnung ausführen, um die Stop-Loss- und Take-Profit-Levels zu überprüfen, werden wir sehen, dass sie mit denen übereinstimmen, die vom EA festgelegt wurden, d.h. der EA hat auch diese Phase bestanden, ohne dass der Code neu kompiliert werden musste, nur indem der Vermögenswert geändert wurde.


2.0.3. Forex

Dieser Teil ist ein wenig komplizierter. Für diejenigen, die nicht mit Forex vertraut sind, sind die Punkte ziemlich seltsam, aber dennoch schafft es der EA, sie herauszufinden.


MetaTrader 5 informiert uns wie folgt über diesen schwebenden Auftrag:


Denken Sie daran, dass sich die Hebelwirkung im Forex-Bereich von den oben genannten Werten unterscheidet. Aber wenn Sie die Berechnungen durchführen, werden Sie sehen, dass der EA es geschafft hat, die richtigen Punkte zu liefern, und der Auftrag wurde perfekt erstellt.

All dies geschieht ohne zusätzliche Änderungen, außer denen, die ich im Implementierungsteil zeigen werde, obwohl viele andere Änderungen zusätzlich vorgenommen wurden, so dass der EA wirklich sowohl für den Aktienmarkt als auch für den Forex-Markt geeignet ist, ohne dass er neu kompiliert werden muss. Wir sind also dabei, frühere Artikel zu vervollständigen. Wir werden sehen, wie man Stop-Loss- und Take-Profit-Levels direkt auf dem Chart verschiebt.


3.0. Umsetzung

Schauen wir uns zunächst einige Änderungen im Code an.

Erstens habe ich das vor 3 oder 4 Versionen eingeführte Begrenzungssystem entfernt. Dies liegt daran, dass der EA manchmal die Losgrößenberechnung zwischen Aktienmärkten und Forex falsch anpasst.

Ich habe ein neues Berechnungsmodell hinzugefügt, damit der EA auf dem Devisen- und dem Aktienmarkt gleichermaßen gut funktioniert. Dies war vor dieser Version nicht möglich. Am Anfang war der EA auf die Arbeit an den Aktienmärkten ausgerichtet, aber ich beschloss, seine Funktionalität auf Forex zu erweitern, da sich die Handelsmethoden nicht sehr unterscheiden.

Es gibt Details in Bezug auf die Losgrößenberechnung, mit denen der EA in früheren Versionen nicht zurechtkam, aber mit den vorgenommenen Änderungen kann er nun sowohl auf dem Forex- als auch auf dem Aktienmarkt ohne größere Codeänderungen eingesetzt werden. Um jedoch die Kompatibilität mit den Devisen- und Aktienmärkten zu gewährleisten, musste ich mehrere Änderungen vornehmen.

Eine dieser Änderungen findet sich ganz am Anfang des Codes:

int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(user20 * Terminal.GetVolumeMinimal(), user21, user22, user23);
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        TradeView.Initilize();
                
        OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}

Der hervorgehobene Teil war vorher nicht vorhanden. Es gibt auch viele andere Änderungen. Wir werden uns jedoch nicht auf diese Umsetzung konzentrieren. Stattdessen werden wir sehen, wie man den EA verwendet, um Take-Profit und Stopp-Loss auf dem Chart direkt zu verschieben, ohne andere Tricks anzuwenden. Lassen Sie uns also zu diesem Teil übergehen.


3.0.1 Aufträge direkt auf dem Chart verschieben

Dieser Teil war in früheren Versionen sehr schwierig, da es eine Reihe von ungelösten Problemen gab, die im Laufe der Zeit auftraten und die Aufgabe erschwerten. Einer der Gründe dafür war, dass der Code sehr spärlich war, was es schwierig machte, das System der Auftragsverschiebung direkt auf dem Chart zu implementieren. Ursprünglich sollte das System dies nicht tun.

Um Ihnen eine Vorstellung vom Ausmaß der Änderungen zu geben, die der EA benötigte (um Orderbewegungen, einschließlich schwebender Orders und Positionen, verarbeiten zu können, sodass Sie Limit-Order-Bewegungen auf dem Chart mit der Maus steuern können), sehen wir uns an, wie der EA vorher aussah.

Das Problem ist, dass die Objekte bei ihrer Erstellung die Klasse C_HLineTrade ersetzt haben. Dies wurde in dem Artikel Entwicklung eines Handels EA von Grund auf (Teil 20) getan. Das System hat jetzt eine viel komplexere Struktur, und um nicht wieder das ganze Bild oben zu zeigen, werden wir nur betrachten, was passiert ist.

Der Pfeil zeigt auf den Verbindungspunkt, an dem die Klasse C_HLineTrade entfernt wurde, um Platz für neue Klassen zu schaffen. Dies ermöglichte weitere Implementierungen, die wir in früheren Artikeln durchgeführt haben. Aber das Vorhandensein der Klasse C_OrderView störte die Entwicklung, sodass wir sie schließlich ausschließen mussten. Aber das ist noch nicht alles. Die Klasse C_TradeGraphics wurde mit der alten Klasse C_OrderView zusammengelegt, und es entstand eine neue Klasse namens C_IndicatorTradeView. Diese Klasse ersetzte also zwei Klassen und ermöglichte es uns, das System der Auftragsbewegung zu entwickeln.

Was ich hier vorstellen werde, ist die erste Version dieses Systems. Es gibt noch eine weitere Version, die derzeit entwickelt wird, die aber in einem anderen Artikel vorgestellt werden soll.


3.0.1.1 - Schreiben des Codes für das neue System


Nach der Zusammenführung hat das neue System die folgende Konfiguration:


Der grüne Bereich zeigt eine Reihe von Klassen an, die frei sind, d.h. sie werden vom MetaTrader 5 und nicht vom EA verwaltet. Aber wie wird das gemacht? Betrachten wir den Prozess im Detail. Tatsächlich erstellt, platziert und löscht der EA nur Klassen und alle Objekte, die von diesen Klassen erstellt wurden. Schauen Sie sich den EA-Code an, und Sie werden keine Strukturen oder Variablen finden, die sich auf die im grünen Bereich erstellten Objekte beziehen. Dies ermöglicht die Erstellung einer unbegrenzten Anzahl von Objekten, solange MetaTrader 5 im Betriebssystem Speicher zuweisen kann. Die Anzahl der Objekte ist nicht durch Strukturen oder Variablen innerhalb des EA begrenzt.

Man könnte meinen, dass nur eine verrückte Person eine solche Struktur schaffen kann. Also gut, Sie können mich für verrückt erklären, denn ich habe es entwickelt, und es funktioniert. Außerdem wird das System dadurch überraschenderweise nicht überlastet. Die Leute nennen mich verrückt, also ist es keine große Sache... Lassen Sie uns weitergehen. Möglicherweise bemerken Sie eine gewisse Trägheit beim Verschieben von schwebenden oder Limit-Aufträgen. Dies liegt nicht an einem Code-Fehler oder einem Problem mit Ihrem Computer oder MetaTrader 5. Das Problem besteht darin, dass das System ausstehende Aufträge oder Limits verschiebt, indem es sie auf dem Handelsserver selbst verschiebt, und es gibt eine Latenzzeit zwischen der Verschiebung und der Antwort des Servers. Aber das ist die beste und sicherste Art, in einigen Szenarien zu arbeiten, die es uns jedoch nicht erlaubt, etwas anderes zu tun, was ich mit dem EA tun möchte. In den nächsten Artikeln werden wir dies beheben, indem wir dem EA eine neue Funktion hinzufügen und das System flüssiger machen, ohne jedoch die obige Struktur zu verändern. Wir werden nur den Code richtig manipulieren, auch wenn dies das System weniger sicher macht, aber bis dahin, wer weiß, vielleicht finde ich eine gute Lösung für dieses Problem.

Werfen wir einen Blick auf einige wichtige Punkte im aktuellen neuen Gesetzbuch. Wir beginnen mit einer wenig erforschten Funktion, deren Code wie folgt aussieht:

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
#define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY))

        ulong ticket;
        
        if (trans.symbol == Terminal.GetSymbol()) switch (trans.type)
        {
                case TRADE_TRANSACTION_DEAL_ADD:
                case TRADE_TRANSACTION_ORDER_ADD:
                        ticket = trans.order;
                        ticket = (ticket == 0 ? trans.position : ticket);
                        TradeView.IndicatorInfosAdd(ticket);
                        TradeView.UpdateInfosIndicators(0, ticket, trans.price, trans.price_tp, trans.price_sl, trans.volume, (trans.position > 0 ? trans.deal_type == DEAL_TYPE_BUY : def_IsBuy(trans.order_type)));
                        break;
                case TRADE_TRANSACTION_ORDER_DELETE:
                                if (trans.order != trans.position) TradeView.RemoveIndicator(trans.order);
                                else
                                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                                if (!PositionSelectByTicket(trans.position))
                                        TradeView.RemoveIndicator(trans.position);
                        break;
                case TRADE_TRANSACTION_ORDER_UPDATE:
                        TradeView.UpdateInfosIndicators(0, trans.order, trans.price, trans.price_tp, trans.price_sl, trans.volume, def_IsBuy(trans.order_type));
                        break;
                case TRADE_TRANSACTION_POSITION:
                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                        break;
        }
        
        
#undef def_IsBuy
}

Dieser Code ist sehr interessant, weil er uns erspart, jede neue Position, die auftaucht oder geändert wird, zu überprüfen. Eigentlich informiert uns der Server selbst über die Geschehnisse, sodass wir nur dafür sorgen müssen, dass der EA korrekt auf Ereignisse reagiert. Studieren Sie diese Art der Kodierung und die Verwendung des Ereignisses OnTradeTransaction gut, denn wenn ich das Modell zur Analyse der Dinge auf die gleiche Weise wie in früheren Versionen verwenden würde, würden wir viel Zeit mit den Prüfungen verbringen. In diesem Fall übernimmt der Server die ganze Arbeit für uns, und wir können sicher sein, dass die Werte auf dem Chart wirklich das zeigen, was der Server im Moment sieht.

Bevor wir zu den Höhepunkten des obigen Codes kommen, werfen wir einen Blick auf einen anderen Ausschnitt.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        TradeView.DispatchMessage(id, lparam, dparam, sparam);
        ChartRedraw();
}

Es wird also alles an einem Ort erledigt. Wir können in die Klasse gehen und sehen, was dort vor sich geht.


3.1. Die Klasse C_IndicatorTradeView

Diese Klasse wird zur Darstellung und Bearbeitung von Daten verwendet. Sie umfasst im Wesentlichen die alten Klassen C_OrderView und C_TradeGraphics, wie bereits erwähnt. Aber es manipuliert Daten auf eine ganz andere Art und Weise. Werfen wir einen Blick auf einige Punkte in dieser Klasse.

Wir beginnen mit der Initialisierungsfunktion, deren Code wie folgt aussieht:

void Initilize(void)
{
        int orders = OrdersTotal();
        ulong ticket;
        bool isBuy;
        long info;
        double tp, sl;

        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_OBJECT_DESCR, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_DRAG_TRADE_LEVELS, false);
        for (int c0 = 0; c0 <= orders; c0++) if ((ticket = OrderGetTicket(c0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol())
        {
                info = OrderGetInteger(ORDER_TYPE);
                isBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(-1, ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), OrderGetDouble(ORDER_SL), OrderGetDouble(ORDER_VOLUME_CURRENT), isBuy);
        }
        orders = PositionsTotal();
        for (int c0 = 0; c0 <= orders; c0++) if (PositionGetSymbol(c0) == Terminal.GetSymbol())
        {
                tp = PositionGetDouble(POSITION_TP);
                sl = PositionGetDouble(POSITION_SL);
                ticket = PositionGetInteger(POSITION_TICKET);
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(1, ticket, PositionGetDouble(POSITION_PRICE_OPEN), tp, sl, PositionGetDouble(POSITION_VOLUME), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
        }
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PENDING);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP);
}

Im Wesentlichen geht es darum, die erforderlichen Indikatoren zu erstellen und alles darzustellen, was derzeit auf dem Konto vorhanden ist, z. B. Positionen oder ausstehende Aufträge. Aber die hervorgehobenen Linien sind hier wichtig, denn wenn Sie kein Cross-Order-System verwenden, haben Sie auf dem Chart die Orderpunkte (aus MetaTrader 5), und wenn Sie auf diese Punkte klicken und sie ziehen, wird der EA die neuen Punkte mit den Änderungen in den Indikatoren aktualisieren. Das stört zwar nicht allzu sehr, aber wir müssen das System, das wir entwickeln, auch tatsächlich nutzen, sonst hat es keinen Sinn, es zu entwickeln.

Bitte beachten Sie die folgenden Hinweise.

void UpdateInfosIndicators(char test, ulong ticket, double pr, double tp, double sl, double vol, bool isBuy)
{
        bool isPending;
                                
        isPending = (test > 0 ? false : (test < 0 ? true : (ticket == def_IndicatorTicket0 ? true : OrderSelect(ticket))));
        PositionAxlePrice(ticket, (isPending ? IT_RESULT : IT_PENDING), 0);
        PositionAxlePrice(ticket, (isPending ? IT_PENDING : IT_RESULT), pr);
        SetTextValue(ticket, (isPending ? IT_PENDING : IT_RESULT), vol);
        PositionAxlePrice(ticket, IT_TAKE, tp);
        PositionAxlePrice(ticket, IT_STOP, sl);
        SetTextValue(ticket, IT_TAKE, vol, (isBuy ? tp - pr : pr - tp));
        SetTextValue(ticket, IT_STOP, vol, (isBuy ? sl - pr : pr - sl));
}

Es empfängt und aktualisiert die Daten und stellt die korrekten Werte in Bezug auf die finanziellen Werte und die Orte, an denen sich die Aufträge befinden, dar. Im Grunde genommen wollen wir uns nicht darum kümmern, ob es einen schwebenden Auftrag oder eine Position gibt - die Funktion wird sie so positionieren, dass wir sie auf dem Chart richtig sehen können.

Hier ist die nächste Funktion.

inline double SecureChannelPosition(void)
{
        double Res = 0, sl, profit, bid, ask;
        ulong ticket;
                                
        bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID);
        ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                ticket = PositionGetInteger(POSITION_TICKET);
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (ask < sl) ClosePosition(ticket);
                }else
                {
                        if ((bid > sl) && (sl > 0)) ClosePosition(ticket);
                }
                Res += profit;
        }
        return Res;
};

Sie wird durch das Ereignis OnTick aufgerufen und ist daher im Hinblick auf Geschwindigkeit und Systembelastung recht kritisch. Abgesehen von den Prüfungen wird lediglich der Wert im Chart aktualisiert, was durch den hervorgehobenen Code umgesetzt wird. Bitte beachten Sie, dass die Ticketnummer der Position hier sehr wichtig ist.

Schauen wir uns die oben hervorgehobene Funktion genauer an.

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;
                                
        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0 / Terminal.GetVolumeMinimal(), def_ColorVolumeEdit);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        break;
        }
}

So werden die richtigen Werte angezeigt. Die Frage ist jedoch, wie man sie bewegen kann. Dies geschieht durch drei weitere Code-Teile. Natürlich können Sie diese vermeiden und das MetaTrader 5-System selbst verwenden, das viel schneller ist als das aktuelle EA-System. Aber, wie gesagt, ich bevorzuge den EA, da er bald weitere Verbesserungen erhalten wird.

Die erste Funktion, die für das Verschieben verantwortlich ist, ist unten zu sehen, aber sie zeigt nur die Fragmente, die notwendig sind, um Punkte zu verschieben, entweder die Grenzen oder den Auftrag selbst, da der gesamte Code viel umfangreicher ist und nicht benötigt wird, um zu verstehen, wie man mit Mausbewegungen verschiebt.

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;

// ... Code ....

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse click 
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT press 
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL press 
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        Mouse.Hide();
                                        bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange);
                                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                                        bMounting = true;
                                }
                                tp = price + (bKeyBuy ? valueTp : (-valueTp));
                                sl = price + (bKeyBuy ? (-valueSl) : valueSl);
                                UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy);
                                if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT);
                        }else if (bMounting)
                        {
                                UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false);
                                Mouse.Show();
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell))
                        {
                                if (bEClick)
                                {
                                        bIsMove = false;
                                        m_TradeLine.SpotLight();
                                }
                                MoveSelection(price, mKeys);
                        }
                        break;

// ... Code ...
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {

// ... Code ...

                                case EV_MOVE:
                                        if (bIsMove)
                                        {
                                                m_TradeLine.SpotLight();
                                                bIsMove = false;
                                        }else
                                        {
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                                bIsMove = true;
                                        }
                                        break;
                        }
                        break;
        }
}

Versuchen wir zu verstehen, was hier los ist. Am Ende des Artikels finden Sie ein Video, das zeigt, wie es geht und was tatsächlich passiert. Aber lassen Sie uns zunächst versuchen, das herauszufinden.

Jede Angabe hat ein Objekt, das ihre Auswahl ermöglicht (mit Ausnahme des Ergebnisses, das nicht verschoben werden kann). Ein Klick auf diesen Punkt verändert die Anzeigelinie - sie wird dicker. Wenn dies geschieht, werden die Mausbewegungen erfasst und in eine neue Position für dieses Objekt umgewandelt, bis wir einen neuen Klick außerhalb des Auswahlobjekts machen, der die Bewegung des Objekts ermöglicht. Beachten Sie, dass Sie die Maustaste nicht gedrückt halten müssen, sondern nur einmal klicken, ziehen und dann erneut klicken.

Aber in Wirklichkeit wird hier nur ein Teil der Arbeit erledigt. Es gibt zwei weitere Funktionen, die uns helfen. Eine wurde bereits oben gesehen, die für die Anzeige der berechneten Werte verantwortlich ist; die nächste ist für den Stein verantwortlich, der den EA wie eine Schnecke aussehen lässt, wenn er das Order- oder Limit-Level-Bewegungssystem verwendet: Sie ist unten dargestellt:

void MoveSelection(double price, uint keys)
{
        static string memStr = NULL;
        static ulong ticket = 0;
        static eIndicatorTrade it;
        eEventType ev;
        double tp, sl, pr;
        bool isPending;
                                
        string sz0 = m_TradeLine.GetObjectSelected();
        
        if (sz0 != NULL)
        {
                if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev);
                isPending = OrderSelect(ticket);
                switch (it)
                {
                        case IT_TAKE:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP));
                                else ModifyPosition(ticket, price, macroGetPrice(IT_STOP));
                                break;
                        case IT_STOP:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price);
                                else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price);
                                break;
                        case IT_PENDING:
                                pr = macroGetPrice(IT_PENDING);
                                tp = macroGetPrice(IT_TAKE);
                                sl = macroGetPrice(IT_STOP);
                                ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr));
                                break;
                }
        };
}

Ich nenne die Funktion einen Stein, weil sie verantwortlich ist für wodurch das Ortungssystem langsam wird. Wenn Sie etwas nicht verstehen, sehen Sie sich die hervorgehobenen Punkte an. Jede von ihnen ist eine Funktion, die sich in der Klasse C_Router befindet und eine Anfrage an den Handelsserver sendet. Wenn der Server aus dem einen oder anderen Grund eine Weile braucht, um zu antworten (und das wird aufgrund der Latenz immer passieren), wird das Ortungssystem mehr oder weniger langsam sein, aber wenn der Server schnell antwortet, wird das System flüssig sein, oder besser gesagt, die Sache wird reibungsloser laufen. Später werden wir dies ändern, denn dieses System erlaubt es uns nicht, etwas anderes zu tun. Auf jeden Fall müssen Sie bedenken, dass Sie auf diese Weise ein wenig sicherer agieren, vor allem wenn Sie in sehr volatilen Märkten agieren, in denen sich die Preise sehr schnell bewegen können, aber auch dann besteht die Gefahr, dass die Limits springen. Es gibt keinen Weg, etwas muss geopfert werden. Für diejenigen, die sich bereit erklären, in dem Wissen zu arbeiten, dass genau das auf dem Server sein wird, ist der EA an diesem Punkt bereit. Aber für diejenigen, die die Funktionsweise des EA flüssiger gestalten wollen, auch wenn sie nicht punktgenau sind, werden sich die Dinge in den nächsten Artikeln ändern und interessanter werden.

Das folgende Video zeigt, wie das System tatsächlich funktioniert, achten Sie also auf die Werte ​auf dem Chart und in Werkzeuge (Toolbox).


Schlussfolgerung

Obwohl wir jetzt einen sehr interessanten Expert Advisor für den Handel haben, rate ich Ihnen, ihn eine Zeit lang auf einem Demokonto zu verwenden, um sich an seine Funktionsweise zu gewöhnen. Ich verspreche, dass es keine weiteren großen Änderungen an der Funktionsweise geben wird. Es wird nur Verbesserungen geben, und im nächsten Artikel werden wir einige Dinge hinzufügen, die diesem EA fehlen, auf Kosten der Sicherheit, die er bietet. Wie auch immer, dies wird eine großartige Quelle sein, um zu lernen, wie das Handelssystem funktioniert und wie man die Plattform manipuliert, um jede Art von Datenmodellierung zu erhalten, die wir brauchen.

Vergessen Sie nicht, wenn Sie feststellen, dass das Verschieben von Aufträgen oder Limits zu langsam ist, können Sie die Punkte, die ich im Artikel gezeigt habe, entfernen und MetaTrader 5 selbst verwenden, um Aufträge oder Limits zu verschieben und einen EA als Unterstützung bei der Interpretation der Daten zu verwenden. Ob Sie es tun oder nicht, ist Ihre Entscheidung...


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

Beigefügte Dateien |
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 23): Neues Auftragssystems (VI) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 23): Neues Auftragssystems (VI)
Wir werden das Auftragssystem flexibler gestalten. Hier werden wir Änderungen am Code in Erwägung ziehen, die ihn flexibler machen, sodass wir die Positionsstopp-Levels viel schneller ändern können.
Matrix- und Vektoroperationen in MQL5 Matrix- und Vektoroperationen in MQL5
Matrizen und Vektoren wurden in MQL5 für effiziente Operationen mit mathematischen Berechnungen eingeführt. Die neuen Typen bieten integrierte Methoden zur Erstellung von prägnantem und verständlichem Code, der der mathematischen Notation nahe kommt. Arrays bieten umfangreiche Möglichkeiten, aber es gibt viele Fälle, in denen Matrizen viel effizienter sind.
Experimente mit neuronalen Netzen (Teil 2): Intelligente Optimierung neuronaler Netze Experimente mit neuronalen Netzen (Teil 2): Intelligente Optimierung neuronaler Netze
In diesem Artikel werde ich mit Hilfe von Experimenten und unkonventionellen Ansätzen ein profitables Handelssystem entwickeln und prüfen, ob neuronale Netze für Händler eine Hilfe sein können. Der MetaTrader 5 als ein autarkes Tool für den Einsatz neuronaler Netze im Handel.
Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 03): Shannonsche Entropie Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 03): Shannonsche Entropie
Der Händler von heute ist ein Philomath, der fast immer (entweder bewusst oder unbewusst...) nach neuen Ideen sucht, sie ausprobiert, sich entscheidet, sie zu modifizieren oder zu verwerfen; ein explorativer Prozess, der einiges an Sorgfalt kosten sollte. Diese Artikelserie wird vorschlagen, dass der MQL5-Assistent eine Hauptstütze für Händler sein sollte.