//+------------------------------------------------------------------+
//|                                                  NIKA_VO_g01.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//#property indicator_chart_window
#property indicator_separate_window
#property indicator_buffers 12
#property indicator_plots 12

//--- plot Indic
#property indicator_label1  "WhiteLine"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrWhite
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- plot SmaIndic
#property indicator_label2  "OrangeLine"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrange
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- plot Indic
#property indicator_label3  "HA-"
#property indicator_type3   DRAW_NONE
#property indicator_color3  clrNONE
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//---
#property indicator_label7  "Calc"
#property indicator_type7   DRAW_LINE
#property indicator_color7  clrNONE
#property indicator_style7  STYLE_SOLID
#property indicator_width7  1
//--- plot SmaIndic
#property indicator_label8  "CalcLineReg"
#property indicator_type8   DRAW_LINE
#property indicator_color8  clrNONE
#property indicator_style8  STYLE_SOLID
#property indicator_width8  1

enum history_mode
  {
   history_Time, // Time
   history_Days // Days
  };
history_mode InpHistoryMode = history_Days; // History Mode
datetime InpLimitTime = D'2020.01.01.'; // From Date


input bool InpEnableHA = true; // Enable Heiken Ashi
input int InpPeriodSma = 3; // SMA Period

string _level = "<><><>LEVEL LINE<><><>"; // <><><><><><>
color InpLevelColor = clrWhite; // Color
int InpLevelWidth = 2; // Width
ENUM_LINE_STYLE InpLevelStyle = STYLE_SOLID; // Style

string _sma = "<><><>SMA LINE<><><>"; // <><><><><><>
color InpSmaColor = clrOrange; // Color
int InpSmaWidth = 2; // Width
ENUM_LINE_STYLE InpSmaStyle = STYLE_SOLID; // Style

string _additional = "<><><>ADDITIONAL LINE<><><>"; // <><><><><><>
color InpAdditionalColor = clrWhite; // Color
int InpAdditionalWidth = 1; // Width
ENUM_LINE_STYLE InpAdditionalStyle = STYLE_SOLID; // Style

string _belowaboveline = "<><><>BELOW/ABOVE LINE<><><>"; // <><><><><><>
enum abovebelowcolormode
  {
   abovebelowcolormode_disable, // DISABLE
   abovebelowcolormode_full, // OPTION-1 FULL
   abovebelowcolormode_separate, // OPTION-2 SEPARATE
  };
abovebelowcolormode InpAboveBelowColorMode = abovebelowcolormode_disable; // Above/Below Background Color
color InpAboveColor = clrDarkGreen; // Above Zero Color
color InpBelowColor = clrDarkRed; // Below Zero Color

bool InpBacktest = false; // BackTest MODE
int InpLimit = 100000; // Candles Count

double ExtOBuffer[];
double ExtHBuffer[];

bool stat = true;
double ExtLBuffer[];
double ExtCBuffer[];

double         IndicBuffer[];
double         SmaIndicBuffer[];
double         Calc[];
double         CalcLinReg[];

double bull_ar[], bear_ar[];
double bull_bear_ar[], bbp_ar[];

int pidi;
int ha_pidi;

input double length = 13.0;  //Length
input    int offset= 0; //Offset
input int show_   =  0;    //Show on candle (0 - current, 1- previous)
input int InpLimitDays = 500; // From Days
int show=show_;
int smooth = 2;

int subwindow;
datetime last_candle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(show>1)
      show=1;

   int max_bars=iBars(_Symbol,PERIOD_D1)-1;

   last_candle = iTime(_Symbol,PERIOD_D1,max_bars);

