English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
Un exemple d'assemblage de modèles ONNX dans MQL5

Un exemple d'assemblage de modèles ONNX dans MQL5

MetaTrader 5Machine learning | 23 janvier 2024, 11:22
307 0
MetaQuotes
MetaQuotes

Introduction

Pour un trading stable, il est généralement recommandé de diversifier à la fois les instruments négociés et les stratégies de trading. Il en va de même pour les modèles d'apprentissage automatique : il est plus facile de créer plusieurs modèles simples qu'un seul modèle complexe. Mais il peut être difficile d'assembler ces modèles en un seul modèle ONNX.

Mais il est possible de combiner plusieurs modèles ONNX entraînés dans un programme MQL5. Dans cet article, nous examinerons l'un de ces ensembles, appelé le classificateur par vote. Nous allons vous montrer comment il est facile de mettre en œuvre un tel ensemble.


Modèles pour le projet

Pour notre exemple, nous utiliserons 2 modèles simples : un modèle de prédiction des prix par régression et un modèle de prédiction des mouvements de prix par classification. La principale différence entre les modèles est que la régression prédit la quantité, alors que la classification prédit la classe.

Le premier modèle est celui de la régression.

Il est entraîné à l'aide des données de l’EURUSD en D1 de 2003 à fin 2022. L'entraînement est réalisé à partir d'une série de 10 prix OHLC. Pour améliorer la capacité d'apprentissage du modèle, nous normalisons les prix et divisons le prix moyen de la série par l'écart-type de la série. Nous plaçons ainsi une série dans un certain intervalle avec une moyenne de 0 et un écart de 1, ce qui améliore la convergence pendant l'apprentissage.

Le modèle devrait donc prédire le prix de clôture du jour suivant.

Le modèle est très simple. Il n'est fourni ici qu'à des fins de démonstration.

# 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()
En supposant que notre modèle de régression soit exécuté, le prix prédit qui en résulte devrait être transformé dans la classe suivante : le prix baisse, le prix ne change pas, le prix augmente. Cela est nécessaire pour organiser le classificateur de vote.

Le deuxième modèle est celui de la classification.

Il est entraîné sur l'EURUSD en D1 de 2010 à fin 2022. L'entraînement est effectué à l'aide d'une série de 63 prix de clôture. L'une des trois classes suivantes doit être définie à la sortie : le prix baissera, le prix restera dans les 10 points ou le prix augmentera. C'est à cause de la deuxième classe que nous avons dû entraîner le modèle en utilisant des données depuis 2010. Avant cela, en 2009, les marchés sont passés d'une précision de 4 chiffres à une précision à 5 chiffres. Un ancien point est ainsi transformé en 10 nouveaux points.

Comme dans le modèle précédent, le prix est normalisé. La normalisation est la même : nous divisons l'écart par rapport au prix moyen de la série par l'écart type de la série. L'idée de ce modèle a été décrite dans l'article timeseries forecasting with MLP in Keras" (en russe). Ce modèle est également conçu à des fins de démonstration uniquement.

# 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()
Les modèles ont été entraînés avec des données jusqu'à la fin de l'année 2022, ce qui laisse le temps de démontrer leur fonctionnement dans le testeur de stratégie.


Un ensemble de modèles ONNX dans l’Expert Advisor MQL5

Vous trouverez ci-dessous un Expert Advisor simple qui illustre les possibilités offertes par les ensembles de modèles. Les grands principes de l'utilisation des modèles ONNX dans MQL5 ont été décrits dans la deuxième partie de l'article précédent.

Déclarations et définitions prospectives

#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

Fonction OnInit

//+------------------------------------------------------------------+
//| 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);
  }

Nous ne l'utiliserons qu'avec l’EURUSD, en D1. Cela s'explique par le fait que nous utilisons les données de la période du symbole en cours, alors que les modèles sont formés à partir des prix quotidiens.

Les modèles sont inclus dans l’Expert Advisor en tant que ressources.

Il est important de définir explicitement les formes de données d'entrée et de sortie.

Fonction 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();
  }

Toutes les opérations de trading ne sont effectuées qu'en début de journée.

Fonction de prédiction

//+------------------------------------------------------------------+
//| 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;
  }

Une classe est considérée comme sélectionnée lorsque les deux modèles ont reçu la même classe. Il s'agit d'un vote à la majorité. Et comme il n'y a que deux modèles dans l'ensemble, le vote à la majorité est "unanime".

