English Русский Español Português
preview
Marktsimulation: Positionsanzeige (VII)

Marktsimulation: Positionsanzeige (VII)

MetaTrader 5 — Tester |
22 0
Daniel Jose
Daniel Jose

Einleitung

Hallo zusammen und herzlich willkommen zu einem weiteren Artikel dieser Reihe über den Aufbau eines Wiedergabe-/Simulationssystems.

Im vorherigen Artikel „Marktsimulation: Positionsansicht (VI)“ haben wir gezeigt, wie man den Expert Advisor so programmiert, dass er dem Positionsindikator mitteilt, ob dieser aktualisiert werden soll oder – in den meisten Fällen – ob er auf dem Chart verbleiben oder entfernt werden soll. Was wir im vorherigen Artikel behandelt haben, ist zwar als Einstieg in die Entwicklung bestimmter Komponenten für MetaTrader 5 durchaus interessant, aber noch nicht besonders praxisnah. Das liegt daran, dass wir nicht direkt mit dem Positionsindikator interagieren können, um festzulegen, dass eine Preislinie – sei es eine Stop-Loss- oder eine Take-Profit-Linie – für diese Position verschoben oder zumindest entfernt werden soll.

Es gibt jedoch noch ein weiteres Thema, und es könnte sogar noch interessanter sein, sich damit gleich jetzt zu befassen: die Möglichkeit, eine Position zu schließen, indem man auf irgendeine Weise mit dem Positionsindikator interagiert. Dadurch entfällt die Notwendigkeit, das Terminalfenster ständig mit geöffneter Registerkarte „Handel“ anzuzeigen, was zwar beim Testen nützlich sein kann, aber überflüssig wird, sobald wir mit dem Positionsindikator interagieren können.

In diesem Artikel beginnen wir damit, den Positionsindikator zu verbessern, damit wir mit ihm interagieren und Preislinien ändern oder eine Position direkt über den Positionsindikator schließen können. Bevor wir zur Umsetzung übergehen, gibt es ein paar Dinge, die es zu klären gilt, insbesondere für diejenigen, die sich damit noch nicht auskennen. Unter keinen Umständen ist es möglich, mithilfe des Indikators etwas auf dem Handelsserver zu ändern. Das liegt daran, dass MetaTrader 5 über ein Sicherheitssystem verfügt, das es ausschließlich Expert Advisors erlaubt, Aufträge und Positionen zu ändern.

Obwohl MetaTrader 5 selbst in seiner Standardkonfiguration hierfür verwendet werden kann, kann keine andere Anwendung als ein Expert Advisor Aufträge oder Positionen ändern. Daher werden wir ab jetzt einen Kommunikationsmechanismus zwischen dem Positionsindikator und dem Expert Advisor einrichten, sodass es so aussieht, als würde der Positionsindikator die auf dem Server gehaltenen Aufträge oder Positionen beeinflussen. Aber wie ich gerade gesagt habe, ist das nur eine Illusion.

Wenn Sie sich dafür interessieren und das anwenden möchten, was ich gleich erkläre, sollten Sie wissen, dass Sie die Anwendungen so einrichten müssen, dass sie in MetaTrader 5 zusammenarbeiten. Glauben Sie nicht, dass eine einzige Anwendung alles leisten kann, was hier gezeigt wird, denn das ist unrealistisch – es ist lediglich eine Illusion, die wir erschaffen werden. Bei korrekter Umsetzung ermöglicht Ihnen diese Illusion, in MetaTrader 5 auf sehr angenehme und komfortable Weise zu arbeiten oder zu handeln. Kommen wir also zu dem, worauf es wirklich ankommt.


Eine Position über den Positionsindikator schließen

Bevor wir mit der Implementierung bestimmter Mechanismen beginnen, wollen wir zunächst eine einfache Absicherung einbauen. Denn selbst auf einem Demokonto möchten wir von Anfang an eine Methode zum Schließen einer Position implementieren, ohne auf andere Mechanismen als den Positionsindikator zurückzugreifen.

Das ist relativ einfach und leicht zu verstehen. In der Praxis müssen wir lediglich ein Objekt im Chart erstellen und es an einer bestimmten Stelle platzieren. Wenn auf dieses Objekt geklickt wird oder ein anderes Ereignis eintritt, erzeugt der Positionsindikator ein Ereignis. Der Expert Advisor wird dieses Ereignis abfangen. Im Expert Advisor werden wir die erforderlichen Mechanismen einrichten, damit dieses vom Positionsindikator stammende Ereignis dazu genutzt werden kann, eine Anfrage an den Server zu senden. Diese Anfrage dient dazu, die Position zu schließen, die der Positionsindikator im Chart anzeigt.

Auch wenn dies auf den ersten Blick kompliziert erscheinen mag, ist die Umsetzung tatsächlich relativ einfach – allerdings nur, wenn Sie diese Artikelserie zur Entwicklung eines Wiedergabe-/Simulationssystems verfolgen. Der Grund dafür ist, dass wir bereits zuvor etwas sehr Ähnliches gemacht haben, als wir den Indikator Chart Trade entwickelt haben. Wir werden nun einen Mechanismus erstellen, der dem des Indikators Chart Trade sehr ähnlich ist; es gibt jedoch einige geringfügige Unterschiede, die dazu führen, dass der Positionsindikator anders umgesetzt wird als der Indikator Chart Trade. Fangen wir an, damit Sie verstehen, wovon ich spreche.

Fügen wir also zunächst ein neues Ereignis zur Datei „Defines.mqh“ hinzu. Die aktualisierte Datei ist unten dargestellt:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.     #define macro_DEBUG_MODE(A) \
08.                     Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.     #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay          "RePlay"
14. #define def_MaxPosSlider          400
15. #define def_MaskTimeService       0xFED00000
16. #define def_IndicatorTimeFrame    (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame        4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.     double   dValue;
22.     long     _long;                                  // 1 Information
23.     datetime _datetime;                              // 1 Information
24.     uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
25.     ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
26.     uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents     {
30.             evTicTac,                        //Event of tic-tac
31.             evHideMouse,                     //Hide mouse price line
32.             evShowMouse,                     //Show mouse price line
33.             evHideBarTime,                   //Hide bar time
34.             evShowBarTime,                   //Show bar time
35.             evHideDailyVar,                  //Hide daily variation
36.             evShowDailyVar,                  //Show daily variation
37.             evHidePriceVar,                  //Hide instantaneous variation
38.             evShowPriceVar,                  //Show instantaneous variation
39.             evCtrlReplayInit,                //Initialize replay control
40.             evChartTradeBuy,                 //Market buy event
41.             evChartTradeSell,                //Market sales event 
42.             evChartTradeCloseAll,            //Event to close positions
43.             evChartTrade_At_EA,              //Event to communication
44.             evEA_At_ChartTrade,              //Event to communication
45.             evChatWriteSocket,               //Event to Mini Chat
46.             evChatReadSocket,                //Event To Mini Chat
47.             evUpdate_Position,               //Event to communication
48.             evMsgClosePositionEA             //Event to communication
49.                         };
50. //+------------------------------------------------------------------+
51. enum EnumPriority {                          //Priority list on objects
52.             ePriorityNull = -1,
53.             ePriorityDefault = 0,
54.             ePriorityChartTrade = 5,
55.             ePriorityOrders = 10
56.                         };
57. //+------------------------------------------------------------------+

