English Русский 日本語
preview
Einführung in MQL5 (Teil 41): Leitfaden für Anfänger zur Dateiverwaltung in MQL5 (III)

Einführung in MQL5 (Teil 41): Leitfaden für Anfänger zur Dateiverwaltung in MQL5 (III)

MetaTrader 5Experten |
15 0
ALGOYIN LTD
Israel Pelumi Abioye

Einführung

Willkommen zu Teil 41 der Reihe „Einführung in MQL5“! In diesem Artikel setzen wir unseren Einsteigerleitfaden zur Dateiverwaltung in MQL5 fort und bauen dabei auf allem auf, was wir bisher gelernt haben. In den vorangegangenen Teilen haben wir uns damit befasst, wie man Dateien erstellt, strukturierte Trade-Daten darin speichert und sowohl Einstiegs- als auch Ausstiegsinformationen in einem übersichtlichen Trade-Journal organisiert. Zu diesem Zeitpunkt wissen Sie bereits, wie Dateien in MQL5 funktionieren und wie Sie Handelsdaten sicher in einem dauerhaften Format speichern können.

Im vorherigen Artikel haben wir uns hauptsächlich mit dem Senden bzw. dem Schreiben von Daten aus MetaTrader 5 in Dateien befasst. Das Schreiben von Daten ist jedoch nur ein Aspekt des Prozesses. Was passiert, wenn Sie den Inhalt einer Datei auslesen und diesen anschließend für statistische Analysen oder für Handelsentscheidungen nutzen möchten? In diesem Tutorial erfahren Sie, wie Sie gespeicherte Handelsdaten systematisch und sinnvoll abrufen können, indem Sie Daten aus Dateien in MQL5 auslesen. Wie in dieser Reihe üblich, werden wir einen projektbasierten Ansatz verfolgen, um den Lernprozess anschaulicher und praxisnaher zu gestalten. Anstatt das Lesen von Dateien isoliert zu behandeln, werden wir es direkt auf eine praktische Aufgabe anwenden, die Händler und Entwickler häufig benötigen.

Das Projekt, an dem wir arbeiten werden, ist ein Indikator, der eine CSV-Datei mit Ihrem Handelsverlauf ausliest und anhand der Gewinn- und Verlustdaten innerhalb eines bestimmten Zeitraums eine Saldenkurve erstellt. Diese Saldenkurve veranschaulicht, wie sich Ihr Kontostand in Abhängigkeit von Ihrer Handelsperformanz entwickelt.

Abbildung 1. Kapitalkurve

Abbildung 3. Kapitalkurve H

Wir werden uns zunächst darauf konzentrieren, zu verstehen, wie das Einlesen von Dateien in MQL5 funktioniert, da es für Anfänger zu anspruchsvoll sein könnte, direkt mit der Entwicklung des gesamten Indikators zu beginnen. Im Mittelpunkt dieses Artikels stehen die effiziente Organisation und das korrekte Auslesen von Daten aus einer Datei. Indem Sie alle TradeIDs in ein Array und alle LotSize-Werte in ein anderes Array einfügen, werden Sie lernen, wie man zusammengehörige Werte auf verschiedene Arrays aufteilt. Anschließend wiederholen Sie diesen Vorgang für die übrigen Spalten.

Falls Sie diese Reihe aufmerksam verfolgt haben, erinnern Sie sich vielleicht an Teil 32, in dem wir über die Arbeit mit APIs und das Gruppieren von aus einer Datei gelesenen Daten gesprochen haben. Damals ging es in erster Linie darum, die Funktionsweise von APIs zu verstehen, anstatt sich eingehend mit der Dateiverarbeitung zu befassen, und dieser Ansatz war bewusst auf Anfänger ausgerichtet. Daher wurden in den Beispielen einfachere Methoden wie statische Arrays und separate Variablen für jede Datengruppe verwendet. Mithilfe dynamischer Arrays werden wir in diesem Beitrag eine flexiblere und skalierbare Strategie verfolgen. Dadurch können wir so viele Daten speichern, wie wir benötigen, ohne für jeden Wert eine neue Variable anlegen zu müssen, was die Grundlage für eine sinnvolle und effektive Dateiverwaltung in MQL5 stärkt.


Die Gesamtzahl der Datensätze in der Datei ermitteln

Das Programm muss zunächst ermitteln, wie viele Datensätze verfügbar sind, bevor es Daten aus einer Datei lesen kann. Diese Information gewährleistet einen sicheren und effektiven Zugriff auf jeden Datensatz und hilft dabei, die richtigen Schleifengrenzen festzulegen. Wenn dies nicht geschieht, kann es zu falschen oder unvollständigen Ergebnissen kommen, da das Programm auf nicht vorhandene Daten zurückgreift. Das Zählen der Datensätze in einer Datei ist daher ein notwendiger erster Schritt bei jedem Lesevorgang. 

Und nun stellt sich eine häufig gestellte Frage. Was genau wird in der Datei gezählt? Werden ganze Sätze, Wörter oder Zeichen gezählt? Anders als ein Mensch betrachtet das Programm eine CSV-Datei nicht als zusammenhängenden Text, sondern als einzelne Datenelemente. Stattdessen behandelt es Daten als einzelne Teile. In diesem Dateiformat dienen Zeilenumbrüche als Zeilentrenner, und jeder durch ein Komma getrennte Wert wird als eigenständiges Datenelement betrachtet. Die CSV-Datei sieht in Excel übersichtlich und gut strukturiert aus, da jeder Wert genau in der dafür vorgesehenen Spalte steht.

Eine CSV-Datei erscheint strukturiert und geordnet, sobald sie in Excel geöffnet wurde, wobei jeder Datenwert genau in seiner eigenen Spalte positioniert ist. Dank dieser visuellen Anordnung können die Nutzer den Text schnell erfassen. Im Grunde genommen handelt es sich bei der Datei jedoch nach wie vor um ein einfaches Textdokument. Im Gegensatz zu einer Tabellenansicht liest MetaTrader die Datei als Text, wobei CSV-Trennzeichen die einzelnen Werte voneinander abgrenzen.

Abbildung 3. CSV

So sieht das Programm dieselben Daten in einer CSV-Datei jedoch tatsächlich:

Account Name: Abioye Israel Pelumi,
Account Balance: 7404.68000000,
Account Login: 31670702,
Start Time:, 2025.11.01 00:00:00,
End Time:, 2026.01.28 00:00:00,
Last Update:, 2026.02.09 18:23:33,
Total Trades:, 68,

