//+------------------------------------------------------------------+
//|                                             Reward Modelling.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Libraries we need                                                |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
#define MA_PERIOD 3                //--- Moving Average Period
#define MA_TYPE   MODE_SMA         //--- Type of moving average we have
#define HORIZON 10                 //--- How far into the future we should forecast

//+------------------------------------------------------------------+
//| System resources                                                 |
//+------------------------------------------------------------------+
#resource "\\Files\\EURUSD Reward Model.onnx" as uchar onnx_proto[];

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
long onnx_model;
int fetch = HORIZON + 1;

//+------------------------------------------------------------------+
//| Technical indicators                                             |
//+------------------------------------------------------------------+
int ma_handle,ma_o_handle;
double ma_reading[],ma_o_reading[];
int position_timer;


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!setup())
      return(INIT_FAILED);
//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   release();
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   update();
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Setup the system                                                 |
//+------------------------------------------------------------------+
bool setup(void)
  {
//---Setup our technical indicators
   ma_handle = iMA(_Symbol,PERIOD_CURRENT,MA_PERIOD,0,MA_TYPE,PRICE_CLOSE);
   ma_o_handle = iMA(_Symbol,PERIOD_CURRENT,MA_PERIOD,0,MA_TYPE,PRICE_OPEN);
   position_timer = 0;

//--- Setup the ONNX model
   onnx_model = OnnxCreateFromBuffer(onnx_proto,ONNX_DEFAULT);

//--- Validate the ONNX model
   if(onnx_model == INVALID_HANDLE)
     {
      Comment("Failed to create ONNX model");
      return(false);
     }

//--- Register the ONNX model I/O parameters
   ulong input_shape[] = {1,6};
   ulong output_shape[] = {1,1};

   if(!OnnxSetInputShape(onnx_model,0,input_shape))
     {
      Comment("Failed to set input shape");
      return(false);
     }

   if(!OnnxSetOutputShape(onnx_model,0,output_shape))
     {
      Comment("Failed to set output shape");
      return(false);
     }

   return(true);
  }

//+------------------------------------------------------------------+
//| Release system variables we are no longer using                  |
//+------------------------------------------------------------------+
void release(void)
  {
   OnnxRelease(onnx_model);
   IndicatorRelease(ma_handle);
   IndicatorRelease(ma_o_handle);
   return;
  }

//+------------------------------------------------------------------+
//| Update system parameters                                         |
//+------------------------------------------------------------------+
void update(void)
  {
//--- Time stamps
   static datetime time_stamp;
   datetime current_time = iTime(Symbol(),PERIOD_D1,0);

//--- We are on a new day
   if(time_stamp != current_time)
     {
      time_stamp = current_time;
      if(PositionsTotal() == 0)
        {
         //--- Copy indicator values
         CopyBuffer(ma_handle,0,0,fetch,ma_reading);
         CopyBuffer(ma_o_handle,0,0,fetch,ma_o_reading);
         //---Set the values as series
         ArraySetAsSeries(ma_reading,true);
         ArraySetAsSeries(ma_o_reading,true);
         find_setup();
         position_timer = 0;
        }

      //--- Forecasts are only valid for HORIZON days
      if(PositionsTotal() > 0)
        {
         position_timer += 1;
        }

      //--- Otherwise close the position
      if(position_timer == HORIZON)
         Trade.PositionClose(Symbol());
     }
   return;
  }

//+------------------------------------------------------------------+
//| Find a trading oppurtunity                                       |
//+------------------------------------------------------------------+
void find_setup(void)
  {
   double bid = SymbolInfoDouble(Symbol(),SYMBOL_BID), ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double vol = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);

   vector ma_o,ma_c;

   ma_o.CopyIndicatorBuffer(ma_o_handle,0,0,1);
   ma_c.CopyIndicatorBuffer(ma_handle,0,0,1);
//--- Prepare the model's inputs
   vectorf model_input(6);
   model_input[0] = (float)(iOpen(_Symbol,PERIOD_CURRENT,0)   - iOpen(_Symbol,PERIOD_CURRENT,(HORIZON)));
   model_input[1] = (float)(iHigh(_Symbol,PERIOD_CURRENT,0)   - iHigh(_Symbol,PERIOD_CURRENT,(HORIZON)));
   model_input[2] = (float)(iLow(_Symbol,PERIOD_CURRENT,0)    - iLow(_Symbol,PERIOD_CURRENT,(HORIZON)));
   model_input[3] = (float)(iClose(_Symbol,PERIOD_CURRENT,0)  - iClose(_Symbol,PERIOD_CURRENT,(HORIZON)));
   model_input[4] = (float)(ma_reading[0] - ma_reading[(HORIZON)]);
   model_input[5] = (float)(ma_o_reading[0] - ma_o_reading[(HORIZON)]);

//--- Prepare the model's output
   vectorf model_output(1);

//--- We failed to run the model
   if(!OnnxRun(onnx_model,ONNX_DATA_TYPE_FLOAT,model_input,model_output))
      Comment("Failed to obtain a forecast");

//--- Everything went fine
   else
     {
      Comment("Forecast: ",model_output[0]);

      //--- Our model forecasts that our strategy is likely to be profitable
      if(model_output[0] > 0.5)
        {
         if(ma_o[0] > ma_c[0])
           {
            Trade.Sell(vol,Symbol(),ask,0,0,"");
           }

         if(ma_o[0] < ma_c[0])
           {
            Trade.Buy(vol,Symbol(),bid,0,0,"");
           }
        }
     }
   return;
  }

//+------------------------------------------------------------------+
//| Undefine system constatns                                        |
//+------------------------------------------------------------------+
#undef HORIZON
#undef MA_PERIOD
#undef MA_TYPE
//+------------------------------------------------------------------+
