Тестирование паттернов валютных пар: Использование и перспективы для реальной торговли. Часть IV

4 мая 2018, 10:00
Andrei Novichkov
0
2 142

Введение

Эта статья завершает изучение тестирования паттернов, которые возникают во время торговли корзинами валютных пар, начатое здесь. Нам осталось совсем немного: рассмотреть оставшийся паттерн — паттерн пересечения графиком индикатора скользящей средней.

Ранее мы уже рассматривали этот паттерн, но применительно к определенным условиям:

  • Трейдер получает сигнал на вход на закрытии свечи, когда график объединенного индикатора для одной из корзин валютных пар пересекается своей же скользящей средней. Всего таких сигналов два — когда скользящая средняя пересекает график сверху вниз (на продажу) и снизу вверх (на покупку).

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

Сейчас я предлагаю протестировать этот паттерн на графике разницы между значениями объединенного индикатора для двух корзин: по базовой валюте и валюте котировки (см. подробнее здесь). Использование двух корзин валютных пар вместо одной может послужить фактором дополнительной фильтрации.

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

Паттерн для исследований

Повторим основное требование к паттерну: пробитие графиком объединенного индикатора скользящей средней должно быть ясным и заметным.

Искомый, "хороший" паттерн (сигнал)
Отсутствие паттерна, "плохой" сигнал




Здесь зеленая линия — это график объединенного индикатора, розовая — его скользящая средняя.

Для однозначной идентификации паттерна нужно, чтобы расстояние между графиком объединенного индикатора и скользящей средней до и после момента пробоя было не меньше заданного (Delta1 и Delta2) на левом рисунке. Очевидно, что на правом рисунке, на интервале, ограниченном синими вертикальными линиями, это условие не соблюдается, и паттерна мы не наблюдаем.

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

Инструменты для исследований

Все инструменты у нас уже есть. Мы создали их ранее, в прошлых статьях. И хотя код почти не изменился, приведем его здесь. В качестве основного используется советник testEAbasket3.mq5:

//+------------------------------------------------------------------+
//|                                                 testEAbasket.mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade\\Trade.mqh>


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum BSTATE
  {
   BCLOSE = 0,
   BBUY   = 1,
   BSELL  = 2
  };
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
input int wpr = 20;
input int ma  = 10;
input double lt=0.01; 

int h;
ulong  Ticket;

double m[1],ml;
double w[1],wl;

BSTATE g_state;

double g_dMinSize = 2.0;
double g_dMaxSize = 50.0;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   h=iCustom(NULL,0,"testWPR&MA3",wpr,ma);
   if(h==INVALID_HANDLE) 
     {
      Print("Error while creating testWPR&MA3");
      return (INIT_FAILED);
     }

   g_state=BCLOSE;
   EventSetTimer(1);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(h);
   EventKillTimer();

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(IsNewCandle())
     {
      wl=w[0];
      CopyBuffer(h,0,1,1,w);
      ml=m[0];
      CopyBuffer(h,1,1,1,m);
      double d1 = MathAbs(w[0] - m[0]);
      double d2 = MathAbs(ml - wl);
      if(w[0]>m[0] && wl<ml) 
        {
         if(g_state!=BCLOSE) CloseAllPos();
         if(d1 >= g_dMinSize && d2 >= g_dMinSize &&
            d1 <= g_dMaxSize && d2 <= g_dMaxSize) 
           {
            EnterBuy(lt);
            g_state=BBUY;
           }
        }
      if(w[0]<m[0] && wl>ml) 
        {
         if(g_state!=BCLOSE) CloseAllPos();
         if(d1 >= g_dMinSize && d2 >= g_dMinSize &&
            d1 <= g_dMaxSize && d2 <= g_dMaxSize) 
           {
            EnterSell(lt);
            g_state=BSELL;
           }
        }
     }
  }
//+------------------------------------------------------------------+

void CloseAllPos()
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);
   Trade.PositionClose(Ticket);
   g_state=BCLOSE;

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void EnterBuy(double lot)
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);
   Trade.Buy(lot,_Symbol);
   Ticket=Trade.ResultDeal();
   g_state=BBUY;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void EnterSell(double lot)
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);
   Trade.Sell(lot,_Symbol);
   Ticket=Trade.ResultDeal();
   g_state=BSELL;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsNewCandle()
  {
   static int candle=-1;
          int t1=0;
   switch(_Period)
     {
      case PERIOD_H1:  t1 = Hour();   break;
      case PERIOD_H4:  t1 = Hour4();  break;
      case PERIOD_D1:  t1 = Day();    break;
     }
     
   if(t1!=candle) {candle=t1; return(true);}
   return (false);
  }
            
  int Hour4(){return((int)Hour()/4);}

int Day()
  {
   MqlDateTime tm;
   TimeCurrent(tm);
   return(tm.day);
  }

int Hour()
  {
   MqlDateTime tm;
   TimeCurrent(tm);
   return(tm.hour);
  }

Он представляет собой версию советника testEAbasket.mq5 из этой статьи, доработанную для входов на одной валютной паре, а не на всей корзине валют. В работе советник использует данные пользовательского индикатора testWPR&MA3.mq5, который представляет собой версию индикатора testWPR&MA.mq5 из той же статьи:

//+------------------------------------------------------------------+
//|                                                      testWPR.mq5 |
//|                                        MetaQuotes Software Corp. |
//|                                               http://fxstill.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_minimum -100
#property indicator_maximum 100


#property indicator_buffers 2
#property indicator_plots   2

input int WPR       = 20; //Period WPR
input int maperiod  = 10; //Period MA
input color   clr   = clrGreen;
input color   clrMA = clrMagenta;


int h,h1;
double ind[],ma[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
      h = iCustom(NULL,0,"testWPReur",WPR);
      if (h == INVALID_HANDLE) {
         Print("Error while creating testWPR");
         return (INIT_FAILED);
      }
      h1 = iCustom(NULL,0,"testWPRusd",WPR);
      if (h1 == INVALID_HANDLE) {
         Print("Error while creating testWPR");
         return (INIT_FAILED);
      }  
 

   IndicatorSetString(INDICATOR_SHORTNAME,"testWPRusd");
   IndicatorSetInteger(INDICATOR_DIGITS,2);
   IndicatorSetInteger(INDICATOR_LEVELS,2);
   IndicatorSetInteger(INDICATOR_LEVELSTYLE,0,STYLE_SOLID);
   IndicatorSetInteger(INDICATOR_LEVELSTYLE,1,STYLE_SOLID);
   IndicatorSetInteger(INDICATOR_LEVELCOLOR,0,clrRed);
   IndicatorSetInteger(INDICATOR_LEVELCOLOR,1,clrRed);
   IndicatorSetInteger(INDICATOR_LEVELWIDTH,0,1);
   IndicatorSetInteger(INDICATOR_LEVELWIDTH,1,1);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-60);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,1,60);

   ArraySetAsSeries(ind,true);
   SetIndexBuffer(0,ind);
   PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE);
   PlotIndexSetInteger(0,PLOT_LINE_STYLE,STYLE_SOLID);
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,2);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,clr);
   PlotIndexSetString(0,PLOT_LABEL,"_tstWPRusd_");

   ArraySetAsSeries(ma,true);
   SetIndexBuffer(1,ma);
   PlotIndexSetInteger(1,PLOT_DRAW_TYPE,DRAW_LINE);
   PlotIndexSetInteger(1,PLOT_LINE_STYLE,STYLE_SOLID);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,1);
   PlotIndexSetInteger(1,PLOT_LINE_COLOR,clrMA);
   PlotIndexSetString(1,PLOT_LABEL,"Middle_Basket_line_MA");


//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double GetValue(int shift)
  {
   double dBuf[1], dBuf1[1];
   double res=0.0;
   CopyBuffer(h,0,shift,1,dBuf);
   CopyBuffer(h1,0,shift,1,dBuf1);
   return (NormalizeDouble((dBuf[0] - dBuf1[0])/2, _Digits) );
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(prev_calculated==0 || rates_total>prev_calculated+1)
     {
      int rt=rates_total-WPR;
      for(int i=1; i<rt; i++)
        {
         ind[i]=GetValue(i);
        }
      rt-=maperiod;
      for(int i=1; i<rt; i++)
        {
         ma[i]=GetMA(ind,i,maperiod,_Digits);
        }
     }
   else
     {
         ind[0] = GetValue(0);
         ma[0]  = GetMA(ind, 0, maperiod, _Digits);        
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

void OnDeinit(const int reason)
  {
      if(h!=INVALID_HANDLE) IndicatorRelease(h);
      if(h1!=INVALID_HANDLE) IndicatorRelease(h1);
  }
//+------------------------------------------------------------------+

double GetMA(const double &arr[],int index,int period,int digit) 
  {
   double m=0;
   for(int j=0; j<period; j++) m+=arr[index+j];
   m/=period;
   return (NormalizeDouble(m,digit));
  }
//+------------------------------------------------------------------+

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

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

Перед началом тестирования уточним форму паттерна. Этому вопросу, а именно — конкретным значениям параметров Delta1 и Delta2 — мы уже уделили достаточно времени. Эти значения были найдены, и в результате появилась жесткая фильтрация сигналов на вход в рынок. Здесь мы несколько ослабим ограничения. Это повысит количество входов без ущерба для принципа, лежащего в основе паттерна: Delta1 и Delta2 должны быть не меньше 2% и не больше 50%.

Выберем одну из двух стратегий тестирования.

  • Постоянное присутствие в рынке. Трейдер входит рынок по сигналу и ждет сигнала на вход в противоположном направлении. Затем закрывает открытые ордера, входит в противоположном направлении и т.д. Этой стратегии мы придерживаться не будем: она показала свою убыточность.
  • Выход из рынка без открытия новых ордеров. Трейдер входит в рынок по сигналу и ждет сигнала на выход, в роли которого выступает "плохой" паттерн. Он не дает сигналов на вход, но может предупреждать о возможном замедлении тренда или его развороте. Эту стратегию и будем использовать при тестировании.

И, наконец, определимся с таймфреймом. В прошлых статьях мы тестировали паттерн на H1, H4 и D1. Но на D1 было очень мало сделок, поэтому на этом таймфрейме тестировать паттерн мы не будем. Оставим только H1 и H4 и начнем тестирование с пары EURUSD.

Тестирование паттерна

После теста на котировках за последний год на H1 получаем следующие результаты:

На таймфрейме H4:

Как и в прошлых тестах, мы получили убыточность на H1 и профит на H4.

Протестируем паттерн на всех основных валютных парах: EURUSD, GBPUSD, AUDUSD, NZDUSD, USDCAD, USDCHF, USDJPY.

Сюрпризов это тестирование не принесло, результат аналогичен уже полученным данным: обнадеживающие результаты на H4 и убыток на H1. Отчеты о тестировании находятся в прилагаемом архиве testhtml.zip.

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

  • Трейдер НЕ будет входить в рынок на покупку, если график объединенного WPR коснулся или пробил границу перекупленности снизу вверх.
  • Трейдер НЕ будет входить в рынок на продажу, если график объединенного WPR коснулся или пробил границу перепроданности сверху вниз.

Скорректированный код советника находится в прилагаемом архиве test1.zip. Здесь я не буду приводить его полностью: он почти повторяет ранее приводившийся код. Покажу лишь функцию, непосредственно идентифицирующую паттерн:

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(IsNewCandle())
     {
      wl=w[0];
      CopyBuffer(h,0,1,1,w);
      ml=m[0];
      CopyBuffer(h,1,1,1,m);
      double d1 = MathAbs(w[0] - m[0]);
      double d2 = MathAbs(ml - wl);
      if(w[0]>m[0] && wl<ml) 
        {
         if(g_state!=BCLOSE) CloseAllPos();
         if(d1 >= g_dMinSize && d2 >= g_dMinSize &&
            d1 <= g_dMaxSize && d2 <= g_dMaxSize && w[0] < 60) 
           {
            EnterBuy(lt);
            g_state=BBUY;
           }
        }
      if(w[0]<m[0] && wl>ml) 
        {
         if(g_state!=BCLOSE) CloseAllPos();
         if(d1 >= g_dMinSize && d2 >= g_dMinSize &&
            d1 <= g_dMaxSize && d2 <= g_dMaxSize && w[0] > -60) 
           {
            EnterSell(lt);
            g_state=BSELL;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Возникает вопрос: где будут располагаться уровни перекупленности и перепроданности? В прошлых статьях мы показали, что для тестируемых условий можно немного снизить стандартные значения объединенного индикатора на основе WPR (+60 для уровня перекупленности и -60 для уровня перепроданности). Кроме варианта со стандартными значениями, протестируем значения +50% и -50% для уровней перекупленности и перепроданности, соответственно. Отчеты о тестировании для пары EURUSD на таймфреймах H1 и H4 находятся в архивах testhtml50.zip и testhtml60.zip.

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

Напомним суть ситуации.

  • График объединенного WPR пробивает уровень перепроданности снизу вверх. Пробой фиксируется на закрытии свечи. Трейдер входит в рынок на покупку, но только в том случае, если скользящая средняя находится "ниже" графика объединенного индикатора.
  • Для входа в рынок на продажу ситуация должна быть зеркальной.
  • Трейдер выходит из рынка, когда график объединеного WPR доходит до 0%.

Для тестирования используем советник testEAbasket5.mq5:

//+------------------------------------------------------------------+
//|                                                 testEAbasket.mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade\\Trade.mqh>



enum BSTATE
  {
   BCLOSE = 0,
   BBUY   = 1,
   BSELL  = 2
  };
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
input int wpr = 20;
input int ma  = 10;
input double lt=0.01; //lot

int SELLPROFIT =   0;
int SELL1LIMIT =  50;
int SELL2FROM  =  40;
int SELL2TO    =  20;
int BUYPROFIT  =   0;
int BUY1LIMIT  = -50;
int BUY2FROM   = -40;
int BUY2TO     = -20;


int h;
ulong  Ticket;

double m[1],ml;
double w[1],wl;

BSTATE g_state;

double g_dMinSize = 2.0;
double g_dMaxSize = 50.0;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   h=iCustom(NULL,0,"testWPR&MA3",wpr,ma);
   if(h==INVALID_HANDLE) 
     {
      Print("Error while creating testWPR&MA3");
      return (INIT_FAILED);
     }

   g_state=BCLOSE;
   EventSetTimer(1);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(h);
   EventKillTimer();

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(IsNewCandle())
     {
      wl=w[0];
      CopyBuffer(h,0,1,1,w);
      ml=m[0];
      CopyBuffer(h,1,1,1,m);


      if(g_state==BBUY && (w[0]>=BUYPROFIT))
        {
         CloseAllPos();
        }
      if(g_state==BSELL && (w[0]<=SELLPROFIT))
        {
         CloseAllPos();
        }
      if(g_state==BCLOSE && w[0]>=BUY2FROM && w[0]<=BUY2TO && wl<=BUY1LIMIT && w[0] > m[0])
        {
            EnterBuy(lt);
            return;        
        }
        
 
      if(g_state==BCLOSE && w[0]<=SELL2FROM && w[0]>=SELL2TO && wl>=SELL1LIMIT && w[0] < m[0])
        {
            EnterSell(lt);
            return;        
        }
     }
  }
//+------------------------------------------------------------------+

void CloseAllPos()
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);
   Trade.PositionClose(Ticket);
   g_state=BCLOSE;

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void EnterBuy(double lot)
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);
   Trade.Buy(lot,_Symbol);
   Ticket=Trade.ResultDeal();
   g_state=BBUY;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void EnterSell(double lot)
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);
   Trade.Sell(lot,_Symbol);
   Ticket=Trade.ResultDeal();
   g_state=BSELL;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsNewCandle()
  {
   static int candle=-1;
          int t1=0;
   switch(_Period)
     {
      case PERIOD_H1:  t1 = Hour();   break;
      case PERIOD_H4:  t1 = Hour4();  break;
      case PERIOD_D1:  t1 = Day();    break;
     }
     
   if(t1!=candle) {candle=t1; return(true);}
   return (false);
  }
            
  int Hour4(){return((int)Hour()/4);}

int Day()
  {
   MqlDateTime tm;
   TimeCurrent(tm);
   return(tm.day);
  }

int Hour()
  {
   MqlDateTime tm;
   TimeCurrent(tm);
   return(tm.hour);
  }

Начнем тестирование, как и раньше, с пары EURUSD, на двух таймфреймах — H1 и H4.

Результаты на таймфрейме H1:

На таймфрейме H4:

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

Итоги исследования

  1. Мы познакомились с технологией торговли с использованием корзин валютных пар. Были приведены основные принципы и понятия, способы выполнения основных расчетов.
  2. Описаны несколько простых паттернов, возникающих при торговле корзинами валютных пар.
  3. Бегло рассмотрены более сложные конструкции при использовании корзин — одновременное использование индикаторов, специально сконструированных для работы с корзинами валютных пар и стандартных технических индикаторов; использование осцилляторов и трендовых индикаторов одновременно.
  4. В тестере найденные паттерны проверены на пригодность.

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

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

Оценим перспективы, наметим возможные варианты применения описанной технологии в повседневной практике и автоматической торговле.

Перспективы и применение в реальной торговле

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

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

Вход и выход в подробностях

Рассмотрим подробности входа и выхода из рынка на валютной паре NZDUSD, H1. Объединенный индикатор обнаружил сигнал на вход на покупку, оказавшийся прибыльным (вертикальная синяя линия):



Слева изображен график разницы между значениями объединенного индикатора для корзин базовой валюты и валюты котировки. "Псевдоформулу" этого индикатора можно было бы записать так:

Индикатор(NZDUSD) = Индикатор(NZD) — Индикатор (USD)

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

Возможные варианты входа будут такими.

  1. Нижняя красная горизонтальная линия — уровень перепроданности. Мы видим, что сначала образуется паттерн пробоя этого уровня графиком индикатора. Это место отмечено вертикальной зеленой линией, а сам паттерн идентифицируется при открытии свечи №1. В этом месте уже можно открывать ордер на покупку: нисходящий тренд исчерпал свои возможности и начинает разворачиваться. Трейдер захватывает самое начало этого движения и имеет возможность "взять" весь восходящий тренд. Этот паттерн мы протестировали. Он довольно рискованный и встречается нечасто. Риск связан с тем, что нисходящий тренд может продолжиться после коррекции. В этом случае трейдер может понести убытки, но не слишком большие. Здесь вполне можно использовать "короткий" стоп-лосс.
  2. Но на скриншоте мы видим, что новый восходящий тренд начинает набирать силу. Образуется новый паттерн — пробой графиком индикатора скользящей средней. Это хороший сигнал-фильтр после пробоя уровня перепроданности. Он снижает риск продолжения нисходящего тренда. Паттерн отмечен также зеленой вертикальной линией и может быть идентифицирован при открытии свечи №2. Можно войти в рынок и здесь. Стоп-лосс в общем случае должен быть больше, а насколько именно — будет определяться параметрами скользящей средней.
  3. Восходящий тренд набрал силу, окреп, и появляется новый паттерн — пробой "быстрой" скользящей средней графика "медленной" скользящей средней. Этот паттерн идентифицируется на открытии свечи №3. Риск, по сравнению с предыдущими двумя случаями, будет наименьший. Но и прибыль будет меньше, а вот стоп-лосс — больше. Обратите внимание на то, как увеличивается расстояние между скользящими средними после обнаружения паттерна. Это означает продолжение и усиление тренда — благоприятный сигнал для трейдера. А вот уменьшение этого расстояния говорит о замедлении и ослаблении (пример этого мы видим на левом скриншоте).

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

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

  • Ситуацию с попаданием графика индикатора в зону перепроданности (или перекупленности) демонстрирует правый скриншот. Нужное нам место отмечено зеленым прямоугольником. В окне индикатора тот же самый индикатор отображен другим способом — отдельно для корзин по NZD (светло-голубая линия) и USD (темно-зеленая линия). Видно, что корзина USD в какой-то момент оказалась перекуплена, а корзина NZD довольно долго была перепродана. Одновременное сочетание таких состояний корзин и создает картину, как на скриншоте слева — с выходом графика объединенного индикатора для пары NZDUSD в зону перекупленности с последующим пробоем уровня перекупленности и разворотом тренда.

Но такие благоприятные сочетания событий бывают нечасто. Неужели придется ждать сигнала неделями? Необязательно. Можно принять, что уровни перекупленности / перепроданности находятся на других значениях, немного меньших. Для каждой пары и для каждого таймфрейма значение может быть своим. Но представляется, что в большинстве случаев можно ограничиться уровнями +50% и -50%. Это несомненно увеличит риск. Взглянем еще раз на правый скриншот. Новое расположение уровней будет означать, что одна из двух корзин (или даже обе!) еще не исчерпала движение. Значит, оно может продолжиться, и трейдер рискует войти против тренда. Поэтому особое значение имеет паттерн №3, который подразумевает вход по тренду. Но и в этом случае есть "подводные камни" — дивергенция:



На правом скриншоте мы видим, как восходящая быстрая МА пробивает медленную, при восходящем движении самого индикатора. На скриншоте слева наблюдаем дивергенцию: графики индикатора и МА двигаются в разные стороны. Сигнал с дивергенцией несомненно должен быть пропущен! В моей ленте есть и другие примеры различных паттернов с дивергенцией и без неё.

Теперь поговорим о возможных способах выхода из рынка (закрытия ордеров).

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

1. Например, вход индикатора в зону перепроданности — весомый аргумент для закрытия ордеров на покупку или перевода их в безубыток (для покупок — зеркально).

2. Трейдер, имея открытые ордера, может обнаружить пробой МА графиком индикатора в направлении, обратном ранее открытым ордерам.

3. Трейдер может обнаружить замедление тренда по уменьшению расстояния между быстрой и медленной скользящими средними.

4. Наконец, сигналом к закрытию / переводу в безубыток может быть достижение индикатором значений, НЕ являющихся уровнями перекупленности / перепроданности. Например, это может быть уровень 0%. Этот уровень означает "равенство" обеих корзин валют, и на нем индикатор склонен "спотыкаться". Значит, этот уровень представляет собой сопротивление:

Еще в объединенных индикаторах в такой роли могут выступать фибо-уровни. 

5. И последнее: трейдер может назначить стоп-лосс и тейк-профит, исходя из собственных соображений или на стандартном теханализе.

Итак, способы открытия и закрытия ордеров мы рассмотрели. Теперь приступим к поиску паттернов на чарте и их анализу.

Работа с объединенными индикаторами


Чтобы идентифицировать обнаруженные паттерны, помещаем на график объединенный индикатор и смотрим на результаты. Вот пример за последнее время для пары NZDUSD, H1 (график разбит на два изображения из-за ограничений по размерам):


Обратите внимание на те места, где паттерны № 3 и 7 обнаружены вблизи выхода из зон перекупленности / перепроданности. Дивергенции здесь нет, и можно отметить уверенное движение индикатора в сторону покупок, включая переход через нулевой уровень в зоне паттерна №3. С другой стороны, движения в зоне паттерна N7 явно робкие. Поначалу хорошее движение замедлилось, откатилось, через нулевой уровень график еще не перешел и вполне способен от него отскочить (что мы и наблюдаем).

Паттерн №8 демонстрирует отскок от уровня перепроданности. График его коснулся, развернулся и уверенно пошел в сторону покупок, сформировав паттерн. Нулевой уровень пройден, вполне можно входить на покупку. Но график индикатора и цена разворачиваются и уходят в противоположную сторону. Чуть позже у трейдера будет возможность закрыть ордер с минимальным убытком, но если он этого не сделает, то убыток может быть вполне ощутимым.

Паттерн №6 не слишком перспективен: потенциал движения на продажу практически исчерпан, достигнут уровень перепроданности. Есть риск близкого разворота. На практике же мы видим длительное боковое движение, фактически не приносящее прибыли. Нисходящее движение начинается снова, но намного позже: там паттерн можно считать давно отработанным.

Паттерн №5, при схожих условиях с № 6, очень перспективен. Поэтому не стоит однозначно отметать паттерны, которые индикатор находит на подходе к зонам перекупленности / перепроданности, когда тренд к моменту их обнаружения еще вполне может продолжить движение.

Паттерн №4 так же неуверенно движется в районе нулевого уровня, не пробивая его. Дивергенции нет, график индикатора идет горизонтально.

Паттерн №2 перспективен. Нисходящее движение замедлилось и развернулось, не дойдя до нулевого уровня. Дивергенции нет, график еще не дошел до уровня перепроданности. И хотя далее индикатор переходит в боковое движение, паттерн все равно приносит хорошую прибыль.

Паттерн №1 рассматривать не будем: мы не знаем, что происходило до его формирования.

Кроме найденных паттернов, стоит обращать внимание и на участки, подобные отмеченному синей стрелкой. Это мощное движение от уровня перепроданности в сторону покупок, равномерное, с пробоем нулевого уровня и выходом в зону перекупленности. Индикатор не нашел паттерна, хотя обе МА пробиты. Никаких дивергенций нет. Трейдер определенно должен рассмотреть вопрос о входе в рынок, когда индикатор пробил нулевой уровень — тем более, что этот пробой совпадает по времени с пробоем быстрой МА медленной.

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


Продвинутая работа с объединенными индикаторами

Первое, о чем стоит поговорить, — об использовании на одном чарте двух экземпляров одного объединенного индикатора. Косвенно такая возможность уже упоминалась в предыдущем разделе, рассмотрим ее подробнее.

  • На графике располагаются два экземпляра одного объединенного индикатора с одинаковыми настройками, но с разными способами отображения:
    1. По умолчанию — окно V1.
    2. Как два отдельных графика для корзин базовой валюты и валюты котировки (окно V2).

Здесь показаны индикаторы на валютной паре USDCAD. В окне V2 темно-зеленой линией отрисовано состояние корзины валютных пар по USD, а оранжевой — по CAD.

Итак, в окне V1 индикатор нашел два паттерна на вход на покупку (№1 и №2). Видно, что дивергенции нет в обоих случаях и вход в рынок может быть прибыльным.

Посмотрим на окно V2, паттерн №1. Здесь видно, что корзина CAD была перекуплена и теперь ослабляется, приближаясь к нулевому уровню. Корзина по USD тоже ослаблялась, выходя из перекупленности. Но затем ослабление прекратилось, перешло в горизонтальное движение, и на момент обнаружения паттерна имеет тенденцию к усилению. Таким образом, вход на покупку вполне оправдан.

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

Кроме этого, трейдер может обратить внимание на точку №3. Паттерна там нет, но в окне V2 видно, что обе корзины выходят из зон перекупленности и перепроданности навстречу друг другу. Это означает, что, возможно, трейдеру удастся захватить начало только что развернувшегося тренда.

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

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

Это практически тот же участок с графика USDCAD. Ранее мы уже выяснили, что паттерн №1 подходит для входа в рынок, а №2 — несет больший риск. Теперь посмотрим на паттерн №3, тоже неудачный. Дивергенции на нем нет, но и прибыли он не приносит. И в окне со стандартным WPR становится понятно, почему это происходит: WPR находится в зоне перекупленности. В результате пара выполняет боковое движение, за которым вполне может последовать либо коррекция, либо даже разворот тренда. Паттерн №2 и здесь демонстрирует боковое движение с последующей коррекцией. А вот паттерн №1 по-прежнему хорош. Вход по тренду, у индикатора WPR есть запас хода до зоны перекупленности, "нулевой" уровень уже пройден. Все говорит за потенциальную прибыльность паттерна, что и подтверждается впоследствии.

Теперь приведем пример менее впечатляющего "сотрудничества". Та же валютная пара USDCAD, те же участок и таймфрейм. На графике расположены объединенный STOCH и стандартный WPR. Для справки оставлены вертикальные линии, отмечающие ранее найденные паттерны:


Непосредственно объединенный STOCH нам каких-то значимых паттернов не предложил. Он, как и объединенный WPR считает паттерны №№ 1 и 3 пригодными для входа. А вот на паттерне №2 образовалась дивергенция — значит, вход по нему запрещен. Обратите внимание: зоны, которые WPR считает зонами перекупленности, объединенный STOCH относит скорее к зонам бокового движения. Вот еще один пример сочетания индикаторов.

Рассмотренные способы работы с объединенными индикаторами не единственно возможные, но вполне узнаваемые, очевидные, дающие результат.

Заключение

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

Большинство примеров и иллюстраций были выполнени с использованием объединенного WPR. Но существуют и другие объединенные индикаторы — STOCH, RSI, TDI и т.д. Все вместе они составляют индикаторную стратегию торговли корзинами валютных пар. Стратегия эта непростая и подвластна опытному, думающему трейдеру.

Есть ли дальнейшие возможности развития? Конечно, есть. И о них будет обязательно написано.


Программы, используемые в статье:

 # Имя
Тип
 Описание
1 test.zip Архив
Инструменты для тестирования паттерна.
2
testhtml.zip Архив Результаты тестирования инструментами из архива test.zip.
3 test1.zip Архив Код советника для тестирования с фильтром.
4 testhtml50.zip Архив Результаты тестирования инструментами из архива test1.zip
5
testhtml60.zip Архив
Результаты тестирования инструментами из архива test1.zip
 6 testEAbasket5.mq5 Советник Советник для тестирования


Прикрепленные файлы |
test.zip (15.54 KB)
testhtml.zip (1197.56 KB)
testhtml50.zip (226.91 KB)
testhtml60.zip (200.84 KB)
testEAbasket5.mq5 (4.81 KB)
Визуализация результатов оптимизации по выбранному критерию Визуализация результатов оптимизации по выбранному критерию

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

Улучшаем работу с Панелями: добавляем прозрачность, меняем цвет фона и наследуемся от CAppDialog/CWndClient Улучшаем работу с Панелями: добавляем прозрачность, меняем цвет фона и наследуемся от CAppDialog/CWndClient

Продолжаем изучать работу с CAppDialog. Теперь мы научимся задавать цвета фона, рамки и заголовка для графической панели. По шагам рассмотрим, как добавить прозрачность окна приложения при перемещении его на графике. Далее мы рассмотрим создание потомков от CAppDialog или CWndClient и увидим новые тонкости в работе с контролами. Наконец, посмотрим с новой точки зрения на новые Проекты.

Глубокие нейросети (Часть VII). Ансамбль нейросетей: stacking Глубокие нейросети (Часть VII). Ансамбль нейросетей: stacking

Мы продолжаем строить ансамбли. Теперь к bagging-ансамблю, созданному ранее, добавим обучаемый объединитель — глубокую нейросеть. Одна нейросеть объединяет 7 лучших выходов ансамбля после обрезки. Вторая принимает на вход все 500 выходов ансамбля, обрезает и объединяет их. Нейросети будем строить с помощью пакета keras/TensorFlow из Python. Кратко рассмотрим возможности пакета. Проведем тестирование и сравним качество классификации bagging и stacking ансамблей.

Торговый эксперт с графическим интерфейсом: Создание панели (Часть I) Торговый эксперт с графическим интерфейсом: Создание панели (Часть I)

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