English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
preview
Ein Beispiel für die Zusammenstellung von ONNX-Modellen in MQL5

Ein Beispiel für die Zusammenstellung von ONNX-Modellen in MQL5

MetaTrader 5Maschinelles Lernen |
424 7
MetaQuotes
MetaQuotes

Einführung

Für einen stabilen Handel ist es in der Regel empfehlenswert, sowohl die gehandelten Instrumente als auch die Handelsstrategien zu diversifizieren. Das Gleiche gilt für Modelle des maschinellen Lernens: Es ist leichter, mehrere einfachere Modelle zu erstellen als ein komplexes. Es kann jedoch schwierig sein, diese Modelle zu einem ONNX-Modell zusammenzufügen.

Es ist jedoch möglich, mehrere trainierte ONNX-Modelle in einem MQL5-Programm zu kombinieren. In diesem Artikel betrachten wir eines aus so einem Ensemble, den sogenannten Abstimmungsklassifikator. Wir werden Ihnen zeigen, wie einfach es ist, ein solches Ensemble zu realisieren.


Modelle für das Projekt

Für unser Beispiel verwenden wir zwei einfache Modelle: ein Regressionsmodell zur Preisvorhersage und ein Klassifikationsmodell zur Vorhersage von Preisbewegungen. Der Hauptunterschied zwischen den Modellen besteht darin, dass die Regression das Ausmaß vorhersagt, während die Klassifikation die Klasse vorhersagt.

Das erste Modell ist die Regression.

Es wird mit den Daten des EURUSD D1 von 2003 bis Ende 2022 trainiert. Das Training wird mit einer Serie von 10 OHLC-Preisen durchgeführt. Um die Trainierbarkeit des Modells zu verbessern, normalisieren wir die Preise und teilen den Durchschnittspreis der Reihe durch die Standardabweichung der Reihe. So legen wir eine Reihe in einen bestimmten Bereich mit einem Mittelwert von 0 und einer Streuung von 1, was die Konvergenz beim Training verbessert.

Infolgedessen sollte das Modell den Schlusskurs für den nächsten Tag vorhersagen.

Das Modell ist sehr einfach. Sie wird hier nur zu Demonstrationszwecken bereitgestellt.

# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

from datetime import datetime
import MetaTrader5 as mt5
import tensorflow as tf
import numpy as np
import pandas as pd
import tf2onnx
from sklearn.model_selection import train_test_split
from tqdm import tqdm
from sys import argv

if not mt5.initialize():
    print("initialize() failed, error code =",mt5.last_error())
    quit()

# we will save generated onnx-file near the our script
data_path=argv[0]
last_index=data_path.rfind("\\")+1
data_path=data_path[0:last_index]
print("data path to save onnx model",data_path)

# input parameters
inp_model_name = "model.eurusd.D1.10.onnx"
inp_history_size = 10
inp_start_date = datetime(2003, 1, 1, 0)
inp_end_date = datetime(2023, 1, 1, 0)

# get data from client terminal
eurusd_rates = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_D1, inp_start_date, inp_end_date)
df = pd.DataFrame(eurusd_rates)


#
# collect dataset subroutine
#
def collect_dataset(df: pd.DataFrame, history_size: int):
    """
    Collect dataset for the following regression problem:
    - input: history_size consecutive H1 bars;
    - output: close price for the next bar.

    :param df: D1 bars for a range of dates
    :param history_size: how many bars should be considered for making a prediction
    :return: features and labels
    """
    n = len(df)
    xs = []
    ys = []
    for i in tqdm(range(n - history_size)):
        w = df.iloc[i: i + history_size + 1]

        x = w[['open', 'high', 'low', 'close']].iloc[:-1].values
        y = w.iloc[-1]['close']
        xs.append(x)
        ys.append(y)

    X = np.array(xs)
    y = np.array(ys)
    return X, y
###


# get prices
X, y = collect_dataset(df, history_size=inp_history_size)

# normalize prices
m = X.mean(axis=1, keepdims=True)
s = X.std(axis=1, keepdims=True)
X_norm = (X - m) / s
y_norm = (y - m[:, 0, 3]) / s[:, 0, 3]

# split data to train and test sets
X_train, X_test, y_train, y_test = train_test_split(X_norm, y_norm, test_size=0.2, random_state=0)

