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

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

MetaTrader 5Beispiele | 1 November 2022, 09:26
179 0
Daniel Jose
Daniel Jose

Einführung

Im vorigen Artikel, Entwicklung eines Trading Expert Advisors von Grund auf (Teil 26), haben wir einen katastrophalen Fehler behoben, der im Auftragssystem bestand. Wir haben auch damit begonnen, Änderungen vorzunehmen, die den Betrieb des neuen Auftragssystems ermöglichen. Obwohl das System, das ursprünglich in dieser Artikelserie eingeführt wurde, recht interessant ist, hat es einen Fehler, der es unbrauchbar macht. Diese Schwachstelle wurde am Ende des vorherigen Artikels aufgezeigt. Der Grund dafür war, dass ich nicht wusste, wie man handelt, genauer gesagt, wie man eine Verfallszeit für einen Auftrag oder eine Position auswählt, zusätzlich zu anderen kleineren Problemen. Das System wurde so gestaltet, dass am Ende der Handelssitzung oder des laufenden Tages eine Order oder eine Position geschlossen werden sollte. Aber manchmal wollen wir längerfristige Geschäfte machen, da hilft es nicht, alles so zu lassen, wie es ist.

Deshalb zeige ich Ihnen in diesem Artikel eine Lösung. Wir werden sehen, wie das Auftragssystem intuitiver gestaltet werden kann, sodass jeder sofort und genau feststellen kann, worum es sich bei den einzelnen Aufträgen handelt, wie sie bearbeitet werden und welche Art von Bewegung zu erwarten ist.

Dieses System ist so interessant, einfach und intuitiv, dass man, wenn man es einmal in Aktion gesehen haben, nicht mehr ohne es arbeiten will. Was ich Ihnen in diesem Artikel zeige, ist nur eine der vielen Möglichkeiten, die Sie im Auftragssystem umsetzen können. Vielleicht werde ich später noch andere Dinge zeigen, aber das, was wir in diesem Artikel sehen werden, kann eine ausgezeichnete Grundlage für die Erstellung anderer nützlicher und interessanter Änderungen für Ihren speziellen Fall sein. Wie auch immer, ich versuche, alles in diesen Artikeln so allgemein wie möglich zu halten.


2.0. Das intuitive Modell

Bislang haben wir mit Aufträgen wie folgt gearbeitet:

Die Darstellungen von Take-Profit und Stop-Loss haben eine erkennbare Form, die recht intuitiv ist: Grün zeigt an, was auf unserem Konto verdient werden würde wird, und rot, was verloren gehen könnte. Es ist alles klar. Wenn wir, wie unten gezeigt, eine Stop-Darstellung haben, zeigt sie immer noch, dass die Aktivierung des Stop-Loss dazu führen würde, dass diese Summe unserem Konto addiert wird. Mit anderen Worten, die Stop-Order-Preise erfordern, zumindest im Moment, keine Änderung. Vielleicht möchten Sie in Zukunft etwas an ihnen ändern, aber im Moment sind sie durchaus brauchbar.

Diese Art der Verwendung von Stopp-Levels ist für jeden Händler recht einfach und intuitiv zu analysieren. Aber wir haben etwas, das nicht ganz klar ist. Der erste ist die Angabe des Einstiegspunkts für schwebende Aufträge.

Können wir wissen, ob es sich bei diesem schwebenden Auftrag um einen Kauf- oder Verkaufsauftrag handelt? Und noch etwas: Ist es möglich zu wissen, ob dieser schwebende Auftrag am Ende des Tages geschlossen wird oder eine längerfristige Position eröffnet wird? Es ist ganz einfach. Wenn wir nun bereits offene Positionen haben, sieht der Indikator wie folgt aus:

Und auch hier gibt es die gleichen Probleme wie bei den Indikatoren für schwebende Aufträge. Wenn Sie auf dem Chart diese Indikatoren sehen, können Sie nicht mit Sicherheit sagen, ob eine Position am Ende des Tages geschlossen wird oder ob sie länger existieren wird. Wenn er am Ende des Tages geschlossen wird, möchten Sie nicht, dass der Makler ihn zwangsweise stoppt, da Sie dafür bezahlen müssen. Und es ist nicht sehr vernünftig, Aufträge ohne Kriterien zu schließen, da selbst die MetaTrader-Toolbox diese Informationen nicht anzeigt, sodass es einfach großartig ist, sie über den Indikator auf dem Chart zu haben.

Daher müssen wir hier Änderungen vornehmen, insbesondere bei den Indikatoren, die den Einstiegspunkt der Position anzeigen, um besser zu verstehen, was passiert.


2.0.1. Hinzufügen neuer Informationen zu Indikatoren

