Защита от ложных срабатываний торгового робота

Aleksandr Masterskikh | 23 ноября, 2015

Введение

В данной статье речь пойдет о повышении устойчивости работы торгового робота путем устранения возможных повторных срабатываний (дребезга) разными способами: как отдельно в алгоритмах входа и выхода, так и во взаимной увязке этих алгоритмов.


Суть проблемы

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

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

В данной статье я не буду рассматривать вопросы анализа финансовых инструментов (технического и фундаментального характера), которые могут влиять на стабильность работы торгового эксперта и помогают избежать дребезга (это отдельная тема — я являюсь автором теории импульсного равновесия и систем на ее основе). Здесь речь пойдет о мерах программного характера, не зависящих напрямую от методов аналитики финансовых рынков.

Итак, приступаем к решению проблемы. В качестве шаблона для примера я буду использовать эксперт из стандартного комплекта, имеющегося в клиентском терминале МetaТrader 4, под названием "MACD Sample".

Вот как визуально выглядит сама проблема дребезга на примере резкого скачка цены EURUSD 2-го октября этого года (таймфрейм М15, эксперт "MACD Sample" с настройками по умолчанию):

На скриншоте хорошо видно, что имеется 8 последовательных срабатываний (входы Buy) на одной свече. Из них корректный (с точки зрения нормальной рыночной логики) только первый вход, остальные 7 являются дребезгом.

Причины ложных срабатываний в данном конкретном случае:

Мы договорились, что вопросы фильтрации рыночных колебаний мы намеренно не учитываем (так как у каждого трейдера свой алгоритм входа и выхода), поэтому для решения проблемы, с целью универсальности применения, рассмотрим следующие факторы более общего характера:


Решение в алгоритме входа

Наиболее простым, но в то же время и самым надежным вариантом фиксации точки входа, является фактор времени, причины следующие:

Таким образом, главным фактором является момент срабатывания алгоритма входа, вернее, момент срабатывания ордера для открытия позиции (OrderSend), так как эти два момента могут не совпадать при наличии в алгоритме каких-либо специальных задержек открытия ордера.

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

Решение довольно простое. Сначала напишу код с некоторыми комментариями, а потом поясню более подробно. Вот дополнительный код (помечен желтым фоном), который нужно вставить в алгоритм торгового эксперта (см. файл MACD_Sample_plus1.mq4):

//+------------------------------------------------------------------+
//|                                                  MACD Sample.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              https://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "https://www.mql4.com"

input double TakeProfit    =50;
input double Lots          =0.1;
input double TrailingStop  =30;
input double MACDOpenLevel =3;
input double MACDCloseLevel=2;
input int    MATrendPeriod =26;
//--- вводим новую переменную (величина в секундах для 1 бара данного ТФ, для М15 равно 60 с х 15 = 900 с)
datetime Time_open=900;
//--- вводим новую переменную (время открытия бара,где будет 1-й вход)
datetime Time_bar = 0;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnTick(void)
  {
   double MacdCurrent,MacdPrevious;
   double SignalCurrent,SignalPrevious;
   double MaCurrent,MaPrevious;
   int    cnt,ticket,total;
//---
// initial data checks
// it is important to make sure that the expert works with a normal
// chart and the user did not make any mistakes setting external 
// variables (Lots, StopLoss, TakeProfit, 
// TrailingStop) in our case, we check TakeProfit
// on a chart of less than 100 bars
//---
   if(Bars<100)
     {
      Print("bars less than 100");
      return;
     }
   if(TakeProfit<10)
     {
      Print("TakeProfit less than 10");
      return;
     }
//--- to simplify the coding and speed up access data are put into internal variables
   MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0);
   MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
   SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);
   SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);
   MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);

   total=OrdersTotal();
   if(total<1)
     {
      //--- no opened orders identified
      if(AccountFreeMargin()<(1000*Lots))
        {
         Print("We have no money. Free Margin = ",AccountFreeMargin());
         return;
        }
      //--- check for long position (BUY) possibility
      
      //--- вводим новую строку (снимает запрет на повторный вход, если открылся новый бар)
      if( (TimeCurrent() - Time_bar) > 900 ) Time_open = 900; 
      
      if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && 
         MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious && 
         (TimeCurrent()-Time[0])<Time_open) //вводим новую строку в алгоритм входа (выполнится только 1 раз, т.к. далее условие на данной свече невыполнимо)
        {
         ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
         if(ticket>0)
           {
            if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
             {
              Print("BUY order opened : ",OrderOpenPrice());
              Time_open = TimeCurrent()-Time[0]; //вводим новую строку (запоминаем интервал от времени открытия бара, на котором был вход, до момента входа)
              Time_bar = Time[0]; //вводим новую строку (запоминаем время открытия того бара, где был 1-й вход)
             }
           }
         else
            Print("Error opening BUY order : ",GetLastError());
         return;
        }

