
Entwicklung eines Replay-Systems (Teil 76): Neuer Chart Trade (III)
Einführung
Im vorherigen Artikel „Entwicklung eines Replay-Systems (Teil 75): Neuer Chart Trade (II)“ habe ich verschiedene Aspekte der Klasse C_ChartFloatingRAD erläutert. Da das Material jedoch recht konzentriert war, habe ich mich bemüht, möglichst ausführliche Erklärungen zu geben, wo immer dies möglich war. Ein Verfahren war noch zu erörtern. Selbst wenn ich es in der Header-Datei C_ChartFloatingRAD.mqh belassen hätte und versucht hätte, es im vorherigen Artikel zu erklären, wäre es nicht ausreichend gewesen. Um die Funktionsweise des DispatchMessage-Verfahrens vollständig zu verstehen, muss nämlich noch ein weiteres Thema erläutert werden.
In diesem Artikel werde ich näher darauf eingehen, wie das DispatchMessage-Verfahren wirklich funktioniert. Dies ist die wichtigste Prozedur der Klasse C_ChartFloatingRAD, da sie für die Erzeugung und Reaktion auf die Ereignisse, die MetaTrader 5 an den Chart Trade sendet, verantwortlich ist.
Der Inhalt dieses Artikels sollte in Verbindung mit dem vorherigen Artikel gelesen werden. Ich empfehle nicht, sich mit diesem Artikel zu befassen, ohne vorher gründlich zu verstehen, was zuvor behandelt wurde. Die letzten beiden Artikel und dieser Artikel bilden zusammen die vollständige konzeptionelle Grundlage für den Indikator Chart Trade. Daher ist es wichtig, sie alle zu verstehen.
Fahren wir also mit der Erklärung von DispatchMessage fort.
Verstehen der Funktionsweise von DispatchMessage
Wenn Sie immer noch erwarten, dass hier für den Chart Trade zahlreiche Objekte kodiert werden, dann haben Sie noch nicht ganz verstanden, wie die Dinge funktionieren. Bevor wir fortfahren, empfehle ich dringend, den vorherigen Artikel noch einmal zu lesen. Diesmal geht es darum, was Chart Trade funktional macht, indem es auf Ereignisse reagiert und diese erzeugt. Wir werden hier keine Objekte erstellen. Unsere Aufgabe besteht einfach darin, die vom MetaTrader 5 gemeldeten Ereignisse funktionsfähig zu machen.
Im letzten Artikel haben Sie vielleicht bemerkt, dass die Header-Datei C_ChartFloatingRAD.mqh einen Bereich enthielt, in dem Code fehlte. Der fehlende Code ist im folgenden Ausschnitt dargestellt:
259. //+------------------------------------------------------------------+ 260. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 261. { 262. #define macro_AdjustMinX(A, B) { \ 263. B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x; \ 264. mx = x - m_Info.Regions[MSG_TITLE_IDE].w; \ 265. A = (B ? (mx > 0 ? mx : 0) : A); \ 266. } 267. #define macro_AdjustMinY(A, B) { \ 268. B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y; \ 269. my = y - m_Info.Regions[MSG_TITLE_IDE].h; \ 270. A = (B ? (my > 0 ? my : 0) : A); \ 271. } 272. 273. static short sx = -1, sy = -1, sz = -1; 274. static eObjectsIDE obj = MSG_NULL; 275. short x, y, mx, my; 276. double dvalue; 277. bool b1, b2, b3, b4; 278. ushort ev = evChartTradeCloseAll; 279. 280. switch (id) 281. { 282. case CHARTEVENT_CHART_CHANGE: 283. x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS); 284. y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS); 285. macro_AdjustMinX(m_Info.x, b1); 286. macro_AdjustMinY(m_Info.y, b2); 287. macro_AdjustMinX(m_Info.minx, b3); 288. macro_AdjustMinY(m_Info.miny, b4); 289. if (b1 || b2 || b3 || b4) AdjustTemplate(); 290. break; 291. case CHARTEVENT_MOUSE_MOVE: 292. if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (CheckMousePosition(x = (short)lparam, y = (short)dparam)) 293. { 294. case MSG_TITLE_IDE: 295. if (sx < 0) 296. { 297. DeleteObjectEdit(); 298. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 299. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 300. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 301. } 302. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 303. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 304. if (m_Info.IsMaximized) 305. { 306. m_Info.x = (mx > 0 ? mx : m_Info.x); 307. m_Info.y = (my > 0 ? my : m_Info.y); 308. }else 309. { 310. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 311. m_Info.miny = (my > 0 ? my : m_Info.miny); 312. } 313. break; 314. case MSG_BUY_MARKET: 315. ev = evChartTradeBuy; 316. case MSG_SELL_MARKET: 317. ev = (ev != evChartTradeBuy ? evChartTradeSell : ev); 318. case MSG_CLOSE_POSITION: 319. if ((m_Info.IsMaximized) && (sz < 0)) 320. { 321. string szTmp = StringFormat("%d?%s?%c?%d?%.2f?%.2f", ev, _Symbol, (m_Info.IsDayTrade ? 'D' : 'S'), m_Info.Leverage, 322. FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage)); 323. PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp); 324. sz = x; 325. EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp); 326. DeleteObjectEdit(); 327. } 328. break; 329. }else 330. { 331. sz = -1; 332. if (sx > 0) 333. { 334. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 335. sx = sy = -1; 336. } 337. } 338. break; 339. case CHARTEVENT_OBJECT_ENDEDIT: 340. switch (obj) 341. { 342. case MSG_LEVERAGE_VALUE: 343. case MSG_TAKE_VALUE: 344. case MSG_STOP_VALUE: 345. dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT)); 346. if (obj == MSG_TAKE_VALUE) 347. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 348. else if (obj == MSG_STOP_VALUE) 349. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 350. else 351. m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue)); 352. AdjustTemplate(); 353. obj = MSG_NULL; 354. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable); 355. break; 356. } 357. break; 358. case CHARTEVENT_OBJECT_CLICK: 359. if (sparam == m_Info.szObj_Chart) if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (obj = CheckMousePosition(x = (short)lparam, y = (short)dparam)) 360. { 361. case MSG_DAY_TRADE: 362. m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 363. DeleteObjectEdit(); 364. break; 365. case MSG_MAX_MIN: 366. m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 367. DeleteObjectEdit(); 368. break; 369. case MSG_LEVERAGE_VALUE: 370. CreateObjectEditable(obj, m_Info.Leverage); 371. break; 372. case MSG_TAKE_VALUE: 373. CreateObjectEditable(obj, m_Info.FinanceTake); 374. break; 375. case MSG_STOP_VALUE: 376. CreateObjectEditable(obj, m_Info.FinanceStop); 377. break; 378. } 379. if (obj != MSG_NULL) AdjustTemplate(); 380. break; 381. case CHARTEVENT_OBJECT_DELETE: 382. if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown); 383. break; 384. } 385. ChartRedraw(); 386. } 387. //+------------------------------------------------------------------+ 388. }; 389. //+------------------------------------------------------------------+
Fehlender Code in C_ChartFloatingRAD.mqh
Der fehlende Code passt perfekt in diese Lücke, wie das hervorgehobene Fragment unten zeigt. Um den Code der Klasse C_ChartFloatingRAD zu vervollständigen, sollte dieses Fragment genau an dieser Stelle eingefügt werden, und zwar nach den angegebenen Zeilennummern. Dies sollte recht einfach zu bewerkstelligen sein. Schauen wir uns nun an, wie es funktioniert.
Als erstes fällt auf, dass dieses Fragment keine Aufrufe zum Erstellen von Objekten enthält. Wie ich bereits erwähnt habe, programmieren wir hier keine Objekte. Chart Trade wird erstellt und als Vorlage gespeichert. Dies geschieht vollständig im MetaTrader 5. Im vorigen Artikel habe ich am Ende einige Hinweise gegeben, die Ihnen helfen sollen, dieses Konzept zu verstehen.
Diese Art der Programmierung wird von den meisten Entwicklern nicht häufig verwendet. Meiner Meinung nach ist es aber in vielen Fällen viel praktischer und einfacher. Sie müssen nicht jedes Objekt manuell codieren und positionieren. Stattdessen können Sie einfach auf sie in der Vorlage verweisen. Und wir erstellen sie nur dann programmatisch, wenn MetaTrader 5 sie nicht direkt konfigurieren kann. Wie bereits erwähnt, ist die Erstellung von Elementen und deren Speicherung als Vorlage ein eher seltener Vorgang. Das einzige Objekt, das auf diese Weise nicht behandelt werden konnte, war OBJ_EDIT. Dies liegt daran, dass die Erstellung der Logik für die Texteingabe und -manipulation umständlicher ist als die Erstellung des OBJ_EDIT-Objekts selbst. Daher ist dies das einzige Objekt, das tatsächlich erstellt wird.
Wenn Sie sich das Fragment ansehen, werden Sie feststellen, dass es sich ausschließlich mit der Ereignisbehandlung befasst. Das mag Sie überraschen, wenn Sie das Video im vorherigen Artikel gesehen oder die beigefügten ausführbaren Dateien ausgeführt haben. Schließlich scheint der Code nicht viele Objekte zu enthalten, und doch funktioniert er genau wie gezeigt. Sie haben vielleicht angenommen, dass innerhalb von DispatchMessage andere Objekte erscheinen. Aber wie Sie sehen können, gibt es keine. ES GIBT KEINE ANDEREN OBJEKTE. Alles ist nur die Verarbeitung von Ereignissen aus MetaTrader 5. Wie funktioniert das also?
Lassen Sie uns Schritt für Schritt vorgehen. Wir behandeln hier fünf Arten von Ereignissen, die vom MetaTrader 5 gesendet werden. Bevor Sie die Zeilen einzeln betrachten, sollten Sie beachten, dass in den Zeilen 273-278 einige Variablen deklariert werden. Als statisch deklarierte Variablen sind solche, die sich im globalen Bereich der Klasse befinden können. Die als statisch gekennzeichneten Variablen behalten ihre Werte zwischen den Aufrufen und hätten auch Klassenvariablen sein können, aber sie werden nur innerhalb dieser Prozedur benötigt. Wenn Sie genau hinsehen, werden Sie feststellen, dass die in Zeile 278 deklarierte Variable nicht statisch ist. Sie wird zum Zeitpunkt seiner Deklaration initialisiert. Dadurch werden Probleme im Zusammenhang mit der falschen Verwendung dieser Variablen vermieden. Der zugewiesene Wert entspricht dem Ereignis, das in der Datei Defines.mqh deklariert ist.
Beginnen wir mit dem Ereignis CHARTEVENT_CHART_CHANGE, das in den Zeilen 282-290 erscheint. Sie wird immer dann ausgelöst, wenn sich das Chart ändert. Es führt einfache Berechnungen durch, um das OBJ_CHART-Objekt korrekt im Chartfenster zu positionieren und zu halten.
Das zweite Ereignis, CHARTEVENT_MOUSE_MOVE in den Zeilen 291-338, ist der längste Ereignisbehandlung in DispatchMessage. Dieses Ereignis wird ausgelöst, wenn sich die Maus bewegt oder ihre Tasten gedrückt werden. Standardmäßig sendet der MetaTrader 5 diese jedoch nicht. Wir müssen ausdrücklich angeben, dass wir Mausereignisse empfangen wollen. Diese Ereignisse werden vom MetaTrader 5 über den Mausindikator abgefragt. Dieser Indikator wurde in früheren Artikeln ausführlich beschrieben. Das Verständnis des Mauszeigers ist entscheidend, da er die Interaktion des Nutzers mit den von uns erstellten Elementen ermöglicht.
Insbesondere werden hier keine Mausereignisse direkt verarbeitet. Stattdessen wird in Zeile 292 zunächst geprüft, ob ein Linksklick vorliegt. Aber wir müssen auch seine Gültigkeit mit dem Mauszeiger bestätigen. Warum tun wir das? Warum überprüfen wir nicht einfach, ob der Klick gültig ist oder nicht, direkt in der Ereignisbehandlung? Warum müssen wir den Mauszeiger fragen?
Denn der Mauszeiger läuft möglicherweise im „Forschungsmodus“. Wenn dieser Modus auf Wunsch des Nutzers aktiviert ist, sollten keine Klicks verarbeitet werden. Wir können nicht zuverlässig feststellen, wann und wie lange sich der Mauszeiger im „Forschungsmodus“ befinden wird. Daher ist es am besten, stattdessen den Mauszeiger abzufragen. Wenn der Klick gültig ist, konvertieren wir die Positionsdaten aus MetaTrader 5 und prüfen, welches Objekt angeklickt wurde. Dies geschieht über CheckMousePosition. Wir könnten auch die vom Mauszeiger gelieferten Koordinaten verwenden. Aber in diesem Fall müssten wir den Indikatorpuffer lesen. Da die Werte mit denen übereinstimmen, die direkt vom MetaTrader 5 geliefert werden, ist es nicht nötig, den Puffer zu lesen, um diese Informationen zu erhalten.
Bevor wir analysieren, wie der eigentliche Klick behandelt wird, sehen wir uns an, was in Zeile 329 geschieht, die den Fall behandelt, dass das Ereignis CHARTEVENT_MOUSE_MOVE keinen gültigen Klick erhalten hat oder durch eine Bewegung des Nutzers verursacht wurde, der mit der Maus interagiert. In diesem Fall wird der Variablen sz in Zeile 331 ein negativer Wert zugewiesen. In Zeile 332 wird dann geprüft, ob die Variable sx einen positiven Wert hat. Wenn dies der Fall ist, fahren wir mit der Behandlung des Mausereignisses fort, aber das lassen wir vorerst. In Zeile 334 teilen wir MetaTrader 5 also mit, dass die Maus das Chart bewegen kann. Dann weisen wir in Zeile 335 den Variablen sx und sy negative Werte zu, wodurch die Prüfung in Zeile 332 fehlschlägt und verhindert wird, dass Zeile 334 unnötigerweise ausgeführt wird.
Es ist zu beachten, dass Mausereignisse (CHARTEVENT_MOUSE_MOVE) sehr häufig auftreten. In manchen Fällen kann CHARTEVENT_MOUSE_MOVE sogar häufiger auftreten als OnTick. Ein unvorsichtiger Umgang mit diesen häufigen Ereignissen kann den MetaTrader 5 also stark verlangsamen. Aus diesem Grund sind sie standardmäßig deaktiviert.
Okay, das haben wir geklärt. Die Zeilen 293-328 enthalten die Logik für die Interaktion mit bestimmten Elementen von Chart Trade. Wenn CheckMousePosition einen Wert zurückgibt, kann dieser auf eines der in diesem Teil des Codes erwähnten Objekte verweisen, z. B. auf die Titelleiste, die Schaltflächen Market Buy, Market Sell und Close Position. Andere Elemente werden an anderen Stellen behandelt und später beschrieben, aber jedes dieser vier Elemente erfordert einen anderen Ansatz. Beginnen wir mit dem schwierigsten Teil: der Titelleiste.
Wenn die Titelleiste angeklickt wird, kann der Nutzer den Chart Trade verschieben. Die Zeilen 295 bis 313 enthalten also die erforderliche Logik. Wir werden jetzt nicht ins Detail gehen, da dieser Prozess bereits in einem anderen Artikel dieser Serie beschrieben wurde, in dem wir die ersten Versionen von Chart Trade erstellt haben. Wenn Sie Fragen haben, lesen Sie bitte die vorherigen Artikel, sie sind auch wichtig für das, was wir hier entwickeln. Denken Sie daran, dass der Code nicht aus dem Nichts kommt: Er wurde nach und nach erstellt und umgesetzt, bis er die Form angenommen hat, die Sie jetzt sehen.
Der Teil, der uns interessiert, liegt zwischen den Zeilen 314 und 328. Achten Sie also auf den in Zeile 278 definierten Wert. Dies ist wichtig, weil wir hier zwei der drei verwendeten Bedingungen testen werden. Wir behandeln drei Bedingungen, wobei wir als erstes prüfen, ob die Schaltfläche Marktkauf angeklickt wurde. Wenn dies der Fall ist, wird der Variablen „ev“ ein bestimmter Wert zugewiesen, um das Ereignis „Market Buy“ anzuzeigen. Dies geschieht in Zeile 315.
Bitte beachten Sie jetzt: In Zeile 316 wird geprüft, ob die Schaltfläche „Market Sell“ angeklickt wurde. Wir müssen jedoch auch prüfen, ob die Schaltfläche „Market Sell“ angeklickt wurde, um der Variablen „ev“ einen korrekten Wert zuzuweisen. Warum brauchen wir diese Prüfung, wenn wir ein anderes Ereignis behandeln? Ja, das ist ein separates Ereignis.
Der Punkt ist jedoch, dass alle drei Ereignisse („Market Buy“, „Market Sell“ und „Close Position“) die gleiche Art von Aktion ausführen. Es gibt keine Pausen zwischen den Ereignissen, im Gegenteil, sie sind aneinandergereiht. Daher müssen wir in Zeile 317 prüfen, ob das vorherige Ereignis den Wert der Variable ev geändert hat. Wenn dies der Fall ist, sollten wir diese Aktion für das nächste Ereignis in der Reihe nicht durchführen. Andernfalls wird nur das letzte Ereignis ausgelöst und die vorherigen ignoriert.
Auf jeden Fall kommen wir immer zur Zeile 318, wo das Ereignis Close Position behandelt wird. Für dieses Ereignis sind Prüfungen erforderlich, damit MetaTrader 5 das nutzerdefinierte Ereignis ausführen kann. Jetzt werden wir uns ansehen, wie man zu diesem Punkt gelangt, um mögliche Fehler zu vermeiden.
Zeile 319 prüft, ob Chart Trade maximiert ist. Diese Prüfung ist wichtig, weil die Funktion CheckMousePosition nicht berücksichtigt, ob das Fenster maximiert ist. Bitte beachten Sie, dass das nutzerdefinierte Ereignis auch dann ausgelöst wird, wenn das Fenster nicht maximiert ist, wenn wir diese Prüfung nicht durchführen und an der Stelle klicken, an der sich eine der Schaltflächen befinden sollte. Um dieses Problem zu vermeiden und sicherzustellen, dass das Ereignis nur ausgelöst wird, wenn die Objekte im Chart sichtbar sind, überprüfen wir diese Bedingung.
Hier ergibt sich ein wichtiger Punkt: Das Fenster muss maximiert sein, d.h. die Schaltflächen müssen sichtbar sein. Allerdings gibt es bei dieser Prüfung ein kleines Problem. Es ist der Code von Chart Trade, der diese Prüfung durchführt, nicht MetaTrader 5. Warum ist das wichtig? Das Problem tritt auf, wenn die Tasten von einem anderen Objekt verdeckt werden. Wenn wir etwas platzieren, das die Schaltflächen verdeckt, und der Chart Trade maximiert ist, dann wird das nutzerdefinierte Ereignis ausgelöst, wenn wir auf den Bereich klicken, in dem sich die Schaltflächen befinden, und nicht MetaTrader 5, sondern unser Code. Dies ist ein Fehler, den ich in Zukunft beheben möchte. Da dies jedoch nicht so kritisch ist, können wir es vorerst hinnehmen. Sie sollten dieses Problem jedoch berücksichtigen, wenn Sie Chart Trade auf einem echten Konto verwenden. PLATZIEREN SIE NICHTS VOR DEN SCHALTFLÄCHEN, WENN CHART TRADE MAXIMIERT IST.
In der gleichen Zeile 319 gibt es eine weitere Prüfung, die verhindern soll, dass mehrere Ereignisse durch einen Klick gefolgt von einer Mausbewegung ausgelöst werden. Dies ist eine einfache Prüfung, bei der der Wert von sz negativ sein muss. Wenn er positiv ist, bedeutet dies, dass das Ereignis bereits ausgelöst wurde und die Maustaste noch gedrückt ist. Wenn die Taste losgelassen wird und die Maus sich bewegt, sorgt Zeile 331 dafür, dass ein neues Nutzerereignis ausgelöst wird. Obwohl es sich um eine einfache Kontrolle handelt, können dadurch viele Probleme vermieden werden.
Die ganze „Magie“ geschieht in Zeile 321. Um zu verhindern, dass er zu lang wird, habe ich ihn in zwei Teile aufgeteilt. Betrachten wir also Zeile 322 als Teil von Zeile 321. Bevor wir diese Zeile erläutern, wollen wir uns ansehen, was die nächsten vier Zeilen bewirken. Zeile 323 protokolliert das Ereignis in der MetaTrader 5 Toolbox. Sie mögen denken, dass dies nicht notwendig ist, aber glauben Sie mir, es wird Ihnen helfen, viele Probleme zu vermeiden. Dies ist eine unschätzbare Übung zur Fehlersuche. Ignorieren Sie daher niemals die Meldungen, die in der MetaTrader 5 Toolbox erscheinen, viele von ihnen sind sehr wichtig.
Zeile 324 führt lediglich die Sperre durch, um die zuvor beschriebene Bedingung zu verhindern, da die Variable sz bei der Prüfung in Zeile 319 verwendet wird. Das nutzerdefinierte Ereignis wird tatsächlich in Zeile 325 ausgelöst. Beachten Sie, dass die Daten hier, anders als beim Kontrollindikator, im String-Feld gesendet werden. Wäre dies nicht möglich, hätten wir Probleme bei der Übermittlung einer großen Menge an Informationen zwischen den Anwendungen.
Jetzt kommt ein wichtiges Detail. Zeichenketten in MQL5 folgen dem gleichen Prinzip wie in C/C++ - sie enden mit dem Zeichen NULL. Und warum erwähne ich das? Denn es erklärt, warum wir hier die Funktion StringFormat verwenden müssen. Wenn Sie es für unnötig halten, das zu drucken, was über das nutzerdefinierte Ereignis gesendet wird, können Sie einfach den Inhalt aus Zeile 321 nehmen und ihn hier in Zeile 325 einfügen, wobei Sie die Variable szTmp durch die Funktion StringFormat ersetzen und genau denselben Ausdruck wie in Zeile 321 verwenden.
Zurück zur Frage der Zeichenketten: Das Problem ist, dass wir innerhalb der Zeichenkette numerische Werte übergeben müssen. An dieser Stelle fragen Sie sich vielleicht: Warum nicht die Felder lparam und dparam der Nachricht verwenden? Warum müssen wir Sparam verwenden? Der Grund dafür ist einfach die Menge der Daten, die wir weitergeben müssen. Würden wir nur wenige Parameter übergeben, könnten wir lparam und dparam verwenden. Da die Informationsmenge jedoch sehr groß ist, müssen wir das Feld sparam verwenden. Und genau hier machen viele Menschen, insbesondere Anfänger oder angehende Programmierer, Fehler.
Wenn Sie eine Zahl betrachten - zum Beispiel den Wert 65 - kann eine Anwendung sie als die Zahl 65 interpretieren, während eine andere sie als den Buchstaben „A“ interpretieren kann. Viele Leser mögen dies verwirrend finden, aber das Problem ist, dass Sie 65 wörtlich und nicht in binärer Form betrachten. In binärer Form wird sie wie folgt dargestellt 0100 0001 in 8 Bits. Das ist die richtige Denkweise, wenn es um Programmierung geht. Wenn Sie also eine Zeichenkette betrachten, sollten Sie sie nicht als lesbare Zeichen oder als für den Menschen verständlichen Text betrachten. Stellen Sie sich eine Zeichenkette als eine sehr große Zahl vor - nicht nur 1 Byte, 256 Byte oder eine andere feste Größe, sondern möglicherweise eine riesige Folge von Bytes.
Wenn man auf diese Weise darüber nachdenkt, wird etwas anderes leichter verständlich: Wie markiert man das Ende einer Zeichenkette? Einige Programmiersprachen speichern einen Wert am Anfang der Zeichenfolge - einen „toten“ Wert, der die Anzahl der Zeichen oder Bytes angibt, die die Zeichenfolge enthält. BASIC und die alte Sprache Pascal sind Beispiele für diesen Ansatz. In Pascal wird die Länge am Anfang der Zeichenkette gespeichert.
In solchen Fällen können Zeichenketten - oder besser gesagt, Arrays - jeden Byte-Wert von 0 bis 255 enthalten. Während dieser Ansatz in vielen Szenarien gut funktioniert, ist er in anderen schlecht, weil Arrays mit fester Größe Speicher verschwenden. Es kann sein, dass Sie nur 3 Bytes benötigen, aber wenn der Compiler feststellt, dass Sie manchmal 50 Bytes in demselben Array benötigen, wird er das Maximum - 50 Bytes - zuweisen, auch wenn Sie nur 3 benötigen.
Wegen dieser Ineffizienz verwenden andere Sprachen dieses Format für Zeichenketten nicht. Stattdessen legen sie einen bestimmten Code oder Wert fest, der das Ende der Zeichenkette markiert. Die gebräuchlichste Methode ist die Verwendung des NULL-Zeichens. Dies entspricht 0000 0000 im Binärformat. Die Wahl dieses besonderen Zeichens ergibt sich aus den Entscheidungen der Compiler-Implementierung. Theoretisch kann jedes Zeichen verwendet werden. Hier geht es darum, wie sich dies auf unsere Nachricht auswirkt, wenn wir sie innerhalb einer Zeichenkette senden müssen.
Kehren wir zu den numerischen Werten zurück. Unser Chart Trade muss vor allem mit zwei Typen übertragen: „short“ und „double“. Der Typ „short“ verwendet 16 Bits oder 2 Bytes. Ich werde nicht einmal auf den Typ „double“ eingehen; ich werde im Beispiel den Typ „short“ verwenden. In Chart Trade wird ein „short“ für die Hebelwirkung verwendet. Der kleinste Wert für die Hebelwirkung ist 1. Das heißt, in binärer Form: 0000 0000 0000 0001 (hier in 16-Bit-Form geschrieben).
Obwohl „short“ b2 Bytes verwendet, besteht eine Zeichenkette eigentlich aus 1-Byte-Zeichen. Das bedeutet, dass das erste Byte des Wertes 1 in der Zeichenkette das Zeichen NULL darstellt. Das ist richtig. Bevor irgendwelche Informationen gelesen werden können, wäre die Zeichenkette bereits beendet. Aus diesem Grund müssen wir die Werte in druckbare Zeichen umwandeln. Mit anderen Worten: Unabhängig von der binären Form der Daten wandeln wir sie für die Übertragung in eine für Menschen lesbare (druckbare) Form um und konvertieren sie anschließend wieder in ihre binäre Entsprechung. Genau aus diesem Grund müssen wir hier die Funktion StringFormat verwenden.
Abschließende Überlegungen
Obwohl dieser Artikel abrupt zu enden scheint, fühle ich mich nicht wohl dabei, das zweite notwendige Thema - das Kommunikationsprotokoll zwischen Anwendungen - auf so wenig Raum zu erklären. Dies ist ein komplexes Thema, das eine eigene ausführliche Behandlung verdient. Ich kann einfach sagen, dass Zeile 321 das Protokoll erstellt, das wir verwenden werden.
Mit diesem Ansatz bleibt jedoch die gesamte Frage der Kommunikation zwischen Programmen völlig vage und wird Ihnen nicht helfen, wenn Sie versuchen zu verstehen, wie Sie dieses Wissen in Ihren Programmen nutzen können. Obwohl es MetaTrader 5 schon seit einiger Zeit gibt und sich viele Programmierer dafür interessieren, habe ich noch niemanden gesehen, der das hier vorgestellte System verwendet. Die meisten Menschen, die etwas erreichen wollen, fangen bei Null an oder erstellen riesige Programme, die in den meisten Fällen nur sehr schwer zu pflegen, anzupassen oder zu verbessern sind. Mit dem Wissen, das wir hier vermitteln, werden Sie jedoch in der Lage sein, kleinere, viel einfachere Programme zu entwickeln, die auch viel einfacher zu debuggen und zu verbessern sind.
Deshalb wollte ich die Gelegenheit von Chart Trade nutzen, um im Detail zu erklären, wie der Nachrichtenaustausch funktioniert. Ich hoffe, dass Sie als Programmieranfänger erkennen werden, dass wir viel mehr tun können, als man normalerweise von uns erwartet.
Was die übrigen Ereignisse (CHARTEVENT_OBJECT_ENDEDIT, CHARTEVENT_OBJECT_CLICK und CHARTEVENT_OBJECT_DELETE) betrifft, so bin ich zuversichtlich, dass Sie in der Lage sein werden, sie selbst zu verstehen, da ihr Code viel einfacher ist als das, was wir hier besprochen haben.
Im nächsten Artikel werde ich genau erklären, warum die Zeile 321 ihr spezifisches Format hat und wie sie sich auf den Code des Expert Advisors auswirkt, der die Chart Trade-Ereignisse empfängt und es ihm ermöglicht, Aufträge auf der Grundlage der Nutzerinteraktion mit seinen Schaltflächen auszuführen.
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/12443
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.