Having a small problem converting mql4 indicator to mql5

 

Hi everyone, so I want to convert the mql4 code of an indicator to mql5 code


The indicator name is Oscar, it's basically a two lines cross indicator, first line is an oscillator and the second line is the moving average of the oscillator.


Here is how the indicator is calculated according to the author of the indicator:

""

let A = the highest high of the last eight days (including today)

let B = the lowest low of the past eight days (including today)

let C = today's closing price

let X = yesterday's oscillator figure (Oscar)

Today's "rough" oscillator equals (C-B) divided by (A-B) times 100.

Next we "smooth" our rough number (let's call it Y) like this:

Y = ((X divided by 3) times 2), plus (rough divided by 3).


Let’s do an example. We’ll use a common stock, United Technology. The program was

originally written for stocks, but works equally well with the Forex market.

Turn to the next page. Go to April 8th. Count back eight days. The high for that period is

91.32 ( just happened to be today). The low is 84.75, made on March 30th (just happened

to be 8 days back). Today's close is 89.80.

Yesterday's oscillator (shown in the second column), is 87.

Today's closing price, 89.80 minus the 8 day low, 84.75, equals 5.05.

The 8 day high, 91.32, minus the 8 day low, 84.75, equals 6.57.

5.05 divided by 6.57 equals 0.769. 0.769 times 100 equals 76.9.

87 divided by 3 equals 29.00. 29.00 times 2 equals 58.00.

76.9 divided by 3 equals 25.63.

58.00 + 25.63 = 83.63, or 84 rounded off.

""


Here is the mql4 code of the indicator I found online


//+------------------------------------------------------------------+
//|                                                        Oscar.mq4 |
//+------------------------------------------------------------------+

#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_level1 75
#property indicator_level2 25
#property indicator_levelcolor DarkSlateGray

#property indicator_buffers 2
#property indicator_color1 Blue
#property indicator_color2 Red

//---- input parameters
extern  int iLookBack = 8;
extern int OscarAve = 5;
extern int highstop= 3;
extern int lowstop = 2;


/* unused code
double  spr, pnt, tickval;
int     dig;
string  IndiName, ccy;
*/

string  IndiName;

//---- buffers
double Buffer1[];
double Buffer2[];
double Buffer3[];
double Buffer4[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int init()   {
   IndicatorBuffers(2);
//---- indicators



 //----
//   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, indicator_color1);
   SetIndexBuffer(0, Buffer1);
//   SetIndexStyle(1, DRAW_LINE, STYLE_DOT, 0, indicator_color2);
   SetIndexBuffer(1, Buffer2);
  
/* useless code  
  int checksum = 0;
  string str = "1";
  for (int i=0; i<StringLen(str); i++)  
    checksum += i * StringGetChar(str,i);
  IndiName = "Oscar-" + checksum;
*/

  IndiName = "Oscar(" + iLookBack + ")";
  IndicatorShortName(IndiName);
  SetIndexLabel(1,"MA("+OscarAve+")");

/* useless code
  ccy     = Symbol();
  pnt     = MarketInfo(ccy,MODE_POINT);
  dig     = MarketInfo(ccy,MODE_DIGITS);
  spr     = MarketInfo(ccy,MODE_SPREAD);
  tickval = MarketInfo(ccy,MODE_TICKVALUE);
  if (dig == 3 || dig == 5) {
    pnt     *= 10;
    spr     /= 10;
    tickval *= 10;
  }
*/

  return(0);
}

//+------------------------------------------------------------------+
int deinit()  {
//+------------------------------------------------------------------+
  return(0);
}

//+------------------------------------------------------------------+
int start()  {
  double Y = 0;
  for (int i=Bars-(iLookBack+2); i>=0; i--)  {
    double X = Y;
    double A = 0;
    double B = 999999;
    for(int j=i; j<i+iLookBack; j++)   {
      A = MathMax(High[j],A);
      B = MathMin(Low[j],B);
    }
    double C = Close[i];  
    double rough  = (C-B)/(A-B)*100;
    Y = X/3*2 + rough/3;
    Buffer1[i] = Y;
    Buffer3[i] = (A + highstop/10000); 
    Buffer4[i] = (B- lowstop/10000);    
  }
        i = Bars - (iLookBack+2);
        while(i>=0)
        {
                Buffer2[i]=iMAOnArray(Buffer1,0,OscarAve,0,MODE_LWMA,i);
                i--;    
        }
  return(0);
}



And here is my Attempt to write an mql5 code for the indicator


//+------------------------------------------------------------------+
//|                                                         mmaa.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <MovingAverages.mqh>

#property version   "1.00"
#property indicator_buffers 4
#property indicator_plots   2
#property indicator_separate_window

#property indicator_label1 "Oscar"
#property indicator_type1 DRAW_LINE
#property indicator_color1 Blue

#property indicator_label2 "Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 Red


input  int iLookBack = 8;
input int OscarAve = 5;
input int highstop= 3;
input int lowstop = 2;
double Y = 0;

double Buffer1[];
double Buffer2[];
double Buffer3[];
double Buffer4[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0, Buffer1,INDICATOR_DATA);
   SetIndexBuffer(1, Buffer2,INDICATOR_DATA);
   SetIndexBuffer(2, Buffer3,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, Buffer4,INDICATOR_CALCULATIONS);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,iLookBack-1);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   int weights=1;
   int start;
   if(rates_total < iLookBack-1)
   return(0);
   if(prev_calculated==0) start=iLookBack-1;
   else start=prev_calculated-1;
   for(int i=start; i<rates_total; i++)
   {
    double X = Y;
    double A = 0;
    double B = 9999;
    for(int j=i; j+iLookBack>i; j--)   
    {
      A = MathMax(high[j],A);
      B = MathMin(low[j],B);
    }
    double C = close[i];
    double rough  = ((C-B)/(A-B))*100;
    Y = (X/3)*2 + (rough/3);
    Buffer1[i] = Y;
    Buffer3[i] = (A + highstop/10000);
    Buffer4[i] = (B- lowstop/10000);
   }
   LinearWeightedMAOnBuffer(rates_total,prev_calculated,
                            iLookBack,OscarAve,Buffer1,Buffer2,weights);
   
   return(rates_total);
  }
  