Intern verwendet MetaTrader seine Trennzeichen, um die Datei zu lesen, wobei jede Spalte und jede Zeile durchlaufen wird. Jeder Spaltenwert steht für einen einzelnen Datenpunkt, und jede weitere Zeile bildet einen eigenen Datensatz. Diese Methode stellt sicher, dass das System strukturierte Zellen zählt, die in Zeilen und Spalten angeordnet sind, anstatt die Textlänge oder die Wortanzahl zu ermitteln.

Gezählt werden grundsätzlich alle verwertbaren Datenelemente, darunter auch Kopf-/Header-Felder und nicht handelsbezogene Angaben wie Kontodaten. Eine leere Zeile wird weiterhin als ein Element gezählt, auch wenn in der Datei keine Daten sichtbar sind; dadurch erhöht sich die Gesamtzahl der Elemente beim Einlesen der Datei um eins.

Abbildung 4. Elementindex

Es ist von entscheidender Bedeutung, diesen Unterschied zu verstehen. Was der Benutzer als ordentliche Zeilen und Spalten ansieht, sind für das Programm eigentlich Elemente, die sequenziell angeordnet sind. Wir können die Datei bedenkenlos durchlaufen und dynamische Arrays in der richtigen Größe zuweisen, um nur die Informationen zu extrahieren, die wir benötigen – beispielsweise Statistiken zu Trade-Gewinnen oder -Verlusten –, sofern wir diese Komponenten genau zählen. Diese Phase gewährleistet die Genauigkeit, indem sie einen Array-out-of-range-Fehler verhindert, während die Datei verarbeitet wird, um später eine Saldenkurve zu erstellen.

Erinnern Sie sich noch daran, wie wir vorhin über FileOpen gesprochen haben? Dies ist der erste Schritt jedes Dateiverarbeitungsprozesses. Das von dieser Funktion zurückgegebene Dateihandle dient dem Programm als eindeutige Referenz auf die Datei. Um festzulegen, ob die Datei zum Lesen, Schreiben oder für einen anderen Zweck geöffnet werden soll, müssen Sie gleichzeitig den entsprechenden Zugriffsmodus auswählen. 

Beispiel:
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---

   string Filename = "Trading_Journal.csv";

   int  file_handle = FileOpen(Filename, FILE_READ | FILE_CSV | FILE_ANSI, ',');

   if(file_handle == INVALID_HANDLE)
     {
      Print("Failed to open file. Error: ", GetLastError());
     }

   else
      if(file_handle != INVALID_HANDLE)
        {


         FileClose(file_handle);
        }

  }

Erläuterung:

Zunächst wurde eine String-Variable namens Filename angelegt, deren Wert auf Trading_Journal.csv gesetzt wurde. Ihre Aufgabe besteht darin, den Dateinamen zu speichern, auf den das Programm verweist. Diese Vorgehensweise sorgt für einen übersichtlicheren Code und gewährleistet, dass der Dateiname bei Bedarf problemlos geändert werden kann. Nachdem wir den Dateinamen definiert haben, rufen wir FileOpen auf und weisen file_handle den Rückgabewert zu. Das Dateihandle, das durch die zurückgegebene Ganzzahl dargestellt wird, weist der Datei eine eindeutige Programmkennung zu. Das Programm verwendet dieses Handle jedes Mal, wenn es Daten aus der Datei liest. Durch das Setzen des FILE_READ-Flags bei der Verwendung von FileOpen erhält das Programm die Berechtigung, den Inhalt der Datei zu lesen. Das FILE_WRITE-Flag wurde in diesem Fall weggelassen, da keine Änderungen oder Aktualisierungen erforderlich sind. Dadurch wird die Wahrscheinlichkeit verringert, dass die gespeicherten Daten versehentlich verändert werden.

Wir teilen MetaTrader mit, dass die Datei spaltenbasierte Daten im CSV-Format enthält. Jeder Wert wird durch das angegebene Komma getrennt, und der Zeichenkodierungsstandard wird durch die Option FILE_ANSI festgelegt. Zusammen sorgen diese Einstellungen dafür, dass die Datei erfolgreich geöffnet und wie vorgesehen verarbeitet wird. Der nächste entscheidende Schritt besteht darin, nach dem Versuch, die Datei zu öffnen, zu überprüfen, ob der Vorgang erfolgreich war. Es wird geprüft, ob die Variable file_handle den Wert invalid annimmt. Wenn FileOpen fehlschlägt, wird kein Standard-Handle-Wert zurückgegeben. Stattdessen wird ein ungültiges Handle zurückgegeben, was darauf hinweist, dass der Versuch, die Datei zu lesen, fehlgeschlagen ist.

Wenn eine Datei nicht geöffnet werden kann, zeigt das Programm eine Fehlermeldung und die entsprechende Fehlerkennung an. Aufgrund von Zugriffseinschränkungen, weil die Datei gerade verwendet wird oder weil die Datei nicht gefunden wurde, liefert diese Kennung eine Erklärung dafür, warum der Vorgang fehlgeschlagen ist. Diese Diagnosedaten sind nützlich, weil sie helfen, die Ursache des Problems zu finden und die passende Lösung abzuleiten. Wenn die Datei erfolgreich geöffnet wurde, kann das Programm problemlos fortfahren. Da es hier lediglich darum geht, die Zugänglichkeit zu überprüfen, wird die Datei geschlossen. Das Schließen stellt sicher, dass die Datei nicht offen bleibt oder für weitere Vorgänge nicht mehr verfügbar ist, und trägt dazu bei, Systemressourcen freizugeben.

Analogie:

Erstens ist es so, als würde man den Titel des gewünschten Buches auf einen Bestellzettel schreiben, wenn man die Variable Filename deklariert und sie der Datei Trading_Journal.csv zuweist. Man notiert es sich einfach an einer Stelle, anstatt den Buchtitel immer wieder laut auszusprechen. Nur dieser Zettel muss aktualisiert werden, wenn sich der Buchtitel ändert; die anderen nicht. Die Verwendung der Funktion FileOpen ist also vergleichbar mit der Ausleihe eines Buches in der Bibliothek. Sie erhalten von dem Bibliothekar einen Abholbeleg, sobald das Buch gefunden wurde. Dieses Abholticket entspricht dem Dateihandle. Ihre eindeutige Identität dient als Nachweis dafür, dass Sie derjenige sind, der derzeit Zugriff auf dieses Buch hat. Sie müssen diesen Beleg vorlegen, wann immer Sie daraus lesen möchten. Ohne das können Sie nicht auf das Buch zugreifen.

