English 日本語
preview
Scheinkorrelationen in Python

Scheinkorrelationen in Python

MetaTrader 5Statistik und Analyse | 25 Juni 2024, 10:44
60 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Zusammenfassung

Bevor man in den Bereich des algorithmischen Handels mit maschinellem Lernen eintaucht, muss man sich vergewissern, ob eine sinnvolle Beziehung zwischen den Modelleingaben und der Variablen besteht, die man vorhersagen möchte. Dieser Artikel veranschaulicht den Nutzen der Anwendung von Einheitswurzeltests auf Modellresiduen, um das Vorhandensein einer solchen Beziehung in unseren Datensätzen zu überprüfen.

Leider ist es möglich, Modelle auf der Grundlage von Datensätzen zu konstruieren, die keinen echten Zusammenhang aufweisen. Bedauerlicherweise können diese Modelle beeindruckend niedrige Fehlermetriken liefern, was ein falsches Gefühl der Kontrolle und übermäßig optimistische Aussichten verstärkt. Diese fehlerhaften Modelle werden gemeinhin als „Scheinkorrelationen“ bezeichnet.

In diesem Artikel wird zunächst ein intuitives Verständnis von Scheinregressionen entwickelt. Anschließend werden wir synthetische Zeitreihendaten erzeugen, um eine Scheinregressionen zu simulieren und ihre charakteristischen Auswirkungen zu beobachten. Anschließend werden wir uns mit Methoden zur Identifizierung von Fehlregressionen befassen und unsere Erkenntnisse zur Validierung eines in Python erstellten maschinellen Lernmodells nutzen. Wenn unser Modell validiert ist, werden wir es in ONNX exportieren und eine Handelsstrategie in MQL5 implementieren.


Einleitung: Scheinkorrelationen kommen immer wieder vor

In der Mitte des 19. Jahrhunderts lebte Ignaz Semmelweis als praktizierender Arzt in Wien. Er war zutiefst frustriert über die Statistiken, die er in dem Krankenhaus, in dem er praktizierte, beobachtete.

Ignaz Semmelweis

Abb. 1: Ignaz Semmelweis


Das Problem war, dass ⅕ der gesunden Frauen, die im Krankenhaus entbanden, an Fieber starben, das sie sich während der Geburtswehen zuzogen. Ignaz war entschlossen zu verstehen, warum. Die meisten Ärzte führten dies auf die „schlechte Luft“ zurück, von der sie glaubten, dass böse Geister diese Probleme verursachten. So komisch dies heute auch klingen mag, so sehr wurde es seinerzeit akzeptiert. Aber das hat Ignaz nicht befriedigt. Im Laufe der Zeit beobachtete Ignaz eines Tages, dass Ärzte und Medizinstudenten, die in einer Leichenhalle auf der einen Seite des Krankenhauses Autopsien durchführten, zur Entbindung auf die andere Seite des Krankenhauses liefen, ohne sich zwischendurch die Hände zu waschen. Nachdem er das Personal seines örtlichen Krankenhauses davon überzeugt hatte, Handhygiene zu betreiben, sank die Müttersterblichkeitsrate von 20 % auf 1 %.

Leider blieben die Entdeckungen von Ignaz unbemerkt. Alle Anstrengungen, die er unternahm, um seine Erkenntnisse mit anderen Ärzten und medizinischen Einrichtungen zu teilen, führten nur dazu, dass er sich weiter von der damaligen medizinischen Gemeinschaft und ihrem festen Glauben an „schlechte Luft“ entfernte. Ignaz Semmelweis starb im Alter von 46 Jahren als sozial Ausgestoßener in einer Nervenheilanstalt. Was können wir von den Ärzten lernen, die Semmelweis' weise Worte ignorierten, und warum war es so schwer für sie, ihren Fehler einzusehen?

Das Problem besteht darin, dass es möglich ist, ein Modell aus Daten zu erstellen, die in keinerlei Beziehung zueinander stehen. Außerdem kann dieses Modell zufällig niedrige Fehlermetriken erzeugen und fälschlicherweise Beziehungen nachweisen, die gar nicht existieren. Solche Modelle werden als Scheinkorrelationen bezeichnet.

Eine Scheinkorrelation ist ein Modell, das fälschlicherweise eine Beziehung nachweist, die nicht existiert. Die Ärzte hätten sich ja auch sagen können: „Heute sind zu viele böse Geister in der Luft, deshalb werden morgen mehr Mütter sterben.“ Wie vorhergesagt, starben am nächsten Tag mehr Frauen, aber der Arzt hatte aus den falschen Gründen Recht. Bei der Erstellung von Modellen für maschinelles Lernen können unsere Modelle auch aus den falschen Gründen richtig sein.

Wenn Sie sich ausdrücklich bewusst sind, dass es eine Beziehung zwischen Ihren Eingabe- und Ausgabedaten gibt, dann haben Sie keinen Grund zur Sorge. Doch was können Sie tun, wenn Sie unsicher sind? Oder haben Sie es nie überprüft und sind einfach davon ausgegangen, dass es eine Beziehung geben muss?

Die beste bekannte Lösung ist die Durchführung spezieller Tests für die Residuen Ihres Modells. Diese Tests werden als Einheitswurzeltests bezeichnet. Wir werden in dieser Diskussion nicht versuchen, Einheitswurzeln zu definieren, das ist eine andere Diskussion für sich. Um unsere Ziele zu erreichen, genügt es jedoch zu wissen, dass unsere Regression nur scheinbar existiert, wenn wir für unsere Residuen Einheitswurzeln finden können.

Es gibt nur eine wesentliche Einschränkung bei der Einheitswurzellösung, die wir jetzt besprechen werden: Es kann sein, dass wir keine Einheitswurzeln finden, obwohl es sie gibt, Fehler der Klasse 1. Oder wir finden fälschlicherweise Einheitswurzeln, die gar nicht existieren, ein Fehler der Klasse 2.

Es gibt viele Tests, mit denen wir überprüfen können, ob unsere Residuen Einheitswurzeln haben, wie z.B. der Augmented Dickey Fuller Test und der Kwiatkowski-Phillips-Schmidt-Shin Test. Jeder Test hat seine Stärken und Schwächen und wird unter verschiedenen Bedingungen versagen. Um Scheinregressionen in Aktion zu sehen, werden wir unsere eigenen Zeitreihendaten erzeugen. Wir werden zwei Zeitreihendatensätze erstellen, die in keiner Beziehung zueinander stehen, und beobachten, was passiert, wenn wir ein Modell mit diesen beiden unabhängigen Datensätzen trainieren.


Simulation von Scheinregressionen

Scheinregressionen können aus einer Vielzahl von Gründen auftreten, der häufigste Grund ist jedoch die Modellierung zweier unabhängiger und nicht stationärer Zeitreihen. Lassen Sie uns diese technische Definition auspacken. Eine Zeitreihe ist einfach eine gleichmäßig aufgezeichnete Beobachtung einer Zufallsvariablen. Wenn wir sagen, dass eine Zeitreihe stationär ist, bleiben ihre statistischen Eigenschaften wie Mittelwert, Varianz und Autokorrelationsstruktur im Laufe der Zeit relativ konstant.  Eine Zeitreihe ist nichtstationär, wenn ihre statistischen Eigenschaften im Laufe der Zeit schwanken.

In unserer Diskussion werden wir einen praktischen Ansatz verfolgen, indem wir unsere eigenen Daten simulieren, um die Grundwahrheit bei jedem Schritt zu verstehen. Auf diese Weise können wir die Auswirkungen aus erster Hand beobachten. Wir beginnen mit dem Import der erforderlichen Pakete

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller , kpss
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

Als Nächstes werden wir statistische Eigenschaften für unsere beiden Datensätze definieren, von denen einer unsere Eingabedaten und der andere unsere Ausgabedaten spiegelt. Beide Datensätze enthalten zufällig und normal verteilte Zahlen.

size = 1000
mu , sigma = 0 , 1
mu_y , sigma_y = 2 , 4

Der folgende Code erzeugt zwei Zeitreihen, x_non_stationary und y_non_stationary, die Random Walks darstellen. Die Zufälligkeit wird durch die Normalverteilung eingeführt, und die kumulative Summe sorgt dafür, dass jeder Wert in der Reihe von den vorherigen Werten abhängt, wodurch ein dynamisches, nicht-stationäres Verhalten entsteht.

Die beiden Datensätze x_non_stationary und y_non_stationary werden mit der Funktion numpy.random.normal zufällig erzeugt. Es besteht also kein Zusammenhang zwischen den beiden Datensätzen.

steps = np.random.normal(mu,sigma,size)
steps_y = np.random.normal(mu_y,sigma_y,size)
x_non_stationary =  pd.DataFrame(100 + np.cumsum(steps),index= np.arange(0,1000))
x_non_stationary_lagged = x_non_stationary.shift(1)
x_non_stationary_lagged.dropna(axis=0,inplace=True)
y_non_stationary =   pd.DataFrame(100 + np.cumsum(steps_y),index= np.arange(0,1000))

Schauen wir uns unsere beiden Zufallsdatensätze an.

plt.plot(x_non_stationary)

Random walk x

Abb. 2: Random walk x




Stellen wir unsere nicht-stationäre y-Zeitreihe dar.

plt.plot(y_non_stationary)

Random walk y

Abb. 3: Random Walk y.

Betrachten wir nun die Ergebnisse der Regression der beiden unabhängigen und nicht-stationären Zeitreihen.

ols = sm.OLS(y_non_stationary,x_non_stationary)
lm = ols.fit()
print(lm.summary())

x_y nicht-stationäre Regression

Abb. 4:  Die zusammenfassenden Ergebnisse der Regression von zwei unabhängigen nicht-stationären Variablen.

In vielen Zeitreihen gibt es eine zufällige Trendkomponente, die als stochastischer Trend bezeichnet wird. Selbst wenn zwei Zeitreihen unabhängig sind, können ihre stochastischen Trends kurze, lokale Korrelationen aufweisen. Leider können diese momentanen Korrelationen unsere Modelle manchmal dazu verleiten, fälschlicherweise auf eine Beziehung zwischen zwei unabhängigen Zeitreihen zu schließen. Scheinregressionen, die in solchen Fällen auftreten, ergeben oft hohe R-Quadrat-Werte. Es ist wichtig, sich daran zu erinnern, dass das R-Quadrat den Anteil der Varianz in der Antwort misst, der durch die Varianz in den unabhängigen Variablen erklärt wird. Wenn also zwei Variablen korrelierte stochastische Trends aufweisen, kann die Zuverlässigkeit der R-Quadrat-Metrik beeinträchtigt sein. Das Problem der Scheinregressionen ist im Zusammenhang mit Zeitreihendaten besonders relevant und verdient eine sorgfältige Prüfung.

