
Feature Engineering mit Python und MQL5 (Teil II): Winkel des Preises (2), Polarkoordinaten
Das offene Interesse an der Umwandlung von Preisniveauveränderungen in Winkelveränderungen hat nicht nachgelassen. Wie wir bereits in unserem letzten Artikel in dieser Reihe erörtert haben, gibt es viele Herausforderungen zu bewältigen, um Änderungen des Preisniveaus erfolgreich in einen Winkel umzuwandeln, der diese Änderung darstellt.
Eine der am häufigsten genannten Einschränkungen in Diskussionen in der Gemeinschaft und in Forenbeiträgen ist das Fehlen einer interpretierbaren Bedeutung hinter solchen Berechnungen. Erfahrene Community-Mitglieder werden oft erklären, dass ein Winkel zwischen zwei Geraden besteht, weshalb der Versuch, den durch eine Preisänderung gebildeten Winkel zu berechnen, in der realen Welt keine physikalische Bedeutung hat.
Das Fehlen einer realen Interpretation ist nur eine der vielen Herausforderungen, die Händler bewältigen müssen, die an der Berechnung des Winkels interessiert sind, der durch Veränderungen der Preisniveaus entsteht. In unserem vorherigen Artikel haben wir versucht, dieses Problem zu lösen, indem wir die Zeit auf der x-Achse ersetzt haben, damit der gebildete Winkel ein Verhältnis der Preisniveaus ist und eine interpretierbare Bedeutung hat. Bei unseren Untersuchungen haben wir festgestellt, dass unser Datensatz nach dieser Transformation mühelos mit „Unendlich“-Werten gespickt ist. Leser, die an einer kurzen Auffrischung unserer früheren Beobachtungen interessiert sind, finden hier einen Link zu dem Artikel.
In Anbetracht der zahlreichen Herausforderungen, die sich bei dem Versuch ergeben, Änderungen des Preisniveaus in eine entsprechende Änderung des Blickwinkels umzuwandeln, und des Fehlens einer eindeutigen Bedeutung in der realen Welt gibt es nur begrenzte, organisierte Informationen zu diesem Thema.
Wir werden das Problem der Umrechnung von Preisen in Winkel aus einer völlig neuen Perspektive angehen. Diesmal werden wir einen mathematisch ausgefeilteren und robusteren Ansatz verwenden als bei den Tools, die wir bei unserem ersten Versuch entwickelt haben. Leser, die bereits mit Polarkoordinaten vertraut sind, können direkt zum Abschnitt „Erste Schritte in MQL5“ springen, um zu sehen, wie diese mathematischen Werkzeuge in MQL5 implementiert sind.
Andernfalls werden wir jetzt ein Verständnis dafür entwickeln, was Polarkoordinaten sind, und eine Intuition dafür entwickeln, wie wir sie anwenden können, um den Winkel zu berechnen, der durch Preisänderungen auf unserem MetaTrader 5-Terminal gebildet wird, und diese Signale für den Handel zu nutzen. Das heißt, dass:
- Die von uns vorgeschlagene Lösung hat eine reale physikalische Bedeutung.
- Wir werden auch das Problem der unendlichen oder undefinierten Werte in den Griff bekommen.
Was sind Polarkoordinaten, und wie können sie nützlich sein?
Wann immer wir die GPS-Technologie oder sogar einfache Tabellenkalkulationsanwendungen nutzen, verwenden wir eine Technologie, die als kartesische Koordinaten bekannt ist. Dies ist ein mathematisches System zur Darstellung von Punkten in einer Ebene, typischerweise mit 2 senkrechten Achsen.
Jeder einzelne Punkt im kartesischen System wird als Paar von (x, y) Koordinaten dargestellt. Dabei steht x für die horizontale Entfernung vom Ursprung und y für die vertikale Entfernung vom Ursprung.
Wenn wir Prozesse untersuchen wollen, die periodische Komponenten oder eine Form der Kreisbewegung aufweisen, sind Polarkoordinaten besser geeignet als kartesische Koordinaten. Polarkoordinaten sind ein völlig anderes System zur Darstellung von Punkten in einer Ebene unter Verwendung eines Abstands von einem Bezugspunkt und eines Winkels zu einer Bezugsrichtung, der gegen den Uhrzeigersinn zunimmt.
Die Finanzmärkte neigen dazu, fast periodisch wiederkehrende Muster zu zeigen. Daher können sie auch als Polarkoordinaten dargestellt werden. Es scheint, dass der Winkel, den die Händler aus den Veränderungen der Preisniveaus berechnen wollen, auf natürliche Weise erreicht werden kann, indem die Preisniveaus als polare Paare dargestellt werden.
Polarkoordinaten werden als Paar von (r, theta) dargestellt, wobei:
- R: Stellt den radialen Abstand vom Bezugspunkt (Ursprung) dar
- Theta: Stellt den von der Bezugsrichtung gemessenen Winkel dar.
Mit den trigonometrischen Funktionen, die in der MQL5 Matrix- und Vektor-API implementiert sind, können wir Preisänderungen nahtlos in einen Winkel umwandeln, der die Preisänderung darstellt.
Um unser Ziel zu erreichen, müssen wir uns zunächst mit der Terminologie vertraut machen, die wir in unserer heutigen Diskussion verwenden werden. Zunächst müssen wir unsere x- und y-Eingänge definieren, die umgewandelt werden sollen. Für unsere Diskussion werden wir x als den Eröffnungskurs des Symbols und y als den Schlusskurs festlegen.
Abb. 1: Definieren unserer kartesischen Punkte, die in polare Punkte umgewandelt werden
Nachdem wir nun unsere x- und y-Eingänge definiert haben, müssen wir das erste Element des Polpaares, den radialen Abstand r vom Ursprung, berechnen.
Abb. 2: Die geschlossene Formel zur Berechnung von r, aus (x, y)
Geometrisch dargestellt, kann man sich Polarkoordinaten als Beschreibung eines Kreises vorstellen. Der Winkel, der zwischen r und x gebildet wird, ist Theta. Daher schlagen die Polarkoordinaten einfach vor, dass die Verwendung von r und Theta genauso informativ ist wie die direkte Verwendung von x und y. Wenn man sich x und y wie in Abb. 3 vorstellt, wird der radiale Abstand r durch Anwendung des Satzes von Pythagoras auf die Seiten x und y berechnet.
Abb. 3: Polarkoordinaten können als Beschreibung eines Punktes auf einem Kreis visualisiert werden
Der Winkel zwischen r und x, Theta, befriedigt unseren Wunsch nach realer Bedeutung. In diesem einfachen Beispiel korreliert das Theta mit der Richtung des Trends, der sich aus den Veränderungen des Eröffnungs- und Schlusskurses ergibt. Theta erhalten wir durch die Berechnung des inversen Tangens des Schlusskurses geteilt durch den Eröffnungskurs, wie in der Abbildung unten dargestellt.
Abb. 4: Berechnung von Theta aus unserem Eröffnungs- (x) und Schlusskurs (y)
Ausgehend von beliebigen Polarkoordinaten (r, theta) können wir sie mit den beiden folgenden Formeln leicht in ihre ursprünglichen Preisniveaus umrechnen:
Abb. 5: Wie konvertiert man Polarkoordinaten zurück in kartesische Koordinaten?
Wir haben bisher 4 Formeln besprochen, aber nur die letzten 3 Formeln enthalten Theta. Die erste Formel, die wir zur Berechnung von r verwenden, hat keinen Bezug zu Theta. Die letzten 3 Formeln, die wir besprochen haben, enthalten Theta und können leicht differenziert werden. Die Ableitungen dieser trigonometrischen Funktionen sind bekannte Ergebnisse, die leicht im Internet oder in jedem Lehrbuch der Elementarrechnung zu finden sind.
Wir werden diese 3 Derivate als zusätzliche Eingaben verwenden, um unseren Computer zu trainieren, damit er die Beziehung zwischen Änderungen der Winkel und den entsprechenden Änderungen des Preisniveaus lernt.
Erste Schritte in MQL5
Lassen Sie uns beginnen. Zunächst müssen wir ein Skript in MQL5 erstellen, das historische Marktdaten von unserem MetaTrader 5-Terminal abruft und auch die Transformationen durchführt, um uns die zu erstellenden Winkel zu liefern.
Zunächst müssen wir den Namen der zu erstellenden CSV-Datei und die Anzahl der abzurufenden Datenbalken festlegen. Da die Anzahl der abzurufenden Bars je nach Broker variieren kann, haben wir diesen Parameter als Eingabe für das Skript festgelegt.
#property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs //---File name string file_name = _Symbol + " " + " Polar Coordinates.csv"; //---Amount of data requested input int size = 100; int size_fetch = size + 100;
Wenn unser Skript ausgeführt wird, erstellen wir eine Dateihandhabung, um die Preisniveaus und die entsprechenden Änderungen des Winkels zu notieren.
void OnStart() { //---Write to file int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,","); for(int i=size;i>0;i--){ if(i == size){ FileWrite(file_handle,"Time","Open","High","Low","Close","R","Theta","X Derivatie","Y Derivative","Theta Derivative"); } else{
Berechnen wir nun r anhand der in Abb. 2 beschriebenen Formel.
double r = MathSqrt(MathPow(iOpen(_Symbol,PERIOD_CURRENT,i),2) + MathPow(iClose(_Symbol,PERIOD_CURRENT,i),2));
Theta wird durch den inversen tan des Verhältnisses zwischen y und x berechnet. Dies ist für uns in der MQL5-API implementiert.
double theta = MathArctan2(iClose(_Symbol,PERIOD_CURRENT,i),iOpen(_Symbol,PERIOD_CURRENT,i));
Die Formel zur Berechnung von x (offener Preis) ist in Abbildung 5 dargestellt. Wir können diese Formel bezüglich Theta differenzieren, um die erste Ableitung des offenen Preises zu berechnen. Die Ableitung von cos() ist, wie Sie bereits wissen, -sin().
double derivative_x = r * (-(MathSin(theta)));
Wir können auch die Ableitung von y berechnen, da wir die Ableitung von trigonometrischen Funktionen kennen.
double derivative_y = r * MathCos(theta);
Schließlich kennen wir auch die erste Ableitung von Theta. Die Trigonometrie-Funktion ist jedoch nicht direkt in der MQL5-API implementiert, sondern wir werden eine mathematische Identität verwenden, um sie durch die entsprechenden MQL5-Funktionen zu ersetzen.
double derivative_theta = (1/MathPow(MathCos(theta),2));
Nachdem wir nun unsere Winkel berechnet haben, können wir unsere Daten aufschreiben.
FileWrite(file_handle,iTime(_Symbol,PERIOD_CURRENT,i), iOpen(_Symbol,PERIOD_CURRENT,i), iHigh(_Symbol,PERIOD_CURRENT,i), iLow(_Symbol,PERIOD_CURRENT,i), iClose(_Symbol,PERIOD_CURRENT,i), r, theta, derivative_x, derivative_y, derivative_y ); } } FileClose(file_handle); } //+---------
Analyse unserer Daten
Nun, da unsere Daten im CSV-Format vorliegen, können wir sie verwenden, um unseren Computer für den Handel mit den gebildeten Winkeln zu trainieren. Wir werden einige Python-Bibliotheken verwenden, um unseren Entwicklungsprozess zu beschleunigen.
import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt
Wir kennzeichnen die Daten, um zu vermerken, ob das Preisniveau am folgenden Tag gestiegen oder gefallen ist.
data = pd.read_csv("EURUSD Polar Coordinates.csv") data["UP DOWN"] = 0 data.loc[data["Close"] < data["Close"].shift(-1),"UP DOWN"] = 1 data
Hier ist das Handelssignal, nach dem wir suchen. Beachten Sie, dass das Preisniveau in unserem ursprünglichen Datensatz nie sinkt, wenn sowohl R als auch Theta steigen. Dieser Aufruf an pandas gibt nichts zurück, was die reale Bedeutung von Polpaaren zeigt. Die Kenntnis der zukünftigen Werte von R und Theta ist genauso gut wie die Kenntnis des zukünftigen Kursniveaus.
data.loc[(data["R"] < data["R"].shift(-1)) & (data['Theta'] < data['Theta'].shift(-1)) & (data['Close'] > data['Close'].shift(-1))
Führen wir dieselbe Abfrage in umgekehrter Richtung durch, d. h. wir suchen nach Fällen, in denen R und Theta gestiegen sind, der Futures-Kurs aber gefallen ist, wird Pandas wiederum 0 Fälle liefern.
data.loc[(data["R"] > data["R"].shift(-1)) & (data['Theta'] > data['Theta'].shift(-1)) & (data['Close'] < data['Close'].shift(-1))]
Daher werden unsere Handelssignale immer dann gebildet, wenn unser Computer erwartet, dass die zukünftigen Werte von R und Theta größer sind als ihre aktuellen Werte. Nun können wir unsere Preisdaten als Punkte auf einem Polarkreis visualisieren. Wie in Abbildung 6 zu sehen ist, ist es immer noch schwierig, die Daten effektiv zu trennen.
data['Theta_rescaled'] = (data['Theta'] - data['Theta'].min()) / (data['Theta'].max() - data['Theta'].min()) * (2 * np.pi) data['R_rescaled'] = (data['R'] - data['R'].min()) / (data['R'].max() - data['R'].min()) # Create the polar plot fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}) # Plot data points on the polar axis ax.scatter(data['Theta_rescaled'], data['R_rescaled'],c=data["UP DOWN"], cmap='viridis', edgecolor='black', s=100) # Add plot labels ax.set_title("Polar Plot of OHLC Points") plt.colorbar(plt.cm.ScalarMappable(cmap='viridis'), ax=ax, label='1(UP) | O(DOWN)') plt.show()
Abb. 6: Visualisierung unserer Preisdaten als polare Punkte auf einem Polarkreis
Lassen Sie uns schnell prüfen, ob einer unserer Werte null ist.
data.isna().any()
Abb. 7: Prüfen, ob einer unserer Werte null ist
Modellierung der Daten
Alle unsere Werte sind definiert, großartig. Außerdem müssen wir die Daten kennzeichnen. Erinnern Sie sich, dass unser Ziel die zukünftigen Werte von Theta und r ist.
LOOK_AHEAD = 1 data['R Target'] = data['R'].shift(-LOOK_AHEAD) data['Theta Target'] = data['Theta'].shift(-LOOK_AHEAD) data.dropna(inplace=True) data.reset_index(drop=True,inplace=True)
Denken Sie daran, dass wir die Daten der letzten 2 Jahre löschen wollen, damit wir sie als Test für unsere Anwendung verwenden können.
#Let's entirely drop off the last 2 years of data _ = data.iloc[-((365 * 2) + 230):,:] data = data.iloc[:-((365 * 2) + 230),:] data
Abb. 8: Unser Datensatz nach der Herausnahme der Daten der letzten 2 Jahre
Nun wollen wir den Computer mit den vorliegenden Daten trainieren. Wir werden einen Gradient-Boosted-Baum als Modell der Wahl verwenden, weil er besonders gut Interaktionseffekte lernen kann.
from sklearn.ensemble import GradientBoostingRegressor from sklearn.model_selection import train_test_split,TimeSeriesSplit,cross_val_score
Definieren wir nun unser geteiltes Zeitreihenobjekt
tscv = TimeSeriesSplit(n_splits=5,gap=LOOK_AHEAD)
und die Inputs und Ziele.
X = data.columns[1:-5] y = data.columns[-2:]
Wir unterteilen die Daten in Trainings- und eine Testanteil.
train , test = train_test_split(data,test_size=0.5,shuffle=False)
Jetzt bereiten wir das Training und die Tests mit den aufgeteilten Daten.
train_X = train.loc[:,X] train_y = train.loc[:,y] test_X = test.loc[:,X] test_y = test.loc[:,y]
Die Aufteilung von Training und Test muss standardisiert werden.
mean_scores = train_X.mean() std_scores = train_X.std()
Skalierung der Daten.
train_X = ((train_X - mean_scores) / std_scores) test_X = ((test_X - mean_scores) / std_scores)
Initialisieren des Modells.
model = GradientBoostingRegressor()
Vorbereiten einer Tabelle zur Speicherung der Ergebnisse.
results = pd.DataFrame(index=["Train","Test"],columns=["GBR"])
Passen wir das Modell an, um R vorherzusagen.
results.iloc[0,0] = np.mean(np.abs(cross_val_score(model,train_X,train_y["R Target"],cv=tscv))) results.iloc[1,0] = np.mean(np.abs(cross_val_score(model,test_X,test_y["R Target"],cv=tscv))) results
GBR | |
---|---|
Training | 0.76686 |
Test | 0.89129 |
Passen wir das Modell zur Vorhersage von Theta an.
results.iloc[0,0] = np.mean(np.abs(cross_val_score(model,train_X,train_y["Theta Target"],cv=tscv))) results.iloc[1,0] = np.mean(np.abs(cross_val_score(model,test_X,test_y["Theta Target"],cv=tscv))) results
GBR | |
---|---|
Training | 0.368166 |
Test | 0.110126 |
Exportieren nach ONNX
Laden wir die benötigten Bibliotheken
import onnx import skl2onnx from skl2onnx.common.data_types import FloatTensorType
und initialisieren die Modelle.
r_model = GradientBoostingRegressor() theta_model = GradientBoostingRegressor()
Speichern wir die globalen Standardisierungswerte für den gesamten Datensatz im CSV-Format.
mean_scores = data.loc[:,X].mean() std_scores = data.loc[:,X].std() mean_scores.to_csv("EURUSD Polar Coordinates Mean.csv") std_scores.to_csv("EURUSD Polar Coordinates Std.csv")
Wir normalisieren den gesamten Datensatz
data[X] = ((data.loc[:,X] - mean_scores) / std_scores)
und passen die Modelle an die skalierten Daten an.
r_model.fit(data.loc[:,X],data.loc[:,'R Target']) theta_model.fit(data.loc[:,X],data.loc[:,'Theta Target'])
Definieren wir die Eingabeform.
initial_types = [("float_input",FloatTensorType([1,len(X)]))]
Wir bereiten die zu speichernden ONNX-Prototypen vor
r_model_proto = skl2onnx.convert_sklearn(r_model,initial_types=initial_types,target_opset=12) theta_model_proto = skl2onnx.convert_sklearn(theta_model,initial_types=initial_types,target_opset=12)
und speichern die ONNX-Dateien.
onnx.save(r_model_proto,"EURUSD D1 R Model.onnx") onnx.save(theta_model_proto,"EURUSD D1 Theta Model.onnx")
Erste Schritte in MQL5
Wir sind nun bereit, unsere Handelsanwendung zu erstellen.
//+------------------------------------------------------------------+ //| EURUSD Polar EA.mq5 | //| Gamuchirai Ndawana | //| https://www.mql5.com/en/users/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| System Constants | //+------------------------------------------------------------------+ #define ONNX_INPUTS 9 //The total number of inputs for our onnx model #define ONNX_OUTPUTS 1 //The total number of outputs for our onnx model #define TF_1 PERIOD_D1 //The system's primary time frame #define TRADING_VOLUME 0.1 //The system's trading volume
Wir laden die ONNX-Modelle als Systemressourcen
//+------------------------------------------------------------------+ //| System Resources | //+------------------------------------------------------------------+ #resource "\\Files\\EURUSD D1 R Model.onnx" as uchar r_model_buffer[]; #resource "\\Files\\EURUSD D1 Theta Model.onnx" as uchar theta_model_buffer[];
und definieren unsere globalen Variablen. Wir werden einige dieser Variablen verwenden, um unsere Daten zu standardisieren, die Prognosen unseres ONNX-Modells zu speichern und vieles mehr.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ double mean_values[] = {1.1884188643844635,1.1920754015799868,1.1847545720868993,1.1883860236998025,1.6806588395310122,0.7853854898794739,-1.1883860236998025,1.1884188643844635,1.1884188643844635}; double std_values[] = {0.09123896995032886,0.09116171300874902,0.0912656190371797,0.09120265318308786,0.1289537623737421,0.0021932437785043796,0.09120265318308786,0.09123896995032886,0.09123896995032886}; double current_r,current_theta; long r_model,theta_model; vectorf r_model_output = vectorf::Zeros(ONNX_OUTPUTS); vectorf theta_model_output = vectorf::Zeros(ONNX_OUTPUTS); double bid,ask; int ma_o_handler,ma_c_handler,state; double ma_o_buffer[],ma_c_buffer[];
Wir laden die Handelsbibliothek.
//+------------------------------------------------------------------+ //| Library | //+------------------------------------------------------------------+ #include <Trade/Trade.mqh> CTrade Trade;
Unsere Handelsanwendung besteht hauptsächlich aus Ereignisbehandlungen. In jeder Phase des Lebenszyklus der Anwendung werden wir spezielle Funktionen aufrufen, um Aufgaben auszuführen, die unserem Ziel zu diesem Zeitpunkt entsprechen. Während der Initialisierung richten wir also unsere technischen Indikatoren ein, und wenn neue Kurse verfügbar sind, aktualisieren wir unsere Messwerte anhand dieser Indikatoren.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- if(!setup()) { Comment("Failed To Load Corretly"); return(INIT_FAILED); } Comment("Started"); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- OnnxRelease(r_model); OnnxRelease(theta_model); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- update(); } //+------------------------------------------------------------------+
Eine Vorhersage von unserem ONNX-Modell erhalten. Beachten Sie, dass wir alle Modelleingaben in den Typ float umwandeln, um sicherzustellen, dass das Modell Daten im richtigen Format und in der richtigen Größe erhält.
//+------------------------------------------------------------------+ //| Get a prediction from our models | //+------------------------------------------------------------------+ void get_model_prediction(void) { //Define theta and r double o = iOpen(_Symbol,PERIOD_CURRENT,1); double h = iHigh(_Symbol,PERIOD_CURRENT,1); double l = iLow(_Symbol,PERIOD_CURRENT,1); double c = iClose(_Symbol,PERIOD_CURRENT,1); current_r = MathSqrt(MathPow(o,2) + MathPow(c,2)); current_theta = MathArctan2(c,o); vectorf model_inputs = { (float) o, (float) h, (float) l, (float) c, (float) current_r, (float) current_theta, (float)(current_r * (-(MathSin(current_theta)))), (float)(current_r * MathCos(current_theta)), (float)(1/MathPow(MathCos(current_theta),2)) }; //Standardize the model inputs for(int i = 0; i < ONNX_INPUTS;i++) { model_inputs[i] = (float)((model_inputs[i] - mean_values[i]) / std_values[i]); } //Get a prediction from our model OnnxRun(r_model,ONNX_DATA_TYPE_FLOAT,model_inputs,r_model_output); OnnxRun(theta_model,ONNX_DATA_TYPE_FLOAT,model_inputs,theta_model_output); //Give our prediction Comment(StringFormat("R: %f \nTheta: %f\nR Forecast: %f\nTheta Forecast: %f",current_r,current_theta,r_model_output[0],theta_model_output[0])); }
Aktualisieren wir das System, wenn neue Preise angeboten werden.
//+------------------------------------------------------------------+ //| Update system state | //+------------------------------------------------------------------+ void update(void) { static datetime time_stamp; datetime current_time = iTime(_Symbol,TF_1,0); bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(current_time != time_stamp) { CopyBuffer(ma_o_handler,0,0,1,ma_o_buffer); CopyBuffer(ma_c_handler,0,0,1,ma_c_buffer); time_stamp = current_time; get_model_prediction(); manage_account(); if(PositionsTotal() == 0) get_signal(); } }
Kontoverwaltung. Wenn wir bei einem Handel Geld verlieren, würden wir ihn lieber sofort schließen. Andernfalls, wenn wir einen Handel eingegangen sind, aber die gleitenden Durchschnitte sich in einer Weise kreuzen, die unser Vertrauen in diese Position untergräbt, werden wir sie sofort schließen.
//+------------------------------------------------------------------+ //| Manage the open positions we have in the market | //+------------------------------------------------------------------+ void manage_account() { if(AccountInfoDouble(ACCOUNT_BALANCE) < AccountInfoDouble(ACCOUNT_EQUITY)) { while(PositionsTotal() > 0) Trade.PositionClose(Symbol()); } if(state == 1) { if(ma_c_buffer[0] < ma_o_buffer[0]) Trade.PositionClose(Symbol()); } if(state == -1) { if(ma_c_buffer[0] > ma_o_buffer[0]) Trade.PositionClose(Symbol()); } }
Einrichten von Systemvariablen, wie z.B. technische Indikatoren und ONNX-Modelle.
//+------------------------------------------------------------------+ //| Setup system variables | //+------------------------------------------------------------------+ bool setup(void) { ma_o_handler = iMA(Symbol(),TF_1,50,0,MODE_SMA,PRICE_CLOSE); ma_c_handler = iMA(Symbol(),TF_1,10,0,MODE_SMA,PRICE_CLOSE); r_model = OnnxCreateFromBuffer(r_model_buffer,ONNX_DEFAULT); theta_model = OnnxCreateFromBuffer(theta_model_buffer,ONNX_DEFAULT); if(r_model == INVALID_HANDLE) return(false); if(theta_model == INVALID_HANDLE) return(false); ulong input_shape[] = {1,ONNX_INPUTS}; ulong output_shape[] = {1,ONNX_OUTPUTS}; if(!OnnxSetInputShape(r_model,0,input_shape)) return(false); if(!OnnxSetInputShape(theta_model,0,input_shape)) return(false); if(!OnnxSetOutputShape(r_model,0,output_shape)) return(false); if(!OnnxSetOutputShape(theta_model,0,output_shape)) return(false); return(true); }
Prüfen Sie, ob wir ein Handelssignal haben. Wir werden vor allem die Ausrichtung unserer Strategie des Kreuzens der gleitenden Durchschnitte überprüfen. Danach werden wir unsere Erwartungen, die uns die ONNX-Modelle vermitteln, überprüfen. Wenn also der gleitende Durchschnitt eine Abwärtsstimmung anzeigt, aber unsere ONNX-Modelle von R und Theta eine Aufwärtsstimmung anzeigen, werden wir keine Positionen eröffnen, bis die beiden Systeme übereinstimmen.
//+------------------------------------------------------------------+ //| Check if we have a trading signal | //+------------------------------------------------------------------+ void get_signal(void) { if(ma_c_buffer[0] > ma_o_buffer[0]) { if((r_model_output[0] < current_r) && (theta_model_output[0] < current_theta)) { return; } if((r_model_output[0] > current_r) && (theta_model_output[0] > current_theta)) { Trade.Buy(TRADING_VOLUME * 2,Symbol(),ask,0,0); Trade.Buy(TRADING_VOLUME * 2,Symbol(),ask,0,0); state = 1; return; } Trade.Buy(TRADING_VOLUME,Symbol(),ask,0,0); state = 1; return; } if(ma_c_buffer[0] < ma_o_buffer[0]) { if((r_model_output[0] > current_r) && (theta_model_output[0] > current_theta)) { return; } if((r_model_output[0] < current_r) && (theta_model_output[0] < current_theta)) { Trade.Sell(TRADING_VOLUME * 2,Symbol(),bid,0,0); Trade.Sell(TRADING_VOLUME * 2,Symbol(),bid,0,0); state = -1; return; } Trade.Sell(TRADING_VOLUME,Symbol(),bid,0,0); state = -1; return; } }
Undefinierte Systemkonstanten, die wir nicht verwenden.
//+------------------------------------------------------------------+ //| Undefine system variables we don't need | //+------------------------------------------------------------------+ #undef ONNX_INPUTS #undef ONNX_OUTPUTS #undef TF_1 //+------------------------------------------------------------------+
Testen unseres Systems
Beginnen wir nun mit dem Testen unseres Systems. Erinnern Sie sich daran, dass wir bei der Datenaufbereitung die Daten vom 1. Januar 2022 weggelassen haben, damit unser Backtest zeigt, wie gut unsere Strategie bei Daten abschneiden könnte, die sie noch nie gesehen hat.
Abb. 9: Unsere Einstellungen für den Backtest
Nun geben wir die anfänglichen Kontoeinstellungen an.
Abb. 10: Unsere zweite Gruppe von Einstellungen für unseren entscheidenden Backtest über Daten, die nicht in der Stichprobe enthalten sind
Wir können die Kapitalkurve der von unserem neuen System erzeugten Handelssignale beobachten. Unsere Strategie begann mit einem Guthaben von 5000 $ und endete mit einem Guthaben von etwa 7000 $. Das sind gute Ergebnisse und ermutigen uns, unsere Strategie weiter zu erforschen und weiterzuentwickeln.
Abb. 11: Die Backtest-Ergebnisse, die wir durch den Handel mit den von unseren Winkeltransformationen erzeugten Signalen erhalten haben
Lassen Sie uns unsere Ergebnisse im Detail analysieren. Unsere Strategie hatte eine Genauigkeit von 88 % bei Daten aus der Stichprobe. Dies sind ermutigende Informationen, die dem Leser eine gute Ausgangsbasis für die Entwicklung eigener Anwendungen bieten, indem sie die Funktionsweise und die Möglichkeiten des MetaTrader 5 Terminals erweitern, die wir in dieser Anwendung demonstriert haben. Oder der Leser kann stattdessen unseren Rahmen als Leitfaden verwenden und unsere Handelsstrategie durch seine eigene ersetzen.
Abb. 12: Analyse der Ergebnisse unseres Backtests im Detail
Schlussfolgerung
Die Lösung, die wir Ihnen heute vorgestellt haben, hat Ihnen gezeigt, wie Sie den potenziellen Vorteil nutzen können, der sich aus der sinnvollen Umwandlung von Änderungen des Preisniveaus in Änderungen der Blickwinkel ergibt. Unser Verfahren bietet Ihnen einen einfachen und eleganten Rahmen, um dieses Problem anzugehen, sodass Sie die beste Mischung aus Handelslogik und mathematischer Logik erhalten. Im Gegensatz zu gelegentlichen Marktteilnehmern, die versuchen müssen, den Preis direkt vorherzusagen, haben Sie jetzt alternative Ziele, die genauso nützlich sind wie die Kenntnis des Preises selbst und gleichzeitig leichter zu prognostizieren sind als der Preis selbst.
Angehängte Datei | Beschreibung |
---|---|
Polar Fetch Data | Unser angepasstes Skript zum Abrufen unserer Preisdaten und deren Umwandlung in Polarkoordinaten. |
EURUSD Polar EA | Der Expert Advisor, der mit den Signalen handelt, die aus den festgestellten Winkelveränderungen generiert werden. |
EURUSD D1 R Model | Das ONNX-Modell, das für die Vorhersage unserer zukünftigen Werte von R |
EURUSD D1 Theta Model | Das ONNX-Modell, das für die Vorhersage unserer zukünftigen Werte von Theta |
EURUSD Polar Coordinates | Das Jupyter Notebook, das wir zur Analyse der Daten verwendet haben, die wir mit unserem MQL5-Skript abgerufen haben |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/17085






- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.