English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Сказки торговых роботов: лучше меньше - да лучше?

Сказки торговых роботов: лучше меньше - да лучше?

MetaTrader 5Трейдинг | 11 апреля 2014, 17:55
7 224 8
Roman Zamozhnyy
Roman Zamozhnyy

Чтобы решить проблему, ее нужно сначала сформулировать. Если я считаю, что нашел решение, его нужно проверить в деле, чтобы убедиться в своей правоте.

Я знаю только один способ проверки — на собственных деньгах.

Д.Ливермор


Пролог

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

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

Copyright (c) 2012-2014 Roman Rich
Euro vs US Dollar, Box-20, Reverse-3


    1.4588 | \.....\.................................................................... | 1.4588
    1.4521 | X\....X\................................................................... | 1.4521
    1.4454 | XO\.\.XO\.................................................................. | 1.4454
    1.4388 | XOX\X\XO.\................................................................. | 1.4388
    1.4322 | XOXOXOXO..\................................................................ | 1.4322
    1.4256 | XOXOXOXO...\....\.......................................................... | 1.4256
    1.4191 | XOXO/OXO....\...X\......................................................... | 1.4191
    1.4125 | XOX/.O/O.....\..XO\........................................................ | 1.4125
    1.4060 | XO/../.O......\.XO.\....................................................... | 1.4060
    1.3996 | ./.....O.......\XO..\...................................................... | 1.3996
    1.3932 | .......OX.......XO...\....................................................X | 1.3932
    1.3868 | .......OXO..X.X.XOX...\.................................................X.X | 1.3868
    1.3804 | .......OXO..XOXOXOXOX..\..............................................X.XOX | 1.3804
    1.3740 | .......OXO..XOXOXOXOXO..\.................................\...........XOXOX | 1.3740
    1.3677 | .......OXOX.XO.O.OXOXO...\................................X\..........XOXOX | 1.3677
    1.3614 | .......OXOXOX....O.OXO....\...............................XO\.........XOXOX | 1.3614
    1.3552 | .......O.OXOX...../OXO.....\..............................XO.\........XOXOX | 1.3552
    1.3490 | .........OXOX..../.O.OX.....\.............................XO..\.......XOXO. | 1.3490
    1.3428 | .........OXOX.../....OXO.....\X.\.........................XO...\\...X.XOX.. | 1.3428
    1.3366 | .........O.OX../.....OXO......XOX\........................XO....X\..XOXOX.. | 1.3366
    1.3305 | ...........OX./......OXO....X.XOXO\.....................X.XO....XO\.XOXO... | 1.3305
    1.3243 | ...........OX/.......O.O....XOXOXOX\....................XOXO....XO.\XOX.../ | 1.3243
    1.3183 | ...........O/..........OX...XOXOXOXO\...................XOXOX.X.XOX.XOX../. | 1.3183
    1.3122 | .........../...........OXO..XOXOXOXO.\..........X...X.X.XOXOXOXOXOXOXO../.. | 1.3122
    1.3062 | .......................OXOX.XOXO.OXO..\.........XOX.XOXOXOXOXOXOXOXOX../... | 1.3062
    1.3002 | .......................O.OXOXO...O/O...\........XOXOXOXOXO.OXO.OXOXO../.... | 1.3002
    1.2942 | .........................OXOX..../.O....\.......XOXOXOXOX..OX..OXOX../..... | 1.2942
    1.2882 | .........................O.OX.../..O.....\......XOXO.OXO...OX..OXOX./...... | 1.2882
    1.2823 | ...........................OX../...OX.....\.....XO...OX.../OX..O/OX/....... | 1.2823
    1.2764 | ...........................OX./....OXO.....\....X....OX../.O.../.O/........ | 1.2764
    1.2706 | ...........................OX/.....OXO..X...\...X....O../......../......... | 1.2706
    1.2647 | ...........................O/......O.OX.XOX..\..X....../................... | 1.2647
    1.2589 | .........................../.........OXOXOXO..\.X...../.................... | 1.2589
    1.2531 | .....................................OXOXOXO...\X..../..................... | 1.2531
    1.2474 | .....................................OXO.OXO....X.../...................... | 1.2474
    1.2417 | .....................................OX..O.O..X.X../....................... | 1.2417
    1.2359 | .....................................OX....OX.XOX./........................ | 1.2359
    1.2303 | .....................................O.....OXOXOX/......................... | 1.2303
    1.2246 | ...........................................OXOXO/.......................... | 1.2246
    1.2190 | ...........................................OXO./........................... | 1.2190
    1.2134 | ...........................................OX.............................. | 1.2134
    1.2078 | ...........................................O............................... | 1.2078
    1.2023 | ........................................................................... | 1.2023

             222222222222222222222222222222222222222222222222222222222222222222222222222
             000000000000000000000000000000000000000000000000000000000000000000000000000
             111111111111111111111111111111111111111111111111111111111111111111111111111
             111111111111111111111111112222222222222222222222222222222333333333333333344
             ...........................................................................
             000000000001111111111111110000000000000000000000011111111000000000000011100
             788888899990000001111112221122233445566666677888900001222123444567778901213
             ...........................................................................
             200011211220111220011231220101212121201112222001100010001002123110112020231
             658801925683489071404504193396436668111288937260415979579417630739120547713
                                                                                        
             000100001012111111110111111100112010210001111101101101011111111101011101110
             910501876933613095500253237788652909250001557626626824655375907538165785367
             :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
             550433251023230204310404232105354323532031240033315125241340044324523153453
             000000000000000000000000000000000000000000000000000000000000000000000000000

Не стану утверждать, что торговые возможности явно видны на этом графике, предложу лишь проверить две торговые гипотезы:

Торгуйте по тренду — покупайте на бычьем рынке и продавайте на медвежьем.

Торгуйте со стоп-ордерами, определенными до входа в рынок.

  • покупаем над линией поддержки стоп-ордером выше максимума предыдущей колонки "Х", продаем под линией сопротивления стоп-ордером ниже минимума предыдущей колонки "О", скользящий стоп - на уровне разворота;

Дайте прибыли расти.

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

  • покупаем при пробое линии сопротивления, продаем при пробое линии поддержки, стоп-лосс - на уровне разворота, скользящий стоп - по трендовой линии.


А каким объемом входим?

Обратите внимание на приведенные выше цитаты классика биржевых спекуляций: торгуйте со стоп-ордерами. Я предпочитаю входить в рынок таким объемом, чтобы при срабатывании ордера Стоп Лосс потери баланса составляли не более приемлемого для меня процента от этого самого баланса (то, что Ральф Винс в своей Математике управления капиталом назвал оптимальное F). Риск потери с одной сделки - отлично оптимизируемая переменная (в приведенном ниже коде - opt_f).

Таким образом, имеем функцию установки ордера на покупку/продажу с механизмом расчета объема, зависящего от приемлемого для вас риска на одну сделку:

//+------------------------------------------------------------------+
//| Функция выставления ордера с предварительно подсчитанным объемом |
//+------------------------------------------------------------------+
void PlaceOrder()
  {
//--- Переменные для расчета лота
   uint digits_2_lot=(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   double trade_risk=AccountInfoDouble(ACCOUNT_EQUITY)*opt_f;
   double one_tick_loss_min_lot=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE_LOSS)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
//--- Заполняем основные поля запроса
   trade_request.magic=magic;
   trade_request.symbol=symbol;
   trade_request.action=TRADE_ACTION_PENDING;
   trade_request.tp=NULL;
   trade_request.comment=NULL;
   trade_request.type_filling=NULL;
   trade_request.stoplimit=NULL;
   trade_request.type_time=NULL;
   trade_request.expiration=NULL;
   if(is_const_lot==true)
     {
      order_vol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
     }
   else
     {
      order_vol=trade_risk/(MathAbs(trade_request.price-trade_request.sl)*MathPow(10,digits_2_lot)*one_tick_loss_min_lot)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
      order_vol=MathMax(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN));
      if(SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT)!=0) order_vol=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT));
      order_vol=NormalizeDouble(order_vol,(int)MathAbs(MathLog10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP))));
     }