Unser Modell hat eine bereinigte R-Quadrat-Metrik, die fast 1 erreicht, was bedeutet, dass das Modell die Anpassung als nahezu perfekt ansieht. Das Modell geht davon aus, dass etwa 90 % der Variation in der Antwortvariable durch die Varianz in den Prädiktoren erklärt werden kann. Diese Demonstration dient jedoch als wichtige Erinnerung an die Fallstricke, die mit Scheinregressionen verbunden sind. Wir wissen, dass es keine Beziehung zwischen Inputs und Outputs gibt, sie sind beide zufällig und haben nichts gemeinsam.

Die Herausforderungen bleiben bestehen, da der P-Wert signifikant zu sein scheint und 0 in unseren Konfidenzintervallen auffällig nicht vorkommt. Auch wenn dies in der Regel als Zeichen für eine hervorragende Passform gedeutet werden könnte, ist Vorsicht geboten. Das Modell deutet in diesem Fall fälschlicherweise auf eine starke Beziehung hin, die in Wirklichkeit nicht besteht. Wir haben die Eingabe- und Ausgabedaten selbst erstellt, daher wissen wir, dass es keinen Zusammenhang gibt.


Verzögerung des Prädiktors

Ein verräterisches Zeichen für eine Scheinregressionen tritt auf, wenn wir eine verzögerte Version des Prädiktors einbeziehen. In solchen Fällen wird der einst signifikante Koeffizient plötzlich unbedeutend, wie wir gleich sehen werden. Dieses Phänomen dient als entscheidender Indikator, der uns von falschen Schlussfolgerungen ablenkt und deutlich macht, wie wichtig es ist, Scheinregressionen in der Zeitreihenanalyse zu verstehen und zu bekämpfen.

Wir wiederholen das oben beschriebene Verfahren, wobei wir dieses Mal auch eine verzögerte Version der Eingabedaten einbeziehen.

ols = sm.OLS(y_non_stationary.iloc[0:998,0],x_matrix.loc[0:998,['current_x','lagged_x']])
lm = ols.fit()
print(lm.summary())

X_y nicht-stationäre Regression

Abb. 5: Zusammenfassende Statistiken aus unserer Scheinregression.

Als weitere Vorsichtsmaßnahme ist zu beachten, dass unser R-Quadrat-Wert unseren Durbin-Watson-Wert übersteigt. Diese Beobachtung kann als zusätzlicher Hinweis auf eine mögliche Scheinregressionen gewertet werden. Die Durbin-Watson-Statistik, die in der Regressionsanalyse verwendet wird, dient dazu, das Vorhandensein von Autokorrelation in den Residuen eines Regressionsmodells festzustellen. Autokorrelation tritt auf, wenn die Residuen in einer Zeitreihe oder einem Regressionsmodell miteinander korrelieren. Im Zusammenhang mit Zeitreihendaten, bei denen die Beobachtungen von früheren Beobachtungen abhängen, ist der Durbin-Watson-Test besonders wichtig. Die Ergebnisse liefern wertvolle Erkenntnisse über das Vorhandensein von Autokorrelation, die uns bei der Interpretation der Leistung des Modells weiterhelfen.


Durbin-Watson-Statistik

  •     Bereich: Die Durbin-Watson-Statistik hat Werte zwischen 0 und 4.
  •     Interpretation: Ein Wert nahe 2 bedeutet, dass keine signifikante Autokorrelation vorliegt. Werte deutlich unter 2 deuten auf eine positive Autokorrelation hin (die Residuen sind positiv korreliert). Werte deutlich über 2 deuten auf eine negative Autokorrelation hin (die Residuen sind negativ korreliert).

Ein hohes R-Quadrat und ein niedriger Durbin-Watson-Wert können zwar den Verdacht auf eine Scheinregression aufkommen lassen, doch ist zu beachten, dass diese Indikatoren allein das Vorhandensein einer Scheinregression nicht schlüssig bestätigen. Im Bereich der Zeitreihenanalyse werden zusätzliche diagnostische Tests und Fachkenntnisse zu wesentlichen Bestandteilen einer gründlichen Beurteilung. 


Einheitswurzel-Tests

Die zuverlässigste Methode zur Identifizierung einer Scheinregression ist die Analyse der Residuen. Wenn die Residuen unseres Modells nicht stationär sind, ist dies ein deutlicher Hinweis darauf, dass es tatsächlich eine Scheinregression ist. Die Feststellung, ob eine bestimmte Zeitreihe stationär ist, ist jedoch keine einfache Aufgabe. In unserem Fall kann der Augmented Dickey Fuller Test die Nullhypothese nicht zurückweisen, wenn man weiß, dass es eine Scheinregression ist und die Residuen daher nicht stationär sind. Mit anderen Worten, es kann sein, dass es nicht gelingt, nachzuweisen, dass die Daten nicht stationär sind, obwohl es eine Scheinregression ist, was die Feinheiten und Herausforderungen bei der Identifizierung von Scheinregressionen verdeutlicht. Dies unterstreicht die Bedeutung eines nuancierten Ansatzes, bei dem statistische Tests und Fachwissen kombiniert werden, um die Feinheiten der Zeitreihenanalyse effektiv zu bewältigen.

Wir werden nun sklearn verwenden, um ein Modell auf unsere Trainingsmenge anzuwenden.

lm = LinearRegression()
lm.fit(x[train_start:train_end],y[train_start:train_end])

Anschließend berechnen wir die Residuen.

residuals = y[test_start:test_end] - lm.predict(x[test_start:test_end])

Stellen wir die Residuen dar.

residuals.plot()

Darstellung der Residuen

Abb. 6: Die Residuen unseres mit Scikit-Learn erstellten Regressionsmodells.


Nun werden wir unsere Residuen dem Augmented Dickey-Fuller Test unterziehen, um ihre Stationarität zu bestimmen.


Erweiterter Dickey-Fuller-Test

Der Augmented Dickey-Fuller-Test (ADF) dient als zentrales statistisches Instrument zur Bewertung der Stationarität von Zeitreihen oder zur Identifizierung einer Einheitswurzel, die auf Nicht-Stationarität hinweist. Im Bereich der Zeitreihenanalyse nimmt die Stationarität eine herausragende Rolle ein, da sie die Konstanz statistischer Attribute wie Mittelwert und Varianz über die Zeit bedeutet. Das Vorhandensein einer Einheitswurzel in einer Zeitreihe, die Nicht-Stationarität bedeutet, lässt den begründeten Verdacht aufkommen, dass die Beobachtungen innerhalb der Zeitreihe stochastischer oder zufälliger Natur sein könnten. Der ADF-Test bietet daher eine robuste Methodik zur Untersuchung des zeitlichen Verhaltens eines Datensatzes und trägt zu einem differenzierten Verständnis seiner inhärenten Merkmale und potenziellen Auswirkungen auf nachfolgende Analysen bei.

  1. Nullhypothese: Die Nullhypothese des ADF-Tests besagt, dass die Zeitreihe eine Einheitswurzel besitzt, was auf Nicht-Stationarität hinweist.
  2. Alternative Hypothese: Die Alternativhypothese des Augmented Dickey-Fuller (ADF)-Tests lautet, dass die Zeitreihe keine Einheitswurzel hat und stationär ist.
  3. Die Entscheidungsregel: Die Entscheidungsregel beim ADF-Test beinhaltet den Vergleich der Teststatistik mit kritischen Werten. Wenn die Teststatistik kleiner als der kritische Wert ist, wird die Nullhypothese (Vorhandensein einer Einheitswurzel) abgelehnt, was auf Stationarität hindeutet. Ist die Teststatistik hingegen größer als der kritische Wert, so reicht die Evidenz nicht aus, um die Nullhypothese zurückzuweisen, was auf Nicht-Stationarität schließen lässt.

Da wir den Augmented Dickey-Fuller (ADF)-Test für die Residuen unseres Modells durchführen, werden die daraus resultierenden Ergebnisse als entscheidende Determinante für die Feststellung des Vorhandenseins stationärer oder nicht-stationärer Merkmale in den Residuen dienen. Der ADF-Test ist für die Validierung unserer Regressionsergebnisse von großer Bedeutung und spielt eine zentrale Rolle bei der Gewährleistung der Zuverlässigkeit unseres Analyserahmens. Indem er die Stationaritätseigenschaften der Residuen aufklärt, trägt dieser Test wesentlich zur Verbesserung der interpretativen Robustheit unserer Zeitreihenanalyse bei.

adfuller(residuals)

(-12.753804093890963, 8.423533501802878e-24, 2, 497, {'1%': -3.4435761493506294, '5%': -2.867372960189225, '10%': -2.5698767442886696}, 1366.9343966932422)

Unser Hauptaugenmerk liegt auf dem aus diesem Experiment abgeleiteten p-Wert, insbesondere auf dem zweiten Wert in der angegebenen Liste, der 8,423533501802878e-24 beträgt. Bemerkenswert ist, dass dieser p-Wert gegen Null geht und jeden vernünftigen kritischen Wert deutlich übertrifft. Im Zusammenhang mit dem Augmented Dickey-Fuller (ADF)-Test wird die Ablehnung der Nullhypothese relevant, wenn die ADF-Statistik kleiner als der kritische Wert ist, was auf das Vorhandensein von Stationarität hinweist.

Es ist unbedingt anzuerkennen, dass der ADF-Test, wie jeder statistische Test, mit inhärenten Einschränkungen und Annahmen verbunden ist. Es gibt verschiedene Faktoren, die dazu beitragen können, dass der ADF-Test die Nullhypothese nicht akzeptiert und somit das Vorhandensein einer Einheitswurzel abgelehnt wird, selbst wenn die zugrunde liegenden Daten nicht stationär sind. Das Verständnis dieser Nuancen ist entscheidend für eine umfassende Interpretation der Testergebnisse.

  1. Kleine Stichprobengröße: Die Leistung des ADF-Tests kann durch kleine Stichprobengrößen beeinträchtigt werden. In solchen Fällen könnte der Test nicht genügend aussagekräftig sein, um Nicht-Stationarität aufzudecken.
  2. Schlechte Verzögerungsreihenfolge: Die Wahl der Verzögerungsreihenfolge beim ADF-Test ist entscheidend. Wenn die Reihenfolge der Verzögerungen nicht korrekt angegeben ist, kann dies zu ungenauen Ergebnissen führen. Die Verwendung von zu wenigen oder zu vielen Verzögerungen kann die Fähigkeit des Tests beeinträchtigen, die zugrunde liegende Struktur der Daten zu erfassen.
  3. Vorhandensein von deterministischen Trends: Wenn die Daten deterministische Trends enthalten (z. B. lineare Trends, quadratische Trends), die im Testmodell nicht berücksichtigt werden, kann der ADF-Test die Nullhypothese möglicherweise nicht zurückweisen. In solchen Fällen können Vorverarbeitungsschritte wie Detrending erforderlich sein.
  4. Unzureichende Differenzierung: Wenn die im ADF-Test verwendete Differenzierungsreihenfolge nicht ausreicht, um die Daten stationär zu machen, kann der Test die Nullhypothese nicht zurückweisen.