Die einfachste Möglichkeit, neue Informationen hinzuzufügen, ohne viel Platz auf dem Chart zu beanspruchen, ist die Verwendung von Bitmaps, da sie leicht zu verstehen und ziemlich repräsentativ sind. Ohne zusätzlichen Code einzufügen, fügen wir also vier neue Bitmaps zum EA hinzu, die in der Klasse C_IndicatorTradeView zu sehen sind.

#define def_BtnClose            "Images\\NanoEA-SIMD\\Btn_Close.bmp"
#define def_BtnCheckEnabled     "Images\\NanoEA-SIMD\\CheckBoxEnabled.bmp"
#define def_BtnCheckDisabled    "Images\\NanoEA-SIMD\\CheckBoxDisabled.bmp"
#define def_BtnDayTrade         "Images\\NanoEA-SIMD\\Inf_DayTrade.bmp"
#define def_BtnSwing            "Images\\NanoEA-SIMD\\Inf_Swing.bmp"
#define def_BtnInfoBuy          "Images\\NanoEA-SIMD\\Inf_Buy.bmp"
#define def_BtnInfoSell         "Images\\NanoEA-SIMD\\Inf_Sell.bmp"
//+------------------------------------------------------------------+
#resource "\\" + def_BtnClose
#resource "\\" + def_BtnCheckEnabled
#resource "\\" + def_BtnCheckDisabled
#resource "\\" + def_BtnDayTrade
#resource "\\" + def_BtnSwing
#resource "\\" + def_BtnInfoBuy
#resource "\\" + def_BtnInfoSell

Darüber hinaus müssen wir nur zwei neue Objekte im Auftragssystem implementieren.

//+------------------------------------------------------------------+
enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PENDING, IT_RESULT};
enum eEventType {EV_NULL, EV_GROUND = 65, EV_LINE, EV_CLOSE, EV_EDIT, EV_PROFIT, EV_MOVE, EV_CHECK, EV_TYPE, EV_DS};
//+------------------------------------------------------------------+
C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;
C_Object_BtnBitMap      m_BtnClose,
                        m_BtnCheck,
                        m_BtnInfoType,
                        m_BtnInfo_DS;
C_Object_Edit           m_EditInfo1,
                        m_EditInfo2;
C_Object_Label          m_BtnMove;

Jedes Mal, wenn wir ein neues Objekt hinzufügen, müssen wir auch ein mit dem Objekt verknüpftes Ereignis (event) hinzufügen, um sicherzustellen, dass das Objekt einen eindeutigen Namen hat.

Jetzt kommt der interessanteste Teil der Programmierung. Als Erstes müssen wir uns um die Geister (ghosts) kümmern. Wir müssen sie aktualisieren, damit sie die darin enthaltenen Informationen behalten. Natürlich könnte man sie löschen, aber ich denke, es ist besser, die Basisdaten zu behalten. Schauen Sie sich die folgende Animation an:

#define macroSwapName(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorGhost, A, B));
                void CreateGhostIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                if (GetInfosTradeServer(m_Selection.ticket = ticket) != 0)
                                {
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                        macroSwapName(it, EV_LINE);
                                        macroSwapName(it, EV_GROUND);
                                        macroSwapName(it, EV_MOVE);
                                        macroSwapName(it, EV_EDIT);
                                        macroSwapName(it, EV_CLOSE);
                                        if (it == IT_PENDING)
                                        {
                                                macroSwapName(it, EV_CHECK);
                                                macroSwapName(it, EV_TYPE);
                                                macroSwapName(it, EV_DS);
                                        }
                                        m_TradeLine.SetColor(macroMountName(def_IndicatorGhost, it, EV_LINE), def_IndicatorGhostColor);
                                        m_BackGround.SetColor(macroMountName(def_IndicatorGhost, it, EV_GROUND), def_IndicatorGhostColor);
                                        m_BtnMove.SetColor(macroMountName(def_IndicatorGhost, it, EV_MOVE), def_IndicatorGhostColor);
                                        ObjectDelete(Terminal.Get_ID(), macroMountName(def_IndicatorGhost, it, EV_CLOSE));
                                        m_TradeLine.SpotLight();
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                                        m_Selection.it = it;
                                }else m_Selection.ticket = 0;
                        }
#undef macroSwapName

Die hervorgehobenen Zeilen übergeben die Objekte an den Geist, es ist etwas sehr Einfaches und Klares. Ein weiterer einfacher Code wandelt die Indikatoren von schwebend in fließend um.

