English Русский 中文 Español 日本語 Português
preview
Marktsimulation (Teil 08): Sockets (II)

Marktsimulation (Teil 08): Sockets (II)

MetaTrader 5Beispiele |
104 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel, Marktsimulation (Teil 07): Sockets (I), habe ich Ihnen die ersten Schritte zum Studium von Sockets gezeigt. Allerdings dürfte die dort gezeigte Anwendung nicht besonders spannend gewesen sein. Um ehrlich zu sein, war es eher ein „HELLO WORLD“-Programm – das erste Programm, das wir normalerweise ausprobieren, wenn wir etwas Neues in der Programmierung lernen. Es war ein erster Schritt auf einer langen Reise.

Deshalb zeige ich Ihnen in diesem Artikel, wie Sie etwas Interessanteres schaffen können. Um es klar zu sagen: Es wird nicht besonders nützlich sein, aber es wird Ihnen ermöglichen, zu experimentieren und Spaß zu haben, während Sie die damit verbundenen Konzepte studieren. Genau wie im vorigen Artikel müssen wir uns auf externen Code verlassen. Auch hier ist der Grund für die Verwendung von externem Code, dass ich keine DLLs zu MetaTrader 5 hinzufügen möchte, zumindest noch nicht.

Was den externen Code betrifft, so möchte ich nicht zu sehr ins Detail gehen. Sie können verschiedene Codes finden, die die gleiche Art von Aufgabe erfüllen, oder Ihre eigenen erstellen, indem Sie die Referenzen studieren, die ich im vorherigen Artikel erwähnt habe. Was werden wir also in diesem Artikel tun? Im Wesentlichen möchte ich zeigen, wie interessant Sockets sein können. Ich habe auch vor, sie im Replay/Simulator-System zu verwenden. Wir werden dies auf die einfachste Art und Weise angehen: indem wir einen Mini-Chat erstellen. Richtig – ich zeige Ihnen, wie Sie einen Mini-Chat in MetaTrader 5 erstellen können.


Planung

Das Konzept eines Minichats ist recht einfach und offensichtlich. Sie haben einen Bereich, in dem Sie Nachrichten schreiben können, eine Schaltfläche zum Versenden und einen weiteren Bereich, in dem Sie Nachrichten von anderen sehen können. Kurz gesagt, es ist einfach zu implementieren. Wir müssen ein Bearbeitungsfeld, eine Schaltfläche und ein Objekt zur Anzeige der veröffentlichten Nachrichten hinzufügen. MetaTrader 5 bietet alle diese Werkzeuge.

Gut. Normalerweise verwenden Chat-Programme ein eingebautes Client-Server-System. Aber hier, um reines MQL5 zu verwenden, wird der Server ein externes Programm sein, während die Clients in MQL5 implementiert werden. Dies ist einer der Vorteile der Verwendung von Sockets: Sie sind nicht auf einen einzigen Ansatz beschränkt. Sie können sie auf verschiedene Weise umsetzen. Sie denken jetzt vielleicht: Wie können wir Verbindungen verwalten, um eine beliebige Anzahl von Teilnehmern an unserem Mini-Chat zuzulassen? Das muss sehr kompliziert zu entwickeln sein. Richtig? Nun, das hängt davon ab, was Sie erreichen wollen und wie Sie es umsetzen.

Das Serverprogramm, das ich demonstrieren werde, ist so einfach, dass Sie es auf einem Raspberry Pi laufen lassen und als Miniserver verwenden könnten. Dieses System kann eine große Anzahl von Teilnehmern unterstützen, ohne dass der Code neu kompiliert werden muss, da der Server dynamisch sein wird. Mit „dynamisch“ meine ich, dass das Verbindungslimit durch das Betriebssystem oder die Hardwarekapazitäten bestimmt wird, nicht durch den Servercode selbst. Bevor wir uns mit dem Server-Code beschäftigen, wollen wir uns den Client-Teil ansehen. Es wird in MQL5 implementiert werden.


Grundlegende Implementierung

Dieser Teil der Implementierung wird recht interessant sein, insbesondere innerhalb von MQL5, da wir einen etwas ungewöhnlichen Ansatz verwenden werden. Zunächst müssen wir ein Fenster für die Interaktion mit dem Mini-Chat erstellen. Der einfachste Weg, dies in MQL5 für MetaTrader 5 zu tun, ist die Verwendung eines Indikators. Es gibt jedoch eine Einschränkung: Indikatoren können keine Sockets verwenden, da sie, je nach Socket-Implementierung, den Berechnungsfluss anderer Indikatoren blockieren können. Alle Indikatoren teilen sich denselben Berechnungsraum. Wie können wir also das Fenster erstellen? Der einfachste Code lautet wie folgt:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property indicator_separate_window
04. #property indicator_plots 0
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.    return INIT_SUCCEEDED;
09. }
10. //+------------------------------------------------------------------+
11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
12. {
13.    return rates_total;
14. }
15. //+------------------------------------------------------------------+

Ursprünglicher Indikatorcode

Dieser grundlegende Code erstellt ein Fenster für uns, dank Zeile 3, die angibt, dass das Hinzufügen dieses Codes zu einem Chart ein neues Fenster erzeugt. Aber es gibt ein Problem. Wenn wir es so belassen, könnten die Nutzer mehrere Chat-Fenster in das Chart einfügen. Ein weiteres Problem ist, dass Indikatoren keine Sockets verwenden können.