Defines.mqh

Das neue Ereignis befindet sich in Zeile 48. Ich denke, es ist völlig klar, was genau getan wird. Aber warum nicht das in Zeile 42 deklarierte Ereignis des Indikators Chart Trade verwenden? Der Grund dafür ist einfach: Ich möchte weder den Positionsindikator noch den Expert Advisor auf nur einen Kontotyp beschränken. Wenn wir sicher wären, dass die Anwendung ausschließlich mit Netting-Konten funktioniert, könnten wir das Ereignis in Zeile 42 problemlos verwenden. Wenn Sie jedoch dieselben Anwendungen – also den Positionsindikator zusammen mit dem Expert Advisor – auf einem Hedging-Konto verwenden, werden beim Versuch, eine Position zu schließen, alle Positionen gleichzeitig geschlossen. Dies ist einer der Unterschiede bei der Umsetzung dieser beiden Indikatoren.

Gut, jetzt, da Sie wissen, was wir vorhaben, müssen wir sowohl den Code des Positionsindikators als auch den Code des Expert Advisors anpassen, damit das Ereignis korrekt interpretiert und verarbeitet wird. Okay, vielleicht machen Sie sich Sorgen und denken: „Das wird bestimmt sehr zeitaufwendig. Wir müssen eine Menge Änderungen am Code vornehmen.“ Aber wenn Sie das gedacht haben, bedeutet das, dass Sie den Code nicht wirklich verstehen. Diese Aufgabe erfordert nur wenige Änderungen. Schauen wir uns zunächst einmal an, welche Änderungen am Code des Expert Advisors vorgenommen werden müssen. Diese sind im folgenden Code dargestellt:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. class C_Orders
007. {
008.     protected:
009. //+------------------------------------------------------------------+
010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; }
011. //+------------------------------------------------------------------+
012.         bool ClosePosition(const ulong ticket)
013.             {
014.                 bool     IsBuy;
015.                 string szContract;
016.                 
017.                 if    (!PositionSelectByTicket(ticket)) return false;
018.                 IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
019.                 szContract = PositionGetString(POSITION_SYMBOL);
020.                 ZeroMemory(m_Base.TradeRequest);
021.                 m_Base.TradeRequest.action     = TRADE_ACTION_DEAL;
022.                 m_Base.TradeRequest.type       = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
023.                 m_Base.TradeRequest.price      = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS));
024.                 m_Base.TradeRequest.position   = ticket;
025.                 m_Base.TradeRequest.symbol     = szContract;
026.                 m_Base.TradeRequest.volume     = PositionGetDouble(POSITION_VOLUME);
027.                 m_Base.TradeRequest.deviation  = 1000;
028.                 
029.                 return SendToPhysicalServer() != 0;
030.             };
031. //+------------------------------------------------------------------+    
032.     private    :
033. //+------------------------------------------------------------------+
034.         struct stBase
035.         {
036.             MqlTradeRequest TradeRequest;
037.             ulong           MagicNumber;
038.             bool            bTrash;
039.         }m_Base;
040. //+------------------------------------------------------------------+
041.         struct stChartTrade
042.         {
043.             struct stEvent
044.             {
045.                 EnumEvents  ev;
046.                 string      szSymbol,
047.                             szContract;
048.                 bool        IsDayTrade;
049.                 ushort      Leverange;
050.                 double      PointsTake,
051.                             PointsStop;
052.             }Data;
053. //---
054.             bool Decode(const EnumEvents ev, const string sparam)
055.                 {
056.                     string Res[];
057.         
058.                     if (StringSplit(sparam, '?', Res) != 7) return false;
059.                     stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
060.                     if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc;
061.                     else return false;
062.                     
063.                     return true;
064.                 }
065. //---
066.         }m_ChartTrade;
067. //+------------------------------------------------------------------+
068.         ulong SendToPhysicalServer(void)
069.             {
070.                 MqlTradeCheckResult TradeCheck;
071.                 MqlTradeResult      TradeResult;
072.                 
073.                 ZeroMemory(TradeCheck);
074.                 ZeroMemory(TradeResult);
075.                 if (!OrderCheck(m_Base.TradeRequest, TradeCheck))
076.                 {
077.                     PrintFormat("Order System - Check Error: %d", GetLastError());
078.                     return 0;
079.                 }
080.                 m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult);
081.                 if (TradeResult.retcode != TRADE_RETCODE_DONE)
082.                 {
083.                     PrintFormat("Order System - Send Error: %d", TradeResult.retcode);
084.                     return 0;
085.                 };
086.                 
087.                 return TradeResult.order;
088.             }
089. //+------------------------------------------------------------------+    
090.         ulong ToMarket(const ENUM_ORDER_TYPE type)
091.             {
092.                 double price  = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID));
093.                 double vol    = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP);
094.                 uchar  nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS);
095.                 
096.                 ZeroMemory(m_Base.TradeRequest);
097.                 m_Base.TradeRequest.magic         = m_Base.MagicNumber;
098.                 m_Base.TradeRequest.symbol        = m_ChartTrade.Data.szContract;
099.                 m_Base.TradeRequest.price         = NormalizeDouble(price, nDigit);
100.                 m_Base.TradeRequest.action        = TRADE_ACTION_DEAL;
101.                 m_Base.TradeRequest.sl            = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit);
102.                 m_Base.TradeRequest.tp            = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit);
103.                 m_Base.TradeRequest.volume        = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit);
104.                 m_Base.TradeRequest.type          = type;
105.                 m_Base.TradeRequest.type_time     = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
106.                 m_Base.TradeRequest.stoplimit     = 0;
107.                 m_Base.TradeRequest.expiration    = 0;
108.                 m_Base.TradeRequest.type_filling  = ORDER_FILLING_RETURN;
109.                 m_Base.TradeRequest.deviation     = 1000;
110.                 m_Base.TradeRequest.comment       = "Order Generated by Experts Advisor.";
111. 
112.                 MqlTradeRequest TradeRequest[1];
113. 
114.                 TradeRequest[0] = m_Base.TradeRequest;
115.                 ArrayPrint(TradeRequest);
116. 
117.                 return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0);
118.             };
119. //+------------------------------------------------------------------+
120.         void CloseAllsPosition(void)
121.             {
122.                 for (int count = PositionsTotal() - 1; count >= 0; count--)
123.                 {
124.                     if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue;
125.                     if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue;
126.                     ClosePosition(PositionGetInteger(POSITION_TICKET));
127.                 }
128.             };
129. //+------------------------------------------------------------------+    
130.     public    :
131. //+------------------------------------------------------------------+
132.         C_Orders(const ulong magic)
133.             {
134.                 m_Base.MagicNumber = magic;
135.             }
136. //+------------------------------------------------------------------+    
137.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
138.             {
139.                 switch (id)
140.                 {
141.                     case CHARTEVENT_CUSTOM + evChartTradeBuy     :
142.                     case CHARTEVENT_CUSTOM + evChartTradeSell    :
143.                     case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
144.                         if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev)
145.                         {
146.                             case evChartTradeBuy:
147.                                 ToMarket(ORDER_TYPE_BUY);
148.                                 break;
149.                             case evChartTradeSell:
150.                                 ToMarket(ORDER_TYPE_SELL);
151.                                 break;
152.                             case evChartTradeCloseAll:
153.                                 CloseAllsPosition();
154.                                 break;
155.                         }
156.                         break;
157.                     case CHARTEVENT_CUSTOM + evMsgClosePositionEA:
158.                         ClosePosition((ulong)StringToInteger(sparam));
159.                         break;
160.                 }
161.             }
162. //+------------------------------------------------------------------+    
163. };
164. //+------------------------------------------------------------------+