#define macroSwapAtFloat(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorFloat, A, B));
                bool PendingAtFloat(ulong ticket)
                        {
                                eIndicatorTrade it;
                                
                                if (macroGetLinePrice(def_IndicatorFloat, IT_PENDING) > 0) return false;
                                macroSwapAtFloat(IT_PENDING, EV_CHECK);
                                macroSwapAtFloat(IT_PENDING, EV_TYPE);
                                macroSwapAtFloat(IT_PENDING, EV_DS);
                                for (char c0 = 0; c0 < 3; c0++)
                                {
                                        switch(c0)
                                        {
                                                case 0: it = IT_PENDING;        break;
                                                case 1: it = IT_STOP;           break;
                                                case 2: it = IT_TAKE;           break;
                                                default:
                                                        return false;
                                        }
                                        macroSwapAtFloat(it, EV_CLOSE);
                                        macroSwapAtFloat(it, EV_MOVE);
                                        macroSwapAtFloat(it, EV_EDIT);
                                        macroSwapAtFloat(it, EV_GROUND);
                                        macroSwapAtFloat(it, EV_LINE);
                                        m_EditInfo1.SetOnlyRead(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT), false);
                                }
                                return true;
                        }
#undef macroSwapAtFloat

Die hervorgehobenen Zeilen konvertieren die Objekte in den fließenden Indikator, sodass wir später die erforderlichen Aktionen durchführen können. Nun müssen wir einige Änderungen im Code vornehmen, der den Indikator erstellt. Das ist etwas, das Sie testen und anpassen können, bis es Ihnen gefällt. Im Wesentlichen wurden die Änderungen an den hervorgehobenen Punkten im nachstehenden Code vorgenommen:

#define macroCreateIndicator(A, B, C, D)        {                                                                               \
                m_TradeLine.Create(ticket, sz0 = macroMountName(ticket, A, EV_LINE), C);                                        \
                m_BackGround.Create(ticket, sz0 = macroMountName(ticket, A, EV_GROUND), B);                                     \
                m_BackGround.Size(sz0, (A == IT_RESULT ? 100 : (A == IT_PENDING ? 144 : 92)), (A == IT_RESULT ? 34 : 22));      \
                m_EditInfo1.Create(ticket, sz0 = macroMountName(ticket, A, EV_EDIT), D, 0.0);                                   \
                m_EditInfo1.Size(sz0, 60, 14);                                                                                  \
                if (A != IT_RESULT)     {                                                                                       \
                        m_BtnMove.Create(ticket, sz0 = macroMountName(ticket, A, EV_MOVE), "Wingdings", "u", 17, C);            \
                        m_BtnMove.Size(sz0, 21, 23);                                                                            \
                                        }else                   {                                                               \
                        m_EditInfo2.Create(ticket, sz0 = macroMountName(ticket, A, EV_PROFIT), clrNONE, 0.0);                   \
                        m_EditInfo2.Size(sz0, 60, 14);  }                                                                       \
                                                }
                                                                                                                
#define macroInfoBase(A)        {                                                                                               \
                m_BtnInfoType.Create(ticket, sz0 = macroMountName(ticket, A, EV_TYPE), def_BtnInfoBuy, def_BtnInfoSell);        \
                m_BtnInfoType.SetStateButton(sz0, m_Selection.bIsBuy);                                                          \
                m_BtnInfo_DS.Create(ticket, sz0 = macroMountName(ticket, A, EV_DS), def_BtnDayTrade, def_BtnSwing);             \
                m_BtnInfo_DS.SetStateButton(sz0, m_Selection.bIsDayTrade);                                                      \
                                }

                void CreateIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                string sz0;
                                
                                switch (it)
                                {
                                        case IT_TAKE    : macroCreateIndicator(it, clrForestGreen, clrDarkGreen, clrNONE); break;
                                        case IT_STOP    : macroCreateIndicator(it, clrFireBrick, clrMaroon, clrNONE); break;
                                        case IT_PENDING:
                                                macroCreateIndicator(it, clrCornflowerBlue, clrDarkGoldenrod, def_ColorVolumeEdit);
                                                m_BtnCheck.Create(ticket, sz0 = macroMountName(ticket, it, EV_CHECK), def_BtnCheckEnabled, def_BtnCheckDisabled);
                                                m_BtnCheck.SetStateButton(sz0, true);
                                                macroInfoBase(IT_PENDING);
                                                break;
                                        case IT_RESULT  :
                                                macroCreateIndicator(it, clrSlateBlue, clrSlateBlue, def_ColorVolumeResult);
                                                macroInfoBase(IT_RESULT);
                                                break;
                                }
                                m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose);
                        }
#undef macroInfoBase
#undef macroCreateIndicator