Wir müssen den Code ändern, damit er in einem Kontext verwendet werden kann, in dem Sockets erlaubt sind. Zur Vereinfachung werden wir alles in einen Expert Advisor einbetten. Dies ist nur zur Demonstration. Das Gleiche könnte mit einem Skript gemacht werden. Die Skripte werden jedoch entfernt, wenn sich der Zeitrahmen des Charts ändert, daher werde ich die Implementierung mit einem Expert Advisor zeigen. Der erste Schritt ist die Erstellung des folgenden Codes:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property version   "1.00"
04. //+------------------------------------------------------------------+
05. #define def_IndicatorMiniChat   "Indicators\\Mini Chat\\Mini Chat.ex5"
06. #resource "\\" + def_IndicatorMiniChat
07. //+------------------------------------------------------------------+
08. long gl_id;
09. int subWin;
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.    gl_id = ChartID();
14.    subWin = (int) ChartGetInteger(gl_id, CHART_WINDOWS_TOTAL);
15.    
16.    ChartIndicatorAdd(gl_id, subWin, iCustom(NULL, 0, "::" + def_IndicatorMiniChat));
17. 
18.    return INIT_SUCCEEDED;
19. }
20. //+------------------------------------------------------------------+
21. void OnDeinit(const int reason)
22. {
23.    ChartIndicatorDelete(gl_id, subWin, ChartIndicatorName(gl_id, subWin, 0));
24. }
25. //+------------------------------------------------------------------+
26. void OnTick()
27. {
28. }
29. //+------------------------------------------------------------------+
30. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
31. {
32. }
33. //+------------------------------------------------------------------+

Expert Advisor Startcode

Dies ist das Grundgerüst unseres Expert Advisors, mit dem sich ein Fenster öffnen lässt. Beachten Sie Zeile 5: Sie gibt an, dass wir in Zeile 6 den Indikator als interne Ressource des Expert Advisors verwenden werden. Warum? Denn wir wollen, dass der Indikator das Fenster für uns erstellt. Der Expert Advisor kann dies jedoch nicht direkt tun. Aus diesem Grund verwenden wir einen Indikator.

Wenn der Expert Advisor an einen Chart angehängt wird, fügt er den Indikator hinzu (Zeile 16), um das erforderliche Fenster zu erstellen. Der bisherige Indikatorcode reicht jedoch nicht aus, da er vom Nutzer direkt zu einem Chart hinzugefügt werden kann. Wir möchten, dass der Expert Advisor sie automatisch hinzufügt, damit der Nutzer sie nicht manuell hinzufügen muss.

Wie können wir dieses Problem lösen? Einfach. Wir ändern den Indikator so, dass die Nutzer ihn nicht auf dem Chart platzieren können. Sobald der Expert Advisor kompiliert ist, kann die ausführbare Datei des Indikators sogar gelöscht werden, da sie eingebettet wird. Wir brauchen aber auch eine Sicherung für den Fall, dass die ausführbare Datei zugänglich ist. Die Lösung findet sich im folgenden Code:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Base indicator for Mini Chat."
04. #property description "It cannot be used without outside assistance."
05. #property version   "1.00"
06. #property indicator_chart_window
07. #property indicator_plots 0
08. //+------------------------------------------------------------------+
09. #define def_ShortName "Mini Chat"
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.    long id = ChartID();
14.    string sz0 = def_ShortName + "_TMP";
15.    int i0;
16.    
17.    IndicatorSetString(INDICATOR_SHORTNAME, sz0);
18.    for (int c0 = (int)ChartGetInteger(id, CHART_WINDOWS_TOTAL) - 1; c0 >= 0; c0--)
19.       if (ChartIndicatorName(id, c0, 0) == def_ShortName)
20.       {
21.          ChartIndicatorDelete(id, ChartWindowFind(id, sz0), sz0);
22.          return INIT_FAILED;
23.       }
24.    IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);   
25.    i0 = ChartWindowFind(id, def_ShortName);
26.    if ((i0 == 0) || (ChartIndicatorsTotal(id, i0) > 1))
27.    {
28.       ChartIndicatorDelete(id, i0, def_ShortName);
29.       return INIT_FAILED;
30.    }
31.    
32.    return INIT_SUCCEEDED;
33. }
34. //+------------------------------------------------------------------+
35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
36. {
37.    return rates_total;
38. }
39. //+------------------------------------------------------------------+

Erste Änderung des Indikatorcodes

Dieser Code verhindert, dass der Nutzer den Indikator auf dem Chart platzieren kann, sodass er nur vom Expert Advisor hinzugefügt werden kann. Versuchen wir zu verstehen, warum dieser Code es dem Nutzer nicht erlaubt, den Indikator im Chart zu platzieren. Beachten Sie, dass wir in Zeile 06 festlegen, dass der Indikator kein neues Fenster erstellt.

In Zeile 09 wird ein Name für den Indikator festgelegt. Dieser Name wird in Tests verwendet. Um einen Indikator aus der Liste entfernen zu können, muss er einen Namen haben. Dieser Name SOLLTE NICHT DER ENDGÜLTIGE sein, sondern nur ein vorübergehender. Dies geschieht in Zeile 14. Dieser vorläufige Name wird dann in Zeile 17 als Indikatorname festgelegt.