Kwiatkowski-Phillips-Schmidt-Shin

Der Kwiatkowski-Phillips-Schmidt-Shin-Test (KPSS) ist eine brauchbare Alternative zum Augmented Dickey-Fuller (ADF)-Test zur Bewertung der Stationarität von Zeitreihendaten. Obwohl beide Tests in der Zeitreihenanalyse weit verbreitet sind, unterscheiden sie sich in ihren Null- und Alternativhypothesen sowie den zugrunde liegenden Modellen. Die Wahl zwischen dem ADF- und dem KPSS-Test hängt von den spezifischen Merkmalen der untersuchten Zeitreihen und der übergeordneten Forschungsfrage ab. Die gleichzeitige Anwendung beider Tests ermöglicht häufig eine umfassendere Analyse der Stationarität und bietet den Forschern ein differenziertes Verständnis der Zeitreihendynamik.

  1. Nullhypothese: Die Nullhypothese des KPSS-Tests lautet, dass die Zeitreihe trend-stationär ist. Trend-Stationarität bedeutet, dass die Reihe eine Einheitswurzel aufweist, was auf das Vorhandensein eines deterministischen Trends hindeutet.
  2. Alternative Hypothese: Die Alternativhypothese des KPSS-Tests lautet, dass die Zeitreihe nicht trendstabil ist, was bedeutet, dass sie differenzstabil oder stationär um einen stochastischen Trend ist.
  3. Die Entscheidungsregel: Die Entscheidungsregel für den KPSS-Test beinhaltet den Vergleich der Teststatistik mit kritischen Werten auf einem gewählten Signifikanzniveau (z.B. 1%, 5% oder 10%). Wenn die Teststatistik größer als der kritische Wert ist, wird die Nullhypothese abgelehnt, was darauf hindeutet, dass die Zeitreihe nicht trendstabil ist. Ist die Teststatistik hingegen kleiner als der kritische Wert, kann die Nullhypothese nicht verworfen werden, was auf Trendstabilität schließen lässt.

Im Falle des KPSS-Tests wird üblicherweise ein Signifikanzniveau von 0,05 zugrunde gelegt. Wenn die KPSS-Statistik unter diesen Schwellenwert fällt, deutet dies auf Nicht-Stationarität in den Daten hin. In unserer Analyse ergab die KPSS-Statistik einen Wert von 0,016, was die Abweichung von der kritischen Schwelle bestätigt und auf eine Tendenz zur Nicht-Stationarität des Datensatzes hinweist. Dieses Ergebnis unterstreicht, wie wichtig es ist, mehrere Diagnoseinstrumente, wie den ADF- und den KPSS-Test, in Betracht zu ziehen, um eine gründliche und genaue Bewertung der Zeitreihenmerkmale zu gewährleisten.

kpss(residuals)

(0.6709994557854182, 0.016181867655871072, 1, {'10%': 0.347, '5%': 0.463, '2.5%': 0.574, '1%': 0.739})

Der KPSS-Test kann unter bestimmten Umständen die Nullhypothese (H0) fälschlicherweise zurückweisen, was zu einem Fehler vom Typ I führt. Ein Fehler vom Typ I tritt auf, wenn der Test fälschlicherweise zu dem Schluss kommt, dass die Zeitreihe nicht trend-stationär ist, obwohl sie es in Wirklichkeit ist.

Im Folgenden sind einige Situationen aufgeführt, in denen der KPSS-Test die Nullhypothese fälschlicherweise zurückweisen könnte:

  1. Saisonale Muster: Der KPSS-Test reagiert sowohl auf Trends als auch auf Saisonalität. Weist eine Zeitreihe ein starkes saisonales Muster auf, kann der Test sie als nicht-stationären Trend interpretieren. In solchen Fällen könnte eine Differenzierung erforderlich sein, um die Saisonalität zu berücksichtigen.
  2. Strukturelle Brüche: Wenn es strukturelle Brüche in den Zeitreihen gibt, wie z. B. plötzliche und signifikante Veränderungen im zugrunde liegenden datenerzeugenden Prozess, kann der KPSS-Test diese als nichtstationäre Trends erkennen. Strukturelle Brüche können zur Ablehnung der Nullhypothese führen.
  3. Ausreißer: Das Vorhandensein von Ausreißern in den Daten kann die Leistung des KPSS-Tests beeinflussen. Ausreißer könnten als Trendabweichungen wahrgenommen werden, was zu einer Ablehnung der Trend-Stationarität führt. Die Robustheit gegenüber Ausreißern ist ein wichtiger Aspekt bei der Interpretation der Ergebnisse des KPSS-Tests.
  4. Nichtlineare Trends: Der KPSS-Test setzt einen linearen Trend voraus. Wenn der zugrunde liegende Trend in der Zeitreihe nichtlinear ist, kann der Test irreführende Ergebnisse liefern. Nichtlineare Trends werden durch den Test möglicherweise nicht angemessen erfasst, was zu einer falschen Ablehnung der Stationarität führt.

Es ist von entscheidender Bedeutung, die Ergebnisse des KPSS-Tests mit Vorsicht zu interpretieren und die spezifischen Merkmale der analysierten Zeitreihen zu berücksichtigen. Darüber hinaus kann die Kombination des KPSS-Tests mit anderen Stationaritätstests, wie dem Augmented Dickey-Fuller (ADF)-Test, eine umfassendere Bewertung der Stationaritätseigenschaften der Zeitreihen liefern.


Alles zusammenfügen

Nachdem wir eine Vertrauensbasis geschaffen haben, ist es nun an der Zeit, unseren Schwerpunkt von synthetischen Kontrolldaten auf die Analyse authentischer Marktdaten zu verlagern, die direkt von unserem MetaTrader 5 Terminal stammen. Um diesen Übergang zu erleichtern, schlagen wir die Entwicklung eines MetaQuotes Language 5 (MQL5) Skripts vor. Dieses Skript wird speziell für den Abruf von Daten aus unserem Handelsterminal, die Formatierung und den Export in das CSV-Format entwickelt.

Zu Beginn unseres Skripts werden zunächst globale Variablen deklariert, wobei der erste Satz für die Handles unserer technischen Indikatoren bestimmt ist. Diese Variablen spielen eine zentrale Rolle bei der effizienten Verwaltung und dem Zugriff auf die relevanten Indikatoren während der Skriptausführung und tragen zur allgemeinen Kohärenz und Organisation unseres MetaQuotes Language 5 (MQL5) Programms bei.

//---Our handlers for our indicators
int ma_handle;
int rsi_handle;
int cci_handle;
int ao_handle;

In der Folge benötigen wir Datenstrukturen, die sorgfältig entworfen wurden, um die Messwerte unserer technischen Indikatoren aufzunehmen und zu organisieren. Diese Datenstrukturen werden während der gesamten Skriptausführung verwendet. 

//---Data structures to store the readings from our indicators
double ma_reading[];
double rsi_reading[];
double cci_reading[];
double ao_reading[];

Anschließend erstellen wir natürlich einen Namen für die Datei.

//---File name
string file_name = "Market Data.csv";

Nun wird festgelegt, wie viele Daten abgerufen werden sollen.

//---Amount of data requested
int size = 3000;

Zu Beginn der Entwicklung unseres OnStart-Event-Handlers beinhaltet der erste Aufruf zur Bestellung die Initialisierung unserer designierten technischen Indikatoren.

//---Setup our technical indicators
ma_handle = iMA(_Symbol,PERIOD_CURRENT,20,0,MODE_EMA,PRICE_CLOSE);
rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,60,PRICE_CLOSE);
cci_handle = iCCI(_Symbol,PERIOD_CURRENT,10,PRICE_CLOSE);
ao_handle = iAO(_Symbol,PERIOD_CURRENT);

Im weiteren Verlauf der Skriptausführung geht es um die Übertragung von Werten aus unseren Indikator-Handles in die entsprechenden Datenstrukturen. Dieser wesentliche Prozess beinhaltet eine sorgfältige Zuordnung der Indikatorergebnisse zu den zuvor festgelegten Datenstrukturen.

//---Set the values as series
CopyBuffer(ma_handle,0,0,size,ma_reading);
ArraySetAsSeries(ma_reading,true);
CopyBuffer(rsi_handle,0,0,size,rsi_reading);
ArraySetAsSeries(rsi_reading,true);
CopyBuffer(cci_handle,0,0,size,cci_reading);
ArraySetAsSeries(cci_reading,true);
CopyBuffer(ao_handle,0,0,size,ao_reading);
ArraySetAsSeries(ao_reading,true);

Bei der Vorbereitung des Dateischreibvorgangs ist die Einrichtung eines Dateihandlers in unserem Skript eine wichtige Vorstufe.

//---Write to file
int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");

Anschließend beginnt eine entscheidende Phase in unserem Skript, in der wir den Datensatz systematisch durchgehen und den akribischen Prozess des Schreibens von Daten in die von uns vorgesehene CSV-Datei orchestrieren. Dieses iterative Verfahren umfasst eine detaillierte Prüfung und Extraktion jedes einzelnen Datenpunkts, wobei eine sorgfältig abgestimmte Reihenfolge eingehalten wird, die mit den festgelegten Parametern übereinstimmt.

for(int i=-1;i<=size;i++){
      if(i == -1){
            FileWrite(file_handle,"Open","High","Low","Close","MA 20","RSI 60","CCI 10","AO");
      }
      
        else{
                FileWrite(file_handle,iOpen(_Symbol,PERIOD_CURRENT,i),
                                        iHigh(_Symbol,PERIOD_CURRENT,i),
                                        iLow(_Symbol,PERIOD_CURRENT,i),
                                        iClose(_Symbol,PERIOD_CURRENT,i),
                                        ma_reading[i],
                                        rsi_reading[i],
                                        cci_reading[i],
                                        ao_reading[i]);
      } 
}

Nach Abschluss der Skripteinrichtung führen Sie das Skript auf dem von Ihnen gewünschten Symbol aus. Anschließend generiert das Skript eine CSV-Datei, die das unten dargestellte Format widerspiegelt und so eine umfassende und strukturierte Darstellung der mit dem ausgewählten Symbol verbundenen relevanten Daten liefert.

Handelsdaten

Abb. 7: Unsere CSV-Datei mit den Marktdaten

Wir konzentrieren uns nun auf die Analyse authentischer Marktdaten und versuchen, ein Regressionsmodell zu erstellen, das die erwartete Kursentwicklung innerhalb der nächsten 15 Minuten vorhersagt. Das zentrale Kriterium für unser analytisches Vorgehen ist die Validierung der Authentizität des Regressionsmodells. Sobald diese Authentizität hergestellt ist, beabsichtigen wir, das validierte Modell in das ONNX-Format zu exportieren und es anschließend für die Entwicklung eines Expert Advisors zu nutzen.

