//+------------------------------------------------------------------+
//|                                      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 7
#property indicator_plots 6

#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                    = "555";            // Indicator Prefix
extern int                trend_data_count              = 240;              // Trend Length
extern int                MaxBarsMulti                  = 0;                // Max Bars as Multiplier of Trend Length (0 = ignore)
extern enPrices           RTIPrice                      = pr_close;         // RTI price to use
extern int                RTISmoothLength               = 1;                // RTI Jurik Smoothening Length (1 = ignore)
extern double             RTIPhase                      = 0;                // RTI Jurik phase
extern bool               RTIDouble                     = false;            // RTI Double Smooth
extern double             PSAR_Step                     = 0.006;            // SAR Increment - Accumulation step
extern double             PSAR_Max                      = 0.015;            // SAR Maximum - Accumulation limit
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[];
double sarUp[],sarDn[],saraUp[],saraDn[],slope[];
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);
    
    SetIndexBuffer(2, sarUp);  SetIndexStyle(2,DRAW_ARROW,STYLE_SOLID,1,obcolor); SetIndexArrow(2,159);
    SetIndexBuffer(3, sarDn);  SetIndexStyle(3,DRAW_ARROW,STYLE_SOLID,1,oscolor); SetIndexArrow(3,159);
    SetIndexBuffer(4, saraUp); SetIndexStyle(4,DRAW_ARROW,STYLE_SOLID,1,obcolor); SetIndexArrow(4,159);
    SetIndexBuffer(5, saraDn); SetIndexStyle(5,DRAW_ARROW,STYLE_SOLID,1,oscolor); SetIndexArrow(5,159);
    SetIndexBuffer(6, slope);  SetIndexStyle(6,DRAW_NONE);

    SetIndexLabel(2,"SAR Up");
    SetIndexLabel(3,"SAR Down");
    SetIndexLabel(4,"SAR Change Up");
    SetIndexLabel(5,"SAR Change Down");
    SetIndexLabel(6,"Slope of SRC");
    
    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, ((ob+os)/2));
    
    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, (string) ((ob+os)/2));
    ObjectSetDouble(ChartID(), OBJName, OBJPROP_PRICE, 0, ((ob+os)/2));
    
    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--)
    {
        double sarClose;
        double sarOpen;
        double sarPosition;
        double sarChange; 
            
        iParabolic(RelativeTrendIndex[i],RelativeTrendIndex[i],PSAR_Step,PSAR_Max,sarClose,sarOpen,sarPosition,sarChange,i);
		
        sarUp[i]  = EMPTY_VALUE;
        sarDn[i]  = EMPTY_VALUE;
        saraUp[i] = EMPTY_VALUE;
        saraDn[i] = EMPTY_VALUE;

        slope[i]  = (i<Bars-1) ? (src[i]>src[i+1]) ? 1 : (src[i]<src[i+1]) ? -1 : slope[i+1] : 0;		
        if (sarPosition==1)
              sarUp[i] = sarClose;
        else  sarDn[i] = sarClose;
        if (sarChange!=0)
           if (sarPosition==1)
                 saraUp[i] = sarClose;
           else  saraDn[i] = sarClose;
        
               
        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]);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double work[][7];
#define _high     0
#define _low      1
#define _ohigh    2
#define _olow     3
#define _open     4
#define _position 5
#define _af       6


void iParabolic(double high, double low, double step, double limit, double& pClose, double& pOpen, double& pPosition, double& pChange, int i)
{
    if (ArrayRange(work,0)!=Bars) ArrayResize(work,Bars); i = Bars-i-1;
    
    //
    //
    //
    //
    //
    
       pChange = 0;
          work[i][_ohigh]    = high;
          work[i][_olow]     = low;
             if (i<1)
                {
                   work[i][_high]     = high;
                   work[i][_low]      = low;
                   work[i][_open]     = high;
                   work[i][_position] = -1;
                   return;
                }
          work[i][_open]     = work[i-1][_open];
          work[i][_af]       = work[i-1][_af];
          work[i][_position] = work[i-1][_position];
          work[i][_high]     = fmax(work[i-1][_high],high);
          work[i][_low]      = fmin(work[i-1][_low] ,low );
       
    //
    //
    //
    //
    //
             
    if (work[i][_position] == 1)
       if (low<=work[i][_open])
          {
             work[i][_position] = -1;
                pChange = -1;
                pClose  = work[i][_high];
                          work[i][_high] = high;
                          work[i][_low]  = low;
                          work[i][_af]   = step;
                          work[i][_open] = pClose + work[i][_af]*(work[i][_low]-pClose);
                             if (work[i][_open]<work[i  ][_ohigh]) work[i][_open] = work[i  ][_ohigh];
                             if (work[i][_open]<work[i-1][_ohigh]) work[i][_open] = work[i-1][_ohigh];
          }
       else
          {
                pClose = work[i][_open];
                     if (work[i][_high]>work[i-1][_high] && work[i][_af]<limit) work[i][_af] = fmin(work[i][_af]+step,limit);
                         work[i][_open] = pClose + work[i][_af]*(work[i][_high]-pClose);
                             if (work[i][_open]>work[i  ][_olow]) work[i][_open] = work[i  ][_olow];
                             if (work[i][_open]>work[i-1][_olow]) work[i][_open] = work[i-1][_olow];
          }
    else
       if (high>=work[i][_open])
          {
             work[i][_position] = 1;
                pChange = 1;
                pClose  = work[i][_low];
                          work[i][_low]  = low;
                          work[i][_high] = high;
                          work[i][_af]   = step;
                          work[i][_open] = pClose + work[i][_af]*(work[i][_high]-pClose);
                             if (work[i][_open]>work[i  ][_olow]) work[i][_open] = work[i  ][_olow];
                             if (work[i][_open]>work[i-1][_olow]) work[i][_open] = work[i-1][_olow];
          }
       else
          {
                pClose = work[i][_open];
                if (work[i][_low]<work[i-1][_low] && work[i][_af]<limit) work[i][_af] = fmin(work[i][_af]+step,limit);
                    work[i][_open] = pClose + work[i][_af]*(work[i][_low]-pClose);
                             if (work[i][_open]<work[i  ][_ohigh]) work[i][_open] = work[i  ][_ohigh];
                             if (work[i][_open]<work[i-1][_ohigh]) work[i][_open] = work[i-1][_ohigh];
          }
    
    //
    //
    //
    //
    //
    
    pOpen     = work[i][_open];
    pPosition = work[i][_position];
}