# define model
model = tf.keras.Sequential([
    tf.keras.layers.LSTM(64, input_shape=(inp_history_size, 4)),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dropout(0.1),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dropout(0.1),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(1)
])

model.compile(optimizer='adam', loss='mse', metrics=['mae'])

# model training for 50 epochs
lr_reduction = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, min_lr=0.000001)
history = model.fit(X_train, y_train, epochs=50, verbose=2, validation_split=0.15, callbacks=[lr_reduction])

# model evaluation
test_loss, test_mae = model.evaluate(X_test, y_test)
print(f"test_loss={test_loss:.3f}")
print(f"test_mae={test_mae:.3f}")

# save model to onnx
output_path = data_path+inp_model_name
onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path)
print(f"saved model to {output_path}")

# finish
mt5.shutdown()
Es wird davon ausgegangen, dass unser Regressionsmodell ausgeführt wird und der sich daraus ergebende vorhergesagte Preis in die folgenden Klassen umgewandelt werden sollte: Preis geht nach unten, Preis ändert sich nicht, Preis geht nach oben. Dies ist erforderlich, um den Wahlklassifikator zu organisieren.

Das zweite Modell ist das Klassifizierungsmodell.

Es wird auf EURUSD D1 von 2010 bis Ende 2022 trainiert. Das Training wird mit einer Serie von 63 Schlusskursen durchgeführt. Bei der Ausgabe muss eine von drei Klassen definiert werden: Der Preis wird sinken, der Preis wird innerhalb von 10 Punkten bleiben oder der Preis wird steigen. Wegen der zweiten Klasse mussten wir das Modell mit Daten aus dem Jahr 2010 trainieren. Davor, im Jahr 2009, wechselten die Märkte von vierstelliger zu fünfstelliger Genauigkeit. So wurden aus einem alten Punkt zehn neue Punkte.

Wie im vorherigen Modell ist der Preis normalisiert. Die Normalisierung ist dieselbe: Wir teilen die Abweichung vom Durchschnittspreis in der Reihe durch die Standardabweichung in der Reihe. Die Idee dieses Modells wurde in dem Artikel „Finanzielle Zeitreihenprognose mit MLP in Keras“ (auf Russisch) beschrieben. Auch dieses Modell ist nur für Demonstrationszwecke gedacht.

# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com
#
# Classification model
# 0,0,1 - predict price down
# 0,1,0 - predict price same
# 1,0,0 - predict price up
#

from datetime import datetime
import MetaTrader5 as mt5
import tensorflow as tf
import numpy as np
import pandas as pd
import tf2onnx
from sklearn.model_selection import train_test_split
from tqdm import tqdm
from keras.models import Sequential
from keras.layers import Dense, Activation,Dropout, BatchNormalization, LeakyReLU
from keras.optimizers import SGD
from keras import regularizers
from sys import argv

# initialize MetaTrader 5 client terminal
if not mt5.initialize():
    print("initialize() failed, error code =",mt5.last_error())
    quit()

# we will save the generated onnx-file near the our script
data_path=argv[0]
last_index=data_path.rfind("\\")+1
data_path=data_path[0:last_index]
print("data path to save onnx model",data_path)

# input parameters
inp_model_name = "model.eurusd.D1.63.onnx"
inp_history_size = 63
inp_start_date = datetime(2010, 1, 1, 0)
inp_end_date = datetime(2023, 1, 1, 0)

# get data from the client terminal
eurusd_rates = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_D1, inp_start_date, inp_end_date)
df = pd.DataFrame(eurusd_rates)


#
# collect dataset subroutine
#
def collect_dataset(df: pd.DataFrame, history_size: int):
    """
    Collect dataset for the following regression problem:
    - input: history_size consecutive H1 bars;
    - output: close price for the next bar.

    :param df: H1 bars for a range of dates
    :param history_size: how many bars should be considered for making a prediction
    :return: features and labels
    """
    n = len(df)
    xs = []
    ys = []
    for i in tqdm(range(n - history_size)):
        w = df.iloc[i: i + history_size + 1]
        x = w[['close']].iloc[:-1].values

        delta = x[-1] - w.iloc[-1]['close']
        if np.abs(delta)<=0.0001:
           y = 0, 1, 0
        else:
           if delta<0:
              y = 1, 0, 0
           else:
              y = 0, 0, 1

        xs.append(x)
        ys.append(y)

    X = np.array(xs)
    Y = np.array(ys)
    return X, Y
