//+------------------------------------------------------------------+
//|                                                      ProjectName |
//|                                      Copyright 2020, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs

//--- The RSI class will manage our indicator settings and provide useful transformations we need
class RSI
  {
   //--- Private members
private:
   //--- Have the indicator values been copied to the buffer?
   bool              indicator_values_initialized;
   bool              indicator_differenced_values_initialized;

   //--- Give the user feedback
   string            user_feedback(int flag)
     {
      string message;

      //--- Check if the RSI indicator loaded correctly
      if(flag == 0)
        {
         //--- Check the indicator was loaded correctly
         if(IsValid())
            message = "RSI Indicator Class Loaded Correcrtly \n";
         return(message);
         //--- Something went wrong
         message = "Error loading RSI Indicator: [ERROR] " + (string) GetLastError();
         return(message);
        }

      //--- User tried getting indicator values before setting them
      if(flag == 1)
        {
         message = "Please set the indicator values before trying to fetch them from memory";
         return(message);
        }

      //--- We sueccessfully set our differenced indicator values
      if(flag == 2)
        {
         message = "Succesfully set differenced indicator values.";
         return(message);
        }

      //--- Failed  to set our differenced indicator values
      if(flag == 3)
        {
         message = "Failed to set our differenced indicator values: [ERROR] " + (string) GetLastError();
         return(message);
        }

      //--- The user is trying to retrieve an index beyond the buffer size and must update the buffer size first
      if(flag == 4)
        {
         message = "The user is attempting to use call an index beyond the buffer size, update the buffer size first";
         return(message);
        }

      //--- No feedback
      else
         return("");
     }

   //--- Protected members
protected:
   //--- The handler for our RSI
   int               rsi_handler;
   //--- The Symbol our RSI should be applied on
   string            rsi_symbol;
   //--- Our RSI period
   int               rsi_period;
   //--- How far into the future we wish to forecast
   int               forecast_horizon;
   //--- The buffer for our RSI indicator
   double            rsi_reading[];
   vector            rsi_differenced_values;
   //--- The current size of the buffer the user last requested
   int               rsi_buffer_size;
   int               rsi_differenced_buffer_size;
   //--- The time frame our RSI should be applied on
   ENUM_TIMEFRAMES   rsi_time_frame;
   //--- The price should the RSI be applied on
   ENUM_APPLIED_PRICE rsi_price;

//--- Now, we can define public members:
public:

   //--- Check if our indicator handler is valid
   bool              IsValid(void)
     {
      return((this.rsi_handler != INVALID_HANDLE));
     }

   //--- Our default constructor
   void              RSI(void):
                     indicator_values_initialized(false),
                     rsi_symbol("EURUSD"),
                     rsi_time_frame(PERIOD_D1),
                     rsi_period(5),
                     rsi_price(PRICE_CLOSE),
                     rsi_handler(iRSI(rsi_symbol,rsi_time_frame,rsi_period,rsi_price))
     {
      //--- Give the user feedback on initilization
      Print(user_feedback(0));
      //--- Remind the user they called the default constructor
      Print("Default Constructor Called: ",__FUNCSIG__," ",&this);
     }

   //--- Parametric constructor
   void              RSI(string user_symbol,ENUM_TIMEFRAMES user_time_frame,int user_period,ENUM_APPLIED_PRICE user_price)
     {
      indicator_values_initialized = false;
      rsi_symbol                   = user_symbol;
      rsi_time_frame               = user_time_frame;
      rsi_period                   = user_period;
      rsi_price                    = user_price;
      rsi_handler                  = iRSI(rsi_symbol,rsi_time_frame,rsi_period,rsi_price);
      //--- Give the user feedback on initilization
      Print(user_feedback(0));
     }

   //--- Destructor
   void             ~RSI(void)
     {
      //--- Free up resources we don't need and reset our flags
      if(IndicatorRelease(rsi_handler))
        {
         indicator_differenced_values_initialized = false;
         indicator_values_initialized = false;
         Print("RSI System logging off");
        }
     }

   //--- Copy readings for our RSI indicator
   bool              SetIndicatorValues(int buffer_size,bool set_as_series)
     {
      rsi_buffer_size = buffer_size;
      CopyBuffer(this.rsi_handler,0,0,buffer_size,rsi_reading);
      if(set_as_series)
         ArraySetAsSeries(this.rsi_reading,true);
      indicator_values_initialized = true;

      //--- Did something go wrong?
      vector rsi_test;
      rsi_test.CopyIndicatorBuffer(rsi_handler,0,0,buffer_size);
      if(rsi_test.Sum() == 0)
         return(false);

      //--- Everything went fine.
      return(true);
     }

   //--- Get the current RSI reading
   double            GetCurrentReading(void)
     {
      double temp[];
      CopyBuffer(this.rsi_handler,0,0,1,temp);
      return(temp[0]);
     }

   //--- Get a specific RSI reading
   double            GetReadingAt(int index)
     {
      //--- Is the user trying to call indexes beyond the buffer?
      if(index > rsi_buffer_size)
        {
         Print(user_feedback(4));
         return(-1e10);
        }

      //--- Get the reading at the specified index
      if((indicator_values_initialized) && (index < rsi_buffer_size))
         return(rsi_reading[index]);

      //--- User is trying to get values that were not set prior
      else
        {
         Print(user_feedback(1));
         return(-1e10);
        }
     }

   //--- Let's set the conditions for our differenced data
   bool              SetDifferencedIndicatorValues(int buffer_size,int differencing_period,bool set_as_series)
     {
      //--- Internal variables
      rsi_differenced_buffer_size = buffer_size;
      rsi_differenced_values = vector::Zeros(rsi_differenced_buffer_size);

      //--- Prepare to record the differences in our RSI readings
      double temp_buffer[];
      int fetch = (rsi_differenced_buffer_size + (2 * differencing_period));
      CopyBuffer(rsi_handler,0,0,fetch,temp_buffer);
      if(set_as_series)
         ArraySetAsSeries(temp_buffer,true);

      //--- Fill in our values iteratively
      for(int i = rsi_differenced_buffer_size;i > 1; i--)
        {
         rsi_differenced_values[i-1] = temp_buffer[i-1] - temp_buffer[i-1+differencing_period];
        }

      //--- If the norm of a vector is 0, the vector is empty!
      if(rsi_differenced_values.Norm(VECTOR_NORM_P) != 0)
        {
         Print(user_feedback(2));
         indicator_differenced_values_initialized = true;
         return(true);
        }

      indicator_differenced_values_initialized = false;
      Print(user_feedback(3));
      return(false);
     }

   //--- Get a differenced value at a specific index
   double            GetDifferencedReadingAt(int index)
     {
      //--- Make sure we're not trying to call values beyond our index
      if(index > rsi_differenced_buffer_size)
        {
         Print(user_feedback(4));
         return(-1e10);
        }

      //--- Make sure our values have been set
      if(!indicator_differenced_values_initialized)
        {

         //--- The user is trying to use values before they were set in memory
         Print(user_feedback(1));
         return(-1e10);
        }

      //--- Return the differenced value of our indicator at a specific index
      if((indicator_differenced_values_initialized) && (index < rsi_differenced_buffer_size))
         return(rsi_differenced_values[index]);

      //--- Something went wrong.
      return(-1e10);
     }
  };

