//+------------------------------------------------------------------+
//|                                      Relative_Trend_Index_V0.mq4 |
//|                                  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 strict
#property indicator_separate_window

#property indicator_buffers 2
#property indicator_plots 2

#property indicator_color1 White
#property indicator_label1 "Relative Trend Index"
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1

#property indicator_color2 SaddleBrown
#property indicator_label2 "MA Relative Trend Index"
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1

enum enPrices
{
   pr_close,      // Close
   pr_open,       // Open
   pr_high,       // High
   pr_low,        // Low
   pr_median,     // Median
   pr_typical,    // Typical
   pr_weighted,   // Weighted
   pr_average,    // Average (high+low+open+close)/4
   pr_medianb,    // Average median body (open+close)/2
   pr_tbiased,    // Trend biased price
   pr_tbiased2,   // Trend biased (extreme) price
   pr_haclose,    // Heiken ashi close
   pr_haopen ,    // Heiken ashi open
   pr_hahigh,     // Heiken ashi high
   pr_halow,      // Heiken ashi low
   pr_hamedian,   // Heiken ashi median
   pr_hatypical,  // Heiken ashi typical
   pr_haweighted, // Heiken ashi weighted
   pr_haaverage,  // Heiken ashi average
   pr_hamedianb,  // Heiken ashi median body
   pr_hatbiased,  // Heiken ashi trend biased price
   pr_hatbiased2, // Heiken ashi trend biased (extreme) price
   pr_habclose,   // Heiken ashi (better formula) close
   pr_habopen ,   // Heiken ashi (better formula) open
   pr_habhigh,    // Heiken ashi (better formula) high
   pr_hablow,     // Heiken ashi (better formula) low
   pr_habmedian,  // Heiken ashi (better formula) median
   pr_habtypical, // Heiken ashi (better formula) typical
   pr_habweighted,// Heiken ashi (better formula) weighted
   pr_habaverage, // Heiken ashi (better formula) average
   pr_habmedianb, // Heiken ashi (better formula) median body
   pr_habtbiased, // Heiken ashi (better formula) trend biased price
   pr_habtbiased2 // Heiken ashi (better formula) trend biased (extreme) price
};


enum stdMethods
{
   std_custSam, // Custom - with sample correction
   std_custNos  // Custom - without sample correction
};

extern string             IndiPrefix                    = "444";            // Indicator Prefix
extern int                trend_data_count              = 240;              // Trend Length
extern int                MaxBarsMulti                  = 10;               // Max Bars as Multiplier of Trend Length (0 = ignore)
extern enPrices           RTIPrice                      = pr_close;         // RTI price to use
extern int                RTISmoothLength               = 15;               // RTI Jurik Smoothening Length
extern double             RTIPhase                      = 0;                // RTI Jurik phase
extern bool               RTIDouble                     = false;            // RTI Double Smooth
extern int                trend_sensitivity_percentage  = 98;               // Sensitivity
extern int                signal_length                 = 60;               // Signal Length
extern int                std_dev_length                = 2;                // Dev.Standard Length (2 default)
extern stdMethods         BandsDeviation1Type1          = std_custSam;      // Deviation calculation type
extern double             os                            = 20;               // Oversold
extern double             ob                            = 80;               // Overbought
extern color              obcolor                       = Blue;             // Overbought color
extern color              oscolor                       = OrangeRed;        // Oversold color
extern int                obwidth                       = 5;                // Overbought fill width
extern int                oswidth                       = 5;                // Oversold fill width
extern color              LevelsColoru                  = C'96,96,96';      // Level up color
extern color              LevelsColord                  = C'96,96,96';      // Level down color
extern color              LevelsColorm                  = C'96,96,96';      // Level mid color
extern int                Levelswu                      = 1;                // Level up width
extern int                Levelswd                      = 1;                // Level down width
extern int                Levelswm                      = 1;                // Level mid width
extern ENUM_LINE_STYLE    Levelssu                      = STYLE_DASH;       // Level up style
extern ENUM_LINE_STYLE    Levelssd                      = STYLE_DASH;       // Level down style
extern ENUM_LINE_STYLE    Levelssm                      = STYLE_DASH;       // Level mid style