Подробнее:

Используем не абсолютное время (момент входа), а относительное — интервал времени от момента открытия текущей свечи до момента входа. И далее эту величину сравниваем с заранее заданной, достаточно большой величиной времени (длительности всей свечи), что позволяет сработать первому входу. В момент открытия позиции мы меняем (уменьшаем) величину переменной Time_open, записывая в нее значение интервала времени с начала свечи до момента фактического открытия. А так как в любой последующий момент времени значение (TimeCurrent() - Time[0]) будет больше величины, записанной нами в момент входа, то условие (TimeCurrent() - Time[0]) < Time_open становится невыполнимым, чем и достигается блокировка второго и следующих входов на данной свече.

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

Вот результат такой простой доработки исходного алгоритма входа эксперта ("MACD Sample_plus1"):

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

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


Решение в алгоритме выхода

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

Ранее мы использовали надежный параметр — фактор времени, снова воспользуемся им — жестко регламентируем момент закрытия позиции по времени, а именно в момент открытия следующей (после входа) свечи. Этот момент в алгоритме выхода отразим так:

if(!OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES))
         continue;
      if(OrderType()<=OP_SELL &&   // check for opened position 
         OrderSymbol()==Symbol())  // check for symbol
        {
         //--- long position is opened
         if(OrderType()==OP_BUY)
           {
            //--- should it be closed?
            if(/* MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && // убираем код срабатывания выхода по MACD, чтобы не мешать новому условию закрытия (см. далее)
               MacdCurrent>(MACDCloseLevel*Point) &&
             */
               Bid > OrderOpenPrice() &&  // вводим новую строку - необязательно (цена в положительной области относительно уровня входа)
               TimeCurrent() == Time[0] ) // вводим новую строку (простая доработка алгоритма выхода: выход строго в момент открытия текущей свечи)
              {
               //--- close order and exit
               if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet))
                  Print("OrderClose error ",GetLastError());

               return;
              }

Такая небольшая модификация позволит "работать" алгоритму входа (позиция открылась, условий для закрытия нет), позиция длится до момента TimeCurrent() == Time[0] и закрывается одномоментно в начале новой после импульса свечи. В результате, помимо защиты от дребезга, неплохо заработали (смотри фото, "MACD Sample_plus2"):

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

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


Увязка алгоритмов входа и выхода

Увязка заключается в некотором предварительном моделировании всего процесса: открытие позиции — сопровождение — закрытие позиции. Это отражается, в том числе, в выборе сдвига в индикаторах и функциях, используемых в алгоритмах входа и выхода.

Например, если вы используете в алгоритме выхода условие TimeCurrent() = Time[0], то есть точка выхода задана строго началом текущей свечи, то алгоритм входа должен отработать на предыдущих, завершенных барах, чтобы условие выхода смогло выполниться. То есть, чтобы закрыть позицию по условию TimeCurrent() = Time[0] без дополнительных условий, необходимо, чтобы весь алгоритм сравнения (на входе) был осуществлен (закончен) на предыдущем баре. Для этого в настройках индикаторов, участвующих в сравнении величин, должен быть сдвиг, равный 1. В этом случае сравнение величин будет корректным, а начало текущей свечи будет логичным завершением алгоритма выхода.

Таким образом, увязка алгоритмов входа и выхода также связана с фактором времени.


Заключение

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

Ниже представлены коды экспертов: в начальном виде (MACD_Sample.mq4), с доработанным входом (MACD_Sample_plus1.mq4), с доработанным выходом (MACD_Sample_plus2.mq4). Причем доработаны только каналы Buy, а каналы Sell намеренно оставлены без изменения, чтобы можно было сравнить исходные и доработанные алгоритмы.

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