###


# get prices
X, Y = collect_dataset(df, history_size=inp_history_size)

# normalize prices
m = X.mean(axis=1, keepdims=True)
s = X.std(axis=1, keepdims=True)
X_norm = (X - m) / s

# split data to train and test sets
X_train, X_test, Y_train, Y_test = train_test_split(X_norm, Y, test_size=0.1, random_state=0)

# define model
model = Sequential()
model.add(Dense(64, input_dim=inp_history_size, activity_regularizer=regularizers.l2(0.01)))
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Dropout(0.3))
model.add(Dense(16, activity_regularizer=regularizers.l2(0.01)))
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Dense(3))
model.add(Activation('softmax'))

opt = SGD(learning_rate=0.01, momentum=0.9)
model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])

# model training for 300 epochs
lr_reduction = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.9, patience=5, min_lr=0.00001)
history = model.fit(X_train, Y_train, epochs=300, validation_data=(X_test, Y_test), shuffle = True, batch_size=128, verbose=2, callbacks=[lr_reduction])

# model evaluation
test_loss, test_accuracy = model.evaluate(X_test, Y_test)
print(f"test_loss={test_loss:.3f}")
print(f"test_accuracy={test_accuracy:.3f}")

# save model to onnx
output_path = data_path+inp_model_name
onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path)
print(f"saved model to {output_path}")

# finish
mt5.shutdown()
Die Modelle wurden mit Daten bis Ende 2022 trainiert, sodass noch genügend Zeit blieb, um ihre Funktionsweise im Strategietester zu demonstrieren.


Ein Ensemble von ONNX-Modellen im MQL5 Expert Advisor

Nachfolgend finden Sie einen einfachen Expert Advisor, der die Möglichkeiten von Modell-Ensembles demonstriert. Die Hauptprinzipien der Verwendung von ONNX-Modellen in MQL5 wurden im zweiten Teil des vorherigen Artikels beschrieben.

Forward-Deklarationen und Definitionen

#include <Trade\Trade.mqh>

input double InpLots = 1.0;              // Lots amount to open position

#resource "Python/model.eurusd.D1.10.onnx" as uchar ExtModel1[]
#resource "Python/model.eurusd.D1.63.onnx" as uchar ExtModel2[]

#define SAMPLE_SIZE1 10
#define SAMPLE_SIZE2 63

long     ExtHandle1=INVALID_HANDLE;
long     ExtHandle2=INVALID_HANDLE;
int      ExtPredictedClass1=-1;
int      ExtPredictedClass2=-1;
int      ExtPredictedClass=-1;
datetime ExtNextBar=0;
CTrade   ExtTrade;

//--- price movement prediction
#define PRICE_UP   0
#define PRICE_SAME 1
#define PRICE_DOWN 2

OnInit-Funktion

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(_Symbol!="EURUSD" || _Period!=PERIOD_D1)
     {
      Print("model must work with EURUSD,D1");
      return(INIT_FAILED);
     }

//--- create first model from static buffer
   ExtHandle1=OnnxCreateFromBuffer(ExtModel1,ONNX_DEFAULT);
   if(ExtHandle1==INVALID_HANDLE)
     {
      Print("First model OnnxCreateFromBuffer error ",GetLastError());
      return(INIT_FAILED);
     }
//--- since not all sizes defined in the input tensor we must set them explicitly
//--- first index - batch size, second index - series size, third index - number of series (OHLC)
   const long input_shape1[] = {1,SAMPLE_SIZE1,4};
   if(!OnnxSetInputShape(ExtHandle1,0,input_shape1))
     {
      Print("First model OnnxSetInputShape error ",GetLastError());
      return(INIT_FAILED);
     }
   
//--- since not all sizes defined in the output tensor we must set them explicitly
//--- first index - batch size, must match the batch size of the input tensor
//--- second index - number of predicted prices (we only predict Close)
   const long output_shape1[] = {1,1};
   if(!OnnxSetOutputShape(ExtHandle1,0,output_shape1))
     {
      Print("First model OnnxSetOutputShape error ",GetLastError());
      return(INIT_FAILED);
     }

//--- create second model from static buffer
   ExtHandle2=OnnxCreateFromBuffer(ExtModel2,ONNX_DEFAULT);
   if(ExtHandle2==INVALID_HANDLE)
     {
      Print("Second model OnnxCreateFromBuffer error ",GetLastError());
      return(INIT_FAILED);
     }
  