extern bool               RTIcrossOB                    = false;            // RTI cross OB
extern bool               RTIcrossOS                    = false;            // RTI cross OB
extern bool               RTIcrossMA                    = true;             // RTI cross MA

extern bool               Alerts                        = false;            // pop-up Alert
extern bool               Phone                         = false;            // Phone Alert 
extern bool               Email                         = false;            // e-mail Alert

double upper_trend[], lower_trend[], src[], stdv[], upper_array[], lower_array[], RelativeTrendIndex[];// upper_index[], lower_index[];
double UpperTrend[], LowerTrend[], MA_RelativeTrendIndex[];
int myBars, upper_index, lower_index, width=0, mywindow, maxbarsmult;
datetime newtime=0;
color BGColor;
string Indiname="Relative_Trend_Index", OBJName="";
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   IndicatorShortName(Indiname + "_" + Symbol() + IndiPrefix);
   mywindow = WindowFind(Indiname + "_" + Symbol() + IndiPrefix);
   //Print("Windiw: " ,mywindow);
      //--- indicator buffers mapping
   ArraySetAsSeries(UpperTrend, true); ArraySetAsSeries(LowerTrend, true);ArraySetAsSeries(MA_RelativeTrendIndex, true);
   ArraySetAsSeries(upper_trend, true); ArraySetAsSeries(lower_trend, true);
   ArraySetAsSeries(upper_array, true); ArraySetAsSeries(lower_array, true);ArraySetAsSeries(RelativeTrendIndex, true);
   //ArraySetAsSeries(upper_index, true); ArraySetAsSeries(lower_index, true);
   
   if (Alerts || Phone || Email)
      {
      if (!GlobalVariableCheck(Indiname + "RTIcrossOB" + Symbol() + IndiPrefix) && RTIcrossOB) 
         GlobalVariableSet(Indiname + "RTIcrossOB" + Symbol() + IndiPrefix, TimeCurrent());
         
      if (!GlobalVariableCheck(Indiname + "RTIcrossOS" + Symbol() + IndiPrefix) && RTIcrossOS) 
         GlobalVariableSet(Indiname + "RTIcrossOS" + Symbol() + IndiPrefix, TimeCurrent());
         
      if (!GlobalVariableCheck(Indiname + "RTIcrossMA" + Symbol() + IndiPrefix) && RTIcrossMA) 
         GlobalVariableSet(Indiname + "RTIcrossMA" + Symbol() + IndiPrefix, TimeCurrent());      
      }   
   
   //Ensure number of historical bars is at least 2 times the Trend Length
   if(MaxBarsMulti!=0) maxbarsmult = MathMax(MaxBarsMulti,2);
   
   width = (int) ChartGetInteger(ChartID(), CHART_SCALE)+1; 
   width=5;
   BGColor = (color) ChartGetInteger(ChartID(), CHART_COLOR_BACKGROUND);
   
   SetIndexBuffer(0, RelativeTrendIndex);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexEmptyValue(0, EMPTY_VALUE);

   SetIndexBuffer(1, MA_RelativeTrendIndex);
   SetIndexStyle(1, DRAW_LINE);
   SetIndexEmptyValue(1, EMPTY_VALUE);
   
   Indiname = Indiname + IndiPrefix;
   
   OBJName = Indiname + "ob_Level";
   ObjectCreate(ChartID(), OBJName, OBJ_HLINE, mywindow, 0, 0); //Print("error: ", GetLastError());
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[0]);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, LevelsColoru);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_WIDTH, Levelswu);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_STYLE, Levelssu);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, true);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
   ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, ob);
   
   OBJName = Indiname + "ob_Level_text";
   ObjectCreate(ChartID(), OBJName, OBJ_TEXT, mywindow, 0, 0);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[0]);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, LevelsColoru);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, false);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
   ObjectSetString(ChartID(), OBJName, OBJPROP_TEXT, DoubleToString(ob,1));
   ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, ob);
   
   OBJName = Indiname + "os_Level";
   ObjectCreate(ChartID(), OBJName, OBJ_HLINE, mywindow, 0, 0);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[0]);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, LevelsColord);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_WIDTH, Levelswd);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_STYLE, Levelssd);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, true);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
   ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, os);
   
   OBJName = Indiname + "os_Level_text";
   ObjectCreate(ChartID(), OBJName, OBJ_TEXT, mywindow, 0, 0);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[0]);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, LevelsColord);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, true);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
   ObjectSetString(ChartID(), OBJName, OBJPROP_TEXT, DoubleToString(os,1));
   ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, os);
   
   OBJName = Indiname + "mid_Level";
   ObjectCreate(ChartID(), OBJName, OBJ_HLINE, mywindow, 0, 0);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[0]);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, LevelsColorm);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_WIDTH, Levelswm);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_STYLE, Levelssm);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, true);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
   ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, 50);
   
   OBJName = Indiname + "mid_Level_text";
   ObjectCreate(ChartID(), OBJName, OBJ_TEXT, mywindow, 0, 0);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[0]);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, LevelsColorm);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);   
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, true);
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
   ObjectSetString(ChartID(), OBJName, OBJPROP_TEXT, "50.0");
   ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, 50);

   newtime=0;
   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[])
{
   if (iTime(Symbol(), Period(), 0) == newtime) return(rates_total);
   
   upper_index = (int) MathRound((double) trend_sensitivity_percentage / 100 * trend_data_count) - 1;
   lower_index = (int) MathRound((double) (100 - trend_sensitivity_percentage) / 100 * trend_data_count) - 1;

   myBars = iBars(Symbol(), Period());
   if(MaxBarsMulti!=0) myBars = MathMin(iBars(Symbol(), Period()), maxbarsmult*trend_data_count);

   ArrayResize(src, myBars); ArrayInitialize(src, 0); ArraySetAsSeries(src, true);
   ArrayResize(stdv, ArraySize(src)); 
   ArrayResize(upper_trend, myBars); ArrayResize(lower_trend, myBars); 
   //ArrayResize(upper_trend, trend_data_count); ArrayResize(lower_trend, trend_data_count); 
   
   for (int i=myBars-1; i>=0; i--)
      {
      src[i]  = iDSmooth(getPrice(RTIPrice,Open,Close,High,Low,i,Bars),RTISmoothLength,RTIPhase,RTIDouble,i);
      stdv[i] = iDeviation(src[i],std_dev_length,BandsDeviation1Type1==std_custSam,i);
      upper_trend[i] = src[i] + stdv[i];
      lower_trend[i] = src[i] - stdv[i];
      }

   ArrayResize(UpperTrend, myBars); ArrayResize(LowerTrend, myBars); 
   ArrayResize(RelativeTrendIndex, myBars); ArrayResize(MA_RelativeTrendIndex, myBars); 
   ArrayInitialize(RelativeTrendIndex, EMPTY_VALUE);ArrayInitialize(MA_RelativeTrendIndex, EMPTY_VALUE);
   
   for (int j=0; j<myBars-trend_data_count; j++)
      {
      ArrayCopy(upper_array, upper_trend, 0, j, trend_data_count);
      ArrayCopy(lower_array, lower_trend, 0, j, trend_data_count);
      
      ArraySort(upper_array, WHOLE_ARRAY, 0, MODE_ASCEND);
      ArraySort(lower_array, WHOLE_ARRAY, 0, MODE_ASCEND);
      
      UpperTrend[j] = upper_array[upper_index];
      LowerTrend[j] = lower_array[lower_index];
      
      if (UpperTrend[j]!=LowerTrend[j])
         RelativeTrendIndex[j] = ((src[j] - LowerTrend[j]) / (UpperTrend[j] - LowerTrend[j]))*100;
      }
   
   TV_EMA(MA_RelativeTrendIndex, RelativeTrendIndex, signal_length);
   ObjectsDeleteAll(ChartID(), Indiname, mywindow, OBJ_TREND);
   width = (int) ChartGetInteger(ChartID(), CHART_SCALE)+2; 
   
   for (int i=myBars-1; i>=1; i--)
      {
      if (RelativeTrendIndex[i]>ob)
         {
         OBJName = Indiname + "ob"+IntegerToString(i);
         ObjectCreate(ChartID(), OBJName, OBJ_TREND, mywindow, 0, 0);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[i]);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 1, Time[i]);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, obcolor);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_RAY, false);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_WIDTH, obwidth);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, true);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
         ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, RelativeTrendIndex[i]);
         ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 1, ob);
         }
      else if (RelativeTrendIndex[i]<os)
         {
         OBJName = Indiname + "os"+IntegerToString(i);
         ObjectCreate(ChartID(), OBJName, OBJ_TREND, mywindow, 0, 0);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 0, Time[i]);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, 1, Time[i]);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_COLOR, oscolor);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_RAY, false);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_WIDTH, oswidth);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_BACK, true);
         ObjectSetInteger(ChartID(), OBJName, OBJPROP_SELECTABLE, false);
         ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, RelativeTrendIndex[i]);
         ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 1, os);
         }
      }
   
   if (RTIcrossOB && Time[0]>GlobalVariableGet(Indiname + "RTIcrossOB" + Symbol() + " M" + (string) Period() + IndiPrefix)) 
      {
      if (RelativeTrendIndex[2]<ob && RelativeTrendIndex[1]>=ob) 
         {
         SendAlert("RTI crosses Overbought up for " + Symbol() + " M" + (string) Period() + " @" + TimeToString(Time[0], TIME_DATE|TIME_SECONDS));
         GlobalVariableSet(Indiname + "RTIcrossOB" + Symbol() + " M" + (string) Period() + IndiPrefix, Time[0]);
         }
      else if (RelativeTrendIndex[2]>=ob && RelativeTrendIndex[1]<ob) 
         {
         SendAlert("RTI crosses Overbought down for " + Symbol() + " M" + (string) Period() + " @" + TimeToString(Time[0], TIME_DATE|TIME_SECONDS));
         GlobalVariableSet(Indiname + "RTIcrossOB" + Symbol() + " M" + (string) Period() + IndiPrefix, Time[0]);
         }
      }   
   
   if (RTIcrossOS && Time[0]>GlobalVariableGet(Indiname + "RTIcrossOS" + Symbol() + " M" + (string) Period() + IndiPrefix)) 
      {
      if (RelativeTrendIndex[2]<=os && RelativeTrendIndex[1]>os) 
         {
         SendAlert("RTI crosses Oversold up for " + Symbol() + " M" + (string) Period() + " @" + TimeToString(Time[0], TIME_DATE|TIME_SECONDS));
         GlobalVariableSet(Indiname + "RTIcrossOS" + Symbol() + " M" + (string) Period() + IndiPrefix, Time[0]);
         }
      else if (RelativeTrendIndex[2]>os && RelativeTrendIndex[1]<=os) 
         {
         SendAlert("RTI crosses Oversold down for " + Symbol() + " M" + (string) Period() + " @" + TimeToString(Time[0], TIME_DATE|TIME_SECONDS));
         GlobalVariableSet(Indiname + "RTIcrossOS" + Symbol() + " M" + (string) Period() + IndiPrefix, Time[0]);
         }
      }   
   
   if (RTIcrossMA && Time[0]>GlobalVariableGet(Indiname + "RTIcrossMA" + Symbol() + " M" + (string) Period()+ IndiPrefix)) 
      {
      if (RelativeTrendIndex[2]<MA_RelativeTrendIndex[2] && RelativeTrendIndex[1]>=MA_RelativeTrendIndex[1]) 
         {
         SendAlert("RTI crosses MA RTI up for " + Symbol() + " M" + (string) Period() + " @" + TimeToString(Time[0], TIME_DATE|TIME_SECONDS));
         GlobalVariableSet(Indiname + "RTIcrossMA" + Symbol() + " M" + (string) Period() + IndiPrefix, Time[0]);
         }
      else if (RelativeTrendIndex[2]>=MA_RelativeTrendIndex[2] && RelativeTrendIndex[1]<MA_RelativeTrendIndex[1]) 
         {
         SendAlert("RTI crosses MA RTI down for " + Symbol() + " M" + (string) Period() + " @" + TimeToString(Time[0], TIME_DATE|TIME_SECONDS));
         GlobalVariableSet(Indiname + "RTIcrossMA" + Symbol() + " M" + (string) Period() + IndiPrefix, Time[0]);
         }
      }   
      
   
   newtime = iTime(Symbol(), Period(), 0);
   return(rates_total);
}
//+------------------------------------------------------------------+
void SendAlert(string S)
{
   if (Alerts) Alert(S);
   if (Phone) SendNotification(S);
   if (Email) SendMail(S, S);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if (reason==1 || reason==4) 
      {
      ObjectsDeleteAll(ChartID(), Indiname, mywindow, -1);
      GlobalVariablesDeleteAll(Indiname + "RTIcrossOB" + Symbol() + " M" + (string) Period() + IndiPrefix);
      GlobalVariablesDeleteAll(Indiname + "RTIcrossOS" + Symbol() + " M" + (string) Period() + IndiPrefix);
      GlobalVariablesDeleteAll(Indiname + "RTIcrossMA" + Symbol() + " M" + (string) Period() + IndiPrefix);
   
      EventKillTimer();
      }
}

