Индикатор NextBarType

 

Заветной мечтой любого трейдера является умение точного предсказания будущего изменения цены. Правда, такое высказывание изначально обречено на неправильное понимание слушателем, которому оно представлено. И действительно, если подобную формулировку желания трейдера передать Золотой рыбке, то в ответ можем получить что угодно. К примеру, в качестве осуществления желания может быть выдана распечатка прогноза изменения цен услуг рикши в Калькутте на декабрь 2100-го года. В этом случае незадачливому трейдеру остается лишь надеяться на то, что его внуки все же дождутся включения такого экзотического финансового инструмента в перечень доступных для торговли символов у какого-нибудь брокера.

Получается, что диапазон предсказания цен мы можем значительно уменьшить, ограничившись продолжительностью своей жизни. Но и в этом случае нам нужен прогноз вовсе не на то, что будет лет через 10. Мало ли чем мы к тому моменту будем заниматься? Может, уже придется перышки на крыльях ангелов пересчитывать... Конечно же, нам просто необходимо знать ближайшую историю развития цен! "Ближайшая" - это здесь и сейчас, буквально следующая свеча, мы не можем ждать. Такое желание уже нельзя отнести к категории неисполнимых, для этого тратить энергию на вызов Золотой рыбки не нужно. Достаточно обратиться к старому знакомому - техническому анализу, в котором далеко не последнюю роль играет свечной анализ.

Как известно, свечной анализ базируется на поиске определенных свечных комбинаций (далее - паттернов или фигур), перечень которых постоянно расширяется. Многочисленные исследования закономерностей движения цены вслед за появлением различных паттернов привели к нерадостному заключению - большинство распространенных фигур свечного анализа являются бесполезными на рынке Форекс. Выходит, что необходимо искать новые фигуры, которые будут полезнее классических. С другой стороны, нет никакой гарантии того, что новые фигуры сохранят свои свойства в будущем. Чтобы они не накапливались в нашей базе данных, нужно будет отслеживать потерю эффективности каждого паттерна и исключать его из рассмотрения. Как-то сложно и ненадежно все получается. 

Более интересным решением проблемы поиска "рабочих" паттернов является рассмотрение ближайшего сформированного рынком паттерна и поиска в истории похожих на него фигур. Каждая историческая фигура содержит свечу, которая сформировалась сразу за появлением паттерна. Рассмотрев совокупность таких свечей, можно сделать вывод о наличии или об отсутствии закономерности, связанной с конкретным паттерном. В результате, у нас не будет необходимости запоминания каких-либо видов паттернов. Все исследования будут производиться только для текущей сформированной фигуры, которую назовем эталонной. С этой фигурой станем сравнивать весь имеющийся набор истории. При нахождении похожего на эталон паттерна остается только запомнить тип свечи, следующей сразу после паттерна. Такую свечу будем называть исходом паттерна. Второй похожий паттерн - вторая свеча и т.д., пока не дойдем до конца истории, сформировав некоторую статистику (см. рис. 1). 

 

Рис. 1. Нахождение паттернов в истории, используя эталонный паттерн.

Будем считать, что сейчас тот момент времени, который соответствует времени открытия свечи, следующей сразу после паттерна, обозначенного на рис. 1, как эталонный. Нам необходимо собрать статистику по сходным паттернам истории, определив наиболее часто встречающееся развитие дальнейших событий, т.е. тип следующей свечи: бычья или медвежья (додж нас не интересует). Три приведенных на рисунке исторических паттерна указывают на то, что чаще всего после эталонного паттерна появляется медвежья свеча. Видя дальнейшее развитие событий, можем констатировать, что так оно и есть.