//--- Ставим ордер
   while(order_vol>0)
     {
      trade_request.volume=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX));
      if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
      order_vol=order_vol-SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
     };
   ticket=trade_result.order;
  };


А по какому критерию оптимизируем?

Реально критериев оптимизации всего два: либо минимизация просадки при заданном уровне доходности, либо максимизация баланса при заданном уровне просадки. Я предпочитаю оптимизировать по второму критерию:

//+------------------------------------------------------------------+
//| Результат работы стратегии в режиме тестирования                 |
//+------------------------------------------------------------------+
double OnTester()
  {
   if(TesterStatistics(STAT_EQUITY_DDREL_PERCENT)>(risk*100))
      return(0);
   else
      return(NormalizeDouble(TesterStatistics(STAT_PROFIT),(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS)));
  };

где risk - уровень просадки, выше которого стратегия для меня считается неприемлемой. Эта также одна из оптимизируемых переменных.


А когда необходимо оптимизировать повторно?

Кто-то проводит повторную оптимизацию через интервалы времени (например, раз в неделю, в месяц), кто-то - через интервалы сделок (через 50 сделок, через 100), кто-то - как рынок поменяется. Реально же необходимость в повторной оптимизации диктуется лишь принятым критерием оптимизации в предыдущем пункте. Просела система ниже максимально допустимого параметра risk - проводим повторную оптимизацию, не просела - не трогаем. Для себя считаю неприемлемой просадку больше 10%. Таким образом, если просадка в процессе реальной работы системы превышает это значение - оптимизирую повторно.


А оптимизировать сразу все?

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

//+------------------------------------------------------------------+
//| Перечисление символов                                            |
//+------------------------------------------------------------------+
enum  SYMBOLS
  {
   AA=1,
   AIG,
   AXP,
   BA,
   C,
   CAT,
   DD,
   DIS,
   GE,
   HD,
   HON,
   HPQ,
   IBM,
   IP,
   INTC,
   JNJ,
   JPM,
   KO,
   MCD,
   MMM,
   MO,
   MRK,
   MSFT,
   PFE,
   PG,
   QQQ,
   T,
   SPY,
   UTX,
   VZ,
   WMT,
   XOM
  };
//+------------------------------------------------------------------+
//| Функция выбора символа                                           |
//+------------------------------------------------------------------+
void  SelectSymbol()
  {
   switch(selected_symbol)
     {
      case  1: symbol="#AA";   break;
      case  2: symbol="#AIG";  break;
      case  3: symbol="#AXP";  break;
      case  4: symbol="#BA";   break;
      case  5: symbol="#C";    break;
      case  6: symbol="#CAT";  break;
      case  7: symbol="#DD";   break;
      case  8: symbol="#DIS";  break;
      case  9: symbol="#GE";   break;
      case 10: symbol="#HD";   break;
      case 11: symbol="#HON";  break;
      case 12: symbol="#HPQ";  break;
      case 13: symbol="#IBM";  break;
      case 14: symbol="#IP";   break;
      case 15: symbol="#INTC"; break;
      case 16: symbol="#JNJ";  break;
      case 17: symbol="#JPM";  break;
      case 18: symbol="#KO";   break;
      case 19: symbol="#MCD";  break;
      case 20: symbol="#MMM";  break;
      case 21: symbol="#MO";   break;
      case 22: symbol="#MRK";  break;
      case 23: symbol="#MSFT"; break;
      case 24: symbol="#PFE";  break;
      case 25: symbol="#PG";   break;
      case 26: symbol="#QQQ";  break;
      case 27: symbol="#T";    break;
      case 28: symbol="#SPY";  break;
      case 29: symbol="#UTX";  break;
      case 30: symbol="#VZ";   break;
      case 31: symbol="#WMT";  break;
      case 32: symbol="#XOM";  break;
      default: symbol="#SPY";  break;
     };
  };