Jetzt kommt der interessante Teil: In der Schleife in Zeile 18 suchen wir nach dem Namen des Indikators in der Liste der Indikatoren im Chart. Der Name, den wir suchen, steht jedoch in Zeile 09. Der Test, der dies bewirkt, befindet sich in Zeile 19. Wenn der Indikator gefunden wird, wird Zeile 21 ausgeführt. Diese Zeile löscht nicht den Indikator, der sich bereits im Chart befindet, sondern den Indikator, den wir zu platzieren versuchen. Unmittelbar danach, in Zeile 22, wird gemeldet, dass die Initialisierung fehlgeschlagen ist.

Wenn der Indikator jedoch platziert werden kann, legt 24 den endgültigen Namen für den eingebetteten Indikator fest und stellt sicher, dass nur der Expert Advisor ihn verwenden kann.

Es gibt noch ein weiteres Detail. Dadurch wird verhindert, dass ein Nutzer den Indikator hinzufügt, nachdem er sich bereits im Chart befindet. Aber ein Nutzer könnte trotzdem versuchen, sie zunächst hinzuzufügen. Um dies zu verhindern, führen wir eine zusätzliche Prüfung durch.

Zeile 25 erkennt, in welchem Fenster der Indikator platziert wird. Wenn sich der Indikator im Hauptfenster befindet, ist der Wert 0. Andernfalls wird die Nummer des Teilfensters zurückgegeben. Zeile 26 prüft diesen Wert. Ist der Wert 0, wird der Indikator entfernt. Wenn sich mehr als ein Indikator im Fenster befindet, wird dieser ebenfalls entfernt.

Dies könnte verwirrend sein. „Wenn der EA einen Indikator auf einem Chart platziert, wird er dies dann nicht im Hauptfenster tun? Steht das nicht in Zeile 06, wo es heißt, dass der Indikator im Hauptfenster platziert werden soll?“ Wenn der EA einen Indikator auf dem Chart platziert, erstellt er ein neues Fenster. Wenn also der in Zeile 25 zurückgegebene Wert 0 ist, können wir den Indikator sicher entfernen. Wir können ihn aber auch entfernen, wenn sich ein anderes Element im gleichen Rückgabefenster befindet, da dies darauf hinweist, dass der Nutzer versucht, den Indikator zu platzieren.

Dadurch wird sichergestellt, dass der Nutzer den Indikator nicht manuell in das Chart einfügen kann. Sobald dies erreicht ist, können wir zum nächsten Schritt übergehen.


Implementierung von Interaktionsobjekten

Um unsere MQL5-Anwendung zu vereinfachen, gehen wir folgendermaßen vor: Die Interaktionsobjekte werden im Indikator platziert, während die Verbindungslogik im Expert Advisor untergebracht wird. Das Design wird künftige Erweiterungen des Mini-Chats ermöglichen. Wir werden die Arbeit mit Hilfe von Header-Dateien organisieren. Da alles, was wir tun, Teil des Replay/Simulator-Projekts ist, werden wir den Mini-Chat in denselben Rahmen integrieren. Der erste Schritt besteht darin, zwei neue Ereignisse zu unserer Ereignisaufzählung hinzuzufügen. Dieser Code ist unten zu sehen:

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.                   };
48. //+------------------------------------------------------------------+

Der Quellcode der Datei Defines.mqh

Beachten Sie, dass die alte Datei Defines.mqh zwei neue Zeilen erhalten hat: Zeile 45 und 46. Diese Leitungen ermöglichen uns die Kommunikation über Sockets. Diese Ereignisse wurden jedoch nur hinzugefügt, weil wir den Mini-Chat vom internen Code des Expert Advisors trennen. Mit anderen Worten, wir brauchen eine Möglichkeit für den Indikator, die über den Socket empfangenen Nachrichten anzuzeigen. Da der Indikator keine Sockets direkt verwenden darf, überwacht der Expert Advisor den Socket und leitet ihn an den Mini-Chat weiter, sobald eine Nachricht verfügbar ist. Dies ermöglicht uns den Zugriff auf die Nachrichten innerhalb des Indikators.