C_Orders.mqh

Obwohl wir den vollständigen Code für die Klasse „C_Orders“ zeigen, damit Sie auch dann verstehen können, was hier vor sich geht, wenn Sie diese Serie nicht verfolgt haben, haben wir dem Klassencode lediglich drei Zeilen hinzugefügt. Das sind die Zeilen 157, 158 und 159. Und das war bereits alles, was dem Code des Expert Advisors hinzugefügt werden musste, damit er eine Anfrage zum Schließen einer Position verarbeiten konnte. Bitte beachten Sie: Wir filtern hier keinerlei Daten. Daher kann dieses Ereignis beispielsweise von einem Dienst oder sogar von einem Skript ausgelöst werden. Wir schränken nicht ein, wer genau dem Expert Advisor den Befehl zur Ausführung der Aktion erteilen darf – in diesem Fall zum Schließen einer Position. Das Einzige, worum wir uns wirklich kümmern müssen, ist, im Feld „sparam“ die Ticketnummer der Position anzugeben, die wir schließen möchten. Wenn das Ticket vorhanden ist, wird die Position geschlossen.

Beachten Sie nun den Punkt, den ich zuvor erwähnt habe: Wir konnten das Ereignis „evChartTradeCloseAll“ nicht verwenden, da hier ein anderer Aufruf erforderlich ist als der in „evChartTradeCloseAll“ verwendete. Der Grund dafür ist, dass diese Implementierung so konzipiert ist, dass sie mit jedem Kontotyp funktioniert, unabhängig davon, ob es sich um ein Netting-Konto oder ein Hedging-Konto handelt. In Ordnung. Der Teil, der den Expert Advisor betrifft, wurde bereits implementiert und funktioniert zweifellos, denn der Code, den wir in der Klasse C_Orders verwenden, wird schon seit geraumer Zeit getestet.

Wir können uns auf den Teil konzentrieren, der Ihnen vielleicht am schwersten fällt. Aber Sie werden sehen, dass das alles ganz einfach ist. Um zu zeigen, dass die Umsetzung im Positionsindikator relativ einfach ist, werde ich den Code des Indikators vorerst nicht grundlegend ändern. Ich werde einfach alles Notwendige hinzufügen, damit bei einer Interaktion mit dem Positionsindikator eine Anfrage an den Expert Advisor gesendet wird, um die angegebene Position zu schließen. Das lässt sich ganz einfach bewerkstelligen, wie Sie unten sehen können.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_SufixTake        "Take"
005. #define def_SufixStop        "Stop"
006. //+------------------------------------------------------------------+
007. #define def_NameBtnClose     m_Infos.szShortName + "Close"
008. //+------------------------------------------------------------------+
009. #define def_PathBtns "Images\\Market Replay\\Orders\\"
010. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp"
011. #resource "\\" + def_Btn_Close;
012. //+------------------------------------------------------------------+
013. #include "..\Auxiliar\C_Terminal.mqh"
014. //+------------------------------------------------------------------+
015. class C_IndicatorPosition : private C_Terminal
016. {
017.     private    :
018.         struct st00
019.         {
020.             ulong     ticket;
021.             color     corPrice, corTake, corStop;
022.             string    szShortName;
023.         }m_Infos;
024. //+------------------------------------------------------------------+
025.         inline void MoveLineInfos(const string szDefine, const double price)
026.         {
027.             string szName = m_Infos.szShortName + szDefine;
028.             int x, y;
029.             
030.             if (price <= 0) ObjectDelete(0, szName);
031.             else
032.             {
033.                 if (ObjectFind(0, szName) < 0)
034.                 {
035.                     if (szDefine == def_SufixTake) CreateLineInfos(def_SufixTake, m_Infos.corTake, "Take Profit point."); else
036.                     if (szDefine == def_SufixStop) CreateLineInfos(def_SufixStop, m_Infos.corStop, "Stop Loss point."); else
037.                     {
038.                         CreateLineInfos(NULL, m_Infos.corPrice, "Position opening price.");
039.                         CreateObjectGraphics(def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityOrders + 1));
040.                         ObjectSetString(0, def_NameBtnClose, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close);
041.                     }
042.                 }                    
043.                 ObjectSetDouble(0, szName, OBJPROP_PRICE, price);
044.                 if (szDefine == NULL)
045.                 {
046.                     ChartTimePriceToXY(0, 0, 0, price, x, y);
047.                     ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, 130);
048.                     ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y - 8);
049.                 }
050.             }
051.         }
052. //+------------------------------------------------------------------+
053.         void CreateLineInfos(string szObjName, const color cor, const string szDescription = "\n")
054.         {            
055.             szObjName = m_Infos.szShortName + szObjName;
056.             CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop))));
057.             ObjectSetString(0, szObjName, OBJPROP_TEXT, szDescription);
058.             ObjectSetString(0, szObjName, OBJPROP_TOOLTIP, szDescription);
059.             ObjectSetInteger(0, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice);
060.         }
061. //+------------------------------------------------------------------+
062.     public    :
063. //+------------------------------------------------------------------+
064.         C_IndicatorPosition(color corPrice, color corTake, color corStop)
065.             :C_Terminal()
066.         {
067.             ZeroMemory(m_Infos);
068.             m_Infos.corPrice = corPrice;
069.             m_Infos.corTake  = corTake;
070.             m_Infos.corStop  = corStop;
071.         }
072. //+------------------------------------------------------------------+
073.         ~C_IndicatorPosition()
074.         {
075.             if (m_Infos.ticket != 0)
076.                 ObjectsDeleteAll(0, IntegerToString(m_Infos.ticket));
077.         }
078. //+------------------------------------------------------------------+
079.         bool CheckCatch(ulong ticket)
080.         {
081.             m_Infos.szShortName = IntegerToString(m_Infos.ticket = ticket);            
082.             if (!PositionSelectByTicket(m_Infos.ticket)) return false;
083.             if (ObjectFind(0, m_Infos.szShortName) >= 0)
084.             {
085.                 m_Infos.ticket = 0;
086.                 return false;
087.             }
088.             IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
089.             EventChartCustom(0, evUpdate_Position, ticket, 0, "");
090.             
091.             return true;
092.         }
093. //+------------------------------------------------------------------+
094.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
095.         {
096.             static double price = 0;
097.             
098.             switch (id)
099.             {
100.                 case CHARTEVENT_CUSTOM + evUpdate_Position:
101.                     if (lparam != m_Infos.ticket) return;
102.                     if (!PositionSelectByTicket(m_Infos.ticket))
103.                     {
104.                         ChartIndicatorDelete(0, 0, m_Infos.szShortName);
105.                         return;
106.                     };
107.                     MoveLineInfos(NULL, price = PositionGetDouble(POSITION_PRICE_OPEN));
108.                     MoveLineInfos(def_SufixTake, PositionGetDouble(POSITION_TP));
109.                     MoveLineInfos(def_SufixStop, PositionGetDouble(POSITION_SL));
110.                     break;
111.                 case CHARTEVENT_CHART_CHANGE:
112.                     MoveLineInfos(NULL, price);
113.                     break;
114.                 case CHARTEVENT_OBJECT_CLICK:
115.                     if (sparam == def_NameBtnClose)
116.                         EventChartCustom(0, evMsgClosePositionEA, 0, 0, m_Infos.szShortName);
117.                     break;
118.             }
119.             ChartRedraw();
120.         }
121. //+------------------------------------------------------------------+
122. };
123. //+------------------------------------------------------------------+
124. #undef def_Btn_Close
125. #undef def_PathBtns
126. //+------------------------------------------------------------------+
127. #undef def_NameBtnClose
128. //+------------------------------------------------------------------+
129. #undef def_SufixTake
130. #undef def_SufixStop
131. //+------------------------------------------------------------------+

