Esecuzione di un modello

Per eseguire un modello ONNX in MQL5, completare 3 passaggi:

  1. Caricare il modello da un file *.onnx utilizzando la funzione OnnxCreate o da un array utilizzando OnnxCreateFromBuffer.
  2. Specificare le dimensioni dei dati di ingresso e di uscita utilizzando le funzioni OnnxSetInputShape e OnnxSetOutputShape.
  3. Eseguire il modello utilizzando la funzione OnnxRun, passando i parametri in ingresso e in uscita pertinenti.
  4. Quando necessario, è possibile terminare l'operatività del modello utilizzando la funzione OnnxRelease.

 

Quando si crea un modello ONNX, si dovrebbero considerare i limiti e le restrizioni esistenti, descritti in https://github.com/microsoft/onnxruntime/blob/rel-1.14.0/docs/OperatorKernels.md

Di seguito sono riportati alcuni esempi di tali restrizioni:

Operazione

Tipi di dati supportati

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)

 

Di seguito è riportato un esempio di codice MQL5 dal progetto pubblico ONNX.Price.Prediction.

const long   ExtOutputShape[] = {1,1};    // dimensione del modello d'uscita
const long   ExtInputShape [] = {1,10,4}; // dimensione del modello d'ingresso
#resource "Python/model.onnx" as uchar ExtModel[]// modello come risorsa
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   matrix rates;
//-- ottenere 10 barre
   if(!rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,2,10))
      return(-1);
//---inserire un insieme di vettori 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);
//-- riempire le matrici di normalizzazione
   for(int i=0; i<10; i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
//-- normalizzare i dati d'ingresso
   x_norm-=mm;
   x_norm/=ms;
//-- creare il modello
   long handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
//-- specificare la dimensione dei dati d'ingresso
   if(!OnnxSetInputShape(handle,0,ExtInputShape))
     {
      Print("OnnxSetInputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//-- specificare la dimensione dei dati d'uscita
   if(!OnnxSetOutputShape(handle,0,ExtOutputShape))
     {
      Print("OnnxSetOutputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//-- convertire i dati d'ingresso normalizzati in tipo float
   matrixf x_normf;
   x_normf.Assign(x_norm);
//-- ottenere i dati d'uscita del modello qui, cioè la previsione dei prezzi
   vectorf y_norm(1);
//-- eseguire il modello
   if(!OnnxRun(handle,ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION,x_normf,y_norm))
     {
      Print("OnnxRun failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//-- stampa il valore di uscita del modello nel log
   Print(y_norm);
//-- fare la trasformazione inversa per ottenere il prezzo previsto
   double y_pred=y_norm[0]*s[3]+m[3];
   Print("price predicted:",y_pred);
//-- operazione completata
   OnnxRelease(handle);
   return(0);
  }

Esempio di esecuzione di uno 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

Il terminale MetaTrader 5 ha selezionato l'esecutore ottimale per i calcoli — ONNX Runtime Execution Provider. In questo esempio, il modello è stato eseguito sulla CPU.

Modifichiamo lo script per calcolare la percentuale di successo delle previsioni dei prezzi di Chiusura effettuate in base ai valori delle precedenti 10 barre.

#resource "Python/model.onnx" as uchar ExtModel[]// modello come risorsa
 
#define TESTS 10000  // numero di set di dati del test
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
//-- creare il modello
   long session_handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
   if(session_handle==INVALID_HANDLE)
     {
      Print("Cannot create model. Error ",GetLastError());
      return(-1);
     }
 
//--- poiché la dimensione del tensore d'ingresso non è definita per il modello, specificarlo esplicitamente
//--- il primo indice è la dimensione del lotto, il secondo indice è la dimensione della serie, terzo indice è il numero delle serie (OHLC)
   const long input_shape[]={1,10,4};
   if(!OnnxSetInputShape(session_handle,0,input_shape))
     {
      Print("OnnxSetInputShape error ",GetLastError());
      return(-2);
     }
 
//--- poiché la dimensione del tensore d'uscita non è definita per il modello, specificarlo esplicitamente
//--- il primo indice è la dimensione del lotto, deve coincidere alla dimensione del lotto nel tensore d'ingresso
//--- il secondo indice è il numero dei prezzi previsti (solo la Chiusura è prevista qui)
   const long output_shape[]={1,1};
   if(!OnnxSetOutputShape(session_handle,0,output_shape))
     {
      Print("OnnxSetOutputShape error ",GetLastError());
      return(-3);
     }
//--- escuzione dei test
   vector closes(TESTS);      // vettore per memorizzare i prezzi di convalida
   vector predicts(TESTS);    // vettore per memorizzare le previsioni ottenute
   vector prev_closes(TESTS); // vettore per memorizzare i prezzi precedenti
 
   matrix rates;              // matrice per ottenere la serie OHLC
   matrix splitted[2];        // due sottomatrici per dividere la serie in test e validazione
   ulong  parts[]={10,1};     // dimensioni delle sottomatrici divise
 
//-- parte dalla barra precedente
   for(int i=1; i<=TESTS; i++)
     {
      //--- ottiene 11 barre
      rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,i,11);
      //--- dividere la matrice in test e validazione
      rates.Vsplit(parts,splitted);
      //--- prendere il prezzo di Chiusura dalla matrice di convalida
      closes[i-1]=splitted[1][3][0];
      //---l'ultima Chiusura nella serie testata
      prev_closes[i-1]=splitted[0][3][9];
 
      //--- sottoporre al test la matrice di prova di 10 bar
      predicts[i-1]=PricePredictionTest(session_handle,splitted[0]);
      //--- errore di runtime
      if(predicts[i-1]<=0)
        {
         OnnxRelease(session_handle);
         return(-4);
        }
     }
//-- operazione completata
   OnnxRelease(session_handle);
//-- valutare se il movimento dei prezzi è stato previsto correttamente
   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("right direction predictions = %.2f%%",(right_directions*100.0)/double(TESTS));
//--- 
   return(0);
  }
//+------------------------------------------------------------------+
// Preparare i dati ed eseguire il modello                           |
//+------------------------------------------------------------------+
double PricePredictionTest(const long session_handle,matrix& rates)
  {
   static matrixf input_data(10,4); // matrice per l'ingresso trasformato
   static vectorf output_data(1);   // vettore per ricevere il risultato
   static matrix mm(10,4);          // matrice di vettori orizzontali Mean</T11><segmento 0258 >
   static matrix ms(10,4);          // matrice di vettori orizzontali Std
 
//-- un insieme di vettori verticali OHLC deve essere inserito nel modello
   matrix x_norm=rates.Transpose();
//-- normalizzare i prezzi
   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;
 
//-- eseguire il modello
   input_data.Assign(x_norm);
   if(!OnnxRun(session_handle,ONNX_DEBUG_LOGS,input_data,output_data))
     {
      Print("OnnxRun error ",GetLastError());
      return(0);
     }
//--- normalizzare il prezzo dal valore di uscita
   double y_pred=output_data[0]*s[3]+m[3];
 
   return(y_pred);
  }

Esegui lo script: l'accuratezza della previsione è di circa il 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 %