Gut. Sehen wir uns nun die Header-Datei an, die für die Erstellung von Objekten im Chart verantwortlich ist. Er ist unten zu sehen.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include  "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ShortName    "Mini Chat"
007. #define def_MaxRows      256
008. #define def_FontName     "Lucida Console"
009. #define def_FontSize     12
010. #define def_SizeControls (m_txtHeight + 6)
011. #define macroColorRGBA(A) ((uint)((0xFF << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16)))
012. //+------------------------------------------------------------------+
013. class C_Chat
014. {
015.    private   :
016.       long   m_id;
017.       int    m_sub;
018.       int    m_txtHeight;
019.       bool   m_full;
020.       ushort m_Width,
021.              m_Height,
022.              m_index;
023.       uint   m_Pixel[];
024.       string m_ObjEdit,
025.              m_ObjBtn,
026.              m_ObjPanel;
027.       struct st0
028.       {
029.          string info;
030.          bool   loc;
031.       }m_Msgs[def_MaxRows + 1];
032. //+------------------------------------------------------------------+
033.       void Add(string szMsg, bool isloc = false)
034.       {
035.          m_Msgs[m_index].info = szMsg;
036.          m_Msgs[m_index].loc = isloc;
037.          if ((++m_index) > def_MaxRows)
038.          {
039.             m_full = true;
040.             m_index = 0;
041.          }
042.          Paint();
043.       };
044. //+------------------------------------------------------------------+
045.       void Paint(void)
046.       {
047.          int max, count, p0, p1;
048.          
049.          ArrayInitialize(m_Pixel, macroColorRGBA(clrBlack));
050.          if ((p0 = m_Height - def_SizeControls) < 0) return;
051.          max = (int)(floor(p0 / (m_txtHeight * 1.0)));
052.          p1 = m_index - max;
053.          if (m_full)
054.             count = (max > def_MaxRows ? m_index + 1 : (p1 > 0 ? p1 : (def_MaxRows + p1 + 1)));
055.          else
056.             count = (p1 > 0 ? p1 : 0);         
057.          for (ushort row = 0; row < p0; count++)
058.          {
059.             count = (count > def_MaxRows ? 0 : count);
060.             if (count == m_index) break;
061.             TextOut(m_Msgs[count].info, 2, row, 0, m_Pixel, m_Width, m_Height, macroColorRGBA(m_Msgs[count].loc ? clrSkyBlue : clrLime), COLOR_FORMAT_ARGB_NORMALIZE);
062.             row += (ushort) m_txtHeight;
063.          }
064.          ResourceCreate("::" + m_ObjPanel, m_Pixel, m_Width, m_Height, 0, 0, 0, COLOR_FORMAT_ARGB_NORMALIZE);
065.          ChartRedraw();
066.       }
067. //+------------------------------------------------------------------+
068.       void CreateObjEdit(const string szArg)
069.       {
070.          ObjectCreate(m_id, szArg, OBJ_EDIT, m_sub, 0, 0);
071.          ObjectSetInteger(m_id, szArg, OBJPROP_XDISTANCE, 2);
072.          ObjectSetInteger(m_id, szArg, OBJPROP_YDISTANCE, 0);
073.          ObjectSetInteger(m_id, szArg, OBJPROP_YSIZE, def_SizeControls);
074.          ObjectSetString(m_id, szArg, OBJPROP_FONT, def_FontName);
075.          ObjectSetInteger(m_id, szArg, OBJPROP_FONTSIZE, def_FontSize);
076.          ObjectSetInteger(m_id, szArg, OBJPROP_BGCOLOR, clrDarkGray);
077.          ObjectSetInteger(m_id, szArg, OBJPROP_COLOR, clrBlack);
078.          ObjectSetInteger(m_id, szArg, OBJPROP_BORDER_COLOR, clrNavy);
079.       }
080. //+------------------------------------------------------------------+
081.       void CreateObjButton(const string szArg, const string szTxt)
082.       {
083.          ObjectCreate(m_id, szArg, OBJ_BUTTON, m_sub, 0, 0);
084.          ObjectSetInteger(m_id, szArg, OBJPROP_YDISTANCE, 0);
085.          ObjectSetInteger(m_id, szArg, OBJPROP_XSIZE, 70);
086.          ObjectSetInteger(m_id, szArg, OBJPROP_YSIZE, def_SizeControls);
087.          ObjectSetString(m_id, szArg, OBJPROP_FONT, def_FontName);
088.          ObjectSetInteger(m_id, szArg, OBJPROP_FONTSIZE, def_FontSize);
089.          ObjectSetInteger(m_id, szArg, OBJPROP_BGCOLOR, clrSkyBlue);
090.          ObjectSetInteger(m_id, szArg, OBJPROP_COLOR, clrBlack);
091.          ObjectSetInteger(m_id, szArg, OBJPROP_BORDER_COLOR, clrBlack);
092.          ObjectSetString(m_id, szArg, OBJPROP_TEXT, szTxt);
093.       }
094. //+------------------------------------------------------------------+
095.       void CreateObjPanel(const string szArg)
096.       {      
097.          ObjectCreate(m_id, szArg, OBJ_BITMAP_LABEL, m_sub, 0, 0);
098.          ObjectSetInteger(m_id, szArg, OBJPROP_XDISTANCE, 2);
099.          ObjectSetInteger(m_id, szArg, OBJPROP_YDISTANCE, m_txtHeight + 8);
100.          ObjectSetString(m_id, szArg, OBJPROP_BMPFILE, "::" + m_ObjPanel);
101.       }
102. //+------------------------------------------------------------------+
103.    public   :
104. //+------------------------------------------------------------------+
105.       C_Chat()
106.          :m_index(0),
107.           m_full(false),
108.           m_Width(0),
109.           m_Height(0)
110.       {         
111.          int tmp;
112.          
113.          m_sub = ChartWindowFind(m_id = ChartID(), def_ShortName);
114.          TextSetFont(def_FontName, -10 * def_FontSize, 0, 0);
115.          TextGetSize("M", tmp, m_txtHeight);
116.          CreateObjEdit(m_ObjEdit = def_ShortName + " Edit" + (string)ObjectsTotal(m_id));
117.          CreateObjButton(m_ObjBtn = def_ShortName + " Button" + (string)ObjectsTotal(m_id), "Send");
118.          CreateObjPanel(m_ObjPanel = def_ShortName + " Panel" + (string)ObjectsTotal(m_id));
119.       }
120. //+------------------------------------------------------------------+
121.       ~C_Chat()
122.       {
123.          ObjectsDeleteAll(m_id, def_ShortName);
124.       };
125. //+------------------------------------------------------------------+
126.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
127.       {
128.          switch(id)
129.          {
130.             case CHARTEVENT_CHART_CHANGE:
131.                m_Width = (ushort)ChartGetInteger(m_id, CHART_WIDTH_IN_PIXELS, m_sub);
132.                m_Height = (ushort)ChartGetInteger(m_id, CHART_HEIGHT_IN_PIXELS, m_sub);
133.                ObjectSetInteger(m_id, m_ObjEdit, OBJPROP_XSIZE, m_Width - 75);
134.                ObjectSetInteger(m_id, m_ObjBtn, OBJPROP_XDISTANCE, m_Width - 72);
135.                ObjectSetInteger(m_id, m_ObjPanel, OBJPROP_XSIZE, m_Width - 4);
136.                ObjectSetInteger(m_id, m_ObjPanel, OBJPROP_YSIZE, m_Height - 4);
137.                ArrayResize(m_Pixel, m_Width * m_Height);
138.                Paint();
139.                break;
140.             case CHARTEVENT_OBJECT_CLICK:
141.                if (sparam == m_ObjBtn)
142.                {
143.                   string sz0 = ObjectGetString(m_id, m_ObjEdit, OBJPROP_TEXT);
144.                   if (sz0 != "")
145.                   {
146.                      EventChartCustom(m_id, evChatWriteSocket, 0, 0, sz0);
147.                      Add(sz0, true);
148.                      ObjectSetString(m_id, m_ObjEdit, OBJPROP_TEXT, "");
149.                      ObjectSetInteger(m_id, m_ObjBtn, OBJPROP_STATE, 0);
150.                   }                  
151.                }
152.                break;
153.             case CHARTEVENT_CUSTOM + evChatReadSocket:
154.                Add(sparam);
155.                break;
156.             }
157.       }
158. //+------------------------------------------------------------------+
159. };
160. //+------------------------------------------------------------------+
161. #undef macroColorRGBA
162. #undef def_MaxRows
163. //+------------------------------------------------------------------+

