#property copyright "Scriptong"
#property link "http://autograf.dp.ua"

#property indicator_separate_window                // Отображение в подокне графика
#property indicator_buffers 6                      // Состоит из шести буферов
#property indicator_color1 Green                   // Цвет 1-го буфера (CCI > 0)
#property indicator_color2 Red                     // Цвет 2-го буфера (CCI < 0)
#property indicator_color3 LightGreen              // Цвет 3-го буфера (CCI > 0)
#property indicator_color4 Plum                    // Цвет 4-го буфера (CCI < 0)
#property indicator_color5 Black                   // Цвет 5-го буфера (главный CCI)
#property indicator_color6 Goldenrod               // Цвет 6-го буфера (turbo CCI)

#property indicator_width1 2                       // Толщина линии гистограммы
#property indicator_width2 2                       // Толщина линии гистограммы
#property indicator_width3 2                       // Толщина линии гистограммы
#property indicator_width4 2                       // Толщина линии гистограммы
#property indicator_width5 3                       // Толщина линии главного CCI

#property indicator_minimum -300                   // Зафиксируем минимальный и..
#property indicator_maximum 300                    // ..максимальный масштаб подокна..
                                                   // ..индикатора
#property indicator_levelcolor Black               // Цвет горизонтальных линий
#property indicator_level1 0                       // Горизонтальные уровни индикатора
#property indicator_level2 50
#property indicator_level3 -50
#property indicator_level4 100
#property indicator_level5 -100
#property indicator_level6 200
#property indicator_level7 -200

extern string A1 = "Период главного CCI и TurboCCI";
extern int    mainCCIperiod     = 14;
extern int    turboCCIperiod    = 6;
extern string A2 = "Количество баров, подтверждающих образование тренда";
extern int    trendBars         = 6;
extern string A3 = "======= Время до окончания бара =======================";
extern string A4 = "Сигнальное время (сек) до окончания формирования текущего бара";
extern int    secondsToBarClose = 20;
extern string A5 = "Цвета обычной и сигнальной надписей";
extern color  colorFormingBar   = Blue;
extern color  colorClosingBar   = Red;
extern string A6 = "Включение/выключение звукового сигнала обратного отсчета";
extern bool   closeBarAlert = true;
extern string closeBarSound = "tick.wav";
extern string A7 = "=======================================================";
extern string A8 = "======= Паттерн ZLR (отскок от нулевой линии) =========";
extern string A9 = "Включение/выключение поиска паттерна";
extern bool   showZLRpattern = true;
extern string A10 = "Включение/выключение звукового сигнала при нахождении паттерна";
extern bool   alertZLRpattern = true;
extern string soundZLRpattern = "AG_SoundLevel_1.wav";
extern string A11 = "Асболютное значение CCI на баре отскока не больше";
extern double zlrBarLessThan = 50;
extern string A12 = "Цвета верхнего и нижнего паттернов ZLR";
extern color  upZLRcolor = Blue;
extern color  dnZLRcolor = Red;
extern string A13 = "=======================================================";
extern string A14 = "======= Паттерн TLB (пробой линии тренда) =============";
extern string A15 = "Включение/выключение поиска паттерна";
extern bool   showTLBpattern = true;
extern string A16 = "Включение/выключение звукового сигнала при нахождении паттерна";
extern bool   alertTLBpattern = true;
extern string soundTLBpattern = "AG_SoundLevel_2.wav";
extern string A17 = "Значение CCI на баре пробоя не меньше (отключить - 0)";
extern double tlbBarGreatThan = 100;
extern string A17_1 = "Использовать для построения линии TCCI";
extern bool   tlbUseTCCI = true;
extern string A17_2 = "Начальная точка TL за пределами уровня (отключить - 0)";
extern double tlbBeginTLBarGreatThan = 100;
extern string A18 = "Цвета верхнего и нижнего паттернов TLB";
extern color  upTLBcolor = Blue;
extern color  dnTLBcolor = DarkGreen;
extern string A19 = "=======================================================";
extern string A20 = "======= Паттерн Шаму (Shamu Trade) ====================";
extern string A21 = "Включение/выключение поиска паттерна";
extern bool   showSHAMUpattern = true;
extern string A22 = "Включение/выключение звукового сигнала при нахождении паттерна";
extern bool   alertSHAMUpattern = true;
extern string soundSHAMUpattern = "AG_SoundLevel_3.wav";
extern string A23 = "Значение CCI на пиках/впадинах не больше";
extern double shamuBarLessThan = 50;
extern string A24 = "Цвета верхнего и нижнего паттернов Шаму";
extern color  upSHAMUcolor = Blue;
extern color  dnSHAMUcolor = Red;
extern string A25 = "=======================================================";
extern string A26 = "======= Паттерн Экстремальный крюк (HFE) ==============";
extern string A27 = "Включение/выключение поиска паттерна";
extern bool   showHFEpattern = true;
extern string A28 = "Включение/выключение звукового сигнала при нахождении паттерна";
extern bool   alertHFEpattern = true;
extern string soundHFEpattern = "AG_SoundLevel_4.wav";
extern string A29 = "Крюк выше, чем";
extern double hfeBarGreatThan = 200;
extern string A30 = "Цвета верхнего и нижнего паттернов HFE";
extern color  upHFEcolor = Blue;
extern color  dnHFEcolor = Red;
extern string A31 = "=======================================================";
extern string A32 = "======= Паттерн Вегас (Vegas Trade - VT) ==============";
extern string A33 = "Включение/выключение поиска паттерна";
extern bool   showVTpattern = true;
extern string A34 = "Включение/выключение звукового сигнала при нахождении паттерна";
extern bool   alertVTpattern = true;
extern string soundVTpattern = "AG_SoundLevel_5.wav";
extern string A35 = "Максимальное кол-во баров паттерна";
extern int    vtMaxBarsAmount = 20;
extern string A36 = "Настройки LSMA: использовать или нет, период LSMA";
extern bool   useLSMA = true;
extern int    lsmaPeriod = 25;
extern string A37 = "Цвета верхнего и нижнего паттернов VT";
extern color  upVTcolor = Blue;
extern color  dnVTcolor = Red;
extern string A38 = "=======================================================";
extern string A39 = "======= Паттерн Призрак (Ghost Trade - GT) ============";
extern string A40 = "Включение/выключение поиска паттерна";
extern bool   showGTpattern = true;
extern string A41 = "Включение/выключение звукового сигнала при нахождении паттерна";
extern bool   alertGTpattern = true;
extern string soundGTpattern = "AG_SoundLevel_6.wav";
extern string A42 = "Цвета верхнего и нижнего паттернов GT";
extern color  upGTcolor = DarkOrange;
extern color  dnGTcolor = Orchid;
extern string A43 = "=======================================================";
extern string A44 = "======= Паттерн Разворотная дивергенция (Rev Diver) ===";
extern string A45 = "Включение/выключение поиска паттерна";
extern bool   showRDpattern = true;
extern string A46 = "Включение/выключение звукового сигнала при нахождении паттерна";
extern bool   alertRDpattern = true;
extern string soundRDpattern = "AG_SoundLevel_7.wav";
extern string A47 = "Цвета верхнего и нижнего паттернов Rev Diver";
extern color  upRDcolor = Red;
extern color  dnRDcolor = Olive;
extern string A48 = "=======================================================";
extern string Z1 = "Количество баров отображения индикатора. Все - 0";
extern int    indBarsCount = 500;


double azHistCCI[];                                // Буфер положительной гистограммы CCI
                                                   // ..после образования тренда
double bzHistCCI[];                                // Буфер отрицательной гистограммы CCI
                                                   // ..после образования тренда
double azHistNonTrendCCI[];                        // Буфер положительной гистограммы CCI
                                                   // ..до образования тренда
double bzHistNonTrendCCI[];                        // Буфер отрицательной гистограммы CCI
                                                   // ..до образования тренда
double mainCCI[];                                  // Буфер линии главного CCI
double turboCCI[];                                 // Буфер линии Turbo CCI
double upTrendBars[];                              // Количество баров CCI выше нуля
double dnTrendBars[];                              // Количество баров CCI ниже нуля

double g_lengthValue;                              // Значение для расчета LSMA
int    g_lsmaDeterminator;                         // Значение для расчета LSMA

int g_windowIndex;
string g_shortName;

#define INDICATOR_NAME  "Woodie\'s CCI"
#define REMAINDER_NAME  "REMAINDER"                // Часть имени объекта,..
                                                   // ..представляющего надпись..
                                                   // .."До закрытия свечи"
#define ZLR_STRING      "_ZLR_PATTERN_"            // Часть имени объектов паттерна ZLR
#define TLB_STRING      "_TLB_PATTERN_"            // Часть имени объектов паттерна TLB
#define SHAMU_STRING    "_SHAMU_PATTERN_"          // Часть имени объектов паттерна Шаму
#define HFE_STRING      "_HFE_PATTERN_"            // Часть имени объектов паттерна..
                                                   // .."Эктремальный крюк"
#define VT_STRING      "_VT_PATTERN_"              // Часть имени объекта паттерна..
                                                   // ..Вегас
#define GT_STRING      "_GT_PATTERN_"              // Часть имени объекта паттерна..
                                                   // ..Призрак
#define RD_STRING      "_RevDiver_PATTERN_"        // Часть имени объекта паттерна..
                                                   // ..Разворотная дивергенция

#define SIGN_UP         "UP"                       // Часть имени объектов, отображающих
#define SIGN_DN         "DN"                       // ..паттерны, для установления..
                                                   // ..различий между восходящими и..
                                                   // ..нисходящими паттернами

#define FIRST_PART      "_1_"                      // Части имен объектов для..
#define SECOND_PART     "_2_"                      // ..установления различия объектов,..
#define THIRD_PART      "_3_"                      // ..относящихся к одному паттерну
#define FOURTH_PART     "_4_"                   

// Значения ординат для отображения трех отрезков паттерна Шаму (нисходящий паттерн)
// Для восходящего паттерна каждое значение применяется с множителем -1
#define SHAMU_HIGH_LEFT_VALUE  300                 
#define SHAMU_LOW_LEFT_VALUE   200
#define SHAMU_HIGH_RIGHT_VALUE 250
#define SHAMU_LOW_RIGHT_VALUE  150

// Значения ординат для отображения двух отрезков паттерна "Экстремальный крюк" 
// (восходящий паттерн). Для нисходящего паттерна каждое значение применяется с
// множителем -1
#define HFE_LEFT_VALUE         150                 
#define HFE_MIDDLE_VALUE        50
#define HFE_RIGHT_VALUE        100

