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

MQLabs: Отбой волатильности

Советник  VolatilityBounce

Индикатор  VolatilityBounce

Файлы стратегий для AutoGraf 4.0

Развернутые результаты тестирования эксперта

   

    Одной из важных характеристик рынка является волатильность.

    Волатильность  - это амплитуда изменений цены в течение некоторого периода. Амплитуда вычисляется как разность максимума и минимума цены за исследуемый промежуток времени. К примеру, текущей волатильностью на графике Н1 будет выступать разность High и Low одной свечи. Аналогичным образом можно посчитать волатильность за сутки. Во многих случаях это будет разность High и Low дневной свечи.

    Наиболее распространенным применением волатильности является индикатор ATR (Average True Range - Средний Истинный Диапазон), который отображает величину средней волатильности за указанный период. По значениям ATR можно судить об активности рынка в ближайшем прошлом, исходя из которой, можно производить расчеты размеров риска и возможной прибыли планируемых сделок.

    На показаниях ATR основана система "Отбой волатильности". Ее фундамент зиждется на предположении о том, что после резкого роста волатильности обязательно следует отскок цены в обратном росту направлении. Отличить резкий рост волатильности от "не очень резкого" поможет элементарное сравнение текущей и средней волатильностей, которые для наглядности стоит представить графически (см. рис. 1).

Рис. 1. - Пересечение текущей и средней волатильности.

    Сравнение текущей и средней волатильностей можно произвести простым наложением друг на друга двух индикаторов ATR, один из которых должен оперировать периодом 1. Но в этом случае отображение значений не всегда корректно, так как текущая волатильность показывается в масштабе средней волатильности или наоборот. Для правильного отображения соотношений был разработан индикатор VolatilityBounce. Красная линия индикатора соответствует текущей волатильности, а голубая - средней. Резкий рост текущей волатильности сопровождается пересечением линии средней волатильности снизу вверх. О направлении будущей сделки стоит судить по типу свечи, вызвавшей рост волатильности. Если свеча бычья, то предполагается отскок цены вниз и необходимо становиться в короткую сделку. Если свеча медвежья, то предполагается отскок цены вверх и необходимо открывать длинную сделку.

    Но далеко не всегда описанные случаи нужно рассматривать в качестве сигналов открытия сделки. Локальный экстремум сигнальной свечи может быть очень малого периода, то есть превосходить небольшое количество свечей. Подобный случай показан на рис. 2.

Рис. 2. - Не всякое пересечение линий стоит воспринимать как сигнал

    Сигнал от индикатора получен, но минимум сигнальной свечи является минимумом лишь среди трех последних свечей. Как видно по дальнейшей истории, этого мало для достижения ценой дна и она еще проходит достаточно большое расстояние вниз. VolatilityBounce в данном случае настроен на период экстремума в пять свечей, то есть максимум должен превосходить пять предыдущих максимумов свечей, а минимум - быть ниже пяти предыдущих минимумов свечей.

    Чтобы пользователь не высчитывал период экстремума, в индикаторе VolatilityBounce добавлено отображение стрелок, появляющихся при открытии свечи. Кроме подтверждения выполнения всех условий, стрелка помогает сориентироваться в направлении будущей сделки.

    Двумя дополнительными условиями открытия сделки являются:

        1) Сигнальная свеча не должна иметь тень более половины высоты свечи. Для бычьих свечей это означает нахождение цены закрытия выше середины высоты свечи, а для медвежьих - ниже середины.

        2) Бычья сигнальная свеча должна иметь минимум выше предыдущей свечи, а медвежья - максимум ниже предыдущей.

    Критерии открытия сделок по системе "Отбой волатильности" рассмотрены полностью. Далее следует не менее насущный вопрос: критерий закрытия сделок. Одним из них будет наличие обратного сигнала. Но практика показывает, что подобные выходы из сделок далеко не всегда  оправданы, так как съедают львиную долю прибыли. Намного эффективнее применить уровень профита, который позволит закрыть сделку по более выгодной цене. Главное - правильно рассчитать профит. То же самое касается закрытия сделки в убытке. Совсем не обязательно дожидаться обратного сигнала, намного разумнее правильно установить уровень стопа.

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

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

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

    Расчет направления сигналов, уровней стопов и профитов производится в функции GetSignal:

 