Code der Datei C_Chat.mqh

Dieser Code erzeugt die Objekte, mit denen wir interagieren werden. Da ich weiß, dass diese Artikel von vielen Enthusiasten gelesen werden, bitte ich diejenigen, die bereits über gute Programmierkenntnisse verfügen, um Geduld. Ich werde diesen Code ausführlich erläutern, damit jeder, der ihn verwenden und verbessern möchte, dies tun kann. Gleichzeitig soll es mehr Leser dazu motivieren, eigene Lösungen zu entwickeln.

Schauen wir uns die Erklärungen an. Zeile 04 enthält die Header-Datei mit einigen unserer Definitionen. Die Definitionen, die wir tatsächlich verwenden werden, sind jedoch die kürzlich hinzugefügten. In Zeile 06 wird ein Name für unseren Indikator festgelegt. Zeile 07 legt die maximale Anzahl der Zeilen fest, die unser Mini-Chat anzeigen wird. Dies wird später noch näher erläutert. In den Zeilen 08 und 09 werden der Name und die Größe der Schrift festgelegt. Dies erleichtert die globale Anpassung der Abmessungen: Einmal hier eingestellt, passt sich der gesamte Mini-Chat entsprechend an. In Zeile 10 wird die Höhe der Steuerelemente festgelegt, um Anzeigeprobleme zu vermeiden. Zeile 11 ist ein Makro zur Platzierung von Text im Chart. Dies wird weiter unten erläutert.

Mit diesen Definitionen können wir der Klasse beginnen. In Zeile 15 wird der private Bereich deklariert. Alle Variablen und Funktionen, die nach diesem Punkt deklariert werden, gehören ausschließlich zu der Klasse C_Chat. In den Zeilen 16 bis 26 wird die Mindestanzahl der benötigten Variablen festgelegt. Wenn Sie dem Mini-Chat weitere Funktionen hinzufügen möchten, sollten Sie die Anzahl der Objekte erhöhen. Dies sollte hier geschehen. In Zeile 27 wird eine Struktur zur Speicherung von Nachrichten deklariert, die einen schnellen Zugriff auf die von anderen Nutzern eingestellten Nachrichten ermöglicht.

Wichtiges Detail: Der Server speichert keine Nachrichten. Alle Nachrichten, die vor dem Verbindungsaufbau gesendet wurden, gehen verloren. Es gibt noch ein weiteres wichtiges Detail: Wenn Sie den Zeitrahmen des Charts ändern, werden alle Chart-Objekte entfernt. Daher gehen auch die in dieser Struktur gespeicherten Nachrichten verloren. Um Nachrichten aufzubewahren, müssen Sie eine Methode implementieren, um sie in einer Datei zu speichern und wiederherzustellen. Sie brauchen nur die in Zeile 27 definierte Struktur zu speichern und beim Zurücklesen die unten erläuterte Prozedur aufzurufen. Das ist einfach.