Все описанное выше является идеальной ситуацией. В реальной жизни никогда нельзя быть в чем-то уверенным на 100%. К оценке будущего развития событий необходимо подходить с позиций теории вероятности. Расчет вероятности появления того или иного типа свечи после эталонного паттерна мы можем произвести, используя данные, полученные от исторических паттернов. Среди исторических паттернов (при условии наличия не одного-двух паттернов, а нескольких десятков паттернов) всегда будут представлены различные типы исходов: бычьи и медвежьи свечи, а также доджи. Отношение одного типа свечей к общему количеству исходов и является вероятностью появления соответствующего типа свечи вслед за эталонным паттерном. Чем выше вероятность одного из исходов, тем более вероятно его появление в данной ситуации.

 

Автоматизация поиска паттернов

Собирать статистику по графику является утомительным и неблагодарным делом, т.к. приводит к существенным ошибкам. Наиболее современным методом сбора статистики является программная обработка данных. В нашем случае такой программой является индикатор, который может рассчитать вероятность какого-либо исхода не только для текущей ситуации, но и для имеющейся истории, что позволит бегло оценить эффективность выбранной тактики прогнозирования.

Для программирования индикатора нам потребуются формальные критерии, на основании которых будет выноситься заключение о схожести или различии сравниваемых свечных фигур. Таким образом, нужно разработать некий универсальный механизм соотношения свечей одной фигуры, которое является признаком определенного вида паттерна. Чтобы не усложнять программу, предлагается три критерия идентификации паттерна: тип свечи, соотношение максимумов соседних свечей и соотношение минимумов соседних свечей (см. рис. 2).

 Рис. 2. Идентификация паттерна по трем критерия.

Программное описание эталонного паттерна осуществляется при помощи трех массивов: high, low и candle_type. Размерность каждого массива равна количеству свечей, составляющих паттерн. Эта величина может быть изменена пользователем при помощи настроечного параметра AnalysisBars. Первый элемент (индекс 0) массива high описывает соотношение максимума первой (индекс 0) и второй (индекс 1) свечей паттерна. Если максимум первой свечи больше, чем максимум второй, то значение high[0] будет 1. При равенстве максимумов записывается значение 0, а если максимум первой свечи меньше максимума второй свечи, то в элемент high[0] записывается значение -1. Аналогичным образом производится запись значений в другие элементы массива high, а также в элементы массива low. Исключение в этой технологии составляет массив candle_type, который описывает тип одной свечи, а не соотношение соседних. Бычьей свече (цена закрытия выше цены открытия) соответствует значение 1, медвежьей (цена закрытия ниже цены открытия) -1, а доджу (равенство цен закрытия и открытия) - 0. Описанный алгоритм реализован в функции CreateEtalonPattern индикатора NextBarType:

 
//+-------------------------------------------------------------------------------------+
//| Формирование эталонного паттерна                                                    |
//+-------------------------------------------------------------------------------------+
void CreateEtalonPattern(int i)
{
   int last_index = i + AnalysisBars;              // Расчет индекса первого бара, не..
                                                   // ..входящего в паттерн
   int cnt = 0;                                    // Счетчик индексов эталонного..
                                                   // ..паттерна
   for (int k = i; k < last_index; k++)            // Пройдем по всем барам паттерна
   {
      high[cnt] = DoubleCompare(High[k], High[k+1], Point);// Соотношение максимумов
      low[cnt] = DoubleCompare(Low[k], Low[k+1], Point);// Соотношение минимумов
      candle_type[cnt] = DoubleCompare(Close[k], Open[k], Point);// Типы свечей
      cnt++;
   }
}

 

Единственным аргументом функции является индекс бара, соответствующий началу эталонного паттерна. Это первый бар паттерна, описание которого будет сохранено в элементах массивов high, low и candle_type с индексом 0. Первый бар, который находится за пределами паттерна, имеет индекс i + AnalysisBars. Это значение сохраняется в переменной last_index. Достижение значения last_index означает окончание цикла for, в котором заполняются все перечисленные массивы.

