//+-------------------------------------------------------------------+
//|                               StochDifZoneX Indicator (C) xixi    |
//|                               StochDifZones Indicator (C) Ralome  |
//|                               StochDifZones Principle (C) Eurusdd |
//| v1.2                                                              |
//|        This code comes as is and carries NO WARRANTY whatsoever   |
//|                                           Use at your own risk!   |
//+-------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_buffers 6

#property indicator_color4 Green
#property indicator_width4 3
#property indicator_color5 Plum
#property indicator_width5 3
#property indicator_color6 White
#property indicator_width6 1

extern int stoch_period = 300;
extern int bb_period = 24; 
extern string note_1 = " -- bars_limit: 0 means all bars -- ";
extern int bars_limit = 1000;

extern bool show_dissim_zone = true;
extern color dissim_zone_color = Plum;
extern color gap_zone_color = Blue;
extern double zone_lifetime_in_days = 14;
extern string note_2 = " -- What to do with old/cleared zones -- ";
extern string note_3 = " -- 0: nothing, 1: remove, 2: gray out -- "; 

extern string extras_1 = " ** Change bottom dots into lines ** ";
extern bool lines_instead_of_dots = true;
extern double line_height = 2;

extern string extras_3 = " ** Alert when a new zone starts ** "; 
extern bool alert_popup = false;
extern bool alert_sound = false;

extern string extras_7 = "price_field param of Stochastic indicator: 0: H/L, 1: C/C";
extern int price_field = 0; 

#property indicator_minimum 0
#property indicator_maximum 2.2

SetLevelValue(0, 1); 
SetLevelValue(1, 2); 
SetLevelStyle(STYLE_SOLID, 1, DarkKhaki); 


double stoch[], price_ma[], price_std[], dif[], sim[], dissim[];
double marked_sim_or_dissim; 

int tf; 

//
double sim_dissim_threshold = 0.0001; 

string dissim_prefix = "dsim_"; 
string dissim_correction_prefix = "dsimcorrection_";

int init()
{
   if (Bars < (stoch_period + bb_period) || (bars_limit > 0 && bars_limit < (stoch_period + bb_period)))
   {
      Alert("Adjust limit settings or put more bars on your chart!");
      return(0); 
   }
   
   IndicatorBuffers(6); 
   
   SetIndexStyle(0, DRAW_NONE);
   SetIndexBuffer(0, stoch);
   
   SetIndexStyle(1, DRAW_NONE);
   SetIndexBuffer(1, price_ma); 
   
   SetIndexStyle(2, DRAW_NONE); 
   SetIndexBuffer(2, price_std);
   
   SetIndexBuffer(3, sim);
   SetIndexBuffer(4, dissim); 
   
   if (lines_instead_of_dots == true)
   {
      SetIndexStyle(3, DRAW_HISTOGRAM);
      SetIndexStyle(4, DRAW_HISTOGRAM);
      marked_sim_or_dissim = line_height;       
   } 
   else 
   {
      SetIndexStyle(3, DRAW_ARROW);
      SetIndexArrow(3, 159);
      SetIndexStyle(4, DRAW_ARROW);
      SetIndexArrow(4, 159); 
      marked_sim_or_dissim = 0; 
   }
   
   SetIndexStyle(5, DRAW_LINE);
   SetIndexBuffer(5, dif);

   tf = Period();
   if (!show_dissim_zone) 
      dissim_zone_color = CLR_NONE; 
      
   return(0);
}

int deinit()
{
   delete_objects(dissim_prefix);
   delete_objects(dissim_correction_prefix); 
  
   return(0);
}

int start()
{
   double stoch_perc, price_perc; 
   double stoch_ma, stoch_std;
   string name;   
   int limit, i; 
   int counted_bars = IndicatorCounted();
   
   if (counted_bars > 0)   // last counted bar will be recounted   
      counted_bars--;  
   
   limit = Bars - counted_bars; 
   
   if (bars_limit != 0 && limit > (bars_limit - 1))   
      limit = bars_limit - 1;      
   
   for (i = 0; i < limit; i++)
   {
      stoch[i] = iStochastic(NULL, 0, stoch_period, 1, 1, MODE_SMA, price_field, MODE_MAIN, i); 
      price_ma[i] = iMA(NULL, 0, bb_period, 0, MODE_SMA, PRICE_CLOSE, i); 
      price_std[i] = iStdDev(NULL, 0, bb_period, 0, MODE_SMA, PRICE_CLOSE, i); 
   }
   
   // calculate differences
   for (i = 0; i < limit; i++)
   {
      stoch_ma = iMAOnArray(stoch, 0, bb_period, 0, MODE_SMA, i); 
      stoch_std = iStdDevOnArray(stoch, 0, bb_period, 0, MODE_SMA, i); 
      
      stoch_perc = 0; 
      price_perc = 0; 
      
      if (stoch_std > 0) 
         stoch_perc = (stoch[i] - stoch_ma) / (2 * stoch_std); 
      if (price_std[i] > 0) 
         price_perc = (Close[i] - price_ma[i]) / (2 * price_std[i]); 
      
      dif[i] = MathAbs(stoch_perc - price_perc); 
   }
   
   for (i = 1; i <= limit; i++)
   {
      if (is_start_of_sim(i)) 
         sim[i] = marked_sim_or_dissim; 
      if (is_start_of_dissim(i)) 
         dissim[i] = marked_sim_or_dissim;          
   }
   
   for (i = limit; i >= 0; i--)
   {     
      // create new dissimilarity zone
      if (is_start_of_dissim(i)) 
      {
         name = StringConcatenate(dissim_prefix, DoubleToStr(Time[i], 0)); 
         ObjectCreate(name, OBJ_RECTANGLE, 0, Time[i], High[i], Time[i], Low[i]); 
         ObjectSet(name, OBJPROP_COLOR, dissim_zone_color);
         ObjectSet(name, OBJPROP_BACK, true); 
         ObjectSet(name, OBJPROP_WIDTH, 0); 
         ObjectSet(name, OBJPROP_STYLE, STYLE_SOLID);
         
         if (alert_popup)
            Alert("new dissimilarity zone"); 
         if (alert_sound)
            PlaySound("alert.wav"); 
      }
      
      // update other zones 
      update_zones(i);
   }

   return(0);
}