//--- indicator buffers mapping
   SetIndexBuffer(0,IndicBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,SmaIndicBuffer,INDICATOR_DATA);

   SetIndexBuffer(2,ExtOBuffer,INDICATOR_DATA);
   SetIndexBuffer(3,ExtHBuffer,INDICATOR_DATA);
   SetIndexBuffer(4,ExtLBuffer,INDICATOR_DATA);
   SetIndexBuffer(5,ExtCBuffer,INDICATOR_DATA);
   PlotIndexSetString(2,PLOT_LABEL,"HA-Open");
   PlotIndexSetString(3,PLOT_LABEL,"HA-High");
   PlotIndexSetString(4,PLOT_LABEL,"HA-Low");
   PlotIndexSetString(5,PLOT_LABEL,"HA-Close");

   SetIndexBuffer(6,Calc,INDICATOR_DATA);
   SetIndexBuffer(7,CalcLinReg,INDICATOR_DATA);

   SetIndexBuffer(8,bull_ar,INDICATOR_DATA);
   SetIndexBuffer(9,bear_ar,INDICATOR_DATA);
   SetIndexBuffer(10,bull_bear_ar,INDICATOR_DATA);
   SetIndexBuffer(11,bbp_ar,INDICATOR_DATA);

   ArraySetAsSeries(ExtOBuffer,stat);
   ArraySetAsSeries(ExtCBuffer,stat);
   ArraySetAsSeries(ExtLBuffer,stat);
   ArraySetAsSeries(ExtHBuffer,stat);
   ArraySetAsSeries(IndicBuffer,stat);
   ArraySetAsSeries(SmaIndicBuffer,stat);
   ArraySetAsSeries(Calc,stat);
   ArraySetAsSeries(CalcLinReg,stat);

   ArraySetAsSeries(bull_ar,stat);
   ArraySetAsSeries(bear_ar,stat);
   ArraySetAsSeries(bull_bear_ar,stat);
   ArraySetAsSeries(bbp_ar,stat);

   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

   IndicatorSetInteger(INDICATOR_LEVELS,1);
   IndicatorSetInteger(INDICATOR_LEVELCOLOR,0,InpLevelColor);
   IndicatorSetInteger(INDICATOR_LEVELWIDTH,0,InpLevelWidth);
   IndicatorSetInteger(INDICATOR_LEVELSTYLE,0,InpLevelStyle);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.0);


   PlotIndexSetInteger(0,PLOT_LINE_COLOR,InpAdditionalColor);
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,InpAdditionalWidth);
   PlotIndexSetInteger(0,PLOT_LINE_STYLE,InpAdditionalStyle);

   PlotIndexSetInteger(1,PLOT_LINE_COLOR,InpSmaColor);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,InpSmaWidth);
   PlotIndexSetInteger(1,PLOT_LINE_STYLE,InpSmaStyle);

   PlotIndexSetInteger(8,PLOT_DRAW_TYPE,DRAW_NONE);
   PlotIndexSetInteger(9,PLOT_DRAW_TYPE,DRAW_NONE);
   PlotIndexSetInteger(10,PLOT_DRAW_TYPE,DRAW_NONE);
   PlotIndexSetInteger(11,PLOT_DRAW_TYPE,DRAW_NONE);

   PlotIndexSetString(8,PLOT_LABEL,"bull_ar");
   PlotIndexSetString(9,PLOT_LABEL,"bear_ar");
   PlotIndexSetString(10,PLOT_LABEL,"bull_bear_ar");
   PlotIndexSetString(11,PLOT_LABEL,"bbp_ar");

   IndicatorSetString(INDICATOR_SHORTNAME,"NIKA_Backtest_"+_Symbol+"_"+EnumToString(Period()));

   pidi = iMA(_Symbol,PERIOD_CURRENT,(int)length,0,MODE_EMA,PRICE_CLOSE);
   ha_pidi = iCustom(_Symbol,PERIOD_CURRENT,"Heiken_Ashi");


   ArrayInitialize(IndicBuffer,EMPTY_VALUE);
   ArrayInitialize(SmaIndicBuffer,EMPTY_VALUE);

   return(INIT_SUCCEEDED);
  }

