English Русский 中文 Español 日本語 Português
preview
Kategorientheorie in MQL5 (Teil 8): Monoide

Kategorientheorie in MQL5 (Teil 8): Monoide

MetaTrader 5Tester | 19 Juli 2023, 14:59
105 0
Stephen Njuki
Stephen Njuki

Einführung

In unserem vorangegangenen Artikel über die Kategorientheorie haben wir die Schlüsselkonzepte von Mehrfachmengen, relativen Mengen und indizierten Mengen aufgedeckt und ihre Bedeutung für den algorithmischen Handel untersucht. In dieser Folge stellen wir nun das Konzept der Monoiden vor. Monoide bilden eine wesentliche Grundlage in der Mathematik und Informatik, da sie einen strukturierten Ansatz für die Modellierung von Operationen auf Elementmengen bieten.

Per Definition sind Monoide eine Sammlung von drei Dingen, nämlich einer Menge; einer Binäre Operation, die zwei beliebige Elemente dieser Menge annimmt und immer ein Element ausgibt, das auch ein Mitglied dieser Menge ist; und einem Identitätselement, das zu der Menge gehört, sodass, wenn es mit einem anderen Mitglied dieser Menge in der oben erwähnten binären Operation gepaart wird, die Ausgabe immer das andere Element ist, mit dem dieses Identitätselement gepaart ist. Diese binäre Operation ist auch assoziativ. Anders ausgedrückt: Ein Monoid ist eine Möglichkeit, Elemente in einer Menge unter Einhaltung vorgegebener Regeln zu kombinieren. Monoide bieten einen systematischen und flexiblen Ansatz für die Aggregation und Manipulation von Daten.

Formal kann ein Monoid M mit den Elementen a, b und c, einem Identitätselement e und der binären Operation * wie folgt definiert werden:

M * M - - > M;                      1


e * a - - > a;                        2


a * e - - > a;                        3


a * (b * c) - - > (a * b) * c     4

Gleichung 1 betont also, dass die Paarung von 2 beliebigen Mitgliedern der Menge ein Mitglied der Menge ergibt. Die Gleichungen 2 und 3 betonen die Bedeutung des Identitätselements, da die Ausgabe immer das Element der binären Operation ist, das nicht die Identität ist. Und schließlich verdeutlicht Gleichung 4 die Assoziativität der binären Operation *.


Illustration & Methoden

Um die mögliche Anwendung von Monoiden auf Händler zu veranschaulichen, betrachten wir 5 Entscheidungen, mit denen einige oder die meisten Händler konfrontiert werden können, bevor sie Geschäfte abschließen. Das sind:

  1. Die Länge des Blicks in die Vergangenheit.
  2. Der zu verwendende Chart-Zeitrahmen.
  3. Der zu verwendende angewandte Preis.
  4. Die Auswahl des Indikators.
  5. Und ob man angesichts dieser Informationen innerhalb einer Kursspanne oder mit dem Trend handeln soll.

Für jede dieser Entscheidungen werden wir uns etwas einfallen lassen:

  • eine Reihe möglicher Werte, aus denen man wählen kann;
  • eine binäre Operation, mit der zwischen zwei beliebigen Elementen ausgewählt werden kann. Diese Operation kann eine MQL5-Methode sein, die von einer anderen Methode iterativ durch alle Elemente der Menge aufgerufen wird, bis eine Auswahl getroffen ist.
  • Einen Index für das Identitätselement dieser Menge. Index, da dieses Element in einer Menge, die ein Array ist, enthalten sein wird.
  • Die Wahl der idealen binären Operation, die einfach zwischen 2 Elementen wählt, ist eine der folgenden:
  • Die Operation, die das kleinere der beiden Elemente auswählt.
  •  Die Operation, die das größte der beiden zu bewertenden Elemente auswählt.
  •  Die Operation, die aus der Menge das Element auswählt, das dem Mittelwert der beiden Elemente in der Binäroperation am nächsten kommt.
  •  Und schließlich die Operation, die aus der Menge das Element auswählt, das am weitesten vom Mittelwert der beiden Elemente der binären Operation entfernt ist.

Wir berücksichtigen 5 Entscheidungspunkte: Rückblickzeitraum, Zeitrahmen, angewandter Preis, Indikator und Signalinterpretation. Ein anderer Händler hat möglicherweise andere wichtige Entscheidungspunkte. Es ist daher wichtig zu bedenken, dass dies keine endgültige Schritt-für-Schritt-Anleitung ist, sondern einfach für diesen Artikel ausgewählt wurde.

Bei der Verwendung von Monoiden der Kategorientheorie zur Klassifizierung von Daten sollten einige Vorsichtsmaßnahmen getroffen werden, um genaue und aussagekräftige Ergebnisse zu gewährleisten. Hier eine mögliche Liste, die Sie im Auge behalten sollten:

1) Sie haben eine wohldefinierte Monoidstruktur:

Sie müssen sicherstellen, dass die Daten, mit denen Sie arbeiten, eine gültige Monoidstruktur gemäß der Definition bilden. Dies bedeutet, dass überprüft werden muss, ob sie die Monoid-Axiome erfüllt, d. h. ob sie ein Identitätselement hat und unter der Binäroperation assoziativ ist. Hier sind drei Beispiele für Fallstricke, die zu einer schlecht definierten Monoidstruktur führen können:

Fehlende Abgeschlossenheit:

Wenn die binäre Operation, die im Monoid verwendet wird, nicht zu Elementen führt, die zur gleichen Menge oder zum gleichen Bereich gehören, ist der Abschluss verletzt. Wenn man zum Beispiel versucht, ein Monoid auf natürlichen Zahlen mit Hilfe der Subtraktionsoperation zu definieren, stößt man auf Elemente, die keine natürlichen Zahlen sind (z. B. ist die Subtraktion von 5 von 3 gleich -2, was keine natürliche Zahl ist). Eine Operation ist streng genommen weder eine Additions-, Subtraktions- noch eine Multiplikationsfunktion. Es ist einfach eine Methode mit wohldefinierten Regeln, die zwei beliebige Elemente der Menge annimmt und ein Element zurückgibt, das Mitglied dieser Menge ist.

Nicht-Assoziativität:

Ein weiterer Fallstrick besteht darin, dass die binäre Operation die assoziative Eigenschaft nicht erfüllt. Wenn Elemente in Ihrem Monoid nicht auf assoziative Weise kombiniert werden, kann dies zu mehrdeutigen und inkonsistenten Ergebnissen führen. Betrachten wir zum Beispiel ein Monoid, bei dem die Operation Multiplikation ist und die Elemente Matrizen sind. Bei drei Matrizen a, b und c ist diese Operation nicht assoziativ, d. h. (a * b) * c ≠ a * (b * c), wodurch die monoide Struktur beeinträchtigt wird.

Fehlen eines Identitätselements:

Jedes Monoid muss ein Identitätselement haben, das als neutrales Element unter der binären Operation fungiert. Fehlt dem Monoid ein Identitätselement, wird es problematisch, Operationen mit bestimmten Elementen durchzuführen. Wenn man beispielsweise ein Monoid für reelle Zahlen mit Hilfe der Divisionsoperation definiert, gibt es kein Identitätselement, da die Division durch Null undefiniert ist.

Auf der anderen Seite gibt es drei Beispiele für echte Monoidstrukturen:

Addition von ganzen Zahlen:

Die Menge der ganzen Zahlen, ausgestattet mit der binären Operation der Addition (+), bildet ein Monoid. Das Identitätselement ist 0, und die Addition ist sowohl assoziativ als auch geschlossen für die Menge der ganzen Zahlen.

Multiplikation von rationalen Zahlen, die nicht Null sind:

Die Menge der rationalen Zahlen (Brüche), die nicht Null sind, bildet mit der binären Operation der Multiplikation (×) ein Monoid. Das Identitätselement ist 1, und die Multiplikation ist assoziativ und geschlossen für rationale Zahlen ungleich Null.

Verbinden von Zeichenketten:

Die Menge der Zeichenketten bildet mit der binären Operation der Verkettung ein Monoid. Das Identitätselement ist die leere Zeichenkette (""), und die Verkettung ist assoziativ und für Zeichenketten geschlossen.

Diese Beispiele zeigen wohldefinierte monoide Strukturen, die die geforderten Eigenschaften erfüllen und sich effektiv für Klassifizierungszwecke einsetzen lassen.

2) Semantik und Interpretierbarkeit:

Verstehen Sie die Semantik der Monoid-Operation und ihre Beziehung zu Ihren Daten. Überlegen Sie, ob die resultierenden Klassifizierungen mit den beabsichtigten Interpretationen übereinstimmen und im Kontext Ihres Problembereichs sinnvoll sind. Diese 5 Beispiele sollen dies verdeutlichen:

Klassifizierung der Worthäufigkeit:

Nehmen wir an, Sie verwenden Monoide, um die Quartalsberichte von Unternehmen aus der Gesprächsmitschrift anhand der Worthäufigkeit zu klassifizieren. Während die monoide Operation einfach eine Summierung der Wortzahlen beinhalten könnte, müsste die Interpretierbarkeit der resultierenden Klassifizierungen sorgfältig geprüft werden, da sie von der Semantik abhängt, die den verschiedenen Frequenzbereichen zugeordnet wird. So könnten Sie beispielsweise Dokumente mit einer hohen Worthäufigkeit so interpretieren, dass sie sich eher auf ein bestimmtes Thema konzentrieren, während eine niedrige Häufigkeit bestimmter Wörter auf einen breiteren oder vielfältigeren Inhalt hinweisen könnte. Was Sie nicht tun sollten, ist, sich nur auf die Gesamtwortzahl zu konzentrieren und sie als Grundlage für Ihre monoiden Schlüsseloperationen zu verwenden.

Stimmungsanalyse:

Nehmen wir an, Sie verwenden ein Monoid, um die Stimmung eines Textes zu klassifizieren. Die monoide Operation könnte durch die Aggregation von Stimmungswerten (sentiment scores) aus einzelnen Wörtern oder Sätzen besser funktionieren. Betrachten wir ein Beispiel. Angenommen, Sie haben eine Reihe von Kundenrezensionen für ein Produkt und möchten diese in drei Stimmungskategorien einteilen: positiv, neutral und negativ. Sie entscheiden sich für einen monoiden Ansatz, bei dem die monoide Operation die Aggregation von Stimmungswerten aus einzelnen Sätzen innerhalb jeder Rezension beinhaltet. In diesem Beispiel vergeben Sie Stimmungswerte von -1 bis 1, wobei -1 für eine stark negative Stimmung, 0 für eine neutrale Stimmung und 1 für eine stark positive Stimmung steht. Die monoide Operation würde dann eine einfache Summierung der Stimmungswerte vornehmen. Betrachten wir nun eine Kundenrezension:

Rückblick: „Das Produkt ist gut. Der Kundenservice war jedoch unterdurchschnittlich.“