Prédiction du prix de clôture du jour à partir des 10 prix OHLC précédents

//+------------------------------------------------------------------+
//| 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);
  }

Les données d'entrée doivent être préparées selon les mêmes règles que pour l'entraînement du modèle. Après l'exécution du modèle, la valeur obtenue est reconvertie en prix. La classe est calculée sur la base de la différence entre le dernier prix de clôture de la série et le prix obtenu.

La prédiction du mouvement des prix est basée sur une série de 63 prix de clôture quotidiens :

//+------------------------------------------------------------------+
//| 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()));
  }

Les prix sont normalisés selon les mêmes règles que dans le premier modèle. Mais cette fois-ci, le code est plus compact car l'entrée est un vecteur et non une matrice. La classe est sélectionnée par la valeur maximale des 3 probabilités.

La stratégie de trading est simple. Les opérations de trading sont effectuées au début de chaque journée. Si la prédiction est "le prix va augmenter", nous achetons ; si elle est "le prix va baisser", nous vendons.

//+------------------------------------------------------------------+
//| 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();
     }
  }


Nous avons entraîné notre modèle avec les données jusqu'au début de l'année 2023. Fixons donc l'intervalle de test à partir du début de l'année.

Paramètres du test

Voici le résultat du test basé sur les données depuis le début de l'année.

Résultats des tests de l’Expert Advisor


Il serait intéressant de connaître les résultats des tests pour chaque modèle.

Pour ce faire, modifions le code source de l'EA comme suit :

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;
     }
  }

Activons le paramètre "Utiliser uniquement le premier modèle".

Paramètres de l'Expert Advisor pour utiliser uniquement le premier modèle

Résultats des tests du premier modèle

Résultats des tests du premier modèle


Testons maintenant le second modèle. Voici les résultats du test du deuxième modèle.

Résultats des tests du deuxième modèle

Le deuxième modèle s'est avéré beaucoup plus solide que le premier. Les résultats confirment la théorie selon laquelle les modèles faibles doivent être assemblés. Toutefois, cet article ne porte pas sur la théorie de l'assemblage, mais sur son application pratique.

Remarque importante : Veuillez noter que les modèles utilisés dans l'article sont présentés uniquement pour démontrer comment travailler avec les modèles ONNX en utilisant le langage MQL5. L'Expert Advisor n'est pas destiné à être utilisé sur des comptes réels.



Conclusion

Nous avons présenté un exemple très simple mais illustratif d'un ensemble de 2 modèles ONNX. Le nombre de modèles utilisés simultanément est limité et ne peut excéder 256 modèles. Mais même l'utilisation de plus de 2 modèles nécessitera une approche différente de la programmation de l’Expert Advisor, à savoir une programmation orientée objet.

Mais c'est le sujet d'un autre article.

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/12433

Fichiers joints |
MQL5.zip (105.08 KB)
Matrices et vecteurs en MQL5 : Fonctions d'activation Matrices et vecteurs en MQL5 : Fonctions d'activation
Nous ne décrirons ici qu'un seul des aspects de l'apprentissage automatique, à savoir les fonctions d'activation. Dans les réseaux neuronaux artificiels, la fonction d'activation d'un neurone calcule la valeur d'un signal de sortie en fonction des valeurs d'un signal d'entrée ou d'un ensemble de signaux d'entrée. Nous nous pencherons sur les rouages du processus.
Apprenez à concevoir un système de trading basé sur l’Accelerator Oscillator Apprenez à concevoir un système de trading basé sur l’Accelerator Oscillator
Un nouvel article de notre série sur la façon de créer des systèmes de trading simples à l'aide des indicateurs techniques les plus populaires. Nous découvrirons ensemble un nouvel indicateur : l’Accelerator Oscillator et nous apprendrons comment concevoir un système de trading en l'utilisant.
Envelopper les modèles ONNX dans des classes Envelopper les modèles ONNX dans des classes
La programmation orientée objet permet de créer un code plus compact, facile à lire et à modifier. Nous examinerons ici l'exemple de 3 modèles ONNX.
Algorithmes d'optimisation de la population : Algorithme de la Chauve-Souris (BA) Algorithmes d'optimisation de la population : Algorithme de la Chauve-Souris (BA)
Dans cet article, j'examinerai l'algorithme de la Chauve-Souris, ou Bat (BA), qui présente une bonne convergence pour les fonctions lisses.