| //+------------------------------------------------------------------+
|
| //| ONNX.Price.Prediction.Test.mq5 |
|
| //| Copyright 2023, MetaQuotes Ltd. |
|
| //| https://www.mql5.com |
|
| //+------------------------------------------------------------------+
|
| #property copyright "Copyright 2023, MetaQuotes Ltd."
|
| #property link "https://www.mql5.com"
|
| #property version "1.00"
|
| #property description "Evaluation of the quality of the next Close price prediction by Python\\model.onnx.\n"
|
| "Start in strategy tester on EURUSD,H1, open prices, from 2023.01.01 to 2023.02.01"
|
|
|
| #resource "Python/model.onnx" as uchar ExtModel[]
|
|
|
| #define SAMPLE_SIZE 10 // bars count in sample as defined in Python\PricePredictionTraining.py
|
| // X, y = collect_dataset(df, history_size=10)
|
|
|
| long ExtHandle=INVALID_HANDLE;
|
| double ExtPredicted=0;
|
| datetime ExtNextBar=0;
|
| long ExtTests=0;
|
| long ExtRightDirection=0;
|
| double ExtSumAbsoluteError=0.0;
|
| //+------------------------------------------------------------------+
|
| //| Expert initialization function |
|
| //+------------------------------------------------------------------+
|
| int OnInit()
|
| {
|
| /*
|
| if(Symbol()!="EURUSD" || Period()!=PERIOD_H1)
|
| {
|
| Print("model must work with EURUSD,H1");
|
| return(INIT_FAILED);
|
| }
|
| */
|
| //--- create a model from static buffer
|
| ExtHandle=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
|
| if(ExtHandle==INVALID_HANDLE)
|
| {
|
| Print("OnnxCreateFromBuffer error ",GetLastError());
|
| return(INIT_FAILED);
|
| }
|
| //--- since not all sizes defined in the input tensor we must set them explicitly
|
| //--- first index - batch size, second index - series size, third index - number of series (OHLC)
|
| const long input_shape[] = {1,SAMPLE_SIZE,4};
|
| if(!OnnxSetInputShape(ExtHandle,0,input_shape))
|
| {
|
| Print("OnnxSetInputShape error ",GetLastError());
|
| return(INIT_FAILED);
|
| }
|
| //--- since not all sizes defined in the output tensor we must set them explicitly
|
| //--- first index - batch size, must match the batch size of the input tensor
|
| //--- second index - number of predicted prices (we only predict Close)
|
| const long output_shape[] = {1,1};
|
| if(!OnnxSetOutputShape(ExtHandle,0,output_shape))
|
| {
|
| Print("OnnxSetOutputShape error ",GetLastError());
|
| return(INIT_FAILED);
|
| }
|
| //---
|
| return(INIT_SUCCEEDED);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Expert deinitialization function |
|
| //+------------------------------------------------------------------+
|
| void OnDeinit(const int reason)
|
| {
|
| if(ExtHandle!=INVALID_HANDLE)
|
| {
|
| OnnxRelease(ExtHandle);
|
| ExtHandle=INVALID_HANDLE;
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Expert tick function |
|
| //+------------------------------------------------------------------+
|
| void OnTick()
|
| {
|
| //--- check new bar
|
| if(TimeCurrent()<ExtNextBar)
|
| return;
|
| ExtTests++;
|
| //--- set next bar time
|
| ExtNextBar=TimeCurrent();
|
| ExtNextBar-=ExtNextBar%PeriodSeconds();
|
| ExtNextBar+=PeriodSeconds();
|
| //--- check predicted price
|
| CheckPredicted();
|
| //--- predict next price
|
| PredictPrice();
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Check predicted price |
|
| //+------------------------------------------------------------------+
|
| void CheckPredicted(void)
|
| {
|
| if(ExtPredicted!=0.0)
|
| {
|
| static double closes[2];
|
| static datetime times[2];
|
| if(CopyClose(Symbol(),Period(),1,2,closes)==2 && CopyTime(Symbol(),Period(),1,2,times)==2)
|
| {
|
| ExtSumAbsoluteError+=MathAbs(ExtPredicted-closes[1]);
|
| double delta_predict=ExtPredicted-closes[0];
|
| double delta_actual=closes[1]-closes[0];
|
| if((delta_predict>0 && delta_actual>0) || (delta_predict<0 && delta_actual<0))
|
| ExtRightDirection++;
|
| Print("----------------------------------");
|
| Print("times[0]: ", times[0], "; closes[0]: ", closes[0], "; times[1]: ", times[1], "; closes[1]: ", closes[1]);
|
| Print("ExtPredicted: ", ExtPredicted, "; ExtSumAbsoluteError (ExtPredicted-closes[1]): ", ExtSumAbsoluteError, "; delta_predict (ExtPredicted-closes[0]): ", delta_predict, "; delta_actual (closes[1]-closes[0]): ", delta_actual, "; ExtRightDirection: ", ExtRightDirection, "; ExtTests: ", ExtTests);
|
| }
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Predict next price |
|
| //+------------------------------------------------------------------+
|
| void PredictPrice(void)
|
| {
|
| static matrixf input_data(SAMPLE_SIZE,4); // matrix for prepared input data
|
| static vectorf output_data(1); // vector to get result
|
| static matrix mm(SAMPLE_SIZE,4); // matrix of horizontal vectors Mean
|
| static matrix ms(SAMPLE_SIZE,4); // matrix of horizontal vectors Std
|
| static matrix x_norm(SAMPLE_SIZE,4); // matrix for prices normalize
|
| //--- prepare input data
|
| matrix rates;
|
| //--- request last bars
|
| if(!rates.CopyRates(Symbol(),PERIOD_D1,COPY_RATES_OHLC,1,SAMPLE_SIZE))
|
| {
|
| ExtPredicted=0.0;
|
| return;
|
| }
|
| //--- get series Mean
|
| vector m=rates.Mean(1);
|
| //--- get series Std
|
| vector s=rates.Std(1);
|
| //--- prepare matrices for prices normalization
|
| for(int i=0; i<SAMPLE_SIZE; i++)
|
| {
|
| mm.Row(m,i);
|
| ms.Row(s,i);
|
| }
|
| //--- the input of the model must be a set of vertical OHLC vectors
|
| x_norm=rates.Transpose();
|
| //--- normalize prices
|
| x_norm-=mm;
|
| x_norm/=ms;
|
| //--- run the inference
|
| input_data.Assign(x_norm);
|
| if(!OnnxRun(ExtHandle,ONNX_NO_CONVERSION,input_data,output_data))
|
| {
|
| ExtPredicted=0.0;
|
| return;
|
| }
|
| //--- denormalize the price from the output value
|
| ExtPredicted=output_data[0]*s[3]+m[3];
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Tester function |
|
| //+------------------------------------------------------------------+
|
| double OnTester()
|
| {
|
| double mae=ExtSumAbsoluteError/ExtTests;
|
| Print("mae = ",mae);
|
| double right_directions=(ExtRightDirection*100.0)/ExtTests;
|
| PrintFormat("right_directions = %.2f%%",right_directions);
|
| //---
|
| return(right_directions);
|
| }
|
| //+------------------------------------------------------------------+
|