C_IndicatorPosition.mqh

Hier ist also der Code für die Klasse „C_IndicatorPosition“. Bitte beachten Sie, dass die Änderungen in Bezug auf die Umsetzung nur sehr geringfügig sind. Dieser Code kann jedoch ein Objekt auf dem Chart platzieren, mit dem Sie interagieren und über das Sie beim Expert Advisor das Schließen einer Position anfordern können. Aber wie läuft das ab? Lassen Sie uns diesen Code nun analysieren, bevor wir einige Verbesserungen daran vornehmen. Dieser Code eignet sich noch nicht für die Weiterentwicklung unseres Indikators. Aber, lieber Leser, es ist wichtig, dass Sie dies verstehen, bevor Sie sich mit komplexerem Code befassen.

Beachten Sie, dass Zeile 07 eine Definition enthält. Damit soll der Name des Objekts festgelegt werden, mit dem wir interagieren werden, um eine Position zu schließen. Als Nächstes sehen wir in den Zeilen 09 bis 11, was lokal vorbereitet werden muss. Dies ist erforderlich, wenn Sie eine eigene Vorlage erstellen möchten. Wenn Sie das nicht tun möchten, machen Sie sich keine Sorgen – wir werden die Anwendungen bald zusammen mit dem Artikel zur Verfügung stellen, genau wie im vorherigen Artikel.

Gut, machen wir weiter. In Zeile 09 geben wir den Speicherort des Bitmap-Bildes an. In Zeile 10 legen wir fest, was das Bitmap-Bild darstellt. In diesem Fall müssen Sie ein Bild mit einer Größe von 16 x 16 Pixeln vorbereiten. Sie können jedoch ein anderes Bild verwenden, wenn Sie die entsprechenden Änderungen am Code vornehmen. Später werden wir uns genauer ansehen, an welcher Stelle im Code Sie diese Änderung vornehmen müssen. Konzentrieren wir uns zunächst einmal auf Zeile 11. In dieser Zeile teilen wir dem MQL5-Compiler mit, dass wir die Bitmap als Ressource in die ausführbare Datei einbinden möchten. Dadurch entfällt die Notwendigkeit, die Bitmap nach der Kompilierung zusammen mit der übrigen Anwendung bereitzustellen, was die Aufgabe sowohl für uns als auch für den Endnutzer erheblich vereinfacht.

Großartig, wir arbeiten uns ohne größere Änderungen durch den Code, mit zwei Ausnahmen. Vorerst werden wir uns keine Gedanken über die Prozedur in Zeile 25 machen, da dort die Änderungen vorgenommen wurden. Gehen wir direkt zu Zeile 94, wo der Code implementiert ist, der für die Verarbeitung von Nachrichten aus MetaTrader 5 zuständig ist.

Passen Sie jetzt gut auf, denn für das Folgende ist es sehr wichtig zu verstehen, was in der in Zeile 94 definierten Prozedur geschieht. Bitte beachten Sie, dass in Zeile 96 eine statische Variable hinzugefügt wurde. Sie wird später wieder entfernt. Vorerst ermöglicht uns dies jedoch, den Kurs, zu dem die Position eröffnet wurde, lokal zu speichern und im Speicher zu behalten. Bitte beachten Sie, dass dieselbe Variable in Zeile 107 initialisiert wird. In Ordnung. Nun kennen wir den Kurs, zu dem die Position eröffnet wurde. Es ist wichtig zu beachten, dass dieser Wert während der gesamten Zeit konstant bleibt. Wir benötigen diesen Wert jedoch zu einem ganz bestimmten Zeitpunkt. Und genau das geschieht in Zeile 112. Bitte beachten Sie, dass sich diese Zeile innerhalb der Ereignisbehandlung für CHARTEVENT_CHART_CHANGE befindet. Der Grund dafür ist einfach. Wenn Sie die Preisskala in einem Chart ändern, passen sich die Preislinien entsprechend an.

Es gibt jedoch ein Problem, das wir später bei der Rückkehr zu Zeile 25 sehen werden: Die Schaltfläche, die wir im Chart platzieren, bewegt sich nicht mit den Preislinien mit. Um dieses Problem zu lösen, senden wir, wie in Zeile 112 gezeigt, eine Anfrage, um die Position der Schaltfläche im Chart zu aktualisieren. Deshalb müssen wir wissen, zu welchem Kurs die Position eröffnet wurde. Wenn wir bei jeder Änderung der Preisskala den Preis nachschlagen müssten, zu dem die Position eröffnet wurde, würde dies zu einer erheblichen Verzögerung bei der Ausführung der auf dem Chart laufenden Anwendungen führen. Da sich der Eröffnungspreis der Position nicht ändert, können wir dies so weit wie möglich vereinfachen.