Beachten Sie, dass macroInfoBase die Objekte erstellt, die im Indikator verwendet werden, aber diese Objekte werden nur in den Indikatoren für die Positionseröffnung und das Positionsergebnis erstellt, während es nicht notwendig ist, diese Objekte in anderen Indikatoren zu erstellen. Beachten Sie aber, dass wir die Objekte nicht an der Stelle positionieren, an der wir sie erstellt haben. Dies geschieht an anderer Stelle, die im Folgenden dargestellt wird.

#define macroSetAxleY(A)                {                                                                               \
                m_BackGround.PositionAxleY(macroMountName(ticket, A, EV_GROUND), y);                                    \
                m_TradeLine.PositionAxleY(macroMountName(ticket, A, EV_LINE), y);                                       \
                m_BtnClose.PositionAxleY(macroMountName(ticket, A, EV_CLOSE), y);                                       \
                if (A != IT_RESULT)m_BtnMove.PositionAxleY(macroMountName(ticket, A, EV_MOVE), y, 1);                   \
                else m_EditInfo2.PositionAxleY(macroMountName(ticket, A, EV_PROFIT), y, 1);                             \
                m_EditInfo1.PositionAxleY(macroMountName(ticket, A, EV_EDIT), y, (A == IT_RESULT ? -1 : 0));            \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleY(macroMountName(ticket, A, EV_CHECK), y);                  \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                       \
                        m_BtnInfoType.PositionAxleY(macroMountName(ticket, A, EV_TYPE), y + (A == IT_PENDING ? 0 : 8)); \
                        m_BtnInfo_DS.PositionAxleY(macroMountName(ticket, A, EV_DS), y - (A == IT_PENDING ? 0: 8));     \
                                                                }                                                       \
                                        }
                                                                        
#define macroSetAxleX(A, B)             {                                                                                               \
                m_BackGround.PositionAxleX(macroMountName(ticket, A, EV_GROUND), B);                                                    \
                m_TradeLine.PositionAxleX(macroMountName(ticket, A, EV_LINE), B);                                                       \
                m_BtnClose.PositionAxleX(macroMountName(ticket, A, EV_CLOSE), B + 3);                                                   \
                m_EditInfo1.PositionAxleX(macroMountName(ticket, A, EV_EDIT), B + 21);                                                  \
                if (A != IT_RESULT) m_BtnMove.PositionAxleX(macroMountName(ticket, A, EV_MOVE), B + 80 + (A == IT_PENDING ? 52 : 0));   \
                else m_EditInfo2.PositionAxleX(macroMountName(ticket, A, EV_PROFIT), B + 21);                                           \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleX(macroMountName(ticket, A, EV_CHECK), B + 82);                             \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                                       \
                        m_BtnInfoType.PositionAxleX(macroMountName(ticket, A, EV_TYPE), B + (A == IT_PENDING ? 100 : 82));              \
                        m_BtnInfo_DS.PositionAxleX(macroMountName(ticket, A, EV_DS), B + (A == IT_PENDING ? 118 : 82));                 \
                                                                }                                                                       \
                                        }
//---
        void ReDrawAllsIndicator(void)
                        {
                                C_IndicatorTradeView::st00 Local;
                                int             max = ObjectsTotal(Terminal.Get_ID(), -1, OBJ_EDIT);
                                ulong           ticket;
                                eIndicatorTrade it;
                                eEventType ev;
                                
                                Local = m_Selection;
                                m_Selection.ticket = 0;
                                for (int c0 = 0; c0 <= max; c0++)
                                   if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, OBJ_EDIT), ticket, it, ev))
                                      if ((it == IT_PENDING) || (it == IT_RESULT))
                                      {
                                        PositionAxlePrice(ticket, IT_STOP, macroGetLinePrice(ticket, IT_STOP));
                                        PositionAxlePrice(ticket, IT_TAKE, macroGetLinePrice(ticket, IT_TAKE));
                                        PositionAxlePrice(ticket, it, macroGetLinePrice(ticket, it));
                                        }
                                m_Selection = Local;
                                ChartRedraw();
                        }
//---
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                macroSetLinePrice(ticket, it, price);
                                macroSetAxleY(it);
                                switch (it)
                                {
                                        case IT_TAKE: desl = 160; break;
                                        case IT_STOP: desl = 270; break;
                                        default: desl = 0;
                                }
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2));
                        }
#undef macroSetAxleX
#undef macroSetAxleY

Ich möchte auch betonen, dass ich keine drastischen und radikalen Änderungen am Code vornehmen möchte. Im Grunde wurden die einzigen Änderungen im obigen Code hervorgehoben.


2.0.2. Probleme in Sicht