Wenn wir FILE_READ angeben, ist das so, als würde man dem Bibliothekar sagen: Ich möchte dieses Buch nur innerhalb der Bibliothek lesen. Sie beantragen keine Genehmigung, den Inhalt zu ändern oder Notizen darin einzufügen. Aus diesem Grund ist FILE_WRITE nicht enthalten. Wir ändern den Text nicht, wir lesen ihn nur. Dadurch wird verhindert, dass der ursprüngliche Inhalt unbeabsichtigt verändert wird. Das FILE_CSV-Flag entspricht der Information an den Bibliothekar, dass das Buch in Form einer Tabelle mit Zeilen und Spalten aufgebaut ist. Die Sprache bzw. der Textstil, in dem das Buch verfasst ist, wird durch das FILE_ANSI-Flag angegeben, während der Bibliothekar weiß, wie der Inhalt aufzuteilen ist, wenn er ihn an Sie weitergibt. Das Trennzeichen Komma bedeutet so viel wie, dass jede Information in dem Buch durch ein bestimmtes Symbol getrennt ist.

Vergewissern Sie sich immer wieder, dass der Bibliothekar das von Ihnen angeforderte Buch gefunden hat. Es gibt ein Problem, wenn der Bibliothekar sagt, dass es nicht auffindbar ist. Möglicherweise können Sie nicht auf das Buch zugreifen: Es könnte nicht vorhanden sein oder gerade ausgeliehen sein. Wenn Sie die Ursache herausfinden, wissen Sie, wie Sie das Problem beheben können. Es ist alles in Ordnung, wenn der Bibliothekar Ihnen den Ausleihschein aushändigen kann. Sie können das Buch jetzt lesen. Wenn Sie fertig sind, geben Sie den Abholschein zurück und geben das Buch ab. FileClose funktioniert folgendermaßen: Dadurch werden nicht nur die Ressourcen freigegeben und sichergestellt, dass das Buch nicht mehr unter Ihrem Namen gesperrt ist, sondern dem System wird auch mitgeteilt, dass Sie fertig sind.

Nachdem die Datei nun geöffnet ist, ermitteln wir, wie viele Elemente sie insgesamt enthält, was das Lesen und Verarbeiten der Datei erheblich vereinfachen wird.

Beispiel:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---

   string Filename = "Trading_Journal.csv";

   int  file_handle = FileOpen(Filename, FILE_READ | FILE_CSV | FILE_ANSI, ',');

   if(file_handle == INVALID_HANDLE)
     {
      Print("Failed to open file. Error: ", GetLastError());
     }

   else
      if(file_handle != INVALID_HANDLE)
        {

         int total_elements_count = 0;
         while(!FileIsEnding(file_handle))
           {
            FileReadString(file_handle);
            total_elements_count++;
           }

         Print(total_elements_count);


         FileClose(file_handle);
        }
  }

Erläuterung:

Als ersten Schritt muss eine Variable namens total_elements_count deklariert und mit dem Wert Null belegt werden. Mit dieser Variablen wird die Gesamtanzahl der aus der Datei gelesenen Elemente erfasst. Das Programm hat noch keine Daten gelesen; daher beginnt der Wert bei Null. In dieser Variablen wird die Gesamtanzahl fortlaufend erfasst, deren Wert stetig ansteigt, während das Programm Elemente aus der Datei liest. Solange Daten verfügbar sind, liest die Schleife die Datei wiederholt ein. Um festzustellen, ob noch weiteres Material vorhanden ist, überprüft eine Überwachungsfunktion, wo sich der Dateiverweis befindet. Die Schleife wird fortgesetzt, solange noch Daten vorhanden sind; die Funktion signalisiert der Schleife, dass sie beendet werden soll, sobald die Datei vollständig gelesen ist.

Das Programm liest innerhalb der Schleife das folgende Element aus der Datei. Diese Funktion ruft nicht nur Daten im Zeichenfolgenformat ab, sondern verschiebt auch automatisch den Dateizeiger. Da die Datei im CSV-Format vorliegt, wird bei jedem Aufruf in der Regel ein Element gelesen, das durch das angegebene Trennzeichen abgetrennt ist. Diese Vorgehensweise stellt sicher, dass das Programm jede Komponente einzeln verarbeitet, anstatt zu versuchen, die gesamte Datei auf einmal zu lesen. Nach dem Auslesen jedes Elements erhöht das Programm den Wert total_elements_count um eins. Da dabei die Anzahl der erfolgreich gelesenen Elemente im Auge behalten wird, ist dieser Schritt von entscheidender Bedeutung. Der Zähler wird jedes Mal, wenn ein neues Element geladen wird, auf den aktuellen Gesamtwert erhöht.

Sobald die Schleife endet, hat das Programm alle Elemente der Datei gezählt. Der letzte Schritt besteht darin, den Wert von total_elements_count anzuzeigen. Diese Ausgabe bestätigt, dass der Lesevorgang erfolgreich war, und zeigt die Gesamtanzahl der in der Datei enthaltenen Elemente an.

Analogie:

Angenommen, Sie haben ein Notizbuch dabei, wenn Sie eine Bibliothek betreten, und möchten die Anzahl der Bücher in einem bestimmten Regal ermitteln. Zuerst schreiben Sie 0 oben auf die Seite Ihres Notizblocks. Diese Null gibt an, wie viele Bücher Sie bisher gezählt haben. Da bisher noch nichts gezählt wurde, ist die Zahl null logisch gesehen sinnvoll. Das entspräche der Deklaration und Initialisierung der Variablen total_elements_count auf Null. Da fangen Sie an.

Dann beginnen Sie, sich von links nach rechts durch die Regale zu bewegen. Bevor Sie sich das nächste Buch aussuchen, schauen Sie immer erst ins Regal, um zu sehen, ob noch mehr da sind. Dann machen Sie mit dem nächsten Buch weiter, falls es noch welche gibt. Sie hören auf, nachzuschauen, ob das Regal leer ist und keine Bücher mehr da sind. Genau das macht FileIsEnding. Das ist so, als würde man nachsehen, ob man das Ende des Regals erreicht hat. Nein, Sie sind noch nicht am Ende, antwortet es, wenn noch Bücher übrig sind, und Sie zählen weiter. Sie hören auf, wenn die Meldung „Sie haben das Ende erreicht“ erscheint, sofern keine Bücher mehr vorhanden sind.

