PDA

View Full Version : Вилка Чувашова



Scriptong
07-01-2010, 09:43
В последние несколько месяцев широкую популярность получили различные стратегии Станислава Чувашова. На сегодняшний день советников, при помощи которых можно было бы протестировать предложенные методики, мне найти не удалось. Хотя это не значит, то их нет вовсе. Попадались лишь индикаторы.

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

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

Пересечение ценой линии зубца вилки является сигналом для открытия длинной позиции. Стоп при этом устанавливается за ближайшим DOWN-фракталом (серые стрелки вниз) и в дальнейшем подтягивается при появлении новых фракталов. Глядя на рисунок, стоит учитывать, что фрактал строится по пяти барам (два бара должны быть справа от него и два слева). Поэтому при работе онлайн говорить о формировании фрактала можно лишь спустя три бара после него. Именно поэтому две приведенные длинные позиции, открытые по одной и той же вилке, устанавливают стоп на один и тот же уровень. Хотя, как мы видим, вторая позиция должна была бы поставить стоп дальше, чем первая. Тем не менее, если первая позиция закрылась по стопу, то вторую позицию такая участь миновала. В результате, после семи изменений уровня стопа по ближайшим фракталам, цена достигла стопа, но уже намного выше цены открытия позиции, чем и была принесена прибыль.
http://www.forextrade.ru/media/Image/MQLabs/48_ag/figure_1.gif

Рис. 1. - Нисходящая вилка Чувашова - сигнал для открытия длинных позиций.

Наряду с нисходящими вилками, существуют восходящие вилки, которые являются сигналами открытия коротких позиций (см. рис. 2)
http://www.forextrade.ru/media/Image/MQLabs/48_ag/figure_2.gif

Рис. 2. - Восходящая вилка Чувашова - сигнал для открытия коротких позиций.



Восходящая вилка (красные сплошные линии) строится по трем последовательно восходящим DOWN-фракталам. По первым двум из них проводится луч, соответствующий ручке вилки. Третий фрактал должен пробить полученный луч по направлению вниз. Только тогда по последним двум DOWN-фракталам можно проводить второй луч. Пробитие зубца вилки ценой вниз дает сигнал открытия короткой позиции. Уровень стопа позиции устанавливается за ближайшим UP-фракталом и впоследствии перемещается по вновь сформированным UP-фракталам. Достижением уровня стопа позиция и закрывается.

Но, как хорошо видно на рис. 2, есть еще один момент, при котором закрывается позиция - появление противоположного сигнала. Таким образом, перед открытием новой позиции происходит закрытие противоположной позиции.

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

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

