运行模型

要在MQL5中运行ONNX模型,请完成以下3个步骤:

  1. 使用OnnxCreate函数从*.onnx文件中加载模型或使用OnnxCreateFromBuffer函数从数组中加载模型。
  2. 使用OnnxSetInputShapeOnnxSetOutputShape函数指定输入和输出数据形状。
  3. 使用OnnxRun函数运行模型,并将相关的输入和输出参数传递到这里。
  4. 当需要时,您可以使用OnnxRelease函数终止模型操作。

 

在创建ONNX模型时,您应该考虑现有的范围和限制,请参阅https://github.com/microsoft/onnxruntime/blob/rel-1.14.0/docs/OperatorKernels.md

这类限制的一些示例如下:

Operation

支持的数据类型

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)

 

下面是来自公共项目ONNX.Price.Prediction的MQL5代码示例。

const long   ExtOutputShape[] = {1,1};    // 模型输出形状
const long   ExtInputShape [] = {1,10,4}; // 模型输入形状
#resource "Python/model.onnx" as uchar ExtModel[]// 作为资源的模型
//+------------------------------------------------------------------+
//| 脚本程序起始函数                                                   |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   matrix rates;
//--- 获取10个柱形图
   if(!rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,2,10))
      return(-1);
//--- 输入一组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);
//--- 填充归一化矩阵
   for(int i=0i<10i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
//--- 标准化输入数据
   x_norm-=mm;
   x_norm/=ms;
//--- 创建模型
   long handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
//--- 指定输入数据形状
   if(!OnnxSetInputShape(handle,0,ExtInputShape))
     {
      Print("OnnxSetInputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- 指定输出数据形状
   if(!OnnxSetOutputShape(handle,0,ExtOutputShape))
     {
      Print("OnnxSetOutputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- 将标准化输入数据转换为浮点类型
   matrixf x_normf;
   x_normf.Assign(x_norm);
//--- 在这里获取模型的输出数据,即价格预测
   vectorf y_norm(1);
//--- 运行模型
   if(!OnnxRun(handle,ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION,x_normf,y_norm))
     {
      Print("OnnxRun failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- 将模型的输出值打印到日志中
   Print(y_norm);
//--- 进行反向转换,以获得预测价格
   double y_pred=y_norm[0]*s[3]+m[3];
   Print("price predicted:",y_pred);
//--- 完成操作
   OnnxRelease(handle);
   return(0);
  }

Script run example:

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

The MetaTrader 5 terminal has selected the optimal executor for calculations ― ONNX Runtime Execution Provider. In this example, the model was executed on the CPU.

Let's modify the script to calculate the percentage of successful Close price predictions made based on the values of the preceding 10 bars.

#resource "Python/model.onnx" as uchar ExtModel[]// 作为资源的模型
 
#define TESTS 10000  // number of test datasets
//+------------------------------------------------------------------+
//| 脚本程序起始函数                                                   |
//+------------------------------------------------------------------+
int OnStart()
  {
//--- 创建模型
   long session_handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
   if(session_handle==INVALID_HANDLE)
     {
      Print("Cannot create model. Error ",GetLastError());
      return(-1);
     }
 
//--- 由于没有为模型定义输入张量大小,请明确指定
//--- 第一个指数是批量大小,第二个指数是系列大小,第三个指数是系列数(OHLC)
   const long input_shape[]={1,10,4};
   if(!OnnxSetInputShape(session_handle,0,input_shape))
     {
      Print("OnnxSetInputShape error ",GetLastError());
      return(-2);
     }
 
//--- 由于没有为模型定义输出张量大小,请明确指定
//--- 第一个指数是批量大小,必须匹配输入张量的批量大小
//--- 第二个指数是预测价格的数量( 这里只预测Close)
   const long output_shape[]={1,1};
   if(!OnnxSetOutputShape(session_handle,0,output_shape))
     {
      Print("OnnxSetOutputShape error ",GetLastError());
      return(-3);
     }
//--- 运行测试
   vector closes(TESTS);      // 存储验证价格的向量
   vector predicts(TESTS);    // 存储所得预测的向量
   vector prev_closes(TESTS); // 存储之前价格的向量
 
   matrix rates;              // 获得OHLC系列的矩阵
   matrix splitted[2];        // 将系列分为测试和验证的两个子矩阵
   ulong  parts[]={10,1};     // 划分子矩阵的大小
 
//--- 从前一柱形图开始
   for(int i=1i<=TESTSi++)
     {
      //--- 获取11个柱形图
      rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,i,11);
      //--- 将矩阵分为测试和验证
      rates.Vsplit(parts,splitted);
      //--- 从验证矩阵得到收盘价
      closes[i-1]=splitted[1][3][0];
      //--- 测试系列中的最后收盘价
      prev_closes[i-1]=splitted[0][3][9];
 
      //--- 提交10个柱形图测试矩阵进行测试
      predicts[i-1]=PricePredictionTest(session_handle,splitted[0]);
      //--- 运行时间错误
      if(predicts[i-1]<=0)
        {
         OnnxRelease(session_handle);
         return(-4);
        }
     }
//--- 完成操作
   OnnxRelease(session_handle);
//--- 评估价格移动预测是否正确
   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);
  }
//+------------------------------------------------------------------+
//|  准备数据并运行模型                                                |
//+------------------------------------------------------------------+
double PricePredictionTest(const long session_handle,matrixrates)
  {
   static matrixf input_data(10,4); // 用于转换输入的矩阵
   static vectorf output_data(1);   // 接收结果的向量
   static matrix mm(10,4);          // 水平向量矩阵Mean
   static matrix ms(10,4);          // 水平向量矩阵Std
 
//--- 一组OHLC垂直向量必须输入到模型中
   matrix x_norm=rates.Transpose();
//--- 标准价格
   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;
 
//--- 运行模型
   input_data.Assign(x_norm);
   if(!OnnxRun(session_handle,ONNX_DEBUG_LOGS,input_data,output_data))
     {
      Print("OnnxRun error ",GetLastError());
      return(0);
     }
//--- 将输出值中的价格非标准化
   double y_pred=output_data[0]*s[3]+m[3];
 
   return(y_pred);
  }

运行脚本:预测准确率约为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 %