English 中文 Español Deutsch 日本語 Português
Новый подход к интерпретации классической и обратной дивергенции

Новый подход к интерпретации классической и обратной дивергенции

MetaTrader 5Торговые системы | 26 сентября 2017, 13:26
12 541 26
Alexander Lasygin
Alexander Lasygin

Введение

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

Определение дивергенции / конвергенции

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

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

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

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

Виды дивергенций

Разные исследователи классифицируют рыночную дивергенцию/конвергенцию разными способами. Мы возьмем за основу ее подразделение на два основных вида: классическую и скрытую.

    Классическая дивергенция

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

    Классическая дивергенция широко описана в литературе. В зависимости от признаков, ее можно разбить еще на три подкласса

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

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

    Скрытая дивергенция

    Этот тип дивергенции тоже разделяется на подклассы:

    • Понижающиеся максимумы цены одновременно с возрастающими максимумами осциллятора указывают на подтверждение ценового тренда вниз.
    • Растущие минимумы цены и снижающиеся минимумы осциллятора указывают на подтверждение ценового тренда вверх.


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

    Индикаторы и осцилляторы для поиска дивергенций / конвергенций и как это работает

    Какие индикаторы указывают на дивергенцию и конвергенцию? Индикатор технического анализа должен уметь определять уровни спроса и предложения и отслеживать моментум. К таким относятся осцилляторы, стохастик, RSI, CCI и т.д., а также трендовый индикатор с элементами осциллятора — MACD. MACD можно интерпретировать как дивергенцию и конвергенцию скользящих средних. Он эффективно отслеживает несоответствия в движении цены и ее моментума. Многие трейдеры основывают свои решения по дивергенции и конвергенции именно по этому индикатору.

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

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

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


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

    Ниже в подтверждение приведем тот же участок рынка с индикатором RSI.


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

    Осциллятор RSI

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

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

    Осциллятор Stochastic

    Для осциллятора используются стандартные настройки:

    • Период %K: 5
    • Период %D: 3
    • Замедление: 3

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

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

    Осциллятор MACD

    MACD — интересный осциллятор, который может многое, в том числе и помогать в поиске дивергенции. Индикатор используется со стандартными настройками:

    • Быстрая EMA: 12
    • Медленная EMA: 26
    • MACD SMA: 9


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

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

    Индикатор Volumes (объём)

    Затронем еще и такой важный показатель, как объем. Один из сильнейших разворотных сигналов — это дивергенция (конвергенция) цены и объема (volumes). Суть ее сводится к следующему:

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

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

    А вот классическая дивергенция на этом индикаторе чаще всего сигнализирует только об замедлении и переходе в консолидацию.


    Правила определения дивергенции / конвергенции и торговли по ней

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

    • Обязательно присутствие тренда, дивергенция во флэте не работает.
    • Работая с дивергенциями, подтверждайте свои входы паттернами из PriceAction или Японских свечей.
    • Не стоит рассчитывать, что вы нашли 100%-ный сигнал. Всегда есть вероятность ошибки. Поэтому, торгуя на рынке, соблюдайте все обычные правила совершения сделок.
    • Не стоит использовать для определения дивергенции свечи, построенные на новостных индикаторах. Из-за высокой волатильности во время новостей такие свечи очень часто дают ложные сигналы.
    • Искомая дивергенция должна быть явной. Вы должны быть уверены, что сигнал виден не только вам, но и остальным игрокам на рынке. Только в этом случае шанс принятия правильного решения возрастает.
    • Сопоставляйте только ближайшие пики/впадины. Это дает более четкое понимание ситуации. При использовании трендовых индикаторов, таких как MACD, пересечение нулевой линии если не отменяет сигнал, то значительно его ослабляет.

    Новый подход

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

    В основу эксперимента положим классический инструмент — индикатор Билла Вильямса Accelerator Oscillator. Выбор на него пал не случайно. Этот индикатор будет изменять направление перед изменением движущей силы, а она, в свою очередь, будет изменять направление перед изменением цены. АO — ранний подтверждающий сигнал, и это дает нам очевидные преимущества. Мы имеем возможность чаще и раньше получать сигналы на расхождение. Если в обычной ситуации подобное решение влечет за собой "засорение", то в нашем случае это даст больше сигналов на вход в рынок и более ранние сигналы на его замедление.

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

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

    #property indicator_separate_window
    //---- для расчёта и отрисовки индикатора использован один буфер
    #property indicator_buffers 2
    //---- использовано всего одно графическое построение
    #property indicator_plots 1
    //---- отрисовка индикатора в виде линии
    #property indicator_type1 DRAW_COLOR_HISTOGRAM
    //---- в качестве цвета линии индикатора использован синий цвет
    #property indicator_color1 Green,Red
    //----
    #property indicator_width1 2
    //---- линия индикатора - непрерывная кривая
    #property indicator_style1 STYLE_SOLID
    //---- отображение метки индикатора
    #property indicator_label1 "AC_Div"
    //+------------------------------------------------------------------+
    input int Bars_Calculated=1000;
    //+------------------------------------------------------------------+
    string shortname="";
    double AC_buff[];
    double Color_buff[];
    int wid;
    int Handle_AC;
    //---
    #define DATA_LIMIT 37

    Инициализацию проведем с изначально заданными параметрами многоцветной гистограммы.

    //+------------------------------------------------------------------+
    //| Custom indicator initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
     {
    //---- превращение динамических массивов в индикаторный буфер
     SetIndexBuffer(0,AC_buff,INDICATOR_DATA);
     SetIndexBuffer(1,Color_buff,INDICATOR_COLOR_INDEX);
    //---- установка позиции, с которой начинается отрисовка индикатора
     PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,DATA_LIMIT);
    //---- инициализация переменной для короткого имени индикатора
     shortname="Accelerator_Divergence";
    //---- создание имени для отображения в отдельном подокне и во всплывающей подсказке
     IndicatorSetString(INDICATOR_SHORTNAME,shortname);
    //---- определение точности отображения значений индикатора
     IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    //---- запрет на отрисовку индикатором пустых значений
     PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
     PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
    //--- формируем хэндл индикатора Accelerator
     Handle_AC=iAC(NULL,0);
    //--- выясняем номер подокна индикатора
     wid=ChartWindowFind(0,shortname);
    //---
     return(INIT_SUCCEEDED);
     }

    Функция основного расчета индикатора будет состоять из двух частей.

    Первая часть — это сам базовый индикатор.

    //---- Объявления локальных переменных 
     int limit,bar,pos;
    //---- Проверка количества баров на достаточность для расчёта
     if(rates_total<DATA_LIMIT)
     return(0);
     int barsCalculated=MathMin(Bars_Calculated,rates_total);
    //+------ Задаем направление индексации массива ---------------------+
     ArraySetAsSeries(close,true);
     ArraySetAsSeries(AC_buff,true);
     ArraySetAsSeries(low,true);
     ArraySetAsSeries(high,true);
     ArraySetAsSeries(Color_buff,true);
     ArraySetAsSeries(time,true);
    //+--- Определяем количество необходимых баров для расчета ----------+
     limit=rates_total-DATA_LIMIT-1;
     if(prev_calculated>0) limit=rates_total-prev_calculated;
     pos=limit;
     if(pos>barsCalculated)pos=limit;
     int to_copy;
     if(prev_calculated>rates_total || prev_calculated<0) to_copy=rates_total;
     else
     {
     to_copy=rates_total-prev_calculated;
     if(prev_calculated>0) to_copy++;
     }
    //---
     if(IsStopped()) return(0); //Checking for stop flag
    //+----- Формируем основной массив ----------------------------------+
     if(CopyBuffer(Handle_AC,0,0,to_copy,AC_buff)<=0)
     {
     Print("getting Accelerator Handle is failed! Error",GetLastError());
     return(0);
     }
    //+---------- Раскрашиваем гистограмму ------------------------------+
     for(bar=limit; bar>=0 && !IsStopped(); bar--)
     {
     Color_buff[bar]=0.0;
     if(AC_buff[bar]<AC_buff[bar+1])Color_buff[bar] =1.0;
     if(AC_buff[bar]>AC_buff[bar+1])Color_buff[bar] =0.0;
     }

    Вторая часть — поиск самих расхождений.

    //+----------- Выявляем UP-расхождения -------------------------------+
     int bars=barsCalculated;
     for(bar=pos; bar>=0 && !IsStopped(); bar--)
     {
     int l=bar+2;
     if(Extremum(AC_buff[l+1],AC_buff[l],AC_buff[l-1])<0)
     {
     int i=l;
     int counted=LastPeak(l,bars,AC_buff);
     if(counted!=-1)
     {
     if(AC_buff[i]<AC_buff[counted] && high[i]>high[counted])
     {
     DrawPriceTrendLine(time[i],time[counted],high[i],high[counted],Red,STYLE_SOLID);
     DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Red,STYLE_SOLID);
     }
    
     if(AC_buff[i]>AC_buff[counted] && high[i]<high[counted])
     {
     DrawPriceTrendLine(time[i],time[counted],high[i],high[counted],Red,STYLE_DOT);
     DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Red,STYLE_DOT);
     }
     }
     }
    //+----------- Выявляем DN-расхождения -------------------------------+
     if(Extremum(AC_buff[l+1],AC_buff[l],AC_buff[l-1])>0)
     {
     int i=l;
     int counted=LastTrough(l,bars,AC_buff);
     if(counted!=-1)
     {
     if(AC_buff[i]>AC_buff[counted] && low[i]<low[counted])
     {
     DrawPriceTrendLine(time[i],time[counted],low[i],low[counted],Green,STYLE_SOLID);
     DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Green,STYLE_SOLID);
     }
     if(AC_buff[i]<AC_buff[counted] && low[i]>low[counted])
     {
     DrawPriceTrendLine(time[i],time[counted],low[i],low[counted],Green,STYLE_DOT);
     DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Green,STYLE_DOT);
     }
     }
     }
     }

    Для сокращения объёма кода графические построения и функция поиска экстремумов вынесены отдельно.

    //+----- поиск второго UP-экстремума --------------------------------+
    int LastPeak(int l,int bar,double &buf[]) 
     {
     for(int i=l+5; i<bar-2; i++)
     if(Extremum(buf[i+1],buf[i],buf[i-1])<0)return (i);
     return (-1);
     }
    //+------ поиск второго DN-экстремума -------------------------------+
    int LastTrough(int l,int bar,double &buf[])
     {
     for(int i=l+5; i<bar-2; i++)
     if(Extremum(buf[i+1],buf[i],buf[i-1])> 0)return (i);
     return (-1);
     }
    //+-- поиск экстремумов --------------------------------------------+
    int Extremum(double a,double b,double c)
     {
     if((a-b)*(b-c)<0)
     {
     if(c>b && b<0) return(1); //DN-экстремум
     if(c<b && b>0) return(-1);//UP-экстремум
     }
     return(0);
     }
    //+------ создаем объекты на графике цены ---------------------------+
    void DrawPriceTrendLine(datetime T_0,
     datetime T_1,
     double P_0,
     double P_1,
     color color_0,
     int style)
     {
     string name_2=shortname+DoubleToString(T_0,0);
     string name_0;
     name_0=shortname+"Line_Sn"+ColorToString(color_0);
    //--- 
     if(ObjectFind(0,name_2)<0)
     drawLineS(name_2,T_0,T_1,P_0,P_1,color_0,style,0,true,false,0);
    //+-----------+
     if(style==STYLE_DOT)
     drawLineS(name_0,T_1,T_0,P_1,P_0,clrAqua,0,3,true,true,0);
     }
    //+------ создаем объекты в окне индикатора -------------------------+
    void DrawIndicatorTrendLine(datetime T_0,
     datetime T_1,
     double P_0,
     double P_1,
     color color_0,
     int style)
     {
     string name_1,name_0;
     int window= wid;
     name_1 = shortname+DoubleToString(T_0+wid,0);
     if(ObjectFind(0,name_1)<0)
     drawLineS(name_1,T_0,T_1,P_0,P_1,color_0,style,0,false,false,window);
    //---
     if(style==STYLE_SOLID)
     {
     name_0=shortname+"Line_Pn"+ColorToString(color_0);
     drawLineS(name_0,T_1,T_0,P_1,P_0,clrMagenta,style,2,true,true,window);
     }
     }
    //+------------------------------------------------------------------+
    void drawLineS(string name,
     datetime t0,
     datetime t1,
     double p0,
     double p1,
     color clr,
     int style,
     int width,
     bool back,
     bool ray,
     int window)
     {
     ObjectDelete(0,name);
     ObjectCreate(0,name,OBJ_TREND,window,t0,p0,t1,p1,0,0);
     ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,ray);
     ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
     ObjectSetInteger(0,name,OBJPROP_STYLE,style);
     ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
     ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
     ObjectSetInteger(0,name,OBJPROP_BACK,back);
     }
    //+------------------------------------------------------------------+
    

    Для удобства восприятия классическая дивергенция у нас будет выделяться сплошной линией, а скрытая — пунктирной. Красным цветом обозначены медвежьи расхождения, зеленым — бычьи. Жирными лучами отображены последние «скрытые» бычья и медвежья дивергенции на ценовом графике и классические дивергенции в окне индикатора. Зачем мы это делаем, будет пояснено чуть ниже по тексту статьи. Индикатор будет иметь такой вид:


    Базовые принципы стратегии

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

    Пример преодоления линий поддержки, сформированных "скрытой" дивергенцией, расположенных на графике цены:


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


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

    На этих закономерностях в поведении и будет строиться наша стратегия.


    Правила торговли

    Торговать можно как отложенными, так и рыночными ордерами. Таймфрейм и время не имеют существенного значения. Основное правило: линии должны пробиваться уверенно. При торговле отложенными ордерами ставим их на максимум (минимум) пробивающей свечи или свечи, по которой сформирован столбик гистограммы. Также она должна иметь направление пробоя. Иными словами, если пробой идет вверх, то свеча должна закрыться в buy, и наоборот. 

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


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

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


    Классический сигнал на продажу:

    На иллюстрации ниже видим сигнал на продажу (в дальнейшем отмененный показаниями осциллятора) и два сигнала на покупку.


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

    На малых таймфреймах (М1-М15) очень часто после пробоя цена возвращается к линии, формируя тем самым паттерн 1-2-3, усиливающий сигнал. Поэтому, если рынок не очень активен, то для большей уверенности лучше входить на этих таймфреймах после того, как цена пройдет экстремум пробойной свечи. Конечно же, не будем забывать и о классическом варианте интерпретации дивергенций. Хотя сам осциллятор здесь не несет той смысловой нагрузки, как в других стратегиях, и используется в основном для построений, его поведение все же желательно учитывать в торговле. Индикатор АO входит в состав многих стратегий. Совместное его использование с описанным подходом может значительно поднять их эффективность.

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

    input double InpLots =0.1;           // Lots
    input int InpTakeProfit =150;        // Take Profit (in pips)
    input int InpStopLoss =60;           // StopLoss (in pips)
    input int InpTrailingStop =25;       // Trailing Stop Level (in pips)
    input int InpOffset =5;              // Distance from the price (in pips)
    input int InpDellOorder =30;         // Order removal distance (in pips)
    //---
    int ExtTimeOut=10;                   // время в секундах между торговыми операциями
    int barsCalculated=1000;
    datetime t=0;
    datetime time[];
    

    Ко всем понятным переменным Lots, TakeProfit, StopLoss и TrailingStop мы добавим еще две Offset и DellOorder. Первая из них — это дистанция, на которую будет удален наш ордер (pip) от максимума (минимума) пробойной свечи. Если ордер еще не сработал и цена двигается не в нашу сторону, уходя на расстояние DellOorder (pip), мы удаляем ордер.

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

    //+------------------------------------------------------------------+
    //| основной расчет                                                  |
    //+------------------------------------------------------------------+
     for(int bar=1; bar>0 && !IsStopped() && t!=time[0]; bar--)
     {
     int l=bar+1;
     int p1=0,p2=0;
     //+----------- Выявляем UP расхождения -------------------------------+
     if(Extremum(m_buff_ind[l+1],m_buff_ind[l],m_buff_ind[l-1])<0)
     {
     int i=l;
     int counted=LastPeak(l,bars,m_buff_ind);
     if(counted!=-1)
     {
     if(m_buff_ind[i]<m_buff_ind[counted] && high[i]>high[counted] && !d1)
     { drawLine("Buy_1",time[i],time[counted],m_buff_ind[i],m_buff_ind[counted],Red,1); d1=true;}
     //---
     if(m_buff_ind[i]>m_buff_ind[counted] && high[i]<high[counted] && !d2)
     {
     p1=ArrayMaximum(high,i-1,5);p2=ArrayMaximum(high,counted-2,5);
     drawLine("Buy_2",time[p1],time[p2],high[p1],high[p2],Red,0);d2=true;
     }
     }
     }
     //+----------- Выявляем DN расхождения -------------------------------+
     if(Extremum(m_buff_ind[l+1],m_buff_ind[l],m_buff_ind[l-1])>0)
     {
     int i=l;
     int counted=LastTrough(l,bars,m_buff_ind);
     if(counted!=-1)
     {
     if(m_buff_ind[i]>m_buff_ind[counted] && low[i]<low[counted] && !d3)
     { drawLine("Sell_1",time[i],time[counted],m_buff_ind[i],m_buff_ind[counted],Green,1);d3=true;}
     //---
     if(m_buff_ind[i]<m_buff_ind[counted] && low[i]>low[counted] && !d4)
     {
     p1=ArrayMinimum(low,i-1,5);p2=ArrayMinimum(low,counted-2,5);
     drawLine("Sell_2",time[p1],time[p2],low[p1],low[p2],Green,0);d4=true;
     }
     }
     }
     if(d1 && d2 && d3 && d4)break;
     t=time[0];
     }
    //---
     }

    Мы понимаем, что экстремум индикатора необязательно будет таковым на графике цены. Поэтому, чтобы хоть немного исправить связанные с этим искажения в построениях, мы добавили две функции ArrayMinimum и ArrayMaximum (выделены цветом). Они позволяют выявить на графике максимум и минимум цены на участке, на котором сформировался экстремум индикатора. Также чтобы исключить незначительные колебания индикатора вокруг нулевой линии и связанные с этим ложные сигналы, мы внесли изменения в формулу определения самих экстремумов. Теперь она берет в расчет только те значения индикатора, которые имеют общий потенциал (положительные или отрицательные).

    //+-- поиск экстремумов --------------------------------------------+
    int Extremum(double a,double b,double c)
     {
     if(((a-b)*(b-c)<0) && ((a>0 && b>0 && c>0) || (a<0 && b<0 && c<0)))
     {
     if(c>b && b<0) return(1); //DN экстремум
     if(c<b && b>0) return(-1);//UP экстремум
     }
     return(0);
     }
    //+------

    Чтобы определить значение линии в определенную временную координату, воспользуемся стандартной функцией ObjectGetValueByTime. Она будет использоваться у нас многократно. Для удобства выносим ее отдельно.

    //+------------------------------------------------------------------+
    //|Возвращает значение цены для указанного времени указанного объекта|
    //+------------------------------------------------------------------+
    double CSampleExpert::ValueByTime(string label,int i)
     {
     double p=0.0;
    //---
     p=ObjectGetValueByTime(0,label,time[i],0);
     return(p);
     }
    //+------------------------------------------------------------------+

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

    //+------------------------------------------------------------------+
    //| Проверить условия открытия короткой позиции                      |
    //+------------------------------------------------------------------+
    bool CSampleExpert::ShortOpened(void)
     {
     bool res=false;
    //---
     double pp1=EMPTY_VALUE,pp2=EMPTY_VALUE,pp3=EMPTY_VALUE,
     pp4=EMPTY_VALUE,pp5=EMPTY_VALUE,pp6=EMPTY_VALUE;
    //---
     if(ObjectFind(0,"Sell_2")!=-1)
     {
     pp1=ValueByTime("Sell_2",1);
     pp2=ValueByTime("Sell_2",2);
     pp3=ValueByTime("Sell_2",3);
     }
     if(ObjectFind(0,"Sell_1")!=-1)
     {
     pp4=ValueByTime("Sell_1",1);
     pp5=ValueByTime("Sell_1",2);
     pp6=ValueByTime("Sell_1",3);
     }
    //--- проверить возможность короткой позиции (SELL) 
     if((pp1!=EMPTY_VALUE && close[1]<pp1 && close[2]>pp2&&close[0]<close[1])||
     (pp4!=EMPTY_VALUE && m_ind_1>m_ind_0 && ((m_ind_1<pp4 && m_ind_2>pp5) ||(m_ind_2<pp5 && m_ind_3>pp6))))
     {
     //--- в любом случае, мы должны выйти из эксперта
     res=true;
     }
    //--- результат
     return(res);
     }
    //+------------------------------------------------------------------+
    

    Выше представлен вариант для входа в короткие позиции. Блок входа в длинные позиции выглядит аналогично.

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

    //+------------------------------------------------------------------+
    //| открыть sellstop позицию                                         |
    //+------------------------------------------------------------------+
    bool CSampleExpert::OpenSellStop(void)
     {
     bool res=false;
    //--- найдем bar с наименьшим low из ближайших
     int i=ArrayMinimum(low,0,3);
    //---
     if(ShortOpened())
     {
     double offset=InpOffset;                          // отступ от low свечи для установки ордера, в пунктах
     double limit_price=m_symbol.Bid();
     double price=low[i]-offset*m_adjusted_point;;
     double tp =price-m_take_profit;
     double sl =price+m_stop_losse;
     //--- проверить наличие средств на балансе аккаунта
     if(m_account.FreeMarginCheck(Symbol(),ORDER_TYPE_SELL_STOP,InpLots,price)<0.0)
     printf("У нас нет денег. Свободная маржа = %f",m_account.FreeMargin());
     else
     {
     //--- открываем позицию
     if(m_trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP,InpLots,limit_price,price,sl,tp))
     {res=true; printf("Position by %s to be opened",Symbol());}
     else
     {
     printf("Ошибка открытия позиции SELL STOP %s : '%s'",Symbol(),m_trade.ResultComment());
     printf("Открыть параметры: цена=%f,TP=%f",price,tp);
     }
     }
     }
    //--- результат
     return(res);
     }
    //+------------------------------------------------------------------+

    Если наш ордер не сработал и цена отдалилась от него на дистанцию, превышающую InpDellOorder, мы его удаляем.

    //+------------------------------------------------------------------+
    //| удаляем ненужные ордера                                          |
    //+------------------------------------------------------------------+
    bool CSampleExpert::DelOrder(ulong ticket,string type)
     {
     bool res=false;
     if(m_trade.OrderDelete(ticket))
     printf("Position by %s to be opened",Symbol());
     else
     {
     res=true;// устанавливаем флаг, сигнализирующий, что ордер не удален
     printf("Ошибка удаления ордера"+type+" %s : '%s'",Symbol(),m_trade.ResultComment());
     }
     return(res);
     }
    //+------------------------------------------------------------------+

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

    Результат получаем такой:




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


    Заключение

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

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

    #ИмяТип Описание 
    1Accelerator_DivИндикатор

    Индикатор, выявляющий явную и скрытую дивергенцию/конвергенцию по индикатору Accelerator

    2TestExpertЭксперт

    Эксперт для тестирования стратегии


    Прикрепленные файлы |
    MQL5.zip (10.87 KB)
    Последние комментарии | Перейти к обсуждению на форуме трейдеров (26)
    Andrey F. Zelinsky
    Andrey F. Zelinsky | 3 окт. 2017 в 08:27
    Alexander Lasygin:

    Нет вы же понимаете рассматривать ситуацию апокалипсиса было бы абсурдно.


    Я не понял вами написаное: набор несвязных слов.

    Знаки препинания в школе вас учили расставлять?

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

    Alexander Lasygin
    Alexander Lasygin | 3 окт. 2017 в 08:30

    Простите я тоже не прав. Долго жил в Штатах, а там неделя начинается (как вам известно с воскресенья) а это выходной. Не учел наш менталитет. Исправлюсь.

    Alexander Lasygin
    Alexander Lasygin | 3 окт. 2017 в 08:38
    Andrey F. Zelinsky:

    Я не понял вами написаное: набор несвязных слов.

    Знаки препинания в школе вас учили расставлять?

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


    Да попал. Уверен, что учили. Это была русскоязычная школа в штатах. Поэтому лучше пишу по ихнему. Все больше полагаюсь, в данном вопросе, на Word.

    Andrey F. Zelinsky
    Andrey F. Zelinsky | 3 окт. 2017 в 08:48
    Alexander Lasygin:
    С вопросом про сдвиг котировок -- разобрались.
    Alexander Sevastyanov
    Alexander Sevastyanov | 11 нояб. 2017 в 11:36

    Объясните пожалуйста почему в большинстве случаев дивергенцию определяют по значениям High/Low баров в то время как стандартный MACD рассчитывает по Close, RSI со стандартными значениями тоже по Close? По-моему если индикатор считает по Close, то и на графике тоже надо ориентировать на уровни/точки Close баров. В противном случае самообман получается. Сигналов конечно мы увидим больше, но вопрос в том насколько они корректны?

    Сравнение различных типов скользящих средних в торговле Сравнение различных типов скользящих средних в торговле
    Рассмотрены 7 видов скользящих средних (MA), разработана торговая стратегия по работе с ними. Выполнено тестирование и сравнение различных МА на одной торговой стратегии, дана сравнительная характеристика эффективности применения той или иной скользящей средней.
    Глубокие нейросети (Часть IV). Создание, обучение и тестирование модели нейросети Глубокие нейросети (Часть IV). Создание, обучение и тестирование модели нейросети
    В статье рассматриваются новые возможности пакета darch (v.0.12.0). Описаны результаты обучения глубокой нейросети с различными типами данных, структурой и последовательностью обучения. Проанализированы результаты.
    Использование фильтра Калмана в прогнозе направления цены Использование фильтра Калмана в прогнозе направления цены
    Для успешного трейдинга почти всегда необходимы индикаторы, призванные отделить основное ценовое движение от шумовых колебаний. В этой статье рассматривается один из перспективнейших цифровых фильтров — фильтр Калмана. Описано его построение и использование на практике.
    Нечеткая логика в торговых стратегиях Нечеткая логика в торговых стратегиях
    В статье рассматривается пример использования нечеткой логики для построения простой торговой системы, с использованием библиотеки Fuzzy. Предложены варианты улучшения системы путем сочетания нечеткой логики, генетических алгоритмов и нейронных сетей.