datetime last_change;
//+------------------------------------------------------------------+
//| 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[])
  {
//---
   if(InpBacktest == true)
     {
      if(iTime(_Symbol,PERIOD_D1,0) < last_candle)
        {
         return rates_total;
        }
     }
   else
     {
      if(last_candle == iTime(_Symbol,PERIOD_CURRENT,0))
        {
         return rates_total;
        }
     }
   last_candle = iTime(_Symbol,PERIOD_CURRENT,0);

   CopyBuffer(ha_pidi,0,0,Bars(_Symbol,PERIOD_CURRENT),ExtOBuffer);
   CopyBuffer(ha_pidi,1,0,Bars(_Symbol,PERIOD_CURRENT),ExtHBuffer);
   CopyBuffer(ha_pidi,2,0,Bars(_Symbol,PERIOD_CURRENT),ExtLBuffer);
   CopyBuffer(ha_pidi,3,0,Bars(_Symbol,PERIOD_CURRENT),ExtCBuffer);

   ArraySetAsSeries(ExtOBuffer,stat);
   ArraySetAsSeries(ExtCBuffer,stat);
   ArraySetAsSeries(ExtLBuffer,stat);
   ArraySetAsSeries(ExtHBuffer,stat);
//---

   int limit= rates_total - prev_calculated;
   if(prev_calculated > 0)
     {
      limit++;
     }
   InpLimit = iBarShift(_Symbol,PERIOD_CURRENT,InpLimitTime);
   if(InpHistoryMode == history_Days)
     {
      InpLimit = iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,PERIOD_D1,InpLimitDays));
     }
   if(InpLimit > 0)
     {
      limit = MathMin(limit,InpLimit);
     }
   limit=rates_total;
//Print(InpLimit);
//--- EMA-fast
   double ema_ar[];
   int copy = CopyBuffer(pidi,0,0,Bars(_Symbol,PERIOD_CURRENT),ema_ar);
   CopyBuffer(pidi,1,0,Bars(_Symbol,PERIOD_CURRENT),ema_ar);
   ArraySetAsSeries(ema_ar,true);

   double close_[];
   ArraySetAsSeries(close_,true);
   ArrayResize(close_,limit,0);

   for(int i=limit-1; i>=0; i--)
     {
      if(i > rates_total-100)
        {
         continue;
        }
      double avg_highest_lowest;
      double ema;
      double avg_highest_lowest_ema;
      double val;
      double hh, ll;
      hh = -1;
      ll = 999999;
      for(int k=i; k<i+length; k++)
        {
         if(InpEnableHA)
           {
            hh = MathMax(hh, MathMax(ExtHBuffer[k],ExtLBuffer[k]));
            ll = MathMin(ll, MathMin(ExtHBuffer[k],ExtLBuffer[k]));
           }
         else
           {
            hh = MathMax(hh, iHigh(_Symbol,PERIOD_CURRENT,k));
            ll = MathMin(ll, iLow(_Symbol,PERIOD_CURRENT,k));
           }
        }
      avg_highest_lowest = (hh + ll) / 2.0;
      ema = ema_ar[i];
      avg_highest_lowest_ema = (avg_highest_lowest+ema) / 2.0;
      if(InpEnableHA)
        {
         val = ExtCBuffer[i] - avg_highest_lowest_ema;
        }
      else
        {
         val = iClose(NULL, 0, i) - avg_highest_lowest_ema;
        }
      Calc[i] = val;
     }