Zu Beginn dieser Phase geht es zunächst um das Laden der wesentlichen Abhängigkeiten. Unter diesen Abhängigkeiten ist eine bemerkenswerte Ergänzung das Paket „Arch“, das für sein umfassendes Angebot an statistischen Analysewerkzeugen bekannt ist. Durch die Integration von „Arch“ verfügen wir über eine Reihe von unschätzbaren Ressourcen, die wir bei unseren analytischen Bemühungen einsetzen können und die die Tiefe und Raffinesse unseres Ansatzes zur Marktdatenanalyse verbessern.

import pandas as pd
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from arch.unitroot import PhillipsPerron , ADF , KPSS
import onnx
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import DoubleTensorType

Dann lesen wir die csv-Datei ein, die wir mit unserem MQL5-Skript erstellt haben.

csv = pd.read_csv("/enter/your/path/here")

Wir bereiten dann unser Ziel vor, unser Ziel ist der Anstieg des Schlusskurses.

csv["Target"] = csv["Close"] / csv["Close"].shift(-15)
csv.dropna(axis=0,inplace=True)

Wir können unseren Datenrahmen untersuchen.

Datenrahmen

Abb. 8: Einlesen unserer CSV-Datei der Marktdaten mit Pandas

Von dort aus werden wir unser Datenaufteilung für Train und Test vorbereiten:

train = np.arange(0,20000)
test = np.arange(20020,89984)


Wir werden nun eine multiple, lineare Regression mit statsmodels durchführen:

ols = sm.OLS(csv.loc[:,"Target"],csv.loc[:,["Open","High","Low","Close","MA 20","RSI 60","CCI 10","AO"]])
lm = ols.fit()
print(lm.summary())

Marktdaten OLS

Abb. 9: Ergebnisse der Regression aller Variablen zur Vorhersage des Preiswachstums.

Lassen Sie uns gemeinsam auf die Interpretation unserer Modellergebnisse eingehen. Das Konfidenzintervall für den Koeffizienten des Merkmals Eröffnungspreis liegt bei 0, was bedeutet, dass er statistisch nicht signifikant ist. Die Entscheidung, dieses Merkmal aus unserem Modell auszuschließen, beruht daher auf der potenziellen Geringfügigkeit seines Beitrags. Bei näherer Betrachtung zeigt sich, dass sowohl der Relative Strength Index (RSI) als auch der Commodity Channel Index (CCI) Koeffizienten aufweisen, die nahe bei 0 liegen, wobei ihre jeweiligen Konfidenzintervalle eine ähnliche Nähe zu diesem Wert aufweisen. In Anbetracht dessen haben wir uns mit Bedacht für die Eliminierung dieser Merkmale entschieden, da wir davon ausgehen, dass ihr marginaler Beitrag zur Informativität begrenzt sein könnte.

Obwohl unser Modell einen bemerkenswert hohen R-Quadrat-Wert aufweist, was darauf hindeutet, dass ein erheblicher Teil der Varianz durch die einbezogenen Merkmale erklärt wird, zeigt eine gleichzeitige Untersuchung der Durbin-Watson-Statistik (DW) einen niedrigen Wert. Dies ist mit Vorsicht zu genießen, da die niedrige DW-Statistik auf die Möglichkeit einer Restkorrelation hinweist, die die Gültigkeit unseres Modells beeinträchtigen könnte. Wir plädieren daher für eine sorgfältige Analyse der Residuen, insbesondere im Hinblick auf ihre Stationarität. Diese zusätzliche Prüfungsebene ist unerlässlich, um die Robustheit und Zuverlässigkeit unseres Modells bei der Erfassung der zugrunde liegenden Muster in den Daten zu gewährleisten.

Legen wir eine Datenstruktur an, um die Merkmale zu speichern, die wir für wichtig halten.

predictors = ["High","Low","Close","MA 20","AO"]

Zeichnen wir die Residuen auf.

residuals = csv.loc[test[0]:test[-1],"Target"] - lm.predict(csv.loc[test[0]:test[-1],predictors])

Fahren wir mit der Erstellung von Residuenplots fort, einem kritischen Schritt im Prozess der Modellbewertung. 

plt.plot(residuals)

Residuale Marktdaten

Abb. 10: Aufzeichnung der Residuen von Marktdatenprognosen.

Die Analyse der Residuen ist ein wichtiger Schritt, um zu verstehen, wie gut unser Modell zu den Daten passt. Anhand der Residuen lässt sich feststellen, ob unser Modell systematische Fehler aufweist oder ob es beständige Fehler macht. Dies ist wichtig, denn so können wir überprüfen, ob unser Modell bestimmte Grundregeln befolgt, wie z. B. eine konsistente Variation in seinen Vorhersagen und keine Abhängigkeit von früheren Fehlern.

Eine Sache, auf die wir bei der Betrachtung von Residuen achten müssen, ist die so genannte „Einheitswurzel“. Das bedeutet im Grunde, dass die Residuen ein Muster der Veränderung im Laufe der Zeit zeigen, das sich nicht auflöst. Es ist, als hätten wir einen anhaltenden Trend bei unseren Fehlern. Das Auffinden einer Einheitswurzel in den Residuen ist ein großes Problem, da es einige der grundlegenden Annahmen unseres Modells in Frage stellt, z. B. dass jede Vorhersage unabhängig von den anderen ist.

Der Umgang mit Einheitswurzeln ist wichtig, denn wenn wir das nicht tun, kann das unsere Schätzungen über die Güte unseres Modells durcheinander bringen und es schwieriger machen, den Schlussfolgerungen zu vertrauen, die wir daraus ziehen.

Wenn wir uns also mit den Residualwerten befassen, kreuzen wir nicht nur Kästchen an. Wir stellen sicher, dass unser Modell einer Prüfung standhält, und beheben etwaige Probleme, wie z. B. Einheitswurzeln, um unsere Vorhersagen zuverlässiger zu machen.

Es kann helfen, sich das so vorzustellen: Stellen Sie sich die Aufgabe vor, eine Katze zu klassifizieren, ein scheinbar einfaches Unterfangen. In einem idealen Szenario, in dem unser Verständnis einer Katze eine unveränderliche Wahrheit symbolisiert, wäre jede Klassifizierung fehlerfrei. Wenn wir den Fehler eines solchen idealen Modells aufzeichnen, würden wir eine stationäre und gerade Linie erhalten, dargestellt durch y = a, eine Konstante. Diese Linie symbolisiert die Beständigkeit der Wahrheit, dass eine Katze zu jedem Zeitpunkt immer eine Katze ist, und wenn wir sie zu irgendeinem Zeitpunkt als Katze bezeichnen würden, wäre unser Fehler 0.

Wenn jedoch die Leistungsfähigkeit des Klassifizierers abnimmt, kommt es zu Fehlklassifizierungen, die dazu führen, dass die Residuen von diesem Zustand der perfekten Stationarität abweichen. Diese Abweichung entspricht der Divergenz des Klassifikators von der Wahrheit und äußert sich in Form von Schwankungen in den Residuen. Die Nicht-Stationarität tritt an Tagen auf, an denen ein Hund fälschlicherweise als Katze eingestuft wird oder umgekehrt, was die Unsicherheit des Klassifikators bezüglich der definierenden Merkmale einer Katze widerspiegelt.

Um die statistische Strenge zu vertiefen, sollten Sie den Zusammenhang zwischen einer Einheitswurzel in den Residuen und der Nicht-Stationarität in der Zeitreihenanalyse betrachten. Das Vorhandensein einer Einheitswurzel in den Residuen, analog zur Unkenntnis eines Klassifikators, bedeutet Nicht-Stationarität, was zu einer erhöhten Volatilität der Residuen führt. Je größer die Unkenntnis, desto ausgeprägter sind die Schwankungen, ähnlich wie bei der Verwechslung von Hunden mit Katzen und umgekehrt.

Das Verständnis dieser Konzepte ist wichtig, denn es hilft uns bei der Feinabstimmung unseres Modells, um bessere Vorhersagen zu treffen. Auch wenn die Dinge auf den ersten Blick gut aussehen, lohnt es sich, noch einmal nachzuprüfen, ob sich unsere Residuen auch so verhalten, wie sie sollten. Denken Sie daran, dass es hierfür keinen einheitlichen Test gibt, sondern dass es am besten ist, mehrere Methoden anzuwenden und die Gesamttendenzen unserer Residuen zu betrachten.


Phillip-Perron-Test

Der Phillips-Perron-Test ist ein statistischer Test, der in der Ökonometrie und der Zeitreihenanalyse verwendet wird, um das Vorhandensein einer Einheitswurzel in einem Zeitreihendatensatz zu beurteilen. Eine Einheitswurzel bedeutet, dass eine Zeitreihenvariable einen stochastischen Trend aufweist und somit nicht stationär ist. Stationarität ist eine entscheidende Annahme in vielen statistischen Analysen, und nicht-stationäre Zeitreihendaten können zu den Ergebnissen einer Scheinregression führen.

Der Phillips-Perron-Test ist eine Variante des Dickey-Fuller-Tests und wurde von Peter C.B. vorgeschlagen. Phillips und Pierre Perron im Jahr 1988. Wie der Dickey-Fuller-Test dient auch der Phillips-Perron-Test dazu, das Vorhandensein einer Einheitswurzel festzustellen, indem das Verhalten einer Zeitreihenvariablen im Zeitverlauf untersucht wird.

Die Grundidee des Phillips-Perron-Tests besteht darin, die differenzierte Zeitreihenvariable auf ihre verzögerten Werte zu regressieren. Die Teststatistik wird dann verwendet, um zu beurteilen, ob der Koeffizient für die verzögerte Variable signifikant von Null verschieden ist. Wenn der Koeffizient signifikant von Null verschieden ist, spricht dies gegen das Vorhandensein einer Einheitswurzel und impliziert, dass die Zeitreihe stationär ist.

Ein bemerkenswertes Merkmal des Phillips-Perron-Tests ist, dass er bestimmte Formen der seriellen Korrelation und der Heteroskedastizität in den Daten berücksichtigt, was beim ursprünglichen Dickey-Fuller-Test nicht der Fall ist. Dies macht den Phillips-Perron-Test robust gegenüber bestimmten Verstößen gegen Annahmen, die in realen Zeitreihendaten vorkommen können.

  1. Nullhypothese: Die Nullhypothese des Phillips-Perron-Tests lautet, dass die Zeitreihenvariable eine Einheitswurzel enthält, was bedeutet, dass sie nicht stationär ist.
  2. Alternative Hypothese: Die Alternativhypothese lautet, dass die Zeitreihenvariable keine Einheitswurzel enthält, was bedeutet, dass sie stationär ist.
  3. Entscheidungsregel: Liegt die berechnete Teststatistik unter dem kritischen Wert, so ist die Nullhypothese, dass eine Einheitswurzel vorliegt, zurückzuweisen, was darauf hindeutet, dass die Zeitreihe stationär ist.  Wenn die berechnete Teststatistik größer als der kritische Wert ist, kann die Nullhypothese nicht zurückgewiesen werden, was darauf hindeutet, dass die Beweise nicht ausreichen, um auf die Stationarität der Zeitreihe zu schließen.