Obwohl alles einwandfrei funktioniert, haben wir ein Problem. Ich habe die gesamte MQL5-Dokumentation durchsucht, aber ich habe keine Möglichkeit gefunden, das Problem auf einfache Weise zu lösen. Das Problem ist, woher man weiß, ob es sich bei einer kürzlich eröffneten Position um einen Day Trade (kurzfristige Position innerhalb desselben Tages) oder einen Swing Trade (längere Position) handelt? Im Falle einer älteren Position, die am Vortag eröffnet wurde, ist es recht einfach, diese Art von Analyse durchzuführen, da es ausreicht, den aktuellen Tag und den Tag der Positionseröffnung zu vergleichen: Wenn sie sich unterscheiden, ist die Position ein Swing Trade. Aber was ist, wenn der EA geschlossen ist und Sie ihn am selben Tag initiieren, an dem die Position eröffnet wurde? In diesem Fall lässt sich nicht feststellen, ob es sich bei einer Position um einen Day Trade oder einen Swing Trade handelt.

Dieses Problem besteht nicht für schwebende Aufträge, da es eine Möglichkeit gibt, dies zu überprüfen. Beim Aufruf von OrderGetInteger mit dem Parameter ORDER_TYPE_TIME wird ein Wert der Enumeration ENUM_ORDER_TYPE_TIME zurückgegeben, der angibt, ob es sich bei einem Auftrag um Day Trade oder Swing Trade handelt. Das Gleiche gilt jedoch nicht für Positionen.

Aus diesem Grund besteht meine Lösung für diesen Fall darin, dem Auftrag oder der Position etwas hinzuzufügen, um dem EA die Dauer der Operation mitzuteilen, unabhängig von allen anderen Informationen. Dies ist jedoch keine perfekte Lösung, da sie das Problem zwar in einigen, aber nicht in allen Fällen löst. Denn der Händler kann das vom EA verwendete System ändern, um zu erkennen, ob es sich bei einem Handel um einen Swing- oder Day-Trade handelt, bevor der für die Analyse erforderliche Zeitraum verstrichen ist.

Zum besseren Verständnis wollen wir uns ansehen, wie die Lösung umgesetzt wird.

inline char GetInfosTradeServer(ulong ticket)
{
        long info;
                                
        if (ticket == 0) return 0;
        if (OrderSelect(ticket))
        {
                if (OrderGetString(ORDER_SYMBOL) != Terminal.GetSymbol()) return 0;
                info = OrderGetInteger(ORDER_TYPE);
                m_Selection.bIsBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                m_Selection.pr = OrderGetDouble(ORDER_PRICE_OPEN);
                m_Selection.tp = OrderGetDouble(ORDER_TP);
                m_Selection.sl = OrderGetDouble(ORDER_SL);
                m_Selection.vol = OrderGetDouble(ORDER_VOLUME_CURRENT);
                m_Selection.bIsDayTrade = ((ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME) == ORDER_TIME_DAY);
                                        
                return -1;
        }
        if (PositionSelectByTicket(ticket))
        {
                if (PositionGetString(POSITION_SYMBOL) != Terminal.GetSymbol()) return 0;
                m_Selection.bIsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
                m_Selection.pr = PositionGetDouble(POSITION_PRICE_OPEN);
                m_Selection.tp = PositionGetDouble(POSITION_TP);
                m_Selection.sl = PositionGetDouble(POSITION_SL);
                m_Selection.vol = PositionGetDouble(POSITION_VOLUME);
                if (macroGetDate(PositionGetInteger(POSITION_TIME)) == macroGetDate(TimeTradeServer()))
                        m_Selection.bIsDayTrade = PositionGetString(POSITION_COMMENT) == def_COMMENT_TO_DAYTRADE;
                else m_Selection.bIsDayTrade = false;
                                        
                return 1;
        }
        return 0;
}

Wie bereits erwähnt, reicht es im Fall von schwebenden Aufträgen aus, OrderGetInteger aufzurufen und den benötigten Wert zu erhalten. Bei Positionen ist es etwas komplizierter. Es funktioniert wie folgt: Prüfen Sie den Tag der Positionseröffnung und den aktuellen Tag des Handelsservers. Wenn beide gleich sind, überprüfen Sie den Kommentar des Auftrags. Wenn der Kommentar die Zeichenkette angibt, die in der Klasse C_Router verwendet wird, um anzuzeigen, dass es sich bei der Eröffnung der Position um einen Day Trade handelt, wird der EA diese interpretieren und im Positionsindikator anzeigen. Der Kommentar darf aber erst am Ende des Tages geändert werden, denn wenn er sich ändert, kann es sein, dass der EA meldet, dass die Day-Trade-Position eigentlich ein Swing-Trade ist, was dann nicht am EA liegt, sondern daran, dass der Händler den Kommentar zu früh geändert hat.