Заполнение массивов происходит при помощи вызова функции DoubleCompare, результатом которой является всего три значения: 1, -1 и 0. Положительное значение функция возвращает, если первый аргумент (High[k], Low[k] или Close[k]) больше второго (High[k+1], Low[k+1] или Open[k]) на величину Point и более. Отрицательное значение возвращается, если второй аргумент больше первого, а значение 0 возвращается, если аргументы равны. Рассмотрим, каким образом производится сравнение чисел двойной точности (double):

 
//+-------------------------------------------------------------------------------------+
//| Сравнение двух чисел типа double с заданной точностью                               |
//+-------------------------------------------------------------------------------------+
int DoubleCompare(double d1, double d2, double precision)
{
   double temp = d1 - d2;                          // Получим разность первого и второго
                                                   // ..числа
   if (temp >= precision)                          // Если первое число больше второго,..
      return(1);                                   // ..то вернем 1
   else                                            // Если первое число меньше второго,
      if (temp <= -precision)                      // ..то вернем -1
         return(-1);
   return(0);                                      // Во всех остальных случаях считаем,
                                                   // ..что числа равны - вернем 0
}

Проблема сравнения вещественных чисел является наиболее распространенной проблемой среди людей, взявшихся за изучение программирования. Возникла проблема вследствие того, что человек использует десятичную систему исчисления, а компьютер - двоичную. В процессе преобразования данных происходит потеря точности, которой зачастую бывает достаточно, чтобы признать результаты выражений 1/6.0 и 8/96.0 разными величинами. Поэтому прямое сравнение двух чисел при помощи знаков "<", ">" и "==" неуместно. Для получения правильного результата сравнения необходимо сравнивать числа в пределах требуемой точности. Например, если мы имеем дело с котировками, точность которых 0.0001, то это и будет требуемая точность сравнения. Если разность сравниваемых чисел в абсолютном значении меньше 0.0001, то можно считать, что они равны. Если разность чисел превышает 0.0001, то числа не равны.

На этом принципе и построен алгоритм функции DoubleCompare. Первое действие функции - вычисление разности первого и второго аргументов. Затем результат, записанный в переменную temp, сравнивается со значением точности, которое передано в качестве третьего аргумента функции (precision). Если значение temp больше или равно значению precision, то первое число больше второго. Если значение temp меньше, чем отрицательное значение точности, то первое число меньше второго. Во всех остальных случаях числа d1 и d2 считаются равными.

После формирования эталонного паттерна нужно найти аналогичные паттерны в истории. Для этого потребуется функция, которая определит идентичность двух паттернов. В коде индикатора такую задачу решает функция CompareWithEtalon:

 
//+-------------------------------------------------------------------------------------+
//| Сравнение текущего паттерна с эталонным                                             |
//+-------------------------------------------------------------------------------------+
bool CompareWithEtalon(int i)
{
   int last_index = i + AnalysisBars;              // Расчет индекса первого бара, не..
                                                   // ..входящего в паттерн
   int cnt = 0;                                    // Счетчик индексов эталонного..
                                                   // ..паттерна
   for (int k = i; k < last_index; k++)            // Пройдем по всем барам паттерна   
   {
      if (DoubleCompare(High[k], High[k+1], Point) // Если соотношение максимумов..
                        != high[cnt])              // ..рассматриваемого паттерна не..
         return(false);                            // ..похоже на эталонный паттерн, то..
                                                   // ..сразу бракуем найденный паттерн
      if (DoubleCompare(Low[k], Low[k+1], Point)   // То же самое при рассмотрении..
                        != low[cnt])               // ..соотношения минимумов
         return(false);
      if (DoubleCompare(Close[k], Open[k], Point)  // Типы свечей найденного паттерна..
                        != candle_type[cnt])       // ..также должны соответствовать..
         return(false);                            // ..типам свечей эталонного паттерна
      cnt++;
   }
   return(true);
}