//--- since not all sizes defined in the input tensor we must set them explicitly
//--- first index - batch size, second index - series size
   const long input_shape2[] = {1,SAMPLE_SIZE2};
   if(!OnnxSetInputShape(ExtHandle2,0,input_shape2))
     {
      Print("Second model OnnxSetInputShape error ",GetLastError());
      return(INIT_FAILED);
     }

//--- since not all sizes defined in the output tensor we must set them explicitly
//--- first index - batch size, must match the batch size of the input tensor
//--- second index - number of classes (up, same or down)
   const long output_shape2[] = {1,3};
   if(!OnnxSetOutputShape(ExtHandle2,0,output_shape2))
     {
      Print("Second model OnnxSetOutputShape error ",GetLastError());
      return(INIT_FAILED);
     }
//--- ok
   return(INIT_SUCCEEDED);
  }

Wir werden es nur mit EURUSD, D1 ausführen. Das liegt daran, dass wir die Daten der aktuellen Symbolperiode verwenden, während die Modelle mit Tagespreisen trainiert werden.

Die Modelle sind im Expert Advisor als Ressourcen enthalten.

Es ist wichtig, die Formen der Eingangs- und Ausgangsdaten explizit zu definieren.

Die Funktion OnTick

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- check new bar
   if(TimeCurrent()<ExtNextBar)
      return;
//--- set next bar time
   ExtNextBar=TimeCurrent();
   ExtNextBar-=ExtNextBar%PeriodSeconds();
   ExtNextBar+=PeriodSeconds();

//--- predict price movement
   Predict();
//--- check trading according to prediction
   if(ExtPredictedClass>=0)
      if(PositionSelect(_Symbol))
         CheckForClose();
      else
         CheckForOpen();
  }

Alle Handelsoperationen werden nur zu Beginn des Tages durchgeführt.

Funktion der Vorhersage

//+------------------------------------------------------------------+
//| Voting classification                                            |
//+------------------------------------------------------------------+
void Predict(void)
  {
//--- evaluate first model
   ExtPredictedClass1=PredictPrice(ExtHandle1,SAMPLE_SIZE1);
//--- evaluate second model
   ExtPredictedClass2=PredictPriceMovement(ExtHandle2,SAMPLE_SIZE2);
//--- vote
   if(ExtPredictedClass1==ExtPredictedClass2)
      ExtPredictedClass=ExtPredictedClass1;
   else
      ExtPredictedClass=-1;
  }

Eine Klasse gilt als ausgewählt, wenn beide Modelle für die gleiche Klasse gestimmt haben. Dies ist eine Mehrheitsentscheidung. Und da es nur zwei Modelle im Ensemble gibt, bedeutet die Mehrheitsentscheidung „einstimmig“.

Vorhersage des Tagesschlusskurses aus 10 vorherigen OHLC-Kursen