When I compare the output of the buffers for both the original mql4 version and the version I developed for mql5, the outputs differs slightly, like mql4 version will give value of 80 but mql5 will give 85, but then when it comes to most recent value the mql5 behave weirdly, like it output totally unexpected value, and the moving average line gives very high number like 800 or 900 even though all previous numbers were less than 100.

So can anyone help me, I think the code needs very little tweaking then it will work perfectly, but can't find where is the problem in the code.


 
//+------------------------------------------------------------------+
//|                                                         mmaa.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <MovingAverages.mqh>

#property version   "1.00"
#property indicator_buffers 2 //4
#property indicator_plots   2
#property indicator_separate_window

#property indicator_label1 "Oscar"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrBlue  //Blue

#property indicator_label2 "Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrRed   //Red


input int   iLookBack   = 8;
input int   OscarAve    = 5;
input int   highstop    = 3;
input int   lowstop     = 2;
//double Y = 0;

double      Buffer1[];
double      Buffer2[];
//double Buffer3[];
//double Buffer4[];
int         weights = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
   SetIndexBuffer(0, Buffer1, INDICATOR_DATA);
   SetIndexBuffer(1, Buffer2, INDICATOR_DATA);
   //SetIndexBuffer(2, Buffer3, INDICATOR_CALCULATIONS);
   //SetIndexBuffer(3, Buffer4, INDICATOR_CALCULATIONS);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, iLookBack);

   for (int i = 1; i <= OscarAve; i++)
   {
      for (int j = i; j <= OscarAve; j++)
         weights += 1;
   }
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
//---
   //int weights = 1;
   int    start = iLookBack;
   double Y = 0;

   if(rates_total < iLookBack)
      return(0);
   //if(prev_calculated == 0) start = iLookBack - 1;
   //else start = prev_calculated - 1;

   for(int i = start; i < rates_total; i++)
   {
      double X = Y;
      double A = high[ArrayMaximum(high, i - iLookBack + 1, iLookBack)];
      double B =  low[ArrayMinimum(low,  i - iLookBack + 1, iLookBack)];

      /*for(int j = i; j + iLookBack > i; j--)
      {
         A = MathMax(high[j], A);
         B = MathMin(low[j], B);
      }*/

      double C = close[i];
      double rough  = ((C - B) / (A - B)) * 100;

      Y = (X / 3) * 2 + (rough / 3);
      Buffer1[i] = Y;
      //Buffer3[i] = (A + highstop / 10000);
      //Buffer4[i] = (B - lowstop / 10000);
   }

   LinearWeightedMAOnBuffer(rates_total, prev_calculated, iLookBack, OscarAve, Buffer1, Buffer2, weights);

   return(rates_total);
}
//+------------------------------------------------------------------+
 