//+-------------------------------------------------------------------------------------+
//| Расчет границ канала и подсчет баров ниже и выше линии открытия |
//+-------------------------------------------------------------------------------------+
void GetSignal()
{
// - 1 - =========================== Инициализация значений =============================
BuyLevel = 0; SellLevel = 0; // Уровни входа не определены, значит, вилки нет
LastUp = 0; LastDn = 0; // Уровни последних сформированных фракталов
double VUp[3], VDn[3]; // Значения трех последовательных фракталов
int NUp[3], NDn[3]; // Соответствующие номера баров, на которых найдены фракталы
int UpCnt = 0, DnCnt = 0; // Счетчики количества найденных фракталов
ArrayInitialize(VUp, 0);
ArrayInitialize(VDn, 0);
// - 1 - ============================== Окончание блока =================================

// - 2 - ====== Поиск трех последовательно понижающихся или повышающихся фракталов ======
for (int i = 3; i < Bars; i++)
{
double Fup = iFractals(Symbol(), 0, MODE_UPPER, i);
double Fdn = iFractals(Symbol(), 0, MODE_LOWER, i);
// - 2.1 - ================== Поиск трех подряд понижающихся UP-фракталов ===========
if (Fup != 0)
{
if (LastUp == 0) LastUp = Fup; // Запоминаем первый найденный фрактал
if (UpCnt > 0 && VUp[UpCnt-1] > Fup) // Если предыдущий Up-фрактал выше найденного
{ // то начинаем поиск трех Up-фракталов заново
ArrayInitialize(VUp, 0); // Обнуление массива
UpCnt = 0; // Обнуление счетчика верхних фракталов
}
VUp[UpCnt] = Fup; // Сохраняем значение фрактала
NUp[UpCnt] = i; // Сохраняем номер бара, на котором найден фрактал
UpCnt++;
if (UpCnt == 3) break; // найдены три фрактала - прерывается цикл
}
// - 2.1 - ========================== Окончание блока ===============================

// - 2.2 - ================== Поиск трех подряд повышающихся DN-фракталов ===========
if (Fdn != 0)
{
if (LastDn == 0) LastDn = Fdn; // Запоминаем первый найденный фрактал
if (DnCnt > 0 && VDn[DnCnt-1] < Fdn) // Если предыдущий Dn-фрактал ниже найденного
{ // то начинаем поиск трех Dn-фракталов заново
ArrayInitialize(VDn, 0); // Обнуление массива
DnCnt = 0; // Обнуление счетчика нижних фракталов
}
VDn[DnCnt] = Fdn; // Сохраняем значение фрактала
NDn[DnCnt] = i; // Сохраняем номер бара, на котором найден фрактал
DnCnt++;
if (DnCnt == 3) break; // найдены три фрактала - прерывается цикл
}
// - 2.2 - ========================== Окончание блока ===============================
}
// - 2 - ============================== Окончание блока =================================

if (i == Bars) return;

// - 3 - ====== Обработка найденных трех понижающихся UP-фракталов подряд ===============
if (UpCnt == 3)
{
// Расчет линии ручки вилки, пробитие которой дает возможность построения линии
// зубца вилки
double B = (VUp[1]*NUp[2] - VUp[2]*NUp[1])/(NUp[2] - NUp[1]); // Коэф. B для прямой
double K = (VUp[2] - B)/NUp[2]; // Коэффициент K из уравнения прямой Y = K*X + B
if (VUp[0] > K*NUp[0]+B) // Если последний фрактал выше линии, то строим вилку
{
ShowFork(Time[NUp[2]], VUp[2], Time[NUp[1]], VUp[1], // Отображение вилки
Time[NUp[0]], VUp[0], ForkDnColor);
BuyLevel = (VUp[0]*NUp[1] - VUp[1]*NUp[0])/(NUp[1] - NUp[0]); // Расчет уровня
// открытия длинной позиции. Он равен значению линии зубца вилки на текущем баре
}
else
DeleteFork(); // Нет активной вилки, формируется новая. Поэтому старую удаляем
}
// - 3 - ============================== Окончание блока =================================

// - 4 - ====== Обработка найденных трех повышающихся DN-фракталов подряд ===============
if (DnCnt == 3)
{
// Расчет линии ручки вилки, пробитие которой дает возможность построения линии
// зубца вилки
B = (VDn[1]*NDn[2] - VDn[2]*NDn[1])/(NDn[2] - NDn[1]); // Коэффициент B для прямой
K = (VDn[2] - B)/NDn[2]; // Коэффициент K из уравнения прямой Y = K*X + B
if (VDn[0] < K*NDn[0]+B) // Если последний фрактал ниже линии ручки, то строим вилку
{
ShowFork(Time[NDn[2]], VDn[2], Time[NDn[1]], VDn[1], // Отображение вилки
Time[NDn[0]], VDn[0], ForkUpColor);
SellLevel = (VDn[0]*NDn[1] - VDn[1]*NDn[0])/(NDn[1] - NDn[0]); // Расчет уровня
// открытия длинной позиции. Он равен значению линии зубца вилки на текущем баре
}
else
DeleteFork(); // Нет активной вилки, новая формируется
}
// - 4 - ============================== Окончание блока =================================

return(True);
}
Функция состоит из четырех блоков. В первом производится инициализация значений переменных, четыре из которых являются видимыми для всех функций советника. Это BuyLevel (значение зубца нисходящей вилки на текущем баре), SellLevel (значение зубца восходящей вилки на текущем баре), LastUp (цена последнего сформированного UP-фрактала, которая используется для установки и перемещения стопа короткой позиции) и LastDn (цена последнего сформированного DOWN-фрактала, которая используется для установки и перемещения стопа длинной позиции). Остальные переменные необходимы только для функции GetSignal. Это два вещественных трехэлементных массива (VUp и VDn), где сохраняются значения соответствующих фракталов, и два целочисленных трехэлементных массива (NUp и NDn), в которых сохраняются номера баров, соответствующие найденным фракталам.