При необходимости вы можете добавлять в перечисление и функцию выбора символа (которую вызовем в OnInit()) нужные вам инструменты.


Что за робот у нас получился?

Обработчик тиков:

//+------------------------------------------------------------------+
//| Типичный обработчик тиков OnTick()                               |
//|     График строим только по сформированным барам и сначала       |
//|     проверяем, не новый ли сейчас бар?                           |
//|     Если бар новый, то при наличии позиций проверяем,            |
//|     не нужно ли передвинуть стоп-лосс,                           |
//|     а при отсутствии позиций проверяем,                          |
//|     может есть условия для открытия сделки?                      |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Если новый бар
   if(IsNewBar()==true)
     {
      RecalcIndicators();
      //--- Режим тестера/оптимизатора?
      if((MQLInfoInteger(MQL_TESTER)==true) || (MQLInfoInteger(MQL_OPTIMIZATION)==true))
        {
         //--- Это уже период тестирования?
         if(cur_bar_time_dig[0]>begin_of_test)
           {
            //--- Если есть открытая по символу позиция
            if(PositionSelect(symbol)==true)
               //--- проверяем, не нужно-ли передвинуть SL, и если нужно - передвигаем
               TrailCondition();
            //--- а если позиций нет
            else
            //--- проверяем, нужно-ли открыть позицию, и если нужно - открываем
               TradeCondition();
           }
        }
      else
        {
         //--- Если есть открытая по символу позиция
         if(PositionSelect(symbol)==true)
            //--- проверяем, не нужно-ли передвинуть SL, и если нужно - передвигаем
            TrailCondition();
         //--- а если позиций нет
         else
         //--- проверяем, нужно-ли открыть позицию, и если нужно - открываем
            TradeCondition();
        }

     };
  };

Для стратегии №1 "покупаем над линией поддержки стоп-ордером выше максимума предыдущей колонки "Х", продаем под линией сопротивления стоп-ордером ниже минимума предыдущей колонки "О", скользящий стоп - на уровне разворота":

//+------------------------------------------------------------------+
//| Функция проверки торговых условий для открытия сделки            |
//+------------------------------------------------------------------+
void TradeCondition()
  {
   if(order_col_number!=column_count)
      //--- Завалялись какие-то ордера по символу?
     {
      if(OrdersTotal()>0)
        {
         //--- Удалить их!
         for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++)
           {
            ticket=OrderGetTicket(loc_count_1);
            if(!OrderSelect(ticket)) Print("Failed to select order #",ticket);
            if(OrderGetString(ORDER_SYMBOL)==symbol)
              {
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
              };
           };
         order_col_number=column_count;
         return;
        }
      else
        {
         order_col_number=column_count;
         return;
        }
     }
   else
      if((MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK)) && 
         (pnf[column_count-1].column_type=='X') && 
         (pnf[column_count-1].max_column_price<=pnf[column_count-3].max_column_price))
        {
         //--- Условия для BUY выполнены, смотрим, а нет ли отложенных ордеров BUY по символу с нужной ценой?
         trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price+double_box),digit_2_orders);
         trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price-(reverse-1)*double_box),digit_2_orders);
         trade_request.type=ORDER_TYPE_BUY_STOP;
         if(OrderSelect(ticket)==false)
            //--- Нет, отложенных ордеров нет - размещаем ордер
           {
            PlaceOrder();
            order_col_number=column_count;
           }
         else
         //--- или отложенный ордер есть
           {
            //--- а что за тип и цена у отложенного ордера?
            if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || 
               ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
              {
               //--- Тип не тот или цена отличается - закрываем ордер
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
               //--- и открываем с нужной ценой
               PlaceOrder();
               order_col_number=column_count;
              };
           };
         return;
        }
   else
      if((MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK)) && 
         (pnf[column_count-1].column_type=='O') && 
         (pnf[column_count-1].min_column_price>=pnf[column_count-3].min_column_price))
        {
         //--- Условия для SELL выполнены, смотрим, а нет ли отложенных ордеров SELL по символу с нужной ценой?
         trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price-double_box),digit_2_orders);
         trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price+(reverse-1)*double_box),digit_2_orders);
         trade_request.type=ORDER_TYPE_SELL_STOP;
         if(OrderSelect(ticket)==false)
            //--- Нет, отложенных ордеров нет - размещаем ордер
           {
            PlaceOrder();
            order_col_number=column_count;
           }
         else
         //--- или отложенный ордер есть
           {
            //--- а что за тип и цена у отложенного ордера?
            if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || 
               ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
              {
               //--- Тип не тот или цена отличается - закрываем ордер
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
               //--- и открываем с нужной ценой
               PlaceOrder();
               order_col_number=column_count;
              };
           };
         return;
        }
   else
      return;
  };