Die richtige Art, dies zu klassifizieren, wäre, es in einzelne Sätze aufzuteilen und jedem Satz Stimmungswerte zuzuordnen:

Satz 1: „Das Produkt ist gut“ - Stimmungswert: 0,8 (positiv)

Satz 2: „Allerdings war der Kundenservice unterdurchschnittlich“ - Stimmungswert: -0,7 (negativ)

Um dann die Gesamtbewertung für die Bewertung zu erhalten, wenden wir die monoide Operation an, die in diesem Fall die Summierung ist:

Gesamtbewertung der Stimmung = 0,8 + (-0,7) = 0,1

Auf der Grundlage der Semantik, die den Stimmungsbewertungsbereichen zugewiesen wurde, interpretieren Sie die Gesamtstimmungsbewertung von 0,1 als eine leicht positive Stimmung. Daher stufen Sie diese Bewertung auf der Grundlage der monoiden Klassifizierung als „neutral“ oder „leicht positiv“ ein. Was Sie nicht wollen, ist, beide Sätze als einen zu betrachten und eine anekdotische Note zu vergeben, weil das Wort „gut“ vorkommt. Es wäre eine gute Praxis, die Details zu berücksichtigen.

Bild-Klassifizierung:

Ein Monoid zur Klassifizierung von Bildern auf der Grundlage visueller Merkmale wie Farbe, Textur oder Form würde die Kombination dieser Merkmale beinhalten, und die Interpretierbarkeit dieser kombinierten Merkmale, die zu Klassifizierungen führt, hängt davon ab, wie man sie in die beabsichtigten oder sinnvollen Kategorien einordnet. Die Semantik, die verschiedenen Kombinationen von Merkmalen zugewiesen wird, kann das Verständnis der Klassifizierungsergebnisse stark beeinflussen.

Die folgende Abbildung zeigt, wie wichtig Semantik und Interpretierbarkeit bei der Bildklassifizierung mit Monoiden sind. Angenommen, Sie verwenden einen monoiden Ansatz, um Bilder in zwei Kategorien zu klassifizieren: „Hund“ und „Katze“ (für Händler könnte dies durch z.B. Auf- und Abwärtsmuster von Kopf und Schulter ersetzt werden, aber das Prinzip bleibt dasselbe), die auf visuellen Merkmalen basieren. Die monoide Operation würde die Kombination von Farb- und Texturmerkmalen aus den Bildern beinhalten. Für unsere Zwecke nehmen wir an, dass Sie zwei wichtige visuelle Merkmale haben: „Fellfarbe“ und „Texturkomplexität“. Die Fellfarbe kann entweder als „hell“ oder „dunkel“ eingestuft werden, während die Komplexität der Textur entweder als „einfach“ oder „komplex“ eingestuft werden kann. Betrachten wir nun zwei Bilder.

Bild 1 zeigt eine weiße Katze mit einfacher Felltextur:

  • Fellfarbe: Licht
  • Komplexität der Textur: Einfach
  • Und Bild 2 zeigt einen schwarzen Hund mit komplexer Felltextur, was darauf hindeutet:
  • Fellfarbe: dunkel
  • Komplexität der Textur: komplex

Um diese Bilder mit Hilfe des Monoid-Ansatzes zu klassifizieren, kombinieren Sie die visuellen Merkmale gemäß der Monoid-Operation (z. B. Verkettung, Summierung usw.):

Für Bild 1: „Leicht“ + „Einfach“ = „LeichtEinfach“

Für Bild 2: „Dunkel“ + „Komplex“ = „DunkelKomplex“

Jetzt kommt der entscheidende Teil, nämlich die Semantik und die Interpretierbarkeit. Sie müssen den kombinierten Merkmalen eine Bedeutung zuweisen, um sie sinnvollen Kategorien zuordnen zu können. In unserem Fall, da wir ein allzu einfaches Beispiel verwenden:

„LeichtEinfach“ könnte als Kategorie „Katze“ interpretiert werden, da eine helle Fellfarbe und eine einfache Textur gemeinsame Merkmale von Katzen sind.

„DunkelKomplex“ könnte als Kategorie „Hund“ interpretiert werden, da dunkle Fellfarbe und komplexe Textur oft mit Hunden assoziiert werden.

Indem Sie den kombinierten Merkmalen eine angemessene Semantik und Interpretation zuweisen, können Sie Bild 1 als „Katze“ und Bild 2 als „Hund“ korrekt klassifizieren.

Kundensegmentierung:

Angenommen, Sie verwenden Monoide, um Kunden auf der Grundlage ihres Kaufverhaltens zu segmentieren. Die monoide Operation kann die Aggregation von Transaktionsdaten oder Kundenattributen beinhalten. Die Interpretierbarkeit der resultierenden Segmente hängt jedoch davon ab, wie Sie diese Segmente interpretieren und beschriften. Beispielsweise können Sie auf der Grundlage der Semantik und des Domänenwissens Bezeichnungen wie „hochwertige Kunden“ oder „abwanderungsgefährdete Kunden“ vergeben.

Klassifizierung von Zeitreihen:

Erwägen Sie den Einsatz von Monoiden zur Klassifizierung von Zeitreihendaten, wie z. B. Börsentrends. Die monoide Operation könnte die Kombination verschiedener Merkmale wie Preis, Volumen und Volatilität beinhalten. Die Interpretierbarkeit der Klassifizierungen hängt jedoch davon ab, wie Sie die Semantik der resultierenden Kombinationen in Bezug auf die Marktbedingungen definieren. Unterschiedliche Interpretationen können zu unterschiedlichen Erkenntnissen und Entscheidungsfindungen führen.

