//+------------------------------------------------------------------+
//|                     Relative_Trend_Index_V4_with_SAR.mq4         |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "5.00"
#property strict
#property indicator_separate_window

#property indicator_buffers 3
#property indicator_plots 3

#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

#property indicator_color3 Purple
#property indicator_label3 "RTI Parabolic SAR"
#property indicator_style3 STYLE_SOLID
#property indicator_width3 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_simple,    // Simple - standard calculation
   std_recursive, // Recursive - with sample correction
   std_recursive_nos // Recursive - without sample correction
};

// Parametri esistenti
extern string             IndiPrefix                    = "";            // Indicator Prefix
extern int                trend_data_count              = 240;              // Trend Length
extern int                MaxBarsMulti                  = 30;               // Max Bars as Multiplier of Trend Length (0 = ignore)
extern enPrices           RTIPrice                      = pr_close;         // RTI price to use
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         DeviationMethod               = std_simple;       // Deviation calculation method
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

// Nuovi parametri per il SAR
extern double             PSAR_Step                     = 0.006;             // SAR Increment
extern double             PSAR_Max                      = 0.015;              // SAR Maximum

// Buffer esistenti
double upper_trend[], lower_trend[], src[], stdv[], upper_array[], lower_array[], RelativeTrendIndex[];
double UpperTrend[], LowerTrend[], MA_RelativeTrendIndex[];

// Nuovi buffer per il SAR
double PSAR_Buffer[];
int PSAR_TrendDirection[];  // 1 = uptrend, -1 = downtrend
double PSAR_ExtremePoint[]; // Extreme Point
double PSAR_Acceleration[]; // Acceleration Factor

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);
   
   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());      
   }   
   
   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);
   
   // Inizializzazione buffer SAR
   SetIndexBuffer(2, PSAR_Buffer);
   SetIndexStyle(2, DRAW_ARROW);
   SetIndexArrow(2, 159); // Simbolo a freccia
   SetIndexEmptyValue(2, EMPTY_VALUE);
   
   Indiname = Indiname + IndiPrefix;
   
   CreateLevelObjects();
   
   newtime=0;
   return(INIT_SUCCEEDED);
}

void CreateLevelObjects()
{
   OBJName = Indiname + "ob_Level";
   ObjectCreate(ChartID(), OBJName, OBJ_HLINE, mywindow, 0, 0);
   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);
}

