//                                                         VolumeByLastDayMedian_Correct
//______________________________________________________________________________________
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1  DimGray
#property indicator_color2  Lime
#property indicator_color3  Red
               
#define MAX_DAY_VOLATILITY 2000
#define DAY_SUNDAY         0
#define DAY_SATURDAY       6
#define pref               "LastDayMedian_"

extern int    i_indBarsCount = 1000;
extern bool   AlertsMessage  = true; 
extern bool   AlertsEmail    = false;
extern bool   AlertsMobile   = false;
extern bool   AlertsSound    = false;
extern string SoundFile      = "alert.wav";
extern int    SignalBar      = 0;
extern bool   ShowAllZones   = true;
extern color  RedZone        = clrMaroon;
extern color  GreenZone      = clrDarkGreen;
extern bool   FillZones      = true;

datetime TimeBar;
datetime TimeBar1;
double g_simplyVolume[];
double g_highVolume[];
double g_lastDayMedian[];

bool g_activate,
     g_init;

int g_volumesArray[MAX_DAY_VOLATILITY];


int init() {
   IndicatorDigits(0);
   g_activate = false;
   g_init = true;
   if (!IsTuningParametersCorrect()) return (-1);
   if (!BuffersBind()) return (-1);
   g_activate = true;
   
   return(0);
}


bool IsTuningParametersCorrect() {
   string name = WindowExpertName();

   if (_Period>PERIOD_H4) {
      Alert(name, ": Индикатор не работает на таймфреймах выше, чем H4.");
      return (false);
   }

   int period = _Period;
   if (period == 0) {
      Alert(name, ": фатальная ошибка терминала - период 0 минут. Индикатор отключен.");
      return (false);
   }
   
   if (Point == 0) {
      Alert(name, ": фатальная ошибка терминала - величина пункта равна нулю. ",
                  "Индикатор отключен.");
      return (false);
   }
   
   return (true);
}


int deinit() {
   LEVELS_delete();

   return(0);
}


void LEVELS_delete() {
   string name;
   for (int s=ObjectsTotal()-1; s>=0; s--) {
      name=ObjectName(s);
      if (StringSubstr(name,0,StringLen(pref))==pref) ObjectDelete(name);
   }
}


bool DrawRECTANGLE(  const string          name="Rectangle",
                     datetime              time1=0,
                     double                price1=0,
                     datetime              time2=0,
                     double                price2=0,
                     const color           clr=clrRed,
                     const long            chart_ID=0,
                     const int             sub_window=0,
                     const bool            fill=false,
                     const ENUM_LINE_STYLE style=STYLE_SOLID,
                     const int             width=1,
                     const bool            back=true,
                     const bool            selection=true,
                     const bool            hidden=false,
                     const long            z_order=0)
  { 

   ResetLastError(); 
   if(!ObjectCreate(chart_ID,pref+name,OBJ_RECTANGLE,sub_window,time1,price1,time2,price2)) return(false); 
    
   ObjectSetInteger(chart_ID,pref+name,OBJPROP_COLOR,clr); 
   ObjectSetInteger(chart_ID,pref+name,OBJPROP_BACK,FillZones);

   return(true); 
}


bool BuffersBind() {
   string name = WindowExpertName();

   SetIndexBuffer(0, g_simplyVolume);  SetIndexStyle(0, DRAW_HISTOGRAM);
   SetIndexBuffer(1, g_highVolume);    SetIndexStyle(1, DRAW_HISTOGRAM);
   SetIndexBuffer(2, g_lastDayMedian);
      
   SetIndexLabel(0, "Volume");
   SetIndexLabel(1, "High volume");
   SetIndexLabel(2, "Last day median");
   
   return (true);
}
//+-------------------------------------------------------------------------------------
//| Определение индекса бара, с которого необходимо производить перерасчет              
//+-------------------------------------------------------------------------------------
int GetRecalcIndex(int& total) {
   static int lastBarsCnt;                                                
   if (g_init) {                                               
      lastBarsCnt = 0;
      g_init = false;
   }
   total = Bars-2;

   if (i_indBarsCount>0 && i_indBarsCount<total) total = i_indBarsCount;                      

   if (lastBarsCnt < Bars-1) {                                               
      lastBarsCnt = Bars;
      ArrayInitialize(g_simplyVolume, EMPTY_VALUE);
      ArrayInitialize(g_highVolume, EMPTY_VALUE);
      ArrayInitialize(g_lastDayMedian, EMPTY_VALUE);
      return (total);
   }

   int newBarsCnt = Bars - lastBarsCnt;
   lastBarsCnt = Bars;
   return (newBarsCnt);                            
}