In all diesen Beispielen sind die der monoiden Operation zugewiesene Semantik und die sich daraus ergebenden Klassifikationen entscheidend für eine sinnvolle Interpretation und Entscheidungsfindung. Die sorgfältige Berücksichtigung dieser Semantik stellt sicher, dass die resultierenden Klassifizierungen mit den gewünschten Interpretationen übereinstimmen und eine effektive Analyse und das Verständnis der Daten ermöglichen.

3) Vorverarbeitung der Daten:

Dies ist wichtig für die Qualitätskontrolle der Daten, bevor sie mit Monoiden klassifiziert werden. Um die Kompatibilität mit der monoiden Struktur zu gewährleisten, wäre es sinnvoll, die Daten vorzuverarbeiten. Zum Beispiel sollten alle Ausgaben von Operationsfunktionen definitive Mitglieder der monoiden Menge sein und keine fließenden Daten mit mehreren Dezimalstellen, die beim Runden mehrdeutig sein könnten. Dies kann durch Normalisierung der Daten (Regularisierung) oder durch Umwandlung in ein für die Monoidoperation geeignetes Format erreicht werden. Aber auch der Umgang mit fehlenden Werten muss für eine bessere Gesamtkonsistenz Ihres Handelssystems berücksichtigt werden.

4) Homogenität der Daten:

Vergewissern Sie sich, dass die Daten, die Sie klassifizieren, innerhalb jeder Kategorie einen gewissen Grad an Homogenität aufweisen. In der Phase der Indikatorenauswahl sollte das von uns verwendete Mengenmonoid beispielsweise beide Indikatoren mit konsistenten und vergleichbaren Werten oder Gewichtungen enthalten. Da wir den RSI-Oszillator und die Bollinger-Bänder verwenden, ist dies eindeutig nicht standardmäßig der Fall. Wir werden jedoch eine von ihnen normalisieren, um sicherzustellen, dass beide vergleichbar und homogen sind. Monoide funktionieren am besten, wenn sie auf Daten angewendet werden, die innerhalb jeder Klasse ähnliche Merkmale aufweisen.

5) Kardinalität der Kategorien:

Betrachten Sie die Kardinalität oder die Anzahl der verschiedenen Kategorien, die mit Hilfe des Monoids gebildet werden können. Ist die Anzahl der resultierenden Kategorien zu hoch oder zu niedrig, kann dies die Nützlichkeit der Klassifizierung oder die Interpretierbarkeit der Ergebnisse beeinträchtigen.

Lassen Sie uns die Auswirkungen der Datenkardinalität von Kategorien anhand eines Beispiels veranschaulichen:

Angenommen, Sie arbeiten an einer Klassifizierungsaufgabe zur Vorhersage der Richtung eines Devisenpaares auf der Grundlage der Stimmung von Kalendernachrichten und haben einen Datensatz mit einer Zielvariablen „Stimmung“, die drei mögliche Werte annehmen kann: „über der Erwartung“, „entspricht der Erwartung“ und „unter der Erwartung“.

Hier ist ein Beispieldatensatz:

Überprüfung Stimmung
Fed verarbeitendes Gewerbe Produktion m/m unter
GDT Preisindex entspricht
Unternehmensvorräte m/m über
NAHB Wohnungsmarkt-Index unter

In diesem Beispiel können Sie sehen, dass die Variable „Stimmung“ drei Kategorien hat: „Über“, „Entspricht “ und „Unter“. Die Kardinalität bezieht sich auf die Anzahl der unterschiedlichen Kategorien in einer Variablen.

Die Kardinalität der Variable „Sentiment“ in diesem Datensatz ist 3, da sie drei eindeutige Kategorien hat.

Die Datenkardinalität von Kategorien kann Auswirkungen auf Klassifizierungsaufgaben haben. Betrachten wir zwei Szenarien:

Szenario 1:

Hier liegt eine unausgewogene Datenkardinalität vor, die zu einem unausgewogenen Datensatz führt, bei dem die Kategorie „Über“ im Vergleich zu den Kategorien „Entspricht“ und „Unter“ eine erheblich größere Anzahl von Stichproben aufweist. Nehmen wir zum Beispiel an, dass 80 % der Proben als „Über“, 10 % als „Entspricht“ und 10 % als „Unter“ gekennzeichnet sind.

In diesem Szenario kann die unausgewogene Datenkardinalität zu Verzerrungen im Klassifizierungsmodell führen. Das Modell könnte dazu neigen, die Mehrheitsklasse („Über“) häufiger vorherzusagen, während es Schwierigkeiten hat, die Minderheitsklassen („Entspricht“ und „Unter“) genau vorherzusagen. Dies kann zu einer geringeren Genauigkeit und einem geringeren Wiedererkennungswert für die Minderheitenklassen führen, was sich auf die Gesamtleistung und die Interpretierbarkeit des Klassifizierungsmodells auswirkt.

Szenario 2:

In diesem Fall werden den Elementen der monoiden Menge keine Zeichenkettenwerte zugewiesen, wie es oben der Fall war, sondern wir verwenden Fließkommadaten, um jedes Element der monoiden Menge genauer zu gewichten und zu beschreiben. Dies bedeutet auch, dass wir eine ungebundene Kardinalität haben, d. h. es gibt keine festgelegte Anzahl von möglichen Gewichten/Werten für jedes der Mengenelemente wie in Szenario 1.

