тел.: 8 800 200 86 47|+7 (812) 336 61 11Заказать звонок
Admiral Markets UK Admiral Markets UK Choose your country

MQLabs: Торговая система Woodie. Часть 3.

Индикатор Woodie'sCCI_v3

Звуковые файлы (разместить в папке sounds)

 

В продолжение освоения торговой системы Woodie рассмотрим еще два паттерна CCI: Шаму (Shamu Trade) и "Экстремальный крюк" (HFE). Наряду с уже описанными и реализованными паттернами ZLR и TLB (см. первую и вторую части материала), алгоритм распознавания новых паттернов будет встроен в код индикатора Woodie'sCCI. Итогом статьи будет третья версия индикатора.

 

Стратегическое изменение

В ходе диалога с участником форума Genry, в конце концов, было принято решение об отказе от учета взаимного расположения TCCI и CCI при формировании паттернов ZLR и TLB. Напомним, что паттерны рассматривались нами как сигналы открытия позиций по тренду, что должно было бы предполагать невозможность одновременного существования сигналов открытия и закрытия сделки. Одним из сигналов закрытия длинной позиции является нахождение линии TCCI ниже линии CCI. Соответственно, для короткой позиции сигналом закрытия является нахождение линии TCCI выше линии CCI (см. рис. 1).

Рис. 1. Сигналы закрытия позиций.

Указанная на рис. 1 трактовка правил распознавания паттернов приводит к пропуску значительного количества сигналов. По этой причине было принято решение не смотреть на взаимное положение TCCI и CCI.

Для того чтобы индикатор регистрировал паттерны ZLR и TLB в тех случаях, которым соответствует "неправильное" положение TCCI и CCI, необходимо облегчить код функций IsUpTrend и IsDnTrend:

 
//+-------------------------------------------------------------------------------------+
//| Определение наличия восходящего тренда на указанном баре                            |
//+-------------------------------------------------------------------------------------+
bool IsUpTrend(int index)
{
   return(upTrendBars[index] >= trendBars);
}
//+-------------------------------------------------------------------------------------+
//| Определение наличия нисходящего тренда на указанном баре                            |
//+-------------------------------------------------------------------------------------+
bool IsDnTrend(int index)
{
   return(dnTrendBars[index] >= trendBars);
}

Код функций сокращен до одной строки. Это привело к отображению паттернов ZLR и TLB, которые ранее считались несостоявшимися (см. рис. 2).

Рис. 2. "Новые" паттерны ZLR и TLB.

 

Паттерн Шаму (Shamu Trade)

Паттерн Шаму представляет собой блуждание линии главного CCI из отрицательной зоны в положительную и обратно. Для образования паттерна необходимо три пересечения нулевой линии, в течение которых главная линия CCI не выходила за пределы уровня +/-50 (устанавливается трейдером). Кен Вуд называет этот паттерн неудавшимся паттерном ZLR (см. рис. 3).

Рис. 3. Паттерн Шаму.

В обоих, указанных на рис. 3, случаях сначала был зарегистрирован паттерн ZLR, который сменился противоположным паттерном Шаму. Красными вертикальными линиями обозначен нисходящий паттерн Шаму, а синими вертикальными - восходящий паттерн Шаму.

Для регистрации паттерна неважно, какой именно тренд господствует в данный момент времени. Важно лишь то, что было несколько пересечений нулевой линии, в течение которых уровень +/-50 не был пробит.

Новая версия индикатора Woodie'sCCI будет отображать паттерн в виде молнии (зигзага). Это позволит трейдеру интуитивно понимать, о чем идет речь, сводя к минимуму вероятность принятия одного паттерна за другой. Восходящие паттерны Шаму удобно отображать в нижней части подокна индикатора, а нисходящие - в верхней части.

Приступим к реализации алгоритма определения паттерна. Главной функцией распознавания паттерна будет функция FindAndShowSHAMU. Ее код разработан на основе функции FindAndShowTLB (см. вторую часть материала):

 
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);                             // Удаление паттерна, если он был..
                                                   // ..ранее найден
}

В каждом из двух блоков функции производится вызов функции, которая определяет наличие того или иного типа паттерна. При нахождении паттерна Шаму, функции IsUpSHAMUPattern и IsDnSHAMUPattern отображают его, возвращая признак существования паттерна в виде результата false (не существует) или true (существует). От значения, которое вернули функции, зависит вызов функции PatternAlert, воспроизводящей звук нахождения паттерна (регулируется настроечными параметрами индикатора alertSHAMUpattern и soundSHAMUpattern).

