English 中文 Español Deutsch 日本語 Português
Защита от ложных срабатываний торгового робота

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

MetaTrader 4Торговые системы | 23 ноября 2015, 11:52
22 286 68
Aleksandr Masterskikh
Aleksandr Masterskikh

Введение

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


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

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

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

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

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

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

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

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

  • это малая величина TakeProfit в настройках по умолчанию (а это алгоритм выхода), из-за чего каждая позиция быстро закрывается;
  • а также алгоритм входа эксперта "MACD Sample", который срабатывает сразу после закрытия предыдущей позиции, допуская повторный вход независимо от количества входов на данной свече.

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

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


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

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

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

Таким образом, главным фактором является момент срабатывания алгоритма входа, вернее, момент срабатывания ордера для открытия позиции (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 намеренно оставлены без изменения, чтобы можно было сравнить исходные и доработанные алгоритмы.

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

Прикрепленные файлы |
MACD_Sample.mq4 (6.1 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (68)
Sainidyl Olmi
Sainidyl Olmi | 21 июл. 2016 в 12:02
Статья полезная как раз для такого новичка как я.  Более опытным коллегам она уже покажется сухой и скучной
Martingeil
Martingeil | 8 мар. 2019 в 19:25

Прочитал статью и обсуждение, ничего толкового так и не прочитал.

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

static int prevtime=0;

void OnTick()
  {
   if(iTime(NULL,PERIOD_M1,0) == prevtime) return;
   prevtime=iTime(NULL,PERIOD_M1,0);//если появился новый бар , включаемся

  }

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

input int  MinutStep = 7; //пауза в минутах от ордера(закрытого или открытого)


void OnTick()
  {   
   if( TimeCurrent()-StaticPrevtimeOrders(1,Magic1) < MinutStep*PeriodSeconds(PERIOD_M1) ) return;

    .........
    
    .........

  }

//+------------------------------------------------------------------+
//| StaticPrevtimeOrders   2009 год автор: Martingeil                |
//| op_cl = 1; время закрытия ордера (тейкпрофит, стоплосс, клозе).  |
//| op_cl = 2; время открытого ордера                                |
//| Mag - Маджик номер ордера                                        |
//+------------------------------------------------------------------+    
datetime StaticPrevtimeOrders(int op_cl,int Mag) {
  datetime t;
  int      i, k=OrdersHistoryTotal();

  for (i=0; i<k; i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
      if ((OrderSymbol()==Symbol()) && (OrderMagicNumber()==Mag)) {
        if (OrderType()==OP_BUY || OrderType()==OP_SELL) {
          if (op_cl==1 && t<OrderCloseTime()) {
            t=OrderCloseTime();
            if (op_cl==2 && t<OrderOpenTime()) {
            t=OrderOpenTime();
            }
          }
        }
      }
    }
  }
  return(t);
}
В статье вариант вообще туфта полная, по сравнению с этой последней функцией, она универсальна, я искал такую штуку перерыл всю коде базу, нашел эту функцию в советнике Илан.  
Konstantin Nikitin
Konstantin Nikitin | 9 мар. 2019 в 07:44
Lehfr:

Прочитал статью и обсуждение, ничего толкового так и не прочитал.

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

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

В статье вариант вообще туфта полная, по сравнению с этой последней функцией, она универсальна, я искал такую штуку перерыл всю коде базу, нашел эту функцию в советнике Илан.  

Слету видно одну ошибку.

datetime StaticPrevtimeOrders(int op_cl,int Mag) {
  datetime t;
  int      i, k=OrdersHistoryTotal();

  for (i=0; i<k; i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
      if ((OrderSymbol()==Symbol()) && (OrderMagicNumber()==Mag)) {
        if (OrderType()==OP_BUY || OrderType()==OP_SELL) {
          if (op_cl==1 && t<OrderCloseTime())
            t=OrderCloseTime();
          if (op_cl==2 && t<OrderOpenTime())
            t=OrderOpenTime();
        }
      }
    }
  }
  return(t);
}

Будет наверно правильней. Иначе будет срабатывать только op_cl==1

Martingeil
Martingeil | 12 апр. 2019 в 20:38
Konstantin Nikitin:

Слету видно одну ошибку.

Будет наверно правильней. Иначе будет срабатывать только op_cl==1

Да, я не много перемудрил с исправлением, вставил скобки лишние подумал почему их там нет, вот и вставил. 

Исправил.

input int  MinutStep = 7; //пауза в минутах от ордера(закрытого или открытого)


void OnTick()
  {   
   if( TimeCurrent()-StaticPrevtimeOrders(1,Magic1) < MinutStep*PeriodSeconds(PERIOD_M1) ) return;

    .........
    
    .........

  }

//+------------------------------------------------------------------+
//| StaticPrevtimeOrders   2009 год автор: Martingeil                |
//| op_cl = 1; время закрытия ордера (тейкпрофит, стоплосс, клозе).  |
//| op_cl = 2; время открытого ордера                                |
//| Mag - Маджик номер ордера                                        |
//+------------------------------------------------------------------+    
datetime StaticPrevtimeOrders(int op_cl,int Mag) {
  datetime t;
  int      i, k=OrdersHistoryTotal();

  for (i=0; i<k; i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
      if ((OrderSymbol()==Symbol()) && (OrderMagicNumber()==Mag)) {
        if (OrderType()==OP_BUY || OrderType()==OP_SELL) {
          if (op_cl==1 && t<OrderCloseTime())
            t=OrderCloseTime();
          if (op_cl==2 && t<OrderOpenTime())
            t=OrderOpenTime();
        }
      }
    }
  }
  return(t);
}
intercool
intercool | 24 мая 2024 в 19:20

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

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

datetime t = 0;
Индикатор "Канат" Эрика Наймана Индикатор "Канат" Эрика Наймана
В статье описывается построение индикатора «Канат» по книге Эрика Л. Наймана «Малая энциклопедия трейдера». Этот индикатор показывает направление тренда на основе расчетных величин быков и медведей за указанный период. В статье изложены принципы построения и расчета индикатора с примерами кода, на основе индикатора построен эксперт и произведена оптимизация внешних параметров.
Еще раз о картах Кохонена Еще раз о картах Кохонена
Cтатья описывает приемы работы с картами Кохонена. Она будет интересна как исследователям рынка с начальными навыками программирования на MQL4 и MQL5, так и опытным программистам, испытывающим сложности с подключением карт Кохонена к своим проектам.
Модуль торговых сигналов по системе Билла Вильямса Модуль торговых сигналов по системе Билла Вильямса
В статье описываются правила торговой системы Билла Вильямса, порядок использования разработанного MQL5-модуля для поиска и разметки на графике паттернов данной системы, автоматической торговли по найденным паттернам, а также представлены результаты тестирования на различных торговых инструментах.
MQL5 для начинающих: Антивандальная защита графических объектов MQL5 для начинающих: Антивандальная защита графических объектов
Что должна делать ваша программа, если графические панели управления были удалены или изменены кем-то еще? В этой статье мы покажем, как после удаления приложения не иметь на графике "бесхозные" объекты, и как не потерять над ними контроль в случае переименования или удаления созданных программно объектов.