//+------------------------------------------------------------------+
//| Функция проверки условия для перемещения стоп-лосса              |
//+------------------------------------------------------------------+
void TrailCondition()
  {
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box),digit_2_orders);
   else
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box),digit_2_orders);
   if(PositionGetDouble(POSITION_SL)!=trade_request.sl)
      PlaceTrailOrder();
  };

Для стратегии №2 "покупаем при пробое линии сопротивления, продаем при пробое линии поддержки, стоп-лосс - на уровне разворота, скользящий стоп - по трендовой линии":

//+------------------------------------------------------------------+
//| Функция проверки торговых условий для открытия сделки            |
//+------------------------------------------------------------------+
void TradeCondition()
  {
   if(order_col_number!=column_count)
      //--- Завалялись какие-то ордера по символу?
     {
      if(OrdersTotal()>0)
        {
         //--- Удалить их!
         for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++)
           {
            ticket=OrderGetTicket(loc_count_1);
            if(!OrderSelect(ticket)) Print("Failed to select order #",ticket);
            if(OrderGetString(ORDER_SYMBOL)==symbol)
              {
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
              };
           };
         order_col_number=column_count;
         return;
        }
      else
        {
         order_col_number=column_count;
         return;
        }
     }
   else
   if(MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK))
     {
      //--- Условия для BUY выполнены, смотрим, а нет ли отложенных ордеров BUY по символу с нужной ценой?
      trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price),digit_2_orders);
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price-(reverse-1)*double_box),digit_2_orders);
      trade_request.type=ORDER_TYPE_BUY_STOP;
      if(OrderSelect(ticket)==false)
         //--- Нет, отложенных ордеров нет - размещаем ордер
        {
         PlaceOrder();
         order_col_number=column_count;
        }
      else
      //--- или отложенный ордер есть
        {
         //--- а что за тип и цена у отложенного ордера?
         if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || 
            ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
           {
            //--- Тип не тот или цена отличается - закрываем ордер
            trade_request.order=ticket;
            trade_request.action=TRADE_ACTION_REMOVE;
            if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
            //--- и открываем с нужной ценой
            PlaceOrder();
            order_col_number=column_count;
           };
        };
      return;
     }
   else
   if(MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK))
     {
      //--- Условия для SELL выполнены, смотрим, а нет ли отложенных ордеров SELL по символу с нужной ценой?
      trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price),digit_2_orders);
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price+(reverse-1)*double_box),digit_2_orders);
      trade_request.type=ORDER_TYPE_SELL_STOP;
      if(OrderSelect(ticket)==false)
         //--- Нет, отложенных ордеров нет - размещаем ордер
        {
         PlaceOrder();
         order_col_number=column_count;
        }
      else
      //--- или отложенный ордер есть
        {
         //--- а что за тип и цена у отложенного ордера?
         if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || 
            ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
           {
            //--- Тип не тот или цена отличается - закрываем ордер
            trade_request.order=ticket;
            trade_request.action=TRADE_ACTION_REMOVE;
            if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
            //--- и открываем с нужной ценой
            PlaceOrder();
            order_col_number=column_count;
           };
        };
      return;
     }
   else
      return;
  };