6) Skalierbarkeit:

Sie müssten die Skalierbarkeit der Monoid-Klassifikation bewerten, insbesondere bei der Verarbeitung großer Datenmengen. Je nach der Komplexität der Berechnungen kann es notwendig sein, alternative Techniken oder Optimierungen in Betracht zu ziehen, um große Datenmengen effizient zu verarbeiten. Ein Ansatz für den Umgang mit großen Datensätzen ist die technische Merkmalsanalyse. Monoide Homomorphismen können im Rahmen einer technischen Merkmalsanalyse für eine Vielzahl von Aufgaben verwendet werden. Eine davon könnte die Bewertung öffentlicher Unternehmensdaten sein.

Die Homomorphismen könnten die Eingangsmerkmale in einen neuen Merkmalsraum umwandeln, der eine bessere Vorhersagekraft für Bewertungsmodelle bietet. Nehmen wir ein Beispiel: Sie haben einen Datensatz mit verschiedenen Finanzkennzahlen wie Umsatz, Gewinn, Vermögen und Verbindlichkeiten für eine Reihe von veröffentlichten Unternehmensdaten.

Ein gängiger Ansatz ist die Verwendung von Monoidhomomorphismen zur Ableitung und Fokussierung auf wichtige Finanzkennzahlen, die in Bewertungsmodellen häufig verwendet werden. Sie können zum Beispiel einen Homomorphismus definieren, der die monoide Umsatzmenge auf eine neue normalisierte monoide Menge abbildet, die die Umsatzwachstumsrate darstellt. Durch diese Umwandlung werden Ihre Datenanforderungen deutlich reduziert, was Ihre Monoide skalierbarer macht, da viele Unternehmen, die unabhängig voneinander im Umsatzmonoidsatz aufgeführt sind, dieselben Werte für die Umsatzwachstumsrate in der Codomäne haben.

In ähnlicher Weise kann man einen Monoidhomomorphismus verwenden, um das Gewinnmonoid auf ein Monoid abzubilden, das den Gewinn pro Aktie (EPS) darstellt. Der Gewinn pro Aktie (EPS) ist eine weit verbreitete Bewertungskennzahl, die die Rentabilität eines Unternehmens auf einer Basis pro Aktie angibt. Und es gibt noch viele andere wichtige Kennzahlen, die sich als nützlich erweisen können und alle auf das gleiche Ziel ausgerichtet sind, nämlich die Skalierbarkeit Ihres monoiden Klassifikationsmodells zu gewährleisten.

Auf der anderen Seite möchten Sie die Abhängigkeit von verteilten Computing-Frameworks wie Apache Hadoop oder Apache Spark minimieren, um die Daten parallel über mehrere Knoten in einem Cluster zu verarbeiten. Diese Ansätze ermöglichen es, die Arbeitslast zu verteilen und die Verarbeitungszeit zu beschleunigen, sodass große Datenmengen verarbeitet werden können, was jedoch mit erheblichen, nachgelagerten Kosten verbunden ist. ‚Nachgelagert‘, weil all die Probleme, die sie zu lösen versuchen, auf der Ebene des Monoid-Designs von vornherein taktvoller hätten behandelt werden können.

7) Verallgemeinerbarkeit:

Sie müssten die Verallgemeinerbarkeit der Klassifizierungsergebnisse anhand neuer und ungesehener Daten bewerten. Die auf Monoiden basierenden Klassifizierungsmethoden sollten eine zuverlässige und konsistente Kategorisierung über verschiedene Datensätze und Kontexte hinweg ermöglichen.

Angenommen, Sie entwickeln eine monoide Klassifikation zur Vorhersage der Kreditwürdigkeit von Kreditantragstellern. Ihr Datensatz würde historische Darlehensdaten enthalten, z. B. Einkommen, Kreditwürdigkeit, Verschuldungsgrad, Beschäftigungsgeschichte usw.

Um die Generalizierbarkeit der endgültigen Klassifizierungsergebnisse zu bewerten, müssten Sie beurteilen, wie gut das Modell bei neuen, ungesehenen Daten abschneidet. Nachdem Sie das Modell beispielsweise auf einem Datensatz aus einer bestimmten Region oder einem bestimmten Zeitraum trainiert haben, testen Sie es auf einem separaten Datensatz einer anderen Region oder eines anderen Zeitraums. Wenn das Modell eine konsistente und zuverlässige Kategorisierungsleistung in diesem und anderen unterschiedlichen Datensätzen zeigt, würde dies auf eine gute Verallgemeinerbarkeit hindeuten.

Beim Erreichen der Verallgemeinerbarkeit von monoiden Klassifizierungsmethoden gibt es potenzielle Fallstricke wie Überanpassung, Datenverzerrung und Verzerrung bei der Merkmalsauswahl. Eine Überanpassung liegt vor, wenn die monoide Operationsfunktion (z. B.) zu spezifisch für die Trainingsdaten wird, was zu einer schlechten Leistung bei neuen Daten führt. Umgekehrt kann es zu einer Verzerrung der Daten kommen, wenn der Trainingsdatensatz nicht repräsentativ für die Grundgesamtheit ist, was zu einer verzerrten Klassifizierung führt. Bei einer Verzerrung der Merkmalsauswahl beeinträchtigt die Auswahl von Merkmalen, die nicht die relevanten Informationen für den gegebenen Kontext erfassen, die Verallgemeinerbarkeit des Modells.

8) Bewertungsmetriken:

Festlegung geeigneter Bewertungsmaßstäbe zur Beurteilung der Qualität und Wirksamkeit der Monoid-Klassifikation. Diese Metriken sollten auf Ihr spezifisches Problem und Ihre Ziele abgestimmt sein, wobei Faktoren wie Genauigkeit, Präzision, Rückruf oder F1-Score zu berücksichtigen sind.

9) Überanpassung und Unteranpassung:

Schutz vor Überanpassung oder Unteranpassung des monoiden Klassifikationsmodells. Wenden Sie Techniken wie Kreuzvalidierung, Regularisierung oder frühzeitiges Abbrechen an, um diese Probleme zu vermeiden und die Modellgeneralisierung zu fördern.

10) Interpretierbarkeit und Erklärbarkeit:

Berücksichtigen Sie die Interpretierbarkeit und Erklärbarkeit der resultierenden Klassifizierungen. Monoide können leistungsstarke Klassifizierungsfähigkeiten bieten, aber es ist wichtig zu verstehen, wie die Klassifizierungsentscheidungen getroffen werden, und in der Lage zu sein, sie auf sinnvolle Weise zu erklären.


Umsetzung

Rückblickzeitraum:

Ein monoider Bereich von 8 ganzen Zahlen, die von 1 bis 8 nummeriert sind, wird verwendet, um die Optionen für die verfügbaren Rückblickzeiträume darzustellen. Ein Zeitraum entspricht 4 (z.B. Balken) und der Zeitrahmen für unsere Tests wird auf eine Stunde festgelegt, auch wenn unsere Analysezeitrahmen variieren, wie im nächsten Abschnitt beschrieben. Für jeden neuen Balken müssen wir einen Zeitraum auswählen. Als Maßeinheit dient jeweils der relative prozentuale Umfang der Bewegung in diesem Zeitraum im Vergleich zu einem gleich langen vorangegangenen Zeitraum. Wenn zum Beispiel die Preisveränderung in Punkten während der Periode 1 (4 Balken) A war und die in der Periode davor B war, dann würde die Gewichtung oder der Wert der Periode 1 durch die folgende Formel gegeben sein:

= ABS(A)/(ABS(A) + ABS(B))

Dabei steht die Funktion ABS() für den absoluten Wert. Diese Formel wird auf eine Teilung durch Null geprüft, indem sichergestellt wird, dass das Minimum des Nenners mindestens einen Punkt in der Größe des betrachteten Wertpapiers beträgt.

Unsere Monoid-Operation und das Identitätselement werden aus der Optimierung auf die folgenden Methoden am Anfang des Artikels ausgewählt.

Zeitrahmen:

Der Monoidsatz für den Zeitrahmen hat 8 Zeitrahmenperioden, nämlich:

  • PERIOD_H1
  • PERIOD_H2
  • PERIOD_H3,
  • PERIOD_H4,
  • PERIOD_H6,
  • PERIOD_H8,
  • PERIOD_H12,
  • PERIOD_D1

Die Gewichtung und Wertzuweisung für jeden dieser Zeitrahmen erfolgt nach dem gleichen Muster wie oben im Rückblickzeitraum, d. h. wir vergleichen die prozentualen Veränderungen des Schlusskurses auf der Grundlage der vorherigen Kursbalkenveränderungen im jeweiligen Zeitrahmen.

Angewandter Preis:

Das Monoid des angewandten Preises hat 4 mögliche angewandte Preise zur Auswahl:

  • MODE_MEDIAN ((Hoch + Tief) / 2)
  • MODE_TYPICAL ((Hoch + Tief + Schlusskurs) / 3)
  • MODE_OPEN
  • MODE_CLOSE

Die Gewichtung und Wertzuweisung wird hier von der oben beschriebenen abweichen. In diesem Fall wird die Standardabweichung jedes angewandten Preises über den im Rückblick ausgewählten Zeitraum verwendet, um die Gewichtung oder den Wert jedes angewandten Preises zu bestimmen. Die Bedienung und die Auswahl des Identitätsindexes sind dieselben wie oben.

Indikator:

Der Monoidsatz für die Indikatorauswahl hat nur zwei Möglichkeiten, nämlich:

  • RSI-Oszillator
  • Bollinger Bands Umschläge

Der RSI-Oszillator ist auf den Bereich von 0 bis 100 normalisiert, aber der Bollinger-Band-Indikator ist nicht nur nicht normalisiert, sondern verfügt über mehrere Datenströme. Um die Bollinger-Bänder zu normalisieren und sie mit dem RSI-Oszillator vergleichbar zu machen, nehmen wir die Differenz zwischen dem aktuellen Kurs und dem Basisband C und teilen sie durch die Größe des Abstands zwischen den oberen und den unteren Bändern, D. Unser Wert für die Bänder sieht also zunächst wie folgt aus:

= C/(ABS(C) + ABS(D))

Wie bisher wird dieser Wert auf eine Teilung durch Null geprüft. Dieser Wert kann jedoch negativ sein und tendiert zum Dezimalwert 1,0. Um diese beiden Aspekte zu normalisieren und wie beim RSI in den Bereich 0 - 100 zu bringen, addieren wir 1,0, um sicherzustellen, dass er immer positiv ist, und multiplizieren dann die Summe mit 50,0, um sicherzustellen, dass er in den Bereich 0 - 100 fällt. Unsere Werte, die nun sowohl für den RSI als auch für die Bollinger-Bänder zwischen 0 und 100 liegen, stellen unsere Gewichtung dar, und die Operatorfunktion und der Elementindex werden wie in den obigen Methoden beschrieben ausgewählt.