//+-------------------------------------------------------------------------------------+
//| Генерация сигналов покупки и продажи                                                |
//+-------------------------------------------------------------------------------------+
void GetSignal(int Num)
{
 Signal = 0;
 
// - 1 - ============ Определение значений средней и единичной волатильностей ===========
 double ATR1 = iATR(NULL, 0, ATRPeriod, Num);         // Последнее известное значение АТR
 double ATR2 = iATR(NULL, 0, ATRPeriod, Num+1);   // Предпоследнее известное значение ATR
 double ATR3 = iATR(NULL, 0, ATRPeriod, Num+2);//Предпредпоследнее известное значение ATR
 double Curr1 = High[Num] - Low[Num];                       // Волатильность первой свечи
 double Curr2 = High[Num+1] - Low[Num+1];                   // Волатильность второй свечи
 double Curr3 = High[Num+2] - Low[Num+2];                  // Волатильность третьей свечи
// - 1 - ========================== Окончание блока =====================================

 if (Curr1 > ATR1 && Curr2 < ATR2 && Curr3 <= ATR3)//Единичная волатильность стала больше
   {                          // средней волатильности после двух подряд меньших значений
// - 2 - ======================== Генерация сигнала покупки =============================
    if (Open[Num] > Close[Num] &&                                       // свеча медвежья
        Close[Num] < (High[Num] + Low[Num])/2)                 // закрылась ниже середины
      if (iLowest(NULL, 0, MODE_LOW, Range, Num) == Num &&   // Свеча - локальный минимум 
          High[Num] < High[Num+1])           // Максимум свечи ниже предыдущего максимума
        {
         Signal = 1;                                                    // Сигнал покупки
         LastSignal = Time[0];                     // Запоминаем время последнего сигнала
         BegSL = Open[Num-1] - ATR1;                           // Расчет начального стопа
         BegTP = High[Num];                                  // Расчет начального профита
         EndSL = Open[Num-1] - Factor*ATR1;                // Окончательный уровень стопа
         EndTP = Open[Num-1] + Factor*ATR1;              // Окончательный уровень профита
        } 
// - 2 - ========================== Окончание блока =====================================

// - 3 - ======================== Генерация сигнала продажи =============================
    if (Open[Num] < Close[Num] &&                                          // свеча бычья
       Close[Num] > (High[Num] + Low[Num])/2)                  // закрылась выше середины
      if (iHighest(NULL, 0, MODE_HIGH, Range, Num) == Num &&// Свеча - локальный максимум
          Low[Num] > Low[Num+1])               // Минимум свечи выше предыдущего минимума
        {
         Signal = -1;                                                   // Сигнал продажи
         LastSignal = Time[0];                     // Запоминаем время последнего сигнала
         BegSL = Open[Num-1] + ATR1 + Spread;                  // Расчет начального стопа
         BegTP = Low[Num] + Spread;                          // Расчет начального профита
         EndSL = Open[Num-1] + Factor*ATR1 + Spread;       // Окончательный уровень стопа
         EndTP = Open[Num-1] - Factor*ATR1 + Spread;     // Окончательный уровень профита
        } 
// - 3 - ========================== Окончание блока =====================================
   }
}

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

  Блок 1 формирует три значения средней волатильности и три значения единичной волатильности, начиная с бара номер Num. Период для вычисления средней волатильности указывается при помощи внешней переменной эксперта ATRPeriod.

  Блоки 2 и 3 выполняют генерацию сигналов покупки и продажи соответственно. Условия возникновения сигналов в точности повторяют условия, реализованные в коде индикатора VolatilityBounce. Здесь используются значения двух настроечных параметров эксперта: Range и Factor. Range задает величину периода экстремума, а Factor - множитель средней волатильности при расчете уровней окончательного стопа и профита, которым соответствуют переменные EndSL и EndTP соответственно. Начальным стопу и профиту соответствуют переменные BegSL и BegTP.

  Сохранение времени появления текущего сигнала в переменной LastSignal требуется для дальнейшего определения наличия реакции на сигнал, что препятствует бесконечному открытию сделок по одному и тому же сигналу.

  Открытием сделок занимается функция Trade:    

 