//--- How far we want to forecast
#define HORIZON 10

//--- Our handlers for our indicators
int rsi_5_handle;

//--- Data structures to store the readings from our indicators
double rsi_5_reading[];

//--- File name
string file_name = Symbol() + " Testing RSI Class.csv";

//--- Amount of data requested
input int size = 3000;

//+------------------------------------------------------------------+
//| Our script execution                                             |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Testing the RSI Class
//--- Initialize the class
   RSI my_rsi(Symbol(),PERIOD_CURRENT,5,PRICE_CLOSE);
   my_rsi.SetIndicatorValues(size,true);
   my_rsi.SetDifferencedIndicatorValues(size,10,true);

//---Setup our technical indicators
   rsi_5_handle  = iRSI(Symbol(),PERIOD_CURRENT,5,PRICE_CLOSE);
   int fetch = size + (2 * HORIZON);

//---Set the values as series
   CopyBuffer(rsi_5_handle,0,0,fetch,rsi_5_reading);
   ArraySetAsSeries(rsi_5_reading,true);

//---Write to file
   int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");

   for(int i=size;i>=1;i--)
     {
      if(i == size)
        {
         FileWrite(file_handle,"Time","RSI 5","RSI 5 Class","RSI 5 Difference","RSI 5 Class Difference");
        }

      else
        {
         FileWrite(file_handle,
                   iTime(_Symbol,PERIOD_CURRENT,i),
                   rsi_5_reading[i],
                   my_rsi.GetReadingAt(i),
                   rsi_5_reading[i]  - rsi_5_reading[i + HORIZON],
                   my_rsi.GetDifferencedReadingAt(i)
                  );
        }
     }
//--- Close the file
   FileClose(file_handle);
  }
//+------------------------------------------------------------------+
#undef  HORIZON