Сложность
восприятия торговой системы Кена Вуда заключается в многообразии паттернов
CCI, на основании которых принимаются решения об
открытии или закрытии позиций. Неискушенному трейдеру легко запутаться в этой
каше образов. Выбраться же из "каши" можно, автоматизировав процесс
распознавания паттернов CCI, чему и были посвящены
три последние статьи MQLabs.
Текущим
результатом автоматизации является индикатор
Woodie'sCCI_v3, который распознает четыре вида паттернов CCI: отскок от нулевой линии - Zero Line Reject (ZLR),
пробой линии тренда - Trend Line Break (TLB), Шаму - Shamu Trade и
Экстремальный крюк - Hook From Extremum (HFE). Целью
данного материала является создание четвертой версии индикатора, который сможет
распознавать на два паттерна больше, чем его предшественник. Будут добавлены
паттерны: Вегас - Vegas Trade и Призрак - Ghost Trade.
Паттерн Вегас (Vegas Trade)
Паттерн Вегас не является самостоятельным паттерном, т.к. условием его
формирования является наличие паттерна HFE. С этой
точки зрения паттерн Вегас - это сигнал, подтверждающий силу паттерна
HFE. Не удивительно, что вероятность получения убытка
после открытия позиции по паттерну Вегас ниже, чем вероятность получения убытка в результате
закрытия позиции, открытой по паттерну HFE.
Итак, приступать к поиску паттерна Вегас можно только после окончательного
формирования паттерна HFE. Далее нам необходимо
обнаружить дополнительные составляющие паттерна (см. рис. 1):
1. Локальный экстремум CCI по
направлению к нулевой линии.
2. Закругление линии CCI, уходящее
от нулевой линии.
3. Окончание закругления, пробивающее локальный экстремум по
направлению к нулевой линии. Закругление должно содержать минимум три бара.
Рис. 1. Элементарные составляющие паттерна Вегас.
Для формирования восходящего паттерна Вегас необходимо наличие восходящего
паттерна HFE, за которым следует формирование
локального максимума в отрицательной области CCI. После максимума
должно образоваться закругление, имеющее не более одного локального минимума.
Формально закругление представляет собой два участка изменения
CCI: нисходящее и восходящее. Если после восходящего
движения CCI вновь обнаруживается нисходящее движение,
то закругление бракуется, а вместе с ним и паттерн. То же самое происходит, если
закругление слишком быстро (менее чем за три бара) заканчивается, пробивая
уровень локального максимума. Момент пробоя уровня локального максимума вверх
можно считать окончательным формированием паттерна Вегас.
Формирование нисходящего паттерна Вегас происходит после образования нисходящего
паттерна HFE, за которым следует формирование
локального минимума в положительной области CCI. Закругление в этом случае сначала удаляется от нулевой линии, а потом, при
сближении, пробивает уровень локального минимума вниз. Момент пробоя -
формирование паттерна Вегас.
В некоторых случаях к паттерну Вегас применяются дополнительные требования.
Например, весь паттерн, начиная с паттерна HFE и
заканчивая моментом пробоя, должен умещаться в 12 баров. Слишком широкий паттерн
Вегас уменьшает вероятность успешного завершения сделки. В будущем индикаторе
Woodie'sCCI_v4 максимальная ширина паттерна может быть
указана пользователем (параметр vtMaxBarsAmount).
По умолчанию принято значение 20.
Следующее дополнительное требование к паттерну - подтверждение сигнала
соответствующим взаимным расположением текущей рыночной цены и линии
Least Squares Moving Average (LSMA - средняя
скользящая, построенная по методу наименьших квадратов).
Индикатор LSMA для Meta Trader 4
можно скачать по ссылке, указанной вначале статьи. Для восходящего
паттерна должно выполняться условие: цена выше линии LSMA.
Соответственно, для нисходящего паттерна рыночная цена должна быть ниже
LSMA.
В новой версии индикатора Woodie'sCCI использование
приведенного выше условия может быть настроено пользователем. Учет взаимного
расположения цены и LSMA включается и выключается при
помощи параметра useLSMA. Период средней
скользящей устанавливается параметром lsmaPeriod.
Кен Вуд рекомендует использовать LSMA с
периодом 25.
Разработку кода, позволяющего распознать и отобразить паттерн, начнем с
добавления функции FindAndShowVT. Код функции в
значительной мере повторяет коды аналогичных функций (FindAndShowSHAMU,
FindAndShowZLR и т.д.). Поэтому не будем его здесь приводить.
Рассмотрению стоит посвятить код одной из функций, определяющих наличие паттерна
Вегас на указанном баре. Это функции IsUpVTPattern и
IsDnVTPattern. Так как функции алгоритмически подобны,
то приведем код последней из указанных функций. В виду объемности кода разобьем
его на части:
// - 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 - == Окончание блока =============================================================
Напомним, что переменные index
и total
являются аргументами функции. Аргумент index
указывает индекс бара, для которого производится поиск паттерна, а аргумент
total - индекс последнего обрабатываемого бара.
Всего функция содержит пять блоков. Назначение функции - нахождение нисходящего
паттерна Вегас на баре index. Первые два блока
проверяют исполнение двух требований к паттерну Вегас: наличие нисходящего
паттерна HFE и формирование локального минимума,
следующего за паттерном.
Первая задача выполняется путем последовательного поиска паттерна
HFE на каждом баре, начиная с бара, отстоящего от бара
index на заданную пользователем максимальную ширину
паттерна Вегас (vtMaxBarsAmount).
Поиск ведется по графику слева направо и продолжается до бара, отстоящего
от бара index на 5 баров. Значение 5 объясняется
следующим образом. По условиям паттерна Вегас закругление, следующее после
локального минимума, должно содержать не менее трех баров. Еще два бара
необходимо отнести на формирование минимума. Вот и получаем: 3+2 = 5.
Поиск паттерна HFE осуществляется при помощи уже
существующей функции IsDnHFEPattern. Сама функция
претерпела лишь одно изменение - из нее убран вызов функции отображения паттерна
ShowHFE, которая была перенесена в тело функции
FindAndShowHFE.
Второй блок функции IsDnVTPattern ищет локальный
минимум на участке от окончания паттерна HFE до бара,
отстоящего от бара index на 3 бара (оставляем на
формирование закругления). Если локальный минимум не найден или располагается
ниже нулевой линии, то паттерн Вегас считается не обнаруженным.
Вторая часть кода функции IsDnVTPattern:
// - 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);
За локальным
минимумом должно следовать закругление. Его и определяет третий блок функции.
Все значения CCI, составляющие закругление, должны
быть выше значения локального минимума. Иначе - паттерн не сформирован.
Тот факт, что
закругление является именно закруглением, определяется при помощи слежения за
правильностью направления изменения CCI. Так, после
локального минимума, значения CCI должны
увеличиваться. За это отвечает переменная up,
содержащая значение true. В тот момент, когда
зафиксировано первое уменьшение значения
CCI, переменная
up меняет свое значение на false.
С этого времени разрешается только уменьшение значений
CCI. Рост CCI на этом участке означает
нарушение закругления, а, значит, и невозможность формирования паттерна Вегас.
Четвертый блок
служит для проверки пробоя уровня локального минимума вниз. Если пробой не
состоялся, то паттерн нельзя считать сформированным.
Пятый блок
проверяет дополнительное условие для формирования паттерна - взаимное
расположение рыночной цены и LSMA. Если цена закрытия
бара index меньше или равна уровню
LSMA на этом же баре, то это является подтверждением образования паттерна
Вегас.
Функция
GetLSMA достаточно проста:
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);
}
Алгоритм функции
описывает одну из реализаций метода наименьших квадратов. Переменные
g_lengthValue и g_lsmaDeterminator
определены на глобальном уровне программы с целью устранения однотипных
расчетов их значений при каждом вызове функции GetLSMA.
Результат
автоматизации распознавания паттерна Вегас показан на рис. 2. Паттерн
отображается в виде прямоугольника. Горизонтальные стороны прямоугольника
указывают на экстремумы паттерна: пробойный экстремум и экстремум закругления.
Левая вертикальная граница указывает на бар, где был зафиксирован пробойный
экстремум, а правая граница - на бар пробоя этого экстремума.
Рис. 2. Паттерн Вегас.
Паттерн Призрак (Ghost Trade)
Паттерн Призрак,
по сути, тот же паттерн "Голова и плечи", который используется многими
трейдерами. Отличие Призрака от "Головы и плеч" заключается в том, что первый из
них необходимо искать среди показаний, образованных индикатором
CCI. Голова представляет собой локальный экстремум,
абсолютное значение которого больше абсолютного значения каждого из плеч. Левое
и правое плечи - это тоже локальные экстремумы
(см. рис. 3).
Рис. 3. Паттерн Призрак.
Вслед за
обнаружением головы и плеч паттерна слева и справа от головы необходимо
обнаружить еще по одному локальному экстремуму, обращенному в сторону нулевой
линии. Такие экстремумы будем называть впадинами, несмотря на то, что визуально
их смысл меняется в зависимости от стороны их расположения относительно нулевой
линии. И, наоборот, экстремумы, которые образовали голову и плечи, будем
называть пиками, т.к. они отвернуты от нулевой линии. Впадины могут находиться с
противоположной стороны от нулевой линии относительно головы и плеч. Также
допускается нахождение не более одного подряд элемента паттерна с другой стороны
от нулевой линии.
Через впадины паттерна проводится линия тренда. Сигналом образования паттерна
является пробой линии тренда вниз линией CCI в случае
нисходящего паттернаи пробой линии тренда вверх
линией CCI в случае восходящего паттерна.
Реализация кода
паттерна заключается в добавлении стандартного блока настроечных параметров и
функции FindAndShowGT, алгоритмически подобной всем
функциям FindAndShowXX индикатора. Алгоритм
определения наличия паттерна на указанном баре рассмотрим на примере функции
нахождения восходящего паттерна Призрак IsUpGTPattern, разбив ее на две части:
// - 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 - == Окончание блока =============================================================
Первый блок
функции производит поиск трех последних локальных минимумов, проходя по графику
справа налево. Эти минимумы - потенциальные голова и плечи. Значения минимумов
записываются в переменные minVal, а индексы баров, на
которых были найдены минимумы, записываются в переменных
minNum. Правому плечу соответствует индекс переменных 1 (minVal1
и minNum1), голове -
2, а левому плечу - 3. Если до конца истории так и не было найдено три минимума,
то паттерн считается не состоявшимся. Первый блок выносит такой же вердикт, если
в процессе поиска было обнаружено два и более положительных значения
CCI подряд.
Второй блок
проверяет соответствие найденных минимумов паттерну "Голова и плечи" - средний
минимум должен быть ниже плеч.
Функция
определения пика на указанном баре, используемая в первом блоке, является
универсальной:
Аргумент
mul указывает, с какой стороны от нулевой линии должны
находиться значения CCI. Отрицательная величина
mul означает, что производится поиск пика ниже нулевой
линии, т.е. пик должен быть отвернут от нуля вниз. Соответственно, положительное
значение mul свидетельствует о нахождении пика выше
нуля, т.е. пик должен быть отвернут от нуля вверх. Если значение
CCI на баре с индексом index
оказалось пиком с правильной стороны от нулевой линии, то функция
вернет значение true - пик найден. В противном случае
результат - false.
Для записи
значений найденного пика используется простая функция
SaveIndexAndValue:
Функция получает
значения value и index,
которые нужно записать в ту пару переменных, которая пока содержит 0. Три пары
переменных передаются в качестве указателей, что позволяет изменять их значения.
Вместо подобной
функции можно было бы организовать два массива из трех элементов со счетчиком
заполненности каждого элемента, но количество "три" настолько мало, что
преимущество использования массива не будет слишком уж очевидным.
Вторая часть
функции IsUpGTPattern:
// - 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 - == Окончание блока =============================================================
Третий блок
функции занимается поиском двух впадин. Одна впадина должна находиться между
левым плечом и головой, а вторая - между правым плечом и головой. К поиску
привлекается универсальная функция IsDipInThisArea (рассмотрена
ниже). Если хотя бы одна из впадин не была обнаружена,
то паттерн Призрак считается несостоявшимся. Запись значений
найденных впадин осуществляется в переменные maxVal (значение)
и maxNum (индекс).
Четвертый блок
функции проводит виртуальную линию тренда между точками, найденными в третьем
блоке. Результатом проведения линии является получение коэффициентов К и В
уравнения прямой. На основании этих коэффициентов рассчитывается значение линии
тренда на баре index. Пробой линии тренда определяется
сравнением значения CCI и полученной величины. Для
регистрации паттерна Призрак значение CCI должно быть
выше.
В пятом блоке
производится отображение паттерна, который состоит из четырех графических
объектов МТ4: линии тренда и трех горизонтальных подчерка, указывающих на место
регистрации экстремумов. Также стоит отметить, что один и тот же паттерн Призрак
может быть зарегистрирован на нескольких барах подряд (до тех пор, пока будет
пробита линия тренда). Это влечет за собой дублирование графических объектов,
визуально незаметное для пользователя, но приводящее к необоснованному росту
количества объектов. Предотвращение такой ситуации производится путем удаления
паттерна Призрак, если таковой существует на предыдущем баре. Исключение
составляет нулевой бар, т.к. ему соответствует окончательно не сформированный паттерн.
Функция поиска
впадины на указанном участке IsDipInThisArea:
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);
}
Аргументы
startIndex и endIndex
указывают индексы баров, относящихся к диапазону поиска, с учетом того, что
поиск производится слева направо. Коэффициент mul, как
всегда, указывает расположение искомого экстремума относительно нулевой линии.
Если впадина обнаружена (IsLocalDip вернет
true), то в переменные
value и num, доступные по
указателю, записываются данные по впадине, а функция поиска впадины возвращает
true.
Для определения
наличия впадины на указанном баре используется функция
IsLocalDip:
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);
}
Как отмечалось в
описании паттерна, впадина - это один из баров Призрака, которому разрешено
находиться с противоположной стороны от нулевой линии относительно остальных
баров паттерна. Поэтому алгоритм определения впадины делится на два условия.
Первое условие срабатывает, когда все три бара (потенциальная впадина и два
соседних бара) находятся с одной стороны от нулевой линии, а второе условие
учитывает возможность разброса значений относительно нуля.
Результат работы
функции определения паттерна Призрак приведен на рис. 4.
Напомним, что
короткие горизонтальные линии указывают на голову и два плеча, а наклонные линии
- это пробитые линии тренда, принадлежащие паттерну Призрак.
Комментарии
Отправить комментарий