// Для универсальных функций, определяющих некоторые условия зеркально относительно..
// ..нулевой линии, требуется указание поиска максимума или минимума
#define GET_MAX 1
#define GET_MIN -1

#define RD_ARROW_CODE          108                 // Код графического объекта,..
                                                   // ..являющегося частью паттерна..
                                                   // ..Разворотная дивергенция
//+-------------------------------------------------------------------------------------+
//| Custom indicator initialization function                                            |
//+-------------------------------------------------------------------------------------+
int init()
{
   g_windowIndex = -1;
   g_shortName = INDICATOR_NAME + ", ID: " + GetTickCount();
   IndicatorBuffers(8);
// - 1 - == Проверка корректности значений настроечных параметров индикатора ============
   string name = WindowExpertName();
// - 1 - == Окончание блока =============================================================

// - 2 - == Связывание буферов с индексами, определение стилей ==========================

   SetIndexBuffer(0, azHistCCI);                   // Первый буфер - гистограмма..
   SetIndexStyle(0, DRAW_HISTOGRAM);               // ..положительных значений главного..
                                                   // ..CCI после образования тренда
   SetIndexBuffer(1, bzHistCCI);                   // Второй буфер - гистограмма..
   SetIndexStyle(1, DRAW_HISTOGRAM);               // ..отрицательных значений главного..
                                                   // ..CCI
   SetIndexBuffer(2, azHistNonTrendCCI);           // Третий буфер - гистограмма..
   SetIndexStyle(2, DRAW_HISTOGRAM);               // ..положительных значений главного..
                                                   // ..CCI до образования тренда
   SetIndexBuffer(3, bzHistNonTrendCCI);           // Четвертый буфер - гистограмма..
   SetIndexStyle(3, DRAW_HISTOGRAM);               // ..отрицательных значений главного..
                                                   // ..CCI до образования тренда
   SetIndexBuffer(4, mainCCI);                     // Пятый буфер - линия главного CCI
   SetIndexStyle(4, DRAW_LINE);
   
   SetIndexBuffer(5, turboCCI);                    // Шестой буфер - линия Turbo CCI
   SetIndexStyle(5, DRAW_LINE);

   SetIndexBuffer(6, upTrendBars);                 // Седьмой буфер - кол-во баров выше 0
   SetIndexBuffer(7, dnTrendBars);                 // Восьмой буфер - кол-во баров ниже 0
// - 2 - == Окончание блока =============================================================

// - 3 - == Инициализация значений для расчета LSMA =====================================
   g_lengthValue = (lsmaPeriod + 1)/3.0;
   g_lsmaDeterminator = (lsmaPeriod*(lsmaPeriod+1));
   if (g_lsmaDeterminator == 0)
   {
      Alert("Значение lsmaPeriod должно быть больше 0. Принято значение 25.");
      lsmaPeriod = 25;
      g_lsmaDeterminator = 650;
   }
// - 3 - == Окончание блока =============================================================

   IndicatorShortName(g_shortName);

   return(0);
}
//+-------------------------------------------------------------------------------------+
//| Custom indicator deinitialization function                                          |
//+-------------------------------------------------------------------------------------+
int deinit()
{
   DeleteAllObjects();
   return(0);
}
//+-------------------------------------------------------------------------------------+
//| Удаление всех объектов, созданных индикатором                                       |
//+-------------------------------------------------------------------------------------+
void DeleteAllObjects()
{
   for (int i = ObjectsTotal()-1; i >= 0; i--)     // Удалим все свои объекты
      if (StringSubstr(ObjectName(i), 0, StringLen(g_shortName)) == g_shortName)
         ObjectDelete(ObjectName(i));
}
//+-------------------------------------------------------------------------------------+
//| Определение индекса бара, с которого необходимо производить перерасчет              |
//+-------------------------------------------------------------------------------------+
int GetRecalcIndex(int& total)
{
   int counted_bars = IndicatorCounted();
   total = Bars - 1 - MathMax(mainCCIperiod,       // Определение первого бара истории,..
                              turboCCIperiod);     // ..на котором будут доступны..
                                                   // ..адекватные значения обоих CCI
   if (indBarsCount > 0 && indBarsCount < total)   // Если не нужно рассчитывать всю..
      total = indBarsCount;                        // ..историю, то начнем с указанного..
                                                   // ..бара
   if (counted_bars == 0)                          // Кол-во посчитанных баров - 0. Будут
   {                                               // ..пересчитаны все буфера с самого..
      ArrayInitialize(mainCCI, EMPTY_VALUE);       // ..начала. Очистка буферов
      ArrayInitialize(turboCCI, EMPTY_VALUE);
      ArrayInitialize(azHistCCI, EMPTY_VALUE);
      ArrayInitialize(bzHistCCI, EMPTY_VALUE);
      ArrayInitialize(azHistNonTrendCCI, EMPTY_VALUE);
      ArrayInitialize(bzHistNonTrendCCI, EMPTY_VALUE);
      ArrayInitialize(upTrendBars, 0);
      ArrayInitialize(dnTrendBars, 0);
      DeleteAllObjects();                          // И не забудем удалить все созданные
                                                   // ..объекты
      return(total);                               // Если вся история - то total
   }
   return(Bars - counted_bars - 1);                // Начинаем с нового бара
}
//+-------------------------------------------------------------------------------------+
//| Обработка положительных значений главного CCI                                       |
//+-------------------------------------------------------------------------------------+
void ProcessCCIAboveZero(int index)
{
   upTrendBars[index] = upTrendBars[index+1]+1;    // Кол-во баров восходящего тренда..
                                                   // ..увеличивается
   dnTrendBars[index] = dnTrendBars[index+1];      // Кол-во баров нисходящего тренда..
                                                   // ..сохраняется
   if (upTrendBars[index] >= trendBars)            // Если достигнуто кол-во баров для..
   {                                               // ..подтверждения тренда, то..
      dnTrendBars[index] = 0;                      // ..происходит смена тренда
      azHistCCI[index] = mainCCI[index];           // Отображается восходящий тренда
      return;
   }
   azHistNonTrendCCI[index] = mainCCI[index];      // Отображается лишь намек восходящего
                                                   // ..тренда
}
//+-------------------------------------------------------------------------------------+
//| Обработка отрицательных значений главного CCI                                       |
//+-------------------------------------------------------------------------------------+
void ProcessCCIBelowZero(int index)
{
   dnTrendBars[index] = dnTrendBars[index+1]+1;    // Кол-во баров нисходящего тренда..
                                                   // ..увеличивается
   upTrendBars[index] = upTrendBars[index+1];      // Кол-во баров восходящего тренда..
                                                   // ..сохраняется
   if (dnTrendBars[index] >= trendBars)            // Если достигнуто кол-во баров для..
   {                                               // ..подтверждения тренда, то..
      upTrendBars[index] = 0;                      // ..происходит смена тренда
      bzHistCCI[index] = mainCCI[index];           // Отображается нисходящий тренд
      return;
   }
   bzHistNonTrendCCI[index] = mainCCI[index];      // Отображается лишь намек нисходящего
                                                   // ..тренда
}
//+-------------------------------------------------------------------------------------+
//| Отображение информации индикатора на указанном баре                                 |
//+-------------------------------------------------------------------------------------+
void ShowData(int index)
{
   mainCCI[index] = iCCI(NULL, 0, mainCCIperiod,   // Значение главного CCI
                         PRICE_TYPICAL, index);
   turboCCI[index] = iCCI(NULL, 0, turboCCIperiod, // Значение Turbo CCI
                          PRICE_TYPICAL, index);
   azHistCCI[index] = 0;                           // По умолчанию гистограммы не..
   bzHistCCI[index] = 0;                           // ..отображаются
   azHistNonTrendCCI[index] = 0;
   bzHistNonTrendCCI[index] = 0;
   if (mainCCI[index] > 0 ||                       // Положительные значения главного CCI
       (mainCCI[index] == 0 && azHistCCI[index+1] > 0))
      ProcessCCIAboveZero(index);
   else                                            // Отрицательные значения главного CCI
      ProcessCCIBelowZero(index);                  
}
//+-------------------------------------------------------------------------------------+
//| Добавление ведущего нуля при преобразовании числа в строку                          |
//+-------------------------------------------------------------------------------------+
string LeadZero(int val)
{
   if (val > 9)                                    // К числам более 9 (10, 11 и т.д) не
      return(DoubleToStr(val, 0));                 // ..нужно добавлять лидирующий ноль
   return("0" + DoubleToStr(val, 0));              // Ко всем остальным числам - нужно
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего тренда на указанном баре                            |
//+-------------------------------------------------------------------------------------+
bool IsUpTrend(int index)
{
   return(upTrendBars[index] >= trendBars);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего тренда на указанном баре                            |
//+-------------------------------------------------------------------------------------+
bool IsDnTrend(int index)
{
   return(dnTrendBars[index] >= trendBars);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего паттерна ZLR на указанном баре                      |
//+-------------------------------------------------------------------------------------+
bool IsUpZLRPattern(int index)
{
   if (mainCCI[index+3] <= 0 ||                    // Защита от блуждания CCI вокруг 0
       mainCCI[index+2] <= 0 ||                    // Бары вокруг бара отскока должны
       mainCCI[index] <= 0)                        // ..быть ниже нулевого уровня
      return(false);
      
   if (mainCCI[index+1] > zlrBarLessThan)          // Бар отскока должен быть ниже уровня
      return(false);                               // ..zlrBarLessThan
      
   if (mainCCI[index+1] >= mainCCI[index] ||       // Значения баров вокруг бара отскока
       mainCCI[index+1] >= mainCCI[index+2])       // ..должны быть больше значения..
      return(false);                               // ..бара отскока
      
   return(true);                                   // Отскок от нулевой линии вверх
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего паттерна ZLR на указанном баре                      |
//+-------------------------------------------------------------------------------------+
bool IsDnZLRPattern(int index)
{
   if (mainCCI[index+3] >= 0 ||                    // Защита от блуждания CCI вокруг 0
       mainCCI[index+2] >= 0 ||                    // Бары вокруг бара отскока должны 
       mainCCI[index] >= 0)                        // ..быть ниже нулевого уровня
      return(false);

   if (mainCCI[index+1] < -zlrBarLessThan)         // Бар отскока должен быть выше уровня
      return(false);                               // ..zlrBarLessThan
      
   if (mainCCI[index+1] <= mainCCI[index] ||       // Значения баров вокруг бара отскока 
       mainCCI[index+1] <= mainCCI[index+2])       // ..должны быть меньше значения..
      return(false);                               // ..бара отскока
      
   return(true);                                   // Отскок от нулевой линии вниз
}
//+-------------------------------------------------------------------------------------+
//| Отображение паттерна ZLR                                                            |
//+-------------------------------------------------------------------------------------+
void ShowZLR(int index, string sign)
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
         return;                                   // ..подокно, то уходим
         
// - 1 - == Определение параметров объекта ==============================================
   string name = g_shortName + ZLR_STRING +        // Имя объекта
                 Time[index] + sign; 
   string description = "Down Zero Level Reject";  // Подпись нижнего паттерна
   double price1and3 = zlrBarLessThan + 50;        // Цена левой и правой точки
   double price2 = zlrBarLessThan;                 // Цена правой (по графику) точки
   color clr = dnZLRcolor;                         // Цвет объекта
   if (sign == SIGN_UP)                            // Для верхнего паттерна:
   {
      description = "Up Zero Level Reject";        // Подпись верхнего паттерна
      price1and3 = -price1and3;                    // Цена левой и правой точки
      price2 = -price2;                            // Цена правой (по графику) точки
      clr = upZLRcolor;                            // Цвет объекта
   }
// - 1 - == Окончание блока =============================================================

// - 2 - == Создание объекта ============================================================
   if (ObjectFind(name) < 0)                       // Если объект не существует
   {
      ObjectCreate(name, OBJ_TRIANGLE,             // Создадим его
                   g_windowIndex, Time[index+2], 
                   price1and3, Time[index+1], price2, Time[index], price1and3);
      ObjectSet(name, OBJPROP_COLOR, clr);         // Установим цвет,..
      ObjectSet(name, OBJPROP_BACK, true);         // ..положение относительно графики..
      ObjectSetText(name, description);            // ..и подпись
      return;
   }
// - 2 - == Окончание блока =============================================================
   
   ObjectMove(name, 0, Time[index+2], price1and3); // Перемещение существующего объекта
   ObjectMove(name, 1, Time[index+1], price2);
   ObjectMove(name, 2, Time[index], price1and3);
}
//+-------------------------------------------------------------------------------------+
//| Удаление указанного графического объекта индикатора                                 |
//+-------------------------------------------------------------------------------------+
void DeleteObject(string type, int index)
{
   string name = g_shortName + type + Time[index] +// Имя объекта восходящего паттерна
                 SIGN_UP;
   if (ObjectFind(name) > 0)                       // Если нашли такой объект, то..
   {                                               // ..удаляем его
      ObjectDelete(name);
      return;
   }
   
   name = g_shortName + type + Time[index] + SIGN_DN;// Имя объекта нисходящего паттерна
   if (ObjectFind(name) > 0)                       // Если нашли такой паттерн, то..
      ObjectDelete(name);                          // ..удаляем его
}
//+-------------------------------------------------------------------------------------+
//| Поиск и отображение паттерна ZLR                                                    |
//+-------------------------------------------------------------------------------------+
void FindAndShowZLR(int index)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн ZLR ======================================================
   if (IsUpTrend(index))                           // Проверка наличия восходящего тренда
      if (IsUpZLRPattern(index))                   // Обнаружение паттерна
      {
         ShowZLR(index, SIGN_UP);                  // Отображение паттерна
         PatternAlert(alertZLRpattern, index,      // Звуковое оповещение о нахождении..
                      lastAlert, soundZLRpattern); // ..паттерна
         return;
      }
// - 1 - == Окончание блока =============================================================

// - 2 - == Нисходящий паттерн ZLR ======================================================
   if (IsDnTrend(index))                           // Проверка наличия нисходящего тренда
      if (IsDnZLRPattern(index))                   // Обнаружение паттерна
      {
         ShowZLR(index, SIGN_DN);                  // Отображение паттерна
         PatternAlert(alertZLRpattern, index,      // Звуковое оповещение о нахождении..
                      lastAlert, soundZLRpattern); // ..паттерна
         return;
      }
// - 2 - == Окончание блока =============================================================

   DeleteObject(ZLR_STRING, index);                // Удаление паттерна, если он был..
                                                   // ..ранее найден
}
//+-------------------------------------------------------------------------------------+
//| Подача звукового сигнала об образовании паттерна                                    |
//+-------------------------------------------------------------------------------------+
void PatternAlert(bool doAlert, int index, datetime& lastAlert, string soundFileName)
{
   if (!doAlert)                                   // Звуковое оповещение не требуется
      return;
   if (index != 1)                                 // Оповещение только для крайнего..
      return;                                      // ..правого по графику паттерна
   if (lastAlert >= Time[0])                       // На текущем баре оповещение уже..
      return;                                      // ..производилось
      
   PlaySound(soundFileName);                       // Воспроизведение звука
   lastAlert = Time[0];                            // Запомним, что оповещение..
                                                   // ..произведено
}
//+-------------------------------------------------------------------------------------+
//| Преобразование числа секунд в строку: минуты и секунды                              |
//+-------------------------------------------------------------------------------------+
string SecToMinSec(int time)
{
   int minutes = time/60;
   int seconds = time%60;
   return(LeadZero(minutes) + ":" + LeadZero(seconds));
}
//+-------------------------------------------------------------------------------------+
//| Отображение графического объекта с временем, оставшегося до окончания бара          |
//+-------------------------------------------------------------------------------------+
void ShowAndSoundRemainder(datetime timeRemain)
{
   string name = g_shortName + REMAINDER_NAME;
// - 1 - == Создание объекта ============================================================
   if (ObjectFind(name) < 0)                       // Если объект не существует
   {
      ObjectCreate(name, OBJ_LABEL, g_windowIndex, 0, 0);// Создание объекта
      ObjectSet(name, OBJPROP_CORNER, 1);          // Установка угла привязки
      ObjectSet(name, OBJPROP_XDISTANCE, 1);       // Определение координат..
      ObjectSet(name, OBJPROP_YDISTANCE, 1);       // ..отображения
   }
// - 1 - == Окончание блока =============================================================
      
// - 2 - == Определение содержимого объекта =============================================
   color clr = colorFormingBar;                    // По умолчанию цвет определен для..
                                                   // ..формирующегося бара
   if (timeRemain <= secondsToBarClose)            // Если до окончания формирования..
   {                                               // ..осталось мало времени, то..
      clr = colorClosingBar;                       // ..назначим другой цвет и..
      if (closeBarAlert)                           // ..издадим соответствующий звук
         PlaySound(closeBarSound);
   }
   
   ObjectSetText(name,                             // Изменение содержимого объекта
                 "До закрытия свечи: " + SecToMinSec(timeRemain), 10, "Impact", clr);
// - 2 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Отображение времени, оставшегося до окончания бара                                  |
//+-------------------------------------------------------------------------------------+
void BarRemainder()
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
      return;                                      // ..подокно, то уходим
   
   datetime timeRemain = Time[0] + Period()*60 -   // Время до закрытия свечи в секундах
                         TimeCurrent();
   timeRemain  = MathMax(0, timeRemain);           // Время не должно быть отрицательным
   ShowAndSoundRemainder(timeRemain);              // Отображение времени, звуковое..
                                                   // ..оповещение
}
//+-------------------------------------------------------------------------------------+
//| Расчет коэффициентов K и B уравнения прямой по двум точкам                          |
//+-------------------------------------------------------------------------------------+
double GetKAndBFromCoords(int x1, double y1, int x2, double y2, double &bKoef)
{
   if (x1 - x2 == 0 || x1 == 0)                    // Отсечение ситуаций деления на 0
   {
      bKoef = 0;                                   // Такой прямой попросту нет
      return (0);
   }
   
   bKoef = (y2*x1 - y1*x2)/(x1 - x2);              // Коэффициент B уравнения прямой
   return ((y1 - bKoef)/x1);                       // Коэффициент К уравнения прямой
}
//+-------------------------------------------------------------------------------------+
//| Нахождение бара, представлящего последний бар предыдущего тренда                    |
//+-------------------------------------------------------------------------------------+
int GetLastTrendFinish(int index, int total, double& updn_trendBars[], int mul)
{
   for (int i = index+1; i <= total; i++)          // Участок поиска бара нисходящего..
      if (updn_trendBars[i] >= trendBars && mul*mainCCI[i] <= 0)// ..тренда
         break;
   return(MathMin(i, total));
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия локального экстремума или изгиба на указанном баре..            |
//| ..указанного индикаторного буфера                                                   |
//+-------------------------------------------------------------------------------------+
bool IsLocalExt(double& buffer[], int index, int mul)
{
   double centerVal = mul*buffer[index];
   
   if (centerVal <= 0)                             // Проверка нахождения потенциального
      return(false);                               // ..экстремума с нужной стороны от..
                                                   // ..нуля: при mul = 1 - выше нуля,
                                                   // ..при Mul = -1 - ниже нуля
   
   double leftVal = mul*buffer[index+1];
   double rightVal = mul*buffer[index-1];
   if (centerVal > rightVal && centerVal > leftVal)// Условие для экстремума
      return(true);
   if (centerVal - rightVal > leftVal - centerVal &&// Условие для изгиба
       leftVal > 0 && rightVal > 0)                // Все значения с одной стороны от 0
      return(true);
   return(false);
}
//+-------------------------------------------------------------------------------------+
//| Поиск всех локальных экстремумов (или изгибов), начиная с бара beginIndex и..       |
//| заканчивая баром trendFinish                                                        |
//+-------------------------------------------------------------------------------------+
int GetLocalExt(int trendFinish, int beginIndex, int& max[], double& value[], int mul)
{
   int cnt = 0;
   for (int i = beginIndex+1; i < trendFinish; i++)
   {
      double cci = mainCCI[i];                     // По умолчанию экстремум - CCI
      if (tlbUseTCCI && mul*cci < mul*turboCCI[i]) // Если нужно учесть TCCI и..
      {                                            // ..его значение больше CCI, то..
         cci = turboCCI[i];                        // ..экстремум определяется по TCCI
         if (!IsLocalExt(turboCCI, i, mul))             
            continue;                              
      }
      else                                         // Если экстремум нужно определить по
         if (!IsLocalExt(mainCCI, i, mul))         // ..CCI
            continue;
      max[cnt] = i;                                // Экстремум найден, запишем данные
      value[cnt] = cci;
      cnt++;                                       // Следующий элемент max и value
      if (cnt > 1)                                 // Если один экстремум уже найден, то
      {
         if (MathAbs(cci) > MathAbs(value[0]) &&   // Посмотрим, не подойдет ли нам этот
             MathAbs(cci) > tlbBeginTLBarGreatThan)// ..экстремум в качестве начальной..
            return(cnt);                           // ..точки линии тренда
         if (MathAbs(cci) > tlbBeginTLBarGreatThan)// Если второй экстремум за пределами
            return(0);                             // ..уровня, то линия не формируется
      }
   }
   return(0);   
}
//+-------------------------------------------------------------------------------------+
//| Нахождение конечной точки и определение пробоя линии тренда                         |
//+-------------------------------------------------------------------------------------+
bool IsLineBreak(int curBarIndex, int beginIndex, int& localNum[], double& localVal[], 
                 double& currTLB, int mul)
{
// - 1 - == Подбор линии тренда по экстремумам, находящихся правее начальной точки ======
   double bKoef, kKoef;
   for (int i = beginIndex-1; i >= 0; i--)
   {
      kKoef = GetKAndBFromCoords(localNum[i],      // Расчет коэффициентов уравнения..
                                 localVal[i],      // ..прямой - линии тренда
                                 localNum[beginIndex], localVal[beginIndex], bKoef);
      for (int j = i-1; j >= 0; j--)
      {                                 
         currTLB = kKoef*localNum[j] + bKoef;      // Точка линии тренда бара localNum[j]
         if (mul*localVal[j] > mul*currTLB)        // Один из правых экстремумов пробил..
            break;                                 // ..потенциальную линию тренда - не..
      }
      if (j < 0)                                   // Конечная точка подобрана
         break;
   }
   if (i < 0)                                      // Линия тренда не подобрана
      return(false);
// - 1 - == Окончание блока =============================================================

// - 2 - == Определение пробоя линии тренда =============================================
   currTLB = kKoef*curBarIndex + bKoef;            // Точка линии тренда бара curBarIndex

   if (mul*mainCCI[curBarIndex] <= mul*currTLB ||  // Главный CCI не пробил линию тренда
       (mul*mainCCI[curBarIndex] < tlbBarGreatThan &&// Главный CCI не более уровня..
        tlbBarGreatThan != 0))                     // ..tlbBarGreatThan и уровень не..
                                                   // ..отключен
      return(false);                               // Нет пробоя линии тренда
// - 2 - == Окончание блока =============================================================
   return(true);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего паттерна TLB на указанном баре                      |
//+-------------------------------------------------------------------------------------+
bool IsUpTLBPattern(int index, int total)
{
// - 1 - == Определение индекса последнего бара нисходящего тренда ======================
   int dnTrendFinish = GetLastTrendFinish(index, total, dnTrendBars, 1);
// - 1 - == Окончание блока =============================================================

// - 2 - == Поиск всех локальных максимумов или изгибов CCI или TCCI ====================
   int maxPeaksAmount = dnTrendFinish - index;     // Максимальное возможное кол-во пиков
                                                   // ..на участке восходящего тренда
   double localMaxVal[];                           // Значения локальных максимумов
   int localMaxNum[];                              // Индексы баров локальных максимумов
   ArrayResize(localMaxVal, maxPeaksAmount);       // Указание длин массивов, исходя из..
   ArrayResize(localMaxNum, maxPeaksAmount);       // ..длины участка тренда
   int maxAmount = GetLocalExt(dnTrendFinish,      // Получим общее кол-во максимумов и..
                               index, localMaxNum, localMaxVal, GET_MAX);// изгибов
   if (maxAmount == 0)                             // Линию тренда нельзя провести - 
      return(false);                               // ..точки не найдены
// - 2 - == Окончание блока =============================================================

// - 3 - == Определение факта пробоя линии тренда =======================================
   double currTLB;
   if (!IsLineBreak(index, maxAmount-1, localMaxNum, localMaxVal, currTLB, GET_MAX))
      return(false);                               // Линию тренда провести нельзя
// - 3 - == Окончание блока =============================================================

// - 4 - == Отображение паттерна ========================================================
   if (index > 0)                                  // Исключение дублирования паттернов
      DeleteObject(TLB_STRING, index+1);
   ShowTLB(localMaxNum[maxAmount-1],               // Отображение трендовой линии
           localMaxVal[maxAmount-1], index, currTLB, SIGN_UP);
   return(true);                                   // Пробой линии тренда состоялся
// - 4 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего паттерна TLB на указанном баре                      |
//+-------------------------------------------------------------------------------------+
bool IsDnTLBPattern(int index, int total)
{
// - 1 - == Определение индекса последнего бара восходящего тренда ======================
   int upTrendFinish = GetLastTrendFinish(index, total, upTrendBars, 1);
// - 1 - == Окончание блока =============================================================

// - 2 - == Поиск всех локальных минимумов или изгибов CCI или TCCI =====================
   int maxPeaksAmount = upTrendFinish - index;     // Максимальное возможное кол-во пиков
                                                   // ..на участке нисходящего тренда
   double localMinVal[];                           // Значения локального минимума
   int localMinNum[];                              // Индексы баров локальных минимумов
   ArrayResize(localMinVal, maxPeaksAmount);       // Указание длин массивов, исходя из..
   ArrayResize(localMinNum, maxPeaksAmount);       // ..длины участка тренда
   int maxAmount = GetLocalExt(upTrendFinish,      // Получим общее кол-во максимумов и..
                               index, localMinNum, localMinVal, GET_MIN);// изгибов
   if (maxAmount == 0)                             // Линию тренда нельзя провести - 
      return(false);                               // ..точки не найдены
// - 2 - == Окончание блока =============================================================

// - 3 - == Определение пробоя линии тренда =============================================
   double currTLB;
   if (!IsLineBreak(index, maxAmount-1, localMinNum, localMinVal, currTLB, GET_MIN))
      return(false);                               // Линию тренда провести нельзя
// - 3 - == Окончание блока =============================================================

// - 4 - == Отображение паттерна ========================================================
   if (index > 0)                                  // Исключение дублирования паттернов
      DeleteObject(TLB_STRING, index+1);
   ShowTLB(localMinNum[maxAmount-1],               // Отображение трендовой линии
           localMinVal[maxAmount-1], index, currTLB, SIGN_DN);
   return(true);                                   // Пробой линии тренда состоялся
// - 4 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Отображение паттерна TLB                                                            |
//+-------------------------------------------------------------------------------------+
void ShowTLB(int leftIndex, double leftVal, int rightIndex, double rightVal, string sign)
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
         return;                                   // ..подокно, то уходим
         
// - 1 - == Определение параметров объекта ==============================================
   string name = g_shortName + TLB_STRING +        // Имя объекта
                 Time[rightIndex] + sign; 
   string description = "Down Trend Line Break";   // Подпись нижнего паттерна
   color clr = dnTLBcolor;                         // Цвет объекта
   if (sign == SIGN_UP)                            // Для верхнего паттерна:
   {
      description = "Up Trend Line Break";         // Подпись верхнего паттерна
      clr = upZLRcolor;                            // Цвет объекта
   }
// - 1 - == Окончание блока =============================================================

// - 2 - == Создание объекта ============================================================
   if (ObjectFind(name) < 0)                       // Если объект не существует
   {
      ObjectCreate(name, OBJ_TREND,                // Создадим его
                   g_windowIndex, Time[leftIndex], leftVal, Time[rightIndex], rightVal);
      ObjectSet(name, OBJPROP_COLOR, clr);         // Установим цвет,..
      ObjectSet(name, OBJPROP_WIDTH, 2);           // ..толщину,..
      ObjectSet(name, OBJPROP_BACK, false);        // ..положение относительно графики..
      ObjectSetText(name, description);            // ..подпись и..
      ObjectSet(name, OBJPROP_RAY, false);         // ..отсутствие луча
      return;
   }
// - 2 - == Окончание блока =============================================================
   
   ObjectMove(name, 0, Time[leftIndex], leftVal);  // Перемещение существующего объекта
   ObjectMove(name, 1, Time[rightIndex], rightVal);
}
//+-------------------------------------------------------------------------------------+
//| Поиск и отображение паттерна TLB                                                    |
//+-------------------------------------------------------------------------------------+
void FindAndShowTLB(int index, int total)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн TLB ======================================================
   if (IsUpTrend(index))                           // Проверка наличия восходящего тренда
      if (IsUpTLBPattern(index, total))            // Обнаружение и отображение паттерна
      {
         PatternAlert(alertTLBpattern, index,      // Звуковое оповещение о нахождении..
                      lastAlert, soundTLBpattern); // ..паттерна
         return;
      }
// - 1 - == Окончание блока =============================================================

// - 2 - == Нисходящий паттерн TLB ======================================================
   if (IsDnTrend(index))                           // Проверка наличия нисходящего тренда
      if (IsDnTLBPattern(index, total))            // Обнаружение и отображение паттерна
      {
         PatternAlert(alertTLBpattern, index,      // Звуковое оповещение о нахождении..
                      lastAlert, soundTLBpattern); // ..паттерна
         return;
      }
// - 2 - == Окончание блока =============================================================

   DeleteObject(TLB_STRING, index);                // Удаление паттерна, если он был..
                                                   // ..ранее найден
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего паттерна SHAMU на указанном баре                    |
//+-------------------------------------------------------------------------------------+
bool IsUpSHAMUPattern(int index, int total)
{
// - 1 - == Проверка начального условия существования паттерна - пересечение нуля =======
   if (!(mainCCI[index] > 0 && mainCCI[index+1] < 0))// Если CCI не пересек нулевую линию
      return(false);                               // ..снизу вверх, то паттерн не..
                                                   // ..состоялся
// - 1 - == Окончание блока =============================================================
                                                   
// - 2 - == Нахождение еще двух пересечений нулевой линии ===============================
   int i = index + 1, zeroCrossCount = 1,          // Текущий индекс, кол-во пересечений,
       maxBarIndex, minBarIndex = i;               // ..индексы максимума и минимума
   while (!(mainCCI[i] > 0 && mainCCI[i+1] < 0 &&  // Цикл исполняется, пока не будет..
            zeroCrossCount == 2))                  // ..найдено три пересечения нуля
   {
      if (i >= total)                              // Паттерн не сформирован, если..
         return(false);                            // ..достигнут конец истории

      if (MathAbs(mainCCI[i]) > shamuBarLessThan)  // Паттерн не сформирован, если CCI..
         return(false);                            // ..вышел за уровень +/-50
         
      if (zeroCrossCount == 1 &&                   // После первого пересечения..
          mainCCI[i] < mainCCI[minBarIndex])       // ..отслеживается впадина
         minBarIndex = i;
         
      if (zeroCrossCount == 2 &&                   // После второго пересечения..
          mainCCI[i] > mainCCI[maxBarIndex])       // ..отслеживается вершина
         maxBarIndex = i;
      
      if (mainCCI[i] < 0 && mainCCI[i+1] > 0 &&    // Найдено второе пересечение нуля
          zeroCrossCount == 1)                     // Это пересечение сверху вниз
      {
         zeroCrossCount = 2;                       // Найдено два пересечения
         maxBarIndex = i + 1;                      // Отслеживаем вершину
      }
      i++;
   }
// - 2 - == Окончание блока =============================================================
   
// - 3 - == Отображение паттерна ========================================================
   ShowShamu(i+1, maxBarIndex, minBarIndex, index, SIGN_UP); 
   return(true);
// - 3 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего паттерна SHAMU на указанном баре                    |
//+-------------------------------------------------------------------------------------+
bool IsDnSHAMUPattern(int index, int total)
{
// - 1 - == Проверка начального условия существования паттерна - пересечение нуля =======
   if (!(mainCCI[index] < 0 && mainCCI[index+1] > 0))// Если CCI не пересек нулевую линию
      return(false);                               // ..сверху вниз, то паттерн не..
                                                   // ..состоялся
// - 1 - == Окончание блока =============================================================
                                                   
// - 2 - == Нахождение еще двух пересечений нулевой линии ===============================
   int i = index + 1, zeroCrossCount = 1,          // Текущий индекс, кол-во пересечений,
       maxBarIndex = i, minBarIndex;               // ..индексы максимума и минимума
   while (!(mainCCI[i] < 0 && mainCCI[i+1] > 0 &&  // Цикл исполняется, пока не будет..
            zeroCrossCount == 2))                  // ..найдено три пересечения нуля
   {
      if (i >= total)                              // Паттерн не сформирован, если..
         return(false);                            // ..достигнут конец истории

      if (MathAbs(mainCCI[i]) > shamuBarLessThan)  // Паттерн не сформирован, если CCI..
         return(false);                            // ..вышел за уровень +/-50
         
      if (zeroCrossCount == 1 &&                   // После первого пересечения..
          mainCCI[i] > mainCCI[maxBarIndex])       // ..отслеживается вершина
         maxBarIndex = i;
         
      if (zeroCrossCount == 2 &&                   // После второго пересечения..
          mainCCI[i] < mainCCI[minBarIndex])       // ..отслеживается впадина
         minBarIndex = i;
      
      if (mainCCI[i] > 0 && mainCCI[i+1] < 0 &&    // Найдено второе пересечение нуля
          zeroCrossCount == 1)                     // Это пересечение снизу вверх
      {
         zeroCrossCount = 2;                       // Найдено два пересечения
         minBarIndex = i + 1;                      // Отслеживаем впадину
      }
      i++;
   }
// - 2 - == Окончание блока =============================================================
   
// - 3 - == Отображение паттерна ========================================================
   ShowShamu(i+1, minBarIndex, maxBarIndex, index, SIGN_DN); 
   return(true);
// - 3 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Отображение паттерна Шаму                                                           |
//+-------------------------------------------------------------------------------------+
void ShowShamu(int leftIndex, int maxBarIndex, int minBarIndex, int rightIndex, 
               string sign)
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
         return;                                   // ..подокно, то уходим
// - 1 - == Определение характеристик объекта в зависимости от типа паттерна ============
   int mul = -1;                                   // Множитель для восходящего паттерна
   color clr = upSHAMUcolor;                       // Цвет восходящего паттерна
   string description = "Up Shamu Pattern";        // Описание восходящего паттерна
   if (sign == SIGN_DN)                            // Если паттерн нисходящий
   {
      mul = 1;                                     // Множитель нисходящего паттерна
      clr = dnSHAMUcolor;                          // Цвет нисходящего паттерна
      description = "Down Shamu Pattern";          // Описание нисходящего паттерна
   }
// - 1 - == Окончание блока =============================================================
      
// - 2 - == Отображение первой трети паттерна - первый отрезок зиг-зага =================
   string name = g_shortName + SHAMU_STRING +      // Имя объекта, представляющего..
                 FIRST_PART + Time[rightIndex]     // ..первую треть паттерна
                 + sign;           
   ShowTrendLine(name, mul*SHAMU_HIGH_LEFT_VALUE,  // Отображение объекта
                 Time[leftIndex], 
                 mul*SHAMU_LOW_LEFT_VALUE, Time[maxBarIndex], clr, description);
// - 2 - == Окончание блока =============================================================
                 
// - 3 - == Отображение средней трети паттерна - второй отрезок зиг-зага ================
   name = g_shortName + SHAMU_STRING + SECOND_PART // Имя объекта, представляющего..
          + Time[rightIndex] + sign;               // ..среднюю треть паттерна
   ShowTrendLine(name, mul*SHAMU_LOW_LEFT_VALUE,   // Отображение объекта
                 Time[maxBarIndex], 
                 mul*SHAMU_HIGH_RIGHT_VALUE, Time[minBarIndex], clr, description);
// - 3 - == Окончание блока =============================================================

// - 4 - == Отображение третьей трети паттерна - третий отрезок зиг-зага ================
   name = g_shortName + SHAMU_STRING + THIRD_PART +// Имя объекта, представляющего третью
                 Time[rightIndex] + sign;          // ..треть паттерна
   ShowTrendLine(name, mul*SHAMU_HIGH_RIGHT_VALUE, // Отображение объекта
                 Time[minBarIndex], 
                 mul*SHAMU_LOW_RIGHT_VALUE, Time[rightIndex], clr, description);
// - 4 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Отображение трендовой линии                                                         |
//+-------------------------------------------------------------------------------------+
void ShowTrendLine(string name, double leftVal, datetime leftTime, double rightVal,
                   datetime rightTime, color clr, string description)
{
   if (ObjectFind(name) < 0)
   {
      ObjectCreate(name, OBJ_TREND, g_windowIndex, 
                   leftTime, leftVal, rightTime, rightVal);
      ObjectSet(name, OBJPROP_COLOR, clr);
      ObjectSet(name, OBJPROP_RAY, false);
      ObjectSet(name, OBJPROP_WIDTH, 2);
      ObjectSetText(name, description);
      return;
   }
   
   ObjectMove(name, 0, leftTime, leftVal);
   ObjectMove(name, 1, rightTime, rightVal);
}
//+-------------------------------------------------------------------------------------+
//| Отображение прямоугольника                                                          |
//+-------------------------------------------------------------------------------------+
void ShowRectangle(string name, double leftVal, datetime leftTime, double rightVal,
                   datetime rightTime, color clr, string description)
{
   if (ObjectFind(name) < 0)
   {
      ObjectCreate(name, OBJ_RECTANGLE, g_windowIndex, 
                   leftTime, leftVal, rightTime, rightVal);
      ObjectSet(name, OBJPROP_COLOR, clr);
      ObjectSet(name, OBJPROP_BACK, false);
      ObjectSet(name, OBJPROP_WIDTH, 2);
      ObjectSetText(name, description);
      return;
   }
   
   ObjectMove(name, 0, leftTime, leftVal);
   ObjectMove(name, 1, rightTime, rightVal);
}
//+-------------------------------------------------------------------------------------+
//| Удаление паттерна Шаму                                                              |
//+-------------------------------------------------------------------------------------+
void DeleteShamu(int index)
{
   DeleteObject(SHAMU_STRING + FIRST_PART, index);
   DeleteObject(SHAMU_STRING + SECOND_PART, index);
   DeleteObject(SHAMU_STRING + THIRD_PART, index);
}
//+-------------------------------------------------------------------------------------+
//| Поиск и отображение паттерна Шаму                                                   |
//+-------------------------------------------------------------------------------------+
void FindAndShowSHAMU(int index, int total)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн Шаму =====================================================
   if (IsUpSHAMUPattern(index, total))             // Обнаружение и отображение паттерна
   {
      PatternAlert(alertSHAMUpattern, index,       // Звуковое оповещение о нахождении..
                   lastAlert, soundSHAMUpattern);  // ..паттерна
      return;
   }
// - 1 - == Окончание блока =============================================================

// - 2 - == Нисходящий паттерн Шаму =====================================================
   if (IsDnSHAMUPattern(index, total))             // Обнаружение и отображение паттерна
   {
      PatternAlert(alertSHAMUpattern, index,       // Звуковое оповещение о нахождении..
                   lastAlert, soundSHAMUpattern);  // ..паттерна
      return;
   }
// - 2 - == Окончание блока =============================================================

   DeleteShamu(index);                             // Удаление паттерна, если он был..
                                                   // ..ранее найден
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего паттерна "Экстремальный крюк" на указанном баре     |
//+-------------------------------------------------------------------------------------+
bool IsUpHFEPattern(int index, int total)
{
   if (index + 2 >= total)                         // До конца истории должно быть не..
      return(false);                               // ..меньше трех баров
      
   if (mainCCI[index+1] > -hfeBarGreatThan)        // Средний бар предполагаемого крюка..
      return(false);                               // ..должен быть ниже уровня -200
      
   if (mainCCI[index] <= mainCCI[index+1])         // Текущий бар должен быть выше..
      return(false);                               // ..среднего бара - крюк вверх
      
   if (mainCCI[index+2] <= mainCCI[index+1])       // Бар слева от среднего должен быть..
      return(false);                               // ..выше среднего бара - начало крюка
      
   return(true);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего паттерна "Экстремальный крюк" на указанном баре     |
//+-------------------------------------------------------------------------------------+
bool IsDnHFEPattern(int index, int total)
{
   if (index + 2 >= total)                         // До конца истории должно быть не..
      return(false);                               // ..меньше трех баров
      
   if (mainCCI[index+1] <= hfeBarGreatThan)        // Средний бар предполагаемого крюка..
      return(false);                               // ..должен быть выше уровня 200
      
   if (mainCCI[index] >= mainCCI[index+1])          // Текущий бар должен быть ниже..
      return(false);                               // ..среднего бара - крюк вниз
      
   if (mainCCI[index+2] >= mainCCI[index+1])       // Бар слева от среднего должен быть..
      return(false);                               // ..ниже среднего бара - начало крюка
      
   return(true);
}
//+-------------------------------------------------------------------------------------+
//| Отображение паттерна "Экстремальный крюк"                                           |
//+-------------------------------------------------------------------------------------+
void ShowHFE(int index, string sign)
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
         return;                                   // ..подокно, то уходим
// - 1 - == Определение характеристик объекта в зависимости от типа паттерна ============
   int mul = 1;                                    // Множитель для восходящего паттерна
   color clr = upHFEcolor;                         // Цвет восходящего паттерна
   string description = "Up HFE Pattern";          // Описание восходящего паттерна
   if (sign == SIGN_DN)                            // Если паттерн нисходящий
   {
      mul = -1;                                    // Множитель нисходящего паттерна
      clr = dnHFEcolor;                            // Цвет нисходящего паттерна
      description = "Down HFE Pattern";            // Описание нисходящего паттерна
   }
// - 1 - == Окончание блока =============================================================
      
// - 2 - == Отображение первой половины паттерна - первый отрезок крюка =================
   string name = g_shortName + HFE_STRING +        // Имя объекта, представляющего..
                 FIRST_PART + Time[index]          // ..первую половину крюка
                 + sign;           
   ShowTrendLine(name, mul*HFE_LEFT_VALUE,         // Отображение объекта
                 Time[index+2], 
                 mul*HFE_MIDDLE_VALUE, Time[index+1], clr, description);
// - 2 - == Окончание блока =============================================================
                 
// - 3 - == Отображение второй половины паттерна - второй отрезок крюка =================
   name = g_shortName + HFE_STRING + SECOND_PART   // Имя объекта, представляющего..
          + Time[index] + sign;                    // ..вторую половину крюка
   ShowTrendLine(name, mul*HFE_MIDDLE_VALUE,       // Отображение объекта
                 Time[index+1], 
                 mul*HFE_RIGHT_VALUE, Time[index], clr, description);
// - 3 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Удаление паттерна "Экстремальный крюк"                                              |
//+-------------------------------------------------------------------------------------+
void DeleteHFE(int index)
{
   DeleteObject(HFE_STRING + FIRST_PART, index);
   DeleteObject(HFE_STRING + SECOND_PART, index);
}
//+-------------------------------------------------------------------------------------+
//| Поиск и отображение паттерна "Экстремальный крюк"                                   |
//+-------------------------------------------------------------------------------------+
void FindAndShowHFE(int index, int total)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн "Экстремальный крюк" =====================================
   if (IsUpHFEPattern(index, total))               // Обнаружение и отображение паттерна
   {
      ShowHFE(index, SIGN_UP);                     // Отображение крюка
      PatternAlert(alertHFEpattern, index,         // Звуковое оповещение о нахождении..
                   lastAlert, soundHFEpattern);    // ..паттерна
      return;
   }
// - 1 - == Окончание блока =============================================================

// - 2 - == Нисходящий паттерн "Экстремальный крюк" =====================================
   if (IsDnHFEPattern(index, total))               // Обнаружение и отображение паттерна
   {
      ShowHFE(index, SIGN_DN);                     // Отображение крюка
      PatternAlert(alertHFEpattern, index,         // Звуковое оповещение о нахождении..
                   lastAlert, soundHFEpattern);    // ..паттерна
      return;
   }
// - 2 - == Окончание блока =============================================================

   DeleteHFE(index);                               // Удаление паттерна, если он был..
                                                   // ..ранее найден
}
//+-------------------------------------------------------------------------------------+
//| Расчет значения LSMA                                                                |
//+-------------------------------------------------------------------------------------+
double GetLSMA(int index)
{
   double sum = 0;
   for (int i = lsmaPeriod; i > 0; i--)
      sum += (i - g_lengthValue)*Close[lsmaPeriod - i + index];
   return (sum*6/g_lsmaDeterminator);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего паттерна Вегас на указанном баре                    |
//+-------------------------------------------------------------------------------------+
bool IsUpVTPattern(int index, int total)
{
// - 1 - == Поиск восходящего паттерна HFE в пределах vtBarsAmount баров ================
   for (int i = vtMaxBarsAmount + index + 1; i > index + 5;// Поиск наиболее раннего HFE
        i--)
      if (IsUpHFEPattern(i, total))
         break;
   if (i == index + 5)                             // Паттерн HFE не найден, а, значит,..
      return(false);                               // ..не найден и паттерн Вегас
// - 1 - == Окончание блока =============================================================

// - 2 - == Поиск максимума, образовавшегося после паттерна HFE =========================
   for (int maxIndex = i; maxIndex > index + 3; maxIndex--)          
      if (mainCCI[maxIndex] > mainCCI[maxIndex+1] &&
          mainCCI[maxIndex] > mainCCI[maxIndex-1])
         break;
   if (maxIndex == index + 3 ||                    // Локальный максимум не найден
       mainCCI[maxIndex] >= 0)
      return(false);
// - 2 - == Окончание блока =============================================================

// - 3 - == Определение наличия закругления после максимума =============================
   bool down = true;                               // Начальное направление CCI - вниз
   double min = 0;                                 // Минимум области закругления
   for (int k = maxIndex - 1; k > index; k--)
   {
      if (mainCCI[k] > mainCCI[maxIndex])          // Преждевременный пробой максимума
         return(false);                            // Паттерн не обнаружен
      if (mainCCI[k] > mainCCI[k+1] && down)       // Разворот закругления - меняем..
         down = false;                             // ..направление
      if (!((mainCCI[k] < mainCCI[k+1] && down) || // Закругление нарушено - паттерн не..
            (mainCCI[k] > mainCCI[k+1] && !down))) // ..найден
         return(false);
      min = MathMin(min, mainCCI[k]);              // Отслеживаем минимум
   }
// - 3 - == Окончание блока =============================================================

// - 4 - == Определение пробоя максимума ================================================
   if (mainCCI[index] <= mainCCI[maxIndex])        // Пробой максимума не обнаружен
      return(false);                               // Паттерн не найден
// - 4 - == Окончание блока =============================================================

// - 5 - == Проверка взаимного расположения цены и LSMA =================================
   if (useLSMA)                                    // Если нужно проверять LSMA
   {
      double lsmaValue = GetLSMA(index);           // Получение значения LSMA
      if (Close[index] < lsmaValue)                // Для восходящего паттерна необходимо
         return(false);                            // ..нахождение цены выше линии LSMA
   }
// - 5 - == Окончание блока =============================================================

   ShowVT(maxIndex, index, mainCCI[maxIndex], min, SIGN_UP);
   return(true);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего паттерна Вегас на указанном баре                    |
//+-------------------------------------------------------------------------------------+
bool IsDnVTPattern(int index, int total)
{
// - 1 - == Поиск нисходящего паттерна HFE в пределах vtBarsAmount баров ================
   for (int i = vtMaxBarsAmount + index + 1; i > index + 5;// Поиск наиболее раннего HFE
        i--)
      if (IsDnHFEPattern(i, total))
         break;
   if (i == index + 5)                             // Паттерн HFE не найден, а, значит,..
      return(false);                               // ..не найден и паттерн Вегас
// - 1 - == Окончание блока =============================================================

// - 2 - == Поиск минимума, образовавшегося после паттерна HFE ==========================
   for (int minIndex = i; minIndex > index + 3; minIndex--)          
      if (mainCCI[minIndex] < mainCCI[minIndex+1] &&
          mainCCI[minIndex] < mainCCI[minIndex-1])
         break;
   if (minIndex == index + 3 ||                    // Локальный минимум не найден
       mainCCI[minIndex] <= 0)
      return(false);
// - 2 - == Окончание блока =============================================================

// - 3 - == Определение наличия закругления после минимума ==============================
   bool up = true;                                 // Начальное направление CCI - вверх
   double max = 0;                                 // Максимум области закругления
   for (int k = minIndex - 1; k > index; k--)
   {
      if (mainCCI[k] < mainCCI[minIndex])          // Преждевременный пробой минимума
         return(false);                            // Паттерн не обнаружен
      if (mainCCI[k] < mainCCI[k+1] && up)         // Разворот закругления - меняем..
         up = false;                               // ..направление
      if (!((mainCCI[k] > mainCCI[k+1] && up) ||   // Закругление нарушено - паттерн не..
            (mainCCI[k] < mainCCI[k+1] && !up)))   // ..найден
         return(false);
      max = MathMax(max, mainCCI[k]);              // Отслеживаем максимум
   }
// - 3 - == Окончание блока =============================================================

// - 4 - == Определение пробоя минимума =================================================
   if (mainCCI[index] >= mainCCI[minIndex])        // Пробой минимума не обнаружен
      return(false);                               // Паттерн не найден
// - 4 - == Окончание блока =============================================================

// - 5 - == Проверка взаимного расположения цены и LSMA =================================
   if (useLSMA)                                    // Если нужно проверять LSMA
   {
      double lsmaValue = GetLSMA(index);           // Получение значения LSMA
      if (Close[index] > lsmaValue)                // Для нисходящего паттерна необходимо
         return(false);                            // ..нахождение цены ниже линии LSMA
   }
// - 5 - == Окончание блока =============================================================

   ShowVT(minIndex, index, mainCCI[minIndex], max, SIGN_DN);
   return(true);
}
//+-------------------------------------------------------------------------------------+
//| Отображение паттерна Вегас                                                          |
//+-------------------------------------------------------------------------------------+
void ShowVT(int firstExtremumIndex, int index, double firstExtremumValue, 
            double secondExtremumValue, string sign)
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
         return;                                   // ..подокно, то уходим
// - 1 - == Определение характеристик объекта в зависимости от типа паттерна ============
   color clr = upVTcolor;                          // Цвет восходящего паттерна
   string description = "Up Vegas Pattern";        // Описание восходящего паттерна
   if (sign == SIGN_DN)                            // Если паттерн нисходящий
   {
      clr = dnVTcolor;                             // Цвет нисходящего паттерна
      description = "Down Vegas Pattern";          // Описание нисходящего паттерна
   }
// - 1 - == Окончание блока =============================================================
      
// - 2 - == Отображение прямоугольника паттерна =========================================
   string name = g_shortName + VT_STRING +         // Имя объекта паттерна
                 Time[index] + sign;           
   ShowRectangle(name, firstExtremumValue,         // Отображение объекта
                 Time[firstExtremumIndex], 
                 secondExtremumValue, Time[index], clr, description);
// - 2 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Поиск и отображение паттерна Вегас                                                  |
//+-------------------------------------------------------------------------------------+
void FindAndShowVT(int index, int total)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн Вегас ====================================================
   if (IsUpVTPattern(index, total))                // Обнаружение и отображение паттерна
   {
      PatternAlert(alertVTpattern, index,          // Звуковое оповещение о нахождении..
                   lastAlert, soundVTpattern);     // ..паттерна
      return;
   }
// - 1 - == Окончание блока =============================================================

// - 2 - == Нисходящий паттерн Вегас ====================================================
   if (IsDnVTPattern(index, total))                // Обнаружение и отображение паттерна
   {
      PatternAlert(alertVTpattern, index,          // Звуковое оповещение о нахождении..
                   lastAlert, soundVTpattern);     // ..паттерна
      return;
   }
// - 2 - == Окончание блока =============================================================

   DeleteObject(VT_STRING, index);                 // Удаление паттерна, если он был..
                                                   // ..ранее найден
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия локального экстремума главного CCI - пика от нуля               |
//+-------------------------------------------------------------------------------------+
bool IsLocalPeak(int index, int mul)
{
   if (mul*mainCCI[index] > 0 && 
       mul*mainCCI[index] > mul*mainCCI[index-1] && 
       mul*mainCCI[index] > mul*mainCCI[index+1])
      return(true);
   return(false);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия локального экстремума главного CCI - впадины к нулю             |
//+-------------------------------------------------------------------------------------+
bool IsLocalDip(int index, int mul)
{
   if (mul*mainCCI[index] < mul*mainCCI[index-1] &&// Все значения CCI с одной стороны..
       mul*mainCCI[index] < mul*mainCCI[index+1])  // ..от нулевой линии
      return(true);

   if (mul*mainCCI[index-1] > 0 &&                 // Значение экстремума на..
       mul*mainCCI[index+1] > 0 &&                 // ..противоположной стороне нулевой..
       mul*mainCCI[index] < 0)                     // ..линии от соседних значений CCI
      return(true);           

   return(false);
}
//+-------------------------------------------------------------------------------------+
//| Запись первой пары значений в те переменные, которые не содержат 0                  |
//+-------------------------------------------------------------------------------------+
void SaveIndexAndValue(double value, int index, double& val1, int& num1, double& val2, 
                       int& num2, double& val3, int& num3)
{
   if (num1 == 0)
   {
      num1 = index;
      val1 = value;
      return;
   }

   if (num2 == 0)
   {
      num2 = index;
      val2 = value;
      return;
   }
   
   if (num3 == 0)
   {
      num3 = index;
      val3 = value;
   }
}                       
//+-------------------------------------------------------------------------------------+
//| Нахождение локального экстремума - впадины на указанном участке                     |
//+-------------------------------------------------------------------------------------+
bool IsDipInThisArea(int startIndex, int endIndex, int mul, double& value, int& num)
{
   for (int i = startIndex; i > endIndex; i--)
      if (IsLocalDip(i, mul))
      {
         num = i;
         value = mainCCI[i];
         return(true);
      }      
   return(false);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего паттерна Призрак на указанном баре                  |
//+-------------------------------------------------------------------------------------+
bool IsUpGTPattern(int index, int total)
{
// - 1 - == Поиск трех последних локальных минимумов главного CCI =======================
   double minVal1 = 0, minVal2 = 0, minVal3 = 0;   // Значения минимумов
   int minNum1 = 0, minNum2 = 0, minNum3 = 0;      // Индексы баров минимумов
   for (int i = index+1; i < total && minNum3 == 0; i++)// Поиск справа налево по графику
   {
      if (mainCCI[i] > 0 && mainCCI[i-1] > 0)      // Найдено два положительных значения
         return(false);                            // ..CCI подряд - паттерн не найден
      if (IsLocalPeak(i, -1))                      // Минимум найден
         SaveIndexAndValue(mainCCI[i], i, minVal1, // Запись значений в соответствующие..
                           minNum1, minVal2, minNum2, minVal3, minNum3);// ..переменные
   }                           
   if (i == total || minNum3 == 0)                 // Не удалось найти три локальных..
      return(false);                               // ..минимума. Паттерн не обнаружен
// - 1 - == Окончание блока =============================================================

// - 2 - == Образуют ли найденные минимумы голову и плечи? ==============================
   if (minVal2 >= minVal1 || minVal2 >= minVal3)
      return(false);
// - 2 - == Окончание блока =============================================================

// - 3 - == Поиск двух локальных максимумов - по одному с каждой из сторон от головы ====
   double maxVal1 = 0, maxVal2 = 0;
   int maxNum1 = 0, maxNum2 = 0;
   if (!IsDipInThisArea(minNum3 - 1, minNum2, -1,  // Нет локальной впадины между левым..
                        maxVal2, maxNum2))         // ..плечом и головой
      return(false);
   if (!IsDipInThisArea(minNum2 - 1, minNum1, -1,  // Нет локальной впадины между правым
                        maxVal1, maxNum1))         // ..плечом и головой
      return(false);
// - 3 - == Окончание блока =============================================================

// - 4 - == Регистрация пробоя линии тренда =============================================
   double bKoef;
   double kKoef = GetKAndBFromCoords(maxNum1,      // Расчет коэффициентов уравнения..
                                     maxVal1,      // ..прямой - линии тренда
                                     maxNum2, maxVal2, bKoef);
   double currTLB = kKoef*index + bKoef;           // Точка линии тренда бара index

   if (mainCCI[index] <= currTLB)                  // Главный CCI не пробил линию тренда
      return(false);                               // Нет паттерна
// - 4 - == Окончание блока =============================================================

// - 5 - == Отображение паттерна ========================================================
   if (index > 0)                                  // Исключение дублирования паттернов
      DeleteGT(index+1);
   ShowGT(minNum3, minVal3-20, minNum2, minVal2-20,// Отображение паттерна
          minNum1, minVal1-20, Time[maxNum2], maxVal2, Time[index], currTLB, SIGN_UP);
   return(true);
// - 5 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего паттерна Призрак на указанном баре                  |
//+-------------------------------------------------------------------------------------+
bool IsDnGTPattern(int index, int total)
{
// - 1 - == Поиск трех последних локальных максимумов главного CCI ======================
   double maxVal1 = 0, maxVal2 = 0, maxVal3 = 0;   // Значения максимумов
   int maxNum1 = 0, maxNum2 = 0, maxNum3 = 0;      // Индексы баров максимумов
   for (int i = index+1; i < total && maxNum3 == 0; i++)// Поиск справа налево по графику
   {
      if (mainCCI[i] < 0 && mainCCI[i-1] < 0)      // Найдено два отрицательных значения
         return(false);                            // ..CCI подряд - паттерн не найден
      if (IsLocalPeak(i, 1))                       // Максимум найден
         SaveIndexAndValue(mainCCI[i], i, maxVal1, // Запись значений в соответствующие..
                           maxNum1, maxVal2, maxNum2, maxVal3, maxNum3);// ..переменные
   }
   if (i == total || maxNum3 == 0)                 // Не удалось найти три локальных..
      return(false);                               // ..мaксимума. Паттерн не обнаружен
// - 1 - == Окончание блока =============================================================

// - 2 - == Образуют ли найденные максимумы голову и плечи? =============================
   if (maxVal2 <= maxVal1 || maxVal2 <= maxVal3)
      return(false);
// - 2 - == Окончание блока =============================================================

// - 3 - == Поиск двух локальных минимумов - по одному с каждой из сторон от головы ====
   double minVal1 = 0, minVal2 = 0;
   int minNum1 = 0, minNum2 = 0;
   if (!IsDipInThisArea(maxNum3 - 1, maxNum2, 1,   // Нет локальной впадины между левым..
                        minVal2, minNum2))         // ..плечом и головой
      return(false);
   if (!IsDipInThisArea(maxNum2 - 1, maxNum1, 1,   // Нет локальной впадины между правым
                        minVal1, minNum1))         // ..плечом и головой
      return(false);
// - 3 - == Окончание блока =============================================================

// - 4 - == Регистрация пробоя линии тренда =============================================
   double bKoef;
   double kKoef = GetKAndBFromCoords(minNum1,      // Расчет коэффициентов уравнения..
                                     minVal1,      // ..прямой - линии тренда
                                     minNum2, minVal2, bKoef);
   double currTLB = kKoef*index + bKoef;           // Точка линии тренда бара index

   if (mainCCI[index] >= currTLB)                  // Главный CCI не пробил линию тренда
      return(false);                               // Нет паттерна
// - 4 - == Окончание блока =============================================================

// - 5 - == Отображение паттерна ========================================================
   if (index > 0)                                  // Исключение дублирования паттернов
      DeleteGT(index+1);
   ShowGT(maxNum3, maxVal3+20, maxNum2, maxVal2+20,// Отображение паттерна
          maxNum1, maxVal1+20, Time[minNum2], minVal2, Time[index], currTLB, SIGN_DN);
   return(true);
// - 5 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Отображение паттерна Призрак                                                        |
//+-------------------------------------------------------------------------------------+
void ShowGT(int leftArmIndex, double leftArmValue, int headIndex, double headValue, 
            int rightArmIndex, double rightArmValue, 
            datetime lineLeftTime, double lineLeftValue, datetime lineRightTime, 
            double lineRightValue, string sign)
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
      return;                                      // ..подокно, то уходим
// - 1 - == Определение характеристик объекта в зависимости от типа паттерна ============
   color clr = upGTcolor;                          // Цвет восходящего паттерна
   string description = "Up Ghost Pattern";        // Описание восходящего паттерна
   if (sign == SIGN_DN)                            // Если паттерн нисходящий
   {
      clr = dnGTcolor;                             // Цвет нисходящего паттерна
      description = "Down Ghost Pattern";          // Описание нисходящего паттерна
   }
// - 1 - == Окончание блока =============================================================
      
// - 2 - == Отображение линии тренда ====================================================
   string name = g_shortName + GT_STRING +         // Имя объекта паттерна
                 FIRST_PART + lineRightTime + sign;           
   ShowTrendLine(name, lineLeftValue,              // Отображение объекта
                 lineLeftTime, lineRightValue, lineRightTime, clr, description);
// - 2 - == Окончание блока =============================================================

// - 3 - == Подчерк левого плеча ========================================================
   name = g_shortName + GT_STRING + SECOND_PART +  // Имя объекта паттерна
          lineRightTime + sign;           
   ShowTrendLine(name, leftArmValue,               // Отображение объекта
                 Time[leftArmIndex+1], 
                 leftArmValue, Time[leftArmIndex-1], clr, description);
// - 3 - == Окончание блока =============================================================

// - 4 - == Подчерк головы ==============================================================
   name = g_shortName + GT_STRING + THIRD_PART +   // Имя объекта паттерна
          lineRightTime + sign;           
   ShowTrendLine(name, headValue,                  // Отображение объекта
                 Time[headIndex+1], 
                 headValue, Time[headIndex-1], clr, description);
// - 4 - == Окончание блока =============================================================

// - 5 - == Подчерк правого плеча =======================================================
   name = g_shortName + GT_STRING + FOURTH_PART +  // Имя объекта паттерна
          lineRightTime + sign;           
   ShowTrendLine(name, rightArmValue,              // Отображение объекта
                 Time[rightArmIndex+1], 
                 rightArmValue, Time[rightArmIndex-1], clr, description);
// - 5 - == Окончание блока =============================================================

}           
//+-------------------------------------------------------------------------------------+
//| Удаление паттерна Призрак                                                           |
//+-------------------------------------------------------------------------------------+
void DeleteGT(int index)
{
   DeleteObject(GT_STRING + FIRST_PART, index);
   DeleteObject(GT_STRING + SECOND_PART, index);
   DeleteObject(GT_STRING + THIRD_PART, index);
   DeleteObject(GT_STRING + FOURTH_PART, index);
}
//+-------------------------------------------------------------------------------------+
//| Поиск и отображение паттерна Призрак                                                |
//+-------------------------------------------------------------------------------------+
void FindAndShowGT(int index, int total)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн Призрак ==================================================
   if (IsUpGTPattern(index, total))                // Обнаружение и отображение паттерна
   {
      PatternAlert(alertGTpattern, index,          // Звуковое оповещение о нахождении..
                   lastAlert, soundGTpattern);     // ..паттерна
      return;
   }
// - 1 - == Окончание блока =============================================================

// - 2 - == Нисходящий паттерн Призрак ==================================================
   if (IsDnGTPattern(index, total))                // Обнаружение и отображение паттерна
   {
      PatternAlert(alertGTpattern, index,          // Звуковое оповещение о нахождении..
                   lastAlert, soundGTpattern);     // ..паттерна
      return;
   }
// - 2 - == Окончание блока =============================================================

   DeleteGT(index);                                // Удаление паттерна, если он был..
                                                   // ..ранее найден
}
//+-------------------------------------------------------------------------------------+
//| Отображение графического объекта стрелки                                            |
//+-------------------------------------------------------------------------------------+
void ShowArrow(string name, datetime time, double price, int code, color clr,
               string description)
{
   if (ObjectFind(name) < 0)                       // Если объект не существует
   {
      ObjectCreate(name, OBJ_ARROW,                // Создадим его
                   g_windowIndex, time, price); 
      ObjectSet(name, OBJPROP_ARROWCODE, code);    // Объект в виде указанного значка                   
      ObjectSet(name, OBJPROP_COLOR, clr);         // Установим цвет,..
      ObjectSet(name, OBJPROP_BACK, false);        // ..положение относительно графики..
      ObjectSetText(name, description);            // ..и подпись
      return;
   }
   
   ObjectMove(name, 0, time, price);               // Перемещение существующего объекта
}
//+-------------------------------------------------------------------------------------+
//| Отображение паттерна Разворотная дивергенция                                        |
//+-------------------------------------------------------------------------------------+
void ShowRD(int leftIndex, double leftValue, int rightIndex, double rightValue, 
            string sign)
{
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
      return;                                      // ..подокно, то уходим
// - 1 - == Определение характеристик объекта в зависимости от типа паттерна ============
   color clr = upRDcolor;                          // Цвет восходящего паттерна
   string description = "Up Rev Diver Pattern";    // Описание восходящего паттерна
   if (sign == SIGN_DN)                            // Если паттерн нисходящий
   {
      clr = dnRDcolor;                             // Цвет нисходящего паттерна
      description = "Down Rev Diver Pattern";      // Описание нисходящего паттерна
   }
// - 1 - == Окончание блока =============================================================
      
// - 2 - == Отображение левой (по графику) впадины ======================================
   string name = g_shortName + RD_STRING +         // Имя объекта паттерна
                 FIRST_PART + Time[rightIndex-1] + sign;           
   ShowArrow(name, Time[leftIndex], leftValue,     // Отображение объекта
             RD_ARROW_CODE, clr, description);
// - 2 - == Окончание блока =============================================================

// - 3 - == Отображение правой (по графику) впадины =====================================
   name = g_shortName + RD_STRING +                // Имя объекта паттерна
          SECOND_PART + Time[rightIndex-1] + sign;           
   ShowArrow(name, Time[rightIndex], rightValue,   // Отображение объекта
             RD_ARROW_CODE, clr, description);
// - 3 - == Окончание блока =============================================================
}           
//+-------------------------------------------------------------------------------------+
//| Удаление паттерна Разворотная дивергенция                                           |
//+-------------------------------------------------------------------------------------+
void DeleteRD(int index)
{
   DeleteObject(RD_STRING + FIRST_PART, index);
   DeleteObject(RD_STRING + SECOND_PART, index);
}
//+-------------------------------------------------------------------------------------+
//| Поиск двух последних впадин (пиков в сторону нулевой линии)                         |
//+-------------------------------------------------------------------------------------+
bool FindTwoDips(double& updn_TrendBars[], int beginIndex, int endIndex, 
                 double& leftVal, int& leftNum, double& rightVal, int& rightNum, int mul)
{
   for (int i = beginIndex; i < endIndex; i++)     // Поиск справа налево по графику
   {
      if (updn_TrendBars[i] < trendBars &&         // Поиск заканчивается, если..
          mul*mainCCI[i] <= 0)                     // ..зафиксирована смена тренда
         return(false);
         
      if (IsLocalDip(i, mul))                      // Обнаружена впадина
      {
         if (rightNum == 0)                        // Если это первая впадина, то запишем
         {                                         // ..данные и приступим к поиску..
            rightNum = i;                          // ..второй впадины
            rightVal = mainCCI[i];
            continue;
         }
         leftNum = i;                              // Обнаружена вторая впадина. В любом
         leftVal = mainCCI[i];                     // ..случае поиск окончен
         if (mul*leftVal <= mul*rightVal)          // Впадины не сходятся к нулю -..
            return(false);                         // ..паттерн не создан
         return(true);                             // Впадины сходятся - есть паттерн
      }
   }
   return(false);                                  // Не удалось найти две впадины - нет
                                                   // ..паттерна
}                 
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего паттерна Разворотная дивергенция на указанном баре  |
//+-------------------------------------------------------------------------------------+
bool IsUpRDPattern(int index, int total)
{
// - 1 - == Поиск двух нисходящих впадин ================================================
   double leftVal, rightVal;                       // Значения CCI на впадинах
   int leftNum = 0, rightNum = 0;                  // Индексы баров впадин
   if (!FindTwoDips(upTrendBars, index+1, total,   // Если две впадины не найдены, то..
                   leftVal, leftNum, rightVal, rightNum, GET_MAX))// ..паттерн не..
      return(false);                               // ..найден
// - 1 - == Окончание блока =============================================================
   
// - 2 - == Отображение паттерна ========================================================
   ShowRD(leftNum, leftVal, rightNum, rightVal, SIGN_UP);
   return(true);
// - 2 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего паттерна Разворотная дивергенция на указанном баре  |
//+-------------------------------------------------------------------------------------+
bool IsDnRDPattern(int index, int total)
{
// - 1 - == Поиск двух восходящих впадин ================================================
   double leftVal, rightVal;                       // Значения CCI на впадинах
   int leftNum = 0, rightNum = 0;                  // Индексы баров впадин
   if (!FindTwoDips(dnTrendBars, index+1, total,   // Если две впадины не найдены, то..
                   leftVal, leftNum, rightVal, rightNum, GET_MIN))// ..паттерн не..
      return(false);                               // ..найден
// - 1 - == Окончание блока =============================================================
   
// - 2 - == Отображение паттерна ========================================================
   ShowRD(leftNum, leftVal, rightNum, rightVal, SIGN_DN);
   return(true);
// - 2 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Поиск и отображение паттерна Разворотная дивергенция                                |
//+-------------------------------------------------------------------------------------+
void FindAndShowRevDiver(int index, int total)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн Разворотная дивергенция ==================================
   if (IsUpTrend(index))
      if (IsUpRDPattern(index, total))             // Обнаружение и отображение паттерна
      {
         PatternAlert(alertRDpattern, index,       // Звуковое оповещение о нахождении..
                      lastAlert, soundRDpattern);  // ..паттерна
         return;
      }
// - 1 - == Окончание блока =============================================================

// - 2 - == Нисходящий паттерн Разворотная дивергенция ==================================
   if (IsDnTrend(index))
      if (IsDnRDPattern(index, total))             // Обнаружение и отображение паттерна
      {
         PatternAlert(alertRDpattern, index,       // Звуковое оповещение о нахождении..
                      lastAlert, soundRDpattern);  // ..паттерна
         return;
      }
// - 2 - == Окончание блока =============================================================

   DeleteRD(index);                                // Удаление паттерна, если он был..
                                                   // ..ранее найден
}
//+-------------------------------------------------------------------------------------+
//| Custom indicator iteration function                                                 |
//+-------------------------------------------------------------------------------------+
int start()
{
// - 1 - == Организационные действия ====================================================
   if (g_windowIndex < 0)                          // Если индикатор еще не нашел свое..
      g_windowIndex = WindowFind(g_shortName);     // ..собственное подокно, найдем его
      
   int total;                                      // Индекс наиболее раннего в истории..
                                                   // ..бара
   int limit = GetRecalcIndex(total);              // Определим первый расчетный бар
// - 1 - == Окончание блока =============================================================
  
// - 2 - == Расчет значений буферов и отображение паттернов =============================
   for (int i = limit; i >= 0; i--)                // Обработаем все новые бары
   {
      ShowData(i);                                 // Отображение данных буферов
      
      if (showZLRpattern)                          // Если разрешено обрабатывать..
         FindAndShowZLR(i);                        // ..паттерн ZLR, то отобразим его

      if (showTLBpattern)                          // Если разрешено обрабатывать..
         FindAndShowTLB(i, total);                 // ..паттерн TLB, то отобразим его

      if (showSHAMUpattern)                        // Если разрешено обрабатывать..
         FindAndShowSHAMU(i, total);               // ..паттерн Шаму, то отобразим его

      if (showHFEpattern)                          // Если разрешено обрабатывать..
         FindAndShowHFE(i, total);                 // ..паттерн "Экстремальный крюк", то
                                                   // ..отобразим его

      if (showVTpattern)                           // Если разрешено обрабатывать..
         FindAndShowVT(i, total);                  // ..паттерн Вегас, то отобразим его

      if (showGTpattern)                           // Если разрешено обрабатывать..
         FindAndShowGT(i, total);                  // ..паттерн Призрак, то отобразим его

      if (showRDpattern)                           // Если разрешено обрабатывать..
         FindAndShowRevDiver(i, total);            // ..паттерн Разворотная дивергенция,
                                                   // ..то отобразим его
   }
// - 2 - == Окончание блока =============================================================
      
   BarRemainder();                                 // Время до закрытия текущей свечи
   WindowRedraw();
   
   return(0);
}