Sehen Sie sich nun Zeile 31 an. Er verwendet den in Zeile 7 definierten Wert. Beachten Sie, dass wir eine hinzufügen, weil MQL5, wie C/C++, nullbasierte Indizierung verwendet. Durch die Hinzufügung von eins wird sichergestellt, dass die letzten 256 Zeilen wie angegeben erhalten bleiben. Sie können diese Zahl nach Bedarf anpassen.

Jetzt kommen wir zum funktionalen Teil der Klasse: die Prozeduren, mit denen der Mini-Chat funktioniert. Das erste Verfahren ist im Gange. Diese Prozedur fügt neue Nachrichten zu der in Zeile 27 deklarierten Struktur hinzu. In den Zeilen 35 und 36 werden den entsprechenden Feldern Werte zugewiesen.

In Zeile 37 wird geprüft, ob der Nachrichtenzähler das Maximum erreicht hat. Ist dies der Fall, wird der Zähler auf Null gesetzt. Dies geschieht in Zeile 40. Um zu wissen, wann diese Grenze erreicht ist, verwenden wir Zeile 39, die ein Flag setzt, das anzeigt, dass die Nachrichtenliste voll ist.

Warum ist dies notwendig? Sobald die maximale Anzahl von Nachrichten erreicht ist, werden ältere Nachrichten überschrieben. So entsteht ein zirkulärer Puffer: Neue Nachrichten werden in die ältesten Positionen geschrieben, während die Liste voll bleibt. Dieses Verhalten kann auf Wunsch geändert werden.

Der entscheidende nächste Schritt ist die Anzeige der Meldung auf dem Chart, die in der Prozedur ab Zeile 45 erfolgt.

Bevor wir das analysieren, sollten wir uns einige andere Einrichtungsschritte ansehen, um Zeile 45 richtig zu verstehen. Springen wir zu Zeile 95. Hier erstellen wir ein Panel, das den Text aus der kreisförmigen Liste enthält. Wir machen es sehr einfach. Zeile 97 erzeugt ein OBJ_BITMAP_LABEL-Objekt. In den Zeilen 98-99 wird die linke obere Ecke des Objekts festgelegt. Die wirklich wichtige Zeile ist die Zeile 100, die angibt, welche Bitmap verwendet werden soll.

Doch halt. Bitmap? Sie werden sich vielleicht fragen, warum wir eine Bitmap anstelle von einfachem Text verwenden. Das macht keinen Sinn. MQL5 bietet jedoch keine einfache Möglichkeit, Text in einem Chart an den von uns benötigten Stellen zu zeichnen. Die Funktion Comment() ist unzureichend. Wir müssen die Kontrolle darüber haben, wo und wie der Text angezeigt wird. Die Verwendung einer Bitmap ermöglicht es uns, Buchstaben präzise zu zeichnen, und MetaTrader 5 rendert sie für die Anzeige.

Obwohl es kompliziert klingt, ist es eigentlich ganz einfach. In Zeile 100 wird lediglich der Name der Bitmap definiert. Jetzt können wir zu Zeile 45 zurückkehren. Was dort getan wird, wird bald einen Sinn ergeben.

Zunächst wird in Zeile 49 die Pixelmatrix gelöscht. Sie können jede Farbe verwenden. In diesem Fall ist die Hintergrundfarbe clrBlack, was bedeutet, dass der Hintergrund des Textfeldes schwarz ist. Stellen Sie ihn auf einen beliebigen Wert ein. Unmittelbar danach führen wir eine kleine Berechnung durch, um zu prüfen, ob das Mini-Chat-Fenster ein Textfeld enthalten soll. Wenn nicht, steigen wir aus. Andernfalls zeichnen wir einen Text.

In Zeile 51 wird dann die maximale Anzahl von Zeilen innerhalb des zulässigen Bereichs des Textfensters gezählt. In Zeile 52 wird der Anfangspunkt der kreisförmigen Liste gesucht, um alle möglichen Zeilen anzuzeigen. Hier ein Detail: Wenn die Liste voll ist, kann die Variable m_index kleiner, größer oder gleich der Anzahl der Zeilen im Panel sein. Daher müssen wir diesen Wert korrigieren.

In den Zeilen 53-56 wird dieser Index korrekt angepasst. Jetzt zeigt die Variable, die für die Zählung verwendet wird, tatsächlich auf die älteste Zeile, die angezeigt werden soll. In Zeile 57 beginnen wir mit der Schleife. Diese Schleife mag Ihnen seltsam vorkommen, aber sie ist ganz einfach. Es wird lediglich die kreisförmige Liste Position für Position durchlaufen, und in Zeile 61 wird mit einem Aufruf der MQL5-Bibliothek der Text auf die Bitmap gezeichnet.

Sobald diese Schleife beendet ist, enthält die Bitmap den darauf gezeichneten Text. Anschließend müssen wir MetaTrader 5 mitteilen, dass die Bitmap nun verwendet werden kann. Dies geschieht in Zeile 64. Achten Sie genau auf diese Abfolge von Ereignissen. Zunächst legen wir den Namen der Bitmap fest. Dann zeichnen wir es. Und schließlich teilen wir MetaTrader 5 mit, dass die Bitmap nun im Chart angezeigt werden kann. Genau das geschieht in Zeile 64. Einfach, nicht wahr?