//+------------------------------------------------------------------+
void TV_SMA(double & sma_src[], double & out[], int length)
{
   int size = ArraySize(sma_src);
   ArrayResize(out, size); ArrayInitialize(out, 0.0); ArraySetAsSeries(out, true);

   for (int i=0; i<size-length; i++)
      {
      double sum=0;
      for (int j=i; j<i+length; j++)  sum+=sma_src[j];
      out[i] = sum/(double)length;
      }

   return;
}
//+------------------------------------------------------------------+
void TV_EMA(double &out[], double &ema_src[], int length)
{
   double alpha = 2.0 / (length + 1); 
   int size = ArraySize(ema_src);
   ArrayResize(out, size); ArrayInitialize(out, 0);
   //Print("out: " , size," ", ArraySize(out));
   for (int i=size-length; i<=size-1; i++) out[size-1] += (ema_src[i]/length);
    
   for (int i =size-2; i>=0; i--)
      out[i] = alpha * ema_src[i] + (1.0 - alpha) * out[i+1];
}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   int window=0;
   datetime lastbar = 0;
   double price;

   ChartXYToTimePrice(ChartID(), (int) ChartGetInteger(ChartID(), CHART_WIDTH_IN_PIXELS, mywindow)-5, 0, window, lastbar, price);
   //Print("OrigWindow ", window," ",mywindow);
   OBJName = Indiname + "ob_Level_text";
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, lastbar);
   OBJName = Indiname + "os_Level_text";
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, lastbar);
   OBJName = Indiname + "mid_Level_text";
   ObjectSetInteger(ChartID(), OBJName, OBJPROP_TIME, lastbar);
}