//+------------------------------------------------------------------+
//| Функция проверки условия для перемещения стоп-лосса              |
//+------------------------------------------------------------------+
void TrailCondition()
  {
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
      trade_request.sl=NormalizeDouble(MathMax(SymbolInfoDouble(symbol,SYMBOL_ASK),MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box)),digit_2_orders);
   else
      trade_request.sl=NormalizeDouble(MathMin(SymbolInfoDouble(symbol,SYMBOL_BID),MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box)),digit_2_orders);
   if(PositionGetDouble(POSITION_SL)!=trade_request.sl)
      PlaceTrailOrder();
  };

Обратите внимание, уважаемый читатель, на несколько тонкостей.

  • Цены на рыночные инструменты колеблются в довольно широком диапазоне от центов до десятков тысяч (пример - акции и контракты на разницу на японских биржах). Поэтому при построении графика крестиков-ноликов я использую логарифм цены, чтобы не вводить в качестве размеров бокса значения от 1 пипса до тысяч и десятков тысяч пипсов.
  • Массив графика крестиков-ноликов начинает индексироваться с нуля, последняя колонка имеет индекс, соответственно, число колонок минус один.
  • При наличии линии поддержки ее значение будет больше -10.0, но меньше логарифма цены, при отсутствии линии поддержки (и сопротивления тоже) ее значение в массиве графика составляет -10.0. Поэтому условия пробоя линии поддержки/сопротивления записаны в таком виде, как выше в коде стратегии №2.

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


Результаты роботрейдинга

Для оптимизации я подготовил два набора рыночных инструментов в файлах symbol_list_1.mhq и symbol_list_2.mhq: валютные пары и контракты на разницу по акциям, входящим в индекс Доу.

Окно настроек:

Окно параметров в тестере в первом случае для нашей стратегии имеет следующий вид:

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

Значение false для параметра "Торгуем минимальным лотом?" говорит о том, что размер лота у нас переменный, зависит от баланса и переменной "Риск на сделку, %" (в нашем случае 1% на сделку, но его можно также оптимизировать). "Допустимая просадка, %" - тот самый критерий оптимизации из функции OnTester(). Для примера я выбрал для оптимизации лишь две переменные: торговый инструмент "Символ" и "Размер бокса в пипсах".

Оптимизацию проведем на данных 2012-2013 года. Сам робот лучше всего прикрепить к графику EURUSD, как символу с наибольшим покрытием тиками. В таблице ниже приведу полный отчет для тестирования по валютным парам для размера бокса 10 по первой стратегии:

Pass Result Profit Expected Payoff Profit Factor Recovery Factor Sharpe Ratio Custom Equity DD % Trades selected_symbol box
0,00 0,00 -1 002,12 -18,91 0,54 -0,79 -0,24 0,00 12,67 53,00 AUDCAD 10,00
1,00 886,56 886,56 14,53 1,40 1,52 0,13 886,56 5,76 61,00 AUDCHF 10,00
2,00 0,00 -1 451,63 -10,60 0,77 -0,70 -0,09 0,00 19,92 137,00 AUDJPY 10,00
3,00 -647,66 -647,66 -17,50 0,57 -0,68 -0,24 -647,66 9,46 37,00 AUDNZD 10,00
4,00 -269,22 -269,22 -3,17 0,92 -0,26 -0,03 -269,22 9,78 85,00 AUDUSD 10,00
5,00 0,00 -811,44 -13,52 0,72 -0,64 -0,14 0,00 12,20 60,00 CADCHF 10,00
6,00 0,00 1 686,34 16,53 1,36 1,17 0,12 0,00 11,78 102,00 CHFJPY 10,00
7,00 356,68 356,68 5,66 1,13 0,40 0,06 356,68 8,04 63,00 EURAUD 10,00
8,00 0,00 -1 437,91 -25,68 0,53 -0,92 -0,25 0,00 15,47 56,00 EURCAD 10,00
9,00 0,00 -886,66 -46,67 0,34 -0,74 -0,46 0,00 11,56 19,00 EURCHF 10,00
10,00 0,00 -789,59 -21,93 0,54 -0,75 -0,26 0,00 10,34 36,00 EURGBP 10,00
11,00 0,00 3 074,86 28,47 1,62 1,72 0,20 0,00 12,67 108,00 EURJPY 10,00
12,00 0,00 -1 621,85 -19,78 0,55 -0,97 -0,25 0,00 16,75 82,00 EURNZD 10,00
13,00 152,73 152,73 2,88 1,07 0,21 0,03 152,73 6,90 53,00 EURUSD 10,00
14,00 0,00 -1 058,85 -14,50 0,65 -0,66 -0,16 0,00 15,87 73,00 GBPAUD 10,00
15,00 0,00 -1 343,47 -25,35 0,43 -0,64 -0,34 0,00 20,90 53,00 GBPCAD 10,00
16,00 0,00 -2 607,22 -44,19 0,27 -0,95 -0,59 0,00 27,15 59,00 GBPCHF 10,00
17,00 0,00 1 160,54 11,72 1,27 0,81 0,10 0,00 12,30 99,00 GBPJPY 10,00
18,00 0,00 -1 249,91 -14,70 0,69 -0,85 -0,15 0,00 14,41 85,00 GBPNZD 10,00
19,00 208,94 208,94 5,36 1,12 0,25 0,05 208,94 7,81 39,00 GBPUSD 10,00
20,00 0,00 -2 137,68 -21,17 0,53 -0,79 -0,24 0,00 25,62 101,00 NZDUSD 10,00
21,00 0,00 -1 766,80 -38,41 0,30 -0,97 -0,53 0,00 18,10 46,00 USDCAD 10,00
22,00 -824,69 -824,69 -11,95 0,73 -0,90 -0,13 -824,69 9,11 69,00 USDCHF 10,00
23,00 2 166,53 2 166,53 26,10 1,58 2,40 0,18 2 166,53 7,13 83,00 USDJPY 10,00

2 029,87 -10 213,52




13,40 1 659,00

и сводную таблицу для разных инструментов и размеров бокса:

Стратегия Инструменты Размер бокса Trades Equity DD % Profit Result Прогноз баланса
1 Валюты 10 1 659 13 -10 214 2 030 2 030
1 Валюты 20 400 5 1 638 2 484 2 484
1 Акции 50 350 4 7 599 7 599 15 199
1 Акции 100 81 2 4 415 4 415 17 659
2 Валюты 10 338 20 -4 055 138 138
2 Валюты 20 116 8 4 687 3 986 3 986
2 Акции 50 65 6 6 770 9 244 9 244
2 Акции 100 12 1 -332 -332 -5 315

Что мы видим?

Существуют круглые дураки, которые все и всегда делают неверно.

И существуют дураки с Уолл-Стрит, которые считают, что торговать надо всегда.

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

Видим неожиданную для многих картину: депозит с большей вероятностью будет выше при меньшем числе сделок. Если бы мы без всякой оптимизации два года назад накинули на акции наш эксперт и задали размер бокса 100, а риск на сделку 1%, то за два года робот совершил бы всего 81 сделку (в среднем за год по одному инструменту 1,25 сделки), наш депо вырос бы на 44%, и при этом в среднем просадка по эквити была бы чуть выше 2%. Принимая для себя допустимую просадку в 10%, мы бы могли рисковать на сделку в 4% и за два года депозит бы прибавил 177%, доходность - под 90% годовых в долларах США!


Эпилог

Курс никогда не бывает слишком высоким, чтобы начать покупать, и никогда не бывает слишком низким, чтобы начать продавать.

Большие деньги делаются не в раздумьях, а в ожидании.

Предложенные к рассмотрению стратегии могут быть модифицированы и они покажут даже большую доходность при просадке не выше 10%. Не пытайтесь мельтешить, торговать часто, лучше найдите брокера, который предоставляет не просто "стандартный набор" инструментов из двух десятков валютных пар и трех десятков акций, а хотя бы сотни три-четыре инструментов (акций, фьючерсов). С большей вероятностью инструменты не будут коррелированы и ваш депозит будет в большей безопасности. И да, акции почему-то показывают лучшие результаты, чем валютные пары.