In der Prozedur „DispatchMessage“ interessiert uns jedoch eigentlich der nächste Block, also der Code in Zeile 114. Bitte beachten Sie, dass wir an dieser Stelle prüfen, ob auf das Objekt geklickt wurde. Wenn MetaTrader 5 uns darüber informiert, dass dies geschehen ist, wird genau angegeben, auf welches Objekt geklickt wurde. Der Objektname wird an die Variable „sparam“ übergeben. Daher prüfen wir in Zeile 115, ob der Name mit dem Namen des Objekts übereinstimmt, das ganz am Anfang, also in Zeile 07, erstellt wurde. Wenn die in Zeile 115 durchgeführte Überprüfung den Wert „wahr“ ergibt, fordern wir in Zeile 116 MetaTrader 5 auf, ein benutzerdefiniertes Ereignis zu senden, damit der Expert Advisor dieses später abfangen kann.

Analysieren Sie den Wert dieses Ereignisses. Genau diese Werte erwartet der Expert Advisor, um eine Anfrage zum Schließen einer Position zu interpretieren. Das ist doch interessant, oder? Schauen wir uns nun einmal an, wie das Objekt (bzw. die Bitmap), das die Schaltfläche zum Schließen einer Position darstellt, erstellt und im Chart positioniert wird. Dazu konzentrieren wir uns vorerst auf Zeile 25, wo der entscheidende Teil ausgeführt wird.

Bitte beachten Sie, dass wir hier im Vergleich zur vorherigen Version einige Änderungen vorgenommen haben. Als ersten Schritt haben wir in Zeile 28 die Variablen x und y hinzugefügt. Danach haben wir in Zeile 38, genau wie zuvor, eine Zeichenkette erstellt, die den Preis enthält. Da wir jedoch noch ein weiteres Objekt benötigen – in diesem Fall eine Schaltfläche zum Schließen einer Position –, erstellen wir dieses in Zeile 39. Es ist wichtig zu beachten, wie wir dabei vorgehen und welche Objekte wir erstellen. Wir erstellen also ein OBJ_BITMAP_LABEL-Objekt. Für die korrekte Positionierung benötigt dieses Objekt X- und Y-Koordinatenwerte. Wir können keine Preiskoordinaten verwenden. Unmittelbar nach der Erstellung des Objekts, in Zeile 40, weisen wir MetaTrader 5 an, das Bild zu verwenden, das als Ressource in der ausführbaren Datei enthalten ist. An dieser Stelle – und nicht in Zeile 11 – binden wir das in der ausführbaren Datei enthaltene Bild ein und verwenden das Bild aus dem Verzeichnis nicht mehr. Bislang haben wir das Objekt jedoch nur erstellt; wir haben es noch nicht auf dem Bildschirm angezeigt.

Um es an der richtigen Stelle zu platzieren, müssen wir die Preis-Zeit-Koordinaten in kartesische Koordinaten umrechnen. Glücklicherweise verfügt MQL5 über eine Hilfsfunktion, die in Zeile 46 verwendet wird. Dort wandeln wir den Preis in die entsprechende Y-Koordinate um. Das ist der einfachste Teil, aber es sollten einige kleine Vorsichtsmaßnahmen getroffen werden. Um das zu verstehen, schauen Sie sich einmal an, was wir in Zeile 48 tun. Dort ziehe ich acht von dem Wert ab, den die Funktion in Zeile 46 zurückgibt. Warum führe ich diese Subtraktion durch, und warum verwende ich die Zahl acht? Wir führen diese Subtraktion durch, da sich der Ankerpunkt des OBJ_BITMAP_LABEL-Objekts in dessen oberer linker Ecke und nicht in dessen Mitte befindet.

Da ich den Ankerpunkt des Objekts nicht auf dessen Mittelpunkt gesetzt habe, muss ich dessen Position anpassen. Deshalb müssen wir acht abziehen. Da das von uns verwendete Bitmap-Bild 16 × 16 Pixel groß ist, müssen wir acht Pixel abziehen, um es zu zentrieren. So einfach ist das. Sobald dies erledigt ist, können wir über den Positionsindikator die Position schließen, indem wir auf das gerade erstellte und positionierte Objekt klicken. Aber wir können noch viel mehr tun. Damit der Code jedoch wartbar bleibt, müssen wir ihn verbessern, und genau das werden wir tun. Dadurch erhalten wir mit minimalem Codeaufwand deutlich mehr Funktionalität. Um die Themen jedoch klar voneinander abzugrenzen, werden wir dies in einem neuen Abschnitt behandeln.


Weitere Verbesserung des Indikators

Wenn Sie einen Moment innehalten und darüber nachdenken, werden Sie feststellen, dass wir bereits über ein Objekt verfügen, mit dem wir eine Position schließen können. Die Preislinien sind bereits im Chart dargestellt. Wir haben die Möglichkeit, Anfragen vom Positionsindikator an den Expert Advisor zu senden. All das mit nur den unbedingt notwendigen Änderungen. Aber wie könnten wir dieselbe Art der Interaktion wie beim Schließen einer Position auch für die Stop-Loss- und Take-Profit-Linien nutzen, um die Verlust- und Gewinnbegrenzung aufzuheben? Und zwar direkt durch Interaktion mit dem Positionsindikator, ohne die Registerkarte „Handel“ im MetaTrader 5-Terminal zu verwenden.

Sie könnten meinen, das wäre ganz einfach. Es würde ausreichen, den Code zum Schließen einer Position zu kopieren und ihn so anzupassen, dass das Stop-Loss- oder Take-Profit-Niveau entfernt wird. In gewisser Weise sollten Sie es genau so betrachten. Stellen Sie sich jedoch folgendes Szenario vor: Wenn Sie den Code duplizieren, um Ihr Ziel zu erreichen, werden Sie Erfolg haben. Wenn Sie den Indikator jedoch weiter verbessern möchten, wird der Arbeitsaufwand erheblich steigen, da Sie den Code an allen Stellen aktualisieren müssen, an denen er dupliziert wurde. In vielen Fällen ist diese Aufgabe relativ einfach und erfordert nur wenig Aufwand.

Aber was wäre, wenn wir es auf eine etwas bequemere Art und Weise machen würden? Oder genauer gesagt: Wenn wir dafür sorgen würden, dass Sie sich darum überhaupt nicht kümmern müssen? Würde es ausreichen, einfach festzulegen, dass eine Linie ein bestimmtes Element darstellt, und den Code selbst dafür sorgen zu lassen, dass sie korrekt erstellt wird? Mit allen zugehörigen Objekten, bereits vollständig funktionsfähig. Das wäre doch toll, oder? In Ordnung. Genau das werden wir jetzt tun. Wir werden einen Mechanismus entwickeln, um diesen Vorgang zu automatisieren. Das ist jedoch eine Aufgabe, die Aufmerksamkeit erfordert. Und ich möchte, dass Sie verstehen, was ich tue und warum ich es tue. Wir gehen das Schritt für Schritt durch. Auf diese Weise können Sie den Code ganz einfach anpassen, wenn Sie möchten.