//+-------------------------------------------------------------------------------------+
//| Открытие позиций                                                                    |
//+-------------------------------------------------------------------------------------+
bool Trade()
{
// - 1 - ==================== Открытие длинной позиции ==================================
 if (Signal > 0)
   {
    int Res = CheckOrders(OP_SELL);     // закрытие коротких сделок, если таковые имеются
    if (Res == 0)     // Сделок, открытых по текущему сигналу, нет, можно открывать новую
      {
       if (OpenOrderCorrect(OP_BUY, Lots, NP(Ask), NP(BegSL), NP(BegTP)) != 0)
         return(False);                    
      }   
    if (Res == 1) return(False);//Существует короткая позиция, которую закрыть не удалось
   }       
// - 1 - ==================== Окончание блока ===========================================
           
// - 2 - ==================== Открытие короткой позиции =================================
 if (Signal < 0)
   {
    Res = CheckOrders(OP_BUY);           // закрытие длинных сделок, если таковые имеются
    if (Res == 0)     // Сделок, открытых по текущему сигналу, нет, можно открывать новую
      { 
       if (OpenOrderCorrect(OP_SELL, Lots, NP(Bid), NP(BegSL), NP(BegTP)) != 0)
         return(False);                   
      }   
    if (Res == 1) return(False);// Существует длинная позиция, которую закрыть не удалось
   }       
// - 2 - ==================== Окончание блока ===========================================
 
 return(True);    
}

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

    Код CheckOrders претерпел изменения, по сравнению со стандартным подходом:

 
//+-------------------------------------------------------------------------------------+
//| Поиск своих ордеров. Возвращает:                                                    |
//|    0 - Своих позиций нет. Можно открывать новую позицию                             |
//|    1 - Не удалось закрыть указанную позицию. Требуется еще попытка                  |
//|    2 - Существует противоположная указанной позиция, открытая по последнему сигналу |                
//+-------------------------------------------------------------------------------------+
int CheckOrders(int Type)   // Type - Тип позиции, которую нужно закрыть или -1, если тип
{                                                            // позиции не имеет значения
 for (int i = OrdersTotal()-1; i >= 0; i--)           // Используется весь список ордеров
   if (OrderSelect(i, SELECT_BY_POS))                       // Убедимся, что ордер выбран
     if (OrderMagicNumber() == MagicNumber &&                  // Ордер открыт экспертом,
         OrderSymbol() == Symbol())         // который прикреплен к текущей валютной паре
       if (OrderType() == Type || Type < 0)// Если тип ордера равен заданному или неважен
         {
          if (WaitForTradeContext())                       // Свободен ли торговый поток?
            {
             if (Type == OP_BUY)                  // Если следует закрыть длинную сделку,
               double Price = MarketInfo(Symbol(), MODE_BID);  // то применяется цена BID
              else                               // Если следует закрыть короткую сделку,
               Price = MarketInfo(Symbol(), MODE_ASK);         // то применяется цена ASK
             if (!OrderClose(OrderTicket(), OrderLots(), NP(Price), 3)) // Если сделку не 
               return(1);                    // удалось закрыть, то результат функции - 1
            }
         }                          
        else                       // найден противоположный параметру Type по типу ордер
         if (OrderOpenTime() >= LastSignal)    // Если последний сигнал уже отработан, то
           return(2);                      // открывать новую сделку нельзя - результат 2
 
 return(0);                                                     // Можно открывать сделку
}

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

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

    При удачном выполнении всех операций, функция возвращает значение 0, разрешающее открытие новой сделки.

    Так как стратегия оперирует двумя типами уровней стопа и профита, ей попросту необходим блок, следящий за подстановкой правильного значения уровней закрытия сделки. Этим блоком в теле эксперта является функция ModifyCheck:

 