Задача второго блока сводится к поиску трех подряд восходящих DOWN-фракталов или трех подряд нисходящих UP-фракталов. Нахождение той или иной последовательности прекращает цикл поиска.

До третьего и четвертого блоков выполнение доходит только в случае нахождения одной из двух последовательностей фракталов. Иначе исполнение функции GetSignal прерывается.

Третий блок проверяет последнее условие для формирования нисходящей вилки - пробитие линии ручки вилки вверх последним сформированным фракталом. Его значение хранится в первом элементе массива VUp (индекс равен нулю), а номер бара - в первом элементе массива NUp. Для сравнения значений фрактала и линии ручки вилки, необходимо найти значение линии на том баре, где найден фрактал. Найти его можно, только зная коэффициенты K и B уравнения прямой, описывающей луч. Они находятся из системы уравнений, в которую в качестве X и Y подставляются значения двух известных точек - фракталов, по которым построена линия ручки вилки. После нахождения коэффициентов, в уравнение прямой подставляется номер бара фрактала NUp[0] и сравнивается со значением фрактала VUp[0]. Если фрактал выше, то можно отобразить вилку при помощи функции ShowFork и рассчитать уровень открытия длинной позиции. Уровень BuyLevel рассчитывается уже по другой прямой - линии зубца вилки. Он будет равен коэффициенту B, так как первое слагаемое в уравнении прямой для нулевого бара равно нулю. Если же фрактал VUp[0] не смог пробить уровень линии ручки вилки, то имеющаяся вилка все равно теряет свое влияние и удаляется с графика при помощи функции DeleteFork.

Подобным же образом производятся расчеты в четвертом блоке, который обрабатывает данные при нахождении трех восходящих DOWN-фракталов подряд. Только для построения линии зубца вилки значение фрактала VDn[0] должно быть ниже уровня линии ручки вилки.

Теперь рассмотрим, как используются полученные данные. Использование данных определяется алгоритмом функции start:

//+-------------------------------------------------------------------------------------+
//| Функция 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 - == Окончание блока ============================================================

FindOwnOrders(); // Сбор информации о существующих ордерах и позициях

// - 3 - =============== Расчет сигнала при окончании флэта =============================
if (LastBar != Time[0])
{
GetSignal();
LastBar = Time[0];
}
// - 3 - == Окончание блока ============================================================

// - 4 - == Открытие позиций ============================================================
if (AllWork != Time[0])
CheckTrades();
// - 4 - == Окончание блока ============================================================

return(0);
}
Функция составлена довольно лаконично и, в свою очередь, вызывает только три функции - FindOwnOrders, GetSignal и CheckTrades. Для оптимизации вычислений введены две переменные - LastBar, запоминающая время последнего расчета вилки, и AllWork, в которой сохраняется время последней успешной обработки всех имеющихся ордеров и позиций. Благодаря этим переменным, в течение одного бара только один раз производится расчет показателей вилки и проверка цен открытия ордеров и уровней стопа позиций.

Задачей функции FindOwnOrders является поиск всех своих ордеров и позиций с записью тикетов. Для этого формируются значения в переменных BuyStopTicket, BuyTicket, SellStopTicket и SellTicket. Если значение переменной меньше нуля, то соответствующая позиция или ордер не существуют.

Всю же администраторскую деятельность ведет функция CheckTrades, разбитая на три блока:

//+-------------------------------------------------------------------------------------+
//| Открытие, модификация, удаление и закрытие позиций и ордеров |
//+-------------------------------------------------------------------------------------+
void CheckTrades()
{
// - 1 - =============== Если вилки нет, то нужно удалить несработавшие ордера ==========
if (BuyLevel == 0 && SellLevel == 0)
{
if (BuyTicket > 0) // Если существует BUY, то отслеживаем его стоп
if (!ModifyStop(BuyTicket, LastDn - Tick)) // подтягивая его за последним фракталом
return;
if (SellTicket > 0)
if (!ModifyStop(SellTicket, LastUp + Spread + Tick))
return;
if (BuyStopTicket > 0 || SellStopTicket > 0)
{
if (BuyStopTicket > 0)
DeleteOrder(BuyStopTicket);
if (SellStopTicket > 0)
DeleteOrder(SellStopTicket);
return;//Вне зависимости от результата удаления заканчиваем, хотя бы для пересчета
}
}
// - 1 - ============================== Окончание блока =================================

// - 2 - =============== Существует нисходящая вилка - ждем пробоя вверх ================
if (BuyLevel > 0)
{
if (SellStopTicket > 0) // Имеющийся Sell Stop следует удалить
if (!DeleteOrder(SellStopTicket))
return;
if (SellTicket > 0) // Если открыта позиция Sell, то переносим ее стоп на уровень
if (!ModifyStop(SellTicket, BuyLevel+Spread+Tick)) // открытия Buy Stop
return;
if (BuyTicket > 0) // Если открыта позиция Buy, то переносим ее стоп за последний
{ // фрактал
ModifyStop(BuyTicket, LastDn - Tick);
return; // Выход в любом случае, так как устанавливать ордер не нужно
}
if (BuyStopTicket > 0)
{
ModifyOrder(BuyStopTicket, BuyLevel + Spread + Tick, LastDn - Tick);
return; // Выход в любом случае, так как устанавливать ордер не нужно
}
double Price = NP(BuyLevel+Spread+Tick);
if (Price - Ask > StopLevel && Bid > LastDn)
{
double TP = IF(TakeProfit == 0, 0,
IF(TakeProfit < 0, Price+(Price-LastDn+Tick)*(TargetPercent/100.0),
Price+TakeProfit*Tick));
OpenOrderCorrect(OP_BUYSTOP, Price, NP(LastDn-Tick), NP(TP));
}
}
// - 2 - ============================== Окончание блока =================================

// - 3 - =============== Существует восходящая вилка - ждем пробоя вниз =================
if (SellLevel > 0)
{
if (BuyStopTicket > 0) // Имеющийся Buy Stop следует удалить
if (!DeleteOrder(BuyStopTicket))
return;
if (BuyTicket > 0) // Если открыта позиция Buy, то переносим ее стоп на уровень
if (!ModifyStop(BuyTicket, SellLevel-Tick)) // открытия Sell Stop
return;
if (SellTicket > 0) // Если открыта позиция Sell, то переносим ее стоп за последний
{ // фрактал
ModifyStop(SellTicket, LastUp + Spread + Tick);
return; // Выход в любом случае, так как устанавливать ордер не нужно
}
if (SellStopTicket > 0)
{
ModifyOrder(SellStopTicket, SellLevel - Tick, LastUp + Spread + Tick);
return; // Выход в любом случае, так как устанавливать ордер не нужно
}
Price = NP(SellLevel-Tick);
if (Bid - Price > StopLevel && Bid < LastUp)
{
TP = IF(TakeProfit == 0, 0,
IF(TakeProfit < 0, Price-(LastUp+Spread+Tick-Price)*(TargetPercent/100.0),
Price-TakeProfit*Tick));
OpenOrderCorrect(OP_SELLSTOP, Price, NP(LastUp+Spread+Tick), NP(TP));
}
}
AllWork = Time[0];
// - 3 - ============================== Окончание блока =================================
}
Функция обрабатывает результаты, полученные после выполнения функции GetSignal. Результатов GetSignal может быть только три: нет вилки, есть восходящая вилка и есть нисходящая вилка. Каждому из этих вариантов и соответствует один блок тела CheckTrades.

Первый блок выполняется, если вилки нет. Понятно, что отложенные ордера при этом существовать не должны, так как нет ориентира для цены открытия ордера. Поэтому существующие Buy Stop и Sell Stop должны быть удалены. Это действие выполняет функция DeleteOrder, текст которой разбирать не будем, он довольно прозаичный. В случае же обнаружения открытых позиций требуется просто проверить правильность установки уровней их стопов. Эту обязанность на себя возлагает функция ModifyStop, которая будет рассмотрена ниже.

