Scriptong
20-01-2010, 11:20
Продолжая рассмотрение методик Станислава Чувашова, перейдем к более обширной его разработке - ТААЧ (Торгово-аналитический алгоритм Чувашова). Система ТААЧ, по сути, не является абсолютно законченной и строго оговоренной. Например, вообще не указано, где необходимо устанавливать уровень стопа при открытии сделки. Тем не менее, в отличие от большинства теорий (например, волны Эллиотта или Тактика Адверса) ТААЧ обладает рядом четко формализованных критериев, которые трудно истолковать двояко.
Перейдем же непосредственно к рассмотрению системы. ТААЧ вновь оперирует двумя линиями, но, в отличие от стратегии "Вилка Чувашова", базируется на образовании каналов. Одна из методик построения каналов уже была рассмотрена в прошлогодней статье Откат в тренде. ТААЧ отличается и от нее. Согласно ТААЧ, каналы существуют в любом месте графика, а не только при выполнении определенных условий. В качестве опорных точек канала используются три последних фрактала. Единственное условие - один из трех фракталов должен отличаться по типу от других двух. Например, два верхних фрактала и один нижний или два нижних фрактала и один верхний. Если подряд следует три одинаковых по типу фрактала, то из них берутся только первые два, и поиск продолжается до нахождения первого фрактала другого типа.
Три фрактала - это три точки, по которым можно построить две параллельные линии. Одну линию строим по двум фракталам, а вторую по третьему фракталу и углу наклона первой линии так, чтобы вторая линия была параллельная первой. В результате выходит, что можно построить только четыре типа каналов:
восходящий канал, который в качестве первых двух опорных точек использует верхние фракталы (рис. 1) восходящий канал, который в качестве первых двух опорных точек использует нижние фракталы (рис. 2) нисходящий канал, который в качестве первых двух опорных точек использует верхние фракталы (рис. 3) нисходящий канал, который в качестве первых двух опорных точек использует нижние фракталы (рис. 4)
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_11.gif
Рис. 1. - Восходящий канал, построенный по двум верхним фракталам. http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_12.gif
Рис. 2. - Восходящий канал, построенный по двум нижним фракталам
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_13.gif
Рис. 3. - Нисходящий канал, построенный по двум верхним фракталам.http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_14.gif
Рис. 4. - Нисходящий канал, построенный по двум нижним фракталам
Полученные границы канала выступают в роли линий поддержки и сопротивления. Поэтому относительно каждой из них могут быть применены две тактики торговли: пробойная и отбойная. Таким образом, если совершено пробитие верхней границы канала (сопротивления), то нужно покупать. Если произошел отбой от линии сопротивления, то нужно продавать. То же самое справедливо для нижней линии канала (поддержки), только наоборот. Если произошел пробой линии, то нужно продавать, если отбой, то покупать.
Дальше следует довольно важный момент - что считать пробитием или отбоем от линии? В стратегии ТААЧ специально для четкого определения этих понятий вводится термин Зона Условного Касания (ЗУК). Границы ЗУК располагаются по обе стороны от каждой линии канала на расстоянии 10%, взятой от ширины канала. В результате отбоем (см. рис. 5) считается нахождение максимума или минимума свечи в ЗУК, а закрытие свечи вне ЗУК, но внутри канала.
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_15.gif
Рис. 5. - Отбой от линии поддержки
По аналогии, пробой линии - нахождение одного из экстремумов свечи внутри канала (или в ЗУК), а закрытие свечи вне пределов ЗУК (см. рис. 6)
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_16.gif
Рис. 6. - Пробой линии поддержки
Описанные правила помогают всего лишь определиться с сигнальной свечей. А вот пробой сигнальной свечи в направлении отбоя или пробоя уже является сигналом для открытия позиции. Поэтому после идентификации сигнальной свечи будем откладывать ордер на максимум (минимум) свечи.
С ценой входа в рынок мы определились. А вот с целями - нет. ТААЧ имеет ответ и на этот вопрос. При отбое от линии возможно два типа целей:
Если направление отбоя совпадает с направлением канала (отбой от нижней границы восходящего канала или отбой от верхней границы нисходящего канала), то целью является противоположная граница ЗУК. Если направление отбоя не совпадает с направлением канала (отбой от верхней границы восходящего канала или отбой от нижней границы нисходящего канала), то целью является середина канала.
Цель при пробое канала не разделяется на варианты. Это всегда будет граница двойного канала. Например, при пробое линии поддержки нужно от нижней границы канала отложить ширину имеющегося канала вниз.
А вот насчет стопов для позиции стратегия ТААЧ молчит. Поэтому сегодняшняя реализация стратегии не будет устанавливать уровни стопа, так как на данный момент круг ее применения будет ограничен тестером стратегий.
Приступая к реализации, по обыкновению начнем с функции GetSignal. Но так как она получилась очень объемной (более 100 строк), рассмотрим ее поблочно:
// - 1 - =========================== Инициализация значений =============================
BuyLevel = 0; SellLevel = 0; // Уровни входа не определены, значит, вилки нет
double VUp[2], VDn[2]; // Значения верхних и нижних фракталов
int NUp[2], NDn[2]; // Соответствующие номера баров, на которых найдены фракталы
int UpCnt = 0, DnCnt = 0; // Счетчики количества верхних и нижних фракталов
ArrayInitialize(VUp, 0);
ArrayInitialize(VDn, 0);
// - 1 - ============================== Окончание блока =================================
// - 2 - ====== Поиск трех последовательно понижающихся или повышающихся фракталов ======
for (int i = Bar+3; i < Bars && UpCnt+DnCnt < 3; i++)
{
double Fup = iFractals(Symbol(), 0, MODE_UPPER, i);
double Fdn = iFractals(Symbol(), 0, MODE_LOWER, i);
// - 2.1 - ================== Сохранение верхних фракталов ==========================
if (Fup != 0 && UpCnt != 2)
{
VUp[UpCnt] = Fup; // Сохраняем значение фрактала
NUp[UpCnt] = i; // Сохраняем номер бара, на котором найден фрактал
UpCnt++; // Увеличение счетчика верхних фракталов
}
// - 2.1 - ========================== Окончание блока ===============================
// - 2.2 - ================== Сохранение нижних фракталов ===========================
if (Fdn != 0 && DnCnt != 2)
{
VDn[DnCnt] = Fdn; // Сохраняем значение фрактала
NDn[DnCnt] = i; // Сохраняем номер бара, на котором найден фрактал
DnCnt++; // Увеличение счетчика нижних фракталов
}
// - 2.2 - ========================== Окончание блока ===============================
}
// - 2 - ============================== Окончание блока =================================
if (i == Bars) return;
Первый блок затруднений не вызывает, обычная инициализация значений переменных. Как и в прошлом эксперте по стратегии "Вилка Чувашова", используются переменные BuyLevel и SellLevel, ненулевое значение которых свидетельствует о наличии сигнала для установки отложенного ордера и указывает цену Bid для входа. Точно также как и в "Вилке Чувашова", используются массивы VUp, VDn, NUp и NDn. Только на этот раз они двумерные, так как нам всего потребуется три фрактала, а не три фрактала каждого типа.
Во втором блоке впервые встречается переменная Bar. Она является входным параметром функции GetSignal. Переменная задает номер бара, относительно которого производится нахождение и отображение канала. В данном советнике это дополнение не особо нужно и введено исключительно с прицелом на следующие версии советника.
Назначением второго блока является поиск фракталов. Как уже замечалось выше, требуется именно три фрактала, но такие, чтобы среди них было два типа фракталов. Блок устроен таким образом, что если после нахождения двух фракталов одинакового типа находится еще один такой же, то он игнорируется. Это достигается запретом приращения счетчиков UpCnt и DnCnt, если они достигли 2.
Последняя строка, не относящаяся ни к одному из блоков, страхует эксперт от слишком короткой истории котировок, доступной терминалу.
Следующие два блока производят расчеты каналов на основании полученных данных:
// - 3 - ====================== Строим канал по верхним фракталам =======================
if (UpCnt == 2)
{
// Параметры отображение канала
datetime Time1 = Time[NUp[1]];
datetime Time2 = Time[NUp[0]];
datetime Time3 = Time[NDn[0]];
double Price1 = VUp[1];
double Price2 = VUp[0];
double Price3 = VDn[0];
// Расчет верхней границы канала по двум верхним фракталам
double BUp = (VUp[0]*NUp[1] - VUp[1]*NUp[0])/(NUp[1] - NUp[0]); // Коэф. B для прямой
double K = (VUp[1] - BUp)/NUp[1]; // Коэффициент K из уравнения прямой Y = K*X + B
// Расчет коэффициента B для нижней границы канала (B = Y - K*X, Y = VDn, X = NDn)
double BDn = (VDn[0] - K*NDn[0]);
}
// - 3 - ============================== Окончание блока =================================
// - 4 - ========================= Строим канал по нижним фракталам =====================
if (DnCnt == 2)
{
// Параметры отображение канала
Time1 = Time[NDn[1]];
Time2 = Time[NDn[0]];
Time3 = Time[NUp[0]];
Price1 = VDn[1];
Price2 = VDn[0];
Price3 = VUp[0];
// Расчет нижней границы канала по двум нижним фракталам
BDn = (VDn[0]*NDn[1] - VDn[1]*NDn[0])/(NDn[1] - NDn[0]); // Коэффициент B для прямой
K = (VDn[1] - BDn)/NDn[1]; // Коэффициент K из уравнения прямой Y = K*X + B
// Расчет коэффициента B для верхней границы канала (B = Y - K*X, Y = VUp, X = NUp)
BUp = (VUp[0] - K*NUp[0]);
}
// - 4 - ============================== Окончание блока =================================
В данном случае каналы разделяются не на восходящие и нисходящие, а на каналы, построенные по верхним и нижним фракталам. Блок 3 как раз занимается каналами, у которых две опорные точки являются верхними фракталами. Это определяется по значению счетчика верхних фракталов UpCnt. Он должен быть равен 2, что автоматически означает равенство DnCnt единице. Переменные Time1-Time3 и Price1-Price3 подготавливаются для последующего использования в функции ShowChannel, которая отображает сам канал и границы ЗУК. В качестве первых двух координат берутся время и значения верхних фракталов, а в качестве третьей координаты используется единственный найденный нижний фрактал.
Следующим шагом блока 3 является расчет коэффициентов К и B, участвующих в уравнении прямой. Так как все шесть интересующих нас линий (верхняя и нижняя границы канала, плюс четыре линии ЗУК) параллельны друг другу, то коэффициент К у них одинаковый. Разница будет лишь в коэффициентах B. Сначала рассчитывается коэффициент B для верхней границы канала - BUp. Зная ее значение, уже можно будет рассчитать К. А затем по значению К находится коэффициент B нижней границы канала - BDn.
Четвертый блок идентичен третьему. В нем производятся расчеты, если две опорные точки канала являются нижними фракталами (DnCnt равен 2). Поэтому в качестве первых двух координат канала (Time1, Price1, Tim2, Price2) используются значения двух нижних фракталов. В качестве третьей координаты выступает значение единственного верхнего фрактала. Далее рассчитывается коэффициент B для нижней границы канала, по которому впоследствии рассчитывается угол наклона прямой К. Зная значение К, становится возможным рассчитать коэффициент B для верхней границы канала, чем и заканчивается четвертый блок.
Последние два блока функции GetSignal отображают канал и ЗУК, а также генерируют сигналы установки отложенных ордеров:
double CW = (BUp - BDn)*(ZUK/100.0); // Ширина ЗУК в процентах канала
// - 5 - ================== Расчет коэффициентов для ЗУК ================================
// Расчет коэффициентов B для четырех линий: 1) ЗУК выше верхней границы канала;
// 2) ЗУК ниже верхней границы канала; 3) ЗУК выше нижней границы канала; 4) ЗУК ниже
// нижней границы канала
double BZUKUpUp = BUp + CW; // Коэф. B для линии выше верхней границы канала
double BZUKUpDn = BUp - CW; // Коэф. B для линии ниже верхней границы канала
double BZUKDnUp = BDn + CW; // Коэф. B для линии выше нижней границы канала
double BZUKDnDn = BDn - CW; // Коэф. B для линии ниже нижней границы канала
ShowChannel(Time1, Price1, Time2, Price2, Time3, Price3, Price1 + CW, Price2 + CW,
Price3 - CW, Price1 - CW, Price2 - CW, Price3 + CW, IFc(K < 0, Blue, Red));
// - 5 - ============================== Окончание блока =================================
// - 6 - ============================= Генерация сигналов ===============================
// Пробой линии сопротивления - сигнал BUY
if (Close > (Bar+1)*K+BZUKUpUp &&//закрытие бара выше точки UpUp на этой же свече
Close[Bar+2] <= (Bar+2)*K + BZUKUpUp) // а предыдущая свеча закрылась ниже
{
BuyLevel = High[Bar+1]; // уровень входа - максимум первой свечи
SellLevel = 0;
Target = (Bar+1)*K+BUp + (BUp - BDn); // при любом канале цель - двойной канал
Signal = 1;
}
// Отбой от линии поддержки - сигнал BUY
if (Close[Bar+1] > (Bar+1)*K+BZUKDnUp &&//закрытие бара выше точки DnUp на этой же свече
Low[Bar+1] >= (Bar+1)*K + BZUKDnDn && Low[Bar+1] < (Bar+1)*K + BZUKDnUp &&
Close[Bar+1] < (Bar+1)*K+BZUKUpDn) // а минимум находится в нижнем ЗУКе
{
BuyLevel = High[Bar+1]; // уровень входа - максимум первой свечи
SellLevel = 0;
if (K < 0) //при трендовом сигнале цель - нижняя граница верхнего ЗУК
Target = (Bar+1)*K+BZUKUpDn;
else // если сигнал противотрендовый, то цель - середина канала
Target = (Bar+1)*K+BDn + (BUp - BDn)/2;
Signal = 2;
}
// Пробой линии поддержки - сигнал SELL
if (Close[Bar+1] < (Bar+1)*K+BZUKDnDn &&//закрытие бара ниже точки DnDn на этой же свече
Close[Bar+2] >= (Bar+2)*K + BZUKDnDn) // а предыдущая свеча закрылась выше
{
SellLevel = Low[Bar+1]; // уровень входа - минимум первой свечи
BuyLevel = 0;
Target = (Bar+1)*K+BDn - (BUp - BDn); // при любом канале цель - двойной канал
Signal = -1;
}
// Отбой от линии сопротивления - сигнал SELL
if (Close[Bar+1] < (Bar+1)*K+BZUKUpDn &&//закрытие бара ниже точки UpDn на этой же свече
High[Bar+1]>=(Bar+1)*K + BZUKUpDn && High[Bar+1] < (Bar+1)*K + BZUKUpUp &&
Close[Bar+1] > (Bar+1)*K+BZUKDnUp) // а максимум находится в верхнем ЗУКе
{
SellLevel = Low[Bar+1]; // уровень входа - минимум первой свечи
BuyLevel = 0;
if (K > 0) //при трендовом сигнале цель - верхняя граница нижнего ЗУК
Target = (Bar+1)*K+BZUKDnUp;
else // если сигнал противотрендовый, то цель - середина канала
Target = (Bar+1)*K+BDn + (BUp - BDn)/2;
Signal = -2;
}
// - 6 - ============================== Окончание блока =================================
}
В первой строке, не входящей ни в один из блоков, рассчитывается ширина ЗУК (переменная CW), выраженная в процентах от ширины канала. В расчете участвует входной параметр эксперта ZUK, при помощи которого пользователь имеет возможность изменять ширину ЗУК.
В пятом блоке рассчитываются коэффициенты B каждой из четырех прямых, относящихся к ЗУК. Названия переменных соответствующие и понятны без описания. Завершает пятый блок вызов функции ShowChannel, которая отображает сам канал и ЗУК, что дает наглядное представление о происходящих событиях.
Шестой блок логически состоит из четырех подблоков. Каждый подблок соответствует одному варианту торговли: два варианта торговли на отбой внутрь канала и два варианта торговли на пробой за пределы канала. Первым следует вариант пробития линии сопротивления. Формально он описывается так: последняя свеча закрылась выше ЗУК, а предыдущая ей свеча закрылась ниже или на уровне верхней линии ЗУК. При выполнении этого условия BuyLevel заполняется значением максимума свечи, закрытой выше ЗУК, а Target указывает на уровень двойного канала, отложенного от существующего вверх. Код сигнала пробития линии сопротивления 1.
Следующий подблок имеет код сигнала 2 - отбой от линии поддержки. Его формальное описание: закрытие последней свечи выше верхней линии ЗУК, опоясывающего нижнюю границу канала, но не выше нижней линии ЗУК, относящейся к верхней границе канала, то есть закрытие должно быть внутри канала и вне ЗУК. При этом минимум свечи должен находиться внутри нижнего ЗУК, а именно: выше нижней линии ЗУК, опоясывающей нижнюю границу канала, и ниже верхней линии ЗУК, относящейся к нижней границе канала. Значение цены открытия позиции BuyLevel и в этом случае устанавливается равным максимуму последней свечи. А вот цель Target принимает разные значения, в зависимости от наклона линий канала.
Наклон линий определяется коэффициентом К. Из курса геометрии многим должно быть известно, что если К положительное, то прямая является восходящей, а если отрицательное, то - нисходящей. Но в нашем случае декартовая система координат получилась "перевернутой" по оси абсцисс. Поэтому при положительном К канал будет нисходящим, а при отрицательном К - восходящим. В связи с этим Target принимает значение нижней линии ЗУК, относящейся к верхней границе канала, если К < 0 (то есть сигнал у нас по тренду), и средней линии канала, если К > 0 (сигнал против тренда).
Подобным образом работают подблоки по генерации сигналов пробоя линии поддержки и отбоя от линии сопротивления, которые соответственно используют коды сигнала -1 и -2.
Использование полученных данных производится в функции start. Она хоть и не менее громоздкая, чем GetSignal, но воспринимается проще. Поэтому приводим ее целиком:
//+-------------------------------------------------------------------------------------+
//| Функция START эксперта |
//+-------------------------------------------------------------------------------------+
int start()
{
// - 1 - == Разрешено ли советнику работать? ===========================================
if (!Activate || FatalError) // Отключается работа советника, если функция
return(0); // init завершилась с ошибкой или имела место фатальная ошибка
// - 1 - ============================= Окончание блока ==================================
// - 2 - == Сбор информации об условиях торговли ========================================
Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд
StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов
FreezeLevel = ND(MarketInfo(Symbol(), MODE_FREEZELEVEL)*Point); // уровень заморозки
// - 2 - ============================= Окончание блока ==================================
// - 3 - =================== Оптимизация вычислений и генерация сигнала =================
if (LastTrans == Time[0]) // Все действия на данной свече выполнены,
return(0); // до следующей бездействуем
if (LastBar != Time[0])
{
GetSignal(0); // Рассчитываем сигнал,
LastBar = Time[0]; // но не чаще одного раза за свечу
}
// - 3 - ============================= Окончание блока ==================================
// - 4 - ============================ Установка ордеров =================================
if (BuyLevel > 0) // активен один из Buy-сигналов
{
GetOrdersInfo(); // Сбор информации о своих ордерах
// - 4.1 - ===================== Расчет цены открытия ордера ======================
if (BuyLevel - Bid <= StopLevel) // Цена открытия ордера не должна быть слишком
double Price = NP(Ask + StopLevel + Tick); // близко к текущей цене
else
Price = NP(BuyLevel + Spread + Tick);
// - 4.1 - ============================ Окончание блока ===========================
// - 4.2 - ============== Проверка противоположного ордера или позиции ============
if (!CheckStopOfPos(SellTicket, Price)) // Установка стопа позиции Sell
return(0);
if (!CheckPresentOrder(SSTicket)) // Удаление ордера Sell Stop
return(0);
// - 4.2 - ============================ Окончание блока ===========================
// - 4.3 - ============== Установка или коррекция ордера Buy Stop =================
if (BuyTicket < 0) // Если позиция Buy не существует
if (!CheckOpenPos(BSTicket, Price)) // и нет ордера Buy Stop (если есть, то
if (Target - Price > StopLevel) // проверяем цену открытия)
if (OpenOrderCorrect(OP_BUYSTOP, Price, 0, Target) != 0)
return(0);
else
// - 4.3.1 - ======= Вывод сигнала с описанием действий советника =========
if (Signal == 1)
{
if (UseAlert)
Alert("Ордер BUYSTOP установлен по пробою линии сопротивления");
Print("Ордер BUYSTOP установлен по пробою линии сопротивления");
}
else
{
if (UseAlert)
Alert("Ордер BUYSTOP установлен по отбою от линии поддержки");
Print("Ордер BUYSTOP установлен по отбою от линии поддержки");
}
// - 4.3.1 - ================= Окончание блока ============================
// - 4.3 - ============================ Окончание блока ===========================
}
if (SellLevel > 0) // Активен один из Sell-сигналов
{
GetOrdersInfo(); // Сбор информации о своих ордерах
// - 4.4 - ===================== Расчет цены открытия ордера ======================
if (Bid - SellLevel <= StopLevel) // Цена открытия ордера не должна быть слишком
Price = NP(Bid - StopLevel - Tick); // близко к текущей цене
else
Price = NP(SellLevel - Tick);
// - 4.4 - ============================ Окончание блока ===========================
// - 4.5 - ============== Проверка противоположного ордера или позиции ============
if (!CheckStopOfPos(BuyTicket, Price)) // Установка уровня стопа для позиции BUY
return(0);
if (!CheckPresentOrder(BSTicket)) // Удаление ордера Buy Stop
return(0);
// - 4.5 - ============================ Окончание блока ===========================
// - 4.6 - ============ Установка или коррекция ордера Sell Stop ==================
if (SellTicket < 0) // Если позиция Sell не существует
if (!CheckOpenPos(SSTicket, Price)) // и нет ордера Sell Stop (если есть, то
if (Price - Target > StopLevel) // проверяем цену открытия)
if (OpenOrderCorrect(OP_SELLSTOP, Price, 0, Target) != 0)
return(0);
else
// - 4.6.1 - ======= Вывод сигнала с описанием действий советника =========
if (Signal == -1)
{
if (UseAlert)
Alert("Ордер SELLSTOP установлен по пробою линии поддержки");
Print("Ордер SELLSTOP установлен по пробою линии поддержки");
}
else
{
if (UseAlert)
Alert("Ордер SELLSTOP установлен по отбою от линии сопротивления");
Print("Ордер SELLSTOP установлен по отбою от линии сопротивления");
}
// - 4.6.1 - ================= Окончание блока ============================
// - 4.6 - ============================ Окончание блока ===========================
}
// - 4 - ============================= Окончание блока ==================================
LastTrans = Time[0];
return(0);
}
Описание начнем с третьего блока. LastTrans используется для сохранения времени последней успешной обработки всех имеющихся ордеров и позиций эксперта. Если это время указывает на время открытия текущей свечи, то до ее закрытия никаких действий эксперт предпринимать не будет. Этот ход позволяет существенно ускорить тестирование эксперта и снижает нагрузку на процессор во время онлайн торговли. Точно такие же функции у переменной LastBar, но они относятся к ограничению вызова GetSignal не более одного раза за свечу.
Все самое интересное происходит в четвертом блоке, который для удобства разделен на подблоки.
Первая часть четвертого блока реагирует на сигнал установки ордера Buy Stop (BuyLevel > 0). В этом случае происходит обращение к функции GetOrdersInfo, которая находит все позиции Buy и Sell эксперта, а также ордера Buy Stop и Sell Stop, записывая номера их тикетов в соответствующие переменные. После этого блок 4.1 проверяет корректность указанной цены открытия ордера BuyLevel, которая может быть слишком близко от текущей цены (в этом случае устанавливается ближайшая возможная цена).
Далее в игру вступает подблок 4.2, обязанностью которого является слежение за противоположными ордерами - Sell и SellStop. В случае присутствия Sell, уровень стопа позиции переносится на цену открытия ордера Buy Stop. Это делает функция CheckStopOfPos. Она выполняется с каждым новым тиком до тех пор, пока стоп все же не будет перенесен на нужный уровень. Далее, если Sell не существует, то проверяется наличие ордера Sell Stop, наличие которого в рынке вовсе не нужно, ведь у нас сигнал BUY. Проверкой и удалением ордера занимается функция CheckPresentOrder, которая тоже разрешает дальнейшее исполнение эксперта только в случае успеха.
Подблок 4.3 непосредственно устанавливает ордер Buy Stop. Но сначала проверяется существование позиции BUY (если существует, то BuyTicket > 0). Если она уже имеется, то никаких действий эксперт не совершает. Если же BUY нет, то проверяется существование ордера Buy Stop и цена его открытия сверяется с необходимой. Это производится в функции CheckOpenPos. Если же и Buy Stop нет, то происходит установка ордера и вывод соответствующих сообщений.
В подобной последовательности работают подблоки 4.4-4.6, обрабатывающие сигнал установки ордера Sell Stop. Только в них устанавливается стоп для позиции BUY и удаляется существующий Buy Stop для того, чтобы установить Sell Stop.
Функции CheckStopOfPos, CheckPresentOrder и CheckOpenPos тривиальны и в особых комментариях не нуждаются. Перейдем к тестированию советника.
Как уже упоминалось выше, затронутая сегодня тема является очень обширной и на ее развитие планируется еще как минимум одна статья. Поэтому полученный эксперт ни в коем случае нельзя воспринимать как законченный продукт. Его основное слабое место - отсутствие уровня стопа. Хотя у более придирчивого трейдера возникнет еще немало, кроме этого недостатка, замечаний.
В "чистом виде", то есть с такими настройками, которые были предложены самим автором стратегии, хороших результатов от эксперта добиться не удалось. А вот при подборе параметра ZUK некоторые тесты выглядят многообещающе. Все приведенные ниже рисунки показывают кривые баланса после тестирования эксперта на таймфрейме H4 и периоде истории 01.01.2009 - 15.01.2010.
Для валютной пары EURUSD наилучшие результаты показаны при ширине ЗУК = 5% (см. рис. 7).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/EURUSD.gif
Рис. 7. - График кривой баланса, получаемый при тестировании советника на валютной паре EURUSD.
В данном случае даже оптимизация эксперта не помогла. Чистая прибыль так и не была достигнута, а кривая баланса уж слишком "кривая".
Валютная пара USDCHF показала себя гораздо лучше. Этот результат зафиксирован при ZUK = 12, совсем недалеко от авторского значения (см. рис. 8).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/USDCHF.gif
Рис. 8. - График кривой баланса, получаемый при тестировании советника на валютной паре USDCHF.
Чистая прибыль составила 1016.38 долларов при максимальной просадке 593.94 доллара. В итоге фактор восстановления почти добрался до двух - 1.71. К тому же, вид кривой баланса довольно стабильный и даже в зоне потенциального убытка (вторая половина графика) убыток не дошел до огромной величины.
Валютная пара GBPUSD дала ростки при ZUK = 23 (см. рис. 9).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/GBPUSD.gif
Рис. 9. - График кривой баланса, получаемый при тестировании советника на валютной паре GBPUSD.
Чистая прибыль 1429.85, вот только максимальная просадка тоже велика - 1283.45. Фактор восстановления чуть больше единицы. Но вновь радует довольно красивый вид кривой баланса, который подпорчен лишь началом. Еще один интересный показатель - в среднем на каждые три подряд прибыльные сделки приходится всего одна убыточная.
Валютная пара USDJPY попыталась что-то показать при ZUK = 11 (см. рис. 10).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/USDJPY.gif
Рис. 10. - График кривой баланса, получаемый при тестировании советника на валютной паре USDJPY.
Даже по графику видно, что чистая прибыль отрицательная. К тому же, особых попыток для достижения приличного заработка не было сделано.
Итак, до приемлемых результатов мы пока не добрались. Но это лишь начало пути, все "полезные нововведения", еще впереди.
[B]Доработка стратегии для использования в AutoGraf 4.0
Для возможности запуска стратегии в AutoGraf 4.0 эксперт переделывается довольно просто. Ведь у него имеется всего один значимый параметр - ZUK. Чтобы не отягощать пользователя поиском нужно параметра AT_1 для установки нужной ширины ZUK, вынесем его настройку непосредственно на панель управления AG. Это будет значение параметра Ds (дистанция). В результате, трейдер сможет менять ширину ЗУК "на ходу". Правда, с некоторой оговоркой. Реакция стратегии на новое значение наступит только на следующей свече.
Для запуска советника из-под AutoGraf 4.0 совершите такие шаги:
Воспользуйтесь ссылкой Файлы стратегий для Autograf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/49_ag/AG_Chuvashovs_Channels.zip) и полученный архив распакуйте в папку MT4\experts\libraries. Запустите AutoGraf. Для получения похожих с тестами результатов работы выставьте объем открываемой позиции (Lots) 0.1, а Ds 5, 12, 23 или 11 соответственно для EURUSD, USDHF, GBPUSD или USDJPY. Выберите стратегию №3. Для этого передвиньте вверх значок So и среди названий стратегий найдите значок S3, который также потяните вверх. Запустите функцию автоматической торговли, передвинув значок AT в верхнее положение.
Использование полученного советника рекомендуется только в полуавтоматическом режиме под присмотром трейдера и после всестороннего изучения слабых и сильных сторон стратегии.
Советник ChuvashovsChannels_Expert (http://www.forextrade.ru/media/Image/MQLabs/49_ag/ChuvashovsChannels_Expert.mq4)
Файлы стратегии для AutoGraf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/49_ag/AG_Chuvashovs_Channels.zip)
Развернутые результаты тестирования эксперта (http://www.forextrade.ru/media/Image/MQLabs/49_ag/Test.zip)
Перейдем же непосредственно к рассмотрению системы. ТААЧ вновь оперирует двумя линиями, но, в отличие от стратегии "Вилка Чувашова", базируется на образовании каналов. Одна из методик построения каналов уже была рассмотрена в прошлогодней статье Откат в тренде. ТААЧ отличается и от нее. Согласно ТААЧ, каналы существуют в любом месте графика, а не только при выполнении определенных условий. В качестве опорных точек канала используются три последних фрактала. Единственное условие - один из трех фракталов должен отличаться по типу от других двух. Например, два верхних фрактала и один нижний или два нижних фрактала и один верхний. Если подряд следует три одинаковых по типу фрактала, то из них берутся только первые два, и поиск продолжается до нахождения первого фрактала другого типа.
Три фрактала - это три точки, по которым можно построить две параллельные линии. Одну линию строим по двум фракталам, а вторую по третьему фракталу и углу наклона первой линии так, чтобы вторая линия была параллельная первой. В результате выходит, что можно построить только четыре типа каналов:
восходящий канал, который в качестве первых двух опорных точек использует верхние фракталы (рис. 1) восходящий канал, который в качестве первых двух опорных точек использует нижние фракталы (рис. 2) нисходящий канал, который в качестве первых двух опорных точек использует верхние фракталы (рис. 3) нисходящий канал, который в качестве первых двух опорных точек использует нижние фракталы (рис. 4)
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_11.gif
Рис. 1. - Восходящий канал, построенный по двум верхним фракталам. http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_12.gif
Рис. 2. - Восходящий канал, построенный по двум нижним фракталам
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_13.gif
Рис. 3. - Нисходящий канал, построенный по двум верхним фракталам.http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_14.gif
Рис. 4. - Нисходящий канал, построенный по двум нижним фракталам
Полученные границы канала выступают в роли линий поддержки и сопротивления. Поэтому относительно каждой из них могут быть применены две тактики торговли: пробойная и отбойная. Таким образом, если совершено пробитие верхней границы канала (сопротивления), то нужно покупать. Если произошел отбой от линии сопротивления, то нужно продавать. То же самое справедливо для нижней линии канала (поддержки), только наоборот. Если произошел пробой линии, то нужно продавать, если отбой, то покупать.
Дальше следует довольно важный момент - что считать пробитием или отбоем от линии? В стратегии ТААЧ специально для четкого определения этих понятий вводится термин Зона Условного Касания (ЗУК). Границы ЗУК располагаются по обе стороны от каждой линии канала на расстоянии 10%, взятой от ширины канала. В результате отбоем (см. рис. 5) считается нахождение максимума или минимума свечи в ЗУК, а закрытие свечи вне ЗУК, но внутри канала.
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_15.gif
Рис. 5. - Отбой от линии поддержки
По аналогии, пробой линии - нахождение одного из экстремумов свечи внутри канала (или в ЗУК), а закрытие свечи вне пределов ЗУК (см. рис. 6)
http://www.forextrade.ru/media/Image/MQLabs/49_ag/figure_16.gif
Рис. 6. - Пробой линии поддержки
Описанные правила помогают всего лишь определиться с сигнальной свечей. А вот пробой сигнальной свечи в направлении отбоя или пробоя уже является сигналом для открытия позиции. Поэтому после идентификации сигнальной свечи будем откладывать ордер на максимум (минимум) свечи.
С ценой входа в рынок мы определились. А вот с целями - нет. ТААЧ имеет ответ и на этот вопрос. При отбое от линии возможно два типа целей:
Если направление отбоя совпадает с направлением канала (отбой от нижней границы восходящего канала или отбой от верхней границы нисходящего канала), то целью является противоположная граница ЗУК. Если направление отбоя не совпадает с направлением канала (отбой от верхней границы восходящего канала или отбой от нижней границы нисходящего канала), то целью является середина канала.
Цель при пробое канала не разделяется на варианты. Это всегда будет граница двойного канала. Например, при пробое линии поддержки нужно от нижней границы канала отложить ширину имеющегося канала вниз.
А вот насчет стопов для позиции стратегия ТААЧ молчит. Поэтому сегодняшняя реализация стратегии не будет устанавливать уровни стопа, так как на данный момент круг ее применения будет ограничен тестером стратегий.
Приступая к реализации, по обыкновению начнем с функции GetSignal. Но так как она получилась очень объемной (более 100 строк), рассмотрим ее поблочно:
// - 1 - =========================== Инициализация значений =============================
BuyLevel = 0; SellLevel = 0; // Уровни входа не определены, значит, вилки нет
double VUp[2], VDn[2]; // Значения верхних и нижних фракталов
int NUp[2], NDn[2]; // Соответствующие номера баров, на которых найдены фракталы
int UpCnt = 0, DnCnt = 0; // Счетчики количества верхних и нижних фракталов
ArrayInitialize(VUp, 0);
ArrayInitialize(VDn, 0);
// - 1 - ============================== Окончание блока =================================
// - 2 - ====== Поиск трех последовательно понижающихся или повышающихся фракталов ======
for (int i = Bar+3; i < Bars && UpCnt+DnCnt < 3; i++)
{
double Fup = iFractals(Symbol(), 0, MODE_UPPER, i);
double Fdn = iFractals(Symbol(), 0, MODE_LOWER, i);
// - 2.1 - ================== Сохранение верхних фракталов ==========================
if (Fup != 0 && UpCnt != 2)
{
VUp[UpCnt] = Fup; // Сохраняем значение фрактала
NUp[UpCnt] = i; // Сохраняем номер бара, на котором найден фрактал
UpCnt++; // Увеличение счетчика верхних фракталов
}
// - 2.1 - ========================== Окончание блока ===============================
// - 2.2 - ================== Сохранение нижних фракталов ===========================
if (Fdn != 0 && DnCnt != 2)
{
VDn[DnCnt] = Fdn; // Сохраняем значение фрактала
NDn[DnCnt] = i; // Сохраняем номер бара, на котором найден фрактал
DnCnt++; // Увеличение счетчика нижних фракталов
}
// - 2.2 - ========================== Окончание блока ===============================
}
// - 2 - ============================== Окончание блока =================================
if (i == Bars) return;
Первый блок затруднений не вызывает, обычная инициализация значений переменных. Как и в прошлом эксперте по стратегии "Вилка Чувашова", используются переменные BuyLevel и SellLevel, ненулевое значение которых свидетельствует о наличии сигнала для установки отложенного ордера и указывает цену Bid для входа. Точно также как и в "Вилке Чувашова", используются массивы VUp, VDn, NUp и NDn. Только на этот раз они двумерные, так как нам всего потребуется три фрактала, а не три фрактала каждого типа.
Во втором блоке впервые встречается переменная Bar. Она является входным параметром функции GetSignal. Переменная задает номер бара, относительно которого производится нахождение и отображение канала. В данном советнике это дополнение не особо нужно и введено исключительно с прицелом на следующие версии советника.
Назначением второго блока является поиск фракталов. Как уже замечалось выше, требуется именно три фрактала, но такие, чтобы среди них было два типа фракталов. Блок устроен таким образом, что если после нахождения двух фракталов одинакового типа находится еще один такой же, то он игнорируется. Это достигается запретом приращения счетчиков UpCnt и DnCnt, если они достигли 2.
Последняя строка, не относящаяся ни к одному из блоков, страхует эксперт от слишком короткой истории котировок, доступной терминалу.
Следующие два блока производят расчеты каналов на основании полученных данных:
// - 3 - ====================== Строим канал по верхним фракталам =======================
if (UpCnt == 2)
{
// Параметры отображение канала
datetime Time1 = Time[NUp[1]];
datetime Time2 = Time[NUp[0]];
datetime Time3 = Time[NDn[0]];
double Price1 = VUp[1];
double Price2 = VUp[0];
double Price3 = VDn[0];
// Расчет верхней границы канала по двум верхним фракталам
double BUp = (VUp[0]*NUp[1] - VUp[1]*NUp[0])/(NUp[1] - NUp[0]); // Коэф. B для прямой
double K = (VUp[1] - BUp)/NUp[1]; // Коэффициент K из уравнения прямой Y = K*X + B
// Расчет коэффициента B для нижней границы канала (B = Y - K*X, Y = VDn, X = NDn)
double BDn = (VDn[0] - K*NDn[0]);
}
// - 3 - ============================== Окончание блока =================================
// - 4 - ========================= Строим канал по нижним фракталам =====================
if (DnCnt == 2)
{
// Параметры отображение канала
Time1 = Time[NDn[1]];
Time2 = Time[NDn[0]];
Time3 = Time[NUp[0]];
Price1 = VDn[1];
Price2 = VDn[0];
Price3 = VUp[0];
// Расчет нижней границы канала по двум нижним фракталам
BDn = (VDn[0]*NDn[1] - VDn[1]*NDn[0])/(NDn[1] - NDn[0]); // Коэффициент B для прямой
K = (VDn[1] - BDn)/NDn[1]; // Коэффициент K из уравнения прямой Y = K*X + B
// Расчет коэффициента B для верхней границы канала (B = Y - K*X, Y = VUp, X = NUp)
BUp = (VUp[0] - K*NUp[0]);
}
// - 4 - ============================== Окончание блока =================================
В данном случае каналы разделяются не на восходящие и нисходящие, а на каналы, построенные по верхним и нижним фракталам. Блок 3 как раз занимается каналами, у которых две опорные точки являются верхними фракталами. Это определяется по значению счетчика верхних фракталов UpCnt. Он должен быть равен 2, что автоматически означает равенство DnCnt единице. Переменные Time1-Time3 и Price1-Price3 подготавливаются для последующего использования в функции ShowChannel, которая отображает сам канал и границы ЗУК. В качестве первых двух координат берутся время и значения верхних фракталов, а в качестве третьей координаты используется единственный найденный нижний фрактал.
Следующим шагом блока 3 является расчет коэффициентов К и B, участвующих в уравнении прямой. Так как все шесть интересующих нас линий (верхняя и нижняя границы канала, плюс четыре линии ЗУК) параллельны друг другу, то коэффициент К у них одинаковый. Разница будет лишь в коэффициентах B. Сначала рассчитывается коэффициент B для верхней границы канала - BUp. Зная ее значение, уже можно будет рассчитать К. А затем по значению К находится коэффициент B нижней границы канала - BDn.
Четвертый блок идентичен третьему. В нем производятся расчеты, если две опорные точки канала являются нижними фракталами (DnCnt равен 2). Поэтому в качестве первых двух координат канала (Time1, Price1, Tim2, Price2) используются значения двух нижних фракталов. В качестве третьей координаты выступает значение единственного верхнего фрактала. Далее рассчитывается коэффициент B для нижней границы канала, по которому впоследствии рассчитывается угол наклона прямой К. Зная значение К, становится возможным рассчитать коэффициент B для верхней границы канала, чем и заканчивается четвертый блок.
Последние два блока функции GetSignal отображают канал и ЗУК, а также генерируют сигналы установки отложенных ордеров:
double CW = (BUp - BDn)*(ZUK/100.0); // Ширина ЗУК в процентах канала
// - 5 - ================== Расчет коэффициентов для ЗУК ================================
// Расчет коэффициентов B для четырех линий: 1) ЗУК выше верхней границы канала;
// 2) ЗУК ниже верхней границы канала; 3) ЗУК выше нижней границы канала; 4) ЗУК ниже
// нижней границы канала
double BZUKUpUp = BUp + CW; // Коэф. B для линии выше верхней границы канала
double BZUKUpDn = BUp - CW; // Коэф. B для линии ниже верхней границы канала
double BZUKDnUp = BDn + CW; // Коэф. B для линии выше нижней границы канала
double BZUKDnDn = BDn - CW; // Коэф. B для линии ниже нижней границы канала
ShowChannel(Time1, Price1, Time2, Price2, Time3, Price3, Price1 + CW, Price2 + CW,
Price3 - CW, Price1 - CW, Price2 - CW, Price3 + CW, IFc(K < 0, Blue, Red));
// - 5 - ============================== Окончание блока =================================
// - 6 - ============================= Генерация сигналов ===============================
// Пробой линии сопротивления - сигнал BUY
if (Close > (Bar+1)*K+BZUKUpUp &&//закрытие бара выше точки UpUp на этой же свече
Close[Bar+2] <= (Bar+2)*K + BZUKUpUp) // а предыдущая свеча закрылась ниже
{
BuyLevel = High[Bar+1]; // уровень входа - максимум первой свечи
SellLevel = 0;
Target = (Bar+1)*K+BUp + (BUp - BDn); // при любом канале цель - двойной канал
Signal = 1;
}
// Отбой от линии поддержки - сигнал BUY
if (Close[Bar+1] > (Bar+1)*K+BZUKDnUp &&//закрытие бара выше точки DnUp на этой же свече
Low[Bar+1] >= (Bar+1)*K + BZUKDnDn && Low[Bar+1] < (Bar+1)*K + BZUKDnUp &&
Close[Bar+1] < (Bar+1)*K+BZUKUpDn) // а минимум находится в нижнем ЗУКе
{
BuyLevel = High[Bar+1]; // уровень входа - максимум первой свечи
SellLevel = 0;
if (K < 0) //при трендовом сигнале цель - нижняя граница верхнего ЗУК
Target = (Bar+1)*K+BZUKUpDn;
else // если сигнал противотрендовый, то цель - середина канала
Target = (Bar+1)*K+BDn + (BUp - BDn)/2;
Signal = 2;
}
// Пробой линии поддержки - сигнал SELL
if (Close[Bar+1] < (Bar+1)*K+BZUKDnDn &&//закрытие бара ниже точки DnDn на этой же свече
Close[Bar+2] >= (Bar+2)*K + BZUKDnDn) // а предыдущая свеча закрылась выше
{
SellLevel = Low[Bar+1]; // уровень входа - минимум первой свечи
BuyLevel = 0;
Target = (Bar+1)*K+BDn - (BUp - BDn); // при любом канале цель - двойной канал
Signal = -1;
}
// Отбой от линии сопротивления - сигнал SELL
if (Close[Bar+1] < (Bar+1)*K+BZUKUpDn &&//закрытие бара ниже точки UpDn на этой же свече
High[Bar+1]>=(Bar+1)*K + BZUKUpDn && High[Bar+1] < (Bar+1)*K + BZUKUpUp &&
Close[Bar+1] > (Bar+1)*K+BZUKDnUp) // а максимум находится в верхнем ЗУКе
{
SellLevel = Low[Bar+1]; // уровень входа - минимум первой свечи
BuyLevel = 0;
if (K > 0) //при трендовом сигнале цель - верхняя граница нижнего ЗУК
Target = (Bar+1)*K+BZUKDnUp;
else // если сигнал противотрендовый, то цель - середина канала
Target = (Bar+1)*K+BDn + (BUp - BDn)/2;
Signal = -2;
}
// - 6 - ============================== Окончание блока =================================
}
В первой строке, не входящей ни в один из блоков, рассчитывается ширина ЗУК (переменная CW), выраженная в процентах от ширины канала. В расчете участвует входной параметр эксперта ZUK, при помощи которого пользователь имеет возможность изменять ширину ЗУК.
В пятом блоке рассчитываются коэффициенты B каждой из четырех прямых, относящихся к ЗУК. Названия переменных соответствующие и понятны без описания. Завершает пятый блок вызов функции ShowChannel, которая отображает сам канал и ЗУК, что дает наглядное представление о происходящих событиях.
Шестой блок логически состоит из четырех подблоков. Каждый подблок соответствует одному варианту торговли: два варианта торговли на отбой внутрь канала и два варианта торговли на пробой за пределы канала. Первым следует вариант пробития линии сопротивления. Формально он описывается так: последняя свеча закрылась выше ЗУК, а предыдущая ей свеча закрылась ниже или на уровне верхней линии ЗУК. При выполнении этого условия BuyLevel заполняется значением максимума свечи, закрытой выше ЗУК, а Target указывает на уровень двойного канала, отложенного от существующего вверх. Код сигнала пробития линии сопротивления 1.
Следующий подблок имеет код сигнала 2 - отбой от линии поддержки. Его формальное описание: закрытие последней свечи выше верхней линии ЗУК, опоясывающего нижнюю границу канала, но не выше нижней линии ЗУК, относящейся к верхней границе канала, то есть закрытие должно быть внутри канала и вне ЗУК. При этом минимум свечи должен находиться внутри нижнего ЗУК, а именно: выше нижней линии ЗУК, опоясывающей нижнюю границу канала, и ниже верхней линии ЗУК, относящейся к нижней границе канала. Значение цены открытия позиции BuyLevel и в этом случае устанавливается равным максимуму последней свечи. А вот цель Target принимает разные значения, в зависимости от наклона линий канала.
Наклон линий определяется коэффициентом К. Из курса геометрии многим должно быть известно, что если К положительное, то прямая является восходящей, а если отрицательное, то - нисходящей. Но в нашем случае декартовая система координат получилась "перевернутой" по оси абсцисс. Поэтому при положительном К канал будет нисходящим, а при отрицательном К - восходящим. В связи с этим Target принимает значение нижней линии ЗУК, относящейся к верхней границе канала, если К < 0 (то есть сигнал у нас по тренду), и средней линии канала, если К > 0 (сигнал против тренда).
Подобным образом работают подблоки по генерации сигналов пробоя линии поддержки и отбоя от линии сопротивления, которые соответственно используют коды сигнала -1 и -2.
Использование полученных данных производится в функции start. Она хоть и не менее громоздкая, чем GetSignal, но воспринимается проще. Поэтому приводим ее целиком:
//+-------------------------------------------------------------------------------------+
//| Функция START эксперта |
//+-------------------------------------------------------------------------------------+
int start()
{
// - 1 - == Разрешено ли советнику работать? ===========================================
if (!Activate || FatalError) // Отключается работа советника, если функция
return(0); // init завершилась с ошибкой или имела место фатальная ошибка
// - 1 - ============================= Окончание блока ==================================
// - 2 - == Сбор информации об условиях торговли ========================================
Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд
StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов
FreezeLevel = ND(MarketInfo(Symbol(), MODE_FREEZELEVEL)*Point); // уровень заморозки
// - 2 - ============================= Окончание блока ==================================
// - 3 - =================== Оптимизация вычислений и генерация сигнала =================
if (LastTrans == Time[0]) // Все действия на данной свече выполнены,
return(0); // до следующей бездействуем
if (LastBar != Time[0])
{
GetSignal(0); // Рассчитываем сигнал,
LastBar = Time[0]; // но не чаще одного раза за свечу
}
// - 3 - ============================= Окончание блока ==================================
// - 4 - ============================ Установка ордеров =================================
if (BuyLevel > 0) // активен один из Buy-сигналов
{
GetOrdersInfo(); // Сбор информации о своих ордерах
// - 4.1 - ===================== Расчет цены открытия ордера ======================
if (BuyLevel - Bid <= StopLevel) // Цена открытия ордера не должна быть слишком
double Price = NP(Ask + StopLevel + Tick); // близко к текущей цене
else
Price = NP(BuyLevel + Spread + Tick);
// - 4.1 - ============================ Окончание блока ===========================
// - 4.2 - ============== Проверка противоположного ордера или позиции ============
if (!CheckStopOfPos(SellTicket, Price)) // Установка стопа позиции Sell
return(0);
if (!CheckPresentOrder(SSTicket)) // Удаление ордера Sell Stop
return(0);
// - 4.2 - ============================ Окончание блока ===========================
// - 4.3 - ============== Установка или коррекция ордера Buy Stop =================
if (BuyTicket < 0) // Если позиция Buy не существует
if (!CheckOpenPos(BSTicket, Price)) // и нет ордера Buy Stop (если есть, то
if (Target - Price > StopLevel) // проверяем цену открытия)
if (OpenOrderCorrect(OP_BUYSTOP, Price, 0, Target) != 0)
return(0);
else
// - 4.3.1 - ======= Вывод сигнала с описанием действий советника =========
if (Signal == 1)
{
if (UseAlert)
Alert("Ордер BUYSTOP установлен по пробою линии сопротивления");
Print("Ордер BUYSTOP установлен по пробою линии сопротивления");
}
else
{
if (UseAlert)
Alert("Ордер BUYSTOP установлен по отбою от линии поддержки");
Print("Ордер BUYSTOP установлен по отбою от линии поддержки");
}
// - 4.3.1 - ================= Окончание блока ============================
// - 4.3 - ============================ Окончание блока ===========================
}
if (SellLevel > 0) // Активен один из Sell-сигналов
{
GetOrdersInfo(); // Сбор информации о своих ордерах
// - 4.4 - ===================== Расчет цены открытия ордера ======================
if (Bid - SellLevel <= StopLevel) // Цена открытия ордера не должна быть слишком
Price = NP(Bid - StopLevel - Tick); // близко к текущей цене
else
Price = NP(SellLevel - Tick);
// - 4.4 - ============================ Окончание блока ===========================
// - 4.5 - ============== Проверка противоположного ордера или позиции ============
if (!CheckStopOfPos(BuyTicket, Price)) // Установка уровня стопа для позиции BUY
return(0);
if (!CheckPresentOrder(BSTicket)) // Удаление ордера Buy Stop
return(0);
// - 4.5 - ============================ Окончание блока ===========================
// - 4.6 - ============ Установка или коррекция ордера Sell Stop ==================
if (SellTicket < 0) // Если позиция Sell не существует
if (!CheckOpenPos(SSTicket, Price)) // и нет ордера Sell Stop (если есть, то
if (Price - Target > StopLevel) // проверяем цену открытия)
if (OpenOrderCorrect(OP_SELLSTOP, Price, 0, Target) != 0)
return(0);
else
// - 4.6.1 - ======= Вывод сигнала с описанием действий советника =========
if (Signal == -1)
{
if (UseAlert)
Alert("Ордер SELLSTOP установлен по пробою линии поддержки");
Print("Ордер SELLSTOP установлен по пробою линии поддержки");
}
else
{
if (UseAlert)
Alert("Ордер SELLSTOP установлен по отбою от линии сопротивления");
Print("Ордер SELLSTOP установлен по отбою от линии сопротивления");
}
// - 4.6.1 - ================= Окончание блока ============================
// - 4.6 - ============================ Окончание блока ===========================
}
// - 4 - ============================= Окончание блока ==================================
LastTrans = Time[0];
return(0);
}
Описание начнем с третьего блока. LastTrans используется для сохранения времени последней успешной обработки всех имеющихся ордеров и позиций эксперта. Если это время указывает на время открытия текущей свечи, то до ее закрытия никаких действий эксперт предпринимать не будет. Этот ход позволяет существенно ускорить тестирование эксперта и снижает нагрузку на процессор во время онлайн торговли. Точно такие же функции у переменной LastBar, но они относятся к ограничению вызова GetSignal не более одного раза за свечу.
Все самое интересное происходит в четвертом блоке, который для удобства разделен на подблоки.
Первая часть четвертого блока реагирует на сигнал установки ордера Buy Stop (BuyLevel > 0). В этом случае происходит обращение к функции GetOrdersInfo, которая находит все позиции Buy и Sell эксперта, а также ордера Buy Stop и Sell Stop, записывая номера их тикетов в соответствующие переменные. После этого блок 4.1 проверяет корректность указанной цены открытия ордера BuyLevel, которая может быть слишком близко от текущей цены (в этом случае устанавливается ближайшая возможная цена).
Далее в игру вступает подблок 4.2, обязанностью которого является слежение за противоположными ордерами - Sell и SellStop. В случае присутствия Sell, уровень стопа позиции переносится на цену открытия ордера Buy Stop. Это делает функция CheckStopOfPos. Она выполняется с каждым новым тиком до тех пор, пока стоп все же не будет перенесен на нужный уровень. Далее, если Sell не существует, то проверяется наличие ордера Sell Stop, наличие которого в рынке вовсе не нужно, ведь у нас сигнал BUY. Проверкой и удалением ордера занимается функция CheckPresentOrder, которая тоже разрешает дальнейшее исполнение эксперта только в случае успеха.
Подблок 4.3 непосредственно устанавливает ордер Buy Stop. Но сначала проверяется существование позиции BUY (если существует, то BuyTicket > 0). Если она уже имеется, то никаких действий эксперт не совершает. Если же BUY нет, то проверяется существование ордера Buy Stop и цена его открытия сверяется с необходимой. Это производится в функции CheckOpenPos. Если же и Buy Stop нет, то происходит установка ордера и вывод соответствующих сообщений.
В подобной последовательности работают подблоки 4.4-4.6, обрабатывающие сигнал установки ордера Sell Stop. Только в них устанавливается стоп для позиции BUY и удаляется существующий Buy Stop для того, чтобы установить Sell Stop.
Функции CheckStopOfPos, CheckPresentOrder и CheckOpenPos тривиальны и в особых комментариях не нуждаются. Перейдем к тестированию советника.
Как уже упоминалось выше, затронутая сегодня тема является очень обширной и на ее развитие планируется еще как минимум одна статья. Поэтому полученный эксперт ни в коем случае нельзя воспринимать как законченный продукт. Его основное слабое место - отсутствие уровня стопа. Хотя у более придирчивого трейдера возникнет еще немало, кроме этого недостатка, замечаний.
В "чистом виде", то есть с такими настройками, которые были предложены самим автором стратегии, хороших результатов от эксперта добиться не удалось. А вот при подборе параметра ZUK некоторые тесты выглядят многообещающе. Все приведенные ниже рисунки показывают кривые баланса после тестирования эксперта на таймфрейме H4 и периоде истории 01.01.2009 - 15.01.2010.
Для валютной пары EURUSD наилучшие результаты показаны при ширине ЗУК = 5% (см. рис. 7).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/EURUSD.gif
Рис. 7. - График кривой баланса, получаемый при тестировании советника на валютной паре EURUSD.
В данном случае даже оптимизация эксперта не помогла. Чистая прибыль так и не была достигнута, а кривая баланса уж слишком "кривая".
Валютная пара USDCHF показала себя гораздо лучше. Этот результат зафиксирован при ZUK = 12, совсем недалеко от авторского значения (см. рис. 8).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/USDCHF.gif
Рис. 8. - График кривой баланса, получаемый при тестировании советника на валютной паре USDCHF.
Чистая прибыль составила 1016.38 долларов при максимальной просадке 593.94 доллара. В итоге фактор восстановления почти добрался до двух - 1.71. К тому же, вид кривой баланса довольно стабильный и даже в зоне потенциального убытка (вторая половина графика) убыток не дошел до огромной величины.
Валютная пара GBPUSD дала ростки при ZUK = 23 (см. рис. 9).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/GBPUSD.gif
Рис. 9. - График кривой баланса, получаемый при тестировании советника на валютной паре GBPUSD.
Чистая прибыль 1429.85, вот только максимальная просадка тоже велика - 1283.45. Фактор восстановления чуть больше единицы. Но вновь радует довольно красивый вид кривой баланса, который подпорчен лишь началом. Еще один интересный показатель - в среднем на каждые три подряд прибыльные сделки приходится всего одна убыточная.
Валютная пара USDJPY попыталась что-то показать при ZUK = 11 (см. рис. 10).
http://www.forextrade.ru/media/Image/MQLabs/49_ag/USDJPY.gif
Рис. 10. - График кривой баланса, получаемый при тестировании советника на валютной паре USDJPY.
Даже по графику видно, что чистая прибыль отрицательная. К тому же, особых попыток для достижения приличного заработка не было сделано.
Итак, до приемлемых результатов мы пока не добрались. Но это лишь начало пути, все "полезные нововведения", еще впереди.
[B]Доработка стратегии для использования в AutoGraf 4.0
Для возможности запуска стратегии в AutoGraf 4.0 эксперт переделывается довольно просто. Ведь у него имеется всего один значимый параметр - ZUK. Чтобы не отягощать пользователя поиском нужно параметра AT_1 для установки нужной ширины ZUK, вынесем его настройку непосредственно на панель управления AG. Это будет значение параметра Ds (дистанция). В результате, трейдер сможет менять ширину ЗУК "на ходу". Правда, с некоторой оговоркой. Реакция стратегии на новое значение наступит только на следующей свече.
Для запуска советника из-под AutoGraf 4.0 совершите такие шаги:
Воспользуйтесь ссылкой Файлы стратегий для Autograf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/49_ag/AG_Chuvashovs_Channels.zip) и полученный архив распакуйте в папку MT4\experts\libraries. Запустите AutoGraf. Для получения похожих с тестами результатов работы выставьте объем открываемой позиции (Lots) 0.1, а Ds 5, 12, 23 или 11 соответственно для EURUSD, USDHF, GBPUSD или USDJPY. Выберите стратегию №3. Для этого передвиньте вверх значок So и среди названий стратегий найдите значок S3, который также потяните вверх. Запустите функцию автоматической торговли, передвинув значок AT в верхнее положение.
Использование полученного советника рекомендуется только в полуавтоматическом режиме под присмотром трейдера и после всестороннего изучения слабых и сильных сторон стратегии.
Советник ChuvashovsChannels_Expert (http://www.forextrade.ru/media/Image/MQLabs/49_ag/ChuvashovsChannels_Expert.mq4)
Файлы стратегии для AutoGraf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/49_ag/AG_Chuvashovs_Channels.zip)
Развернутые результаты тестирования эксперта (http://www.forextrade.ru/media/Image/MQLabs/49_ag/Test.zip)