Функция получает индекс бара, соответствующий началу исторического паттерна, идентичность которого эталонному паттерну требуется подтвердить или опровергнуть. Первые три строки функции ничем не отличаются от первых трех строк функции CreateEtalonPattern. Отличия заключаются в теле цикла перебора баров паттерна. Результат функции DoubleCompare сравнивается с соответствующим элементом массивов high, low и candle_type, которые описывают эталонный паттерн. Для ускорения работы функции первое же несоответствие приводит к мгновенному прерыванию цикла и выходу из функции с результатом false, что означает несоответствие паттернов. Только естественное окончание цикла приводит к возврату функцией положительного результата - true, который означает идентичность паттернов в пределах правил, принятых в начале статьи.

Функцию CompareWithEtalon в своей работе использует другая функция FindDublicateEtalonPattern:

 
//+-------------------------------------------------------------------------------------+
//| Нахождение в истории паттернов, похожих на эталонный                                |
//+-------------------------------------------------------------------------------------+
void FindDublicateEtalonPattern(int i)
{
   double last_index = Bars - AnalysisBars - 1;    // Расчет индекса бара, на котором..
                                                   // ..начинается первый в имеющейся..
                                                   // ..истории паттерн
   buy = 0; sell = 0; dodg = 0;                    // Инициализируем количество исходов..
                                                   // ..каждого типа нулем
   for (int k = i; k < last_index; k++)            // Пройдем по истории от указанного..
                                                   // ..бара до начала истории
      if (CompareWithEtalon(k))                    // Если найденный паттерн..
      {                                            // ..соответствует эталонному, то..
         int temp = DoubleCompare(Close[k-1],      // ..определим тип свечи, следующей..
                                  Open[k-1], Spread);// ..после паттерна
         if (temp == 1)                            // От типа свечи будет зависеть,..
            buy++;                                 // ..количество исходов какого типа..
         else                                      // ..будет увеличено
            if (temp == -1)
               sell++;
            else
               dodg++;      
      }
}

Эту функцию можно назвать статистической, т.к. ее задачей является сбор данных о частоте появления свечей различного типа после появления паттерна. Аргумент функции i указывает индекс бара, с которого необходимо начать сбор статистики. Поиск исторических паттернов продолжается до последнего бара в истории, на котором возможно формирование паттерна. Если паттерн состоит из пяти свечей, то это будет пятый бар от конца истории.

Переменные buy, sell и dodg - глобальные переменные программы. Каждая из них соответствует соответствующему исходу паттерна: buy - бычьей свече, sell - медвежьей, а dodg - доджу. В начале поиска все переменные принимают нулевое значение. 

В теле цикла каждый раз вызывается функция CompareWithEtalon, которой передается текущее значение переменной цикла k. Именно от бара с индексом k вглубь истории на AnalysisBars растягивается потенциальный исторический паттерн. Если функция возвращает результат false (исторический паттерн не соответствует эталонному), то происходит переход к следующему бару. При нахождении паттерна, идентичного эталонному, переменной temp присваивается тип свечи, находящейся справа от исторического паттерна. Причем тип свечи будет считаться доджем в том случае, если ее тело не больше спрэда, указанного пользователем. Например, если спрэд 2 пункта, то нас не интересуют свечи с высотой тела 2 и менее пунктов, т.к. при открытии позиции мы не сможем получить прибыль, не перекрыв спрэд. Для указания величины спрэда пользователь может изменять значение настроечного параметра индикатора SpreadSize. К указанному значению индикатор добавляет 1, приводит к точности текущей котировки и записывает в глобальную переменную Spread.