//+-------------------------------------------------------------------------------------+
//| Функция установки новых стопа и профита после отработки ордером более одного периода|
//+-------------------------------------------------------------------------------------+
bool ModifyCheck()
{
 for (int i = OrdersTotal()-1; i >= 0; i--)           // Используется весь список ордеров
   if (OrderSelect(i, SELECT_BY_POS))                       // Убедимся, что ордер выбран
     if (OrderMagicNumber() == MagicNumber &&                  // Ордер открыт экспертом,
         OrderSymbol() == Symbol())         // который прикреплен к текущей валютной паре
       if (TimeCurrent() - OrderOpenTime() >= Period()*60)     // Ордер существует дольше
                                                     // одного временного периода графика
         if (MathAbs(OrderStopLoss() - EndSL) >= Tick ||// не равен нужному уровень стопа
             MathAbs(OrderTakeProfit() - EndTP) >= Tick)           // или уровень профита
           // Попытка изменения уровней стопа или профита (или обоих уровней вместе)  
           if (WaitForTradeContext())                      // Свободен ли торговый поток?
             {
              RefreshRates();                 // Обновление значений переменных Bid и Ask
              if ((OrderType() == OP_BUY &&                       // Если сделка длинная,
                   Bid - EndSL > StopLevel &&                           // а новый стоп и 
                   EndTP - Bid > StopLevel) ||      // новый профит достаточно далеки или
                  (OrderType() == OP_SELL &&                          // сделка короткая,
                   EndSL - Ask > StopLevel &&                             // а новый стоп 
                   Ask - EndTP > StopLevel))          // и новый профит достаточно далеки
                {                                // то можно произвести попытку изменения
                 if (!OrderModify(OrderTicket(), 0, NP(EndSL), NP(EndTP), 0))      // при 
                   return(False);                         // неудаче функция вернет False
                }
               else      // если новый стоп или новый профит расположены близко к текущей    
                return(false);                           // цене, то результат тоже False
             }
            else  // Не удалось дождаться освобождения торгового потока - результат False
             return(False);      
 
 return(True);                         // Все действия завершены успешно - результат True 
}

    Подобно CheckOrders, производится поиск своих сделок в списке открытых ордеров. Среди своих сделок выбираются те, которые существуют дольше одного периода графика. У таких сделок проверяется равенство текущего стопа и профита значениям EndSL и EndTP соответственно. Причем автоматически учитывается тот факт, что значения EndTP и EndSL могут быть сформированы уже по следующему сигналу в том же направлении. В итоге достигается эффект одновременного закрытия всех однонаправленных сделок, кроме случаев, когда новая сделка смогла получить "быструю" прибыль.

    Неудачная модификация значимых уровней сделки мгновенно прерывает выполнение ModifyCheck с возвратом результата False. Только успешная модификация всех ордеров позволяет окончить функцию со значением True.

     Описание наиболее важных функций эксперта окончено, что позволяет перейти к этапу тестирования.

    Рекомендуемый таймфрейм для описанной стратегии H1. Тестирование проводилось на участке истории - с 01.01.200009 до 12.06.2010. Это позволило по всем без исключения валютным парам получить необходимое и достаточное количество сделок. В некоторых случаях (результаты по GBPUSD)  количество сделок значительно превысило нужный уровень.

    Два из четырех имеющихся настроечных параметров подбирались индивидуально для каждой из валютных пар. Речь идет о переменной Range, указывающей значимость локального экстремума, и переменной Factor, на основании которой рассчитываются окончательные уровни стопа и профита. Результаты тестирования приведены на рис. 3 - 6.

                Рис. 3. - Результаты тестирования эксперта VolatilityBounce на валютной паре EURUSD.

EURUSD. Значения изменяемых параметров были взяты такие: Range = 12, Factor = 5.5. Кривая баланса выглядит очень уверенно, рост равномерный, стабильность - на высоком уровне. Максимальная просадка проявилась лишь в самом начале тестирования и достигла отметки 1422 доллара. Затем наступила эра чистой прибыли, которая достигла величины 5014 долларов, что в результате вылилось в высокий фактор восстановления - 3.52.

