Exécution d'un modèle

Pour exécuter un modèle ONNX dans MQL5, effectuez 3 étapes :

  1. Chargez le modèle à partir d'un fichier *.onnx à l'aide de la fonction OnnxCreate ou à partir d'un tableau à l'aide de OnnxCreateFromBuffer.
  2. Spécifiez les formes de données d'entrée et de sortie à l'aide des fonctions OnnxSetInputShape et OnnxSetOutputShape.
  3. Exécutez le modèle à l'aide de la fonction OnnxRun, en lui transmettant les paramètres d'entrée et de sortie pertinents.
  4. Si nécessaire, vous pouvez mettre fin à l'opération du modèle à l'aide de la fonction OnnxRelease.

 

Lors de la création d'un modèle ONNX, vous devez tenir compte des limites et restrictions existantes, qui sont décrites sur https://github.com/microsoft/onnxruntime/blob/rel-1.14.0/docs /OperatorKernels.md

Certains exemples de telles restrictions sont présentés ci-dessous :

Opération

Types de données supportés

ReduceSum

tensor(double), tensor(float), tensor(int32), tensor(int64)

Mul

tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)

 

Vous trouverez ci-dessous un exemple de code MQL5 du projet public ONNX.Price.Prediction.

const long   ExtOutputShape[] = {1,1};    // forme de sortie du modèle
const long   ExtInputShape [] = {1,10,4}; // forme d'entrée du modèle
#resource "Python/model.onnx" as uchar ExtModel[]// modèle en tant que ressource
//+------------------------------------------------------------------+
//| Fonction de démarrage du programme                               |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   matrix rates;
//--- récupère 10 barres
   if(!rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,2,10))
      return(-1);
//--- saisie d'un ensemble de vecteurs OHLC
   matrix x_norm=rates.Transpose();
   vector m=x_norm.Mean(0);               
   vector s=x_norm.Std(0);
   matrix mm(10,4);
   matrix ms(10,4);