Entscheidung:

Für dieses letzte und endgültige Monoid hat unsere Menge ebenfalls nur zwei Möglichkeiten. Das sind:

  • Handel mit dem Trend
  • Handel innerhalb einer Kursspanne

Um diese beiden Elemente zu quantifizieren, betrachten wir über den gewählten Zeitraum in die Vergangenheit die Anzahl der Kurspunkte, die ein Wertpapier am Ende des Zeitraums entgegen seinem endgültigen Trend zurückgeht, als Prozentsatz der gesamten Kursspanne dieses Zeitraums. Dies muss ein Dezimalwert zwischen 0,0 und 1,0 sein. Er misst direkt das Gewicht des Handels mit der Spanne, was bedeutet, dass der Handel mit dem Trend diesen Wert von 1,0 subtrahiert. Die Arbeitsweise und Indexauswahl ist wie oben beschrieben.

So würden wir unsere Monoid-Entscheidungen als Instanz der eingebauten Expert-Trailing-Klasse implementieren

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_8(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<8;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(O==OP_LEAST)
      {
         for(int i=0;i<8;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_MOST)
      {
         for(int i=0;i<8;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_CLOSEST)
      {
         for(int i=0;i<8;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<8;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
      else if(O==OP_FURTHEST)
      {
         for(int i=0;i<8;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<8;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_4(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<4;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            /*printf(__FUNCSIG__+
               " values size: "+IntegerToString(ArraySize(Values))+
               " in indices size: "+IntegerToString(ArraySize(InputIndices))+
               " in indices index: "+IntegerToString(InputIndices[i])
               );*/
               
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(O==OP_LEAST)
      {
         for(int i=0;i<4;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_MOST)
      {
         for(int i=0;i<4;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_CLOSEST)
      {
         for(int i=0;i<4;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<4;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
      else if(O==OP_FURTHEST)
      {
         for(int i=0;i<4;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<4;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_2(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<2;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(m_lookback_operation==OP_LEAST)
      {
         for(int i=0;i<2;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[0]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[0]=i+1; }
            else { OutputIndices[0]=m_lookback.Identity(); }
         }
      }
      else if(m_lookback_operation==OP_MOST)
      {
         for(int i=0;i<2;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[0]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[0]=i+1; }
            else { OutputIndices[0]=m_lookback.Identity(); }
         }
      }
      else if(m_lookback_operation==OP_CLOSEST)
      {
         for(int i=0;i<2;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<2;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[0]=_index;
         }
      }
      else if(m_lookback_operation==OP_FURTHEST)
      {
         for(int i=0;i<2;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<2;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[0]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CTrailingCT::GetLookback()
   {
      m_close.Refresh(-1);
      
      int _x=StartIndex();
      
      for(int i=0;i<8;i++)
      {
         int _period=(__LOOKBACKS[i]*PeriodSeconds(PERIOD_H4))/PeriodSeconds(m_period);
         double _value=fabs(m_close.GetData(_x)-m_close.GetData(_x+_period))/(fabs(m_close.GetData(_x)-m_close.GetData(_x+_period))+fabs(m_close.GetData(_x+_period)-m_close.GetData(_x+_period+_period)));
         
         m_element.Let();
         m_element.Cardinality(1);
         if(m_element.Set(0,_value))
         {
            ResetLastError();
            if(!m_lookback.Set(i,m_element,true))
            {
               printf(__FUNCSIG__+" Failed to assign element at index: "+IntegerToString(i)+", for lookback. ERR: "+IntegerToString(GetLastError()));
            }
         }
      }
      
      //r of 8
      double _v1[8];ArrayInitialize(_v1,0.0);
      int _i1_in[8];for(int i=0;i<8;i++){ _i1_in[i]=i; }
      int _i1_out[4];ArrayInitialize(_i1_out,-1);
      Operate_8(m_lookback,m_lookback_operation,_v1,_i1_in,_i1_out);
      
      
      //r of 4
      double _v2[8];ArrayInitialize(_v2,0.0);
      int _i2_out[2];ArrayInitialize(_i2_out,-1);
      Operate_4(m_lookback,m_lookback_operation,_v2,_i1_out,_i2_out);
      
      
      //r of 2
      double _v3[8];ArrayInitialize(_v3,0.0);
      int _i3_out[1];ArrayInitialize(_i3_out,-1);
      Operate_2(m_lookback,m_lookback_operation,_v2,_i2_out,_i3_out);
      
      return(4*__LOOKBACKS[_i3_out[0]]);
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CTrailingCT::GetTimeframe(void)
   {
      for(int i=0;i<8;i++)
      {
         ResetLastError();
         double _value=0.0;
         double _buffer[];ArrayResize(_buffer,3);ArrayInitialize(_buffer,0.0);ArraySetAsSeries(_buffer,true);
         if(CopyClose(m_symbol.Name(),__TIMEFRAMES[i],0,3,_buffer)>=3)
         {
            _value=fabs(_buffer[0]-_buffer[1])/(fabs(_buffer[0]-_buffer[1])+fabs(_buffer[1]-_buffer[2]));
         }
         else{ printf(__FUNCSIG__+" Failed to copy: "+EnumToString(__TIMEFRAMES[i])+" close prices. err: "+IntegerToString(GetLastError())); }
         
         m_element.Let();
         m_element.Cardinality(1);
         if(m_element.Set(0,_value))
         {
            ResetLastError();
            if(!m_timeframe.Set(i,m_element,true))
            {
               printf(__FUNCSIG__+" Failed to assign element at index: "+IntegerToString(i)+", for lookback. ERR: "+IntegerToString(GetLastError()));
            }
         }
      }
      
      //r of 8
      double _v1[8];ArrayInitialize(_v1,0.0);
      int _i1_in[8];for(int i=0;i<8;i++){ _i1_in[i]=i; }
      int _i1_out[4];ArrayInitialize(_i1_out,-1);
      Operate_8(m_timeframe,m_timeframe_operation,_v1,_i1_in,_i1_out);
      
      
      //r of 4
      double _v2[8];ArrayInitialize(_v2,0.0);
      int _i2_out[2];ArrayInitialize(_i2_out,-1);
      Operate_4(m_timeframe,m_timeframe_operation,_v2,_i1_out,_i2_out);
      
      
      //r of 2
      double _v3[8];ArrayInitialize(_v3,0.0);
      int _i3_out[1];ArrayInitialize(_i3_out,-1);
      Operate_2(m_timeframe,m_timeframe_operation,_v2,_i2_out,_i3_out);
      
      return(__TIMEFRAMES[_i3_out[0]]);
   }


Die Funktion „Operate_8“ ordnet also 8 Elemente der monoiden Menge einander zu und liefert eine Auswahl von 4 Elementen, eines aus jedem Paar. In ähnlicher Weise paart die Funktion 'Operate_4' die 4 Elemente aus 'Operate_8' und ergibt eine Auswahl von 2, wiederum eines aus jedem Paar, und schließlich paart die Funktion 'Operate_2' diese beiden Elemente aus 'Operate_4', um das gewinnende Element zu erhalten.


Wenn wir mit diesem System Tests zur Bestimmung des idealen Trailing-Stops für offene Positionen als Teil eines Expert Advisors durchführen, der das eingebaute RSI-Signal aus der Klasse der Expertensignale und das feste Geldmanagement aus der Klasse der Expertengelder verwendet, erhalten wir den folgenden Bericht.

r_1

Zur Kontrolle wurde ein ähnlicher Lauf mit einem sehr ähnlichen Expert Advisor durchgeführt, dessen einziger Unterschied zu unserem System das Trailing-System ist, d.h. der eingebaute Trailing-Stop, der auf dem gleitenden Durchschnitt basiert, und der den folgenden Bericht liefert.

r_2


Schlussfolgerung

Wir haben Monoide als Mittel zur Datenklassifizierung und damit als Entscheidungsblock betrachtet. Es wurde darauf hingewiesen, wie wichtig es ist, gut geformte Monoide zu haben, die verallgemeinerbar und nicht kurvenangepasst sind, sowie andere Vorsichtsmaßnahmen, die für die Realisierung eines abgerundeten Systems entscheidend sein könnten. Wir haben uns auch eine mögliche Implementierung dieses Systems angesehen, das Stop-Loss-Positionen anpasst, indem es als Instanz der eingebauten Expert-Trailing-Klasse arbeitet.


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

Beigefügte Dateien |
ct_8.mqh (64.34 KB)
TrailingCT8.mqh (35.04 KB)
Entwicklung eines Replay-Systems — Marktsimulation (Teil 01): Erste Versuche (I) Entwicklung eines Replay-Systems — Marktsimulation (Teil 01): Erste Versuche (I)
Wie wäre es, ein System zu schaffen, das es uns ermöglicht, den Markt zu studieren, wenn er geschlossen ist, oder sogar Marktsituationen zu simulieren? Wir beginnen hier eine neue Artikelserie, in der wir uns mit diesem Thema beschäftigen werden.
Automatisierter Raster-Handel mit Stop-Pending-Aufträge an der Moscow Exchange (MOEX) Automatisierter Raster-Handel mit Stop-Pending-Aufträge an der Moscow Exchange (MOEX)
Der Artikel befasst sich mit dem Ansatz des Raster-Handels (Grid-Trading), der auf Stop-Pending-Aufträge basiert und in einem MQL5 Expert Advisor an der Moscow Exchange (MOEX) implementiert wurde. Eine der einfachsten Strategien beim Handel am Markt ist eine Reihe von Aufträgen, die darauf abzielen, den Marktpreis zu „fangen“.
Entwicklung eines Replay-Systems — Marktsimulation (Teil 02): Erste Versuche (II) Entwicklung eines Replay-Systems — Marktsimulation (Teil 02): Erste Versuche (II)
Diesmal wollen wir einen anderen Ansatz wählen, um das 1-Minuten-Ziel zu erreichen. Diese Aufgabe ist jedoch nicht so einfach, wie man vielleicht denkt.
Integration von ML-Modellen mit dem Strategy Tester (Teil 3): Verwaltung von CSV-Dateien (II) Integration von ML-Modellen mit dem Strategy Tester (Teil 3): Verwaltung von CSV-Dateien (II)
Dieses Material bietet eine vollständige Anleitung zur Erstellung einer Klasse in MQL5 für die effiziente Verwaltung von CSV-Dateien. Wir werden die Implementierung von Methoden zum Öffnen, Schreiben, Lesen und Umwandeln von Daten sehen. Wir werden auch überlegen, wie wir sie zum Speichern und Abrufen von Informationen nutzen können. Darüber hinaus werden wir die Grenzen und die wichtigsten Aspekte bei der Verwendung einer solchen Klasse erörtern. Dieser Artikel kann eine wertvolle Ressource für diejenigen sein, die lernen wollen, wie man CSV-Dateien in MQL5 verarbeitet.