Nagisa Unada:
Thank you very much. That was really helpful.



I guess now I know what was the problem, there were actually two mistakes in the code I posted above.



First one was the value of "weights" for the linear weighted moving average, can you tell me why you specifically made it this way? I mean inside two nested loop, is there some logic or math calculations behind it.



The second problem was that for the most recent value of the oscar line, the X value was recalculated with every new tick, so now X will be the the value of Y of the previous tick, when it's supposed to be equal to the value of Y of the previous Bar not previous tick.


You solved this problem by calculating the values of the indicator from first bar up to the most recent bar with each new tick, which solves the miscalculation problem, but it has one pitfall, it's inefficient, we don't need to calculate all values with each new tick, we only need to calculate the value of oscar for most recent bar.


So what I did to solve the problem is by making X and Y dynamics array, and for each bar there is a corresponding value of X and Y, so when we want to calculate the value of X[i], we use Y[i-1], by doing that we ensure that the value of X is actually the value of oscar of the previous bar and not the previous tick. and then used prev_calculated like I did in my earlier post.



Anyway your post was absolutely helpful, thanks again

 

About the weights:

Bar1          1  --> 1

Bar2        11  --> 2

Bar3      111  --> 3

Bar4    1111  --> 4

Bar5  11111  --> 5

The "LWMA" weights are as above when it's "Period" is five. The sum of these weights is the "weights".

I made this into a program as it is, but it can be more easily calculated as "1 + 2 + 3 + 4 + 5". 

for (int i = 1; i <= OscarAve; i++)
   weights += i;
   /*{
      for (int j = i; j <= OscarAve; j++)
         weights += 1;
   }*/

About the XY:

"Buffer1[i] = Y;", as you point out, it can be "X = Buffer1[i - 1];". 

In this case, the value of Buffer1[i - 1] is already fixed and does not need to be recalculated.

In the end, I can rewrite it as follows. Maybe there will be no problem.

{
//---
   int start;

   if(rates_total < iLookBack)
      return(0);
   if(prev_calculated == 0)
   {
      start = iLookBack;
      Buffer1[start - 1] = 0.0;
   }
   else
      start = prev_calculated - 1;

   for(int i = start; i < rates_total; i++)
   {
      double X = Buffer1[i - 1];
      double A = high[ArrayMaximum(high, i - iLookBack + 1, iLookBack)];
      double B =  low[ArrayMinimum(low,  i - iLookBack + 1, iLookBack)];

      double C = close[i];
      double rough  = ((C - B) / (A - B)) * 100;

      Buffer1[i] = (X / 3) * 2 + (rough / 3);
   }

   LinearWeightedMAOnBuffer(rates_total, prev_calculated, iLookBack, OscarAve, Buffer1, Buffer2, weights);

   return(rates_total);
}
Reason: