Inicio del modelo

Para iniciar un modelo ONNX en MQL5, deberemos ejecutar 3 pasos:

  1. Cargarlo desde una archivo *.onnx con ayuda de la función OnnxCreate o desde un array con ayuda de OnnxCreateFromBuffer.
  2. Indicar el formulario de los datos de entrada y salida con las funciones OnnxSetInputShape y OnnxSetOutputShape.
  3. Iniciar el modelo con la ayuda de OnnxRun, transmitiéndole los parámetros de entrada y salida.
  4. Finalizar (en caso necesario) el funcionamiento del modelo con ayuda de OnnxRelease.

 

Al crear un modelo ONNX, deberemos considerar los límites y limitaciones existentes descritos en https://github.com/microsoft/onnxruntime/blob/rel-1.14.0/docs/OperatorKernels.md

Aquí tenemos algunos de ellos:

Operación

Tipos de datos soportados

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)

 

Más abajo le mostramos un ejemplo de código MQL5 del proyecto público ONNX.Price.Prediction.

const long   ExtOutputShape[] = {1,1};    // formulario de datos de entrada del modelo
const long   ExtInputShape [] = {1,10,4}; // formulario de datos de salida del modelo
#resource "Python/model.onnx" as uchar ExtModel[]// modelo en forma de recurso
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   matrix rates;
//--- obtenemos 10 barras
   if(!rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,2,10))
      return(-1);
//--- suministramos a la entrada un conjunto de vectores 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);
//--- rellenamos las matrices de normalización
   for(int i=0i<10i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
//--- normalizamos los datos de entrada
   x_norm-=mm;
   x_norm/=ms;
//--- creamos el modelo
   long handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
//--- indicamos el formulario de los datos de entrada
   if(!OnnxSetInputShape(handle,0,ExtInputShape))
     {
      Print("OnnxSetInputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- indicamos el formulario de los datos de salida
   if(!OnnxSetOutputShape(handle,0,ExtOutputShape))
     {
      Print("OnnxSetOutputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- convertimos los datos de entrada normalizados al tipo float
   matrixf x_normf;
   x_normf.Assign(x_norm);
//--- aquí obtenemos los datos de entrada del modelo, el pronóstico del precio
   vectorf y_norm(1);
//--- iniciamos el modelo
   if(!OnnxRun(handle,ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION,x_normf,y_norm))
     {
      Print("OnnxRun failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- mostramos en el log el valor de salida del modelo
   Print(y_norm);
//--- realizamos la conversión inversa para obtener el precio pronosticado
   double y_pred=y_norm[0]*s[3]+m[3];
   Print("price predicted:",y_pred);
//--- finalizamos el funcionamiento
   OnnxRelease(handle);
   return(0);
  }

Ejemplo de ejecución del 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

El terminal MetaTrader 5 ha seleccionado por sí mismo el ejecutor óptimo para realizar los cálculos – ONNX Runtime Execution Provider. En este caso, el modelo ha trabajado utilizando la CPU.

Vamos a cambiar el script para calcular el porcentaje de previsiones existosas del precio Close usando como base las 10 barras anteriores.

#resource "Python/model.onnx" as uchar ExtModel[]// modelo en forma de recurso
 
#define TESTS 10000  // número de muestras de prueba
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
//--- creamos el modelo
   long session_handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
   if(session_handle==INVALID_HANDLE)
     {
      Print("Cannot create model. Error ",GetLastError());
      return(-1);
     }
 
//--- como para el modelo no se ha determinado el tamaño del tensor de entrada, lo estableceremos de forma explícita
//--- el primer índice es el tamaño del paquete; el segundo, el tamaño de la serie; el tercero, el tamaño de las series (OHLC)
   const long input_shape[]={1,10,4};
   if(!OnnxSetInputShape(session_handle,0,input_shape))
     {
      Print("OnnxSetInputShape error ",GetLastError());
      return(-2);
     }
 
//--- como para el modelo no se ha determinado el tamaño del tensor de salida, lo estableceremos de forma explícita
//--- el primer índice es el tamaño del paquete, debe corresponderse con el tamaño del paquete del tesnor de entrada
//--- el segundo índice, es el número de precios pronosticados (solo pronosticamos Close)
   const long output_shape[]={1,1};
   if(!OnnxSetOutputShape(session_handle,0,output_shape))
     {
      Print("OnnxSetOutputShape error ",GetLastError());
      return(-3);
     }
//--- iniciamos las pruebas
   vector closes(TESTS);      // vector para almacenar los precios de verificación
   vector predicts(TESTS);    // vector para almacenar las previsiones obtenidas
   vector prev_closes(TESTS); // vector para almacenar los penúltimos precios 
 
   matrix rates;              // matriz para obtener la serie OHLC
   matrix splitted[2];        // dos submatrices para dividir la serie en serie de prueba y serie de verificación
   ulong  parts[]={10,1};     // tamaño de las submatrices divididas
 
//--- comenzaremos desde la barra anterior
   for(int i=1i<=TESTSi++)
     {
      //--- obtenemos 11 barras
      rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,i,11);
      //--- dividimos la matriz en matriz de prueba y de verificación
      rates.Vsplit(parts,splitted);
      //--- tomamos de la matriz de prueba el precio Close
      closes[i-1]=splitted[1][3][0];
      //--- último Close en la serie probada
      prev_closes[i-1]=splitted[0][3][9];
 
      //--- enviamos a la prueba una matriz de prueba de 10 barras
      predicts[i-1]=PricePredictionTest(session_handle,splitted[0]);
      //--- error de ejecución
      if(predicts[i-1]<=0)
        {
         OnnxRelease(session_handle);
         return(-4);
        }
     }
//--- finalizamos el funcionamiento
   OnnxRelease(session_handle);
//--- evaluamos la corrección del movimiento de precio pronosticado
   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("right direction predictions = %.2f%%",(right_directions*100.0)/double(TESTS));
//--- 
   return(0);
  }
//+------------------------------------------------------------------+
//|  Preparamos los datos e iniciamos el modelo                               |
//+------------------------------------------------------------------+
double PricePredictionTest(const long session_handle,matrixrates)
  {
   static matrixf input_data(10,4); // matriz para convertir los datos de entrada
   static vectorf output_data(1);   // vector para obtener el resultado
   static matrix mm(10,4);          // matriz de vectores Mean horizontales
   static matrix ms(10,4);          // matriz de vectores Std horizontales
 
//--- debemos suministar a la entrada del modelo un conjunto de vectores OHLC verticales
   matrix x_norm=rates.Transpose();
//--- normalizamos los precios
   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;
 
//--- iniciamos el modelo
   input_data.Assign(x_norm);
   if(!OnnxRun(session_handle,ONNX_DEBUG_LOGS,input_data,output_data))
     {
      Print("OnnxRun error ",GetLastError());
      return(0);
     }
//--- renormalizamos el precio del valor de salida
   double y_pred=output_data[0]*s[3]+m[3];
 
   return(y_pred);
  }

Ejecutamos el script y obtenemos una precisión de predicción de alrededor del 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 %