Образцовый трейлинг-стоп и выход с рынка

Sergey Kravchuk | 21 апреля, 2008

Введение

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

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


Постановка задачи

  1. На графике отмечаются "образцовые" точки открытия и закрытия ордера.
  2. Время открытия/закрытия и направление торговли (покупка/продажа) записывается в файл.
  3. Создается эксперт, читающий подготовленный файл и строго исполняющий его команды.

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


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

В торговле "вдоль синей линии" есть три потенциальные точки срабатывания стопов после подтяжек:

  1. Агрессивный трейлинг на минимальном расстоянии от текущей цены.
  2. Нормальный "терпеливый" трейлинг
  3. Идеальный, выжимающий последние капли прибыли трейлинг.

Кроме того, в районе точке 4 возможно ложное срабатывание слишком "нетерпеливого" трейлинга.

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


Инструменты разметки


Чтобы облегчить процесс разметки графика, образцовыми линиями, подготовим набор скриптов. Два скрипта TL_Buy и TL_Sell будут создавать линии-метки для операций покупки и продажи соответственно. Скрипт TL_Write - пробежится по всем созданным линиям и запишет их характеристики в файл для последующей работы эксперта TL_Trade. Еще один скрипт TL_Read сможет прочитать созданный файл, и по нему заново построить все линии. Это может пригодится для того, чтобы подкорректировать имеющиеся линии или добавить новые, или убрать существующие.

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

  1. имена всех образцовых линий начинаются с одного и тогоже префикса (TL_). По нему потом можно будет выбрать и удалить все линии.
  2. после префикса идет один символ - код операции: B-buy, S-sell.
  3. после кода операции идет номер линии, необходимый для того, чтобы их можно было как-то различать.


Итого. У нас должны быть на графике линии с именами, например, TL_B1 TL_B2, TL_S3 и т.д.

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

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

/****************************************************************
 ОБРАЗЦОВАЯ ТОРГОВЛЯ: TL_Buy - создание новой образцовой линии покупки
 Copyright © 2006-2008, Сергей Кравчук. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
int start()
{
  int MaxNo=0,i,No; 
  
  if(WindowOnDropped()!=0) { MessageBox("Скрипт нужно сбрасывать в основное окно","ОШИБКА", IDOK + MB_ICONERROR); return(1); }
 
  // найдем максимальный номер суфикса всех линий
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) 
    {
      No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // выделим номер линии
      if(MaxNo<No) MaxNo=No; // запомним его если он больше
    }
  }
  
  datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // определим координаты точки сброса скрипта
 
  int width = 5*Period()*60;                             // ширина создаваемой линии в барах переведенная во время
  double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // высота создаваемой линии в тиках переведенная в цену
  
  string LineName = _prefix_+"B"+(MaxNo+1);  // создадим имя для новой линии
  ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0-height, t0+width,p0+height); // создадим линию
  ObjectSet(LineName,OBJPROP_RAY,False); // сделаем ее отрезком а не лучом
  ObjectSet(LineName,OBJPROP_WIDTH,2);   // зададим ширину
  ObjectSet(LineName,OBJPROP_COLOR,Blue); // зададим цвет
 
}

/****************************************************************
 ОБРАЗЦОВАЯ ТОРГОВЛЯ: TL_Sell - создание новой образцовой линии продажи
 Copyright © 2006-2008, Сергей Кравчук. http://forextools.com.ua
*****************************************************************/
 
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
int start()
{
  int MaxNo=0,i,No; 
  
  if(WindowOnDropped()!=0) { MessageBox("Скрипт нужно сбрасывать в основное окно","ОШИБКА", IDOK + MB_ICONERROR); return(1); }
 
  // найдем максимальный номер суфикса всех линий
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) 
    {
      No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // выделим номер линии
      if(MaxNo<No) MaxNo=No; // запомним его если он больше
    }
  }
  
  datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // определим координаты точки сброса скрипта
 
  int width = 5*Period()*60;                             // ширина создаваемой линии в барах переведенная во время
  double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // высота создаваемой линии в тиках переведенная в цену
  
  string LineName = _prefix_+"S"+(MaxNo+1);  // создадим имя для новой линии
  ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0+height, t0+width,p0-height); // создадим линию
  ObjectSet(LineName,OBJPROP_RAY,False); // сделаем ее отрезком а не лучом
  ObjectSet(LineName,OBJPROP_WIDTH,2);   // зададим ширину
  ObjectSet(LineName,OBJPROP_COLOR,Red); // зададим цвет
 
}

