Entwicklung eines Replay-Systems (Teil 77): Neuer Chart Trade (IV)
Einführung
Im vorherigen Artikel „Entwicklung eines Replay-Systems (Teil 76): Neuer Chart Trade (III)“ habe ich den kritischsten Teil des DispatchMessage-Codes erläutert und begonnen, darüber zu diskutieren, wie der Kommunikationsprozess – oder genauer gesagt, das Kommunikationsprotokoll – gestaltet werden sollte.
Bevor wir uns dem Hauptthema dieses Artikels widmen, müssen wir eine kleine Anpassung an dem zuvor vorgestellten Code vornehmen. Alles, was zuvor erklärt wurde, bleibt gültig. Aber diese Änderung ist notwendig, um das System zu stabilisieren. Danach können wir uns dem eigentlichen Thema dieses Artikels zuwenden.
Weitere Stabilisierung des DispatchMessage-Codes
Aufgrund eines bestimmten Fehlers in der Interaktion zwischen dem Mausindikator und Chart Trade ist eine kleine Änderung erforderlich. Ich kann nicht vollständig erklären, warum die Interaktion manchmal fehlschlägt. Mit der unten gezeigten Codeanpassung verschwindet das Problem jedoch.
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)) 293. { 294. switch (CheckMousePosition(x = (short)lparam, y = (short)dparam)) 295. { 296. case MSG_MAX_MIN: 297. if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 298. break; 299. case MSG_DAY_TRADE: 300. if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 301. break; 302. case MSG_LEVERAGE_VALUE: 303. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage); 304. break; 305. case MSG_TAKE_VALUE: 306. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake); 307. break; 308. case MSG_STOP_VALUE: 309. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop); 310. break; 311. case MSG_TITLE_IDE: 312. if (sx < 0) 313. { 314. DeleteObjectEdit(); 315. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 316. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 317. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 318. } 319. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 320. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 321. if (m_Info.IsMaximized) 322. { 323. m_Info.x = (mx > 0 ? mx : m_Info.x); 324. m_Info.y = (my > 0 ? my : m_Info.y); 325. }else 326. { 327. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 328. m_Info.miny = (my > 0 ? my : m_Info.miny); 329. } 330. break; 331. case MSG_BUY_MARKET: 332. ev = evChartTradeBuy; 333. case MSG_SELL_MARKET: 334. ev = (ev != evChartTradeBuy ? evChartTradeSell : ev); 335. case MSG_CLOSE_POSITION: 336. if ((m_Info.IsMaximized) && (sz < 0)) 337. { 338. string szTmp = StringFormat("%d?%s?%c?%d?%.2f?%.2f", ev, _Symbol, (m_Info.IsDayTrade ? 'D' : 'S'), m_Info.Leverage, 339. FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage)); 340. PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp); 341. sz = x; 342. EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp); 343. } 344. break; 345. } 346. if (sz < 0) 347. { 348. sz = x; 349. AdjustTemplate(); 350. if (obj == MSG_NULL) DeleteObjectEdit(); 351. } 352. }else 353. { 354. sz = -1; 355. if (sx > 0) 356. { 357. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 358. sx = sy = -1; 359. } 360. } 361. break; 362. case CHARTEVENT_OBJECT_ENDEDIT: 363. switch (obj) 364. { 365. case MSG_LEVERAGE_VALUE: 366. case MSG_TAKE_VALUE: 367. case MSG_STOP_VALUE: 368. dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT)); 369. if (obj == MSG_TAKE_VALUE) 370. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 371. else if (obj == MSG_STOP_VALUE) 372. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 373. else 374. m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue)); 375. AdjustTemplate(); 376. obj = MSG_NULL; 377. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable); 378. break; 379. } 380. break; 381. case CHARTEVENT_OBJECT_CLICK: 382. if (sparam == m_Info.szObj_Chart) if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (obj = CheckMousePosition(x = (short)lparam, y = (short)dparam)) 383. { 384. case MSG_DAY_TRADE: 385. m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 386. DeleteObjectEdit(); 387. break; 388. case MSG_MAX_MIN: 389. m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 390. DeleteObjectEdit(); 391. break; 392. case MSG_LEVERAGE_VALUE: 393. CreateObjectEditable(obj, m_Info.Leverage); 394. break; 395. case MSG_TAKE_VALUE: 396. CreateObjectEditable(obj, m_Info.FinanceTake); 397. break; 398. case MSG_STOP_VALUE: 399. CreateObjectEditable(obj, m_Info.FinanceStop); 400. break; 401. } 402. if (obj != MSG_NULL) AdjustTemplate(); 403. break; 404. case CHARTEVENT_OBJECT_DELETE: 405. if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown); 406. break; 407. } 408. ChartRedraw(); 409. } 410. //+------------------------------------------------------------------+
Fragment der Datei C_ChartFloatingRAD.mqh
Beachten Sie, dass einige Zeilen durchgestrichen wurden, zum Beispiel die Zeilen 314 und 341. Beide wurden in den Test in Zeile 346 verschoben. Diese Anpassung behebt ein Stabilitätsproblem, das beim Anklicken bestimmter Steuerelemente auftrat. Die Variable sz wird in jedem der Objekte verwendet. Dies ist in den Zeilen 297, 300, 303, 306 und 309 sowie in den bedingten Tests in den Zeilen 312 und 336 zu erkennen.
Im Vergleich zur Vorgängerversion stabilisiert diese Änderung insbesondere die Interaktion zwischen dem Mausindikator und Chart Trade. Wenn der Mausindikator zuerst geladen wurde, reagierten einige Steuerelemente von Chart Trade nicht korrekt. Die einzige Abhilfe bestand darin, den Indikator aus dem Chart zu entfernen und ihn erneut einzufügen. Nur dann würden sich die Kontrollen richtig verhalten. Das ist, gelinde gesagt, seltsam.
Aus diesem Grund sollte das Ereignis CHARTEVENT_OBJECT_CLICK aus dem Verarbeitungscode entfernt werden. Alle Zeilen zwischen 381 und 403 müssen daher aus dem im letzten Artikel gezeigten Code gelöscht werden. Da diese Änderungen nichts an den zuvor gegebenen Erklärungen ändern, können wir nun zum Hauptthema dieses Artikels übergehen.
Verstehen der Planung hinter dem Nachrichtenprotokoll
Ich kann nicht davon ausgehen, dass Sie, liebe Leserin und lieber Leser, bereits über Computer-Kommunikationssysteme Bescheid wissen. Um niemanden zurückzulassen, werde ich dies von Grund auf erklären. Wenn Sie bereits Erfahrung mit solchen Protokollen haben, ist vieles davon offensichtlich, und Sie können zum nächsten Abschnitt übergehen.
Im vorigen Artikel habe ich erklärt, warum numerische Werte in ihre literalen (Zeichenketten-) Entsprechungen umgewandelt werden müssen. Wenn Sie z. B. den Binärwert 0001 0011 übertragen wollen, müssen Sie ihn in die Zeichenkette „19“ umwandeln. Das bedeutet, dass zwei Bytes anstelle von einem übertragen werden. Dies mag zwar ineffizient erscheinen, aber das Ziel ist nicht Effizienz, sondern Klarheit: Es soll sichergestellt werden, dass die Informationen auf der Empfängerseite richtig verstanden werden. Effizienz ist natürlich wünschenswert, aber Genauigkeit hat Vorrang.
Wie bereits erläutert, verwenden wir das Feld „sparam“. Die Herausforderung ist nun folgende: Wenn die Informationen innerhalb einer einzigen Zeichenkette übertragen werden müssen, wie bestimmen wir dann, wo ein Teil der Daten endet und ein anderer beginnt? Denn das ist die entscheidende Frage.
Die Ausarbeitung einer entsprechenden Strategie ist unerlässlich. Sie benötigen ein Format, das es Ihnen ermöglicht, die einzelnen Daten später zu extrahieren. Es sind mehrere Ansätze möglich, die jeweils ihre eigenen Vor- und Nachteile haben. Eine Möglichkeit ist die Verwendung von Arrays mit fester Länge für jedes Feld. Verkettet bilden sie die übermittelte Zeichenfolge. Dieser Ansatz hat seine Vor- und Nachteile.
Das macht die Indizierung einfach, da jeder Block immer die gleiche Größe hat. Allerdings werden Speicher und Bandbreite verschwendet, wenn die Felder den ihnen zugewiesenen Platz nicht vollständig ausnutzen. Leere Positionen beanspruchen nämlich Bandbreite, in unserem Fall also Speicherplatz.
Um dies zu verdeutlichen, sehen Sie sich das folgende Bild an:

Der blaue Block steht für das NULL-Zeichen, das das Ende der Zeichenkette markiert. Beachten Sie, dass eines der Arrays leere Positionen hat und damit Platz verschwendet. Es sind Situationen wie diese, die die Verwendung eines Arrays mit fester Größe schwierig machen.
Eine Alternative ist die Verwendung von Arrays mit variabler Länge, deren Größe genau auf die Daten abgestimmt ist. Dadurch wird kein Platz verschwendet und die Entnahme erleichtert. Der Nachteil ist die zusätzliche Komplexität. Wir werden mehr Code schreiben müssen. Wenn wir mehr Code erstellen, müssen wir ihn auch testen. Außerdem besteht in vielen Fällen die Gefahr des Datenverlusts, wenn die Informationen die erwartete Größe überschreiten. Abbildung 2 zeigt eine idealisierte Version dieses Ansatzes:

Wie Sie im ersten Bild sehen können, zeigt der blaue Block an, wo sich das Zeichen befindet, das das Ende der Zeile markiert. Beachten Sie, dass es sich hier um einen idealisierten Zustand handelt, bei dem jede Farbe für Informationen steht, die in einer Zeile komprimiert werden. Obwohl dies in einigen Fällen die Extraktion der Zeichenfolge erschweren kann, scheint dieser Fall für eine vollständige und korrekte Wiederherstellung der ursprünglichen Informationen völlig ausreichend. In diesem Fall hat jedoch jeder Satz eine feste Größe. Dann erhalten wir einen fortschrittlicheren Ansatz als den in der ersten Abbildung gezeigten. Problematisch wird es jedoch, wenn beispielsweise ein Feld, das eigentlich zwei Bytes umfassen sollte, plötzlich drei Bytes benötigt.
Das wäre der schlimmste Fall. Eines der Bytes geht beim Zusammenbau des grünen Sets verloren. Was wäre, wenn das grüne Set wachsen könnte, um dieses zusätzliche Byte unterzubringen? Ist es möglich, dies zu tun? Wenn wir dies tun, werden alle Informationen nach dem grünen Satz kompromittiert, und der Empfänger wird nicht in der Lage sein zu verstehen, dass es drei Bytes im grünen Satz gibt und nicht zwei, wie er erwartet hat.
Dies zeigt, dass der Entwurf von Protokollen nicht trivial ist. Wenn der Empfänger feste Größen erwartet und der Sender sie ändert, schlägt die Kommunikation fehl. Es ist ein anderer Ansatz erforderlich.
Eine Möglichkeit sind Blöcke mit variabler Länge, bei denen jedes Feld so groß wie nötig sein kann. Das wäre doch viel besser, oder? Doch dann stellt sich ein neues Problem: Woher weiß der Empfänger, wo ein Feld endet und das nächste beginnt? Dies erfordert Trennzeichen oder Markierungen. Aber hier müssen Sie genau nachdenken, sonst kann es passieren, dass Sie eine Nachricht senden, die vom Empfänger nicht verstanden wird, auch wenn sie für Sie richtig ist. Aber wie ist es möglich, dass für den Empfänger nicht alles so klar ist?
Denken Sie darüber nach: Jeder Abschnitt einer Nachricht kann beliebig groß sein, sodass wir praktisch jede Art von Information übermitteln können. Die Informationen müssen in einer bestimmten Reihenfolge angeordnet sein, wie auch immer diese aussehen mag, aber die Reihenfolge muss immer eingehalten werden. So weit, so gut. Noch einmal: Wie zeigen wir an, dass ein Abschnitt beendet ist und ein anderer beginnt?
Dies ist der schwierigste Teil. Es hängt alles von der Art der Daten ab, die wir in die Zeichenkette eingeben. Wenn wir alle 255 möglichen Werte in einem Byte verwenden - auch hier müssen wir das Null-Zeichen vermeiden, also 255 statt 256 - haben wir ein großes Problem: Wie können wir angeben, dass wir in einem Block von Zeichenketten unterschiedliche Informationen liefern? Wenn wir die Werte auf Zeichen zwischen 32 und 127 reduzieren, müssen wir etwas mehr Montagearbeit leisten. Dies ermöglicht es uns jedoch, jeden Wert zwischen 128 und 255 als Markierungssymbol zu verwenden.
Wir können die Dinge jedoch noch weiter einschränken. Wir können nur alphanumerische Zeichen verwenden, um die erforderlichen Informationen zu übermitteln. So können wir Satzzeichen als Begrenzer reservieren. Warum können wir das hier tun? Dies funktioniert, weil die zu übermittelnden Daten relativ einfach sind: Namen von Vermögenswerten und numerische Werte wie Hebelwirkung, Take-Profit- und Stop-Loss-Niveau. Diese werden in Chart Trade konfiguriert, müssen aber an den Expert Advisor übermittelt werden.
Aber zusätzlich zu diesen Werten, die recht einfach sind, müssen wir noch einen weiteren übergeben. Auch wenn MetaTrader 5 dies bereits handhabt, werden wir den Operationstyp einbeziehen, um sicherzustellen, dass die Kommunikation unter Kontrolle bleibt. Das ist nicht notwendig.
Denken Sie daran, dass die Kommunikation nicht nur innerhalb eines Terminals stattfinden muss. Mit geeigneten Netzwerkprotokollen könnte ein Computer das Terminal betreiben, während ein anderer die Aufträge und Positionen verwaltet. Selbst ein bescheidener Rechner könnte dann den Handel so abwickeln, als ob er viel leistungsfähiger wäre.
Wir werden nicht weiter auf Einzelheiten eingehen. Die Idee ist, zu zeigen, wie die Verbindung tatsächlich funktionieren wird.
Das Beste aus beiden Welten vereinen
Die von uns gewählte Lösung kombiniert die Stärken der beiden Ansätze mit fester und variabler Länge. Wir übermitteln die Daten als alphanumerische Zeichenketten, verwenden Trennzeichen, um die Übersichtlichkeit zu wahren, und ermöglichen dennoch die Indexierung. Jedes Feld kann so viele Zeichen wie nötig belegen, sodass nichts verloren geht.
Siehe Zeile 338 des früheren Codefragments. Um dies noch deutlicher zu machen, lassen Sie uns ein praktisches Beispiel für eine Sendung betrachten.