Wir werden die Bibliothek „Arch“ verwenden, um den Phillip-Perron-Test durchzuführen.

pp = PhillipsPerron(residuals)

Wir können zusammenfassende Ergebnisse erhalten. Zusammenfassende Statistiken des Phillips-Perron-Tests

pp.summary()

Phillips-Perron Test
(Z-tau)

Test Statistic -73.916

P-value 0.000

Lags 62

Trend: Constant
Critical Values: -3.43 (1%), -2.86 (5%), -2.57 (10%)

Null Hypothesis: The process contains a unit root.
Alternative Hypothesis: The process is weakly stationary.

Lassen Sie uns die Ergebnisse gemeinsam interpretieren. Die Teststatistik beträgt -73,916. Dieser Wert gibt an, um wie viele Standardabweichungen der geschätzte Koeffizient vom hypothetischen Wert 1 abweicht (was auf das Vorhandensein einer Einheitswurzel hinweist). In diesem Fall deutet eine sehr große negative Teststatistik stark auf das Vorhandensein einer Einheitswurzel hin, was für die Stationarität der Zeitreihe spricht.

Der mit der Teststatistik verbundene p-Wert beträgt 0,000. Der p-Wert ist ein Maß für die Evidenz gegen die Nullhypothese. Ein p-Wert von 0,000 bedeutet, dass die beobachtete Teststatistik unter der Annahme, dass die Nullhypothese wahr ist, extrem unwahrscheinlich ist. In der Praxis ist dieser extrem kleine p-Wert ein starker Beweis gegen das Vorhandensein einer Einheitswurzel.

Angesichts des sehr niedrigen p-Wertes (0,000) würde man die Nullhypothese bei herkömmlichen Signifikanzniveaus (z. B. 0,05) normalerweise ablehnen. Die Beweise deuten darauf hin, dass die Zeitreihe wahrscheinlich stationär ist, da der p-Wert unter dem gewählten Signifikanzniveau liegt. Zusammenfassend lässt sich sagen, dass auf der Grundlage der vorgelegten Ergebnisse die Nullhypothese einer Einheitswurzel eindeutig zurückgewiesen werden kann, was darauf hindeutet, dass die Zeitreihe wahrscheinlich stationär ist. Wir können unsere Entscheidungen jedoch nicht auf einen Test stützen, sondern wollen die Messungen der zentralen Tendenz verschiedener Tests beobachten.

Erweiterter Dickey-Fuller-Test

Wir werden die Bibliothek „Arch“ verwenden, um den Augmented Dickey-Fuller Test durchzuführen:

adf = ADF(residuals)

Wir können zusammenfassende Statistiken abrufen. Zusammenfassende Statistiken aus dem Augmented Dickey Fuller Test:

adf.summary()
Augmented Dickey-Fuller Results

Test Statistic -31.300
P-value 0.000
Lags 60

Trend: Constant
Critical Values: -3.43 (1%), -2.86 (5%), -2.57 (10%)

Null Hypothesis: The process contains a unit root.
Alternative Hypothesis: The process is weakly stationary.


Lassen Sie uns nun die Ergebnisse interpretieren. Die Teststatistik beträgt -31.300. Wie der Phillips-Perron-Test wird auch die ADF-Teststatistik verwendet, um das Vorhandensein einer Einheitswurzel in der Zeitreihe zu bewerten. In diesem Fall ist der sehr große negative Wert ein starker Beweis gegen die Nullhypothese einer Einheitswurzel, was die Idee unterstützt, dass die Zeitreihe stationär ist.

Der zugehörige p-Wert beträgt 0,000. Ähnlich wie beim Phillips-Perron-Test bedeutet ein p-Wert von 0,000, dass die beobachtete Teststatistik unter der Annahme, dass die Nullhypothese (Vorhandensein einer Einheitswurzel) wahr ist, extrem unwahrscheinlich ist. Der sehr kleine p-Wert ist ein starker Beweis gegen die Nullhypothese.

Angesichts des niedrigen p-Wertes (0,000) würde man die Nullhypothese bei herkömmlichen Signifikanzniveaus normalerweise ablehnen. Die Ergebnisse des Augmented Dickey-Fuller-Tests unterstützen die Schlussfolgerung, dass die Zeitreihe wahrscheinlich stationär ist.

Sowohl der Phillips-Perron-Test als auch der Augmented-Dickey-Fuller-Test haben deutliche Hinweise auf das Vorhandensein einer Einheitswurzel geliefert, was darauf hindeutet, dass die Zeitreihe wahrscheinlich stationär ist. Die Ähnlichkeit der Ergebnisse zwischen diesen beiden Tests ist zu erwarten, da sie beide dazu dienen, die Stationarität von Zeitreihendaten zu beurteilen. Die Wahl zwischen ihnen hängt oft von den spezifischen Merkmalen der Daten und den Annahmen der Tests ab. In Ihrem Fall deuten beide Tests darauf hin, dass die Zeitreihe stationär ist.


Exportieren wir unser Modell in das Open Neural Network Exchange Format

ONNX (Open Neural Network Exchange) ist ein offenes und interoperables Format für die Darstellung von Modellen des maschinellen Lernens. ONNX wurde von einer kollaborativen Gemeinschaft entwickelt und ermöglicht den nahtlosen Austausch von Modellen zwischen verschiedenen Frameworks und Tools, wodurch die Interoperabilität im Ökosystem für maschinelles Lernen gefördert wird. Es bietet eine standardisierte Möglichkeit, trainierte Modelle darzustellen und zu übertragen, wodurch es für Entwickler einfacher wird, Modelle auf verschiedenen Plattformen einzusetzen und in unterschiedliche Anwendungen zu integrieren. ONNX unterstützt eine breite Palette von Modellen und Frameworks für maschinelles Lernen und fördert so die Flexibilität und Effizienz von Modellentwicklungs- und Bereitstellungsabläufen.

Dieser Code definiert einen anfänglichen Typ für eine Variable namens „double_input“, die im Zusammenhang mit der Erstellung einer ONNX-Datei (Open Neural Network Exchange) verwendet wird. Der angegebene Typ ist „DoubleTensorType“, was anzeigt, dass die Eingabedaten vom Typ „double“ erwartet werden. Die Form des Eingabetensors wird durch die Anzahl der Spalten in einem DataFrame (vermutlich mit dem Namen „csv“) bestimmt, die den für die Vorhersage verwendeten Merkmalen entsprechen (abgerufen mit „csv.loc[:, predictors].shape[1]“). Das „None“ in der Form zeigt an, dass die Größe der ersten Dimension (die wahrscheinlich die Anzahl der Instanzen oder Stichproben darstellt) in diesem Stadium noch nicht festgelegt ist.

initial_type_double = [('double_input', DoubleTensorType([None, csv.loc[:,predictors].shape[1]]))]

Dieser Code verwendet die Funktion „convert_sklearn“, um unser trainiertes lineares Regressionsmodell („lm“) in seine ONNX-Darstellung zu konvertieren. Die Variable „initial_type_double“, die im vorherigen Codeausschnitt definiert wurde, gibt den erwarteten Typ der Eingabedaten als Genauigkeit des Typs „double“ an. Zusätzlich wird der Parameter „target_opset“ auf 12 gesetzt, was die gewünschte Version des ONNX-Operatorsatzes angibt. Das resultierende „onnx_model_double“ ist eine ONNX-Modelldarstellung des bereitgestellten linearen Regressionsmodells, die sich für den Einsatz und die Interoperabilität mit anderen Frameworks eignet, die das ONNX-Format unterstützen.

onnx_model_double = convert_sklearn(lm, initial_types=initial_type_double, target_opset=12)

Dieser Code gibt den Dateinamen („EURUSD_ONNX“) für die Speicherung der ONNX-Modelldarstellung eines linearen Regressionsmodells an. Das resultierende ONNX-Modell, das mit der bereits erwähnten Funktion „convert_sklearn“ konvertiert wurde, wird unter diesem Dateinamen gespeichert, sodass es leicht identifizierbar und für die zukünftige Verwendung oder den Einsatz zugänglich ist.

onnx_model_filename = "EURUSD_ONNX"

Dieser Code kombiniert den zuvor definierten Dateinamen („EURUSD_ONNX“) mit dem Suffix „_Double.onnx“, um einen neuen Dateinamen („EURUSD_ONNX_Double.onnx“) zu erstellen. Anschließend wird die Funktion „onnx.save_model“ verwendet, um das ONNX-Modell („onnx_model_double“) in einer Datei mit dem konstruierten Dateinamen zu speichern. Dieser Vorgang stellt sicher, dass das ONNX-Modell, das das lineare Regressionsmodell in der Genauigkeit des Typs „double“ darstellt, gespeichert wird und über den angegebenen Dateinamen leicht referenziert werden kann.

onnx_filename=onnx_model_filename+"_Double.onnx"
onnx.save_model(onnx_model_double, onnx_filename)


Konstruktion eines Expert Advisors für unsere ONNX-Datei in MetaQuotes Language 5 (MQL5) unter Verwendung des integrierten und vielseitigen MetaEditors.

Die Entwicklung eines Expert Advisor (EA) für unsere ONNX-Datei in MetaQuotes Language 5 (MQL5) beinhaltet die Nutzung der Möglichkeiten des integrierten und vielseitigen MetaEditors. Ein Expert Advisor ist ein in MQL5 geschriebenes Skript, das den automatisierten Handel innerhalb der MetaTrader 5-Plattform ermöglicht. In diesem Zusammenhang wird der EA eine Schnittstelle zur ONNX-Datei haben, die eine nahtlose Integration von Modellen des maschinellen Lernens in Handelsstrategien ermöglicht und so die Entscheidungsfindung auf der Grundlage von prädiktiven Analysen verbessert. Der MetaEditor bietet eine umfassende Umgebung zum Kodieren, Testen und Optimieren von EAs und gewährleistet eine effiziente Bereitstellung und Ausführung innerhalb des MetaTrader 5-Frameworks.

Zunächst binden wir die Trade-Bibliothek in MQL5 ein, die eine Standardbibliothek für die Abwicklung von Handelsoperationen im MetaTrader 5 (MT5) ist. Die Handelsbibliothek bietet vordefinierte Funktionen und Strukturen, die die Ausführung verschiedener Handelsaktivitäten erleichtern, z. B. das Eröffnen und Schließen von Positionen, die Verwaltung von Aufträgen und die Bearbeitung handelsbezogener Ereignisse. Die Einbindung dieser Bibliothek in einen Expert Advisor (EA) ermöglicht eine rationalisierte und effiziente Implementierung von Handelslogik und -operationen innerhalb des MQL5-Codes.