Das ist der Nachteil dieser Lösung, aber wenn jemand eine Idee hat, wie man anhand der Positionsdaten feststellen kann, ob es sich um einen Day Trade handelt oder nicht, kann er sie gerne in den Kommentaren mitteilen.

Wie das im Falle von schwebenden Aufträgen aussieht, sehen Sie im folgenden Video:


Jetzt haben wir fast alles fertig, wir müssen nur noch einige Ergänzungen am Code vornehmen, damit der EA interessant wird.


2.0.3. Reagieren auf Nachrichten der Plattform

Unser gesamtes Auftragssystem basiert auf den vom MetaTrader 5 gesendeten Nachrichten, damit der EA weiß, was er tun oder lassen soll. Deshalb ist es so wichtig zu wissen, wie das Nachrichtensystem zu implementieren ist.

Der vollständige Code für die Nachrichtenübermittlung ist nachstehend aufgeführt:

#define macroGetDataIndicatorFloat      {                                                                                                               \
                m_Selection.vol = m_EditInfo1.GetTextValue(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT)) * Terminal.GetVolumeMinimal();      \
                m_Selection.bIsBuy = m_BtnInfoType.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_TYPE));                             \
                m_Selection.pr = macroGetLinePrice(def_IndicatorFloat, IT_PENDING);                                                                     \
                m_Selection.sl = macroGetLinePrice(def_IndicatorFloat, IT_STOP);                                                                        \
                m_Selection.tp = macroGetLinePrice(def_IndicatorFloat, IT_TAKE);                                                                        \
                m_Selection.bIsDayTrade = m_BtnInfo_DS.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_DS));                           \
                                        }
                                                                                                
                void DispatchMessage(int id, long lparam, double dparam, string sparam)
                        {
                                ulong   ticket;
                                double  price;
                                bool   	bKeyBuy,
                                        bKeySell,
                                        bEClick;
                                datetime dt;
                                uint     mKeys;
                                char     cRet;
                                eIndicatorTrade  it;
                                eEventType       ev;
                                
                                static bool bMounting = false;
                                static double valueTp = 0, valueSl = 0, memLocal = 0;
                                
                                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 pressed
                                                bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
                                                if (bKeyBuy != bKeySell)
                                                {
                                                        if (!bMounting)
                                                        {
                                                                m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
                                                                valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                                                valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                                                m_Selection.it = IT_PENDING;
                                                                m_Selection.pr = price;
                                                        }
                                                        m_Selection.tp = m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp));
                                                        m_Selection.sl = m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl);
                                                        m_Selection.bIsBuy = bKeyBuy;
                                                        m_BtnInfoType.SetStateButton(macroMountName(def_IndicatorTicket0, IT_PENDING, EV_TYPE), bKeyBuy);
                                                        if (!bMounting)
                                                        {
                                                                IndicatorAdd(m_Selection.ticket = def_IndicatorTicket0);
                                                                bMounting = true;
                                                        }
                                                        MoveSelection(price);
                                                        if ((bEClick) && (memLocal == 0)) SetPriceSelection(memLocal = price);
                                                }else if (bMounting)
                                                {
                                                        RemoveIndicator(def_IndicatorTicket0);
                                                        memLocal = 0;
                                                        bMounting = false;
                                                }else if ((!bMounting) && (bKeyBuy == bKeySell) && (m_Selection.ticket > def_IndicatorGhost))
                                                {
                                                        if (bEClick) SetPriceSelection(price); else MoveSelection(price);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_DELETE:
                                                if (GetIndicatorInfos(sparam, ticket, it, ev))
                                                {
                                                        if (GetInfosTradeServer(ticket) == 0) break;
                                                        CreateIndicator(ticket, it);
                                                        if ((it == IT_PENDING) || (it == IT_RESULT))
                                                                PositionAxlePrice(ticket, it, m_Selection.pr);
                                                        ChartRedraw();
                                                        m_TradeLine.SpotLight();
                                                        m_Selection.ticket = 0;
                                                        UpdateIndicators(ticket, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_ENDEDIT:
                                                macroGetDataIndicatorFloat;
                                                m_Selection.ticket = 0;
                                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                break;
                                        case CHARTEVENT_CHART_CHANGE:
                                                ReDrawAllsIndicator();
                                                break;
                                        case CHARTEVENT_OBJECT_CLICK:
                                                if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)
                                                {
                                                        case EV_TYPE:
                                                                if (ticket == def_IndicatorFloat)
                                                                {
                                                                        macroGetDataIndicatorFloat;
                                                                        m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1)));
                                                                        m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1)));
                                                                        m_Selection.ticket = 0;
                                                                        UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                                } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam));
                                                                break;
                                                        case EV_DS:
                                                                if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam));
                                                                break;
                                                        case EV_CLOSE:
                                                                if (ticket == def_IndicatorFloat) RemoveIndicator(def_IndicatorFloat, it);
                                                                else if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)
                                                                {
                                                                        case IT_PENDING:
                                                                        case IT_RESULT:
                                                                                if (cRet < 0) RemoveOrderPendent(ticket); else ClosePosition(ticket);
                                                                                break;
                                                                        case IT_TAKE:
                                                                        case IT_STOP:
                                                                                m_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                                SetPriceSelection(0);
                                                                                break;
                                                                }
                                                                break;
                                                        case EV_MOVE:
                                                                        if (ticket == def_IndicatorFloat)
                                                                        {
                                                                                macroGetDataIndicatorFloat;
                                                                                m_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                        }else   CreateGhostIndicator(ticket, it);
                                                                break;
                                                        case EV_CHECK:
                                                                if (ticket != def_IndicatorFloat)
                                                                {
                                                                        if (PendingAtFloat(ticket)) RemoveOrderPendent(ticket);
                                                                        else m_BtnCheck.SetStateButton(macroMountName(ticket, IT_PENDING, EV_CHECK), true);
                                                                } else
                                                                {
                                                                        macroGetDataIndicatorFloat;
                                                                        m_Selection.ticket = def_IndicatorTicket0;
                                                                        m_Selection.it = IT_PENDING;
                                                                        SetPriceSelection(m_Selection.pr);
                                                                        RemoveIndicator(def_IndicatorFloat);
                                                                }
                                                                break;
                                                }
                                                break;
                                }
                        }