Als Erstes werden wir der Datei „Defines.mqh“ einige neue Ereignisse hinzufügen. Die Datei sieht wie folgt aus. Dabei sind die neuen Ereignisse bereits definiert.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.     #define macro_DEBUG_MODE(A) \
08.                     Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.     #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay          "RePlay"
14. #define def_MaxPosSlider          400
15. #define def_MaskTimeService       0xFED00000
16. #define def_IndicatorTimeFrame    (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame        4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.     double   dValue;
22.     long     _long;                                  // 1 Information
23.     datetime _datetime;                              // 1 Information
24.     uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
25.     ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
26.     uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents     {
30.             evTicTac,                        //Event of tic-tac
31.             evHideMouse,                     //Hide mouse price line
32.             evShowMouse,                     //Show mouse price line
33.             evHideBarTime,                   //Hide bar time
34.             evShowBarTime,                   //Show bar time
35.             evHideDailyVar,                  //Hide daily variation
36.             evShowDailyVar,                  //Show daily variation
37.             evHidePriceVar,                  //Hide instantaneous variation
38.             evShowPriceVar,                  //Show instantaneous variation
39.             evCtrlReplayInit,                //Initialize replay control
40.             evChartTradeBuy,                 //Market buy event
41.             evChartTradeSell,                //Market sales event 
42.             evChartTradeCloseAll,            //Event to close positions
43.             evChartTrade_At_EA,              //Event to communication
44.             evEA_At_ChartTrade,              //Event to communication
45.             evChatWriteSocket,               //Event to Mini Chat
46.             evChatReadSocket,                //Event To Mini Chat
47.             evUpdate_Position,               //Event to communication
48.             evMsgClosePositionEA,            //Event to communication
49.             evMsgCloseTakeProfit,            //Event to communication
50.             evMsgCloseStopLoss               //Event to communication
51.                         };
52. //+------------------------------------------------------------------+
53. enum EnumPriority {                          //Priority list on objects
54.             ePriorityNull = -1,
55.             ePriorityDefault = 0,
56.             ePriorityChartTrade = 5,
57.             ePriorityOrders = 10
58.                         };
59. //+------------------------------------------------------------------+

Defines.mqh

Bitte beachten Sie, dass das alles sehr einfach und praktisch ist. Es ist nicht schwer zu erkennen, was hier gemacht wurde. Danach machen wir mit dem nächsten Schritt weiter. In dieser ersten Phase erweist sich die Arbeit als etwas zeitaufwändiger. Wir werden uns Zeit nehmen, damit Sie wirklich verstehen können, was vor sich geht. Gut, wir möchten einen Mechanismus hinzufügen, mit dem wir die Stop-Loss- und Take-Profit-Linien entfernen können. Es reicht jedoch nicht aus, dies einfach in den Nachrichtenblock einzufügen, da wir die Anfrage nicht ausführen können. Daher müssen wir den Code des Expert Advisors anpassen. Um diese Änderung vorzunehmen, gehen wir direkt zum entscheidenden Teil – der Header-Datei „C_Orders.mqh“. Diese Datei wird um neuen Code ergänzt, wie unten gezeigt.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. class C_Orders
007. {
008.     protected:
009. //+------------------------------------------------------------------+
010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; }
011. //+------------------------------------------------------------------+
012.         bool ClosePosition(const ulong ticket)
013.             {
014.                 bool   IsBuy;
015.                 string szContract;
016.                 
017.                 if    (!PositionSelectByTicket(ticket)) return false;
018.                 IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
019.                 szContract = PositionGetString(POSITION_SYMBOL);
020.                 ZeroMemory(m_Base.TradeRequest);
021.                 m_Base.TradeRequest.action    = TRADE_ACTION_DEAL;
022.                 m_Base.TradeRequest.type      = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
023.                 m_Base.TradeRequest.price     = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS));
024.                 m_Base.TradeRequest.position  = ticket;
025.                 m_Base.TradeRequest.symbol    = szContract;
026.                 m_Base.TradeRequest.volume    = PositionGetDouble(POSITION_VOLUME);
027.                 m_Base.TradeRequest.deviation = 1000;
028.                 
029.                 return SendToPhysicalServer() != 0;
030.             };
031. //+------------------------------------------------------------------+    
032.     private    :
033. //+------------------------------------------------------------------+
034.         struct stBase
035.         {
036.             MqlTradeRequest TradeRequest;
037.             ulong           MagicNumber;
038.             bool            bTrash;
039.         }m_Base;
040. //+------------------------------------------------------------------+
041.         struct stChartTrade
042.         {
043.             struct stEvent
044.             {
045.                 EnumEvents  ev;
046.                 string      szSymbol,
047.                             szContract;
048.                 bool        IsDayTrade;
049.                 ushort      Leverange;
050.                 double      PointsTake,
051.                             PointsStop;
052.             }Data;
053. //---
054.             bool Decode(const EnumEvents ev, const string sparam)
055.                 {
056.                     string Res[];
057.         
058.                     if (StringSplit(sparam, '?', Res) != 7) return false;
059.                     stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
060.                     if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc;
061.                     else return false;
062.                     
063.                     return true;
064.                 }
065. //---
066.         }m_ChartTrade;
067. //+------------------------------------------------------------------+
068.         ulong SendToPhysicalServer(void)
069.             {
070.                 MqlTradeCheckResult  TradeCheck;
071.                 MqlTradeResult       TradeResult;
072.                 
073.                 ZeroMemory(TradeCheck);
074.                 ZeroMemory(TradeResult);
075.                 if (!OrderCheck(m_Base.TradeRequest, TradeCheck))
076.                 {
077.                     PrintFormat("Order System - Check Error: %d", GetLastError());
078.                     return 0;
079.                 }
080.                 m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult);
081.                 if (TradeResult.retcode != TRADE_RETCODE_DONE)
082.                 {
083.                     PrintFormat("Order System - Send Error: %d", TradeResult.retcode);
084.                     return 0;
085.                 };
086.                 
087.                 return TradeResult.order;
088.             }
089. //+------------------------------------------------------------------+    
090.         ulong ToMarket(const ENUM_ORDER_TYPE type)
091.             {
092.                 double price  = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID));
093.                 double vol    = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP);
094.                 uchar  nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS);
095.                 
096.                 ZeroMemory(m_Base.TradeRequest);
097.                 m_Base.TradeRequest.magic        = m_Base.MagicNumber;
098.                 m_Base.TradeRequest.symbol       = m_ChartTrade.Data.szContract;
099.                 m_Base.TradeRequest.price        = NormalizeDouble(price, nDigit);
100.                 m_Base.TradeRequest.action       = TRADE_ACTION_DEAL;
101.                 m_Base.TradeRequest.sl           = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit);
102.                 m_Base.TradeRequest.tp           = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit);
103.                 m_Base.TradeRequest.volume       = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit);
104.                 m_Base.TradeRequest.type         = type;
105.                 m_Base.TradeRequest.type_time    = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
106.                 m_Base.TradeRequest.stoplimit    = 0;
107.                 m_Base.TradeRequest.expiration   = 0;
108.                 m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN;
109.                 m_Base.TradeRequest.deviation    = 1000;
110.                 m_Base.TradeRequest.comment      = "Order Generated by Experts Advisor.";
111. 
112.                 MqlTradeRequest TradeRequest[1];
113. 
114.                 TradeRequest[0] = m_Base.TradeRequest;
115.                 ArrayPrint(TradeRequest);
116. 
117.                 return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0);
118.             };
119. //+------------------------------------------------------------------+
120.         void CloseAllsPosition(void)
121.             {
122.                 for (int count = PositionsTotal() - 1; count >= 0; count--)
123.                 {
124.                     if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue;
125.                     if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue;
126.                     ClosePosition(PositionGetInteger(POSITION_TICKET));
127.                 }
128.             };
129. //+------------------------------------------------------------------+    
130.         void ModifyValueSLTP(const ulong ticket, const string symbol, const double sl, const double tp)
131.             {
132.                 ZeroMemory(m_Base.TradeRequest);            
133.                 MqlTradeRequest TradeRequest[1];
134. 
135.                 m_Base.TradeRequest.magic    = m_Base.MagicNumber;
136.                 m_Base.TradeRequest.action   = TRADE_ACTION_SLTP;
137.                 m_Base.TradeRequest.symbol   = symbol;
138.                 m_Base.TradeRequest.position = ticket;
139.                 m_Base.TradeRequest.sl       = sl;
140.                 m_Base.TradeRequest.tp       = tp;
141.                 
142.                 TradeRequest[0] = m_Base.TradeRequest;
143.                 ArrayPrint(TradeRequest);
144.                 
145.                 SendToPhysicalServer();
146.             }
147. //+------------------------------------------------------------------+    
148.     public    :
149. //+------------------------------------------------------------------+
150.         C_Orders(const ulong magic)
151.             {
152.                 m_Base.MagicNumber = magic;
153.             }
154. //+------------------------------------------------------------------+    
155.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
156.             {
157.                 switch (id)
158.                 {
159.                     case CHARTEVENT_CUSTOM + evChartTradeBuy     :
160.                     case CHARTEVENT_CUSTOM + evChartTradeSell    :
161.                     case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
162.                         if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev)
163.                         {
164.                             case evChartTradeBuy:
165.                                 ToMarket(ORDER_TYPE_BUY);
166.                                 break;
167.                             case evChartTradeSell:
168.                                 ToMarket(ORDER_TYPE_SELL);
169.                                 break;
170.                             case evChartTradeCloseAll:
171.                                 CloseAllsPosition();
172.                                 break;
173.                         }
174.                         break;
175.                     case CHARTEVENT_CUSTOM + evMsgClosePositionEA:
176.                         ClosePosition((ulong)(lparam));
177.                         break;
178.                     case CHARTEVENT_CUSTOM + evMsgCloseTakeProfit:
179.                         ModifyValueSLTP((ulong)(lparam), sparam, dparam, 0);
180.                         break;
181.                     case CHARTEVENT_CUSTOM + evMsgCloseStopLoss:
182.                         ModifyValueSLTP((ulong)(lparam), sparam, 0, dparam);
183.                         break;
184.                 }
185.             }
186. //+------------------------------------------------------------------+    
187. };
188. //+------------------------------------------------------------------+