Второй блок будет выполнен, если существует нисходящая вилка, то есть, определен уровень открытия длинной позиции BuyLevel. Здесь также рассматриваются все четыре случая: существование ордера Sell Stop, существование короткой позиции, существование ордера Buy Stop и существование длинной позиции. Также добавляется пятый случай - отсутствие каких-либо ордеров или позиций. В случае с Sell Stop его необходимо удалить (функция DeleteOrder). При обнаружении короткой позиции закрывать ее не стоит, рано. Нужно просто перенести ее уровень стопа на уровень открытия длинной позиции, то есть на цену открытия Buy Stop. Если же уже существует длинная позиция, то проверяем уровень ее стопа, который должен быть установлен сразу за последним DOWN-фракталом (функция ModifyStop). Далее проверяется существование ордера Buy Stop. При его существовании вызывается функция ModifyOrder (будет рассмотрена после ModifyStop), которая проверяет сразу два параметра - цену открытия и уровень стопа. При необходимости эти уровни корректируются. И, в конце концов, когда не найдено ни одного ордера и ни одной позиции, производится установка ордера Buy Stop.

Третий блок аналогичен второму с той разницей, что срабатывает при существовании восходящей вилки, иными словами, когда определен уровень SellLevel. Если есть Buy Stop, то он удаляется. Если есть BUY, то проверяется его стоп. Присутствие Sell также обрабатывается лишь проверкой стопа, а Sell Stop - проверкой цены открытия и стопа. И лишь при полном отсутствии ордеров и позиций производится установка Sell Stop.

Если выполнение всех блоков завершается успешно, то переменной AllWork присваивается время текущего бара, что прекращает все операции с позициями и ордерами до конца свечи.

Функция ModifyStop работает таким образом:

//+-------------------------------------------------------------------------------------+
//| Проверка и изменение уровня стопа позиции |
//+-------------------------------------------------------------------------------------+
bool ModifyStop(int Ticket, double SL)
{
if (OrderSelect(Ticket, SELECT_BY_TICKET))
{
double NewSL = 0; // Новый уровень стопа. Если 0, то изменение не требуется
// - 1 - =========== Проверка возможности изменения стопа длинной позиции ===============
if (OrderType() == OP_BUY)
if (MathAbs(OrderStopLoss() - SL) >= Tick && Bid - SL > StopLevel)
NewSL = SL;
// - 1 - ============================== Окончание блока =================================

// - 2 - =========== Проверка возможности изменения стопа короткой позиции ==============
if (OrderType() == OP_SELL)
if (MathAbs(OrderStopLoss() - SL) >= Tick && SL - Ask > StopLevel)
NewSL = SL;
// - 2 - ============================== Окончание блока =================================

// - 3 - =========== Изменение стопа для любой позиции, если NewSL не равен нулю ========
if (NewSL > 0)
{
if (WaitForTradeContext())
if (OrderModify(Ticket, 0, NP(NewSL), OrderTakeProfit(), 0))
return(True);
// - 3 - ============================== Окончание блока =================================
}
else
return(True);
}
return(False);
}
Вначале переменной NewSL присваивается 0, что означает отсутствие каких бы то ни было модификаций. Затем в соответствующем блоке (первом или втором в зависимости от типа позиции) проверяется равенство нового уровня и имеющегося, а также возможность установки нового уровня. Если возможность существует и новый стоп не равен старому, то NewSL заполняется новым значением, которое становится новым уровнем стопа позиции во время выполнения третьего блока.

Функция ModifyOrder немного сложнее:

//+-------------------------------------------------------------------------------------+
//| Проверка и изменение уровня стопа и цены открытия ордера |
//+-------------------------------------------------------------------------------------+
void ModifyOrder(int Ticket, double Price, double SL)
{
if (OrderSelect(Ticket, SELECT_BY_TICKET))
{
double NewSL = OrderStopLoss();//Уровень нового стопа.Изменяем,если не равен текущему
double NewPrice = OrderOpenPrice(); // Новая цена. Изменяем, если не равна текущей
// - 1 - ===== Проверка возможности изменения цены открытия и стопа для Buy Stop ========
if (OrderType() == OP_BUYSTOP)
{
if (MathAbs(NewPrice - Price) >= Tick && Price - Ask > StopLevel) // Изменение
{ // цены открытия ордера
NewPrice = Price;
if (NewPrice - NewSL <= StopLevel) // При изменении цены открытия стоит позабо-
NewSL = MathMin(NewPrice - StopLevel - Tick, SL); // титься и о новом стопе
}
if (MathAbs(NewSL - SL) >= Tick && NewPrice - SL > StopLevel) // Изменение уровня
NewSL = SL; // стопа
}
// - 1 - ============================== Окончание блока =================================

// - 2 - ===== Проверка возможности изменения цены открытия и стопа для Sell Stop =======
if (OrderType() == OP_SELLSTOP)
{
if (MathAbs(NewPrice - Price) >= Tick && Bid - Price > StopLevel) // Изменение
{ // цены открытия ордера
NewPrice = Price;
if (NewSL - NewPrice <= StopLevel) // При изменении цены открытия стоит позабо-
NewSL = MathMax(NewPrice + StopLevel + Tick, SL); // титься и о новом стопе
}
if (MathAbs(NewSL - SL) >= Tick && SL - NewPrice > StopLevel) // Изменение уровня
NewSL = SL; // стопа
}
// - 2 - ============================== Окончание блока =================================

// - 3 - ===== Изменение уровней стопа и цены открытия, если есть новые уровни ==========
if (MathAbs(NewSL - OrderStopLoss()) >= Tick ||
MathAbs(NewPrice - OrderOpenPrice()) >= Tick)
if (WaitForTradeContext())
if (!OrderModify(Ticket, NP(NewPrice), NP(NewSL), OrderTakeProfit(), 0))
Print("Ордер #", Ticket, ", стоп = ", NewSL, ", NewPrice = ", NewPrice);
// - 3 - ============================== Окончание блока =================================
}
}
Так как здесь может изменяться сразу два параметра ордера (цена открытия и уровень стопа), то переменным NewSL и NewPrice присваиваются начальные значения стопа и цены открытия соответственно. Поэтому сигналом модификации будет хотя бы одно неравенство нового уровня старому уровню. По сравнению с функцией ModifyStop в первый и второй блоки добавлено сравнение нового уровня цены открытия (Price) с имеющимся. Если равенство не достигнуто и при этом возможно изменение цены открытия, то NewPrice получает новое значение. В этом случае не забываем о том, что неизменный стоп может стать слишком близким к новой цене открытия. Поэтому в случае возможного близкого нахождения корректируем и уровень стопа.

Третий блок, подобно третьему блоку ModifyStop, производит модификацию. Только в данном случае изменено может быть два параметра сразу.

В итоге, программирование советника завершено и можно приступать к тестированию. Таймфрейм для тестирования, следуя совету автора системы, выбираем Н4. Но в тут же оказывается, что сделки производятся довольно редко - порядка 30 штук в год. Поэтому для получения репрезентативной выборки потребуется существенно увеличить исторический диапазон тестирования. В результате останавливаем свой выбор на диапазоне 01.01.2003 - 01.01.2010. Параметры советника выбраны такие:
TakeProfit = 0. Это означает работу без уровня прибыли. Сделки закрываются по стопу или по обратному сигналу. Если ввести отрицательное значение, то уровень профита будет установлен в процентах от первоначального уровня стопа. Процент устанавливается в параметре TargetPercent. Положительное значение задает уровень прибыли в пунктах. Lots = 0.1. Фиксированный объем всех сделок. TargetPercent = 200. При TakeProfit >= 0 игнорируется. ForkUpColor = Red. Цвет линий восходящей вилки, которая дает сигнал для открытия коротких позиций. ForkDnColor = Blue. Цвет линий нисходящей вилки, которая дает сигнал для совершения длинных сделок.

По обычаю начнем с валютной пары EURUSD (см. рис. 3).
http://www.forextrade.ru/media/Image/MQLabs/48_ag/EURUSD.gif