//+------------------------------------------------------------------+
//| Predict next price (first model)                                 |
//+------------------------------------------------------------------+
int PredictPrice(const long handle,const int sample_size)
  {
   static matrixf input_data(sample_size,4);    // matrix for prepared input data
   static vectorf output_data(1);               // vector to get result
   static matrix  mm(sample_size,4);            // matrix of horizontal vectors Mean
   static matrix  ms(sample_size,4);            // matrix of horizontal vectors Std
   static matrix  x_norm(sample_size,4);        // matrix for prices normalize

//--- prepare input data
   matrix rates;
//--- request last bars
   if(!rates.CopyRates(_Symbol,_Period,COPY_RATES_OHLC,1,sample_size))
      return(-1);
//--- get series Mean
   vector m=rates.Mean(1);
//--- get series Std
   vector s=rates.Std(1);
//--- prepare matrices for prices normalization
   for(int i=0; i<sample_size; i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
//--- the input of the model must be a set of vertical OHLC vectors
   x_norm=rates.Transpose();
//--- normalize prices
   x_norm-=mm;
   x_norm/=ms;

//--- run the inference
   input_data.Assign(x_norm);
   if(!OnnxRun(handle,ONNX_NO_CONVERSION,input_data,output_data))
      return(-1);
//--- denormalize the price from the output value
   double predicted=output_data[0]*s[3]+m[3];
//--- classify predicted price movement
   int    predicted_class=-1;
   double delta=rates[3][sample_size-1]-predicted;
   if(fabs(delta)<=0.0001)
      predicted_class=PRICE_SAME;
   else
     {
      if(delta<0)
         predicted_class=PRICE_UP;
      else
         predicted_class=PRICE_DOWN;

     }

   return(predicted_class);
  }

Die Eingabedaten sollten nach denselben Regeln aufbereitet werden wie beim Training des Modells. Nach der Ausführung des Modells wird der resultierende Wert wieder in den Preis umgerechnet. Die Klasse wird auf der Grundlage der Differenz zwischen dem letzten Schlusskurs in der Serie und dem resultierenden Kurs berechnet.

Die Vorhersage der Kursentwicklung basiert auf einer Reihe von 63 täglichen Schlusskursen:

//+------------------------------------------------------------------+
//| Predict price movement (second model)                            |
//+------------------------------------------------------------------+
int PredictPriceMovement(const long handle,const int sample_size)
  {
   static vectorf input_data(sample_size);    // vector for prepared input data
   static vectorf output_data(3);             // vector to get result

//--- request last bars
   if(!input_data.CopyRates(_Symbol,_Period,COPY_RATES_CLOSE,1,sample_size))
      return(-1);
//--- get series Mean
   float m=input_data.Mean();
//--- get series Std
   float s=input_data.Std();
//--- normalize prices
   input_data-=m;
   input_data/=s;

//--- run the inference
   if(!OnnxRun(handle,ONNX_NO_CONVERSION,input_data,output_data))
      return(-1);
//--- evaluate prediction
   return(int(output_data.ArgMax()));
  }

Die Preise werden nach den gleichen Regeln wie im ersten Modell normalisiert. Dieses Mal ist der Code jedoch kompakter, da die Eingabe ein Vektor und keine Matrix ist. Die Klasse wird durch den Maximalwert der drei Wahrscheinlichkeiten ausgewählt.

Die Handelsstrategie ist einfach. Die Handelsgeschäfte werden zu Beginn eines jeden Tages durchgeführt. Wenn die Vorhersage lautet „der Preis wird steigen“, dann kaufen wir; wenn sie lautet „der Preis wird fallen“, dann verkaufen wir.

//+------------------------------------------------------------------+
//| Check for open position conditions                               |
//+------------------------------------------------------------------+
void CheckForOpen(void)
  {
   ENUM_ORDER_TYPE signal=WRONG_VALUE;
//--- check signals
   if(ExtPredictedClass==PRICE_DOWN)
      signal=ORDER_TYPE_SELL;    // sell condition
   else
     {
      if(ExtPredictedClass==PRICE_UP)
         signal=ORDER_TYPE_BUY;  // buy condition
     }

//--- open position if possible according to signal
   if(signal!=WRONG_VALUE && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
      ExtTrade.PositionOpen(_Symbol,signal,InpLots,
                            SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK),
                            0,0);
  }
//+------------------------------------------------------------------+
//| Check for close position conditions                              |
//+------------------------------------------------------------------+
void CheckForClose(void)
  {
   bool bsignal=false;
//--- position already selected before
   long type=PositionGetInteger(POSITION_TYPE);
//--- check signals
   if(type==POSITION_TYPE_BUY && ExtPredictedClass==PRICE_DOWN)
      bsignal=true;
   if(type==POSITION_TYPE_SELL && ExtPredictedClass==PRICE_UP)
      bsignal=true;

//--- close position if possible
   if(bsignal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
     {
      ExtTrade.PositionClose(_Symbol,3);
      //--- open opposite
      CheckForOpen();
     }
  }


Wir haben unser Modell mit den Daten bis Anfang 2023 trainiert. Legen wir also das Prüfintervall vom Jahresanfang an fest.

Test-Einstellungen

Hier ist das Testergebnis auf der Grundlage der Daten seit Anfang des Jahres.

Testergebnisse des Expert Advisors


Es wäre interessant, die Testergebnisse für jedes einzelne Modell zu kennen.

Dazu müssen wir den EA-Quellcode wie folgt ändern:

enum EnModels
  {
   USE_FIRST_MODEL,    // Use first model only
   USE_SECOND_MODEL,   // Use second model only
   USE_BOTH_MODELS     // Use both models
  };
input EnModels InpModels = USE_BOTH_MODELS;  // Models using
input double   InpLots   = 1.0;              // Lots amount to open position

...

//+------------------------------------------------------------------+
//| Voting classification                                            |
//+------------------------------------------------------------------+
void Predict(void)
  {
//--- evaluate first model
   if(InpModels==USE_BOTH_MODELS || InpModels==USE_FIRST_MODEL)
      ExtPredictedClass1=PredictPrice(ExtHandle1,SAMPLE_SIZE1);
//--- evaluate second model
   if(InpModels==USE_BOTH_MODELS || InpModels==USE_SECOND_MODEL)
      ExtPredictedClass2=PredictPriceMovement(ExtHandle2,SAMPLE_SIZE2);

//--- check predictions
   switch(InpModels)
     {
      case USE_FIRST_MODEL :
         ExtPredictedClass=ExtPredictedClass1;
         break;
      case USE_SECOND_MODEL :
         ExtPredictedClass=ExtPredictedClass2;
         break;
      case USE_BOTH_MODELS :
         if(ExtPredictedClass1==ExtPredictedClass2)
            ExtPredictedClass=ExtPredictedClass1;
         else
            ExtPredictedClass=-1;
     }
  }

Aktivieren Sie den Parameter „Use first model only“ (‚Nur das erste Modell verwenden‘).

Expert Advisor-Einstellungen zur Verwendung nur des ersten Modells

Die Ergebnisse des ersten Modelltests

Erste Modelltestergebnisse


Nun wollen wir das zweite Modell testen. Hier sind die Ergebnisse des zweiten Modelltests.

Zweite Modelltest-Ergebnisse

Das zweite Modell erwies sich als viel stärker als das erste. Die Ergebnisse bestätigen die Theorie, dass schwache Modelle zusammengesetzt werden müssen. In diesem Artikel ging es jedoch nicht um die Theorie des Bildens von Ensemble, sondern um die praktische Anwendung.

Wichtiger Hinweis: Bitte beachten Sie, dass die in diesem Artikel verwendeten Modelle nur zur Demonstration der Arbeit mit ONNX-Modellen in der Sprache MQL5 dienen. Der Expert Advisor ist nicht für den Handel auf realen Konten vorgesehen.



Schlussfolgerung

Wir haben ein sehr einfaches, aber anschauliches Beispiel für ein Ensemble aus zwei ONNX-Modellen vorgestellt. Die Anzahl der gleichzeitig verwendeten Modelle ist begrenzt und kann 256 Modelle nicht überschreiten. Aber auch die Verwendung von mehr als zwei Modellen erfordert einen anderen Ansatz bei der Programmierung von Expert Advisors, nämlich die objektorientierte Programmierung.

Aber das ist ein Thema für einen anderen Artikel.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/12433

Beigefügte Dateien |
MQL5.zip (105.08 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (7)
mysticsoul
mysticsoul | 12 Apr. 2023 in 12:49

Ich habe das gleiche Datum und die gleichen Einstellungen gewählt, aber die Ergebnisse sind unterschiedlich ausgefallen - weiß jemand, warum?

Testergebnisse des ersten Modells


Erste Ergebnisse der Modellprüfung


Testergebnisse des zweiten Modells


Ergebnisse der zweiten Modellprüfung
Slava
Slava | 12 Apr. 2023 in 15:06
mysticsoul #: Ich habe dasselbe Datum und dieselben Einstellungen gewählt, aber die Ergebnisse sind unterschiedlich ausgefallen, weiß jemand warum?

Es könnte daran liegen, dass Ihr Handelsserver nicht MetaQuotes-Demo ist.

Rasoul Mojtahedzadeh
Rasoul Mojtahedzadeh | 12 Apr. 2023 in 23:04
mysticsoul #:

Ich habe das gleiche Datum und die gleichen Einstellungen gewählt, aber die Ergebnisse sind unterschiedlich ausgefallen - weiß jemand, warum?

Ergebnisse des ersten Modelltests

Ergebnisse des zweiten Modelltests
Es könnte an einem Unterschied in der Initialisierung der Modelle liegen. Leider verwenden sie nicht die Technik des "Seeding Everything", um ihre Ergebnisse reproduzierbar zu machen.
linfo2
linfo2 | 13 Apr. 2023 in 20:33

Erstens, vielen Dank für die Zusammenstellung, es ist schön, in verschiedene Richtungen zu schauen. Es ist einfach zu folgen und gut zusammengesetzt.

Für mich erhalte ich ähnliche Erfolgsraten und eine etwas geringere Anzahl von Trades mit dem Demokonto, aber wenn ich das Meta Trader Demokonto benutze. Mit meinem Handelskonto wird nur einmal gehandelt. Ich nehme an, dass es an der Zeitzone des Brokers liegt, mein Broker ist in Australien (GMT+10). Die erste Transaktion vom Demokonto ist; Core 1 2023.01.02 07:02:00 deal #2 sell 1 EURUSD at 1.07016 done(based on order #2)

Die erste Transaktion von My Broker Australia (GMT+10) ist; Core 1 2023.01.03 00:00:00 failed market sell 1 EURUSD [Market closed] und nicht genau sicher, wie dies zu lösen. Möglicherweise ist das gesamte Modell zeitzonenabhängig. Wenn das der Fall wäre, sollten die Zeiten in ganzen Stunden angegeben werden... aber wie wird die Starttransaktion 2023.01.02 07:02:00 zu 2023.01.03 00:00:00?

Ich würde mich über jeden Vorschlag zur Ursache dieses Problems freuen.



Too Chee Ng
Too Chee Ng | 19 Nov. 2023 in 19:28
Slava #:
Handelsaufschlag

Auch hier gelingt es mir, sehr ähnliche Ergebnisse mit den ursprünglichen onnx-Dateien auf meinem MetaQuates-Demokonto zu reproduzieren.

Dann schaffe ich es, die Python-MLs bis zum Ende zu trainieren; allerdings mit folgenden Warnungen/Fehlern, die ignoriert werden können:

D:\MT5 Demo1\MQL5\Experts\article_12433\Python>python ONNX.eurusd.D1.10.Training.py
2023-11-19 18:07:38.169418: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'cudart64_110.dll'; dlerror: cudart64_110.dll not found
2023-11-19 18:07:38.169664: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
data path to save onnx model
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 5187/5187 [00:00<00:00, 6068.93it/s]
2023-11-19 18:07:40.434910: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'nvcuda.dll'; dlerror: nvcuda.dll not found
2023-11-19 18:07:40.435070: W tensorflow/stream_executor/cuda/cuda_driver.cc:263] failed call to cuInit: UNKNOWN ERROR (303)
2023-11-19 18:07:40.437138: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:169] retrieving CUDA diagnostic information for host: WIN-SSPXX7BO0B0
2023-11-19 18:07:40.437323: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:176] hostname: WIN-SSPXX7BO0B0
2023-11-19 18:07:40.437676: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX AVX2
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
Epoch 1/50
111/111 - 1s - loss: 1.6160 - mae: 0.9378 - val_loss: 2.7602 - val_mae: 1.3423 - lr: 0.0010 - 1s/epoch - 12ms/step
Epoch 2/50
111/111 - 0s - loss: 1.4932 - mae: 0.8952 - val_loss: 2.4339 - val_mae: 1.2412 - lr: 0.0010 - 287ms/epoch - 3ms/step
...