Die Prozeduren von Zeile 68 bis Zeile 93 sind in der MQL5-Programmierung recht gebräuchlich und müssen daher nicht weiter erläutert werden. Wir sind jedoch noch nicht fertig. Wir gehen nun zum Klassenkonstruktor über, der nach der Deklaration einer öffentlichen Klausel in Zeile 103 erscheint. Somit wird alles nach Zeile 103 außerhalb der Klasse sichtbar sein. In diesem Konstruktor, der sich in Zeile 105 befindet, initialisieren wir unseren Mini-Chat. Dies geschieht in wenigen, sehr einfachen Schritten.

Zunächst müssen wir den Index des Fensters erfassen, in dem der Mini-Chat angezeigt werden soll. Dies geschieht in Zeile 113. Unmittelbar danach, in Zeile 114, teilen wir MetaTrader 5 mit, wie die Schriftart für die Erstellung von Bitmap-Text definiert werden soll. In Zeile 115 erfassen wir die Höhe der Buchstaben. Das liegt daran, dass die Buchstaben je nach Konfiguration des Betriebssystems unterschiedlich groß sein können. Um eine seltsam anmutende Anzeige zu vermeiden, erfassen wir die Texthöhe.

Als Nächstes erstellen wir zwischen den Zeilen 116 und 118 die Objekte, die wir benötigen. Wenn Sie zusätzliche Objekte benötigen, sollten Sie sie an dieser Stelle hinzufügen. Achten Sie nur darauf, dass Sie die Regeln für die Erstellung der im Code gezeigten Objekte befolgen.

Der Destruktor in Zeile 121 hat nur eine Aufgabe: die aus dem Chart erstellten Objekte zu entfernen. Aus diesem Grund enthält er nur die Zeile 123.

Schließlich kommen wir zu Zeile 126, in der wir die Nachrichten behandeln, die MetaTrader 5 an unseren Indikator weiterleitet. Denken Sie daran, dass wir uns immer noch innerhalb des Indikatorcodes befinden. Um die Dinge so einfach wie möglich zu halten, behandeln wir hier nur drei Arten von Ereignissen bzw. Nachrichten. Eine davon ist CHARTEVENT_CHART_CHANGE, die die korrekte Position und die Abmessungen der Objekte definiert. Dies ist die erste Meldung, die von MetaTrader 5 generiert wird, sobald unser Code auf dem Chart platziert wird.

Wir behandeln auch die Nachricht CHARTEVENT_OBJECT_CLICK, die auftritt, wenn ein Klick auf das Chart erfolgt. In diesem Fall wird in Zeile 141 geprüft, ob der Klick auf die Schaltfläche „Nachricht senden“ erfolgte. Wenn ja, wird in Zeile 143 der Text im Bearbeitungsbereich erfasst, damit er kurz darauf gesendet werden kann. In Zeile 144 wird dann überprüft, ob der Text nicht leer ist. Wenn dies nicht der Fall ist, wird in Zeile 146 ein nutzerdefiniertes Ereignis ausgelöst, um denselben Text an den Expert Advisor zu senden.

Ein wichtiges Detail: Obwohl der Expert Advisor direkt auf den Text im Bearbeitungsobjekt zugreifen könnte, sollten wir nicht das Risiko eingehen, den Text vom Expert Advisor abrufen zu lassen. Der Grund dafür ist, dass der Indikator bereits durch Zeile 148 zerstört worden sein kann. In jedem Fall wird derselbe Text, den wir über das nutzerdefinierte Ereignis senden, im Panel in Zeile 147 angezeigt.

Schließlich haben wir in Zeile 153 ein nutzerdefiniertes Ereignis. Sein Zweck ist es, den Text zu erfassen, den der Expert Advisor von dem Socket erhalten hat, und ihn in das Textfeld unseres Mini-Chats einzufügen. Das mag Ihnen, lieber Leser, jetzt verwirrend vorkommen, vor allem, wenn Sie direkt auf diesen Artikel gestoßen sind. In früheren Artikeln dieser Serie habe ich bereits ausführlich erklärt, wie man mit nutzerdefinierten Ereignissen arbeitet. Wenn Sie neugierig sind, schauen Sie sich diese früheren Artikel an. Sie werden Ihnen helfen zu verstehen, was hier vor sich geht.

Nun gut, dann wollen wir uns als letzten Code in diesem Artikel die endgültige Version des Mini-Chat-Indikators ansehen. Sie können es unten sehen:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Base indicator for Mini Chat."
04. #property description "It cannot be used without outside assistance."
05. #property version   "1.00"
06. #property link "https://www.mql5.com/pt/articles/12672"
07. #property indicator_chart_window
08. #property indicator_plots 0
09. //+------------------------------------------------------------------+
10. #include <Market Replay\Mini Chat\C_Chat.mqh>
11. //+------------------------------------------------------------------+
12. C_Chat *Chat;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.    long id = ChartID();
17.    string sz0 = def_ShortName + "_TMP";
18.    int i0;
19.    
20.    IndicatorSetString(INDICATOR_SHORTNAME, sz0);
21.    for (int c0 = (int)ChartGetInteger(id, CHART_WINDOWS_TOTAL) - 1; c0 >= 0; c0--)
22.       if (ChartIndicatorName(id, c0, 0) == def_ShortName)
23.       {
24.          ChartIndicatorDelete(id, ChartWindowFind(id, sz0), sz0);
25.          return INIT_FAILED;
26.       }
27.    IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);   
28.    i0 = ChartWindowFind(id, def_ShortName);
29.    if ((i0 == 0) || (ChartIndicatorsTotal(id, i0) > 1))
30.    {
31.       ChartIndicatorDelete(id, i0, def_ShortName);
32.       return INIT_FAILED;
33.    }
34.    
35.    Chat = new C_Chat();
36.    
37.    return INIT_SUCCEEDED;
38. }
39. //+------------------------------------------------------------------+
40. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
41. {
42.    return rates_total;
43. }
44. //+------------------------------------------------------------------+
45. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
46. {
47.    (*Chat).DispatchMessage(id, lparam, dparam, sparam);
48. }
49. //+------------------------------------------------------------------+
50. void OnDeinit(const int reason)
51. {
52.    delete Chat;
53. }
54. //+------------------------------------------------------------------+