Bevor Sie nun ein Buch in die Hand nehmen und im Regal weiterblättern, überprüfen Sie, ob es auch wirklich da ist. Dieser Vorgang ähnelt dem elementweisen Lesen einer Datei, bei dem bei jedem Lesevorgang ein einzelnes Datenelement an Informationen abgerufen wird, bevor der Vorgang automatisch fortgesetzt wird. Der spaltenorientierte Aufbau der Datei, der dem CSV-Format ähnelt, ermöglicht die sequenzielle Verarbeitung der einzelnen Einträge. Bei jeder Buchbetrachtung erhöhen Sie die laufende Summe im Notizbuch um eins. Der Zähler läuft von 0 auf 1, dann auf 2, dann auf 3 und so weiter. Hierbei wird lediglich die Anzahl der angetroffenen Objekte gezählt, was in etwa dem Erhöhen eines Zählers bei jeder Verarbeitung eines neuen Elements entspricht.

Schließlich gelangt man ans Ende des Regals. Es müssen keine Bücher mehr gezählt werden. In diesem Moment halten Sie inne und schauen nach, welche Nummer in Ihrem Notizbuch steht. Die genaue Anzahl der Bücher im Regal geht aus dieser letzten Zahl hervor. Ebenso gibt das Programm am Ende der Schleife den Wert total_elements_count aus. Diese Zahl gibt an, wie viele Elemente die Datei insgesamt enthält, was bedeutet, dass die Zählung erfolgreich war.

 

Alle Dateielemente in einem dynamischen Array zusammenfassen

Anschließend wird jedes Element durchlaufen und in einem einzigen dynamischen Array gespeichert, sobald die Gesamtanzahl der Elemente ermittelt wurde. Ihr Programm kann jede beliebige Datenangabe schnell und konsistent abrufen, da jedes Element anhand seiner Position in der Datei indiziert ist. Da sich dynamische Arrays dynamisch anpassen können, um so viele Elemente wie nötig aufzunehmen, sind sie besonders nützlich für Dateien unterschiedlicher Größe. Sie können bestimmte Daten wie Trade-IDs, Lot-Größen oder beliebige andere Spalten aus Ihrem Trade-Journal schnell abrufen, indem Sie jedes Element anhand seines Indexes ansprechen, sobald es in das Array aufgenommen wurde.

Beispiel:

if(file_handle == INVALID_HANDLE)
  {
   Print("Failed to open file. Error: ", GetLastError());
  }

else
   if(file_handle != INVALID_HANDLE)
     {

      int total_elements_count = 0;
      while(!FileIsEnding(file_handle))
        {
         FileReadString(file_handle);
         total_elements_count++;
        }

      // Print(total_elements_count);

      FileSeek(file_handle, 0, SEEK_SET);
      string TotalElements[];
      ArrayResize(TotalElements,total_elements_count);

      for(int i = 0; i < total_elements_count; i++)
        {

         TotalElements[i] = FileReadString(file_handle);

        }

      Print(TotalElements[1]);

      FileClose(file_handle);
     }

Erläuterung:

Um das Auslesen aller Elemente zu ermöglichen, setzen wir die Datei zunächst auf den Anfang zurück. Wenn wir diesen Schritt weglassen, bleibt der Zeiger nach dem Zählen am Ende stehen; daher wird nichts gelesen. Um alle Inhalte von Anfang an zu sehen, ist das so, als würde man eine Kassette vor dem Abspielen zurückspulen. Anschließend durchläuft das Programm mithilfe einer for-Schleife jedes Element. Die Schleife wird so lange fortgesetzt, bis die gesamte Anzahl der gemessenen Elemente erreicht ist, beginnend mit dem Index 0. Bei dieser Methode wird jede Komponente der Datei unabhängig und systematisch verarbeitet.

Das Programm liest bei jeder Schleifeniteration das nächste verfügbare Element ein und fügt es an der aktuellen Indexposition in das Array ein. Im nächsten Schleifenzyklus springt die Prozedur automatisch zum nächsten Element. Dank der Verwendung eines dynamischen Arrays zur Speicherung der Elemente kann später systematisch und effizient auf jedes Element zugegriffen werden. Bei jeder Iteration der Schleife wird vom Programm ein Element aus der Datei entnommen und am entsprechenden Index in das Array eingefügt. Jedes Element wird separat behandelt, egal ob es sich um Überschriften, Text oder Zahlen handelt. Damit beim nächsten Durchlauf das nächste Element gelesen wird, springt der Lesemechanismus nach jeder Phase automatisch ein Element weiter.

Indem dem Programm jedem gelesenen Element ein eindeutiger Speicherplatz im dynamischen Array zugewiesen wird, ordnet es die Daten an. Auf diese Weise können Sie über den Index zu einem späteren Zeitpunkt auf jedes beliebige Element zugreifen. Das erste Element wird beispielsweise unter dem Index 0 gespeichert, das zweite unter dem Index 1 und so weiter. Die Verwendung eines dynamischen Arrays ist praktisch, da man so nicht mehr schätzen muss, wie viele Elemente die Datei enthalten wird. Jeder Datenwert kann sicher im Array gespeichert werden, sobald dessen Größe so angepasst wurde, dass alle Teile hineinpassen. Dadurch wird sichergestellt, dass alle Daten gespeichert werden und für spätere Analysen, Kategorisierungen oder Verarbeitungen zur Verfügung stehen. Zuletzt haben wir das zweite Element des Arrays ausgegeben, um zu überprüfen, ob die Elemente korrekt gespeichert wurden. Dieser Schritt dient als kurze Überprüfung, um sicherzustellen, dass die Indizierung ordnungsgemäß funktioniert und das Array den erwarteten Dateiinhalt enthält.

Analogie:

Zunächst setzen wir den Dateizeiger mit FileSeek an den Anfang. Das Zurückspulen einer VHS-Kassette oder einer Tonbandkassette vor dem erneuten Abspielen ist damit vergleichbar. Ohne diesen Schritt würde nichts gelesen werden, da sich der Zeiger am Ende befindet, nachdem alle Elemente gezählt wurden. FileSeek sorgt dafür, dass wir mit dem Lesen ab dem ersten Element der Datei beginnen. Das Programm verwendet dann eine for-Schleife, um jedes Element der Reihe nach durchzugehen. Stellen Sie sich ein Förderband vor, auf dem Kartons nacheinander transportiert werden. Ausgehend von der ersten Box durchläuft die Schleife nacheinander alle Boxen bis zur letzten. Jedes Kästchen steht für ein Datei-Element. Um sicherzustellen, dass kein Element übersehen wird, durchläuft das Programm jede Box einzeln.

Am Ende jedes Schleifenzyklus wird das folgende Dateielement gelesen und an der entsprechenden Indexposition in das Array eingefügt. Stellen Sie sich das so vor, als würden Sie Waren von einem Förderband nehmen und in beschriftete Behälter legen. Jedes nachfolgende Element wird in den nächsten Behälter gelegt, der mit der Nummer 0 gekennzeichnet ist. Wenn sich das Förderband von selbst vorwärts bewegt, wird das nächste Teil für den nächsten Zyklus vorbereitet. Das Programm weist jedem Element einen bestimmten Speicherplatz zu, indem es die Elemente in einem dynamischen Array speichert. Das ist vergleichbar mit der Nummerierung jedes Regals in einem Lager, damit man jeden Artikel später leicht finden kann. Durch die Verwendung eines dynamischen Arrays entfällt die Notwendigkeit, die Anzahl der Elemente im Voraus zu schätzen – das ist vergleichbar mit einem Lager, das so weit erweitert werden kann, dass es Platz für alle Ihre Produkte bietet.

 

Gruppierung von Dateidaten nach Spalten in dynamischen Arrays

Nachdem wir alle relevanten Elemente aus der Datei in ein einziges dynamisches Array eingelesen haben, fahren wir mit dem nächsten Schritt in diesem Abschnitt fort. Auch wenn es hilfreich ist, alle Elemente in einem Array zu haben, ist dies für die Verarbeitung oder Analyse bestimmter Datentypen wie TradeID, LotSize oder OpenPrice nicht besonders praktisch. Wir werden alle Elemente unter derselben Spaltenüberschrift in separate dynamische Arrays organisieren, um die Daten leichter verarbeiten zu können. Ein Array enthält beispielsweise alle TradeID-Daten, ein anderes alle LotSize-Werte und so weiter. Mit dieser Methode können wir schnell auf jede Art von Daten zugreifen, sie analysieren und getrennt weiterverarbeiten.

Stellen Sie sich das ähnlich wie die Organisation eines Aktenschranks vor. Anfangs liegen vielleicht alle Unterlagen in einem großen Stapel, sodass es schwierig ist, die benötigten Dokumente zu finden. Jede Kategorie – wie beispielsweise Rechnungen, Belege oder Berichte – kann einen eigenen Ordner haben, sodass Sie jedes Dokument leicht finden und bearbeiten können. Die Dateidaten können auch in spaltenspezifische Arrays gruppiert werden, um sicherzustellen, dass jede Art von Informationen über einen eigenen Speicherbereich verfügt. Dadurch wird Ihr Programm übersichtlicher, effektiver und besser auf die weitere Verarbeitung oder Analyse vorbereitet.

Zunächst fassen wir alle TradeID-Werte zu einer Gruppe zusammen. Jeder Eintrag in diesem Array – unserem ersten spaltenspezifischen Array – entspricht einer TradeID aus der Datei. Wir entwickeln eine systematische Methode, um schnell auf die eindeutige Kennung jedes Trades zugreifen zu können, indem wir die TradeID in ein eigenes dynamisches Array auslagern. Als Erstes sollten wir prüfen, ob die Datei die Gesamtzahl der Trades enthält. Nachdem wir dies überprüft haben, müssen wir die Position dieses Werts im Array ermitteln.

Abbildung 5. Gesamtzahl der Trades

Dieser Schritt ist entscheidend, da die Gesamtzahl der Trades direkten Einfluss darauf hat, wie viele TradeID-Werte im dynamischen Array gespeichert werden. Wenn wir diese Anzahl kennen, können wir das Array richtig zuweisen und Fehler vermeiden, wie zum Beispiel das Einfügen von TradeID-Werten, die außerhalb des zulässigen Bereichs liegen.

Beispiel:

FileSeek(file_handle, 0, SEEK_SET);
string TotalElements[];
ArrayResize(TotalElements,total_elements_count);

for(int i = 0; i < total_elements_count; i++)
  {

   TotalElements[i] = FileReadString(file_handle);

  }

int total_deals = (int)StringToInteger(TotalElements[10]);

Erläuterung:

Jedes Element der Datei wurde im vorangegangenen Abschnitt in einem dynamischen Zeichenfolgenarray namens TotalElements gespeichert. Die Gesamtzahl der Deals ist am elften Element bzw. unter Index 10 zu finden, da die Indizierung des Arrays bei Null beginnt. Der Variablen total_deals wird dieser Wert zugewiesen, nachdem sie von einer Zeichenkette in eine Ganzzahl umgewandelt wurde. Um die Größe des TradeID-Arrays sowie aller Schleifen, die die Deals verarbeiten, korrekt festzulegen, wird dieser numerische Wert benötigt, der die Gesamtzahl der Deals angibt.

Analogie:

Man kann sich das dynamische Array TotalElements beispielsweise als eine Reihe nummerierter Postfächer vorstellen, von denen jedes eine Dateiinformation enthält. Wir beginnen bei Null; daher befindet sich die Gesamtzahl der Deals im elften Postfach, das den Index 10 hat. Die Zahl im Briefkasten steht derzeit als Text auf einem Blatt Papier; daher können wir sie vorerst noch nicht für Berechnungen verwenden. Mit StringToInteger wird der Text in eine verwertbare Zahl umgewandelt. Das ist vergleichbar damit, ein Blatt Papier aus dem Briefkasten zu nehmen und es in eine echte Münze zu verwandeln, die gezählt oder für Berechnungen verwendet werden kann. Sobald wir die Zahl in eine Ganzzahl umgewandelt haben, können wir die TradeID-Plätze in unserem dynamischen Array belegen, ohne uns Gedanken darüber machen zu müssen, dass der Speicherplatz knapp werden könnte.