//------------------------------------------------------------------
double workDev[];
double iDeviation(double value, int length, bool isSample, int i)
{
   if (ArraySize(workDev)!=Bars) ArrayResize(workDev,Bars); i=Bars-i-1; workDev[i] = value;
   
      double oldMean   = value;
      double newMean   = value;
      double squares   = 0; int k;
      for (k=1; k<length && (i-k)>=0; k++)
      {
         newMean  = (workDev[i-k]-oldMean)/(k+1)+oldMean;
         squares += (workDev[i-k]-oldMean)*(workDev[i-k]-newMean);
         oldMean  = newMean;
      }
      return(MathSqrt(squares/MathMax(k-isSample,1)));
}
//+------------------------------------------------------------------


//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

#define _prHABF(_prtype) (_prtype>=pr_habclose && _prtype<=pr_habtbiased2)
#define _priceInstances     1
#define _priceInstancesSize 4
double workHa[][_priceInstances*_priceInstancesSize];
double getPrice(int tprice, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars, int instanceNo=0)
{
  if (tprice>=pr_haclose)
   {
      if (ArrayRange(workHa,0)!= Bars) ArrayResize(workHa,Bars); instanceNo*=_priceInstancesSize; int r = bars-i-1;
         
         //
         //
         //
         //
         //
         
         double haOpen  = (r>0) ? (workHa[r-1][instanceNo+2] + workHa[r-1][instanceNo+3])/2.0 : (open[i]+close[i])/2;;
         double haClose = (open[i]+high[i]+low[i]+close[i]) / 4.0;
         if (_prHABF(tprice))
               if (high[i]!=low[i])
                     haClose = (open[i]+close[i])/2.0+(((close[i]-open[i])/(high[i]-low[i]))*MathAbs((close[i]-open[i])/2.0));
               else  haClose = (open[i]+close[i])/2.0; 
         double haHigh  = fmax(high[i], fmax(haOpen,haClose));
         double haLow   = fmin(low[i] , fmin(haOpen,haClose));

         //
         //
         //
         //
         //
         
         if(haOpen<haClose) { workHa[r][instanceNo+0] = haLow;  workHa[r][instanceNo+1] = haHigh; } 
         else               { workHa[r][instanceNo+0] = haHigh; workHa[r][instanceNo+1] = haLow;  } 
                              workHa[r][instanceNo+2] = haOpen;
                              workHa[r][instanceNo+3] = haClose;
         //
         //
         //
         //
         //
         
         switch (tprice)
         {
            case pr_haclose:
            case pr_habclose:    return(haClose);
            case pr_haopen:   
            case pr_habopen:     return(haOpen);
            case pr_hahigh: 
            case pr_habhigh:     return(haHigh);
            case pr_halow:    
            case pr_hablow:      return(haLow);
            case pr_hamedian:
            case pr_habmedian:   return((haHigh+haLow)/2.0);
            case pr_hamedianb:
            case pr_habmedianb:  return((haOpen+haClose)/2.0);
            case pr_hatypical:
            case pr_habtypical:  return((haHigh+haLow+haClose)/3.0);
            case pr_haweighted:
            case pr_habweighted: return((haHigh+haLow+haClose+haClose)/4.0);
            case pr_haaverage:  
            case pr_habaverage:  return((haHigh+haLow+haClose+haOpen)/4.0);
            case pr_hatbiased:
            case pr_habtbiased:
               if (haClose>haOpen)
                     return((haHigh+haClose)/2.0);
               else  return((haLow+haClose)/2.0);        
            case pr_hatbiased2:
            case pr_habtbiased2:
               if (haClose>haOpen)  return(haHigh);
               if (haClose<haOpen)  return(haLow);
                                    return(haClose);        
         }
   }
   
   //
   //
   //
   //
   //
   
   switch (tprice)
   {
      case pr_close:     return(close[i]);
      case pr_open:      return(open[i]);
      case pr_high:      return(high[i]);
      case pr_low:       return(low[i]);
      case pr_median:    return((high[i]+low[i])/2.0);
      case pr_medianb:   return((open[i]+close[i])/2.0);
      case pr_typical:   return((high[i]+low[i]+close[i])/3.0);
      case pr_weighted:  return((high[i]+low[i]+close[i]+close[i])/4.0);
      case pr_average:   return((high[i]+low[i]+close[i]+open[i])/4.0);
      case pr_tbiased:   
               if (close[i]>open[i])
                     return((high[i]+close[i])/2.0);
               else  return((low[i]+close[i])/2.0);        
      case pr_tbiased2:   
               if (close[i]>open[i]) return(high[i]);
               if (close[i]<open[i]) return(low[i]);
                                     return(close[i]);        
   }
   return(0);
}