Beide ML-Skripte enden mit:

111/111 - 0 s - loss: 1.2812 - mae: 0.8145 - val_loss: 1.2598 - val_mae: 0.8142 - lr: 1.0000 e-06 - 366 ms/epoch - 3 ms/step
Epoch 50/50
111/111 - 0 s - loss: 1.3030 - mae: 0.8203 - val_loss: 1.2604 - val_mae: 0.8143 - lr: 1.0000 e-06 - 365 ms/epoch - 3 ms/step
33/33 [==============================] - 0 s 1 ms/step - loss: 1.1542 - mae: 0.7584
test_loss=1.154
test_mae=0.758
2023-11-19 18:07:57.480814: I tensorflow/core/grappler/devices.cc:66] Number of eligible GPUs (core count >= 8, compute capability >= 0.0): 0
2023-11-19 18:07:57.481315: I tensorflow/core/grappler/clusters/single_machine.cc:358] Starting new session
2023-11-19 18:07:57.560110: I tensorflow/core/grappler/devices.cc:66] Number of eligible GPUs (core count >= 8, compute capability >= 0.0): 0
2023-11-19 18:07:57.560380: I tensorflow/core/grappler/clusters/single_machine.cc:358] Starting new session
2023-11-19 18:07:57.611678: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:354] MLIR V1 optimization pass is not enabled
saved model to model.eurusd.D1.10.onnx
24/24 - 0 s - loss: 0.6618 - accuracy: 0.6736 - val_loss: 0.8993 - val_accuracy: 0.4759 - lr: 4.1746 e-05 - 37 ms/epoch - 2 ms/step
Epoch 300/300
24/24 - 0 s - loss: 0.6531 - accuracy: 0.6770 - val_loss: 0.8997 - val_accuracy: 0.4789 - lr: 4.1746 e-05 - 39 ms/epoch - 2 ms/step
11/11 [==============================] - 0 s 682 us/step - loss: 0.8997 - accuracy: 0.4789
test_loss=0.900
test_accuracy=0.479
2023-11-19 18:07:19.838160: I tensorflow/core/grappler/devices.cc:66] Number of eligible GPUs (core count >= 8, compute capability >= 0.0): 0
2023-11-19 18:07:19.838516: I tensorflow/core/grappler/clusters/single_machine.cc:358] Starting new session
2023-11-19 18:07:19.872285: I tensorflow/core/grappler/devices.cc:66] Number of eligible GPUs (core count >= 8, compute capability >= 0.0): 0
2023-11-19 18:07:19.872584: I tensorflow/core/grappler/clusters/single_machine.cc:358] Starting new session
saved model to model.eurusd.D1.63.onnx