В зависимости от значения переменной temp происходит увеличение соответствующей статистической переменной. Их значения анализируются во втором блоке функции OnCalculate индикатора:

 
// - 2 - == Расчет сигналов на истории ==================================================
   for (int i = limit; i > 0; i--)                 // По всей обновленной истории
   {
      CreateEtalonPattern(i);                      // Формирование эталонного паттерна
      FindDublicateEtalonPattern(i+AnalysisBars);  // Нахождение паттернов, похожих на..
                                                   // ..эталонный
      if (buy + sell + dodg >= MinHistory)         // Если найденое количество паттернов
      {                                            // ..не меньше указанного количества
         if (buy > sell && buy > dodg)             // Наибольший вес у бычьих исходов
         {
            double total = buy + sell + dodg;      // Общее количество паттернов
            ShowArrow(Time[i-1],                   // Отобразим стрелку вверх, в подписи 
                      Low[i-1] - iATR(NULL, 0, 14, i), // ..которой указано распределение
                      true,                        // ..исходов каждого типа в процентах
                      "Бычьих "+                   // ..от общего количества
                      DoubleToStr(buy/total*100, 1)+
                      "%, Медвежьих "+
                      DoubleToStr(sell/total*100, 1)+
                      "%, Доджей "+
                      DoubleToStr(dodg/total*100, 1)+
                      "% из "+DoubleToStr(total, 0)+" паттернов");
         }   
         if (sell > buy && sell > dodg)             // Наибольший вес у медвежьих исходов
         {
            total = buy + sell + dodg;             // Общее количество паттернов
            ShowArrow(Time[i-1],                   // Отобразим стрелку вниз, в подписи..
                      High[i-1] + iATR(NULL, 0, 14, i)/2,// ..которой указано..
                      false,                       // ..распределение исходов каждого..
                      "Бычьих "+                   // ..типа в процентах от общего.. 
                      DoubleToStr(buy/total*100, 1)+// ..количества
                      "%, Медвежьих "+
                      DoubleToStr(sell/total*100, 1)+
                      "%, Доджей "+DoubleToStr(dodg/total*100, 1)+
                      "% из "+DoubleToStr(total, 0)+" паттернов");
         }   
      }   
   }
// - 2 - == Окончание блока =============================================================

Цикл блока перебирает все измененные после последнего вызова индикатора свечи, для каждой из которых формируется свой эталонный паттерн со своей статистикой появления. С этой целью и вызываются функции CreateEtalonPattern и FindDublicateEtalonPattern. После их завершения формируются значения переменных buy, sell и dodg. С этого момента можно приступать к анализу полученных данных.

Первый критерий, который принимается во внимание перед проведением анализа, это достаточность имеющихся данных. Например, на основании одного-двух случаев нельзя говорить о какой-либо закономерности. В статистике для описания количества данных существует специальный термин - глубина выборки. Чем больше глубина выборки, тем достовернее выявленная закономерность. Каких-либо общепринятых норм глубины выборки не существует, для каждого случая необходимо применять индивидуальный подход. Поэтому индикатор NextBarType располагает специальным настроечным параметром MinHistory, при помощи которого пользователь может указать минимально необходимую глубину выборки. В итоге индикатор проводит анализ, если количество найденных исторических паттернов (сумма buy, sell и dodg) больше или равна значению MinHistory.

На данном этапе развития стратегии анализ достаточно прост - выявить из трех полученных величин наибольшую. Если наибольшей является величина buy, то под следующим баром отображается значок "стрелка вверх", в подписи которой указываются все полученные данные: часть бычьих исходов, часть медвежьих исходов и часть исходов додж в процентах от общего количества паттернов. Подпись стрелки, чтобы не загромождать ценовой график, отображается при наведении курсора мыши на соответствующую стрелку. Если наибольшей величиной является значение sell, то над следующим баром отображается значок "стрелка вниз". Вариант с преобладанием исходов додж не рассматривается, т.к. полезного применения данная информация не имеет.