void delete_objects(string prefix)
{
   int ot = ObjectsTotal();
   
   while (ot >= 0)
   {
      if (StringFind(ObjectName(ot), prefix, 0) > -1)
         ObjectDelete(ObjectName(ot)); 
      ot--;
   }
}

void update_zones(int cur_bar) 
{
   int ot = ObjectsTotal();
   string obj_name, obj_correction_name;
   double obj_start_time, obj_end_time;
   double obj_high, obj_low; 
   int obj_start_bar, obj_end_bar; 
   color obj_color; 
   
   while (ot > 0) 
   {
      obj_name = ObjectName(ot); 
      obj_color = ObjectGet(obj_name, OBJPROP_COLOR); 
      obj_start_time = ObjectGet(obj_name, OBJPROP_TIME1);
      obj_start_bar = iBarShift(NULL, 0, obj_start_time); 
      obj_end_time = ObjectGet(obj_name, OBJPROP_TIME2);
      obj_end_bar = iBarShift(NULL, 0, obj_end_time); 
      obj_high = ObjectGet(obj_name, OBJPROP_PRICE1);
      obj_low = ObjectGet(obj_name, OBJPROP_PRICE2);
      
      
      if (obj_end_bar == cur_bar + 1)
      {
         //
         // update 'dissimilarity' objects 
         
         if (is_dissim_obj(obj_name))
         {         
            if (is_dissim(cur_bar))    // is still dis-similar
            {
               ObjectSet(obj_name, OBJPROP_TIME2, Time[cur_bar]);
               if (High[cur_bar] > obj_high)
                  ObjectSet(obj_name, OBJPROP_PRICE1, High[cur_bar]); 
               if (Low[cur_bar] < obj_low)
                  ObjectSet(obj_name, OBJPROP_PRICE2, Low[cur_bar]); 
            }
            else     
               if (is_start_of_sim(cur_bar))    // create dissim correction zone
               {
                  obj_correction_name = StringConcatenate(dissim_correction_prefix, DoubleToStr(Time[cur_bar], 0)); 
                  ObjectCreate(obj_correction_name, OBJ_RECTANGLE, 0, Time[cur_bar], obj_high, Time[cur_bar], obj_low); 
                  ObjectSet(obj_correction_name, OBJPROP_COLOR, gap_zone_color);
                  ObjectSet(obj_correction_name, OBJPROP_BACK, true); 
                  ObjectSet(obj_correction_name, OBJPROP_WIDTH, 0); 
                  ObjectSet(obj_correction_name, OBJPROP_STYLE, STYLE_SOLID);           
               }
         }   
      
         //
         // update dissimilarity correction objects 
      
         if (is_dissim_correction_obj(obj_name))
         {
            if (is_sim(cur_bar))
               ObjectSet(obj_name, OBJPROP_TIME2, Time[cur_bar]);
         }
      }      
      
      ot--;
   }
}


//
// helpers 

bool is_start_of_sim(int i)
{
   return (dif[i] < sim_dissim_threshold && dif[i+1] >= sim_dissim_threshold); 
}

bool is_sim(int i)
{
   return (dif[i] < sim_dissim_threshold); 
}

bool is_start_of_dissim(int i)
{
   return (dif[i] >= sim_dissim_threshold && dif[i+1] < sim_dissim_threshold); 
}

bool is_dissim(int i)
{
   return (dif[i] >= sim_dissim_threshold); 
}

bool is_dissim_obj(string obj_name)
{
   return (StringFind(obj_name, dissim_prefix, 0) > -1);
}

bool is_dissim_correction_obj(string obj_name)
{
   return (StringFind(obj_name, dissim_correction_prefix, 0) > -1);
}