#undef macroGetDataIndicatorFloat

Lassen Sie sich von diesem Code nicht abschrecken. Obwohl es groß und kompliziert aussieht, ist es eigentlich ganz einfach. Ich werde mich auf die hervorgehobenen Teile konzentrieren, um zu erklären, was im Code für die Nachrichtenverarbeitung neu ist.

Die erste Neuerung betrifft den Code für das Ereignis CHARTEVENT_OBJECT_ENDEDIT. Sie wird von MetaTrader 5 jedes Mal ausgelöst, wenn wir die Bearbeitung des Inhalts im EDIT-Objekt beenden. Was bewirkt es? Das ist sehr wichtig, denn wenn wir dieses Ereignis nicht verarbeiten und versuchen, die Daten der Stopp-Level-Indikatoren nach der Bearbeitung des Hebelwerts zu manipulieren, kommt es zu einer Fehlanpassung der Werte. Obwohl der EA den Wert zwingen wird, zu seinem ursprünglichen Wert zurückzukehren, aber wenn wir dieses Ereignis wie im Code gezeigt behandeln, wird dieses Problem nicht bestehen, und wir können reibungslos mit den Hebeldaten handeln. Denken Sie daran, dass Sie, wenn Sie den EA auffordern, diese Anpassungen vorzunehmen, tatsächlich prüfen sollten, ob es eine gute Idee ist, die Operation mit einem höheren oder niedrigeren Hebel einzugehen oder nicht. Auf diese Weise können Sie überprüfen, ohne ein Risiko einzugehen, da der EA den Auftrag nur dann an den Server sendet, wenn Sie ihn dazu auffordern, und dies ist der Fall, wenn das Kontrollkästchen aktiviert ist.

Schauen wir uns nun das Ereignis CHARTEVENT_OBJECT_CLICK genauer an. Dazu nehmen wir das im vorherigen Code hervorgehobene Fragment.

case CHARTEVENT_OBJECT_CLICK:
        if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)
        {
                case EV_TYPE:
                        if (ticket == def_IndicatorFloat)
                        {
                                macroGetDataIndicatorFloat;
                                m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1)));
                                m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1)));
                                m_Selection.ticket = 0;
                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                        } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam));
                        break;
                case EV_DS:
                        if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam));
                        break;

// ... Rest of the code...

Was macht dieser Code eigentlich? Haben Sie eine Ahnung? Nun, die Videos in diesem Artikel zeigen dies, aber können Sie verstehen, wie so etwas gemacht wird? Viele würden vermuten, dass es sich um einen äußerst komplexen Code handelt, aber er steht einfach da oben.

Es gibt zwei Dinge, die wir tun müssen. Erstens ändert sich der Status eines BitMap-Objekts, wenn es angeklickt wird, und wir müssen prüfen, ob sein Ticket von etwas stammt, das bereits auf dem Server existiert, oder ob es sich um etwas handelt, das sich nur auf dem Chart befindet. Dies geschieht über die grün hervorgehoben Punkte. Wenn das Ticket auf dem Server existiert, muss die Statusänderung rückgängig gemacht werden, dann wird der EA dies korrigieren, indem er die gewünschte Änderung vornimmt.