Если запустить индикатор на графике для подсчета статистики на каждом имеющемся историческом баре, то данный процесс займет значительное время, исчисляющееся в минутах или в десятках минут. Поэтому в индикатор включена возможность управления глубиной расчета. Для этого используется настроечный параметр BarsCount. Его значение по умолчанию 100. Реализация алгоритма этого ограничения возложена на первый блок функции OnCalculate:

 
// - 1 - == Определение значения индекса бара, с которого начнется расчет индикатора ====
   int cb = IndicatorCounted();                    // Кол-во баров, посчитанных во время
                                                   // ..предыдущего вызова функции start
   if (cb > 0) cb--;                               // Последний посчитанный бар будет..
                                                   // ..пересчитан
   int limit = MathMin(Bars - cb, BarsCount);      // Расчет индекса нового бара
// - 1 - == Окончание блока =============================================================

Первые две строки являются стандартными для любого индикатора. Изменения коснулись лишь третьей строки, в которой рассчитывается индекс начального бара для отображения индикатора. В имеющемся варианте индикатор рассчитывает статистику появления паттернов только для последних 100 баров. Стоит уточнить, что 100 барами ограничивается не история для сбора статистики по каждому эталонному паттерну, а именно бары, для которых эта статистика будет собираться. Например, начиная от бара с индексом 100, будет обозначен эталонный паттерн, для которого на всей имеющейся истории будет произведен поиск идентичных паттернов, сформирована статистика исходов и произведен анализ с отображением или без оного соответствующих стрелок. После этого будет совершен переход к бару с индексом 99, для которого будут повторены описанные действия. Затем все то же самое произойдет с баром №98 и т.д. до бара с индексом 1 включительно.

Для проверки правильности сбора информации пользователю требуется визуальное подтверждение, т.е. необходимо, чтобы индикатор отображал текущий эталонный паттерн, а также соответствующие ему исторические паттерны. Под словосочетанием "текущий эталонный паттерн" подразумевается не только последний сформированный паттерн, но и любой другой, который укажет пользователь. Для указания паттерна удобно использовать вертикальную линию, которую пользователь может передвигать в процессе работы индикатора. Ближайший левый от линии бар будет являться баром начала эталонного паттерна. Протяженность паттерна определяется настройками индикатора (параметр AnalysisBars). В соответствии с заданным эталонным паттерном, индикатор отображает идентичные исторические паттерны. На каждом тике индикатор должен анализировать положение вертикальной линии. Если положение изменилось, то это является сигналом запуска поиска исторических паттернов. 

Описанный алгоритм реализован в индикаторе при помощи третьего блока функции OnCalculate:

 
// - 3 - == Отображение паттернов, соответствующих указанному паттерну ==================
   int nBar = GetVLineBarIndex();                  // Определим местоположение..
                                                   // ..вертикальной линии
   if (nBar == LastnBar)                           // Если положение линии не..
      return(0);                                   // ..отличается от предыдущего..
                                                   // ..положения, то на этом заканчиваем
   DeleteAllRectangle();                           // Удалим все старые прямоугольники
   LastnBar = nBar;                                // Запомним новый номер бара
   ShowRectangle(Time[nBar+1],                     // Выделим эталонный паттерн
                 Low[iLowest(NULL, 0, MODE_LOW, AnalysisBars, nBar+1)], 
                 Time[nBar+AnalysisBars], 
                 High[iHighest(NULL, 0, MODE_HIGH, AnalysisBars, nBar+1)], EtalonColor);
   CreateEtalonPattern(nBar+1);                    // Создадим описание эталонного..
                                                   // ..паттерна
   limit = nBar + AnalysisBars + 1;                // Определим индекс начального бара..
                                                   // ..для поиска дубликатов паттерна
   int end = Bars - AnalysisBars - 1;              // Определим конечный бар в истории
   for (i = limit; i < end; i++)                   // Пройдем по всем барам
      if (CompareWithEtalon(i))                    // Если найден дубликат эталонного..
         ShowRectangle(Time[i],                    // ..паттерна, то выделим дубликат..
                       Low[iLowest(NULL, 0, MODE_LOW, AnalysisBars, i)],
                       Time[i+AnalysisBars-1],     // ..прямоугольником
                       High[iHighest(NULL, 0, MODE_HIGH, AnalysisBars, i)], 
                       HistoricalPatternColor);