C_Orders.mqh

Der oben gezeigte Code ist vollständig, genau wie alle anderen auch. Wenn Sie sich das ansehen, könnten Sie denken: „Na ja, ich sehe keinen großen Unterschied zur vorherigen Version.“ Tatsächlich versuche ich immer, sehr drastische Änderungen am Code zu vermeiden. Allerdings wurden hier einige Änderungen vorgenommen. Diese Änderungen sollen uns in der aktuellen und der nächsten Entwicklungsphase die nötige Unterstützung bieten. Aber konzentrieren wir uns zunächst auf diesen Schritt, bei dem es darum geht, die Stop-Loss- und Take-Profit-Linien zu entfernen. Bitte beachten Sie, dass in Zeile 130 des obigen Codes nun eine neue Prozedur vorhanden ist. Diese Prozedur dient dazu, die Stop-Loss- und Take-Profit-Werte für eine offene Position zu ändern. Bitte beachten Sie dies. Derzeit arbeiten wir nicht mit Aufträgem im Orderbuch, sondern mit einer bereits offenen Position. Es gibt einen Unterschied zwischen diesen beiden Situationen.

Im Wesentlichen erhalten wir bestimmte Parameter und senden eine Anfrage an den Handelsserver, um die Take-Profit- und Stop-Loss-Niveaus anzupassen. In Zeile 143 geben wir die an den Server gesendete Anfrage im Terminal aus. Auf diese Weise können wir die Anfrage überprüfen und analysieren, wie das System funktioniert. Gut, da diese Prozedur recht einfach ist und lediglich die Übergabe von Argumenten erfordert, können wir nun zum nächsten Abschnitt übergehen, in dem der Code geändert wurde. Diese Änderung ist in der in Zeile 155 dargestellten Prozedur zu finden.

An dieser Stelle werden wir nun die vom Expert Advisor empfangenen Anfragen tatsächlich bearbeiten. Beachten Sie, dass die Anfrage aus jeder beliebigen Quelle stammen kann – nicht unbedingt vom Positionsindikator, den ich hier zeige. Sehen Sie sich also genau an, was ich Ihnen zeige, damit Sie Ihre eigene Lösung entwickeln können. Bitte beachten Sie, dass der Code für diese „DispatchMessage“-Prozedur praktisch unverändert bleibt. Schauen Sie sich das genauer an. Sie werden feststellen, dass sich hier etwas geändert hat. Den Grund dafür werden wir im nächsten Artikel erfahren.

