Комфортная пипсовка

Eryomin Sergey | 14 февраля, 2008

Введение

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

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

Данная статья является продолжением статьи "Моделирование беттинга как средство развития "чувства рынка"". Рекомендую ознакомиться с ней, прежде чем читать далее.

Хочется так же напомнить, что такое "пипсовка". Пипсовка это скоротечная торговля. Как правило, при таком подходе к торговле прибыль фиксируется в 1-10 пипсов (пунктов). Пипсовка славится своей сложностью, нервностью, потребностью в повышенном внимании. Кто-то относится к ней несерьёзно, кто-то считает высшим пилотажем. Однако оценку данной торговли я давать не буду. Об этом написано достаточно много и у каждого своё мнение.


Концепция

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

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

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

Значит, нужен инструмент, который по команде трейдера будет открывать сделку с фиксированным лотом и уровнем TP и SL. Инструмент этот должен работать достаточно просто и по-минимуму отвлекать трейдера от ценового графика.

Создать подобный инструмент достаточно просто средствами языка MQL4.


Реализация

За основу будет взята игра, о которой написано в статье "Моделирование беттинга как средство развития "чувства рынка"". Будет создан инструмент, благодаря которому будет возможно одновременно и играть в эту игру, и торговать.

Кратко об игре. При игре на график наносятся две стрелочки, вверх и вниз. Трейдеру нужно удалить "лишнюю" стрелочку, тем самым, сделав выбор – вырастет, или упадёт инструмент по его мнению. В начале новой свечи советник проверяет, дал трейдер верный прогноз или нет. Успешность прогноза влияет на игровой счет. Кроме того, сделать свой выбор трейдер может ограниченное количество времени, которое он сам может установить, как и совсем убрать его.

Для реализации, будем рисовать ещё две стрелочки. На один бар назад от текущего. Текущий бар, по-прежнему, будет использоваться для игры. Удаление одной из стрелочек на предыдущем баре будет сигналом для советника к совершению сделки в указанном направлении. Кроме того, для торговли будет убрано ограничение выбора по времени. В изменяемые переменные будет вынесен уровень TP и SL, лот, допустимое проскальзывание и магическое число. Помимо этого, будет возможно через extern bool переменную отключать поддержку торговли, сводя советника только к игре.

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

Сам робот будет разбит на два блока, - сама игра и открытие сделок. Таким образом, читатель сможет проследить, что конкретно было добавлено в код.

В итоге код, со всеми добавлениями получился следующий:

//+------------------------------------------------------------------+
//|                                                       trener.mq4 |
//|                                       Copyright © 2008, FXRaider |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2008, FXRaider"
extern int gap=2;
extern bool Trading=true;
extern int TP=2;
extern int SL=20;
extern double Lots=0.02;
extern int slippage=1;
extern int MagicNumber=777;
extern int time_limit=30;
int start()
  {
//----
//#################################################################################
//####################################### ИГРА ####################################
//------------------------------
string solution="none"; 
int point, 
    point_neg, 
    point_pos;
//------------------------------    
//+---------------------------------------------------------------+
//|                      поиск выбора "вверх"                     | 
 if(
    ObjectGet("up", OBJPROP_PRICE1)==Open[1]+gap*Point
    &&iBarShift(NULL,0,ObjectGet("up",OBJPROP_TIME1))==1
    &&ObjectFind("down") != 0
    &&ObjectFind("up") == 0
    )
    {
     solution="up";
    }
//|                      поиск выбора "вверх"                     |  
//+---------------------------------------------------------------+
  
//+---------------------------------------------------------------+
//|                      поиск выбора "вниз"                      |     
 if(
    ObjectGet("down", OBJPROP_PRICE1)==Open[1]-gap*Point
    &&iBarShift(NULL,0,ObjectGet("down",OBJPROP_TIME1))==1    
    &&ObjectFind("up") != 0
    &&ObjectFind("down") == 0
    )
    {
     solution="down";
    }
//|                      поиск выбора "вниз"                      |       
//+---------------------------------------------------------------+    
 
//+---------------------------------------------------------------+
//|             расчёт очков при положительном ответе             |
    if((solution=="up"&&Open[1]<Close[1])
      ||(solution=="down"&&Open[1]>Close[1]))
    {
     point=1;
     point_pos=1;
     point_neg=0;     
    }  
//|             расчёт очков при положительном ответе             |   
//+---------------------------------------------------------------+
 
//+---------------------------------------------------------------+
//|             расчёт очков при отрицательном ответе             |    
    if((solution=="up"&&Open[1]>Close[1])
      ||(solution=="down"&&Open[1]<Close[1]))
    {
     point=-1;
     point_pos=0;
     point_neg=1;     
    } 
//|             расчёт очков при отрицательном ответе             |
//+---------------------------------------------------------------+
 
//+----------------------------------------------------------------------------------+
//|                              работа с внешним файлом                             |       
      int handle; 
      double points,     //общий счёт
             points_pos, //счёт положительных ответов
             points_neg; //счёт отрицательных ответов 
       handle=FileOpen("trener_"+Symbol()+"_"+Period()+".csv",
                       FILE_CSV|FILE_WRITE|FILE_READ,";");
       if(handle>0) //если файл есть, то читаем его
       { 
        points=NormalizeDouble(StrToDouble(FileReadString(handle)),Digits); 
        points_pos=NormalizeDouble(StrToDouble(FileReadString(handle)),Digits); 
        points_neg=NormalizeDouble(StrToDouble(FileReadString(handle)),Digits);       
        FileClose(handle);
       } 
       
    if(solution!="none") //если выбор сделан 
    {        
      handle=FileOpen("trener_"+Symbol()+"_"+Period()+".csv",
                      FILE_CSV|FILE_WRITE|FILE_READ,";");
      FileWrite(handle ,points+point);         //записываем общий счёт
      FileWrite(handle ,points_pos+point_pos); //записываем счёт положительных ответов
      FileWrite(handle ,points_neg+point_neg); //записываем счёт отрицательных ответов                    
      FileClose(handle); 
    } 
//|                              работа с внешним файлом                             | 
//+----------------------------------------------------------------------------------+    
 
//+------------------------------------------------------------------------------------+
//|                                 работа с объектами                                 |   
  if(iBarShift(NULL,0,ObjectGet("down",OBJPROP_TIME1))>0
     ||ObjectGet("down",OBJPROP_PRICE1)!=Open[0]-gap*Point) 
    {
     ObjectDelete("down"); 
    }  
 if(iBarShift(NULL,0,ObjectGet("up",OBJPROP_TIME1))>0
    ||ObjectGet("up",OBJPROP_PRICE1)!=Open[0]+gap*Point)            
    { 
     ObjectDelete("up"); 
    } 
   
  int sec_lim;  
  if(!time_limit)
  {
   sec_lim=0; 
  }
  else
  {
   sec_lim=TimeCurrent()-time_limit;
  }
  if(sec_lim>ObjectGet("up",OBJPROP_TIME1)
     &&sec_lim>ObjectGet("down",OBJPROP_TIME1) 
     &&ObjectFind("down") == 0&&ObjectFind("up") == 0
     &&iBarShift(NULL,0,ObjectGet("down",OBJPROP_TIME1))==0
     &&iBarShift(NULL,0,ObjectGet("up",OBJPROP_TIME1))==0)            
    { 
     ObjectDelete("up"); 
     ObjectDelete("down");      
    } 
  
   if((ObjectFind("down") != 0&&ObjectFind("up") != 0) //если объектов нет
      &&sec_lim<Time[0])
   {
     ObjectCreate("down", OBJ_ARROW, 0, Time[0], Open[0]-gap*Point); //рисуем стрелку вниз
     ObjectSet("down", OBJPROP_STYLE, STYLE_DOT);
     ObjectSet("down", OBJPROP_ARROWCODE, SYMBOL_ARROWDOWN);
     ObjectSet("down", OBJPROP_COLOR, Red);
 
     ObjectCreate("up", OBJ_ARROW, 0, Time[0], Open[0]+gap*Point); //рисуем стрелку вверх
     ObjectSet("up", OBJPROP_STYLE, STYLE_DOT);
     ObjectSet("up", OBJPROP_ARROWCODE, SYMBOL_ARROWUP);
     ObjectSet("up", OBJPROP_COLOR, Blue);
    }      
//|                                 работа с объектами                                 |   
//+------------------------------------------------------------------------------------+
//####################################### ИГРА ####################################
//#################################################################################
 
 
//#################################################################################
//#################################### ТОРГОВЛЯ ###################################
//+------------------------------------------------------------------------------------+  
//|                                работа с объектами I                                | 
  if(iBarShift(NULL,0,ObjectGet("down_1",OBJPROP_TIME1))>1
  ||ObjectGet("down_1",OBJPROP_PRICE1)!=Open[0]-gap*Point
  ||!Trading)  
  {
   ObjectDelete("down_1"); 
  }  
  
  if(iBarShift(NULL,0,ObjectGet("up_1",OBJPROP_TIME1))>1
  ||ObjectGet("up_1",OBJPROP_PRICE1)!=Open[0]+gap*Point
  ||!Trading)  
  { 
   ObjectDelete("up_1"); 
  } 
  
  if(iBarShift(NULL,0,ObjectGet("sell",OBJPROP_TIME1))>0
  ||ObjectGet("sell",OBJPROP_PRICE1)!=Open[0]-300*Point
  ||!Trading) 
  {
   ObjectDelete("sell"); 
  }
  if(iBarShift(NULL,0,ObjectGet("buy",OBJPROP_TIME1))>0
  ||ObjectGet("buy",OBJPROP_PRICE1)!=Open[0]+300*Point 
  ||!Trading)
  
  {
   ObjectDelete("buy"); 
  } 
  
   if(ObjectFind("down_1") != 0&&ObjectFind("up_1") != 0 && Trading)
   {
     ObjectCreate("down_1", OBJ_ARROW, 0, Time[1], Open[0]-gap*Point);
     ObjectSet("down_1", OBJPROP_STYLE, STYLE_DOT);
     ObjectSet("down_1", OBJPROP_ARROWCODE, SYMBOL_ARROWDOWN);
     ObjectSet("down_1", OBJPROP_COLOR, Red);
 
     ObjectCreate("up_1", OBJ_ARROW, 0, Time[1], Open[0]+gap*Point);
     ObjectSet("up_1", OBJPROP_STYLE, STYLE_DOT);
     ObjectSet("up_1", OBJPROP_ARROWCODE, SYMBOL_ARROWUP);
     ObjectSet("up_1", OBJPROP_COLOR, Blue);
    }  
//|                                работа с объектами I                                |    
//+------------------------------------------------------------------------------------+     
 if(Trading)
 {
//+----------------------------------------------------------------------------------------------+
//|                              поиск открытых ордеров по паре                                  |
    int pos_sell=0, bar_op_buy, bar_op_sell;
  for (int i_op_sell=OrdersTotal()-1; i_op_sell>=0; i_op_sell--) 
  { 
   if (!OrderSelect(i_op_sell,SELECT_BY_POS,MODE_TRADES)) break; 
   if (Symbol()==OrderSymbol()
   &&(OrderType()==OP_SELLSTOP||OrderType()==OP_SELL)
   &&(OrderMagicNumber()==MagicNumber)
   &&iBarShift(NULL,0,OrderOpenTime())==0)
   {
    pos_sell=1; break;   
   } 
  }
    
    int pos_buy=0;
  for (int i_op_buy=OrdersTotal()-1; i_op_buy>=0; i_op_buy--) 
  { 
   if (!OrderSelect(i_op_buy,SELECT_BY_POS,MODE_TRADES)) break; 
   if (Symbol()==OrderSymbol()
   &&(OrderType()==OP_BUYSTOP||OrderType()==OP_BUY)
   &&(OrderMagicNumber()==MagicNumber)
   &&iBarShift(NULL,0,OrderOpenTime())==0)
   {
    pos_buy=1; break;   
   } 
  }    
//|                              поиск открытых ордеров по паре                                  |
//+----------------------------------------------------------------------------------------------+ 
 
//+------------------------------------------------------------------------------------+  
//|                                работа с объектами II                               |   
 if(pos_buy==1)
 {   
      ObjectCreate("buy", OBJ_ARROW, 0, Time[0], Open[0]+300*Point);
      ObjectSet("buy", OBJPROP_STYLE, STYLE_DOT);
      ObjectSet("buy", OBJPROP_ARROWCODE, SYMBOL_ARROWUP);
      ObjectSet("buy", OBJPROP_COLOR, Red);
 }
 
 if(pos_sell==1)
 {
      ObjectCreate("sell", OBJ_ARROW, 0, Time[0], Open[0]-300*Point);
      ObjectSet("sell", OBJPROP_STYLE, STYLE_DOT);
      ObjectSet("sell", OBJPROP_ARROWCODE, SYMBOL_ARROWDOWN);
      ObjectSet("sell", OBJPROP_COLOR, Red);    
 }
//|                                работа с объектами II                               |   
//+------------------------------------------------------------------------------------+ 
  
//+------------------------------------------------------------------------------------+ 
//|                                   открытие сделок                                  |
double sl_buy, sl_sell;
 if(!SL)
 { 
  sl_buy=0;
  sl_sell=0;
 }
 else
 {
  sl_buy=Ask-SL*Point;
  sl_sell=Bid+SL*Point;
 }
  if(
     ObjectGet("up_1", OBJPROP_PRICE1)==Open[0]+gap*Point
     &&iBarShift(NULL,0,ObjectGet("up_1",OBJPROP_TIME1))==1
     &&ObjectFind("down_1") != 0
     &&ObjectFind("up_1") == 0
     &&!pos_buy
     &&ObjectFind("buy") != 0    
     )
     {
      OrderSend(Symbol(),OP_BUY, Lots,Ask,slippage,sl_buy,Ask+TP*Point,"trener",MagicNumber,0,Blue);            
     }
  if(
     ObjectGet("down_1", OBJPROP_PRICE1)==Open[0]-gap*Point
     &&iBarShift(NULL,0,ObjectGet("down_1",OBJPROP_TIME1))==1
     &&ObjectFind("up_1") != 0
     &&ObjectFind("down_1") == 0
     &&!pos_sell
     &&ObjectFind("sell") != 0
     )
     {
      OrderSend(Symbol(),OP_SELL, Lots,Bid,slippage,sl_sell,Bid-TP*Point,"trener",MagicNumber,0,Red);  
     }
//|                                   открытие сделок                                  |  
//+------------------------------------------------------------------------------------+   
 }
//#################################### ТОРГОВЛЯ ###################################
//#################################################################################
 
Comment("Score: ", points," (",points_pos,"/",points_neg,   //выводим счёт
        ") | Time: ", Hour(),":", Minute(),":", Seconds()); //выводим время (для удобства) 
     
   return(0);
  }