//--- remplissage des matrices de normalisation
   for(int i=0i<10i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
//--- normalisation des données d'entrée
   x_norm-=mm;
   x_norm/=ms;
//--- crée le modèle
   long handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
//--- spécifie la forme des données d'entrée
   if(!OnnxSetInputShape(handle,0,ExtInputShape))
     {
      Print("Echec de OnnxSetInputShape, erreur ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- spécifie la forme des données de sortie
   if(!OnnxSetOutputShape(handle,0,ExtOutputShape))
     {
      Print("Echec de OnnxSetOutputShape, erreur ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- convertit les données d'entrée normalisées en type flottant
   matrixf x_normf;
   x_normf.Assign(x_norm);
//--- récupère les données de sortie du modèle ici, c'est-à-dire la prévision de prix
   vectorf y_norm(1);
//--- exécute le modèle
   if(!OnnxRun(handle,ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION,x_normf,y_norm))
     {
      Print("Echec de OnnxRun, erreur ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- affiche la valeur de sortie du modèle dans le journal
   Print(y_norm);
//--- effectue la transformation inverse pour obtenir le prix prédit
   double y_pred=y_norm[0]*s[3]+m[3];
   Print("prix prédit :",y_pred);
//--- termine l'opération
   OnnxRelease(handle);
   return(0);
  }

Exemple d'exécution du script :

ONNXCreating and using per session threadpools since use_per_session_threads_ is true
ONNXDynamic block base set to 0
ONNXInitializing session.
ONNXAdding default CPU execution provider.
ONNXTotal shared scalar initializer count0
ONNXTotal fused reshape node count0
ONNXTotal shared scalar initializer count0
ONNXTotal fused reshape node count0
ONNXUse DeviceBasedPartition as default
ONNXSaving initialized tensors.
ONNXDone saving initialized tensors
ONNXSession successfully initialized.
[0.28188983]
predicted 1.0559258806393044

Le terminal MetaTrader 5 a sélectionné l'exécuteur optimal pour les calculs : ONNX Runtime Execution Provider. Dans cet exemple, le modèle a été exécuté sur le CPU.

Modifions le script pour calculer le pourcentage de prédictions de prix de clôture réussies basées sur les valeurs des 10 barres précédentes.

#resource "Python/model.onnx" as uchar ExtModel[]// modèle en tant que ressource
 
#define TESTS 10000  // nombre de jeux de données de test
//+------------------------------------------------------------------+
//| Fonction de démarrage du programme                               |
//+------------------------------------------------------------------+
int OnStart()
  {
//--- crée le modèle
   long session_handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
   if(session_handle==INVALID_HANDLE)
     {
      Print("Ne peut pas créer le modèle. Erreur ",GetLastError());
      return(-1);
     }
 
//--- puisque la taille du tenseur d'entrée n'est pas définie pour le modèle, spécifiez-la explicitement
//--- le premier index est la taille du lot, le deuxième index est la taille de la série, le troisième index est le nombre de séries (OHLC)
   const long input_shape[]={1,10,4};
   if(!OnnxSetInputShape(session_handle,0,input_shape))
     {
      Print("Erreur OnnxSetInputShape ",GetLastError());
      return(-2);
     }
 
//--- puisque la taille du tenseur de sortie n'est pas définie pour le modèle, spécifiez-la explicitement
//--- le premier index est la taille du lot, doit correspondre à la taille du lot dans le tenseur d'entrée
//--- le deuxième index est le nombre de prix prédits (seul Close est prédit ici)
   const long output_shape[]={1,1};
   if(!OnnxSetOutputShape(session_handle,0,output_shape))
     {
      Print("Erreur OnnxSetOutputShape ",GetLastError());
      return(-3);
     }
//--- exécution des tests
   vector closes(TESTS);      // vecteur pour stocker les prix de validation
   vector predicts(TESTS);    // vecteur pour stocker les prédictions obtenues
   vector prev_closes(TESTS); // vecteur pour stocker les prix précédents
 
   matrix rates;              // matrice pour obtenir la série OHLC
   matrix splitted[2];        // 2 sous-matrices pour diviser la série en test et validation
   ulong  parts[]={10,1};     // tailles des sous-matrices divisées
 
//--- commence à partir de la barre précédente
   for(int i=1i<=TESTSi++)
     {
      //--- récupère 11 barres
      rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,i,11);
      //--- divise la matrice en test et validation
      rates.Vsplit(parts,splitted);
      //--- prend le prix de clôture de la matrice de validation
      closes[i-1]=splitted[1][3][0];
      //--- dernière clôture de la série testée
      prev_closes[i-1]=splitted[0][3][9];
 
      //--- soumet la matrice de test de 10 barres au test
      predicts[i-1]=PricePredictionTest(session_handle,splitted[0]);
      //--- erreur d'exécution
      if(predicts[i-1]<=0)
        {
         OnnxRelease(session_handle);
         return(-4);
        }
     }
//--- termine l'opération
   OnnxRelease(session_handle);
//--- évalue si le mouvement des prix a été prédit correctement
   int    right_directions=0;
   vector delta_predicts=prev_closes-predicts;
   vector delta_actuals=prev_closes-closes;
 
   for(int i=0i<TESTSi++)
      if((delta_predicts[i]>0 && delta_actuals[i]>0) || (delta_predicts[i]<0 && delta_actuals[i]<0))
         right_directions++;
   PrintFormat("prédictions dans la bonne direction = %.2f%%",(right_directions*100.0)/double(TESTS));
//--- 
   return(0);
  }
//+------------------------------------------------------------------+
//| Prépare les données et exécute le modèle                         |
//+------------------------------------------------------------------+
double PricePredictionTest(const long session_handle,matrixrates)
  {
   static matrixf input_data(10,4); // matrice pour les entrées transformées
   static vectorf output_data(1);   // vecteur pour recevoir le résultat
   static matrix mm(10,4);          // matrice des vecteurs horizontaux Moyenne
   static matrix ms(10,4);          // matrice des vecteurs horizontaux Std
 
//--- un ensemble de vecteurs verticaux OHLC doit être entré dans le modèle
   matrix x_norm=rates.Transpose();
//--- normalisation des prix
   vector m=x_norm.Mean(0);
   vector s=x_norm.Std(0);
   for(int i=0i<10i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
   x_norm-=mm;
   x_norm/=ms;
 
//--- exécute le modèle
   input_data.Assign(x_norm);
   if(!OnnxRun(session_handle,ONNX_DEBUG_LOGS,input_data,output_data))
     {
      Print("Erreur OnnxRun ",GetLastError());
      return(0);
     }
//--- dénormalisation du prix de la valeur de sortie
   double y_pred=output_data[0]*s[3]+m[3];
 
   return(y_pred);
  }

Exécution du script : la précision de la prédiction est d'environ 51 %

ONNX: Creating and using per session threadpools since use_per_session_threads_ is true
ONNX: Dynamic block base set to 0
ONNX: Initializing session.
ONNX: Adding default CPU execution provider.
ONNX: Total shared scalar initializer count: 0
ONNX: Total fused reshape node count: 0
ONNX: Total shared scalar initializer count: 0
ONNX: Total fused reshape node count: 0
ONNX: Use DeviceBasedPartition as default
ONNX: Saving initialized tensors.
ONNX: Done saving initialized tensors
ONNX: Session successfully initialized.
right direction predictions = 51.34 %