Quellcode des Indikators


Abschließende Überlegungen

In diesem Artikel haben wir den ersten Teil des Mini-Chat-Codes vorgestellt. Der Expert Advisor und der Servercode müssen noch diskutiert werden. Da dieses Thema sehr umfangreich ist, können Sie den Code des Indikators noch einmal durchgehen und mit ihm experimentieren. Im nächsten Artikel werden wir die Mini-Chat-Implementierung, einschließlich des Servers, fertigstellen und in Aktion sehen.

DateiBeschreibung
Experts\Expert Advisor.mq5
Demonstriert die Interaktion zwischen Chart Trade und dem Expert Advisor (für die Interaktion ist ein Mauszeiger erforderlich)
Indicators\Chart Trade.mq5Erzeugt ein Fenster zur Konfiguration des zu versendenden Auftrags (Mouse Study ist für die Interaktion erforderlich)
Indicators\Market Replay.mq5Erstellt Steuerelemente für die Interaktion mit dem Wiedergabe-/Simulationsdienst (Mausstudie ist für die Interaktion erforderlich)
Indicators\Mouse Study.mq5Ermöglicht die Interaktion zwischen den grafischen Steuerelementen und dem Nutzer (erforderlich für den Betrieb des Replay-Simulators und des Live-Markthandels)
Servicios\Market Replay.mq5Erstellt und pflegt den Marktwiedergabe- und Simulationsdienst (Hauptdatei des gesamten Systems)
VS C++ Server.cppErstellt und pflegt einen Socket-Server in C++ (Mini-Chat-Version)
Python code Server.pyErstellt und pflegt einen Python-Socket für die Kommunikation zwischen MetaTrader 5 und Excel
ScriptsCheckSocket.mq5Überprüft die Verbindung zu einem externen Socket
Indicators\Mini Chat.mq5Implementiert einen Mini-Chat als Indikator (erfordert einen Server)
Experts\Mini Chat.mq5Implementiert einen Mini-Chat als Expert Advisor (erfordert einen Server)

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

Beigefügte Dateien |
Anexo.zip (560.03 KB)
Entwicklung eines Expert Advisors für mehrere Währungen (Teil 22): Beginn des Übergangs zum Hot-Swapping von Einstellungen Entwicklung eines Expert Advisors für mehrere Währungen (Teil 22): Beginn des Übergangs zum Hot-Swapping von Einstellungen
Wenn wir die periodische Optimierung automatisieren wollen, müssen wir über automatische Aktualisierungen der Einstellungen der bereits auf dem Handelskonto laufenden EAs nachdenken. Dies sollte es uns auch ermöglichen, den EA im Strategietester laufen zu lassen und seine Einstellungen in einem einzigen Durchgang zu ändern.
Von der Grundstufe bis zur Mittelstufe: Structs (II) Von der Grundstufe bis zur Mittelstufe: Structs (II)
In diesem Artikel werden wir versuchen zu verstehen, warum Programmiersprachen wie MQL5 Strukturen haben und warum in einigen Fällen Strukturen der ideale Weg sind, um Werte zwischen Funktionen und Prozeduren zu übergeben, während sie in anderen Fällen vielleicht nicht der beste Weg sind, dies zu tun.
Kreis-Such-Algorithmus (CSA) Kreis-Such-Algorithmus (CSA)
Der Artikel stellt einen neuen metaheuristischen Optimierungs-Kreis-Such-Algorithmus (CSA) vor, der auf den geometrischen Eigenschaften eines Kreises basiert. Der Algorithmus nutzt das Prinzip der Bewegung von Punkten entlang von Tangenten, um die optimale Lösung zu finden, und kombiniert die Phasen der globalen Erkundung und der lokalen Ausbeutung.
Chaos Game Optimization (CGO) Chaos Game Optimization (CGO)
Der Artikel stellt einen neuen metaheuristischen Algorithmus, Chaos Game Optimization (CGO), vor, der eine einzigartige Fähigkeit zur Aufrechterhaltung einer hohen Effizienz bei hochdimensionalen Problemen aufweist. Im Gegensatz zu den meisten Optimierungsalgorithmen verliert CGO nicht nur nicht an Leistung, sondern steigert sie manchmal sogar, wenn ein Problem skaliert wird, was sein Hauptmerkmal ist.