// ---------------------------------------------------------------------------
//  Построение экстремумов, описанных как "структура рынка" у Л.Вильямса.
//  На этот раз учёт внутренних дней сделан ПРАВИЛЬНО.
// ---------------------------------------------------------------------------

#property indicator_chart_window
#property indicator_buffers 6
#property indicator_color1  Aqua
#property indicator_color2  Blue
#property indicator_color3  DeepPink
#property indicator_color4  Aqua
#property indicator_color5  Blue
#property indicator_color6  DeepPink

// Коды стрелок.
#define ARROW_CODE_ST 0x6C
#define ARROW_CODE_MT 0x6C
#define ARROW_CODE_LT 0x6C

// Глубина просматриваемой истории, 0 - вся история.
extern int HISTORY_DEPTH = 16384; 

// Буферы индикатора.
double   buf_st_max[], buf_mt_max[], buf_lt_max[];
double   buf_st_min[], buf_mt_min[], buf_lt_min[];
// Вспомогательные массивы для расчётов.
int      min[], max[];
// Отслеживание формирования баров.
datetime last_bar = 0;

// Инициализация.
int init() {
  // Контроль параметров.
  if((HISTORY_DEPTH == 0) || (HISTORY_DEPTH > Bars)) {
    HISTORY_DEPTH = Bars;
  }

  IndicatorShortName(StringConcatenate(
    "AZZX - LARRY v2.0 (", HISTORY_DEPTH, ")"));
  
  SetIndexBuffer(0, buf_st_max);
  SetIndexBuffer(1, buf_mt_max);
  SetIndexBuffer(2, buf_lt_max);
  SetIndexBuffer(3, buf_st_min);
  SetIndexBuffer(4, buf_mt_min);
  SetIndexBuffer(5, buf_lt_min);
  
  SetIndexStyle(0, DRAW_ARROW);
  SetIndexStyle(1, DRAW_ARROW);
  SetIndexStyle(2, DRAW_ARROW);
  SetIndexStyle(3, DRAW_ARROW);
  SetIndexStyle(4, DRAW_ARROW);
  SetIndexStyle(5, DRAW_ARROW);

  SetIndexArrow(0, ARROW_CODE_ST);
  SetIndexArrow(1, ARROW_CODE_MT);
  SetIndexArrow(2, ARROW_CODE_LT);
  SetIndexArrow(3, ARROW_CODE_ST);
  SetIndexArrow(4, ARROW_CODE_MT);
  SetIndexArrow(5, ARROW_CODE_LT);
  
  SetIndexEmptyValue(0, 0);
  SetIndexEmptyValue(1, 0);
  SetIndexEmptyValue(2, 0);
  SetIndexEmptyValue(3, 0);
  SetIndexEmptyValue(4, 0);
  SetIndexEmptyValue(5, 0);
  
  SetIndexLabel(0, "SHORT-TERM MAXIMUM");
  SetIndexLabel(1, "MEDIUM-TERM MAXIMUM");
  SetIndexLabel(2, "LONG-TERM MAXIMUM");
  SetIndexLabel(3, "SHORT-TERM MINIMUM");
  SetIndexLabel(4, "MEDIUM-TERM MINIMUM");
  SetIndexLabel(5, "LONG-TERM MINIMUM");
    
  ArrayResize(min, HISTORY_DEPTH);
  ArrayResize(max, HISTORY_DEPTH);
  
  return(0);
}