//Our trade class helps us open trades
#include <Trade\Trade.mqh>
CTrade Trade;

Dieses Codeschnipsel in MQL5 beinhaltet die Verwendung eines Expert Advisors (EA) und beinhaltet ein ONNX-Modell für prädiktive Analysen. Die Direktive #resource wird verwendet, um die ONNX-Modelldatei „EURUSD_ONNX_Double.onnx“ als Byte-Array mit dem Namen ONNXModel in die EA-Ressourcen einzubetten. Dies erleichtert den Zugang und die Nutzung des maschinellen Lernmodells innerhalb des EA.

Die Variable ONNXHandle wird als INVALID_HANDLE initialisiert, was bedeutet, dass sie zum Speichern des Handles oder der Kennung des ONNX-Modells verwendet wird, sobald dieses während der Ausführung des EA geladen wird.

Darüber hinaus wird PredictedMove auf -1 initialisiert, was bedeutet, dass die vorausgesagte Bewegung oder das Ergebnis auf der Grundlage des ONNX-Modells noch nicht bestimmt ist. Diese Variable wird wahrscheinlich mit dem vorhergesagten Wert aktualisiert, sobald der EA während seiner Ausführung relevante Daten durch das ONNX-Modell verarbeitet. Die Einzelheiten der Vorhersagelogik und der weiteren Verarbeitung werden in den nachfolgenden Abschnitten des EA-Codes erläutert.

//Loading our ONNX model
#resource "ONNX\\EURUSD_ONNX_Double.onnx" as uchar EURUSD_ONNX_MODEL[]
long     ONNXHandle=INVALID_HANDLE;

In diesem Abschnitt des MQL5-Codes für einen Expert Advisor werden zwei Gruppen von Variablen deklariert: ma_handle und ma_reading[] für einen gleitenden Durchschnitt und ao_handle und ao_reading[] für einen Awesome Oscillator.

Die Variable ma_handle dient als Referenz oder Identifikator für den Indikator des gleitenden Durchschnitts, sodass der EA mit diesem spezifischen technischen Analyseinstrument interagieren und Informationen darüber abrufen kann. Das Array ma_reading[] dient dazu, die berechneten Werte des gleitenden Durchschnitts zu speichern, damit der EA auf die historischen Werte zugreifen und sie für seine Entscheidungsfindung analysieren kann.

In ähnlicher Weise soll die Variable ao_handle einen Bezeichner für den Awesome Oscillator Indikator darstellen, während das Array ao_reading[] die entsprechenden berechneten Werte speichern soll. 

//Handles for our technical indicators and dynamic arrays to store their readings
int ma_handle;
double ma_reading[];
int ao_handle;
double ao_reading[];

//Inputs
int input sl_width = 150; //How wide should the stoploss be?
int input  positions = 1; //How many positions should the we open?
int input lot_multiple = 1; //How many times greater than minimum lot should each position be?

//Symbol variables
double min_volume;
double bid,ask;

//We'll use this time stamp to keep track of the number of candles passing
static datetime time_stamp;

//Our model's forecast will be stored here
vector model_forecast(1);


Die Funktion OnInit()

Die Funktion OnInit() ist ein entscheidender Teil eines Expert Advisors (EA) in MQL5 und dient als Initialisierungsfunktion, die automatisch ausgeführt wird, wenn der EA mit einem Chart in MetaTrader 5 verbunden wird. In dieser Funktion werden in der Regel verschiedene Aufgaben im Zusammenhang mit der Einrichtung und Vorbereitung des EA durchgeführt. Der Rest des Codes, den wir untersuchen werden, ist in unserem OnInit().

Diese bedingte Anweisung innerhalb von OnInit() des Expert Advisors prüft, ob das Handelssymbol „EURUSD“ lautet und der Zeitrahmen des Charts auf M1 (Ein-Minuten-Intervalle) eingestellt ist. Wenn die Bedingungen nicht erfüllt sind, gibt der EA mit der Funktion Print() eine Meldung auf der Konsole aus, die besagt, dass das Modell speziell mit dem Währungspaar „EURUSD“ auf dem einminütigen Zeitrahmen arbeiten muss. Anschließend wird die Anweisung return(INIT_FAILED) verwendet, um den Initialisierungsprozess des EA zu beenden und anzuzeigen, dass die Initialisierung fehlgeschlagen ist, wenn die angegebenen Bedingungen nicht erfüllt sind.

int OnInit(){
    //Validating trading conditions
   if(_Symbol!="EURUSD" || _Period!=PERIOD_M1)
     {
      Print("Model must work with EURUSD on the M1 timeframe");
      return(INIT_FAILED);
     }

Dieses Codesegment innerhalb des Expert Advisors (EA) in MQL5 ist für die Erstellung eines ONNX-Modells aus einem statischen Puffer verantwortlich. Zu diesem Zweck wird die Funktion OnnxCreateFromBuffer verwendet, die als Parameter die im ONNXModel-Puffer gespeicherten ONNX-Modelldaten übernimmt und die Standardeinstellungen für die Modellerstellung verwendet.

Bei der Ausführung wird der ONNXHandle-Variable der Handle oder Identifier zugewiesen, der mit dem erstellten ONNX-Modell verbunden ist. Anschließend wird durch eine bedingte Anweisung geprüft, ob das ONNXHandle ein gültiges Handle ist (und nicht INVALID_HANDLE). Ist das Handle ungültig, gibt der EA mit der Funktion Print() eine Fehlermeldung auf der Konsole aus, die Informationen über den aufgetretenen Fehler enthält (über GetLastError()), und signalisiert dann einen Initialisierungsfehler durch Rückgabe von INIT_FAILED.

Dieser Codeabschnitt ist entscheidend für die Initialisierung des EA mit einem funktionierenden ONNX-Modell und stellt sicher, dass das Modell erfolgreich aus dem bereitgestellten Puffer erstellt wird. Jeder Fehler in diesem Prozess wird umgehend mitgeteilt.

ONNXHandle=OnnxCreateFromBuffer(ONNXModel,ONNX_DEFAULT);

if(ONNXHandle==INVALID_HANDLE)
{
   Print("OnnxCreateFromBuffer error ",GetLastError());
   return(INIT_FAILED);
}

Die Code-Zeile deklariert ein konstantes Array namens input_shape mit zwei Elementen. Dieses Array ist vom Typ long für ganze Zahlen. Die Elemente des Arrays stellen die Form der Eingabedaten für ein maschinelles Lernmodell oder einen Algorithmus dar. In diesem speziellen Fall wird das Array input_shape mit den Werten {1, 5} initialisiert, was anzeigt, dass die Eingabedaten eine Zeile und fünf Spalten haben sollen.

//Defining the model's input shape
const long input_shape[] = {1,5};

Dieser Codeblock innerhalb des Expert Advisors in MQL5 prüft, ob die Einstellung der Eingabeform für das ONNX-Modell mit der Funktion OnnxSetInputShape erfolgreich war. Die Eingabeform wird durch das input_shape-Array angegeben, das die erwarteten Dimensionen der Eingabedaten angibt.

Die if-Anweisung prüft, ob die Negation der Bedingung OnnxSetInputShape (die true zurückgibt, wenn das Setzen nicht erfolgreich war) zutrifft. Wenn die Bedingung erfüllt ist, gibt der EA mit der Funktion Print() eine Fehlermeldung auf der Konsole aus, die Einzelheiten über den aufgetretenen Fehler enthält, die mit GetLastError() ermittelt wurden. Anschließend gibt die Funktion INIT_FAILED zurück, was auf einen Initialisierungsfehler hinweist.

if(!OnnxSetInputShape(ONNXHandle,ONNX_DEFAULT,input_shape)) 
{
    Print("OnnxSetInputShape error ",GetLastError());
    return(INIT_FAILED); 
}

Diese Codezeile deklariert ein konstantes Array namens output_shape mit zwei Elementen. Ähnlich wie bei der vorherigen Deklaration für input_shape ist dieses Array vom Typ long für ganze Zahlen. In diesem Fall werden output_shape die Werte {1, 1} zugewiesen, was bedeutet, dass die erwartete Form der Ausgabedaten des maschinellen Lernmodells ein eindimensionales Array mit einem einzigen Element ist.

Die Spezifikation der Ausgabeform ist entscheidend für die angemessene Handhabung und Interpretation der vom maschinellen Lernmodell generierten Ergebnisse. 

//Defining the model's output shape
const long output_shape[] = {1,1};

Dieser Codeblock innerhalb des Expert Advisors in MQL5 prüft, ob die Einstellung der Ausgabeform für das ONNX-Modell mit der Funktion OnnxSetOutputShape erfolgreich war. Die Ausgabeform wird durch das Array output_shape angegeben, das die erwarteten Abmessungen der Ausgabedaten angibt.

Die if-Anweisung prüft, ob die Negation der Bedingung OnnxSetOutputShape (die true zurückgibt, wenn das Setzen nicht erfolgreich war) zutrifft. Wenn die Bedingung erfüllt ist, gibt der EA mit der Funktion Print() eine Fehlermeldung auf der Konsole aus, die Einzelheiten über den aufgetretenen Fehler enthält, die mit GetLastError() ermittelt wurden. Anschließend gibt die Funktion INIT_FAILED zurück, was auf einen Initialisierungsfehler hinweist.

Ähnlich wie bei der Eingabeform ist die Konfiguration der Ausgabeform wichtig, um das erwartete Format der Ausgabe des maschinellen Lernmodells mit den nachgelagerten Verarbeitungsanforderungen im EA abzustimmen. 

if(!OnnxSetOutputShape(ONNXHandle,0,output_shape))
{
    Print("OnnxSetOutputShape error ",GetLastError());
    return(INIT_FAILED);
}

In diesem Teil von OnInit() des Expert Advisors in MQL5 werden zwei technische Indikatoren initialisiert.

  1. ma_handle wird das Handle oder Identifikator für einen 20-Perioden Exponential Moving Average (EMA) zugewiesen, der auf der Grundlage der Schlusskurse (PRICE_CLOSE) des Ein-Minuten-Charts (PERIOD_M1) für das durch _Symbol angegebene Symbol berechnet wird.
  2. ao_handle wird das Handle für den Awesome Oscillator (AO) Indikator zugewiesen, der auf dem Ein-Minuten-Chart für dasselbe Symbol berechnet wurde.
  3. min_volume ist die kleinste vom Broker erlaubte Kontraktgröße.
//Setting up our technical indicators
ma_handle = iMA(_Symbol,PERIOD_M1,20,0,MODE_EMA,PRICE_CLOSE);
ao_handle = iAO(_Symbol,PERIOD_M1);
min_volume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
return(INIT_SUCCEEDED);
}


Die Funktion OnDeinit

Mit der Funktion OnDeinit wird in diesem MQL5 Expert Advisor der EA glöscht. Sie wird automatisch ausgeführt, wenn der EA entfernt wird oder die Handelsplattform geschlossen wird.