Als nächstes kompiliere ich die ursprüngliche ONNX.Price.Prediction.2M.D1.mq5 neu, um die neuen MLs zu verwenden, die ich trainiert habe.

Die Backtest-Ergebnisse mit dem gleichen MetaQuates-Demo-Konto waren viel anders als das Original; das sieht nicht gut aus.

Ich würde wirklich gerne wissen, was schief gelaufen ist.
Vielen Dank.

Algorithmen zur Optimierung mit Populationen: Harmonie-Suche (HS) Algorithmen zur Optimierung mit Populationen: Harmonie-Suche (HS)
In diesem Artikel werde ich den leistungsstärksten Optimierungsalgorithmus untersuchen und testen - die Harmonie-Suche (HS), inspiriert durch den Prozess der Suche nach der perfekten Klangharmonie. Welcher Algorithmus ist nun der führende in unserer Bewertung?
Backpropagation von Neuronalen Netze mit MQL5-Matrizen Backpropagation von Neuronalen Netze mit MQL5-Matrizen
Der Artikel beschreibt die Theorie und Praxis der Anwendung des Backpropagation-Algorithmus in MQL5 unter Verwendung von Matrizen. Es bietet vorgefertigte Klassen zusammen mit Beispielen von Skripten, Indikatoren und Expert Advisors.
Datenwissenschaft und maschinelles Lernen (Teil 11): Naïve Bayes, Wahrscheinlichkeitsrechnung im Handel Datenwissenschaft und maschinelles Lernen (Teil 11): Naïve Bayes, Wahrscheinlichkeitsrechnung im Handel
Der Handel mit Wahrscheinlichkeiten ist wie ein Drahtseilakt - er erfordert Präzision, Ausgewogenheit und ein ausgeprägtes Risikobewusstsein. In der Welt des Handels ist die Wahrscheinlichkeit alles. Das ist der Unterschied zwischen Erfolg und Misserfolg, Gewinn und Verlust. Indem sie sich die Macht der Wahrscheinlichkeit zunutze machen, können Händler fundierte Entscheidungen treffen, Risiken effektiv verwalten und ihre finanziellen Ziele erreichen. Ob Sie nun ein erfahrener Anleger oder ein Anfänger sind, das Verständnis der Wahrscheinlichkeit ist der Schlüssel zur Entfaltung Ihres Handelspotenzials. In diesem Artikel werden wir die aufregende Welt des Handels mit Wahrscheinlichkeiten erkunden und Ihnen zeigen, wie Sie Ihr Handelsspiel auf die nächste Stufe heben können.
Wie man ONNX-Modelle in MQL5 verwendet Wie man ONNX-Modelle in MQL5 verwendet
ONNX (Open Neural Network Exchange) ist ein offenes Format, das zur Darstellung von Modellen des maschinellen Lernens entwickelt wurde. In diesem Artikel wird untersucht, wie ein CNN-LSTM-Modell zur Vorhersage von Finanzzeitreihen erstellt werden kann. Wir werden auch zeigen, wie man das erstellte ONNX-Modell in einem MQL5 Expert Advisor verwendet.