// Главный цикл.
int start() {
  int i, l, r;
  
  // Работаем только по сформированным барам.
  if(last_bar == Time[0]) return(0);
  last_bar = Time[0];

  // Инициализация массивов.  
  ArrayInitialize(min, 0);
  ArrayInitialize(max, 0);

  // Поиск краткосрочных экстремумов.
  for(i = HISTORY_DEPTH - 2; i > 1; i--) {
    l = left_bar(i);
    r = right_bar(i);
    
    if((l > 0) && (r > 0)) {
      if((Low[l] > Low[i]) && (Low[r] > Low[i])) {
        min[i] = 1;
      }
    }

    if((l > 0) && (r > 0)) {
      if((High[l] < High[i]) && (High[r] < High[i])) {
        max[i] = 1;
      }
    }
  }

  // Поиск среднесрочных экстремумов.
  for(i = HISTORY_DEPTH - 2; i > 1; i--) {
    l = left_st_min(i);
    r = right_st_min(i);
    
    if((l > 0) && (r > 0)) {
      if((Low[l] > Low[i]) && (Low[r] > Low[i])) {
        min[i] = 2;
      }
    }

    l = left_st_max(i);
    r = right_st_max(i);
    
    if((l > 0) && (r > 0)) {
      if((High[l] < High[i]) && (High[r] < High[i])) {
        max[i] = 2;
      }
    }
  }

  // Поиск долгосрочных экстремумов.
  for(i = HISTORY_DEPTH - 2; i > 1; i--) {
    l = left_mt_min(i);
    r = right_mt_min(i);
    
    if((l > 0) && (r > 0)) {
      if((Low[l] > Low[i]) && (Low[r] > Low[i])) {
        min[i] = 3;
      }
    }

    l = left_mt_max(i);
    r = right_mt_max(i);
    
    if((l > 0) && (r > 0)) {
      if((High[l] < High[i]) && (High[r] < High[i])) {
        max[i] = 3;
      }
    }
  }

  // Отметка экстремумов.
  ArrayInitialize(buf_st_min, 0);
  ArrayInitialize(buf_mt_min, 0);
  ArrayInitialize(buf_lt_min, 0);
  ArrayInitialize(buf_st_max, 0);
  ArrayInitialize(buf_mt_max, 0);
  ArrayInitialize(buf_lt_max, 0);
  
  for(i = HISTORY_DEPTH - 1; i > 0; i--) {
    switch(min[i]) {
      case 1 : buf_st_min[i] = Low[i]; break;
      case 2 : buf_mt_min[i] = Low[i]; break;
      case 3 : buf_lt_min[i] = Low[i]; break;
    }

    switch(max[i]) {
      case 1 : buf_st_max[i] = High[i]; break;
      case 2 : buf_mt_max[i] = High[i]; break;
      case 3 : buf_lt_max[i] = High[i]; break;
    }
  }
  
  return(0);
}

// Проверка, что заданный бар - внутренний.
bool is_internal(int bar) {
  return((High[bar] < High[bar + 1]) && 
         (Low [bar] > Low [bar + 1]));
}

// Возвращает бар слева от заданного бара или -1.
int left_bar(int bar) {
  int i;
  
  for(i = bar + 1; i < HISTORY_DEPTH - 2; i++) {
    if(is_internal(i) == false) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает бар справа от заданного бара или -1.
int right_bar(int bar) {
  int i;
  
  for(i = bar - 1; i > 0; i--) {
    if(is_internal(i) == false) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает краткосрочный минимум слева от заданного бара, или -1.
int left_st_min(int bar) {
  int i;
  
  for(i = bar + 1; i < HISTORY_DEPTH - 2; i++) {
    if(min[i] > 0) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает краткосрочный минимум справа от заданного бара, или -1.
int right_st_min(int bar) {
  int i;
  
  for(i = bar - 1; i > 0; i--) {
    if(min[i] > 0) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает краткосрочный максимум слева от заданного бара, или -1.
int left_st_max(int bar) {
  int i;
  
  for(i = bar + 1; i < HISTORY_DEPTH - 2; i++) {
    if(max[i] > 0) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает краткосрочный максимум справа от заданного бара, или -1.
int right_st_max(int bar) {
  int i;
  
  for(i = bar - 1; i > 0; i--) {
    if(max[i] > 0) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает среднесрочный минимум слева от заданного бара, или -1.
int left_mt_min(int bar) {
  int i;
  
  for(i = bar + 1; i < HISTORY_DEPTH - 2; i++) {
    if(min[i] > 1) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает среднесрочный минимум справа от заданного бара, или -1.
int right_mt_min(int bar) {
  int i;
  
  for(i = bar - 1; i > 0; i--) {
    if(min[i] > 1) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает среднесрочный максимум слева от заданного бара, или -1.
int left_mt_max(int bar) {
  int i;
  
  for(i = bar + 1; i < HISTORY_DEPTH - 2; i++) {
    if(max[i] > 1) {
      return(i);
    }
  }
  
  return(-1);
}

// Возвращает среднесрочный максимум справа от заданного бара, или -1.
int right_mt_max(int bar) {
  int i;
  
  for(i = bar - 1; i > 0; i--) {
    if(max[i] > 1) {
      return(i);
    }
  }
  
  return(-1);
}