Der nächste Schritt besteht darin, den Index im Array TotalElements zu ermitteln, an dem die TradeID-Werte beginnen. Dies ist von entscheidender Bedeutung, da wir den genauen Speicherort der ursprünglichen TradeID kennen müssen, um alle daraus resultierenden Trade-Tickets ordnungsgemäß abrufen zu können. Nachdem wir diesen Startpunkt ermittelt haben, können wir alle Tickets für die Gesamtzahl der Trades in einem einzigen dynamischen Array vom Typ ulong speichern. Wir stellen sicher, dass jede TradeID aus der Datei ordnungsgemäß erfasst, für die weitere Verarbeitung vorbereitet oder wieder in die Datei geschrieben wird, indem wir ab dem Startindex eine Schleife durchlaufen und dieses Array füllen.

Beispiel:
else
   if(file_handle != INVALID_HANDLE)
     {

      int total_elements_count = 0;
      while(!FileIsEnding(file_handle))
        {
         FileReadString(file_handle);
         total_elements_count++;
        }

      // Print(total_elements_count);

      FileSeek(file_handle, 0, SEEK_SET);
      string TotalElements[];
      ArrayResize(TotalElements,total_elements_count);

      for(int i = 0; i < total_elements_count; i++)
        {

         TotalElements[i] = FileReadString(file_handle);

        }

      int total_deals = (int)StringToInteger(TotalElements[10]);

      ulong TradeID[];
      ArrayResize(TradeID,total_deals);

      int id_count = 0;

      for(int i = 24; i < total_elements_count; i += 12)
        {

         TradeID[id_count] = (ulong)StringToInteger(TotalElements[i]);
         id_count++;
        }

      ArrayPrint(TradeID);

      FileClose(file_handle);
     }

Erläuterung:

Zunächst wird ein dynamisches Array vom Typ ulong mit dem Namen TradeID deklariert. Da es sich dabei um sehr große Zahlen handeln kann, werden die jedem Trade in MQL5 zugewiesenen Ticketnummern als vorzeichenlose Long-Ganzzahlen gespeichert. Das Array kann jede mögliche Ticketnummer mithilfe von ulong sicher speichern, wodurch die Möglichkeit eines Überlaufs oder negativer Werte ausgeschlossen wird. Wir verwenden ein dynamisches Array, da wir erst nach dem Zählen wissen, wie viele Trade-Ticket wir haben werden. Wir passen die Größe des Arrays an die Gesamtzahl der gefundenen Deals an, die in total_deals gespeichert wird, nachdem dieses deklariert wurde. Dadurch werden Fehler verhindert, wenn wir versuchen, mehr Elemente hinzuzufügen, als das Array aufnehmen kann. Die Position des nächsten Trade-Tickets im TradeID-Array wird durch die Variable id_count verfolgt, die bei Null beginnt. Der Zähler wird jedes Mal erhöht, wenn ein Ticket hinzugefügt wird, um den nächsten verfügbaren Index anzuzeigen.

Ab Index 24 werden während der Schleife die Trade-Tickets aus dem Array TotalElements abgerufen. Kopfzeilen und kontobezogene Daten gehören zu den ersten 24 Elementen des CSV-Dateiformats, woraus sich diese Anordnung ergibt. Die erste TradeID befindet sich am 25. Element der Datei, was dem Index 24 im Array entspricht, da das Array eine nullbasierte Indizierung verwendet. Jeder Trade wird in der Datei in zwölf einzelne Elemente unterteilt. Um sicherzustellen, dass der Zeiger am Anfang des nächsten Trades steht, wird die Schleife jedes Mal um 12 erhöht. In dieser Phase wird die Übereinstimmung des Lesevorgangs mit der Struktur der Datei gewährleistet.

Abbildung 6. Dynamisches Array

Das Programm speichert das Element am aktuellen Index in TotalElements im Array TradeID an der durch id_count angegebenen Stelle innerhalb der Schleife. Id_count wird nach dem Speichern um eins erhöht, sodass das nächste Ticket die nächste Position im Array einnehmen kann. Dieser Vorgang wird so lange wiederholt, bis jedes Ticket für jeden Deal gespeichert ist.

Analogie:

Das TradeID-Array lässt sich mit einer Reihe leerer Fächer vergleichen, von denen jeder für die Aufnahme eines Trade-Tickets vorgesehen ist. Ähnlich wie bei einem eindeutigen Schlüssel oder einem Ausweis hat jeder Trade in Ihrem Konto eine Ticketnummer, und diese Nummern können ziemlich lang sein. Die Wahl eines Fächersystems, das groß genug ist, um alle Schlüssel sicher aufzunehmen, ohne dass sie zerbrechen oder überquellen, ist vergleichbar mit der Verwendung von ulong. Wir gestalten die Reihe der Fächer dynamisch, sodass sie sich genau auf die benötigte Anzahl von Schlüsseln erweitern lässt, da wir im Voraus nicht wissen, wie viele Transaktionen anfallen werden. Um die Gesamtzahl der Trades zu erreichen, vergrößern wir die Zeile, bevor wir damit beginnen, Schlüssel in die Fächer einzufügen. Um sicherzustellen, dass für jeden Schlüssel genau genügend Fächer vorhanden sind, ist dies vergleichbar mit dem Ausmessen der Reihe von Fächern.

Es kann vorkommen, dass einige Schlüssel zu Boden fallen, wenn wir die Anzahl der Fächer falsch berechnet haben – was in der Programmierung einem Out-of-Range-Fehler entspricht. Die Variable id_count dient als Zähler und gibt an, in welches Fach der nachfolgende Schlüssel eingeordnet werden soll. Wenn wir einen Schlüssel einlegen, springt der Zähler zum nächsten Fach. 

Stellen Sie sich nun eine lange Tabelle vor, in der Trade-Daten in Zeilen angeordnet sind. Die TradeID befindet sich im ersten der zwölf Fächer jeder Reihe. Da die ersten 24 Felder durch Kopfzeilen und Kontoinformationen belegt sind, beginnt die Schleife bei der ersten TradeID, die sich an der 25. Stelle der Tabelle befindet. Um zur TradeID in der folgenden Zeile zu gelangen, gehen wir die Tabelle entlang und durchlaufen dabei jeweils zwölf Spalten. Wir erfassen jede TradeID, sobald wir auf sie stoßen, und fügen sie in den nächsten freien Platz in unserem TradeID-Array ein. Am Ende werden die Trade-Tickets in die entsprechenden Fächer eingelegt. Durch die Suche nach einem bestimmten Platz können Sie nun jedes Trade-Ticket schnell finden.

