Tipps von einem professionellen Programmierer (Teil II): Speichern und Austauschen von Parametern zwischen einem Expert Advisor, Skripten und externen Programmen
Inhalt
- Einführung
- Wo die Parameter gespeichert werden
- Die globalen Variablen des Terminals
- Grafische Objekte
- Auftragskommentare
- Textdateien
- Anwendungseinstellungen
- Parameter für die Analyse
- Übergabe von Skriptparametern an einen Expert Advisor
- Übergabe von Parametern an externe Programme
- Übernahme von Parametern von externen Programmen
- Übergabe von Parametern an ein Smartphone
- Schlussfolgerung
Einführung
In diesem Artikel werden wir Parameter besprechen, die nach einem Terminal-Neustart (Shutdown) wiederhergestellt werden können. Alle Beispiele sind echte funktionierende Codesegmente aus meinem Cayman-Projekt.
Wo die Parameter gespeichert werden
Parameter-Beispiele
- Zeit des Balkens Null. Zum Beispiel bei der Erkennung eines Kerzenmusters ist es logisch, dieses einmalig, nach dem Auftauchen eines neuen Balkens auf einem bestimmten Zeitrahmen, auszuwerten.
- Parameter der Handelsebene. Sie können z.B. eine Handelsebene ("trading level") auswählen und ein Skript verwenden, um die Zeit und die Größe eines Deals festzulegen, der im Falle eines Level-Ausbruchs geöffnet werden soll. Das Skript übergibt Parameter an den Expert Advisor. Der Expert Advisor erstellt einen Analyzer für die Ebenen. Der Analyzer "schaltet" sich erst nach dem Auftauchen eines neuen Balkens auf dem angegebenen Zeitrahmen ein.
- Nutzereinstellungen. Dazu gehören Farbe, Handelsregeln, Zeichenmethoden und andere Parameter. Offensichtlich sollten solche Parameter einmalig installiert werden, z.B. in einer Datei mit allen Einstellungen.
- Die globalen Variablen des Terminals
- Grafische Objekte
- Auftragskommentare
- Textdateien
Speicher | Typ | Geltungsbereich | Laufzeit |
---|---|---|---|
Die globalen Variablen des Terminals | double | Alle Charts | 4 Wochen nach dem letzten Zugriff |
Grafische Objekte | Jeder, Zeichenkette <= 63 Zeichen | Aktueller Chart | Laufzeit des Charts |
Auftragskommentare | Zeichenketten <= 23 Zeichen | Alle Charts | Laufzeit des Terminals |
Textdateien | Jeder, unbeschränkt | Alle Charts | Existenzdauer der Datei |
Die globalen Variablen des Terminals
Die globalen Variablen des Terminals sind von jedem Chart aus verfügbar. Ihr Umfang kann eingeschränkt werden, indem zusätzliche Komponenten an den Variablennamen angehängt werden, z. B. ChartId, Symbol oder Zeitraum. Was nicht geändert werden kann, ist der Variablentyp. Sie können den Text nicht speichern.
Es gibt einen Lifehack: Integer-Werte packen/entpacken. Wie Sie wissen, nimmt double 8 Bytes (64 Bit) ein. Schauen Sie sich das folgende Beispiel an: Es zeigt, wie man mehrere Integer-Werte in einer Variablen speichern kann. Das Wichtigste dabei ist, die Bitgröße ihrer Maximalwerte zu bestimmen.
// -------------------------------------------------------------------------------------| // Beispiel für das Packen/Entpacken von Integer-Werten in/aus einer globalen Variablen | // unter Verwendung bitweiser Operationen | // -------------------------------------------------------------------------------------| void OnStart() { int value10 = 10; // max = 255 (8 bits) int value20 = 300; // max = 65535 (16 bits) bool value30 = true; // max = 1 (1 bit) // die Werte in 25 Bits packen (8+16+1) // 39 Bits (64-25) bleiben frei ulong packedValue = (value10 << 17) + // Leerzeichen reservieren (16+1) für value20, value30 (value20 << 1) + // Leerzeichen reservieren (1) für value30 value30; // Sichern der globalen Variablen string nameGVar = "temp"; GlobalVariableSet(nameGVar, packedValue); // Lesen der globalen Variablen packedValue = (ulong)GlobalVariableGet(nameGVar); // Entpacken der Werte // 0xFF, 0xFFFF, 0x1 - bit-Maske des jew. größten Wertes int value11 = (int)((packedValue >> 17) & 0xFF); int value21 = (int)((packedValue >> 1) & 0xFFFF); bool value31 = (bool)(packedValue & 0x1); // Vergleich der Werte if (value11 == value10 && value21 == value20 && value31 == value30) Print("OK"); else PrintFormat("0x%X / 0x%X /0x%X / 0x%X", packedValue, value11, value21, value31); }
Grafische Objekte
Können wir die Parameter von Skripts in grafischen Objekten speichern? Warum nicht. Setzen Sie die Objekteigenschaft OBJPROP_PRICE = 0 — in diesem Fall ist das Objekt visuell "ausgeblendet", ist aber von einem Programm zugänglich. Zur Sicherheit kann ein solches Objekt in einer Chart-Vorlage gespeichert werden. Die Logik des Parameterzugriffs ist wie folgt: wenn ein Objekt vorhanden ist, werden die Parameter extrahiert; wenn kein Objekt vorhanden ist, werden die Standardwerte gesetzt.
Auftragskommentare
Die maximale Länge der Kommentare für Handelsaufträge ist auf 23 Zeichen begrenzt. Was kann in einem Kommentar gespeichert werden? Zum Beispiel: SOP/H1/SS/C2/Br/Br/Br. Wobei (von links nach rechts)
- SOP - Auftragsabsender (SOP - das Skript SendOrderByPlan)
- H1 — Zeitrahmen der Ordererstellung (H1)
- SS — Ordertyp (SS - Sell Stop)
- C2 — Algorithmus zum Schließen der Order
- Br — D1-Trend (Br - Bear, abwärts)
- Br — H4-Trend (Br - Bear, abwärts)
- Br — Trend im Zeitrahmen der Ordererstellung (Br - Bear, abwärts)
Warum brauchen wir das? Diese Daten können zum Beispiel für die Analyse von Geschäften verwendet werden. Ich verwende sie folgendermaßen: Wenn eine Pending Order ausgelöst wird, extrahiere ich den Wert des Schließalgorithmus und erstelle einen virtuellen Stop-Analyser AnalyserVirtSL, der dann unter bestimmten Bedingungen die Position schließt.
Textdateien
Dies ist vielleicht die zuverlässigste und universellste Art, Wiederherstellungsparameter zu speichern. Sie können einmalig Zugriffsklassen einrichten und diese dann immer und überall verwenden, wenn immer Sie sie benötigen.
Anwendungseinstellungen
Teil der Einstellungsdatei AppSettings.txt:
# --------------------------------------------------------------------------- # Expert Advisor und Skript-Einstellungen # Dateikodierung = UCS-2 LE mit BOM (erforderlich!!!) // es ist Unicode # --------------------------------------------------------------------------- TimeEurWinter = 10:00 # Beginnzeit des europäischen Handels in der Winterzeit (Server-Zeit) TimeEurSummer = 09:00 # Beginnzeit des europäischen Handels in der Sommerzeit (Server-Zeit) ColorSessionEur = 224,255,255 # Farbe der europäischen Handelszeit ColorSessionUsd = 255,240,245 # Farbe der amerikanischen Handelszeit NumberColorDays = 10 # die Anzahl der hervorzuhebenden Tage (Handelszeiten)
Die Klasse AppSettings.mqh
#property copyright "Copyright 2020, Malik Arykov" #property link "malik.arykov@gmail.com" #property strict #include <Cayman/Params.mqh> // Parameternamen der Applikation #define APP_TIME_EUR_SUMMER "TimeEurSummer" #define APP_TIME_EUR_WINTER "TimeEurWinter" #define APP_TIME_TRADE_ASIA "TimeTradeAsia" #define APP_COLOR_SESSION_EUR "ColorSessionEur" #define APP_COLOR_SESSION_USD "ColorSessionUsd" #define APP_NUMBER_COLOR_DAYS "NumberColorDays" // -------------------------------------------------------------------------------------| // Allgemeine Einstellungen für den Expert Advisor und die Skripts | // -------------------------------------------------------------------------------------| class AppSettings { private: Params *m_params; public: // Einzutragen in die Datei AppSettings.txt string TimeEurSummer; // Beginnzeit des europäischen Handels in der Sommerzeit string TimeEurWinter; // Beginnzeit des europäischen Handels in der Winterzeit string TimeTradeAsia; // Endzeit für den asiatischen Korridorhandel color ColorSessionEur; // Farbe der europäischen Handelszeit color ColorSessionUsd; // Farbe der amerikanischen Handelszeit int NumberColorDays; // Anzahl der hervorzuhebenden Tage // bestimmt vom Programm string PeriodTrends; // Zeitrahmen (D1,H4) zur Trendberechnung string TradePlan; // Handelsrichtung (kurzer Plan) bool IsValid; // Parametergültigkeit // Methoden AppSettings(); ~AppSettings() { delete m_params; }; void Dump(string sender); }; // -------------------------------------------------------------------------------------| // Konstruktor | // -------------------------------------------------------------------------------------| AppSettings::AppSettings() { IsValid = true; m_params = new Params(); m_params.Load(PATH_APP_SETTINGS); if (m_params.Total() == 0) { PrintFormat("%s / ERROR: Invalid file / %s", __FUNCTION__, PATH_APP_SETTINGS); IsValid = false; return; } TimeEurWinter = m_params.GetValue(APP_TIME_EUR_WINTER); TimeEurSummer = m_params.GetValue(APP_TIME_EUR_SUMMER); TimeTradeAsia = m_params.GetValue(APP_TIME_TRADE_ASIA); ColorSessionEur = StringToColor(m_params.GetValue(APP_COLOR_SESSION_EUR)); ColorSessionUsd = StringToColor(m_params.GetValue(APP_COLOR_SESSION_USD)); NumberColorDays = (int)StringToInteger(m_params.GetValue(APP_NUMBER_COLOR_DAYS)); } // -------------------------------------------------------------------------------------| // Ausdruck der Parametereinstellungen | // -------------------------------------------------------------------------------------| void AppSettings::Dump(string sender) { PrintFormat("sender=%s / %s", sender, PATH_APP_SETTINGS); PrintFormat("%s = %s", APP_TIME_EUR_WINTER, TimeEurWinter); PrintFormat("%s = %s", APP_TIME_EUR_SUMMER, TimeEurSummer); PrintFormat("%s = %s", APP_TIME_TRADE_ASIA, TimeTradeAsia); PrintFormat("%s = %s / %s", APP_COLOR_SESSION_EUR, ColorToString(ColorSessionEur), ColorToString(ColorSessionEur, true)); PrintFormat("%s = %s / %s", APP_COLOR_SESSION_USD, ColorToString(ColorSessionEur), ColorToString(ColorSessionEur, true)); PrintFormat("%s = %i", APP_NUMBER_COLOR_DAYS, NumberColorDays); }
Charakteristika
Die Deklaration der Klasse AppSettings befindet sich in der Datei Uterminal.mqh, die über #include mit einem Expert Advisor und mit einem beliebigen Skript eingebunden wird.
extern AppSettings *gAppSettings; // Anwendungseinstellungen
Mit dieser Lösung können Sie:
- gAppSettings einmal an beliebiger Stelle initialisieren
- gAppSettings in jeder Klasseninstanz verwenden (anstatt es als Parameter zu übergeben)
Parameter für die Analyse
Der Cayman Expert Advisor verwaltet verschiedene Analyzer wie z.B. AnalyzerTrend, AnalyserLevel, AnalyserVirtSL. Jeder Analyzer ist mit einem bestimmten Zeitrahmen verknüpft. Das bedeutet, dass der Analyzer nur gestartet wird, wenn ein neuer Balken auf dem angegebenen Zeitrahmen auftaucht. Analyzer-Beispiele werden in der Textdatei gespeichert, mit den Zeilen Key = Value. Zum Beispiel speichert der Analyzer für H4-Handelsebenen seine Parameter in der Datei Files\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txt
- Cayman — Projektname
- Params — Unterverzeichnis mit den Parametern von Analyzer
- 128968168864101576 — Chart ID // IntergerToString(ChartID())
- exp_05_Lev607A160E_H4.txt — der Name der Datei mit Analysatorparametern —
- exp — Präfix
- 05 — Typ von Analyzer
- Lev607A160E — der Name des Analyzers (Handelsebenen)
- H4 — verfolgter Zeitrahmen.
Unten ist der Inhalt der Datei mit Kommentaren (die echte Datei hat keine Kommentare)
// Parameter der Handelsebene nameObj=Lev607A160E // Name der Ebene kindLevel=1 // Ebene Typ (1 - Widerstand) riskValue=1.00 // Handelsvolumen bei Durchbruch der Ebene (1) riskUnit=1 // Einheit für die Änderung des Geschäftsvolumens (1 - % der Mittel für die Marge) algClose=2 // Algorithmus zum Schließen der Position (2 - zwei Korrekturbalken) ticketNew=0 // Ticket einer Position, das bei Durchbruch der Ebene geöffnet wurde ticketOld=0 // Ticket zum Schließen einer Position bei Ausbruch aus der Ebene profits=0 // geplanter Gewinn in Punkten losses=0 // geplanter Verlust in Punkten // Parameter des Analyzer symbol=EURUSD // Symbolname period=16388 // Zeitrahmen (H4) für den Analyzer time0Bar=1618603200 // Null-Bar-Zeit (sec) typeAnalyser=5 // Analysator-Typ colorAnalyser=16711935 // Farbe für Analyzer-Ergebnisse resultAnalyser=Lev607A160E, H4, 20:00, RS // Analyzer-Ergebnisse
Es gibt eine Basisklasse Analyzer, die die Parameter eines beliebigen Analyzers speichern und wiederherstellen kann. Wenn ein Expert Advisor neu gestartet wird (z. B. nach einem Wechsel des Zeitrahmens), stellen die Parameter des Analyzers aus den entsprechenden Textdateien wieder her. Wenn der Zeitpunkt für einen neuen Balken noch nicht gekommen ist, wird die Analyse nicht neu gestartet. Die zum vorherigen Balken berechneten Analyzer-Ergebnisse (resultAnalyser, colorAnalyser) werden in den Kommentaren des Expert Advisors angezeigt.
Übergabe von Skriptparametern an einen Expert Advisor
Das Skript SetTradeLevel ermöglicht das Einstellen der Parameter einer Handelsebene. Ein Objekt (Gerade, Trendlinie oder Rechteck) wird im Chart ausgewählt. Das Skript SetTradeLevel findet das ausgewählte Objekt (Handelsebene) und setzt dessen Parameter.
Als Nächstes speichert das Skript die Parameter in der Datei Files\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txt und sendet den Befehl und den Pfad zur Datei über die Funktion SendCommand.
// -------------------------------------------------------------------------------------| // Parameter der Handelsebenen an den Expert Advisor senden | // -------------------------------------------------------------------------------------| NCommand SendCommand() { // Laden der Parameter der Ebenen (wenn vorhanden) Params *params = new Params(); string speriod = UConvert::PeriodToStr(_Period); params.Load(PREFIX_EXPERT, anaLevel, gNameLev, speriod); // Definieren der Befehle NCommand cmd = (gKindLevel == levUnknown) ? cmdDelete : (params.Total() > 0) ? cmdUpdate : cmdCreate; // Sichern der Parameter params.Clear(); params.Add(PARAM_NAME_OBJ, gNameLev); params.Add(PARAM_TYPE_ANALYSER, IntegerToString(anaLevel)); params.Add(PARAM_PERIOD, IntegerToString(_Period)); params.Add(PARAM_KIND_LEVEL, IntegerToString(gKindLevel)); params.Add(PARAM_RISK_VALUE, DoubleToString(gRiskValue, 2)); params.Add(PARAM_RISK_UNIT, IntegerToString(gRiskUnit)); params.Add(PARAM_ALG_CLOSE, IntegerToString(gAlgClose)); params.Add(PARAM_TICKET_OLD, IntegerToString(gTicketOld)); params.Add(PARAM_PROFITS, IntegerToString(gProfits)); params.Add(PARAM_LOSSES, IntegerToString(gLosses)); params.Save(); // Befehl an den Expert Advisor senden params.SendCommand(cmd); delete params; return cmd; }
Die Funktion params.SendCommand(cmd) ist wie folgt:
// -------------------------------------------------------------------------------------| // Befehl an den Expert Advisor senden | // -------------------------------------------------------------------------------------| void Params::SendCommand(NCommand cmd) { string nameObj = NAME_OBJECT_CMD; ObjectCreate(0, nameObj, OBJ_LABEL, 0, 0, 0); ObjectSetString(0, nameObj, OBJPROP_TEXT, m_path); ObjectSetInteger(0, nameObj, OBJPROP_ZORDER, cmd); ObjectSetInteger(0, nameObj, OBJPROP_TIMEFRAMES, 0); }
Bei jedem Tick (OnTick) prüft der Expert Advisor über die Funktion CheckExpernalCommand() die Existenz des Objekts namens NAME_OBJECT_CMD. Wenn es existiert, werden der Befehl und der Pfad zur Datei mit den Analyzer-Parametern gelesen und das Objekt sofort gelöscht. Als Nächstes sucht der Expert Advisor anhand des Dateinamens nach einem laufenden Analyzer. Wenn cmd == cmdDelete, dann wird der Analyzer gelöscht. Wenn cmd == cmdUpdate, dann werden die Analyzerparameter aus der Datei aktualisiert. Wenn cmd == cmdNew, dann wird ein neuer Analyzer mit den Parametern aus der Datei erstellt.
Hier ist der vollständige Code der Klasse Params, die die Logik für die Arbeit mit Parameterdateien (Key=Value-Strings) kapselt.
#property copyright "Copyright 2020, Malik Arykov" #property link "malik.arykov@gmail.com" #include <Arrays/ArrayString.mqh> #include <Cayman/UConvert.mqh> #include <Cayman/UFile.mqh> // -------------------------------------------------------------------------------------| // Parameterklasse (Schlüssel=Wert und Kommentaren nach #) | // -------------------------------------------------------------------------------------| class Params { private: string m_path; // Pfad zur Datei mit den Parametern NCommand m_cmd; // Befehl für den Expert Advisor CArrayString *m_items; // Array der Paare {Schlüssel=Wert} int Find(string key); public: Params(); ~Params() { delete m_items; }; void Clear() { m_items.Clear(); }; int Total() { return m_items.Total(); }; string Path() { return m_path; }; CArrayString *Items() { return m_items; }; void Add(string line) { m_items.Add(line); }; bool Add(string key, string value); string GetValue(string key); void Load(string prefix, int typeAnalyser, string nameObj, string speriod); void Load(string path); void Save(); void SendCommand(NCommand cmd); NCommand TakeCommand(); void Dump(string sender); }; // -------------------------------------------------------------------------------------| // Standard-Konstruktor | // -------------------------------------------------------------------------------------| Params::Params() { m_items = new CArrayString(); } // -------------------------------------------------------------------------------------| // Hinzufügen eines Paares Schlüssel=Wert | // -------------------------------------------------------------------------------------| bool Params::Add(string key, string value) { int j = Find(key); string line = key + "=" + value; if (j >= 0) { // Aktualisieren m_items.Update(j, line); return false; } else { // Hinzufügen m_items.Add(line); return true; } } // -------------------------------------------------------------------------------------| // Abrufen des Wertes gemäß dem Schlüssel | // -------------------------------------------------------------------------------------| string Params::GetValue(string key) { // Finden des Schlüssels int j = Find(key); if (j < 0) return NULL; // no key // Prüfen des Trennzeichens string line = m_items.At(j); j = StringFind(line, "="); if (j < 0) { // nein = PrintFormat("%s / ERROR: Invalid string %s", __FUNCTION__, line); return NULL; } // Rückgabe des Wertes return UConvert::Trim(StringSubstr(line, j + 1)); } // -------------------------------------------------------------------------------------| // Finden des Parameterwerts nach dem Schlüssel | // -------------------------------------------------------------------------------------| int Params::Find(string key) { int index = -1; for (int j = 0; j < m_items.Total(); j++) { if (StringFind(m_items.At(j), key) == 0) { index = j; break; } } return index; } // -------------------------------------------------------------------------------------| // Laden der Parameter | // -------------------------------------------------------------------------------------| void Params::Load(string prefix, int typeAnalyser, string nameObj, string speriod) { string nameFile = StringFormat("%s%02i_%s_%s.txt", prefix, typeAnalyser, nameObj, speriod); m_path = StringFormat("%s%s/%s", PATH_PARAMS, IntegerToString(ChartID()), nameFile); if (FileIsExist(m_path)) Load(m_path); } // -------------------------------------------------------------------------------------| // Laden der Parameter | // -------------------------------------------------------------------------------------| void Params::Load(string path) { m_path = path; if (!FileIsExist(m_path)) return; //PrintFormat("%s / %s", __FUNCTION__, m_path); string text = UFile::LoadText(m_path); if (text == NULL) return; // Aufspalten des Textes in Zeilen string line, lines[]; int numLines = StringSplit(text, DLM_LINE, lines); for (int j = 0; j < numLines; j++) { line = lines[j]; // Löschen des Kommentars int k = StringFind(line, "#"); if (k == 0) continue; // die ganze Zeile ist ein Kommentar if (k > 0) line = StringSubstr(line, 0, k); // Hinzufügen einer nicht-leeren Zeichenkette if (line != "") m_items.Add(line); } } // -------------------------------------------------------------------------------------| // Sichern der Parameter | // -------------------------------------------------------------------------------------| void Params::Save() { string text = ""; for (int j = 0; j < m_items.Total(); j++) { text += m_items.At(j) + "\n"; } // Neuschreiben der bestehenden Datei UFile::SaveText(text, m_path, true); } // -------------------------------------------------------------------------------------| // Befehl an den Expert Advisor senden | // -------------------------------------------------------------------------------------| void Params::SendCommand(NCommand cmd) { string nameObj = NAME_OBJECT_CMD; ObjectCreate(0, nameObj, OBJ_LABEL, 0, 0, 0); ObjectSetString(0, nameObj, OBJPROP_TEXT, m_path); ObjectSetInteger(0, nameObj, OBJPROP_ZORDER, cmd); ObjectSetInteger(0, nameObj, OBJPROP_TIMEFRAMES, 0); } // -------------------------------------------------------------------------------------| // Erhalt eines Befehls von einem Skript | // -------------------------------------------------------------------------------------| NCommand Params::TakeCommand() { string nameObj = NAME_OBJECT_CMD; if (ObjectFind(0, nameObj) < 0) return cmdUnknown; m_path = ObjectGetString(0, nameObj, OBJPROP_TEXT); m_cmd = (NCommand)ObjectGetInteger(0, nameObj, OBJPROP_ZORDER); ObjectDelete(0, nameObj); Load(m_path); return m_cmd; } // -------------------------------------------------------------------------------------| // Ausgabe der Parameter | // -------------------------------------------------------------------------------------| void Params::Dump(string sender) { for (int j = 0; j < m_items.Total(); j++) { PrintFormat("%s / %s", sender, m_items.At(j)); } }
Für MQL5-Fans: Wenn Sie den Typ m_items auf CHashMap ändern, wird der Code der Funktionen Add, GetValue, Find deutlich reduziert. Aber die Klasse Params wird auch in MQL4 verwendet. Außerdem ist die Geschwindigkeit des Parameterzugriffs in diesem Fall nicht wichtig, da die Parameter einmal gelesen werden, um lokale Variablen zu initialisieren. Warum habe ich die Klasse für CHashMap nicht für MQL5 neu entwickelt? Wahrscheinlich, weil ich lange Zeit in einer Bank gearbeitet habe. Entwickler von Finanzsoftware haben einen sehr wichtigen Grundsatz: Wenn es funktioniert, rühre es nicht an! ;-)
Übergabe von Parametern an externe Programme
Die Datenaustauscheinheit zwischen verschiedenen Systemen ist de facto eine json-Datei. Zuvor war es eine xml-Datei. Die wesentlichen Vorteile von json-Dateien sind:
- Einfache Erstellung (Generierung/Formatierung)
- Hervorragende Unterstützung in allen Hochsprachen
- Lesbarkeit
Zum Beispiel gibt es eine Klasse Bar mit folgenden Feldern: m_time, m_open, m_high, m_low, m_close, m_body. Dabei ist m_body die Farbe der Kerze: weiß, schwarz oder doji. Die Klasse Bar hat eine Methode ToJson(), die einen json-String erzeugt
string Bar::ToJson() { return "{" + "\n\t\"symbol\":\"" + _Symbol + "\"," + "\n\t\"period\":" + IntegerToString(_Period) + "," + "\n\t\"digits\":" + IntegerToString(_Digits) + "," + "\n\t\"timeBar\":\"" + TimeToStr(m_time) + "\"," + "\n\t\"open\":" + DoubleToString(m_open, _Digits) + "," + "\n\t\"high\":" + DoubleToString(m_high, _Digits) + "," + "\n\t\"low\":" + DoubleToString(m_low, _Digits) + "," + "\n\t\"close\":" + DoubleToString(m_close, _Digits) + "," + "\n\t\"body\":" + IntegerToString(m_body) + "," + "\n}"; }
Wir könnten stattdessen StringFormat verwenden, aber das würde Probleme beim Umordnen oder Löschen von Werten verursachen. Die Formatierung "\n\t" könnte gelöscht werden, da es eine ganze Reihe von Online-Json-Formatierungsdiensten gibt. Einer von ihnen ist JSON Parser. Sie stellen einmal den Erhalt eines gültigen json ein und verwenden die Funktion bar.ToJson(), wann immer Sie sie benötigen.
Ein externes Programm, zum Beispiel eine C#-Anwendung, kann eine json-Datei beliebiger Komplexität in ein Objekt umwandeln. Wie überträgt man eine json-Datei aus MQL? Es ist ganz einfach. Laden (speichern) Sie die json-Datei z. B. in das Terminalverzeichnis Files/Json. Ein externes Programm überwacht dieses Verzeichnis auf neue Dateien. Wenn es eine Datei gefunden hat, liest es diese ein, wandelt sie in ein Objekt um und löscht die Datei sofort oder verschiebt sie in das Archiv (für Statistiken).
Übernahme von Parametern von externen Programmen
Eine json-Bibliothek mit MQL-Programmen zu verbinden (oder das Rad neu erfinden), verursacht zusätzlichen Ärger. Eine bessere Lösung ist es, Textdateien mit Zeilen der Form Key=Value zu übergeben. Die Dateien können mit der Klasse Params (siehe oben) verarbeitet werden. Der Expert Advisor und der Indikator sind Kandidaten für den Empfang von Parametern aus externen Programmen oder Skripten. Sie müssen z. B. die Funktion CheckExternalCommand() in OnTick aufrufen, die das Vorhandensein von Dateien im Verzeichnis Files/ExtCmd überprüft. Wenn eine Datei gefunden wird, soll sie gelesen, verarbeitet (die Parameter akzeptiert) und die Datei gelöscht werden.
So, wir haben Methoden zum Empfangen und Übergeben von Parametern zwischen MQL und externen Programmen betrachtet. Denken Sie nun über Folgendes nach: Warum brauchen MQL-Programme DLLs? Solche Programme werden vom MQL-Markt nicht akzeptiert, und dafür gibt es nur einen Grund — die Sicherheit, da man von einer DLL aus auf alles zugreifen kann.
Übergabe von Parametern an ein Smartphone
Für weitere Operationen werde ich die Android-App WirePusher verwenden. Das ist ein wunderbarer Dienst (kostenlos und ohne Werbung). Ich weiß nicht, ob es so etwas auch für das iPhone gibt. Wenn es irgendwelche iPhone-Fans gibt, die diesen Artikel lesen, bitte in den Kommentaren teilen.
Um den Dienst zu nutzen:
- Installieren Sie WirePusher auf Ihrem Smartphone
- Starten Sie die Anwendung. Auf dem Hauptbildschirm sehen Sie Ihre Kennung.
- Fügen Sie https://wirepusher.com to Terminal/Service/Settings/Experts/Allow WebRequest hinzu.
Dann starten Sie das Skript (vergessen Sie nicht, Ihre id anstelle der Sternchen in id = "********" einzutragen.
void OnStart() { string id = "**********"; // Ihre Smartphone-Id in WirePusher WirePusher("Profit $1000", "Deal", "Closed", id); } // ------------------------------------------------------------------------------------------------ // Benachrichtigung über den Webservice WirePusher an das Smartphone senden // Fügen Sie hinzu: https://wirepusher.com to Terminal/Service/Settings/Experts/Allow WebRequest // message - Text der Benachrichtigung // title - Titel der Benachrichtigung (z. B. Achtung / Warnung / Deal) // type - Benachrichtigungstyp (z. B. Ausgelöste Pending-Order / Ausbruch aus der Ebene / Geschlossen) // id - eindeutige Smartphone-ID aus der Android-App WirePusher // ------------------------------------------------------------------------------------------------ bool WirePusher(string message, string title, string type, string id) { char data[]; // Datenarray für HTTP-Nachrichten im Hauptteil char result[]; // Datenarray der Antwort des Web-Anfrage string answer; // Header der Antwort der Web-Anfrage string url = "https://wirepusher.com/send?id={id}&title={title}&message={message}&type={type}"; StringReplace(url, "{id}", id); StringReplace(url, "{type}", type); StringReplace(url, "{title}", title); StringReplace(url, "{message}", message); ResetLastError(); int rcode = WebRequest("GET", url, NULL, 3000, data, result, answer); if (rcode != 200) { PrintFormat("%s / error=%i / url=%s / answer=%s / %s", __FUNCTION__, GetLastError(), url, answer, CharArrayToString(result)); return false; } PrintFormat("%s / %s / %s", __FUNCTION__, title, message); return true; }
Im Cayman EA wird die Funktion WirePusher im AnalyserTrade aufgerufen, wenn:
- Eine Pending-Order ausgelöst wird
- Der Preis durchbricht eine Handelsebene
- Eine Position geschlossen wird
Jedem Nachrichtentyp kann bei WirePusher ein eigener Sound zugewiesen werden. Früher hatte ich einen "Ta-da"-Sound für Position, die mit Gewinn geschlossen wurden und einen "Bomben"-Sound für solche, die mit Verlust geschlossen wurden. Aber dann hatte ich genug von "Bomben".
Schlussfolgerung
Die zuverlässigste und bequemste Methode zum Speichern von Parametern ist die Verwendung von Textdateien. Außerdem werden Dateioperationen in jedem Betriebssystem (Anwendung) vollständig unterstützt/gepuffert.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/9327
- 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.