//int offset= 0;

   for(int j=0; j<=rates_total-100; j++)
     {
      double sumX = 0;
      double sumY = 0;
      double sumXY = 0;
      double sumX2 = 0;

      for(int i = 0; i < length; i++)
        {
         double price = Calc[j+i];
         sumX += i;
         sumY += price;
         sumXY += i * price;
         sumX2 += i * i;
        }

      double slope = (length * sumXY - sumX * sumY) / (length * sumX2 - sumX * sumX);
      double intercept = (sumY - slope * sumX) / length;
      double val = intercept + slope * (length - 1 - offset); // Linear regression value with offset

      if(offset!=0)
         CalcLinReg[j] = intercept + val;
         else
            CalcLinReg[j] = intercept;
     }

   IndicBuffer[0] = SmaIndicBuffer[0] = EMPTY_VALUE;
   for(int i=limit-1; i>=show; i--)
     {
      IndicBuffer[i] = CalcLinReg[i];
      //SmaIndicBuffer[i] = iMAOnArrayMQL4(CalcLinReg,19,3,0,MODE_EMA,i);
     }

   subwindow = ChartWindowFind(0,"NIKA_VO_"+_Symbol+"_"+EnumToString(Period()));
   int period= InpPeriodSma;
   int total = limit-1;
   double pr=2.0/(period+1);
   int    pos=total-2;
   pos = total;
   while(pos>=show)
     {
      if(pos >= rates_total-1)
        {
         pos--;
         continue;
        }
      if(pos==total-2)
         SmaIndicBuffer[pos+1]=CalcLinReg[pos+1];
      SmaIndicBuffer[pos]=CalcLinReg[pos]*pr+SmaIndicBuffer[pos+1]*(1-pr);

      if(InpAboveBelowColorMode==abovebelowcolormode_separate)
        {
         if(SmaIndicBuffer[pos] > IndicBuffer[pos] && SmaIndicBuffer[pos+1] <= IndicBuffer[pos+1])
           {
            string obj = "NIKA_VO_FULL"+(string)last_change;
            ObjectCreate(0,obj,OBJ_RECTANGLE,subwindow,0,0,0,0);
            ObjectSetInteger(0,obj,OBJPROP_TIME,0,last_change);
            ObjectSetInteger(0,obj,OBJPROP_TIME,1,iTime(_Symbol,PERIOD_CURRENT,pos));
            ObjectSetDouble(0,obj,OBJPROP_PRICE,0,-10000.0);
            ObjectSetDouble(0,obj,OBJPROP_PRICE,1,+10000.0);
            ObjectSetInteger(0,obj,OBJPROP_FILL,true);
            ObjectSetInteger(0,obj,OBJPROP_COLOR,InpAboveColor);
            ObjectSetInteger(0,obj,OBJPROP_BACK,true);
            last_change = iTime(_Symbol,PERIOD_CURRENT,pos);
           }
         if(SmaIndicBuffer[pos] < IndicBuffer[pos] && SmaIndicBuffer[pos+1] >= IndicBuffer[pos+1])
           {
            string obj = "NIKA_VO_FULL"+(string)last_change;
            ObjectCreate(0,obj,OBJ_RECTANGLE,subwindow,0,0,0,0);
            ObjectSetInteger(0,obj,OBJPROP_TIME,0,last_change);
            ObjectSetInteger(0,obj,OBJPROP_TIME,1,iTime(_Symbol,PERIOD_CURRENT,pos));
            ObjectSetDouble(0,obj,OBJPROP_PRICE,0,-10000.0);
            ObjectSetDouble(0,obj,OBJPROP_PRICE,1,+10000.0);
            ObjectSetInteger(0,obj,OBJPROP_FILL,true);
            ObjectSetInteger(0,obj,OBJPROP_COLOR,InpBelowColor);
            ObjectSetInteger(0,obj,OBJPROP_BACK,true);
            last_change = iTime(_Symbol,PERIOD_CURRENT,pos);
           }

         if(pos == 1)
           {
            if(SmaIndicBuffer[pos] < IndicBuffer[pos])
              {
               string obj = "NIKA_VO_FULL"+(string)last_change;
               ObjectCreate(0,obj,OBJ_RECTANGLE,subwindow,0,0,0,0);
               ObjectSetInteger(0,obj,OBJPROP_TIME,0,last_change);
               ObjectSetInteger(0,obj,OBJPROP_TIME,1,iTime(_Symbol,PERIOD_CURRENT,pos));
               ObjectSetDouble(0,obj,OBJPROP_PRICE,0,-10000.0);
               ObjectSetDouble(0,obj,OBJPROP_PRICE,1,+10000.0);
               ObjectSetInteger(0,obj,OBJPROP_FILL,true);
               ObjectSetInteger(0,obj,OBJPROP_COLOR,InpAboveColor);
               ObjectSetInteger(0,obj,OBJPROP_BACK,true);
              }
            if(SmaIndicBuffer[pos] > IndicBuffer[pos])
              {
               string obj = "NIKA_VO_FULL"+(string)last_change;
               ObjectCreate(0,obj,OBJ_RECTANGLE,subwindow,0,0,0,0);
               ObjectSetInteger(0,obj,OBJPROP_TIME,0,last_change);
               ObjectSetInteger(0,obj,OBJPROP_TIME,1,iTime(_Symbol,PERIOD_CURRENT,pos));
               ObjectSetDouble(0,obj,OBJPROP_PRICE,0,-10000.0);
               ObjectSetDouble(0,obj,OBJPROP_PRICE,1,+10000.0);
               ObjectSetInteger(0,obj,OBJPROP_FILL,true);
               ObjectSetInteger(0,obj,OBJPROP_COLOR,InpBelowColor);
               ObjectSetInteger(0,obj,OBJPROP_BACK,true);
              }
           }
        }
      pos--;
     }

