English 中文 Español Deutsch 日本語 Português
Визуальное тестирование прибыльности индикаторов и сигналов

Визуальное тестирование прибыльности индикаторов и сигналов

MetaTrader 4Примеры | 22 сентября 2008, 07:57
3 306 16
Sergey Kravchuk
Sergey Kravchuk

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

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

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

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

Давайте задумаемся, что у нас есть и что мы хотим получить. Возьмем в качестве примера известный всем ZigZag из стандартной поставки MetaTrader 4. Набросьте его на любую пару на любом таймфрейме: правда хочется торговать? Все очевидно: на верхнем изломе нужно продать, а на нижнем - закрыть продажу и купить.

Теперь вопрос: сколько денег принесет такая торговля? Ответ на него можно получить совершенно элементарно: суммируем все высоты отрезков и пересчитываем с учетом размера торгуемого лота и цены одного пункта в валюте депозита. Доступ к буферу значений зигзага доступен через вызов функции iCustom:

P=iCustom(NULL,0,"ZigZag",0,i); 

где i - номер бара, для которого нужно получить значение.

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

Универсальное решение

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

Функционально индикатор состоит из следующих блоков:

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

Реализация

Вот код самого индикатора с примером заполнения данных из индикатора ZigZag.

/*///———————————————————————————————————————————————————————————————————————————————————————————————————————
 
    IndicatorTester.mq4 
  
    Визуальное тестирование прибыльности индикаторов и сигналов
    
    Copyright © 2006, Кравчук Сергей,  http://forextools.com.ua
 
/*///———————————————————————————————————————————————————————————————————————————————————————————————————————
#property copyright "Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua"
#property link      "http://forextools.com.ua"
 
#property indicator_chart_window
#property indicator_buffers 0
 
// параметры отображения элементов графика
extern bool   ShowZZ          = true;           // рисовать ли ЗЗ
extern bool   ShowMARKERS     = true;           // рисовать ли вертикальные линии-метки
 
//даты построения графика
extern datetime DateStart     = D'1.01.1970';
extern datetime DateEnd       = D'31.12.2037';
 
//параметры расчетов прибыли
extern double LotForCalc      = 0.05;           // размер лота для рассчета прибыли
extern int    Optimizm        = 0;              // -1-пессимистический рассчет 0-по ценам открытия баров 
                                                //+1-оптимистический
 
// цвета линий покупок
extern color  ColorProfitBuy  = Blue;           // цвет линии для профитных покупок
extern color  ColorLossBuy    = Red;            // цвет линии для убыточных покупок
extern color  ColorZeroBuy    = Gray;           // цвет линии для покупок с нулевой прибылью
 
// цвета линий продаж
extern color  ColorProfitSell = Blue;           // цвет линии для профитных продаж
extern color  ColorLossSell   = Red;            // цвет линии для убыточных продаж
extern color  ColorZeroSell   = Gray;           // цвет линии для продаж с нулевой прибылью
 
// цвета линий сигналов
extern color  ColorBuy        = CornflowerBlue; // цвет линии сигнала на покупку
extern color  ColorSell       = HotPink;        // цвет линии сигнала на продажу
extern color  ColorClose      = Gainsboro;      // цвет линии сигнала на закрытие
 
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
double sBuy[],sCloseBuy[],sSell[],sCloseSell[]; // массивы для сигналов
int sBuyCnt,sSellCnt,sBuyCloseCnt,sSellCloseCnt;// счетчики сигналов 
int i,DisplayBars;
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
// служебные коды
#define MrakerPrefix "IT_"
#define OP_CLOSE_BUY  444
#define OP_CLOSE_SELL 555
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
int init()   { ClearMarkers(); return(0); }
int deinit() { ClearMarkers(); return(0); }
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
int start() 
{ 
  double Profit=0,P1,P2; int CntProfit=0,CntLoose=0,i1,i2;
 
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // удалим все метки на случай если индикатор перерисовывается
  ClearMarkers(); 
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // подготовим счетчики сигналов
  sBuyCnt=0; sSellCnt=0; sBuyCloseCnt=0; sSellCloseCnt=0; 
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // отведем память под сигнальные массивы и обнулим их значения
  DisplayBars=Bars; // к-во отображаемых баров
  ArrayResize(sBuy,DisplayBars);  ArrayInitialize(sBuy,0);
  ArrayResize(sSell,DisplayBars); ArrayInitialize(sSell,0);
  ArrayResize(sCloseBuy,DisplayBars);  ArrayInitialize(sCloseBuy,0);
  ArrayResize(sCloseSell,DisplayBars); ArrayInitialize(sCloseSell,0);
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // найдем первую точку и запомним ее место и цену
  for(i1=Bars-1;i1>=0;i1--) { P1=iCustom(NULL,0,"ZigZag",0,i1); if(P1!=0) break; }
  // обработаем точки зигзага
  for(i2=i1-1;i2>=0;i2--) 
  {
    // найдем очередную точку и запомним ее место и цену
    for(i2=i2;i2>=0;i2--) { P2=iCustom(NULL,0,"ZigZag",0,i2); if(P2!=0) break; }
    
    if(i2<0) break; // последнюю точку ставим на текущей цене 
 
    // условия открытия одновременно и условия закрытия противоположного ордера
    if(P1>P2) { sSell[i1]=1; sBuy[i2]=1; sCloseSell[i2]=1; }
    if(P1<P2) { sBuy[i1]=1; sSell[i2]=1; sCloseBuy[i2]=1; }
 
    P1=P2; i1=i2; // запомним бар найденной точки
  }
    //——————————————————————————————————————————————————————————————————————————————————————————————————————
  // удалим повторяющиеся сигналы оставив только самые первые - левые по графику
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy[i]==sBuy[i+1]) sBuy[i]=0;
    if(sSell[i]==sSell[i+1]) sSell[i]=0;
    if(sCloseBuy[i]==sCloseBuy[i+1]) sCloseBuy[i]=0;
    if(sCloseSell[i]==sCloseSell[i+1]) sCloseSell[i]=0;
  }
  // удалим сигналы вне заданного диапазона дат
  for(i=0;i<DisplayBars;i++) 
  {
    if(Time[i]<DateStart || DateEnd<Time[i]) { sBuy[i]=0; sSell[i]=0; sCloseBuy[i]=0; sCloseSell[i]=0; }
  }
  // добавим принудительное закрытие на границе диапазона
  if(DateEnd<=Time[0]) { i=iBarShift(Symbol(),Period(),DateEnd); sBuy[i]=0; sSell[i]=0; 
                                                             sCloseBuy[i]=1; sCloseSell[i]=1; }
  if(DateEnd >Time[0]) { sCloseBuy[0]=1; sCloseSell[0]=1; }
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // посчитаем количество сигналов
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy [i]!=0) sBuyCnt++;  if(sCloseBuy [i]!=0) sBuyCloseCnt++;
    if(sSell[i]!=0) sSellCnt++; if(sCloseSell[i]!=0) sSellCloseCnt++;
  }
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // расставим метки, нарисуем ЗЗ и посчитаем прибыль
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // обработаем покупки
  for(i=DisplayBars-1;i>=0;i--) // пойдем собирать точки
  {
    // найдем очередную точку открытия и запомним ее место и цену
    for(i1=i;i1>=0;i1--) if(sBuy[i1]!=0) break; 
 
    // найдем очередную точку закрытия покупки и запомним ее место и цену
    for(i2=i1-1;i2>=0;i2--) if(sCloseBuy[i2]!=0) break;
    if(i2<0) i2=0; // для последней незакрытой позы считаем закрытие на текущей цене
    i=i2; // новый бар для продолжения поиска точек открытия
 
    // определим цены для рисования в зависимости от параметра оптимистичности Optimizm
    if(Optimizm<0)  { P1=High[i1]; P2=Low[i2];  } 
    if(Optimizm==0) { P1=Open[i1]; P2=Open[i2]; } 
    if(Optimizm>0)  { P1=Low[i1];  P2=High[i2]; } 
    
    P1/=Point; P2/=Point; // приведем цены к пунктам
    
    // определяем профит и заполняем соответствующий буфер
    if(i1>=0) 
    { 
      Profit=Profit+P2-P1; // соберем суммарный профит
      if(P2-P1>=0) CntProfit++; else CntLoose++; // посчитаем число ордеров
      DrawLine(i1,i2,OP_BUY,Optimizm); // нарисуем линию ордера
      PlaceMarker(i1,OP_BUY); 
      PlaceMarker(i2,OP_CLOSE_BUY); 
    }
  }
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // обработаем продажи
  for(i=DisplayBars-1;i>=0;i--) // пойдем собирать точки
  {
    // найдем очередную точку открытия и запомним ее место и цену
    for(i1=i;i1>=0;i1--) if(sSell[i1]!=0) break; 
 
    // найдем очередную точку закрытия покупки и запомним ее место и цену
    for(i2=i1-1;i2>=0;i2--) if(sCloseSell[i2]!=0) break;
    if(i2<0) i2=0; // для последней незакрытой позы считаем закрытие на текущей цене
    i=i2; // новый бар для продолжения поиска точек открытия
 
    // определим цены для рисования в зависимости от параметра оптимистичности Optimizm
    if(Optimizm<0)  { P1=Low[i1];  P2=High[i2]; } 
    if(Optimizm==0) { P1=Open[i1]; P2=Open[i2]; } 
    if(Optimizm>0)  { P1=High[i1]; P2=Low[i2];  } 
    
    P1/=Point; P2/=Point; // приведем цены к пунктам
    
    // если обе точки есть - определяем профит и заполняем соответствующий буфер
    if(i1>=0) 
    { 
      Profit=Profit+P1-P2; // соберем суммарный профит
      if(P1-P2>=0) CntProfit++; else CntLoose++; // посчитаем число ордеров
      DrawLine(i1,i2,OP_SELL,Optimizm); // нарисуем линию ордера
      PlaceMarker(i1,OP_SELL); 
      PlaceMarker(i2,OP_CLOSE_SELL);
    }
  }
  //————————————————————————————————————————————————————————————————————————————————————————————————————————
  // рассчет итоговых цифр для комментария  
  int Cnt=CntProfit+CntLoose; // общее число операций
  // к-т для перевода пунктов в валюту депозита
  double ToCurrency = MarketInfo(Symbol(),MODE_TICKVALUE)*LotForCalc;    
  string msg="Период: "+TimeToStr(MathMax(DateStart,Time[Bars-1]))+" - "
                                                      + TimeToStr(MathMin(DateEnd,Time[0]))+"\n\n";
  
  msg=msg+
  sBuyCnt+" покупок и "+sBuyCloseCnt+" их закрытий\n"+
  sSellCnt+" продаж и "+sSellCloseCnt+" их закрытий\n\n";
  
  // общее время в днях
  int TotalDays = (MathMin(DateEnd,Time[0])-MathMax(DateStart,Time[Bars-1]))/60/60/24; 
                                                                       //переведем секунды в дни
  if(TotalDays<=0) TotalDays=1; // чтоб не было деления на ноль для неполного дня
  
  if(Cnt==0) msg=msg+("Нет ни одной операции");
  else msg=msg+
  (
    DoubleToStr(Profit,0)+" пункт на "+Cnt+" операциях за "+TotalDays+" дней\n"+
    DoubleToStr(Profit/Cnt,1)+" пункт на операцию ("+
    DoubleToStr(Profit/TotalDays,1)+" за день)\n\n"+
    "При торговле лотом "+DoubleToStr(LotForCalc,2)+" получаем в "+AccountCurrency()+":\n"+
    DoubleToStr(Profit*ToCurrency,0)+" всего, по "+
    DoubleToStr(Profit/Cnt*ToCurrency,1)+" на операцию ("+
    DoubleToStr(Profit/TotalDays*ToCurrency,1)+" за день)\n\n"+
    CntProfit+" прибыльных ("+DoubleToStr(((CntProfit)*1.0/Cnt*1.0)*100.0,1)+"%)\n"+
    CntLoose+" убыточных ("+DoubleToStr(((CntLoose)*1.0/Cnt*1.0)*100.0,1)+"%)"
  );
  Comment(msg);
  
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
 
 
 
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
// удаление всех объектов нашего графика
void ClearMarkers() 
{ 
  for(int i=0;i<ObjectsTotal();i++) 
  if(StringFind(ObjectName(i),MrakerPrefix)==0) { ObjectDelete(ObjectName(i)); i--; } 
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
// установка вертикальной линии - метки операции типа op_type
void PlaceMarker(int i, int op_type)
{
  if(!ShowMARKERS) return; // показ маркеров отключен
 
  color MarkerColor; string MarkName; 
 
  // __ чтобы по сортировке линиия закрытия нарисовалась ниже линии открытия 
  if(op_type==OP_CLOSE_SELL) { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__SELL_"+i; }
  if(op_type==OP_CLOSE_BUY)  { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__BUY_"+i;  }  
  if(op_type==OP_SELL)       { MarkerColor=ColorSell;  MarkName=MrakerPrefix+"_SELL_"+i;  }
  if(op_type==OP_BUY)        { MarkerColor=ColorBuy;   MarkName=MrakerPrefix+"_BUY_"+i;   }
 
  ObjectCreate(MarkName,OBJ_VLINE,0,Time[i],0); 
  ObjectSet(MarkName,OBJPROP_WIDTH,1); 
  if(op_type==OP_CLOSE_BUY || op_type==OP_CLOSE_SELL) ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID); 
  else ObjectSet(MarkName,OBJPROP_STYLE,STYLE_DOT);
  ObjectSet(MarkName,OBJPROP_BACK,True);  
 
  ObjectSet(MarkName,OBJPROP_COLOR,MarkerColor);
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————
// установка линии на графике для операции типа op_type по ценам параметра Optimizm
void DrawLine(int i1,int i2, int op_type, int Optimizm)
{
  if(!ShowZZ) return; // показ ЗЗ отключен
  
  color СurColor;
  string MarkName=MrakerPrefix+"_"+i1+"_"+i2;
  double P1,P2;
  
  // определим цены для рисования в зависимости от параметра оптимистичности Optimizm
  if(Optimizm<0 && op_type==OP_BUY)  { P1=High[i1]; P2=Low[i2];  } 
  if(Optimizm<0 && op_type==OP_SELL) { P1=Low[i1];  P2=High[i2]; } 
  if(Optimizm==0)                    { P1=Open[i1]; P2=Open[i2]; } 
  if(Optimizm>0 && op_type==OP_BUY)  { P1=Low[i1];  P2=High[i2]; } 
  if(Optimizm>0 && op_type==OP_SELL) { P1=High[i1]; P2=Low[i2];  } 
 
  ObjectCreate(MarkName,OBJ_TREND,0,Time[i1],P1,Time[i2],P2);
  
  ObjectSet(MarkName,OBJPROP_RAY,False); // рисуем отрезки а не линии
  ObjectSet(MarkName,OBJPROP_BACK,False);
  ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID);
  ObjectSet(MarkName,OBJPROP_WIDTH,2);
 
  // зададим цвет линии в зависимости от прибыльности операции
  if(op_type==OP_BUY) 
  { 
    if(P1 <P2) СurColor = ColorProfitBuy;
    if(P1==P2) СurColor = ColorZeroBuy;
    if(P1 >P2) СurColor = ColorLossBuy;
  }
  if(op_type==OP_SELL) 
  { 
    if(P1 >P2) СurColor = ColorProfitSell;
    if(P1==P2) СurColor = ColorZeroSell;
    if(P1 <P2) СurColor = ColorLossSell;
  }
  ObjectSet(MarkName,OBJPROP_COLOR,СurColor);
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————

В параметрах индикатора есть один специфический параметр Optimizm. Он задает желаемую степень оптимистичности расчетов. Если он меньше нуля - это значит что будет выполнен пессимистический расчет: для цен покупок будут выбираться High сигнального бара, для продаж - Low. Таким образом моделируются самые плохие результаты которые мы можем получить по этим сигналам. Если его значение больше нуля - моделируется самый оптимистичный вариант расчета: когда мы покупаем по самым низким ценам а продаем по самым высоким. При значении параметра равном нулю моделируется нейтральное поведение, при котором мы открываемся и закрываемся по ценам открытия баров - именно такая ситуация будет в большинстве случаем реализовываться при торговле экспертов.

Анализ результатов

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

Как видим - график показывает наличие восьми убыточных участков. Оно и понятно - зигзаг "работает" по самым лучшим ценам и, чтобы полностью повторить его, нам нужно задать параметр Optimizm = 1

Что нас ожидает, если мы будем торговать из рук вон плохо, можно посмотреть, задав параметр Optimizm = -1


Небольшое отступление

Не ругайте меня сильно, уважаемые трейдеры, но этот индикатор может оказаться для вас хорошим кнутом, если попадет в умелые руки вашего начальства, которому, как правило, нет дела до тонкостей торговых тактик - их интересует только результат. Загрузив в тестер алгоритм сигналов ZigZag и включив Optimizm = 1 они получат цифру той максимальной прибыли, которую можно в принципе было получить с данного участка графика и которую они захотят от вас получать. И если вы всегда наторговываете меньше половины - это будет повод задуматься о том, не слишком ли прохладно вы относитесь к своим обязанностям.

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

Как видим, использование только одного параметра Optimizm позволяет использовать индикатор для оценки потенциала тестируемого в нем индикатора сигналов. И если даже оптимистический расчет не дает прибыль - значит с индикатором сигналов нужно еще сильно поработать: подобрать ему другие параметры либо, в самом худшем случае, забросить его как бесперспективный, сэкономив себе время на разработку эксперта и деньги своего депозита, которые могли бы быть слиты, чтобы убедиться в непригодности индикатора сигналов. Таким образом, наш тестирующий индикатор позволяет сэкономить не только время, но деньги.

Анализ сигналов торговых систем

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

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

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

Давайте возьмем в качестве примера готовый код стандартного эксперта MACD. Вот как должен выглядеть. Блок заполнения сигнальных массивов содержимое, которого скопировано и вставлено из текста индикатора:

  // заполним значениями сигнальные массивы и посчитаем их количество
  for(i=DisplayBars;i>=0;i--) 
  {
    double MacdCurrent, MacdPrevious, SignalCurrent;
    double SignalPrevious, MaCurrent, MaPrevious;
  
    // 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,i+0);
    MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,i+1);
    SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,i+0);
    SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,i+1);
    MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,i+0);
    MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,i+1);
    
    // check for long position (BUY) possibility
    if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
       MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious) sBuy[i]=1;
       
    // check for short position (SELL) possibility
    if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
       MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent<MaPrevious) sSell[i]=1;
       
    if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
       MathAbs(MacdCurrent)>(MACDCloseLevel*Point)) sCloseBuy[i]=1;
 
    if(MacdCurrent<0 && MacdCurrent>SignalCurrent &&  MacdPrevious<SignalPrevious && 
       MathAbs(MacdCurrent)>(MACDCloseLevel*Point)) sCloseSell[i]=1;
  }

И вот какой результат мы получаем.

Обратите внимание - линии меток сигналов четко совпадают с точками пересечений графиков MACD. Возможно, глядя на эти совмещенные графики, вы решите что именно необходимо поправить в сигналах. Ну например: отказаться от проверки MaCurrent и MaPrevious. Небольшая коррекция кода

  // заполним значениями сигнальные массивы и посчитаем их количество
  for(i=DisplayBars;i>=0;i--) 
  {
    double MacdCurrent, MacdPrevious, SignalCurrent;
    double SignalPrevious, MaCurrent, MaPrevious;
  
    // 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,i+0);
    MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,i+1);
    SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,i+0);
    SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,i+1);
    MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,i+0);
    MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,i+1);
    
    // check for long position (BUY) possibility
    if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
       MathAbs(MacdCurrent)>(MACDOpenLevel*Point)) sBuy[i]=1;
       
    // check for short position (SELL) possibility
    if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
       MathAbs(MacdCurrent)>(MACDOpenLevel*Point)) sSell[i]=1;
       
    if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
       MathAbs(MacdCurrent)>(MACDCloseLevel*Point)) sCloseBuy[i]=1;
 
    if(MacdCurrent<0 && MacdCurrent>SignalCurrent &&  MacdPrevious<SignalPrevious && 
       MathAbs(MacdCurrent)>(MACDCloseLevel*Point)) sCloseSell[i]=1;
  }

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

Тестирование фантазий

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

Вот в качестве примера простейшая "система": покупать когда для сигналов используется обычный индикатор МА. Условие покупки - МА растет, продажи - МА падает. Смотрите как просто можно проверить прибыльность этой идеи. Блок заполнения сигналов выглядит следующим образом:

  // заполним значениями сигнальные массивы и посчитаем их количество
  for(i=DisplayBars;i>=0;i--) 
  {
    double m1=iMA(NULL,0,MAPeriod,0,MAMode,MAPrice,i+1);
    double m2=iMA(NULL,0,MAPeriod,0,MAMode,MAPrice,i+2);
    
    // условия открытия одновременно и условия закрытия противоположного ордера
    if(m2<=m1 && MathAbs(m1-m2)>0.2*Point) { sBuy[i]=1; sCloseSell[i]=1; sBuyCnt++; sSellCloseCnt++; }
    if(m2>=m1 && MathAbs(m1-m2)>0.2*Point) { sSell[i]=1; sCloseBuy[i]=1; sSellCnt++; sBuyCloseCnt++; }
  }

и вот какие с ним получаются результаты:

Неплохой, но не самый лучший результат - мы ведь нафантазировали себе сумашедшую прибыль. Давайте попробуем уменьшить период МА, чтобы увеличить возможность заработка на профитных сделках. Уменьшив период с 15 до 7 бар, мы чуть ли не в два раза увеличили среднедневную прибыль:

дело осталось за малым - сделать соотношение прибыльных и убыточных сделок равным хотя бы 80% к 20%. Для этого вам придется потрудится, но уже без меня.

Заключение

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

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


Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (16)
[Удален] | 7 окт. 2008 в 12:09
coaster:
... всегда приходится ждать его подтверждения ... Второй минус - это риски...
space_cowboy:

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

Респект


Начало положено: кому надо, тот допишет и задержку в периодах (подтверждение сигнала), и уровень стоп лосса, и дату начала и конца экспресс-анализа.

Как всегда в коментах (komposter, coaster) можно найти то, что еще хотелось бы видеть в заключении.

+1 Респект.

Евгений
Евгений | 29 окт. 2008 в 10:12

Огромное спасибо. Мне очень понравилась работа. Самое главное, шаблон можно использовать даже начинающим прогерам!

Дима
Дима | 1 янв. 2009 в 19:42
Огромное спасибо)
Леонид
Леонид | 14 июн. 2011 в 22:33
Сохранить
[Удален] | 20 июн. 2012 в 02:17

Добрый день

пытаюсь переделать индикатор под мультивалютник, не получается.

можете помочь ?

Спать или не спать? Спать или не спать?
Предлагается альтернатива использованию функции Sleep() при реализации пауз между действиями эксперта. Рассматриваемый подход позволяет более рационально использовать машинное время.
Лень - двигатель прогресса. Полуавтоматическая разметка шаблона Лень - двигатель прогресса. Полуавтоматическая разметка шаблона
Среди множества приемов работы с графиками есть способ ручной разметки шаблона. На график наносятся линии тренда, каналы, уровни поддержки и сопротивления и т.д. Естественно, что для этой работы есть и специальные программы. Какой способ использовать, каждый решает для себя сам. В данной статье я предлагаю рассмотреть способы ручной разметки и затем автоматизировать некоторые элементы рутинного повторения некоторых действий по разметке.
Программная папка клиентского терминала MetaTrader 4 Программная папка клиентского терминала MetaTrader 4
В статье сделано описание содержимого программной папки клиентского терминала MetaTrader 4. Статья будет полезной прежде всего тем, кто уже немного разобрался с работой клиентского терминала.
Дополнительные Материалы Чемпионатов 2006-2007 годов Дополнительные Материалы Чемпионатов 2006-2007 годов
Предлагаем вашему вниманию подборку этих материалов, которые разбиты по темам. В данной теме представлены дополнительные материлы об Автоматическом Трейдинге, разработке экспертов и т.д.