Abbildung 3 zeigt ein reales Beispiel für eine Übertragung von Chart Trade. Auf den ersten Blick sieht die Nachricht verwirrend aus. Um dies zu verstehen, müssen wir uns ansehen, was in Zeile 338 des Fragments geschieht. Die Nachricht folgt nach wie vor einem genau definierten Protokoll. Im Grunde genommen nutzen wir das Beste aus beiden Welten. Die Blöcke in einer Zeile können beliebig groß sein, während die Informationen in der Zeile auf eine bestimmte Weise indiziert werden.
Sie verstehen vielleicht nicht, wie die Indizierung funktioniert, und es ist auch nicht so offensichtlich, aber es gibt sie. Beachten Sie das Symbol D in der Nachricht. Es ist zu beachten, dass vor und nach dem Wort dasselbe Zeichen steht. An dieser Stelle gibt es nur einen einzigen Block, der mit dem Zeichen D gefüllt ist. Diese Situation kommt nirgendwo sonst in der Nachricht vor. Das bedeutet, dass dieses einzelne Zeichen auf irgendeine Weise indiziert werden kann. Dies wird jedoch später noch deutlicher werden. Im Moment sollten wir uns darauf konzentrieren, zu verstehen, was hier passiert.
Der erste Block, der in dieser Nachricht ein einziges Zeichen enthält, gibt die Art des auszuführenden Vorgangs an. Auch hier wird MetaTrader 5 diese Informationen an unseren Expert Advisor weitergeben, wie wir später sehen werden. In diesem Fall denke ich jedoch an die Tatsache, dass Chart Trade tatsächlich mit dem Expert Advisor kommuniziert - sei es über das Netzwerk, per E-Mail oder auf andere Art und Weise. Aus diesem Grund gebe ich ausdrücklich die Art der auszuführenden Operation an.
Bemerkenswertes Detail: Der Wert 9 entspricht dem Ereignis eines Kaufs zum Marktpreis. Dieser Wert kann jedoch abweichen. Enthielte der Block beispielsweise den Wert 11, würde dies tatsächlich die Schließung aller Positionen anzeigen. In diesem Fall würde der Block zwei Zeichen statt des oben gezeigten einzelnen Zeichens enthalten. Aber warum wird 9 für „Kaufen“ und 11 für „Schließen“ verwendet? Woher kommen diese Werte? Diese Frage ist durchaus berechtigt. In Zeile 338 ist der erste Wert in der Zeichenkette ein ushort. Das allein erklärt aber noch nicht, warum 9 auf „Kaufen“ und 11 auf „Schließen“ hinweist. Das ist in der Tat nicht der Fall.
Sehen Sie sich nun Zeile 278 an. Woher kommt dieser Wert? Sie stammt aus der Header-Datei Defines.mqh. Achten Sie genau darauf: In Defines.mqh gibt es eine Enumeration namens EnumEvents. Diese Enumeration beginnt bei Null, und mit jedem neuen Element erhöht der Compiler den Wert um eins. Ab dem ersten Ereignis, evHideMouse, ist das neunte Ereignis evChartTradeBuy und das elfte evChartTradeCloseAll. Jetzt wissen Sie, woher die Werte am Anfang der Zeichenkette kommen: Sie leiten sich von der Enumeration EnumEvents ab.
Fahren wir fort. Beachten Sie, dass alle Fragezeichen violett hervorgehoben sind. Das NULL-Zeichen, das die Zeichenkette beendet, ist blau markiert. Da jeder Block durch ein Fragezeichen abgegrenzt ist, können wir so viele alphanumerische Zeichen einfügen, wie wir zum Senden der Nachricht benötigen. Aber es gibt wichtige Details. Diese Nachricht muss in einer bestimmten Reihenfolge aufgebaut sein. Zur Erinnerung: Der Empfänger erwartet, dass er die Daten in einer bestimmten Reihenfolge erhält. Obwohl die Informationen theoretisch in einer zufälligen Reihenfolge angeordnet werden könnten, erwartet der Empfänger - in der von mir demonstrierten Version - dies nicht.
Der nächste Zeichenblock enthält den Namen der Anlage, für die der Auftrag erteilt wird. Auch dies wäre nicht notwendig, wenn der Expert Advisor und der Chart Trade auf demselben Chart arbeiten würden. Ich ziehe jedoch den Fall in Betracht, dass sie es nicht sind.
Außerdem werden Sie in Zukunft sehen, dass diese Meldungsdetails für andere Zwecke verwendet werden können. Im Beispiel ist der Vermögenswert BOVA11, ein börsengehandelter Fonds. Der Name des Assets macht die Sache kompliziert, wenn kein Trennzeichen verwendet wird. Denn auf einigen Märkten besteht das Symbol für den Vermögenswert aus vier alphanumerischen Zeichen, während es auf anderen Märkten fünf Zeichen haben kann. In diesem Fall haben wir fünf. Selbst an der B3 (der brasilianischen Börse) werden für viele Handelswerte Symbole mit vier Buchstaben verwendet.
Es gibt noch einen weiteren Punkt. Denken Sie daran, dass es hier darum geht, Chart Trade zu entwerfen, der auch in der Wiedergabe/Simulation verwendet werden kann. In solchen Fällen kann der Symbolname eine beliebige Anzahl alphanumerischer Zeichen enthalten.
Nun kehren wir zu unserem Zeichen D zurück. An dieser Stelle möchte ich Sie bitten, sich noch einmal die Zeile 338 anzusehen. Handelt es sich nicht um ein Handelsgeschäft, das am selben Tag abgeschlossen wird (d. h. ein Daytrade), so ist das Zeichen in diesem Block ein anderes. Sie können ein beliebiges anderes Zeichen wählen, aber denken Sie daran, auch den Empfänger zu aktualisieren; andernfalls wird die Kommunikation unterbrochen, da der Empfänger den Buchstaben oder die Zeichenfolge möglicherweise nicht richtig interpretiert.
Unmittelbar danach folgt ein wörtlicher Wert: 250. Was bedeutet das? Schauen Sie sich noch einmal die Zeile 338 an: Dieser Wert ist die gewünschte Hebelwirkung. Hier liegt ein interessanter Punkt. Wir verwenden drei numerische Zeichen, um den Wert des Hebels darzustellen.
Können wir stattdessen einen binären Wert verwenden? Dies mag angemessen erscheinen, da die Hebelwirkung nicht null sein kann. Es gibt jedoch eine Bedingung, die dies verhindert. Nicht, weil wir die Zeichenkette nicht mit einem Zeichen formatieren können, das 250 entspricht, sondern wegen des Begrenzungszeichens selbst. Nehmen Sie eine ASCII-Tabelle und prüfen Sie den Wert des Fragezeichens. Es ist die 63.
Um dies zu erleichtern, habe ich unten eine ASCII-Tabelle von Zeichen 0 bis Zeichen 128 eingefügt.

