Во второй части
материала, посвященному вычислению
наиболее вероятных экстремумов, был рассмотрен только один из возможных
подходов к интерпретации показаний индикатора
ProbablePriceChannel. Рассмотренный способ заключается в открытии
позиции на отбой от границ канала, сформированных индикатором, т.е. в некотором
смысле такая система является контртрендовой.
Противоположностью
контртрендовым системам выступают трендследящие системы. Применительно к
индикатору ProbablePriceChannel это означает, что
позиции будут открываться за границами канала в направлении пробоя
соответствующего уровня (поддержки или сопротивления). Правда, применение
критерия обычного пробоя границы канала без включения в систему дополнительного
фильтра вряд ли принесет какие-либо дивиденды. Поэтому займемся разработкой
правил новой стратегии, используя технику подтверждающего сигнала.
Роль фильтров
трендследящих систем наиболее эффективно выполняют трендовые индикаторы:
Moving Average, Average Directional Movement Index (ADX),
Bollinger Bands, Commodity Channel Index (CCI) и
Parabolic Support And Reverse (PSAR). Исходя из того, что позиции будут
открываться за пределами канала (теряется удобный уровень для размещения
стоп-приказа - противоположная граница канала), ограничим множество выбора
индикаторов двумя
индикаторами - Bollinger Bands и
PSAR. Их преимуществом над остальными трендовыми индикаторами является
наличие графического обозначения уровней поддержки и сопротивления. Из
оставшихся двух индикаторов более удобным индикатором, используемым в качестве
фильтра, можно назвать PSAR, т.к. он всегда явно
указывает текущую тенденцию рынка: отображается либо поддержка, либо
сопротивление, а не два уровня сразу, как у Bollinger Bands.
Итак, основным
положением новой стратегии будет использование сигналов от двух индикаторов
одновременно: ProbablePriceChannel и
PSAR. Когда цена закрытия свечи находится за пределами
канала ProbablePrice Channel, ожидается получение
подтверждения от индикатора PSAR, т.е. переход от
одной тенденции к другой (см. рис. 1).
Рис. 1. Сложение сигналов двух индикаторов.
Как видно из рис.
1, предложенные торговые правила приводят к достаточно большому запаздыванию
сигнала,
но, с другой стороны, приводят к повышению вероятности открытия успешной
позиции.
Стоп-приказ
позиции, как отмечалось выше, будет размещаться за последней неизменной точкой
индикатора PSAR. А вот размещение уровня профита будет
не совсем стандартным. В момент открытия позиции уровень профита не
устанавливается, ожидается дальнейшее развитие событий. Иначе говоря, стратегия
выжидает достижения ценой максимального уровня прибыли. После сформирования
максимума прибыли цена откатывается и некоторое время находится в прострации без
явного движения в ту или иную сторону. В этот момент шаг изменения значений
точек индикатора PSAR начинает уменьшаться, что и
является сигналом к установке уровня профита. Этот уровень определяется как
уровень максимальной прибыли, который был достигнут после смены тенденции
PSAR (см.
рис. 2).
Рис. 2. Алгоритм установки уровня профита.
Здесь (на рис. 2)
в момент открытия позиции Buy (14:45 24-го марта 2011
года) по цене 1.4175 устанавливается лишь уровень стоп-приказа, который
располагается за самой поздней на тот момент точкой индикатора
PSAR (цена 1.4105). При
движении цены в сторону прибыли позиции один раз за один бар производится
перемещение уровня стоп-приказа вслед за продвижением точки
PSAR. Одновременно с этим совершается сравнение уровней последних трех
точек PSAR между собой. Если обнаруживается, что
разность между уровнями уменьшилась, то это является сигналом для установки
уровня профита позиции. Такой момент на рис. 2 наступил в момент открытия свечи
16:15. Ближайшее значение (бар №1)
PSAR было 1.4120, на баре №2 - 1.4118, а на баре №3 - 1.4115. То есть
разность значений PSAR между вторым и третьим баром
составила 3 пункта, а между первым и вторым - 2
пункта. В итоге уровень профита позиции был размещен на цене 1.4212 (максимум
свечи 15:00).
Техническое задание
Перед тем как
приступить к разработке советника, осуществляющего торговлю по новым торговым
правилам, изложим эти правила структурно.
1. Открытие
позиций
1.1 Позиция Buy открывается
при одновременном выполнении условий:
1.1.1 Цена закрытия свечи находится выше верхней границы канала индикатора
ProbablePriceChannel.
1.1.2 Точка индикатора
PSAR на баре №1 находится ниже цены закрытия свечи №1,
а на баре №2 - выше цены закрытия свечи №2 (смена тенденции с нисходящей на
восходящую).
1.2 Позиция
Sell открывается при одновременном выполнении условий:
1.2.1 Цена закрытия свечи находится ниже нижней границы канала индикатора
ProbablePriceChannel.
1.2.2 Точка индикатора
PSAR на баре №1 находится выше цены закрытия свечи №1,
а на баре №2 - ниже цены закрытия свечи №2 (смена тенденции с восходящей на
нисходящую).
2. Установка
и перемещение стоп-приказа.
2.1 Стоп-приказ
длинной позиции размещается ниже точки индикатора PSAR
на один пункт в момент открытия позиции. С образованием новой точки
уровень передвигается вверх, сохраняя дистанцию в один пункт до точки.
2.2 Стоп-приказ
короткой позиции размещается выше точки индикатора PSAR
на один пункт и спред в момент открытия позиции. С образованием новой
точки уровень передвигается вниз, сохраняя дистанцию в один пункт и спред до точки.
3. Установка
уровня профита.
3.1
В момент открытия позицииуровень профита не
устанавливается. Это происходит позже однократно при выполнении условий,
указанных в пп. 3.2 и 3.3.
3.2 Профит длинной
позиции устанавливается в тот момент, когда шаг приращения цены соседних точек
индикатора PSAR начинает уменьшаться. Значение профита
- уровень
максимальной цены, действующей с момента смены тенденции индикатора
PSAR до текущего момента.
3.
3
Профит короткой
позиции устанавливается в тот момент, когда шаг уменьшения цены соседних точек
индикатора PSAR начинает становится меньше. Значение профита - уровень
минимальной цены, действующей с момента смены тенденции индикатора PSAR до
текущего момента, плюс спред.
Создание кода советника
Начало разработки
кода, как это обычно и происходит, положим созданием функции генерации сигналов
GetSignal:
//+-------------------------------------------------------------------------------------+
//| Генерация сигнала закрытия, покупки или продажи |
//+-------------------------------------------------------------------------------------+
int GetSignal(double& lastSar, double &lastLocalExt)
{
// - 1 - == Получение цен точек Parabolic SAR ===========================================
lastSar = iSAR(NULL, 0, Step, Max, 1); // Значение точки на 1-ом баре справа
double pSar2 = iSAR(NULL, 0, Step, Max, 2); // Значение точки на 2-ом баре справа
double pSar3 = iSAR(NULL, 0, Step, Max, 3); // Значение точки на 3-ем баре справа
// - 1 - == Окончание блока =============================================================
// - 2 - == Генерация сигнала покупки ===================================================
if (Close[1] > lastSar && Close[2] < pSar2) // PSAR пробил точку сопротивления и..
{ // ..отображает лишь уровень поддержки
double top = GetTopChannelBorder(); // Расчет верхней границы канала
if (Close[1] > top) // Свеча закрылась выше верхней..
return(SIGNAL_BUY); // ..границы канала. Покупка
}
// - 2 - == Окончание блока =============================================================
// - 3 - == Генерация сигнала продажи ===================================================
if (Close[1] < lastSar && Close[2] > pSar2) // PSAR пробил точку поддержки и..
{ // ..отображает уровень сопротивления
double bottom = GetBottomChannelBorder(); // Расчет нижней границы канала
if (Close[1] < bottom) // Свеча закрылась ниже нижней..
return(SIGNAL_SELL); // ..границы канала. Продажа
}
// - 3 - == Окончание блока =============================================================
// - 4 - == Генерация сигнала закрытия позиции ==========================================
if (((Close[2] > pSar2 && Close[3] > pSar3) || // Восходящая или нисходящая тенденция
(Close[2] < pSar2 && Close[3] < pSar3)) && // ..PSAR продолжается более 3-х баров
NP(MathAbs(lastSar - pSar2)) < // Разница между соседними..
NP(MathAbs(pSar2 - pSar3)))
{ // ..точками PSAR уменьшается.
if (g_type >= OP_BUY) // Если есть позиция
lastLocalExt = GetLocalExt(pSar3); // Нахождение последнего экстремума
return(SIGNAL_SET_PROFIT); // Установка профита позиции
}
// - 4 - == Окончание блока =============================================================
return(NO_SIGNAL); // Нет сигнала
}
Необычность
рассматриваемой функции заключается в наличии двух аргументов, которые
передаются ей по указателю. Первый аргумент (lastSar)
- это указатель на переменную, в которую функция должна записать
результат вычисления последнего значения индикатора PSAR.
В дальнейшем эта информация используется для расчета уровня стоп-приказа
позиции. Второй аргумент (lastLocalExt)
- это указатель на переменную, в которую функция может записать результат
поиска последнего локального экстремума. Значение экстремума используется в
дальнейшем для расчета уровня профита позиции.
Первый блок
функции - расчет трех последних значений индикатора PSAR.
Наиболее свежее значение записывается в переменную
lastSar. Переменные Stepи
Max - настроечные параметры эксперта, при
помощи которых пользователь может управлять характеристиками индикатора
PSAR.
Второй блок -
проверка наличия сигнала покупки. Вначале проверяется условие формирования
сигнала от PSAR (переход от нисходящей к нисходящей
тенденции). Только при выполнении первого условия
проверяется пробой верхней границы канала
ProbablePriceChannel ценой закрытия свечи. С этой целью рассчитывается
цена верхней границы канала путем вызова функции
GetTopChannelBorder:
//+-------------------------------------------------------------------------------------+
//| Получение значения верхней границы канала ProbablePriceChannel |
//+-------------------------------------------------------------------------------------+
double GetTopChannelBorder()
{
return (iCustom(NULL, 0, "ProbablePriceChannel", "1", FindPeriod, "1",
ChannelMeasure, "1", ConfirmationMin, 0, 1));
}
Эта функция
обращается к значению первого буфера (индекс 0) индикатора
ProbablePriceChannel на баре с индексом 1. Переменные
FindPeriod, ChannelMeasureи
ConfirmationMin - настроечные параметры
эксперта, при помощи которых пользователь может настраивать характеристики
индикатора ProbablePriceChannel. Полученное
значение сравнивается с ценой закрытия последней свечи. Если цена закрытия
превышает цену верхней границы, то функция GetSignal
возвращает сигнал открытия длинной позиции - SIGNAL_BUY.
Третий блок
функции GetSignal алгоритмически подобен второму
блоку. Сначала проверяется наличие сигнала продажи индикатора
PSAR, а затем цена закрытия последней свечи сравнивается с ценой нижней
границы канала, которая рассчитывается путем вызова функции
GetBottomChannelBorder.
Четвертый блок
функции исполняется в случае отсутствия сигналов продажи или покупки. Его
задачей является слежение за шагом изменения точек индикатора
PSAR. Причем слежение начинается только в том случае, если текущая
тенденция индикатора PSAR насчитывает три бара и
больше. В противном случае объектов сравнения нет, и блок не исполняется. Если определено, что
разница между точками PSAR уменьшается, то
производится вызов функции GetLocalExt, которая
вычисляет цену последнего локального экстремума, действующего с момента начала
новой тенденции PSAR. Обращение к функции совершается
при условии наличия позиции, открытой советником. Вне зависимости от наличия
позиции функция GetSignal возвращает сигнал установки
профита - SIGNAL_SET_PROFIT.
Если ни одно из
трех предыдущих условий функции GetSignal не было
выполнено, то возвращается признак отсутствия сигнала -
NO_SIGNAL.
Функция
GetLocalExt производит поиск локального экстремума
следующим образом:
//+-------------------------------------------------------------------------------------+
//| Нахождение локального экстремума за время действия последней тенденции PSAR |
//+-------------------------------------------------------------------------------------+
double GetLocalExt(double sar1)
{
// - 1 - == Инициализация переменных ====================================================
int cnt = 4; // Поиск начинается с бара №4, т.к...
double sar2 = iSAR(NULL, 0, Step, Max, cnt); // ..первые три бара точно указывают..
// ..на тенденцию
// - 1 - == Окончание блока =============================================================
// - 2 - == Поиск смены тенденции PSAR ==================================================
while ((Close[cnt-1] > sar1 && Close[cnt] > sar2) ||// Цикл исполняется, пока не..
(Close[cnt-1] < sar1 && Close[cnt] < sar2))// ..встретятся разные тенденции
{
cnt++; // Переход к следующему бару
if (cnt >= Bars) return(); // Достигнут конец истории - выход
sar1 = sar2; // "Ранее" значение PSAR становится..
// .."поздним"
sar2 = iSAR(NULL, 0, Step, Max, cnt); // Расчет нового "раннего" значения
}
// - 2 - == Окончание блока =============================================================
// - 3 - == Расчет максимума ============================================================
if (Close[cnt-1] > sar1 && Close[cnt] < sar2) // Найден переход с нисходящей..
// ..тенденции на восходящую
return(High[iHighest(NULL, 0, MODE_HIGH, cnt, 1)]);// Цена максимума
// - 3 - == Окончание блока =============================================================
// - 4 - == Расчет минимума =============================================================
if (Close[cnt-1] < sar1 && Close[cnt] > sar2) // Найден переход с восходящей..
// ..тенденции на нисходящую
return(Low[iLowest(NULL, 0, MODE_LOW, cnt, 1)]);// Цена минимума
// - 4 - == Окончание блока =============================================================
}
В качестве
аргумента функция получает значение индикатора PSAR на
баре с индексом 3. Соответственно, поиск смены тенденции начинается со сравнения
значений PSAR на барах №3 и №4. На более поздних барах
(№1 и №2) поиск смены
тенденции начинать бесполезно, т.к. вызов функции GetLocalExt
совершается только при выполнении первого условия блока 4 функции
GetSignal (см. выше).
Первый блок
функции GetLocalExt выполняет инициализацию счетчика
будущего цикла cnt и расчет значения индикатора
PSAR на баре №4 - sar2. В
итоге к моменту начала блока 2 функция располагает значением позднего бара -
sar1 (справа по графику) и раннего бара -
sar2 (слева по графику).
Блок 2 выполняет
поиск последней смены тенденции. Тело цикла не выполняется при нахождении
перехода от одного состояния индикатора PSAR к
другому. Исполнение тела цикла заключается в переходе к следующему бару, на
котором поздним значением PSAR становится предыдущее
раннее значение - sar2. Новое раннее значение
рассчитывается.
Блок 3 и блок 4,
в зависимости от типа найденной смены тенденции, выполняют поиск максимума или
минимума цены соответственно.
Обработка
данных, полученных функцией GetSignal, производится в
теле функции Trade:
//+-------------------------------------------------------------------------------------+
//| Открытие позиций |
//+-------------------------------------------------------------------------------------+
bool Trade(int signal, double lastSar, double lastLocalExt)
{
// - 1 - == Установка профита существующей позиции ======================================
if (signal == SIGNAL_SET_PROFIT) // Сигнал установки профита
if (!SetProfit(lastLocalExt)) // Установка уровня профита
return(false); // Неудачная установка - ошибка
// - 1 - == Окончание блока =============================================================
// - 2 - == Открытие длинной позиции ====================================================
if (signal == SIGNAL_BUY && g_type != OP_BUY) // Активен сигнал покупки и..
{ // ..отсутствует длинная позиция
if (g_type == OP_SELL) // Существует короткая позиция
if (!CloseDeal(g_ticket)) // Закроем позицию
return(false); // При неудаче вернем false
double sl = NP(lastSar - g_tickSize); // Формируем цену стоп-приказа
if (OpenOrderCorrect(OP_BUY, GetLots(), NP(Ask), sl, 0) != 0)
return(False);
}
// - 2 - == Окончание блока =============================================================
// - 3 - == Открытие короткой позиции ===================================================
if (signal == SIGNAL_SELL && g_type != OP_SELL) // Активен сигнал продажи и..
{ // ..отсутствует короткая позиция
if (g_type == OP_BUY) // Существует длинная позиция
if (!CloseDeal(g_ticket)) // Закроем позицию
return(false); // При неудаче вернем false
sl = NP(lastSar + g_spread + g_tickSize); // Формируем цену стоп-приказа
if (OpenOrderCorrect(OP_SELL, GetLots(), NP(Bid), sl, 0) != 0)
return(False);
}
// - 3 - == Окончание блока =============================================================
return(True);
}
Все три
аргумента функции - это результат исполнения функции GetSignal.
Второй и третий параметры повторяют аргументы функции
GetSignal, а первый параметр являет собой значение сигнала, которое вернула функция
генерации сигнала, т.е.: NO_SIGNAL - нет сигнала,
SIGNAL_SET_PROFIT - сигнал установки профита,
SIGNAL_BUY - сигнал покупки и SIGNAL_SELL - сигнал продажи.
Первый блок
функции Trade обрабатывает наличие сигнала установки
профита, вызывая функцию SetProfit (рассмотрена ниже).
При успешной установке профита функция возвращает значение
true. Неудачная установка описывается значением false.
Второй блок
обрабатывает сигнал покупки при условии отсутствия длинной позиции. В этом
случае сначала производится проверка существования и, если необходимо, закрытие
короткой позиции. Затем рассчитывается уровень стоп-приказа длинной позиции и
осуществляется открытие рыночного ордера Buy.
Аналогичным
образом исполняется третий блок, который обрабатывает наличие сигнала продажи
при условии отсутствия короткой позиции. Сначала проверяется наличие длинной
позиции и ее закрытие, а затем расчет стоп-приказа новой позиции и ее открытие.
Функция
установки профита SetProfit получает в свое
распоряжение значение последнего найденного экстремума:
//+-------------------------------------------------------------------------------------+
//| Установка профита текущей позиции |
//+-------------------------------------------------------------------------------------+
bool SetProfit(double lastLocalExt)
{
// - 1 - == Проверка существования позиции ==============================================
if (!OrderSelect(g_ticket, SELECT_BY_TICKET) || // Если позиция не существует или..
OrderCloseTime() != 0 || // ..уже закрыта, или..
OrderTakeProfit() != 0) // ..имеет уровень профита, то..
return(true); // ..уходим
// - 1 - == Окончание блока =============================================================
// - 2 - == Расчет уровня профита и проверка возможности внесения изменений =============
double tp = 0; // Инициализация значения
if (g_type == OP_BUY) // Для длинной позиции убедимся, что..
if (lastLocalExt - Bid > g_stopLevel) // ..профит выше Bid на Stop Level..
tp = NP(lastLocalExt); // ..пунктов
if (g_type == OP_SELL && lastLocalExt != 0) // Для короткой позиции убедимся, что
if (Bid - lastLocalExt > g_stopLevel) // ..профит ниже Ask на Stop Level..
tp = NP(lastLocalExt + g_spread); // ..пунктов
// - 2 - == Окончание блока =============================================================
// - 3 - == Изменение уровня профита ====================================================
if (tp != 0) // Уровень профита определен
if (!OrderModify(OrderTicket(), 0, OrderStopLoss(), tp, 0))// Изменение профита
return(false); // Не удалось изменить профит - ошибка
// - 3 - == Окончание блока =============================================================
return(true); // Успешное завершение функции
}
На основании
значения переменной lastLocalExt далее в теле функции
производится расчет уровня профита, который необходимо установить.
Первый блок
функции отвечает за проверку наличия позиции, открытой экспертом. Сначала
производится выбор ордера с тикетом g_ticket, затем
проверка времени его закрытия (если время не равно нулю, то ордер закрыт) и, в
конечном итоге, проверка значения профита. Если профит уже установлен, ордер
закрыт или в принципе не существует, то функция SetProfit
заканчивается возвратом результата true -
профит устанавливать не нужно.
Во втором блоке
рассчитывается значение профита позиции. Для длинной позиции - это значение
lastLocalExt, а для короткой - это же значение, к
которому добавляется спред. После расчета уровня проверяется допустимость его
установки. Так, если текущая цена расположена слишком близко к расчетному
уровню, то установка профита не производится. Технически это осуществляется
путем отказа от сохранения рассчитанного значения в переменной
tp (переменная продолжает содержать 0).
Третий блок
функции изменяет уровень профита позиции, если переменная tp
содержит отличное от нуля значение. Результат установки (true
или false)в этом
случае является результатом исполнения функции.
Тестирование эксперта
Советник
ProbablePriceChannelPSAR был протестирован на таймфрейме
M15. Выбор такого периода графика связан с необходимостью получения большей частоты
совершения сделок. На привычном для проекта MQLabs
таймфрейме Н1 частота сделок получается настолько низкой, что за год тестирования едва
наберется 50 сделок.
В
качестве диапазона тестирования был выбран исторический период 01.01.2010 -
21.01.2012.
Для каждой валютной пары был подобран свой набор настроечных параметров. Отличия
от пары к паре заключались в значениях пяти настроечных параметров: FindPeriod,
ChannelMeasure, ConfirmationMin, Step
и Max.
Остальные параметры использовались со значениями, принятыми по умолчанию. Результаты тестирования показаны на рис.
3-6.
EURUSD.Значения настроечных параметров:
FindPeriod= 85, ChannelMeasure
= 10, ConfirmationMin = 7,
Step = 0.04,
Max = 0.1. Кривая баланса
выглядит стабильно, но с нестабильным ростом. Плюсом такого вида кривой является
ее наклон вверх в конечной фазе тестирования. Чистая прибыль 1 393 доллара, максимальная просадка
325
долларов. Фактор восстановления 4.29. Это наилучший сегодняшний результат.
Рис. 3. Результаты
тестирования эксперта
ProbablePriceChannelPSAR на валютной паре
EURUSD.
USDCHF.Значения настроечных параметров:
FindPeriod= 65, ChannelMeasure
= 8, ConfirmationMin = 5,
Step = 0.01,
Max = 0.01. Кривая баланса
нестабильна, столь же нестабилен и ее рост. Чистая прибыль
1 174 долларов, максимальная просадка 604
доллара. Фактор восстановления 1.94.
Рис. 4. Результаты
тестирования эксперта
ProbablePriceChannelPSAR на валютной паре
USDCHF.
GBPUSD.Значения настроечных параметров:
FindPeriod= 35, ChannelMeasure
= 6, ConfirmationMin = 6,
Step = 0.02,
Max = 0.02. Кривая баланса
вновь нестабильна, но из-за большего, чем у USDCHF,
количества сделок выглядит предпочтительнее. Эта предпочтительность
подтверждается статистическими показателями теста:чистая прибыль
1 625 долларов, максимальная просадка 684
доллара. Фактор восстановления 2.37.
Рис. 5. Результаты
тестирования эксперта
ProbablePriceChannelPSAR на валютной паре
GBPUSD.
USDJPY.Значения настроечных параметров:
FindPeriod= 65, ChannelMeasure
= 9, ConfirmationMin = 5,
Step = 0.01,
Max = 0.04. Кривая
баланса в данном случае показывает ситуацию лучше самого заядлого комментатора. Чистая прибыль
259 долларов, максимальная просадка
444
доллара. Фактор восстановления ниже единицы.
Рис. 6. Результаты
тестирования эксперта
ProbablePriceChannelPSAR на валютной паре
USDJPY.
Использование
полученного советника рекомендуется только в полуавтоматическом режиме под
присмотром трейдера и после всестороннего изучения слабых и сильных сторон
стратегии. Приведенные результаты тестирования характеризуют только то, как
могла бы работать стратегия на указанном историческом периоде, а не ее
результаты в будущем.
Комментарии
Отправить комментарий