Werfen wir nun einen Blick auf den gelb hervorgehoben Abschnitt. Die Idee beruht auf folgendem Konzept: Warum sollte ich eine weitere Order auf dem Chart platzieren, wenn sie bereits auf dem Chart existiert und ich nur die Richtung umkehren möchte? Mit anderen Worten: Wenn es ein Kauf ist, sollte es jetzt Verkauf werden und umgekehrt. Das gelbe Fragment tut genau das: Wenn die BitMap, die dafür verantwortlich ist, ob wir kaufen oder verkaufen, angeklickt wird, ändert sich die Richtung automatisch. Ein Detail: Dies kann nur für einen schwebenden Auftrag geschehen; für Positionen, die sich bereits auf dem Server befinden, ist es verboten.

Mit all diesen Änderungen sehen die Indikatoren jetzt wie folgt aus:

Schwebenden Aufträge

 

Darstellung von Positionen:

Es ist jetzt viel einfacher festzustellen, was ein schwebender Auftrag oder eine offene Position macht, weil Sie die erwartete Bewegung oder die Lebensdauer der Position genau kennen. Der grüne Pfeil, der nach oben zeigt, steht für eine Kaufposition, der rote Pfeil, der nach unten zeigt, für eine Verkaufsposition. Der Buchstabe D steht für einen Day Trade, der am Ende des Tages geschlossen wird. Wenn es S ist, handelt es sich um einen Swing Trade, und das Geschäft wird nicht unbedingt am Ende des Tages geschlossen.

Das nächste Video zeigt, wie das neue Auftragssystem funktioniert. Ich habe mich auf schwebende Aufträge konzentriert, weil sie weiteren Änderungen unterliegen, während die Positionsindikatoren nicht geändert werden können. Sie zeigen nur die vom Server bereitgestellten Daten zur Position an. Schauen Sie sich die Funktionsweise genauer an, bevor Sie es auf einem Live-Konto ausprobieren, denn das System ist praktisch, aber Sie müssen sich mit ihm vertraut machen, um seine Funktionen optimal nutzen zu können.




Schlussfolgerung

Nun, unser Auftragssystem ist jetzt sehr vielseitig. Es kann mehrere Dinge tun und hilft uns sehr, aber es fehlt noch ein wichtiges Detail, das im nächsten Artikel implementiert wird. Also, bis bald...


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

Beigefügte Dateien |
Einen technischen Indikator selber machen Einen technischen Indikator selber machen
In diesem Artikel gehe ich auf die Algorithmen ein, mit denen Sie Ihren eigenen technischen Indikator erstellen können. Sie werden lernen, wie man mit sehr einfachen Ausgangsannahmen ziemlich komplexe und interessante Ergebnisse erzielen kann.
Marktmathematik: Gewinn, Verlust und Kosten Marktmathematik: Gewinn, Verlust und Kosten
In diesem Artikel zeige ich Ihnen, wie Sie den Gesamtgewinn oder -verlust eines Handels einschließlich Provision und Swap berechnen können. Ich werde das genaueste mathematische Modell zur Verfügung stellen und es verwenden, um den Code zu schreiben und ihn mit der Norm zu vergleichen. Außerdem werde ich versuchen, in die Hauptfunktion von MQL5 zur Berechnung des Gewinns einzudringen und alle erforderlichen Werte aus der Spezifikation zu ermitteln.
DoEasy. Steuerung (Teil 15): TabControl WinForms Objekt — mehrere Reihen von Registerkartenüberschriften, Methoden zur Behandlung von Registerkarten DoEasy. Steuerung (Teil 15): TabControl WinForms Objekt — mehrere Reihen von Registerkartenüberschriften, Methoden zur Behandlung von Registerkarten
In diesem Artikel werde ich die Arbeit am Objekt TabControl WinForm fortsetzen — ich werde eine Tabulatorfeld-Objektklasse erstellen, es möglich machen, Tabulatorüberschriften in mehreren Zeilen anzuordnen und Methoden für die Handhabung von Objekttabs hinzufügen.
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 26): Der Zukunft entgegen (I) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 26): Der Zukunft entgegen (I)
Heute werden wir unser Auftragssystem auf die nächste Stufe bringen. Aber vorher müssen wir noch einige Probleme lösen. Jetzt haben wir einige Fragen, die sich darauf beziehen, wie wir arbeiten wollen und welche Dinge wir während des Handelstages tun.