Warum ist 63 für uns wichtig? Denn jeder Hebelwert, der 63 enthält, wird vom Empfänger als Begrenzungszeichen interpretiert (was ich gleich zeigen werde). Mit anderen Worten, der Empfänger wird nicht erkennen, dass der vierte Block den Hebelwert darstellt.
Das könnte man meinen: Was ist, wenn ich die Hebelwirkung durch Addition von 63 ausgleiche? Da die Hebel niemals Null sein kann, wäre der erste gültige Wert 64. Problem gelöst? Ich wünschte, es wäre so einfach. Aber nein: Wenn die Hebel um 63 erhöht werden, verschieben wir das Problem nur.
Hier ist der Grund dafür. Sie fragen sich vielleicht: Was ist das Problem? Wenn ich 63 addiere, werden alle Werte größer als 63 sein? Und genau das ist der springende Punkt. Das Problem ist, dass in der Programmierung kein Wert unendlich ist. Jeder Wert ist auf einen Höchstwert begrenzt, der von der verwendeten Wortgröße abhängt. Selbst auf einem 64-Bit-Prozessor (wie dem, auf dem MetaTrader 5 derzeit läuft) stützt sich das System immer noch auf 8-Bit-Konzepte für die Zeichenverarbeitung.
Das bedeutet, dass man selbst mit einem 64-Bit-Prozessor nicht bis zu 2^64 (18.446.744.073.709.551.615) Zeichen zählen kann. Sie können höchstens bis 255 zählen, was 2^8 entspricht. Warum? Kann man das umgehen? Ja: Eine Möglichkeit besteht darin, einen anderen Zeichensatz als ASCII zu verwenden. Unicode, zum Beispiel.
Aber es gibt noch ein weiteres Problem. StringFormat verwendet kein Unicode, zumindest nicht zum Zeitpunkt der Erstellung dieses Dokuments. String-Funktionen in MQL5 folgen im Allgemeinen den Prinzipien von C/C++ und verwenden daher ASCII. Auch wenn C/C++ mit Unicode umgehen kann, war dies ursprünglich nicht der Fall.
Selbst wenn Sie den Hebel um 63 erhöhen, werden Sie also alle 255 Positionen einen zusammengesetzten Wert erzeugen. Dies ist eine Kombination aus einem Faktor und der aktuellen Anzahl. Der Faktor gibt an, wie oft der Zählzyklus bis 255 stattgefunden hat. Der Wert 575 ist der Faktor 2 plus 63. Und so weiter.
Um dies korrekt darzustellen, bräuchte man zwei Bytes, wobei das zweite Byte irgendwann immer 63 ist. Und das erste Byte, das den Faktor angibt, d.h. wie oft die maximale Anzahl erreicht wurde (2^8, wie oben erklärt). Daraus ergeben sich verschiedene mathematische Implikationen, auf die ich hier nicht näher eingehen werde, da sie den Rahmen dieses Artikels sprengen würden.
Zum Abschluss der Erklärung, wie dieses Nachrichtenprotokoll aufgebaut ist, sei darauf hingewiesen, dass wir auch zwei Werte haben, die als Double oder Float dargestellt werden können. Aus denselben Gründen wie beim Hebel müssen diese als Literalwerte geschrieben werden. Deshalb erscheinen sie so, wie sie auf dem Bild zu sehen sind.
Aber vielleicht fragen Sie sich jetzt: Warum sehen diese Werte so aus? Wofür stehen sie? Sie mögen seltsam erscheinen, weil Sie vielleicht vergessen haben, die Zeile 338 des Codefragments zu überprüfen. Dort wird ein Geldwert in Punkte umgerechnet. Der Wert 3,60 entspricht also 900 $, und 3,02 entspricht 755 $.
Warum nicht direkt Geldwerte anstelle von Punkten verwenden? Der Grund dafür ist die Einfachheit. Es ist viel einfacher, einen Expert Advisor mit bereits konvertierten Werten zu implementieren, als die Konvertierungen intern durchzuführen. Das mag jetzt noch nicht ganz klar sein, aber später werden Sie die Vorteile erkennen. Wir werden dies in Zukunft noch genauer untersuchen, da weitere Erklärungen erforderlich sind, um die Vorteile des direkten Sendens von vorkonvertierten Werten an den Expert Advisor vollständig zu verstehen. Aber wie ich bereits sagte, ist dies eine Frage der Zukunft.
Schlussfolgerung
In diesem Artikel habe ich versucht, so ausführlich wie möglich zu erklären, wie man ein Kommunikationsprotokoll erstellt. Das Thema ist noch nicht abgeschlossen, denn wir müssen uns noch mit dem Teil befassen, der für den Empfang dieser Nachrichten zuständig ist. Ich glaube jedoch, dass das Hauptziel erreicht wurde: zu zeigen, warum Sie bei der Gestaltung Ihres Kommunikationsprotokolls Vorsicht walten lassen müssen, vor allem, wenn Sie sich für etwas anderes entscheiden als das, was ich hier vorstelle.
Das müssen Sie jetzt planen. Wenn Sie es auf später verschieben, werden Sie Schwierigkeiten haben, Ihr Protokoll so zu gestalten, dass die Informationen korrekt übertragen werden. Und diese Informationen sind wichtig, damit der Expert Advisor weiß, was er tun soll und wie er es tun soll. Schieben Sie es nicht auf. Studieren Sie jetzt, und beginnen Sie mit den notwendigen Anpassungen, die Ihrer Meinung nach vorgenommen werden sollten. Im nächsten Artikel befassen wir uns schließlich mit der Empfängerseite, d. h. dem Expert Advisor selbst.
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/12476
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.
Von der Grundstufe bis zur Mittelstufe: Definitionen (II)
Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 02): Aufbau der REQUESTS-Bibliothek, inspiriert von Python
Entwicklung eines Replay-Systems (Teil 78): Neuer Chart Trade (V)
Entwicklung des Price Action Analysis Toolkit (Teil 32): Python-Engine für Kerzenmuster (II) – Erkennung mit Ta-Lib
- 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.