Вместо использования единообразной функции DeleteObject, которая применялась для удаления отмененных паттернов ZLR и TLB, в рассматриваемой функции применяется вызов функции DeleteShamu. Связано это с тем, что изображение молнии невозможно сформировать при помощи одного графического объекта. Графическое представление паттерна Шаму - это три отрезка, соединенных между собой так, чтобы получалась сплошная кривая линия. Достигается сие применением объекта "Трендовая линия", у которой выключено свойство "Луч". В итоге удаление паттерна возможно лишь при помощи троекратного вызова функции DeleteObject с различными параметрами.

Рассмотрим функции определения паттерна Шаму на примере одной из них - IsUpSHAMUPattern:

 
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 - == Окончание блока =============================================================
}

Любой из паттернов Шаму (восходящий или нисходящий) имеет смысл регистрировать в момент его образования. Поэтому подразумевается, что индекс текущего бара index указывает на момент наиболее свежего пересечения нулевой линии линией CCI. Для восходящего паттерна CCI это пересечение снизу вверх. Такое условие обрабатывается в первом блоке функции. Невыполнение условия ведет к завершению функции, которая возвращает результат false.

Задачей второго блока является нахождение еще двух пересечений линии CCI и нулевой линии. Этот процесс протекает уже в цикле. Переменной цикла в данном случае выступает указатель индекса обрабатываемого бара - i. Счетчик пересечений - переменная zeroCrossCount, которая инициализируется значением 1 (ведь одно пересечение уже найдено). Условием выхода из цикла является обнаружение пересечения нулевой линии линией CCI снизу вверх, при котором уже было зарегистрировано два пересечения. Такой выход из цикла свидетельствует о формировании восходящего паттерна Шаму.

В теле цикла также возможно прерывание исполнения цикла, а вместе с ним, и функции. Но в таком случае паттерн признается не сформированным. Первое условие выхода - достигнут конец истории, а второе условие - выход линии CCI за пределы уровня +/-50 (настроечный параметр индикатора shamuBarLessThan). Если выход из цикла не состоялся, то до момента регистрации второго пересечения нулевой линии отслеживается минимальное значение CCI. Цель поиска - определение индекса бара, обладающего минимальным значением CCI на участке от первого до второго пересечения нулевой линии (поиск справа налево). После нахождения второго пересечения отслеживается максимальное значение CCI.

Минимум и максимум CCI нам потребуется для передачи их в качестве параметров функции ShowShamu. Наряду с ними, функции передаются индексы начального (i+1) и конечного (index) баров, формирующих паттерн Шаму. Отрезков три, значит, точек, составляющих их, должно быть четыре.

Функция ShowShamu преобразует переданную ей информацию так, чтобы можно было отобразить отдельные отрезки без дополнительной обработки параметров:

 
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 - == Окончание блока =============================================================
}

Первый блок функции, в зависимости от типа паттерна, определяет цвет линий, пояснительную надпись и место отображения паттерна. Тип паттерна передается функции через аргумент sign, который представляет собой строку - "UP" или "DN". Место отображения паттерна относительно нулевой линии определяется множителем mul. Значение 1 определяет положение выше нуля, а -1 - ниже нуля.

Второй, третий и четвертый блоки алгоритмически похожи. Каждый из блоков подготавливает уникальное имя графического объекта, состоящее из: короткого имени индикатора, надписи "SHAMU", номера отрезка в паттерне ("_1_", "_2_" или "_3_"), времени формирования паттерна и признака типа паттерна sign. Второй этап - определение координат каждого объекта. Левый по графику объект паттерна считается первым и отображается по горизонтали от бара, предшествующего пересечению нулевой линии, до бара максимума (минимума) CCI, а по вертикали от значения +/-300 до значения +/-200 (представлено именованными константами SHAMU_HIGH_LEFT_VALUE и SHAMU_LOW_LEFT_VALUE). Средний объект паттерна по горизонтали отображается между двумя локальными экстремумами CCI, а по вертикали - от +/-200 до +/-250 (SHAMU_LOW_LEFT_VALUE и SHAMU_HIGH_RIGHT_VALUE). Правый по графику объект паттерна по горизонтали отображается от бара последнего локального экстремума до бара регистрации паттерна, а по вертикали - от +/-250 до +/-150 (SHAMU_HIGH_RIGHT_VALUE и SHAMU_LOW_RIGHT_VALUE).

Функция, отображающая каждую из линий, достаточно проста:

 
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);
}

Если объект с именем name не существует, то создается трендовая линия в виде отрезка (выключено свойство "Луч") цвета clr, толщиной 2 пикселя и подписью description. Если объект с именем name найден, то он перемещается по заданным координатам.