Innerhalb dieser Funktion prüft eine bedingte Anweisung, ob die Variable ONNXHandle ein gültiges Handle enthält (nicht INVALID_HANDLE). Wenn die Bedingung erfüllt ist, bedeutet dies, dass das ONNX-Modell während der Laufzeit des EA initialisiert wurde. In solchen Fällen wird die Funktion OnnxRelease aufgerufen, um die mit dem ONNX-Modell verbundenen Ressourcen freizugeben, und anschließend wird der ONNXHandle auf INVALID_HANDLE gesetzt.

Diese Deinitialisierungsroutine gewährleistet die ordnungsgemäße Freigabe von Ressourcen, verhindert Speicherlecks und trägt zur allgemeinen Effizienz und Sauberkeit des Lebenszyklus des EA bei. Es spiegelt eine verantwortungsbewusste Kodierungspraxis wider, um die während der Ausführung des EA erworbenen Ressourcen zu verwalten und freizugeben, wodurch die Robustheit und Zuverlässigkeit des Handelssystems verbessert wird.

void OnDeinit(const int reason)
  {
      //OnDeinit we should freeup resources we don't require
      //We'll begin by releasing the ONNX models then removing the Expert Advisor
      if(ONNXHandle!=INVALID_HANDLE)
     {
      OnnxRelease(ONNXHandle);
      ONNXHandle=INVALID_HANDLE;
     }
   //Lastly remove the Expert Advisor
   ExpertRemove();
  }


Die Funktion OnTick

Die OnTick-Funktion ist eine wichtige Komponente dieses MQL5 Expert Advisors, die den Code darstellt, der bei jedem eingehenden Tick ausgeführt wird. In dieser Funktion:

Historische Daten aus den Indikatoren Gleitender Durchschnitt (MA) und Awesome Oscillator (AO) werden mit der Funktion CopyBuffer abgerufen. Die letzten 10 Werte werden in Arrays kopiert (ma_reading und ao_reading), und ArraySetAsSeries wird verwendet, um die Arrays serienartig zu organisieren.

Der aktuelle Zeitstempel (current_time) wird mit Hilfe der Funktion iTime für den Ein-Minuten-Chart (PERIOD_M1) und das angegebene Symbol (_Symbol) ermittelt.

Eine bedingte Anweisung prüft, ob der aktuelle Zeitstempel vom gespeicherten Zeitstempel (time_stamp) abweicht. Wenn es eine Differenz gibt, die auf einen neuen Tick hinweist, wird die Funktion ModelForecast aufgerufen.

Diese Codestruktur ermöglicht es dem EA, die jüngsten Indikatorwerte bei jedem Tick zu erfassen und zu organisieren, was die periodische Bewertung der Prognosen des maschinellen Lernmodells durch die Funktion ModelForecast erleichtert. Die OnTick-Funktion bildet somit die Grundlage für Echtzeit-Entscheidungen auf der Grundlage der neuesten Marktbedingungen und Modellvorhersagen und trägt zur dynamischen und adaptiven Natur des Expert Advisors bei.

void OnTick()
{
   //Update the arrays storing the technical indicator values to their current values
   //Then set the arrays as series so that the current reading is at the top and the oldest reading is last
   CopyBuffer(ma_handle,0,0,10,ma_reading);
   ArraySetAsSeries(ma_reading,true);
   CopyBuffer(ao_handle,0,0,10,ao_reading);
   ArraySetAsSeries(ao_reading,true);
   ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
//Update time marker
   datetime current_time = iTime(_Symbol,PERIOD_M1,0);
   //Periodically make forecasts if we have no open positions
   if(time_stamp != current_time){
      //No open positions
      if(PositionsTotal() == 0){
               ModelForecast();
      }  
      //We have open positions to manage
      else if(PositionsTotal() > 0){
               ManageTrade();
      }
   }
  }


Modell-Inferenzierung

Die Funktion ModelForecast in diesem MQL5 Expert Advisor dient der Ausführung des maschinellen Lernmodells zur Erstellung von Prognosen auf der Grundlage der aktuellen Marktbedingungen. Hier ist eine Ausarbeitung des Codes:

Innerhalb der Funktion werden zwei Vektoren deklariert:

  1. model_forecast: Ein Vektor, der zum Speichern der Ausgabe des maschinellen Lernmodells verwendet wird und dessen Prognose oder Vorhersage darstellt.
  2. model_input: Ein Vektor, der die für das Modell erforderlichen Eingangsmerkmale enthält. Zu diesen Merkmalen gehören der Höchst-, Tiefst- und Schlusskurs der aktuellen Kerze, der Wert des gleitenden Durchschnitts (ma_reading[0]) und der Wert des Awesome Oscillators (ao_reading[0]).

Die Funktion OnnxRun wird aufgerufen, um die Inferenz unter Verwendung des ONNX-Modells (ONNXHandle) durchzuführen. Das Flag ONNX_NO_CONVERSION zeigt an, dass während des Inferenzprozesses keine Datentypkonvertierung durchgeführt wird. Die Eingabemerkmale (model_input) werden bereitgestellt, und die resultierende Vorhersage wird im model_forecast-Vektor gespeichert.

Eine bedingte Anweisung prüft, ob der Inferenzprozess erfolgreich war. Ist dies nicht der Fall, wird mit dem Print()-Befehl eine Fehlermeldung auf der Konsole ausgegeben, die Einzelheiten über den aufgetretenen Fehler enthält, die mit GetLastError() ermittelt wurden.

Wenn die Inferenz erfolgreich ist, wird die im model_forecast-Vektor gespeicherte Prognose auf der Konsole ausgegeben.

Diese Funktion umfasst die wesentlichen Schritte zur Erstellung von Modellvorhersagen auf der Grundlage der aktuellen Marktbedingungen und fördert so eine dynamische und adaptive Handelsstrategie innerhalb des Expert Advisors. Die Einbeziehung von Mechanismen zur Fehlerbehandlung erhöht die Robustheit des Systems, indem es Einblicke in mögliche Probleme während des Inferenzprozesses bietet. Nach Abschluss ruft die Funktion NextMove() auf.

//This function provides these utilities:
//          1) Inferencing using our ONNX model 
//          2) Calling the next function responsible for intepreting model forecasts and other subroutines
void ModelForecast(void){
      //These are the inputs for our ONNX model:
      //          1)High 
      //          2)Low
      //          3)Close
      //          4)20 PERIOD MA MODE: EMA  APPLIED_PRICE:PRICE CLOSE 
      //          5)Awesome Oscilator
      vector model_input{iHigh(_Symbol,PERIOD_M1,0),iLow(_Symbol,PERIOD_M1,0),iClose(_Symbol,PERIOD_M1,0),ma_reading[0],ao_reading[0]};
      //Inferencing with our model
      if(!OnnxRun(ONNXHandle,ONNX_NO_CONVERSION,model_input,model_forecast)){
            Print("Error performing inference: ",GetLastError());
      }
      //Pring model forecast to the terminal
      else{
               Print(model_forecast);
                NextMove();
      }
}


Interpretation des Modells

Für die Interpretation unseres Modells sind 2 Aufgaben zu erfüllen:

  1. Die Interpretation der Modellausgabe: Die Funktion verwendet die Funktion InterpretForecast, um die Ausgabe eines Vorhersagemodells zu interpretieren. Die Funktion InterpretForecast wird zweimal aufgerufen, zuerst mit einem Argument von 1 und dann mit einem Argument von -1. Mit diesen Aufrufen soll überprüft werden, ob die Modellausgabe eine bestimmte Richtung angibt: 1 für eine positive Prognose und -1 für eine negative Prognose.
  2. Umsetzung der Interpretationen: Je nach Interpretation der Modellausgabe führt die Funktion bestimmte Aktionen aus. Wenn das Modell ein positives Ergebnis (1) vorhersagt, ruft es die Funktion CheckOrder mit einem Argument von 1 auf und kehrt dann zurück. Wenn das Modell ein negatives Ergebnis (-1) vorhersagt, wird die Funktion CheckOrder mit einem Argument von -1 aufgerufen.

Zusammenfassend lässt sich sagen, dass die Funktion NextMove dazu dient, die Ausgabe eines Vorhersagemodells zu verarbeiten, sie auf der Grundlage bestimmter Werte (1 oder -1) zu interpretieren und entsprechende Maßnahmen zu ergreifen, indem die Funktion CheckOrder mit den interpretierten Werten aufgerufen wird.

//This function provides these utilities:
//          1) Getting the model output intepreted
//          2) Acting on the intepretations
void NextMove(){
      if(InterpretForecast(1)){
            CheckOrder(1);
            return;
      }     
      else if(InterpretForecast(-1)){
            CheckOrder(-1);
      }
}


Die Funktion InterpretForecast dient dazu, die Ausgabe eines Vorhersagemodells auf der Grundlage einer bestimmten Richtung zu interpretieren. Die Funktion benötigt ein Argument „direction“, das entweder 1 oder -1 sein kann. Die Interpretation hängt davon ab, ob das Modell einen Wert größer als 1 oder kleiner als 1 vorausgesagt hat.

Hier ist eine Aufschlüsselung des Codes:

Wenn „direction“ gleich 1 ist, prüft die Funktion, ob das erste Element (model_forecast[0]) des Arrays model_forecast größer als 1 ist. Ist dies der Fall, gibt die Funktion den Wert „true“ zurück und zeigt damit an, dass das Modell ein Wachstum prognostiziert, das über dem aktuellen Preis liegt.

Wenn „direction“ gleich -1 ist, prüft die Funktion, ob das erste Element (model_forecast[0]) des Arrays model_forecast kleiner als 1 ist. Ist dies der Fall, gibt die Funktion den Wert „true“ zurück und zeigt damit an, dass das Modell ein Wachstum prognostiziert, das unter dem aktuellen Preis liegt.

Wenn „direction“ weder 1 noch -1 ist, gibt die Funktion false zurück. Dies dient als Standardfall und zeigt an, dass die angegebene Richtung nicht erkannt wird und die Funktion keine sinnvolle Interpretation liefern kann.

Zusammenfassend lässt sich sagen, dass die Funktion InterpretForecast den prognostizierten Wert des Modells auf der Grundlage der angegebenen Richtung prüft und true zurückgibt, wenn die Bedingung erfüllt ist, andernfalls gibt sie false zurück.

//This function provides these utilities:
//          1) Check whether the model forecasted a reading greater than or less than 1.
bool InterpretForecast(int direction){
      //1 means check if the model is forecasting growth greater than the current price
      if(direction == 1){
            return(model_forecast[0] > 1);
      }
      //-1 means check if the model is forecasting growth less than the current price
      if(direction == -1){
            return(model_forecast[0] < 1);
      }
      //Otherwise return false.
      return false;
}


Auftragsausführung

Der folgende Code definiert die Funktion CheckOrder. Diese Funktion hat die Aufgabe, Handelspositionen auf der Grundlage einer bestimmten Auftragsrichtung zu initiieren.