//+------------------------------------------------------------------+

Код снабжён всеми необходимыми комментариями.

После нанесения советника на график, будет получена следующая картина:

Где две последние стрелочки - для игры, две стрелочки на предыдущей свече - для открытия ордеров.

Удаление стрелочки на предыдущей свече вызовет выполнение функции OrderSend(), и будет открыт необходимый ордер:



Окно изменения входных параметров будет выглядеть следующим образом:


Переменная "gap" отвечает за количество пунктов, на которые отдалены стрелочки от цены открытия свечи. Переменная "Trading" отвечает за поддержку функции торговли. Переменная "TP" - отвечает за величину уровня TP, в пунктах. Переменная "SL" - отвечает за величину уровня SL, в пунктах. Переменная "Lots" отвечает за размер лота открываемых позиций. Переменная "slippage" отвечает за допустимое проскальзывание в пунктах, который мы готовы принять. Переменная "MagicNunber" отвечает за магическое число, которое присваивается советником открываемым сделкам (нужно для слежения роботом за "своими" ордерами). Переменная "time_limit" отвечает за количество секунд, в течение которых пользователь должен успеть сделать выбор. Если поставить значение "0", то ограничения не будет, т.е. можно делать выбор в течение всей свечи.

Заключение

В итоге получен инструмент для комфортной торговли ордерами с типовыми параметрами (TP, SL, Slippage, лот). Данное средство может быть полезно в любой торговле. Однако наиболее эффективно оно может быть в случаях, когда открывается достаточно большое число сделок за короткий промежуток времени. Например, при пипсовке.

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