//+------------------------------------------------------------------+
//| 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); 
   
   // Ridimensiona array SAR
   ArrayResize(PSAR_TrendDirection, myBars);
   ArrayResize(PSAR_ExtremePoint, myBars);
   ArrayResize(PSAR_Acceleration, myBars);
   
   for (int i=myBars-1; i>=0; i--)
   {
      src[i] = getPrice(RTIPrice,Open,Close,High,Low,i,Bars);
      
      switch(DeviationMethod)
      {
         case std_simple:
            stdv[i] = iStdDevSimple(src, std_dev_length, i);
            break;
         case std_recursive:
            stdv[i] = iDeviation(src[i], std_dev_length, true, i);
            break;
         case std_recursive_nos:
            stdv[i] = iDeviation(src[i], std_dev_length, false, i);
            break;
      }
      
      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);
   
   // Calcola il SAR sull'RTI
   CalculateRTI_SAR();
   
   ObjectsDeleteAll(ChartID(), Indiname, mywindow, OBJ_TREND);
   width = (int) ChartGetInteger(ChartID(), CHART_SCALE)+2; 
   
   DrawOBOSLines();
   CheckAlerts();
   
   newtime = iTime(Symbol(), Period(), 0);
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Calcola il Parabolic SAR sull'RTI                                |
//+------------------------------------------------------------------+
void CalculateRTI_SAR()
{
   if(myBars < 2) return;
   
   // Inizializzazione
   if(RelativeTrendIndex[myBars-1] != EMPTY_VALUE && RelativeTrendIndex[myBars-2] != EMPTY_VALUE)
   {
      PSAR_Buffer[myBars-1] = RelativeTrendIndex[myBars-1];
      PSAR_TrendDirection[myBars-1] = RelativeTrendIndex[myBars-1] > RelativeTrendIndex[myBars-2] ? 1 : -1;
      PSAR_ExtremePoint[myBars-1] = RelativeTrendIndex[myBars-1];
      PSAR_Acceleration[myBars-1] = PSAR_Step;
   }
   
   for(int i=myBars-2; i>=0; i--)
   {
      if(RelativeTrendIndex[i] == EMPTY_VALUE || RelativeTrendIndex[i+1] == EMPTY_VALUE)
      {
         PSAR_Buffer[i] = EMPTY_VALUE;
         continue;
      }
      
      // Calcola il SAR
      double sar = PSAR_Buffer[i+1] + PSAR_Acceleration[i+1] * (PSAR_ExtremePoint[i+1] - PSAR_Buffer[i+1]);
      
      // Controlla inversioni
      if(PSAR_TrendDirection[i+1] == 1) // Trend rialzista
      {
         if(RelativeTrendIndex[i] < sar) // Inversione
         {
            PSAR_TrendDirection[i] = -1;
            PSAR_Buffer[i] = MathMax(PSAR_ExtremePoint[i+1], RelativeTrendIndex[i+1]);
            PSAR_ExtremePoint[i] = RelativeTrendIndex[i];
            PSAR_Acceleration[i] = PSAR_Step;
         }
         else // Continua trend
         {
            PSAR_TrendDirection[i] = 1;
            PSAR_Buffer[i] = sar;
            
            if(RelativeTrendIndex[i] > PSAR_ExtremePoint[i+1])
            {
               PSAR_ExtremePoint[i] = RelativeTrendIndex[i];
               PSAR_Acceleration[i] = MathMin(PSAR_Acceleration[i+1] + PSAR_Step, PSAR_Max);
            }
            else
            {
               PSAR_ExtremePoint[i] = PSAR_ExtremePoint[i+1];
               PSAR_Acceleration[i] = PSAR_Acceleration[i+1];
            }
         }
      }
      else // Trend ribassista
      {
         if(RelativeTrendIndex[i] > sar) // Inversione
         {
            PSAR_TrendDirection[i] = 1;
            PSAR_Buffer[i] = MathMin(PSAR_ExtremePoint[i+1], RelativeTrendIndex[i+1]);
            PSAR_ExtremePoint[i] = RelativeTrendIndex[i];
            PSAR_Acceleration[i] = PSAR_Step;
         }
         else // Continua trend
         {
            PSAR_TrendDirection[i] = -1;
            PSAR_Buffer[i] = sar;
            
            if(RelativeTrendIndex[i] < PSAR_ExtremePoint[i+1])
            {
               PSAR_ExtremePoint[i] = RelativeTrendIndex[i];
               PSAR_Acceleration[i] = MathMin(PSAR_Acceleration[i+1] + PSAR_Step, PSAR_Max);
            }
            else
            {
               PSAR_ExtremePoint[i] = PSAR_ExtremePoint[i+1];
               PSAR_Acceleration[i] = PSAR_Acceleration[i+1];
            }
         }
      }
   }
}
void DrawOBOSLines()
{
   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);
      }
   }
}

void CheckAlerts()
{
   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]);
      }
   }   
}
//+------------------------------------------------------------------+
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_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);
   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);
   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)));
}

double iStdDevSimple(double &data[], int length, int index)
{
   double sum = 0;
   double sumSq = 0;
   int count = 0;
   
   for(int i=index; i<index+length && i<ArraySize(data); i++)
   {
      sum += data[i];
      sumSq += data[i] * data[i];
      count++;
   }
   
   if(count == 0) return(0);
   
   double mean = sum / count;
   double variance = (sumSq / count) - (mean * mean);
   return(MathSqrt(variance));
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

#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);
}
//+-------------------------------------------------------------