Рис. 3. - График кривой баланса, получаемый при тестировании советника на валютной паре EURUSD.

Чистая прибыль показана, на первый взгляд, впечатляющая - 4373 доллара, да и максимальная просадка не очень большая - 1343 доллара (ФВ = 3.25). Но стоит учитывать, что это все же за 7 лет. Хотя, как видно из графика, наибольший рост наблюдался в течение 2007-2008 гг. А наибольший убыток выпал как раз на 2009 год, то есть прошедший. Рассматривая аналогичные периоды графика баланса, можно заметить, что после подобных спадов в основном наблюдается резкий рост. Поэтому можно поставить "галочку" напротив стратегии для EURUSD на 2010 год.

Валютная пара USDCHF показала себя не наилучшим образом (см. рис. 4).
http://www.forextrade.ru/media/Image/MQLabs/48_ag/USDCHF.gif

Рис. 4. - График кривой баланса, получаемый при тестировании советника на валютной паре USDCHF.

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

Валютная пара GBPUSD оказалась очень непредсказуемой (см. рис. 5).
http://www.forextrade.ru/media/Image/MQLabs/48_ag/GBPUSD.gif

Рис. 5. - График кривой баланса, получаемый при тестировании советника на валютной паре GBPUSD.


Плавный спад первых пяти лет (2003-2007 гг.) тестирования настраивает далеко не лучшим образом. И лишь дикий спурт стратегии в течение 2008 года вытягивает баланс из глубоко минуса. 2009 год здесь, можно сказать, "каши не испортил", мужественно додержав на плаву полученную прибыль. К слову, прибыль все же была получена - 259 долларов. Это и так смешно, а вкупе с максимальной просадкой в 5580 долларов смех может перерасти в истерический.

Завершает показ валютная пара USDJPY (см. рис. 6).
http://www.forextrade.ru/media/Image/MQLabs/48_ag/USDJPY.gif

Рис. 6. - График кривой баланса, получаемый при тестировании советника на валютной паре USDJPY.

Первые пять лет стратегия больше мучалась, чем работала, то падая, то вновь возвращаясь к исходному положению. Лишь последние два года дали конечную чистую прибыль - 1501 доллар при максимальной просадке 1056 долларов. Соотношение вроде бы нормальное, но ведь стабильности не видно. То есть, не исключено, что для получения следующих 1500 долларов прибыли вновь придется ждать 7 лет.


Доработка стратегии для использования в AutoGraf 4.0

Для возможности запуска стратегии в AutoGraf 4.0 доработаем стратегию соответствующими модулями. Исходя из того, что, по сути, уровень Take Profit в пунктах для данной стратегии не нужен, было принято решении о ликвидации параметра TargetPercent и передаче его функций параметру TP. В результате, при применении данной стратегии в AG, нулевое значение TP будет означать нулевой уровень прибыли, а любое положительное значение будет указывать уровень профита в процентах от первоначального уровня стопа.

Для запуска советника из-под AutoGraf 4.0, по обычаю, совершите такие шаги:
Воспользуйтесь ссылкой Файлы стратегий для Autograf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/48_ag/AG_Chuvashovs_Fork.zip) и полученный архив распакуйте в папку MT4\experts\libraries. Запустите AutoGraf. Для получения похожих с тестами результатов работы выставьте объем открываемой позиции (Lots) 0.1, а TP = 0. Выберите стратегию №3. Для этого передвиньте вверх значок So и среди названий стратегий найдите значок S3, который также потяните вверх Запустите функцию автоматической торговли, передвинув значок AT в верхнее положение.

Советник Chuvashovs_Fork_Expert (http://www.forextrade.ru/media/Image/MQLabs/48_ag/Chuvashovs_Fork_Expert.mq4)
Файлы стратегии для AutoGraf 4.0 (http://www.forextrade.ru/media/Image/MQLabs/48_ag/AG_Chuvashovs_Fork.zip)
Развернутые результаты тестирования эксперта (http://www.forextrade.ru/media/Image/MQLabs/48_ag/Test.zip)

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