Validación de modelos en el simulador de estrategias

Los modelos para trabajar en los mercados financieros se pueden probar en el simulador de estrategias del terminal MetaTrader 5. Esta es la opción más rápida y cómoda, pues no requiere esfuerzos adicionales para emular el entorno del mercado y las condiciones comerciales.

Para probar el modelo, reescribimos el código del proyecto público ONNX.Price.Prediction en un asesor experto. Esto requerirá ediciones menores.

Vamos a trasladar la creación del modelo a la función OnInit, y la finalización de la sesión onnx a OnDeinit. El bloque principal para trabajar con el modelo se colocará en el controlador OnTick.

Añadiremos también la obtención del precio de cierre de las 2 barras anteriores para comparar el precio de cierre actual y la predicción.

El código del asesor es pequeño y fácil de leer.

 
const long   ExtInputShape [] = {1,10,4}; // formulario de datos de salida del modelo
const long   ExtOutputShape[] = {1,1};    // formulario de datos de entrada del modelo
#resource "Python/model.onnx" as uchar ExtModel[];// modelo en forma de recurso
 
long handle;         // manejador del modelo
ulong predictions=0// contador de pronósticos
ulong confirmed=0;   // contador de pronósticos exitosos
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- comprobaciones básicas
   if(_Symbol!="EURUSD")
     {
      Print("Symbol must be EURUSD, testing aborted");
      return(-1);
     }
   if(_Period!=PERIOD_H1)
     {
      Print("Timeframe must be H1, testing aborted");
      return(-1);
     }
//--- creamos el modelo
   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);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- hemos finalizado el funcionamiento del modelo
   OnnxRelease(handle);
//--- calculamos y mostramos las estadísticas de los pronósticos
   PrintFormat("Successfull predictions = %.2f %%",confirmed*100./double(predictions));
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime open_time=0;
   static double predict;
//--- comprobamos la hora de apertura de la barra actual
   datetime time=iTime(_Symbol,_Period,0);
   if(time==0)
     {
      PrintFormat("Failed to get Time(0), error %d"GetLastError());
      return;
     }
//--- si la hora de apertura de la barra no ha cambiado, salimos hasta la siguiente llamada de OnTick
   if(time==open_time)
      return;
//--- obtenemos los precios de cierre de las 2 últimas barras finalizadas
   double close[];
   int recieved=CopyClose(_Symbol,_Period,1,2,close);
   if(recieved!=2)
     {
      PrintFormat("CopyClose(2 bars) failed, error %d",GetLastError());
      return;
     }
   double delta_predict=predict-close[0]; // cambio de precio pronosticado
   double delta_actual=close[1]-close[0]; // cambio de precio real
   if((delta_predict>0 && delta_actual>0) || (delta_predict<0 && delta_actual<0))
      confirmed++;
 
//--- calculamos el precio de cierre de la nueva barra para comprobarlo en la siguiente barra
   matrix rates;
//--- obtenemos 10 barras
   if(!rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,1,10))
      return;
//--- 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;
//--- 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());
     }
//--- hacemos la transformación inversa para obtener el precio pronosticado y comprobarlo en una nueva barra
   predict=y_norm[0]*s[3]+m[3];
   predictions++;  // incrementamos el contador de pronósticos
   Print(predictions,". close prediction = ",predict);
//--- recordamos la hora de apertura de la barra para comprobar el próximo tick
   open_time=time;
  }

Compilamos el asesor y comenzamos la prueba en el intervalo de 2022, indicamos el símbolo EURUSD y el marco temporal H1, en los que se entrenó el modelo. El modo de simulación de ticks no importa, porque el código contiene la comprobación de la aparición de una nueva barra.  

Ajustes de la prueba

 

Iniciamos y obtenemos el resultado en el diario de registro de la prueba: algo más del 50% de pronósticos correctos en 2022.

Diario de registro de la prueba

 

Si los resultados de la verificación preliminar del modelo son satisfactorios, podemos comenzar a escribir una estrategia comercial completa usándolos.