Для реальной торговли на валютной паре EURUSD необходимо располагать стартовым депозитом 4200 долларов (объем сделок 0.1 лота, соответственно для 0.01 лота нужно 420 долларов). В этом случае годовая доходность ожидается на отметке 84%.

                Рис. 4. - Результаты тестирования эксперта VolatilityBounce на валютной паре USDCHF.

USDCHF. Оптимальные параметры для франка такие: Range = 14, Factor = 3.5. Кривая баланса растет не совсем равномерно, но имеет хорошую стабильность, не допуская резких и глубоких впадин. Это подтверждает показатель максимальной просадки - 695 долларов. При таких убытках не смогла развить успех чистая прибыль, показав значение 2120 долларов. Фактор восстановления для франка достаточно высокий - 3.05. Зная о консервативности швейцарского франка, с такими результатами можно всерьез задумываться о реальной торговле.

Минимальный стартовый депозит для торговли объемом 0.1 лота должен составлять 2100 долларов. В этом случае можно ожидать доходность на уровне 71% годовых.

                      Рис. 5. - Результаты тестирования эксперта VolatilityBounce на валютной паре GBPUSD.

GBPUSD. Входные параметры эксперта: Range = 4, Factor = 9. Кривая баланса показывает равномерный рост на протяжении всего периода тестирования, но не дала главного - стабильности. В результате после каждой серии прибылей приходится испуганно оглядываться, боясь получить очередную порцию просадки. К слову, максимальная просадка в данном случае весьма велика - 3006 долларов. Правда, чистая прибыль также забралась высоко - 6291 доллар. Но фактор восстановления очень мал для фунта - 2.09. Рассуждать о реальном применении стратегии на валютной паре GBPUSD не стоит. Ведь стартовый депозит потребуется очень большой (около 9000 долларов), а риск получения убытков неоправданно высок.

                        Рис. 6. - Результаты тестирования эксперта VolatilityBounce на валютной паре USDJPY.

USDJPY.  Результаты показаны при параметрах: Range = 7, Factor = 5.5. Кривая баланса показала что-то похожее на равномерный рост. Стабильностью она и вовсе не блещет. Чистая прибыль смогла дотянуться только до отметки 2523 доллара, не дав при этом разгуляться максимальной просадке, которая оказалась на уровне 1083 доллара. В итоге фактор восстановления смог удержать показатель  2.33. В последнее время йена баловала высокими показателями. Поэтому такой результат совсем не впечатляет.

При желании использовать стратегию на реальном счете и валютной паре USDJPY, необходимо обзавестись стартовым депозитом 3300 долларов (для работы лотом 0.1). Ожидаемая прибыльность находится в пределах 54% годовых.

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

    Для работы стратегии в среде AutoGraf 4.0 потребуется доработка имеющегося алгоритма в соответствии с требованиями прикладной программы. Первым делом следует преобразовать входные переменные эксперта в настроечные параметры AutoGraf. Переменной ATRPeriod теперь будет соответствовать параметр AT_1, Range - AT_2, Factor - AT_3. Для указания объема сделки (по аналогии с переменной Lots) в среде AutoGraf следует использовать значок Lots панели настроек приложения.

     Запуск стратегии VolatilityBounce в среде AutoGraf 4.0 состоит из следующих шагов:

  • Получить файл по ссылке Файлы стратегий для AutoGraf 4.0 и распаковать полученный архив в папку MT4\experts\libraries (с перезаписью файлов AG_AT.ex4 и AG_AT.mq4).

  •  Запустить AutoGraf.

  •  Для работы советника в ключе приведенных результатов в окне настроек AutoGraf (закладка "Входные параметры") выставить правильные значения параметров AT_1 -  AT_3 (полное повторение результатов при этом не гарантируется).

  • Выбрать стратегию №5. Для этого необходимо передвинуть вверх значок So и среди названий стратегий найти значок S5, который также потянуть вверх.

  • Запустить функцию автоматической торговли, передвинув значок AT в верхнее положение.

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

 

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

Июнь 2010

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

2.625
 
 
X
Loading