//Comment(TimeLocal());
   if(InpAboveBelowColorMode==abovebelowcolormode_full)
     {
      //Comment("-"+TimeLocal());
      ObjectCreate(0,"NIKA_VO_FULL",OBJ_RECTANGLE,subwindow,0,0,0,0);
      ObjectSetInteger(0,"NIKA_VO_FULL",OBJPROP_TIME,0,iTime(_Symbol,PERIOD_CURRENT,Bars(_Symbol,PERIOD_CURRENT)-100));
      ObjectSetInteger(0,"NIKA_VO_FULL",OBJPROP_TIME,1,iTime(_Symbol,PERIOD_CURRENT,0));
      ObjectSetDouble(0,"NIKA_VO_FULL",OBJPROP_PRICE,0,-10000.0);
      ObjectSetDouble(0,"NIKA_VO_FULL",OBJPROP_PRICE,1,+10000.0);
      ObjectSetInteger(0,"NIKA_VO_FULL",OBJPROP_FILL,true);
      ObjectSetInteger(0,"NIKA_VO_FULL",OBJPROP_BACK,true);

      if(SmaIndicBuffer[1] < IndicBuffer[1])
        {
         ObjectSetInteger(0,"NIKA_VO_FULL",OBJPROP_COLOR,InpAboveColor);
        }
      else
        {
         ObjectSetInteger(0,"NIKA_VO_FULL",OBJPROP_COLOR,InpBelowColor);
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double iMAOnArrayMQL4(double &array[],
                      int total,
                      int period,
                      int ma_shift,
                      int shift)
  {
   double buf[],arr[];
   if(total==0)
      total=ArraySize(array);
   if(total>0 && total<=period)
      return(0);
   if(shift>total-period-ma_shift)
      return(0);

   if(ArrayResize(buf,total)<0)
      return(0);
   double pr=2.0/(period+1);
   int    pos=total-2;
   while(pos>=0)
     {
      if(pos==total-2)
         buf[pos+1]=array[pos+1];
      buf[pos]=array[pos]*pr+buf[pos+1]*(1-pr);
      pos--;
     }
   return(buf[shift+ma_shift]);

   return(0);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   last_candle = iTime(_Symbol,PERIOD_D1,1000);
   ObjectsDeleteAll(0,"NIKA_VO_",-1,-1);

   ArrayFree(ExtOBuffer);
   ArrayFree(ExtHBuffer);
   ArrayFree(ExtLBuffer);
   ArrayFree(ExtCBuffer);

   ArrayFree(Calc);
   ArrayFree(CalcLinReg);
   ArrayFree(IndicBuffer);

   IndicatorRelease(ha_pidi);
  }
//+------------------------------------------------------------------+