Überprüfung der geöffneten Positionen: Die anfängliche bedingte Anweisung (PositionsTotal() == 0) dient als Voraussetzungsprüfung, um sicherzustellen, dass neue Positionen nur dann eröffnet werden, wenn keine aktiven Handelsgeschäfte im Portfolio vorhanden sind.

Ausführung von Kaufaufträgen: Ist der Parameter order_direction gleich 1 (was eine Kauforder bedeutet), verwendet die Funktion eine for-Schleife, um iterativ die gewünschte Anzahl von Positionen auszuführen (positions). Innerhalb dieser Schleife wird die Funktion Trade.PositionOpen aufgerufen, um Kaufpositionen zu initialisieren. Relevante Parameter wie Symbol (_Symbol), Auftragsart (ORDER_TYPE_BUY), Volumen (min_volume * lot_multiple) und Ausführungskurs (ask) werden dieser Funktion als Argumente zur Verfügung gestellt.

Ausführung von Verkaufsaufträgen: Umgekehrt, wenn der Parameter order_direction gleich -1 ist (was auf eine Verkaufsorder hinweist) und derzeit keine Positionen aktiv sind (PositionsTotal() == 0 wird neu bewertet), fährt die Funktion mit der Eröffnung von Verkaufspositionen durch einen ähnlichen iterativen Prozess fort. Die Funktion Trade.PositionOpen wird wieder mit Parametern verwendet, die auf den Verkauf von Positionen zugeschnitten sind.

Zusammenfassend lässt sich sagen, dass die Funktion CheckOrder die disziplinierte Eröffnung von Handelspositionen unter Berücksichtigung des Fehlens bestehender Positionen und unter Einhaltung der vorgegebenen Auftragsrichtung gewährleistet. Der Code kapselt die Handelslogik im Rahmen von algorithmischen Handelsstrategien.

//This function is responsible for opening positions
void CheckOrder(int order_direction){
      //Only open new positions if we have no open positions
     if(PositionsTotal() == 0){
            //Buy
            if(order_direction == 1){
                  //Iterate over the desired number of positions
                  for(int i = 0; i < positions; i++){
                           Trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,min_volume * lot_multiple,ask,0,0,"Volatitlity Doctor AI");
                  }
            }
            //Sell
            else if(order_direction == -1 && PositionsTotal() == 0){
                  //Iterate over the desired number of positions
                  for(int i = 0; i < positions; i++){
                        Trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,min_volume  * lot_multiple ,bid,0,0,"Volatitlity Doctor AI");
            }
         }
   }
}


Handelsmanagement

Die Funktion ManageTrade fungiert als zentrales Handelsverwaltungsmodul und überträgt die Verantwortung für die Anpassung der Stop-Loss- und Take-Profit-Niveaus an die CheckStop-Funktion.

Die Funktion CheckStop iteriert über alle offenen Positionen und extrahiert relevante Informationen wie Symbol, Ticket, Positionstyp, aktueller Stop-Loss und offener Preis der Position. Sie stellt sicher, dass das Symbol der Position mit dem aktiv gehandelten Symbol (_Symbol) übereinstimmt. Für jede gültige Position berechnet der Code neue Stop-Loss- und Take-Profit-Niveaus, die auf vordefinierten Parametern wie GBid- und Ask-Kursen, der Abstand des Stop-Loss (sl_width) und dem Punktwert (_Point) basieren.

Die Funktion unterscheidet dann zwischen Kauf- und Verkaufspositionen. Bei Kaufpositionen werden die neuen Stop-Loss- und Take-Profit-Niveaus auf der Grundlage des Ask-Kurses berechnet und nur dann angepasst, wenn die neuen Niveaus günstiger sind. Bei Verkaufspositionen erfolgt die Berechnung ebenfalls auf der Grundlage des Bid-Kurses, und es werden Anpassungen vorgenommen, wenn die neuen Kurse günstiger sind.

Die Verwendung von NormalizeDouble stellt sicher, dass die berechneten Werte mit der angegebenen Anzahl von Ziffern (_Digits) übereinstimmen. Die Funktion Trade.PositionModify wird verwendet, um das bestehende Handelsgeschäft mit den aktualisierten Stop-Loss- und Take-Profit-Niveaus zu ändern, falls erforderlich.

//This function handles our trade management
void ManageTrade(){
   CheckStop();
}

//This funciton will update our S/L & T/P 
void CheckStop(){
      //First we iterate over the total number of open positions                      
      for(int i = PositionsTotal() -1; i >= 0; i--){
            //Then we fetch the name of the symbol of the open position
            string symbol = PositionGetSymbol(i);
            //Before going any furhter we need to ensure that the symbol of the position matches the symbol we're trading
                  if(_Symbol == symbol){
                           //Now we get information about the position
                           ulong ticket = PositionGetInteger(POSITION_TICKET); //Position Ticket
                           double position_price = PositionGetDouble(POSITION_PRICE_OPEN); //Position Open Price
                           long type = PositionGetInteger(POSITION_TYPE); //Position Type
                           double current_stop_loss = PositionGetDouble(POSITION_SL); //Current Stop loss value
                           //If the position is a buy
                           if(type == POSITION_TYPE_BUY){
                                  //The new stop loss value is just the ask price minus the stop we calculated above
                                  double new_stop_loss = NormalizeDouble(ask - ((sl_width * _Point) / 2) ,_Digits);
                                  //The new take profit is just the ask price plus the stop we calculated above
                                  double new_take_profit = NormalizeDouble(ask + (sl_width * _Point),_Digits);
                                  //If our current stop loss is less than our calculated stop loss 
                                  //Or if our current stop loss is 0 then we will modify the stop loss and take profit
                                 if((current_stop_loss < new_stop_loss) || (current_stop_loss == 0)){
                                       Trade.PositionModify(ticket,new_stop_loss,new_take_profit);
                                 }  
                           }
                            //If the position is a sell
                           else if(type == POSITION_TYPE_SELL){
                                  //The new stop loss value is just the ask price minus the stop we calculated above
                                  double new_stop_loss = NormalizeDouble(bid + ((sl_width * _Point)/2),_Digits);
                                  //The new take profit is just the ask price plus the stop we calculated above
                                  double new_take_profit = NormalizeDouble(bid - (sl_width * _Point),_Digits);
                                 //If our current stop loss is greater than our calculated stop loss 
                                 //Or if our current stop loss is 0 then we will modify the stop loss and take profit 
                                 if((current_stop_loss > new_stop_loss) || (current_stop_loss == 0)){
                                       Trade.PositionModify(ticket,new_stop_loss,new_take_profit);
                                 }
                           }  
                  }  
            }
}


Unser Expert Advisor wird nun diese Parameter in seinem Menü enthalten.


EA

Abb. 11: Unser Expert Advisor.


Und sollte automatisch Stop-Loss- und Take-Profit-Levels für jeden Handel setzen, den er eröffnet.

EA in Aktion

Abb. 12: Unser Expert Advisor in Aktion.


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass Scheinregressionen bei der Modellierung von Zeitreihendaten eine große Herausforderung darstellen und häufig zu irreführenden und unzuverlässigen Ergebnissen führen. Forscher und Praktiker müssen bei der Interpretation von Regressionsergebnissen Vorsicht walten lassen, insbesondere wenn es um nichtstationäre Zeitreihendaten geht. Um das Risiko von Scheinregressionen zu mindern, ist die Anwendung geeigneter statistischer Verfahren wie Einheitswurzeltests, Kointegrationsanalysen und die Verwendung stationärer Variablen von entscheidender Bedeutung. Darüber hinaus kann die Anwendung fortgeschrittener Zeitreihenmethoden, wie z. B. Fehlerkorrekturmodelle, die Robustheit von Regressionsanalysen verbessern und zu genaueren und aussagekräftigeren wirtschaftlichen Interpretationen beitragen. Letztendlich sind ein differenziertes Verständnis der zugrunde liegenden Dateneigenschaften und eine rigorose Anwendung statistischer Methoden für Forscher, die angesichts potenzieller Scheinregressionen zuverlässige und gültige Regressionsergebnisse erzielen wollen, von entscheidender Bedeutung.

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

Modifizierter Grid-Hedge EA in MQL5 (Teil IV): Optimierung der einfachen Grid-Strategie (I) Modifizierter Grid-Hedge EA in MQL5 (Teil IV): Optimierung der einfachen Grid-Strategie (I)
In diesem vierten Teil greifen wir die zuvor entwickelten Simple Hedge und Simple Grid Expert Advisors (EAs) wieder auf. Wir konzentrieren uns darauf, den Simple Grid EA durch mathematische Analysen und einen Brute-Force-Ansatz zu verfeinern, mit dem Ziel, eine optimale Strategie anzuwenden. Dieser Artikel befasst sich eingehend mit der mathematischen Optimierung der Strategie und legt den Grundstein für die künftige Erforschung der kodierungsbasierten Optimierung in späteren Ausgaben.
Einführung in MQL5 (Teil 7): Anleitung für Anfänger zur Erstellung von Expert Advisors und zur Verwendung von AI-generiertem Code in MQL5 Einführung in MQL5 (Teil 7): Anleitung für Anfänger zur Erstellung von Expert Advisors und zur Verwendung von AI-generiertem Code in MQL5
Entdecken Sie die ultimative Anleitung für Anfänger zum Erstellen von Expert Advisors (EAs) mit MQL5 in unserem umfassenden Artikel. Lernen Sie Schritt für Schritt, wie Sie EAs mithilfe von Pseudocode konstruieren und die Leistung von KI-generiertem Code nutzen können. Egal, ob Sie neu im algorithmischen Handel sind oder Ihre Fähigkeiten verbessern wollen, dieser Leitfaden bietet einen klaren Weg zur Erstellung effektiver EAs.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 18): Neuronale Architektursuche mit Eigenvektoren MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 18): Neuronale Architektursuche mit Eigenvektoren
Die Suche nach neuronaler Architektur, ein automatischer Ansatz zur Bestimmung der idealen Einstellungen für neuronale Netze, kann bei vielen Optionen und großen Testdatensätzen von Vorteil sein. Wir untersuchen, wie dieser Prozess bei gepaarten Eigenvektoren noch effizienter gestaltet werden kann.
Neuronale Netze leicht gemacht (Teil 75): Verbesserung der Leistung von Modellen zur Vorhersage einer Trajektorie Neuronale Netze leicht gemacht (Teil 75): Verbesserung der Leistung von Modellen zur Vorhersage einer Trajektorie
Die Modelle, die wir erstellen, werden immer größer und komplexer. Dies erhöht nicht nur die Kosten für ihr Training, sondern auch für ihren Betrieb. Die Zeit, die für eine Entscheidung benötigt wird, ist jedoch oft entscheidend. In diesem Zusammenhang sollten wir Methoden zur Optimierung der Modellleistung ohne Qualitätseinbußen in Betracht ziehen.