Пользовательские индикаторы > "Ручной" StopLoss/TakeProfit

Дополнительные индикаторы от пользователей Альфа-Директ 4. Готовые решения от пользователей.
BugsDigger
Сообщения: 535
Зарегистрирован: 11 ноя 2018, 17:11
Благодарил (а): 21 раз
Поблагодарили: 92 раза

"Ручной" StopLoss/TakeProfit

Непрочитанное сообщение BugsDigger » 14 фев 2019, 10:20

Привет всем.

Код "ручных" StopLoss/TakeProfit (далее SL/TP) для использования в стратегиях. (Ничего "революционного", м.б. кому-то пригодится.)

Цель - пользовательский расчет уровней и сигналов по ним для последующей реакции не через стоповые заявки, а через "обычные" операции. Это позволяет на длинных таймфреймах
  • избегать закрытия позиций по мгновенным колебаниям цен;
  • организовать "боковой коридор" удержания позиции.

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

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

Пороговые значения, естественно, следует оптимизировать. При оптимизации следует выбирать длительные участки с "нормальным" ходом торгов без слишком резких скачков, иначе пороги м.б. излишне завышены. Например, при оптимизации за весь 2018 год пороги SL/TP в моем случае получились аж 6.5/9,5% за счет больших движений в апреле, оптимизация за период с 1 мая дала более консервативные 2,5/8%.

Финальный результат оптимизации по сравнению с вариантом без SL/TP дает 5-10% дополнительной прибыли, т.е. не очень много, но тоже на дороге не валяется. :)

Код выделен в функции ResetSLTP и CalcSLTP для облегчения использования и перетаскивания в другие роботы.

Код: Выделить всё

function Initialize()
{
 StrategyName = "MyRobot";
 AddInput("Input1", Inputs.Candle, 30, true, "SBER=МБ ЦК"); // Т-Ф 30 мин, торгуемый

 //-----------------------------------------
 // Параметры и переменные SL/TP
 AddParameter("StpLs", 2, "Допустимые потери, %");
 AddParameter("TkPft", 2, "Цель прибыли, %");
 AddParameter("TkPftTrack", 0.5, "Точность цели прибыли, %");
 AddParameter("LogLevel", 0, "Уровень лога");

 AddGlobalVariable("TP", Types.Double, -1.0);             // еще не рассчитывалось
 AddGlobalVariable("SL", Types.Double, -1.0);             // еще не рассчитывалось
 AddGlobalVariable("TPinProgress", Types.Boolean, false); // цены были лучше TP
 AddGlobalVariable("TPpeak", Types.Double, -1.0);         // пик цен лучше TP/уровень переоткрытия позиции
 //-----------------------------------------
}