/****************************************************************
 ОБРАЗЦОВАЯ ТОРГОВЛЯ: TL_Write - запись координат образцовых линий в файл
 Copyright © 2006-2008, Сергей Кравчук. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
#property show_inputs
 
extern string FileNameForWrite = "TL_DATA.TXT";
 
int start()
{
  int LinesCNT=0,i; string Operation; double p; datetime t;
  
  int fh=FileOpen(FileNameForWrite,FILE_CSV|FILE_WRITE,';');
 
  // обойдем все линии которые создали и запишем из них команды открытий для эксперта
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) // наша линиия
    {
      string LineName = ObjectName(i);  
 
      datetime t1=ObjectGet(LineName,OBJPROP_TIME1);
      datetime t2=ObjectGet(LineName,OBJPROP_TIME2);
 
      double p1=ObjectGet(LineName,OBJPROP_PRICE1);
      double p2=ObjectGet(LineName,OBJPROP_PRICE2);
 
      LinesCNT++; // увеличим счетчик для выдачи итогового сообщения
      
      Operation = StringSubstr(ObjectName(i),StringLen(_prefix_),1); 
      
      // цены нужны только чтобы восстановить линии на графике
      FileWrite(fh,Operation,TimeToStr(t1),DoubleToStr(p1,Digits),TimeToStr(t2),DoubleToStr(p2,Digits)); 
    }
  }
  
  FileClose(fh);
  
  MessageBox("Записано отрезков "+(LinesCNT)+" шт.","Готово", IDOK + MB_ICONINFORMATION);
}


/****************************************************************
 ОБРАЗЦОВАЯ ТОРГОВЛЯ: TL_Read - отрисовка образцовых линий из файла
 Copyright © 2006-2008, Сергей Кравчук. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
#property show_inputs
 
extern string FileNameForRead = "TL_DATA.TXT";
 
int start()
{
  int LinesCNT=0,i; 
  
  int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';');
  if(fh<0) { MessageBox("Ошибка открытия файла \"" + FileNameForRead + "\"","ОШИБКА", IDOK + MB_ICONERROR); return(1); }
 
  // сначала все удалим
  for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; } }
 
  // обойдем все линии которые создали и запишем из них команды открытий для эксперта
  while(true)
  {
    string Operation=FileReadString(fh);
    
    if(FileIsEnding(fh)) break; // файл закончился? - выходим
    
    // считаем координаты отрезка
    datetime t1=StrToTime(FileReadString(fh));
    double   p1=StrToDouble(FileReadString(fh));
    datetime t2=StrToTime(FileReadString(fh));
    double   p2=StrToDouble(FileReadString(fh));
 
    // нарисуем отрезок
    LinesCNT++;
    string LineName = _prefix_+Operation+(LinesCNT);  // создадим имя для новой линии
    ObjectCreate(LineName,OBJ_TREND,0,t1,p1, t2,p2);  // создадим линию
    ObjectSet(LineName,OBJPROP_RAY,False); // сделаем ее отрезком а не лучом
    ObjectSet(LineName,OBJPROP_WIDTH,2);   // зададим ширину
    if(Operation=="B") ObjectSet(LineName,OBJPROP_COLOR,Blue); else  ObjectSet(LineName,OBJPROP_COLOR,Red);// зададим цвет
 
  }
  FileClose(fh);
  
  MessageBox("Считано отрезков "+(LinesCNT)+" шт.","Готово", IDOK + MB_ICONINFORMATION);
}


/****************************************************************
 ОБРАЗЦОВАЯ ТОРГОВЛЯ: TL_Clear - удаление всех образцовых линий
 Copyright © 2006-2008, Сергей Кравчук. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
int start()
{
  int LinesCNT=0,i;
  
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; LinesCNT++; }
  }
}

Расположение файлов


Очень важный момент касается места расположения файлов. Работающие скрипты стандартными средствами могут создавать файлы только в каталоге c:\Program Files\MetaTrader 4\experts\files. Однако тестер во время тестирования экспертов имеет доступ к аналогичной папке, расположенной "внутри своего каталога" - c:\Program Files\MetaTrader 4\tester\files.

Поэтому, после создания файлов и перед их использованием в тестовом советнике их необходимо будет самостоятельно скопировать из c:\Program Files\MetaTrader 4\experts\files в c:\Program Files\MetaTrader 4\tester\files.

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

Тестовый советник


Советник для тестирования так же не представляет особой сложности в коде. В нем выделены следующие блоки:

  1. блок закрытия ордеров по приходу к концу образцового отрезка.
  2. блок открытия ордеров по проходу начала образцового отрезка.
  3. блок тестирования трейлинг-стопа и выхода с рынка.

Их работа достаточно очевидна из исходного кода. Необходимо дать только несколько комментариев:

  1. Поскольку линии могут создаваться в произвольном порядке, на каждом "тике" тестера будет проверяться весь набор линий, чтобы найти те из них, по которым необходимо открыться/закрыться.
  2. Времена открытия/закрытия записываются в комментарий ордера во внутреннем формате представления даты времени. Это необходимо, во-первых, для того, чтобы мы не открывали один и тотже ордер по нескольку раз, если он закроется трейлингом задолго до окончания своей образцовой линии. Во-вторых, проверяя открытые ордера и выбирая из его комментариев время закрытия, мы четко закроем ордер именно тогда, когда закончится его управляющая линия, поскольку время закрытия записано в самом открытом ордере.
  3. Параметр ProcedTrailing включает или отключает обработку трейлинга. Это позволит прогнать эксперта вообще без трейлинга, чтобы получить самый пессимистический результат, для того, чтобы можно было сопоставлять с ним получаемые результаты оптимизации.

/****************************************************************
 ОБРАЗЦОВАЯ ТОРГОВЛЯ: TL_Trader - торговля по образцовым линиям
 Copyright © 2006-2008, Сергей Кравчук. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
extern string FileNameForRead = "TL_DATA.TXT";
extern double Lots = 0.1;
extern double StopLoss = 0;
extern double TrailingStop = 30;
extern bool   ProcedTrailing=true; // отрабатывать блок трейлинга
 
double SL; // для расчета значения лоса для открытия ордера
 
int start()
{
  int LinesCNT=0,i,ticket,pos; double p; datetime t; string s;
 
  int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';'); // для тестирования файл нужно положить в tester\files\TL_DATA.txt
 
  if(fh<0) { MessageBox("Ошибка открытия файла \"" + FileNameForRead + "\"","ОШИБКА", IDOK + MB_ICONERROR); return(1); }
 
  // проверим все записи: если время открытия уже прошло а ордера с таким коментом нет ни в истории ни в открытых
  // значит он еще не открывался - откроем как там сказано
 
  while(true)
  {
    string Operation=FileReadString(fh);
    
    if(FileIsEnding(fh)) break; // файл закончился? - выходим
    
    // считаем координаты отрезка
    string st1=FileReadString(fh);
    string sp1=FileReadString(fh);
    string st2=FileReadString(fh);
    string sp2=FileReadString(fh);
    datetime t1=StrToTime(st1);
    double   p1=StrToDouble(sp1);
    datetime t2=StrToTime(st2);
    double   p2=StrToDouble(sp2);
    
  
    // а вдруг концы отрезков перепутаны?
    if(t1>t2) { p=p1; p1=p2; p2=p; t=t1; t1=t2; t2=t;  s=st1; st1=st2; st2=s; s=sp1; sp1=sp2; sp2=s; } 
 
    string MarkComent = t1+"="+t2;
    
    //*****************************************************************
    // 1) блок закрытия ордеров по приходу к концу образцового отрезка.
    //*****************************************************************
    for(i=0;i<OrdersTotal();i++)
    {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
      if(OrderComment()==MarkComent && TimeCurrent()>=t2) // ордер нужно закрыть
      {
        if(OrderType()==OP_BUY) OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
        else OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
      }
    }
 
    //****************************************************************
    // 2) блок открытия ордеров по проходу начала образцового отрезка.
    //****************************************************************
    bool OrderNotPresent=true; // признак что такого ордера мы еще не открывали
    if(t1<=TimeCurrent() && TimeCurrent()<t2) // пора открываться - проверим что этот ордер не открыт и не закрыт
    {
      for(i=0;i<OrdersTotal();i++)
      {
        if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
        if(OrderComment()==MarkComent) { OrderNotPresent=false; break; } // есть такой ордер
      }
      for(i=0;i<OrdersHistoryTotal() && OrderNotPresent;i++)
      {
        if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) continue;
        // в ордер в истории дописывается в хвост нечто вроде "[sl]" - его нужно отрезать!!
        pos = StringFind(OrderComment(),"[");
        string CurOrderComment = StringSubstr(OrderComment(),0,pos);
        if(CurOrderComment==MarkComent) { OrderNotPresent=false; break; } // есть такой ордер
      }
      if(OrderNotPresent) // ордера нет - открываемся
      {
        // откроем ордер
        if(Operation=="B") // покупка
        { 
          if(StopLoss<=0) SL=0; else SL=Ask-StopLoss*Point;
          ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,SL,0,MarkComent,1235,0,Blue);
          OrderSelect(ticket,SELECT_BY_TICKET);
        }
        else // продажа
        {
          if(StopLoss<=0) SL=0; else SL=Bid+StopLoss*Point;
          ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,SL,0,MarkComent,1235,0,Red);
          OrderSelect(ticket,SELECT_BY_TICKET);
        }
      }
    }
  }
  
  FileClose(fh);
 
  
  //******************************************************
  // 3) блок тестирования трейлинг-стопа и выхода с рынка.
  //******************************************************
  if(ProcedTrailing)
  {
    for(i=0;i<OrdersTotal();i++)
    {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
      if(OrderType()==OP_BUY)
      {
        if(Bid-OrderOpenPrice()>Point*TrailingStop)
        {
         if(OrderStopLoss()<Bid-Point*TrailingStop)
           {
            OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
            return(0);
           }
        }
      }
      if(OrderType()==OP_SELL)
      {
       if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
         {
          if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
            {
             OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
             return(0);
            }
         }
      }
    }
  }
  
  return(0);
}


Работа системы "в сборе"


Для тестирования системы был использован микронабор из 14 линий - вот содержимое образцового файла TL_DATA.txt


B;2007.12.28 05:00;1.4605;2008.01.09 22:00;1.4658
B;2008.01.29 05:00;1.4767;2008.02.05 05:00;1.4811
B;2008.02.15 16:00;1.4687;2008.02.21 09:00;1.4735
B;2008.02.21 14:00;1.4738;2008.02.26 07:00;1.4812
B;2008.02.28 14:00;1.5129;2008.03.05 12:00;1.5186
B;2008.03.05 22:00;1.5261;2008.03.11 20:00;1.5316
B;2008.03.13 01:00;1.5539;2008.03.18 22:00;1.5620
B;2008.03.26 14:00;1.5724;2008.03.28 10:00;1.5758
S;2007.11.30 13:00;1.4761;2007.12.10 22:00;1.4711
S;2007.12.14 04:00;1.4626;2007.12.28 00:00;1.4610
S;2008.01.17 17:00;1.4688;2008.01.24 13:00;1.4671
S;2008.02.07 12:00;1.4633;2008.02.14 11:00;1.4617
S;2008.03.19 23:00;1.5641;2008.03.25 23:00;1.5629
S;2008.03.31 19:00;1.5811;2008.04.08 04:00;1.5796

Вот как они выглядят на графике:

Не самый эффективный трейдинг, но идеальный полигон для тестирования трейлинга ;)

Если запустить тестер с отключенными подтяжками стопов (ProcedTrailing=false), то мы получим вот такой слабенький результат.


Простейшая оптимизация по величине трейлинг-стопа выдает оптимальный размер стопа для подтяжки - 95 пунктов.

Оптимальная величина, дающая максимальную прибыль, хорошо согласуется со статистикой размеров баров тестируемого периода: 95 пунктов - это 98% всех баров этого периода, что наглядно видно на графике индикатора ft.BarStat.


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



Еще более очевидно они видны на графике. Обратите внимание - все ордера открылись точно в начале образцовых линий!


Посмотрите на самый первый отрезок (5 марта). Прибыль слизал резкий выброс цены вниз, но общая тенденция на покупку осталась неизменной. Если бы мы тестировали трейлинг обычным советником, вполне возможно, что он снова открыл бы позицию на покупку, которая вполне могла бы продержаться вплоть до конца второй линии (после 17 марта). В этом случае мы получили бы несравнимые результаты. Заработанная во втором случае прибыль отнеслась бы к удачному повторному открытию нового ордера, а не к механизму работы трейлинг-стопа. Но задача этого исследования состоит не в том, чтобы получить максимальную прибыль, а в том, чтобы получить максимально эффективный механизм подтяжек стопов. Именно поэтому нам важно чтобы все ордера открывались в одно и тоже время и не передерживались дольше заданного. Только в этом случае увеличение прибыли в результате оптимизации будет отражать эффективность алгоритма трейлинга!

Заключение

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

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

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

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