// - 3 - == Окончание блока =============================================================

Переменная nBar отвечает за индекс бара, на котором в данный момент находится вертикальная линия. Переменная LastnBar содержит индекс бара, на котором находилась линия во время предыдущей итерации индикатора. Если линия не изменила своего положения, то чтобы избежать лишних расчетов, исполнение функции start завершается. Это происходит в случае равенства значений nBar и LastnBar.

Если линия изменила положение, вызывается функция DeleteAllRectangle, которая удаляет все объекты выделения паттернов, соответствующие предыдущему положению вертикальной линии. Затем в переменной LastnBar сохраняется индекс бара нового положения линии. Следующим шагом является отображение текущего эталонного паттерна. Паттерн отображается в виде подсвеченного прямоугольника. Цвет подсветки пользователь может указать при помощи настроечного параметра EtalonColor.

Дальнейший алгоритм программы напоминает алгоритм второго блока: производится вызов функции CreateEtalonPattern, описывающей текущий паттерн, а затем повторяется тело функции FindDublicateEtalonPattern с той лишь разницей, что при нахождении паттерна, идентичного эталонному, происходит отображение найденной свечной комбинации, а не заполнение переменных buy, sell и dodg. Цвет исторического паттерна можно изменить при помощи настроечного параметра HistoricalPatternColor.

Пользовательские функции, использованные в  коде третьего блока, DeleteAllRectangle и ShowRectangle, в отдельном описании не нуждаются. Их коды просты для понимания. Рассмотрения требует функция GetVLineBarIndex, при помощи которой индикатор получает координаты положения вертикальной линии:

 
//+-------------------------------------------------------------------------------------+
//| Получение индекса бара, на котором находится вертикальная линия                     |
//+-------------------------------------------------------------------------------------+
int GetVLineBarIndex()
{
   string name = prefix + "VLine";                    // Генерация имени объекта:..
                                                      // ..префикс + "VLine"
   if (ObjectFind(name) < 0)                          // Если линия не существует, то..
      ShowVLine(Time[0]);                             // ..отобразим ее на нулевом баре
   datetime time = ObjectGet(name, OBJPROP_TIME1);    // Определим время открытия бара,..
                                                      // ..на котором находитяс линия
   return(iBarShift(NULL, 0, time));                  // Вернем индекс этого бара
}

Для получения сведений о вертикальной линии необходимо знать имя объекта, представляющего ее. Все графические объекты, создаваемые индикатором, имеют единый префикс, указываемый в переменной prefix (недоступен для изменения пользователю). Благодаря наличию префикса, индикатор различает собственные объекты и другие графические объекты, расположенные на графике. Для индивидуализации имени объекта после префикса указывается тип объекта. В данном случае это VLine, для стрелок используется строка Arrow, а для прямоугольников - Rectangle. Если используется несколько объектов одного типа, то к имени добавляется время открытия бара, которому принадлежит объект. Так как при текущих условиях на одном и том же баре не может находиться более одного объекта сходного типа, то такой индивидуализации имен достаточно. 

Определив имя объекта, необходимо проверить, существует ли он, т.к. пользователь намерено или случайно мог удалить объект. Если объект не существует, то вызывается функция, создающая его на крайнем правом баре. После этого вызывается функция ObjectGet, которая возвращает время открытия бара, на котором  в данный момент расположена вертикальная линия. Так как нам нужно не время открытия бара, а его индекс, то используем функцию iBarShift, которая по указанному времени определяет индекс соответствующего бара.

Итогом работы представленного кода индикатора является такое отображение рыночной ситуации, которое показано на рис. 3.

Рис. 3. Индикатор NextBarType.

Указанный вид индикатора был получен при следующих значениях настроечных параметров: AnalysisBars = 3, MinHistory = 100, SpreadSize = 2.

Игорь Герасько

Июнь 2011