//+------------------------------------------------------------------+
double wrk[][20];

#define bsmax  5
#define bsmin  6
#define volty  7
#define vsum   8
#define avolty 9

double iDSmooth(double price, double length, double phase, bool isDouble, int i, int s=0) 
{
   if (isDouble)
         return (iSmooth(iSmooth(price,MathSqrt(length),phase,i,s),MathSqrt(length),phase,i,s+10));
   else  return (iSmooth(price,length,phase,i,s));
}
//------------------------------------------------------------------
double iSmooth(double price, double length, double phase, int i, int s=0)
{
   if (length <=1) return(price);
   if (ArrayRange(wrk,0) != Bars) ArrayResize(wrk,Bars);
   
   int r = Bars-i-1; 
      if (r==0) { int k; for(k=0; k<7; k++) wrk[r][k+s]=price; for(; k<10; k++) wrk[r][k+s]=0; return(price); }
   
      double len1   = MathMax(MathLog(MathSqrt(0.5*(length-1)))/MathLog(2.0)+2.0,0);
      double pow1   = MathMax(len1-2.0,0.5);
      double del1   = price - wrk[r-1][bsmax+s];
      double del2   = price - wrk[r-1][bsmin+s];
      double div    = 1.0/(10.0+10.0*(MathMin(MathMax(length-10,0),100))/100);
      int    forBar = MathMin(r,10);
	
         wrk[r][volty+s] = 0;
               if(MathAbs(del1) > MathAbs(del2)) wrk[r][volty+s] = MathAbs(del1); 
               if(MathAbs(del1) < MathAbs(del2)) wrk[r][volty+s] = MathAbs(del2); 
         wrk[r][vsum+s] =	wrk[r-1][vsum+s] + (wrk[r][volty+s]-wrk[r-forBar][volty+s])*div;
   
         wrk[r][avolty+s] = wrk[r-1][avolty+s]+(2.0/(MathMax(4.0*length,30)+1.0))*(wrk[r][vsum+s]-wrk[r-1][avolty+s]);
            double dVolty = 0;
            if (wrk[r][avolty+s] > 0)
                  dVolty = wrk[r][volty+s]/wrk[r][avolty+s];   
	               if (dVolty > MathPow(len1,1.0/pow1)) dVolty = MathPow(len1,1.0/pow1);
                  if (dVolty < 1)                      dVolty = 1.0;
	        
   	double pow2 = MathPow(dVolty, pow1);
      double len2 = MathSqrt(0.5*(length-1))*len1;
      double Kv   = MathPow(len2/(len2+1), MathSqrt(pow2));

         if (del1 > 0) wrk[r][bsmax+s] = price; else wrk[r][bsmax+s] = price - Kv*del1;
         if (del2 < 0) wrk[r][bsmin+s] = price; else wrk[r][bsmin+s] = price - Kv*del2;
      
      double R     = MathMax(MathMin(phase,100),-100)/100.0 + 1.5;
      double beta  = 0.45*(length-1)/(0.45*(length-1)+2);
      double alpha = MathPow(beta,pow2);

         wrk[r][0+s] = price + alpha*(wrk[r-1][0+s]-price);
         wrk[r][1+s] = (price - wrk[r][0+s])*(1-beta) + beta*wrk[r-1][1+s];
         wrk[r][2+s] = (wrk[r][0+s] + R*wrk[r][1+s]);
         wrk[r][3+s] = (wrk[r][2+s] - wrk[r-1][4+s])*MathPow((1-alpha),2) + MathPow(alpha,2)*wrk[r-1][3+s];
         wrk[r][4+s] = (wrk[r-1][4+s] + wrk[r][3+s]); 

   return(wrk[r][4+s]);
}