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=0; i<10; i++)
     {
      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 :

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.
[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=1; i<=TESTS; i++)
     {
      //--- 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=0; i<TESTS; i++)
      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,matrix& rates)
  {
   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=0; i<10; i++)
     {
      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 %