Bitte beachten Sie, dass in Zeile 175, wo wir prüfen, ob die empfangene Nachricht eine Anfrage zum Schließen einer Position ist, die Implementierung zum Schließen der Position geändert wurde. Mit anderen Worten: Zeile 176 wurde leicht abgeändert. Bisher wurde die Kennung über die Variable „sparam“ übergeben, jetzt verwende ich jedoch die Variable „lparam“. Bitte beachten Sie, dass es dafür einen Grund gibt, den Sie jedoch im nächsten Artikel besser verstehen werden. Sehen wir uns nun zwei weitere Prüfungen an, die zur Identifizierung der empfangenen Nachricht durchgeführt werden. Die erste Prüfung erfolgt in Zeile 178, wo wir prüfen, ob sich die Nachricht auf das Entfernen des Take-Profit-Niveaus bezieht. Die zweite Prüfung erfolgt in Zeile 181, wo wir prüfen, ob es sich bei der Nachricht um eine Anfrage zum Entfernen des Stop-Loss-Niveaus handelt.

In beiden Fällen wird dieselbe Prozedur aufgerufen: „ModifyValueSLTP“, mit der die Stop-Loss- und Take-Profit-Niveaus einer Position geändert werden. Bitte beachten Sie, welche Werte genau in den einzelnen Parametern übergeben werden. Der einzige Unterschied besteht darin, wo der Parameter „dparam“ verwendet wird. In einem Fall wird er für Take Profit verwendet, im anderen für Stop Loss. So können wir eines der Niveaus entfernen, denn sobald einem der Niveaus der Wert null zugewiesen wird, wird es entfernt. Dies liegt daran, dass der Handelsserver den Wert null so interpretiert, dass die Begrenzung – ob Take Profit oder Stop Loss – schlichtweg nicht existiert. Damit sind die erforderlichen Änderungen am Code des Expert Advisors abgeschlossen; weitere Anpassungen sind nicht erforderlich. Zumindest vorerst.


Abschließende Gedanken

In diesem Artikel habe ich eine recht einfache und effektive Methode zum Schließen einer Position vorgestellt, bei der der Indikator Mouse Study und der Positionsindikator mit einem entsprechend programmierten Expert Advisor zusammenwirken. Die gesamte Umsetzung erfolgte auf recht elegante Weise, da sie keine größeren Änderungen am Code erforderte. Außerdem habe ich den gesamten Abschnitt gezeigt, der im Code des Expert Advisors implementiert werden musste, damit wir einen Mechanismus zum Entfernen der Take-Profit- und Stop-Loss-Linien erstellen konnten.

Da jedoch zahlreiche Änderungen am Code des Positionsindikators vorgenommen werden müssen, werde ich den geänderten Code in diesem Artikel nicht aufführen. Ich werde dies im nächsten Artikel ausführlich beschreiben und erläutern. Ich werde das tun, damit Sie wirklich verstehen, wie und warum dieser Code funktioniert – vor allem aber, warum MetaTrader 5 Informationen während des Handels korrekt interpretieren und anzeigen kann. Sie werden feststellen, dass Sie sich mit MetaTrader 5 um bestimmte Details keine Gedanken machen müssen. Das meiste, was wir tatsächlich tun müssen, besteht darin, MetaTrader 5 anzuweisen, eine Nachricht zu senden; der Rest erledigt sich von selbst, ohne großen Aufwand oder Schwierigkeiten.

Ich hoffe, dass Sie – ganz gleich, ob Sie bereits viel Erfahrung mit MQL5 haben oder gerade erst anfangen – verstehen, dass es mir hier darum geht, Ihnen zu vermitteln, wie wir solche Lösungen umsetzen können. Wenn das Ihr Interesse geweckt hat, sollten Sie diesen Artikel und den Code unbedingt sorgfältig durchlesen, denn im nächsten Artikel werden wir uns mit dem Teil befassen, der sich auf den Positionsindikator bezieht.

Datei Beschreibung
Experts\Expert Advisor.mq5
Zeigt die Interaktion zwischen Chart Trade und dem Expert Advisor auf dem Chart an. (Für diese Interaktion ist Mouse Study erforderlich.)
Indicators\Chart Trade.mq5 Erstellt ein Fenster zur Konfiguration der zu sendenden Order. (Für diese Interaktion ist „Mouse Study“ erforderlich.)
Indicators\Market Replay.mq5 Erstellt Steuerelemente für die Interaktion mit dem Wiedergabe-/Simulationsdienst. (Für die Interaktion ist „Mouse Study“ erforderlich.)
Indicators\Mouse Study.mq5 Ermöglicht die Interaktion zwischen grafischen Bedienelementen und dem Benutzer, die sowohl für den Betrieb des Wiedergabe-/Simulationssystems als auch im Live-Handel erforderlich ist.
Indicators\Order Indicator.mq5 Zuständig für die Erstellung von Marktorders, die Ermöglichung der Interaktion mit ihnen sowie deren Steuerung.
Indicators\Position View.mq5 Zuständig für die Anzeige offener Positionen sowie für die Interaktion mit ihnen und ihre Steuerung.
Services\Market Replay.mq5 Erstellt und pflegt den Wiedergabe-/Simulationsdienst für den Markt (die Hauptdatei des gesamten Systems).

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

Beigefügte Dateien |
Anexo.zip (779.24 KB)
Neuronale Netze im Trading: Heterogenitätsbasiertes Lernen von Metaparametern (HimNet) Neuronale Netze im Trading: Heterogenitätsbasiertes Lernen von Metaparametern (HimNet)
Wir laden Sie ein, das HimNet-Framework kennenzulernen, das die Flexibilität der räumlich-zeitlichen Anpassung mit hoher Recheneffizienz verbindet und so genaue und stabile Prognosen für finanzielle Zeitreihen ermöglicht. Der Artikel erläutert ausführlich, wie dessen wichtigste Komponenten miteinander interagieren und komplexe Algorithmen in eine handhabbare Architektur überführen.
Trauminspirierter Optimierungsalgorithmus (DOA) Trauminspirierter Optimierungsalgorithmus (DOA)
Ein populationsbasierter Optimierungsalgorithmus, der von einem umstrittenen und bislang kaum erforschten Phänomen inspiriert ist – dem Mechanismus menschlicher Träume. Agentengruppen mit unterschiedlichem „Gedächtnis“, Kosinusmodulation der Bewegung und einer ungewöhnlichen Phasenverteilung von 99/1 – erfahren Sie, wie sich diese Merkmale auf die Effizienz bei der Optimierung Ihrer Handelsstrategien auswirken.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Neuronale Netze im Trading: Das Temporal-Query-Modell (Fazit) Neuronale Netze im Trading: Das Temporal-Query-Modell (Fazit)
Wir freuen uns, Ihnen die abschließende Phase der Entwicklung und des Testens des TQNet-Frameworks vorstellen zu dürfen, in der die Theorie auf die reale Trading-Praxis trifft. Wir werden vom historischen Training zu einem Stresstest mit aktuellen Marktdaten übergehen, um die Robustheit und Genauigkeit des Modells zu bewerten. Die Endergebnisse sind nicht nur trockene Statistiken, sondern auch eine klare Demonstration des praktischen Nutzens des vorgeschlagenen Ansatzes.