P.S. (на правах рекламы)

В Маркете я предлагаю скрипт PnF Chartist для построения графиков крестиков-ноликов в текстовых файлах из котировок терминалов МТ4, МТ5 или Yahoo finance. Используйте его для визуального поиска паттернов поведения цен, ибо нет лучше тестера/оптимизатора, чем своя голова, а найдя закономерности - воспользуйтесь шаблонами экспертов из статьи для проверки в боевых условиях ваших задумок.

Прикрепленные файлы |
licence.txt (1.05 KB)
symbol_list_1.mqh (2.43 KB)
symbol_list_2.mqh (2.17 KB)
strategy_one.mq5 (37.89 KB)
strategy_two.mq5 (37.43 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (8)
Andrey Dik
Andrey Dik | 13 апр. 2014 в 18:26
очень интересно. спасибо. 
Roman Zamozhnyy
Roman Zamozhnyy | 24 апр. 2014 в 15:43
Переработанный для МТ4 эксперт.
Alexandr Gershkevich
Alexandr Gershkevich | 29 апр. 2014 в 07:09
Радует продуманность, детальность и "вероятностная" надежда на успех. Спасибо.
Andriy Sydoruk
Andriy Sydoruk | 7 июл. 2014 в 23:57
Очень интерестно!
fiacko
fiacko | 17 мар. 2015 в 09:42
Rich:
Переработанный для МТ4 эксперт.

Добрый день. В режиме тестирования все роботы вылетают с ошибками. Это касается как mt4 так и mt5

 zero divide in 'strategy_two.mq5' (348,99)

 или 

array out of range in 'strategy_one.mq5' (438,32)

 

Никак не могу понять в чем причина, подскажите плз 

Почему нужно обновить MetaTrader 4 на последний билд до 1 августа? Почему нужно обновить MetaTrader 4 на последний билд до 1 августа?
C 1 августа 2014 года прекращается поддержка десктопных терминалов MetaTrader 4 ниже 600-го билда. А ведь многие трейдеры продолжают сидеть на привычных старых версиях и не знают о возможностях обновленной платформы. Мы вложили много сил в ее разработку и хотели бы вместе с трейдерами двигаться дальше и отказаться от старых билдов. В этой статье мы расскажем о преимуществах нового MetaTrader 4.
Рецепты MQL5 - Мультивалютный советник и работа с отложенными ордерами на MQL5 Рецепты MQL5 - Мультивалютный советник и работа с отложенными ордерами на MQL5
На этот раз рассмотрим создание мультивалютного советника, торговый алгоритм которого строится на работе с отложенными ордерами Buy Stop и Sell Stop. В статье будут рассмотрены следующие вопросы: торговля в указанном временном диапазоне, установка/модификация/удаление отложенных ордеров, проверка закрытия последней позиции по Тейк Профит или Стоп Лосс, контроль истории сделок на каждом символе.
Нужны ли трейдерам услуги разработчиков? Нужны ли трейдерам услуги разработчиков?
Алготрейдинг становится все более популярным и востребованным, что закономерно привело к появлению спроса на экзотические алгоритмы и нестандартные задачи. Определенная часть таких сложных приложений представлена в Code Base или Маркете и их можно получить за пару кликов, но не всё в них устраивает трейдеров. В этом случае они начинают искать разработчиков, способных написать требуемое приложение, находят их во Фрилансе и выдают заказ.
Типичные ошибки в программах на MQL4 и методы их устранения Типичные ошибки в программах на MQL4 и методы их устранения
В старой версии компилятора во избежание критического завершения программ многие ошибки обрабатывались средой исполнения. Например, деление на ноль или выход за пределы массива являются критическими ошибками и обычно приводят к аварийному завершению работы программ. Новый компилятор позволяет обнаружить реальные или потенциальные источники ошибок и повысить качество кода. В этой статье мы рассмотрим возможные ошибки, возникающие при компиляции старых программ и методы их устранения.