Функция удаления паттерна по алгоритму обратна функции создания паттерна:

 
void DeleteShamu(int index)
{
   DeleteObject(TLB_STRING + FIRST_PART, index);
   DeleteObject(TLB_STRING + SECOND_PART, index);
   DeleteObject(TLB_STRING + THIRD_PART, index);
}

Функция трижды обращается к функции DeleteObject, передавая ей характерную для каждого из отрезков подстроку имени. На основании подстроки функция DeleteObject формирует полное имя объекта по установленному алгоритму.

Итог работы функций, добавленных в код индикатора Woodie'sCCI, показан на рис. 4.

Рис. 4. Отображение паттернов Шаму.

 

Паттерн "Экстремальный крюк" (HFE)

Паттерн HFE очень прост в понимании - это разворот линии CCI в области перекупленности или перепроданности. Кен Вуд считает областью перекупленности значения CCI выше уровня 200. Соответственно, область перепроданности  - это значения CCI меньше уровня -200. Разворот CCI должен иметь форму крюка, т.е. располагать ярко выраженным экстремумом (см. рис. 5).

Рис. 5. Паттерн HFE.

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

Приступим к составлению кода. Главная функция поиска паттерна HFE повторяет функцию поиска паттерна Шаму:

 
void FindAndShowHFE(int index, int total)
{
   static datetime lastAlert = 0;
// - 1 - == Восходящий паттерн "Экстремальный крюк" =====================================
   if (IsUpHFEPattern(index, total))               // Обнаружение и отображение паттерна
   {
      PatternAlert(alertHFEpattern, index,         // Звуковое оповещение о нахождении..
                   lastAlert, soundHFEpattern);    // ..паттерна
      return;
   }
// - 1 - == Окончание блока =============================================================

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

   DeleteHFE(index);                               // Удаление паттерна, если он был..
                                                   // ..ранее найден
}

Изменены лишь названия подчиненных функций. Все остальное - копия.

Функции определения наличия паттерна на указанном баре IsUpHFEPattern и IsDnHFEPattern также братья-близнецы. Рассмотрим их алгоритм на примере последней функции:

 
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);                               // ..ниже среднего бара - начало крюка
      
   ShowHFE(index, SIGN_DN);                        // Отображение крюка
   return(true);
}

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

Второе условие - средний бар паттерна (index+1) должен находиться в зоне перекупленности (для обратного паттерна - перепроданности). Третье и четвертое условия - крайние бары паттерна должны находиться выше среднего бара (для обратного паттерна - ниже).

Успешное прохождение четырех условий венчает вызов функции ShowHFE, которая отображает паттерн:

 
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 Shamu 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 - == Окончание блока =============================================================
}

Код функции напоминает функцию ShowShamu, рассмотренную выше. Радикальное отличие лишь одно - функция короче. Объясняется это тем фактом, что паттерн HFE отображается при помощи двух, а не трех, графических объектов. В данном случае графические объекты те же - трендовые линии. Форма линий - крюк.

Менее заметное отличие от паттерна Шаму - различное положение паттернов одного типа относительно нулевой линии. Нисходящий паттерн HFE отображается в отрицательной зоне. Поэтому ему соответствует значение множителя mul = -1. Соответственно, восходящий паттерн HFE отображается в положительной зоне, имея значение множителя mul = 1.

Положение линий, составляющих паттерн, по горизонтали всегда одно и то же - они соединяют соседние бары. Координаты крайних точек линий по вертикали также всегда одинаковы. Начальная точка левой по графику линии - +/-150, конечная - +/-50. Эта же точка является начальной точкой правой части паттерна, а конечной является координата +/-100. По этой причине паттерн HFE всегда и везде выглядит одинаково - крюк, так же как и паттерн ZLR - стабильный треугольник.

Функция удаления паттерна HFE еще короче, чем функция удаления паттерна Шаму:

 
void DeleteHFE(int index)
{
   DeleteObject(HFE_STRING + FIRST_PART, index);
   DeleteObject(HFE_STRING + SECOND_PART, index);
}

К общей функции DeletObject производится всего лишь два обращения, а не три.

Итог работы функций поиска паттерна HFE показан на рис. 6.

Рис. 6. Отображение паттернов HFE.

 

Результат работы индикатора

По отдельности для каждого паттерна мы уже приводили внешний вид индикатора. Теперь посмотрим, как выглядит индикатор, отображающий четыре вида паттернов (см. рис. 7).

Рис. 7. Индикатор Woodie's CCI_v3.

Количество информации постепенно увеличивается. Это вскоре позволит говорить уже о комбинациях паттернов, а не об их отдельных проявлениях.

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

Май 201212

Специально для компании Admiral Markets

2.1
 
 
X
Loading