function OnUpdate()
{
    //-----------------------------------------
    Action ResetSLTP = () =>
    {
     SL=TP=-1.0; TPinProgress=false;
    };
               
   // выходной сигнал: '0'-удержание/нет позиции, '-1'-на выход по SL, '+1'-на выход по TP
                                                // позиция   учетная цена  текущая цена
    Func<double, double, double, int> CalcSLTP = (double AccPos, double AccPrc, double CurPrc) =>
    {
     if(AccPrc==0) { ResetSLTP(); return 0; } // нет позиции
     
     if(TP<0.0)
     {                                        // только что вошли в позицию, расчет уровней
      double s=AccPrc*(StpLs/100.0);           
      double t=AccPrc*(TkPft/100.0);
      if(AccPos>0) { SL=AccPrc-s; TP=AccPrc+t; TPinProgress=(CurPrc>=TP); }
      else         { SL=AccPrc+s; TP=AccPrc-t; TPinProgress=(CurPrc<=TP); }
     }
   
     if(AccPos>0)
     {                                                      // длинная позиция
      if(CurPrc<SL)
      {                                                     // сигнал на закрытие по SL
       ResetSLTP();
       TPpeak=AccPrc;                                       // для переоткрытия ждать цены, равной нынешней учетной
       return -1;
      }
     
      if(CurPrc<AccPrc) { TPinProgress=false; return 0; }   // цена ниже учетной, но пока держим
                                                            // цена выше учетной
      if(TPinProgress)
      {                                                     // ранее цена превышала TP
       TPpeak=Math.Max(TPpeak, CurPrc);                     // пиковая цена над TP

       if(CurPrc<TP)
       {                                                    // сигнал на закрытие по TP
        TPpeak=(TPpeak+TP)/2.0;                             // уровень переоткрытия позиции
        TPinProgress=false;
        return 1;                                           
       }
       
       TP=Math.Max(TP, CurPrc*(1.0-TkPftTrack/100.0));      // удержание, новый уровень TP
       return 0;
      }
      else { TPinProgress=(CurPrc>=TP); return 0; }         // ранее цена не превышала TP; удержание
     }
     else // AccPos<0
     {                                                      // короткая позиция
      if(CurPrc>SL)
      {                                                     // сигнал на закрытие по SL
       ResetSLTP();
       TPpeak=AccPrc;                                       // для переоткрытия ждать цены, равной нынешней учетной
       return -1;
      }
     
      if(CurPrc>AccPrc) { TPinProgress=false; return 0; }   // цена выше учетной, но пока держим
                                                            // цена ниже учетной
      if(TPinProgress)
      {                                                     // ранее цена была ниже TP
       TPpeak=(TPpeak>0.0 ? Math.Min(TPpeak, CurPrc) : CurPrc); // пиковая цена под TP

       if(CurPrc>TP)                                        // сигнал на закрытие по TP
       {
        TPpeak=(TPpeak+TP)/2.0;                             // уровень переоткрытия позиции
        TPinProgress=false;
        return 1;
       }     
       
       TP=Math.Min(TP, CurPrc*(1.0+TkPftTrack/100.0));      // удержание, новый уровень TP
       return 0;
      }
      else { TPinProgress=(CurPrc<=TP); return 0; }         // удержание
     } // AccPos<0
    };
    //-----------------------------------------
   
 // код робота - пример использования

  double cpr=AverPrice();                    // текущая уч. цена позиции
  int cp=(int)CurrentPosition();             // текущая позиция
  double P=(Input1.High[0]+Input1.Low[0])/2; // последняя цена (желательно как-то сглаженная)
  if(cp!=LastPosition)                       // LastPosition-позиция на предыдущем шаге
  {                                          // позиция изменилась
   ResetSLTP();                              // надо начать слежение заново
   LastPosition=cp;
   LastPrice=cpr;
  }

  // SLTP
  if(cp!=0)
  {
   int SigSLTP=CalcSLTP(cp, cpr, P);
   if(LogLevel>0)
   {   
    WriteLine(LogFile, String.Format(@"{0:dd/MM/yy} {1} TPSLsig:{2} Pos:{3} AccPrc:{4:F2} Prc:{5:F2} TP:{6:F2} SL:{7:F2} Peak:{8:F2} %:{9:F2} Prorgess:{10}",  BarDate(), BarTime(), SigSLTP, cp, cpr, P, TP, SL, TPpeak, CurrentPLper(), TPinProgress));
   }
   
   if(SigSLTP!=0)
   {
    ReopenInProgress=(cp<0 ? -1 : 1); // флаг на дальнейшее переоткрытие (код не показан)
    ClosePosition();
    if(LogLevel>0)
     WriteLine(LogFile, String.Format("Closed by {0}; Reop:{1} WaitFor:{2:F2}", (SigSLTP<0 ? "SL":"TP"), ReopenInProgress, TPpeak));
    goto Finish; // грубо против немедленного переоткрытия на этом же шаге робота; можно и без этого, если правильно реализовать переоткрытие  ;)
   } // SigSLTP
  } // SLTP

  // "основной код" робота
  .....

  Finish: ;
  // что-то еще
  .....
}


Если есть замечания, прошу сообщить (ради того и опубликовано; в процессе написания поста нашел у себя неточность - уже польза).

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

Вернуться в «Пользовательские индикаторы»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 7 гостей