Nachdem wir alle TradeID-Werte erfolgreich in einem dynamischen Array zusammengefasst haben, können wir uns den übrigen Spalten zuwenden. Symbol, OrderType, LotSize, OpenTime, OpenPrice, StopLoss, TakeProfit, CloseTime, ClosePrice, Profit und Result sind nur einige der Spalten der CSV-Datei, die abgerufen und in einem separaten dynamischen Array gespeichert werden können. Indem alle Elemente einer Spalte zusammengefasst werden, bleibt die Reihenfolge erhalten, und es ist später ganz einfach, auf einen bestimmten Datensatz zuzugreifen.

Beispiel:
//symbol
string symbol[];
ArrayResize(symbol,total_deals);

int sym_count = 0;

for(int i = 25; i < total_elements_count; i += 12)
  {

   symbol[sym_count] = TotalElements[i];
   sym_count++;
  }

//OrderType

string OrderType[];
ArrayResize(OrderType,total_deals);

int order_type_count = 0;

for(int i = 26; i < total_elements_count; i += 12)
  {

   OrderType[order_type_count] = TotalElements[i];
   order_type_count++;
  }

//LotSize
double LotSize[];
ArrayResize(LotSize,total_deals);

int lot_count = 0;

for(int i = 27; i < total_elements_count; i += 12)
  {

   LotSize[lot_count] = StringToDouble(TotalElements[i]);
   lot_count++;
  }

//OpenTime
datetime OpenTime[];
ArrayResize(OpenTime,total_deals);

int open_time_count = 0;

for(int i = 28; i < total_elements_count; i += 12)
  {

   OpenTime[open_time_count] = StringToTime(TotalElements[i]);
   open_time_count++;
  }

//OpenPrice
double OpenPrice[];
ArrayResize(OpenPrice,total_deals);

int open_price_count = 0;

for(int i = 29; i < total_elements_count; i += 12)
  {

   OpenPrice[open_price_count] = StringToDouble(TotalElements[i]);
   open_price_count++;
  }

//StopLoss
double StopLoss[];
ArrayResize(StopLoss,total_deals);

int StopLoss_count = 0;

for(int i = 30; i < total_elements_count; i += 12)
  {

   StopLoss[StopLoss_count] = StringToDouble(TotalElements[i]);
   StopLoss_count++;
  }

// TakeProfit
double TakeProfit[];
ArrayResize(TakeProfit,total_deals);

int TakeProfit_count = 0;

for(int i = 31; i < total_elements_count; i += 12)
  {

   TakeProfit[TakeProfit_count] = StringToDouble(TotalElements[i]);
   TakeProfit_count++;
  }

//CloseTime
datetime CloseTime[];
ArrayResize(CloseTime,total_deals);

int close_time_count = 0;

for(int i = 32; i < total_elements_count; i += 12)
  {

   CloseTime[close_time_count] = StringToTime(TotalElements[i]);
   close_time_count++;
  }

//ClosePrice
double ClosePrice[];
ArrayResize(ClosePrice,total_deals);

int close_price_count = 0;

for(int i = 33; i < total_elements_count; i += 12)
  {

   ClosePrice[close_price_count] = StringToDouble(TotalElements[i]);
   close_price_count++;

  }

//Profit
double Profit[];
ArrayResize(Profit,total_deals);

int Profit_count = 0;

for(int i = 34; i < total_elements_count; i += 12)
  {

   Profit[Profit_count] = StringToDouble(TotalElements[i]);
   Profit_count++;

  }

//Result
string Result[];
ArrayResize(Result,total_deals);

int Result_count = 0;

for(int i = 35; i < total_elements_count; i += 12)
  {

   Result[Result_count] = TotalElements[i];
   Result_count++;

  }

ArrayPrint(Result);

Erläuterung:

Die erste Spalte, die wir uns ansehen, ist Symbol. Um alle Symbole für jeden Deal zu speichern, deklarieren wir zunächst ein dynamisches Zeichenfolgen-Array namens symbol. Wir passen die Größe des Arrays an total_deals an, da wir nun wissen, wie viele Trades es insgesamt gibt, sodass das Array alle Trade-Symbole sicher speichern kann. Außerdem wird ein Zähler sym_count angelegt, der auf Null initialisiert wird. Der aktuelle Index in dem Array, in dem das folgende Symbol gespeichert wird, wird von diesem Zähler verfolgt. Da das Symbol direkt nach der Trade-ID an Index 24 steht, beginnt die Schleife bei Index 25 des Arrays TotalElements. Die Schleife verläuft in Zwölfer-Schritten, wobei jeder Trade aus zwölf Elementen besteht. Am Ende jeder Iteration wird das aktuelle Element in das Symbol-Array kopiert. Nach dem Speichern wird der Wert der Variablen sym_count erhöht, um den nächsten freien Speicherplatz im Array anzugeben.

Als Nächstes folgt die Spalte OrderType. Wir passen die Größe von OrderType an total_deals an und deklarieren ein weiteres dynamisches Zeichenfolgen-Array. Außerdem wird ein Zähler namens order_type_count verwendet. Da OrderType in der CSV-Datei unmittelbar nach Symbol steht, beginnt die for-Schleife beim Index 26. Die Schleife springt zum nächsten OrderType des nachfolgenden Trades, indem sie bei jeder Iteration um zwölf erhöht wird, genau wie bei den Symbolen. Jeder Wert wird im Array OrderType gespeichert, und der Zähler wird erhöht. Dadurch wird sichergestellt, dass die Art des Deals in der richtigen Reihenfolge erfasst wird. Wir deklarieren ein dynamisches Doppelarray namens LotSize, da es sich bei den Losgrößen um Dezimalzahlen handelt. Wir verwenden einen lot_count-Zähler und erhöhen dessen Wert. Ab Index 27 erhöht sich der Wert der for-Schleife erneut um 12. Bevor ein Element im Array gespeichert wird, wird es einer StringToDouble-Konvertierung unterzogen, d. h., es wird von einer Zeichenkette in einen Double-Wert umgewandelt. Um die nächste Losgröße im nächsten Speicherplatz zu speichern, wird der Zähler erhöht.