double GetMedianValue(int& array[], int cnt) {
   ArraySort(array, cnt);
   
   // Поиск медианы основан на использовании в качестве медианы среднего элемента отсортированной выборки для неченого количества элементов. 
   // При четном количестве элементов выборки медиана - среднее значение между двумя центральными элементами
   int halfElement = cnt/2;
   if (MathMod(cnt, 2) == 0)
      return ((array[halfElement] + array[halfElement-1])/2);

   return (array[halfElement]);   
}
//+-------------------------------------------------------------------------------------
//| Определение медианы объемов свечей за предыдущий день                               
//+-------------------------------------------------------------------------------------
int GetMedianOfPrevDayVolume(int index) {
   ArrayInitialize(g_volumesArray, 0);
   int arraySize = 0;
   for (int i=index; i<Bars; i++) {
      int dayOfWeek = TimeDayOfWeek(Time[i]);
      if (dayOfWeek == DAY_SUNDAY || dayOfWeek == DAY_SATURDAY) continue;
      g_volumesArray[arraySize] = Volume[i];
      arraySize++;
      if (arraySize >= MAX_DAY_VOLATILITY) break;
      if (TimeDayOfYear(Time[i]) != TimeDayOfYear(Time[i + 1])) break;
   }
   
   return (GetMedianValue(g_volumesArray, arraySize));
}


void ProcessOneCandle(int index) {
   static int maxVolume;
   if (TimeDayOfYear(Time[index]) != TimeDayOfYear(Time[index+1])) {
      g_lastDayMedian[index] = GetMedianOfPrevDayVolume(index+1);
      maxVolume = g_lastDayMedian[index];
   } else
      g_lastDayMedian[index] = g_lastDayMedian[index + 1];   

   if (Volume[index] <= maxVolume) {
      g_simplyVolume[index] = Volume[index];
      g_highVolume[index] = EMPTY_VALUE;
      return;
   }

   if (index>0) maxVolume = Volume[index];
      
   g_simplyVolume[index] = EMPTY_VALUE;
   g_highVolume[index] = Volume[index];
   
}


int start() {
   if (!g_activate) return (0);                                 

   static int z=0, y=0;
   int i, total;
   int limit = GetRecalcIndex(total);

   for (i=limit; i>=0; i--) ProcessOneCandle(i);

   if (AlertsMessage || AlertsSound || AlertsEmail || AlertsMobile) {
      string message = (WindowExpertName()+" - "+Symbol()+"  "+PeriodString()+" - Max. Volume");
       
      if (TimeBar!=Time[0] && g_highVolume[SignalBar]!=0 && g_highVolume[SignalBar]!=EMPTY_VALUE) {
         if (AlertsMessage) Alert(message);
         if (AlertsSound)   PlaySound(SoundFile);
         if (AlertsEmail)   SendMail(Symbol()+" - "+WindowExpertName()+" - ",message);
         if (AlertsMobile)  SendNotification(message);
         TimeBar=Time[0];
      }
   }
  
   if (!ShowAllZones || limit==1) {
      ObjectDelete(pref+"RED_ZONE_0");
      ObjectDelete(pref+"GREEN_ZONE_0");
      if (y!=0) int LastY=y-1;
      if (z!=0) int LastZ=z-1;
      ObjectSet(pref+"RED_ZONE_"+LastY,OBJPROP_TIME2,Time[0]);
      ObjectSet(pref+"GREEN_ZONE_"+LastZ,OBJPROP_TIME2,Time[0]);
      
      i=0+SignalBar;
      while (g_highVolume[i]==EMPTY_VALUE || (Open[i]>Close[i])) i++;
      DrawRECTANGLE("RED_ZONE_"+LastY,Time[i],High[i],Time[0],Close[i],RedZone);

      i=0+SignalBar;
      while (g_highVolume[i]==EMPTY_VALUE || (Open[i]<Close[i])) i++;
      DrawRECTANGLE("GREEN_ZONE_"+LastZ,Time[i],Low[i],Time[0],Close[i],GreenZone);

      ChartRedraw(); 
            
      if (!ShowAllZones) return(0);
   }
  
   for (i=limit; i>=SignalBar; i--) {
      if (g_highVolume[i]!=EMPTY_VALUE) {
         if (Open[i]<Close[i] && (TimeBar!=Time[0] || SignalBar==1)) {
            DrawRECTANGLE("RED_ZONE_"+y,Time[i],High[i],Time[0],Close[i],RedZone);
            y-=1;
            ObjectSet(pref+"RED_ZONE_"+y,OBJPROP_TIME2,Time[i]);
            y=y+2;
         }
         if (Open[i]>Close[i] && (TimeBar!=Time[0] || SignalBar==1)) {
            DrawRECTANGLE("GREEN_ZONE_"+z,Time[i],Low[i],Time[0],Close[i],GreenZone);
            z-=1;
            ObjectSet(pref+"GREEN_ZONE_"+z,OBJPROP_TIME2,Time[i]);
            z=z+2;
         }
      }
   }

   return(0);
}


string PeriodString() {
   switch (_Period) {
      case PERIOD_M1:  return("M1");
      case PERIOD_M5:  return("M5");
      case PERIOD_M15: return("M15");
      case PERIOD_M30: return("M30");
      case PERIOD_H1:  return("H1");
      case PERIOD_H4:  return("H4");
      case PERIOD_D1:  return("D1");
      case PERIOD_W1:  return("W1");
      case PERIOD_MN1: return("MN1");
   }    
   return("M" + string(_Period));
}