Da Datum und Uhrzeit in der Spalte OpenTime gespeichert sind, definieren wir ein Datetime-Array und einen Zähler namens open_time_count. Die Schleife verwendet StringToTime, um jedes Element zu konvertieren, beginnend bei Index 28 und mit einer Schrittweite von 12. Dadurch wird sichergestellt, dass das Array die Eröffnungszeit jedes Trades ordnungsgemäß als Datums- und Zeitwert speichert. Wir deklarieren ein Double-Array für OpenPrice, passen dessen Größe an und nutzen anschließend open_price_count. Ab Index 29 erhöht sich der Schleifenwert um 12, jedes Zeichenfolgenelement wird in einen Double-Wert umgewandelt und anschließend in das Array eingefügt. Der Zähler sorgt dafür, dass der nächste Eröffnungskurs im nächsten freien Array-Index gespeichert wird.

Die Spalten TakeProfit und StopLoss weisen dasselbe Muster auf. Ein Zähler, ein Double-Array und eine Schleife, die bei StopLoss beim Index 30 und bei TakeProfit beim Index 31 beginnt, sind jeweils vorhanden. Da die Zähler die Positionen verfolgen, wird jedes Zeichenfolgenelement aus der Datei in einen Double-Wert umgewandelt und im entsprechenden Array gespeichert. Als Nächstes folgt CloseTime, das mit OpenTime vergleichbar ist. Ein Zähler namens close_time_count wird initialisiert, und ein datetime-Array wird generiert und in seiner Größe angepasst. Die Schleife beginnt bei Index 32, erhöht den Index um 12, wandelt jedes Element mit StringToTime um und speichert anschließend das Ergebnis.

Wir verwenden erneut Arrays und Zähler für ClosePrice, Profit und Result. Wir wandeln die Dateielemente vor dem Speichern in den Typ double um, da ClosePrice und Profit vom Typ double sind. Für das Ergebnis wird dagegen ein String-Array verwendet. Um für den nächsten Trade zur nächsten entsprechenden Spalte zu springen, beginnen die Schleifen jeweils bei den Indizes 33, 34 und 35 und werden jedes Mal um zwölf erhöht. Die Zähler jedes Arrays stellen sicher, dass jedes Element an der richtigen Position eingefügt wird. Nach Abschluss des Vorgangs werden die Daten jeder Spalte in einem separaten dynamischen Array gespeichert. Ein einzelner Trade wird in jedem Array durch dieselbe Indexposition dargestellt, und alle Arrays haben dieselbe Anzahl an Einträgen (total_deals). Dank dieses gut strukturierten Rahmens können Trade-Daten auf strukturierte und zuverlässige Weise abgerufen, überprüft und weiterverarbeitet werden.


Schlussfolgerung

Indem wir gelernt haben, wie man eine CSV-Datei ausliest und deren Inhalt in dynamischen Arrays organisiert, haben wir unser Verständnis der Dateiverarbeitung in MQL5 erheblich vertieft. Nachdem wir die Gesamtanzahl der Elemente in der Datei ermittelt hatten, haben wir alle Daten in einem einzigen Array zusammengefasst und anschließend jede Spalte in ein eigenes dynamisches Array aufgeteilt. Diese Methode ermöglicht es uns nicht nur, schnell auf jeden Deal zuzugreifen und ihn zu analysieren, sondern schafft auch die Grundlage für komplexere Vorhaben wie statistische Analysen und die Erstellung einer Saldenkurve. Durch diese Strukturierung der Dateidaten haben wir ein solides Gerüst für die programmgesteuerte Verwaltung der Handelshistorie geschaffen und Sie damit auf den nächsten Teil vorbereitet, in dem wir diese Arrays zur Visualisierung der Handelsperformanz nutzen werden.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/21309

Beigefügte Dateien |
Trading-Disziplin in Code umsetzen (Teil 2): Entwicklung eines MQL5-Systems zur Durchsetzung eines täglichen Handelslimits für alle Trades in MQL5 Trading-Disziplin in Code umsetzen (Teil 2): Entwicklung eines MQL5-Systems zur Durchsetzung eines täglichen Handelslimits für alle Trades in MQL5
Wir haben ein System entwickelt, das ein tägliches Handelslimit durchsetzt, damit Sie Ihre Handelsregeln einhalten können. Es überwacht alle über das Konto ausgeführten Trades und greift automatisch ein, sobald das festgelegte Limit erreicht ist, wodurch weitere Aktivitäten verhindert werden. Durch die direkte Integration der Kontrollmechanismen in die Plattform stellt das System sicher, dass die Disziplin auch bei steigendem Marktdruck gewahrt bleibt.
Statistische Arbitrage anhand kointegrierter Aktien (Abschluss): Datenanalyse mit einer spezialisierten Datenbank Statistische Arbitrage anhand kointegrierter Aktien (Abschluss): Datenanalyse mit einer spezialisierten Datenbank
Der Artikel zeigt, wie man SQLite (OLTP) mit DuckDB (OLAP) für die Datenverarbeitung im Bereich der statistischen Arbitrage kombinieren kann. Die spaltenorientierte Engine von DuckDB, ASOF JOIN und die Array-Funktionen beschleunigen Kernaufgaben wie die zeitliche Zuordnung von Kursen zu Trades sowie RWEC, wobei bei größeren Eingabedaten im Vergleich zu SQLite Geschwindigkeitssteigerungen von 2- bis 23-fach gemessen wurden. Das ermöglicht einfachere Abfragen und schnellere Analysen, während die Handelsausführung weiterhin in SQLite erfolgt.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Erstellen benutzerdefinierter Indikatoren in MQL5 (Teil 8): Integration von Volumendaten für eine tiefere Marktprofilanalyse Erstellen benutzerdefinierter Indikatoren in MQL5 (Teil 8): Integration von Volumendaten für eine tiefere Marktprofilanalyse
In diesem Artikel erweitern wir den hybriden Marktprofil-Indikator Time Price Opportunity (TPO) in MQL5 durch die Einbindung von Volumendaten, um volumenbasierte Kontrollpunkte, Wertbereiche und den volumengewichteten Durchschnittspreis mit anpassbaren Hervorhebungsoptionen zu berechnen. Das System bietet erweiterte Funktionen wie die Erkennung der Initial Balance, Projektionslinien wichtiger Kursniveaus, Split-Profile sowie alternative TPO-Symbole wie Quadrate oder Kreise, um die visuelle Analyse über mehrere Zeitrahmen hinweg zu verbessern.