English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Индикатор для построения графика "трехлинейного прорыва"

Индикатор для построения графика "трехлинейного прорыва"

MetaTrader 5Примеры | 11 июля 2014, 08:42
8 872 3
Dmitriy Zabudskiy
Dmitriy Zabudskiy

Введение

В предыдущих статьях были рассмотрены графики: "Крестики - Нолики", "Каги", "Ренко". Продолжая серию статей о графиках XIX века, на этот раз поговорим о графике "трехлинейного прорыва" (Three Line Break), а точнее, о его реализации при помощи программного кода. Информации о происхождении этого графика очень мало, могу предположить, что изначально он появился в Японии. В США о нём узнали после публикации в 1994 году книги Стива Нисона «За гранью японских свечей» (Beyond Candlesticks).

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

В книге Стива Нисона «За гранью японских свечей» даётся одиннадцать правил для построения графика (стр. 185). Здесь я постараюсь их консолидировать в три.

  • Правило №1: Для построения нужно выбрать начальную цену, далее в зависимости от движения рынка вверх или вниз, построить восходящую или нисходящую линию, отметив тем самым для себя новый минимум и максимум.
  • Правило №2: Далее следим за преодолением минимума или максимума, образуя нисходящую или восходящую линию.
  • Правило №3: Для построения линии в обратном направлении от предыдущего движения нужно преодолеть минимум или максимум. При этом считается, что если одинаковых линий более одной, то минимум или максимум исчисляется от двух (если последовательных одинаковых линий две) или трёх (если последовательных одинаковых линий три или более).

Рассмотрим более подробно пример классического построения из истории (на рис. 1).

Рис.1 Пример построения графика "Трехлинейного прорыва" (EURUSD H1 27.06.2014)

Рис.1 Пример построения графика "трехлинейного прорыва" (EURUSD, H1, 27.06.2014)

На рис. 1 слева представлен график "Японские свечи", справа показан график "Трехлинейного прорыва". График строится на паре EURUSD, таймфрейм H1. Начало построения графика 27.06.2014 на цене 1.3613 (время закрытия свечи 00:00), следом свеча (01:00) закрывается на 1.3614, тем самым образуя первую восходящую линию графика "трехлинейного прорыва", следом идёт свеча "медвежьего" направления (02:00), так образуется нисходящая линия с закрытием на 1.3612 (цена закрытия ниже предыдущего минимума).

Далее "быки" опять выходят вперёд, двигая цену к отметке 1.3619 (03:00) образуя новый максимум и линию. Свече на 04:00 не удаётся пробить минимум, т.е. на построении это никак не отражается. Следом свеча на 05:00 закрывается на 1.3623, образуя теперь новый максимум (новая восходящая линия).

Теперь для построения нисходящей линии необходимо преодолеть два минимума (1.3613), но "быки" не уступают своей позиции и образуют новый максимум 1.3626 (06:00). Следом "медведи" в течение двух часов пытаются опустить рынок, но тенденция продолжается и достигается новый максимум 1.3634 (09:00), "быки" снова лидируют. Теперь для построения нисходящей линии уже требуется преодолеть три минимума (1.3626; 1.3623 и 1.3619).

Последующие три часа мы наблюдаем как "медведи" овладевают рынком, опуская его до отметки 1.3612 (12:00), тем самым образуя нисходящую линию. Но в последующие пять часов "быки", берут рынок на свои "рога" и поднимают его вверх до отметки 1.3641, преодолевая предыдущий максимум в 1.3626 и образуя новую восходящую линию на 17:00. "Медведи" в 18:00 не могут преодолеть предыдущий минимум, и оставшиеся пять часов "быки" поднимают рынок до отметки 1.3649, образуя при этом каждый час новую восходящую линию.


Основы построения графика

Теперь немного о самом индикаторе, чем же он отличается и зачем? Зачем, конечно, ответ логичный - для облегчения анализа рынка и поиска новых стратегий. А вот что в нём нового? Тут можно сказать, что довольно многое. В индикаторе предусмотрена смена цены для расчёта (охватывает все четыре стандартные цены баров). В классическом варианте предусмотрено построение только по одной из цен, а модернизированный режим позволяет использовать все четыре вида цен (open, high, low и close), что видоизменяет вид классического построения, добавляя "тени" линиям и делая из них "японские свечи", что расширяет визуальное осмысление графика.

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

Модернизированный вид построения графика представлен на рис. 2:

Рис.2 Модифицированное построение графика на основе четырёх цен

Рис.2 Модифицированное построение графика на основе четырёх цен

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

Другим нововведением стало обозначение разделителей периодов для удобства разделения сигналов. Как известно, разделители периодов включаются в настройке графика, в индикаторе они также меняются в зависимости от таймфрейма, указанного в настройках. В отличие от графиков в MetaTrader 5, где разделение производится за счёт вертикальных пунктирных линий, в индикаторе это представляется изменением цвета линии ("свечи", рис. 3):

Рис.3 Обозначение разделителей периодов на индикаторе

Рис.3 Обозначение разделителей периодов на индикаторе

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

Рис.4 Внутренняя скользящая средняя

Рис.4 Внутренняя скользящая средняя

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


Код индикатора

Алгоритм индикатора довольно прост и включает в себя три стадии: копирование данных, расчёт на основе скопированных данных и заполнение буферов индикатора (вывод на график полученных данных). Весь код разделен на функции, которые тесно переплетены либо между собой, либо с входными данными. Рассмотрим подробнее весь код.

1. Входные параметры индикатора

В преамбулу индикатора входит объявление графических построений, в индикаторе их два: основное построение, график "ABCTB" (DRAW_COLOR_CANDLES) и дополнительная скользящая средняя "LINE_TLB" (DRAW_LINE), соответственно буферов 6. Далее идут данные перечислимого типа enum (для улучшения интерфейса настроек) и сами настройки:

  • magic_numb - Магический номер имеет тип long, это уникальное число для обозначения индикатора, при желании с небольшими корректировками можно заменить на тип string;
  • time_frame - Период расчёта, тип ENUM_TIMEFRAMES, служит основным параметром (таймфрейм для работы индикатора);
  • time_redraw - Период обновления графика, тип ENUM_TIMEFRAMES, таймфрейм, за который происходит перерасчёт графика (для ускоренной перерисовки в индикатор внесено управление с кнопки на клавиатуре "R");
  • first_date_start - Начальная дата, тип datetime, основной параметр, служащий отправной точкой для копирования и построения графика;
  • chart_price - Тип цены для расчёта (0-Close, 1-Open, 2-High, 3-Low), выбор цены для расчёта классического построения (игнорируется при включённом модифицированном построении)
  • step_min_f - Минимальный шаг для нового столбца (>0, тип int), необходимый скачок в количестве пунктов для построения линии;
  • line_to_back_f - Количество линий для показа разворота (>0, тип int), классический вариант предлагает три линии для показа разворота;
  • chart_type - Вид построения (0-классический, 1-модифицированный), тип выбор из списка (переключатель вида построения);
  • chart_color_period - Изменение цвета при переходе периода (логический тип) для включения обозначения разделителей периодов другим цветом;
  • chart_synchronization - Построение графика только по полной синхронизации (логический тип, если true, то происходит полная синхронизация с отбрасыванием недостающих значений перед построением графика);
  • chart_priority_close - Уровень приоритета цены закрытия (тип выбор из списка, имеет четыре вариации и указывает приоритет цены закрытия при частичной синхронизации, при полной синхронизации игнорируется);
  • chart_priority_open - Уровень приоритета цены открытия (то же для цены открытия);
  • chart_priority_high - Уровень приоритета максимальной цены (то же для максимальной цены);
  • chart_priority_low - Уровень приоритета минимальной цены (то же для минимальной цены);
  • ma_draw - Рисовать среднюю (логический тип, если выставлено в true, то строится скользящая средняя);
  • ma_price - Тип цены построения средней, может быть одним из ENUM_APPLIED_PRICE;
  • ma_method - Тип построения, может быть одним из ENUM_MA_METHOD;
  • ma_period - Период усреднения скользящей средней;

Далее объявляются массивы буферов, переменные и структуры, используемые для расчётов.

//+------------------------------------------------------------------+
//|                                                        ABCTB.mq5 |
//|                                 "Azotskiy Aktiniy ICQ:695710750" |
//|                        "https://www.mql5.com/ru/users/Aktiniy" |
//+------------------------------------------------------------------+
// ABCTB - Auto Build Chart Three Line Break
#property copyright "Azotskiy Aktiniy ICQ:695710750"
#property link      "https://www.mql5.com/ru/users/Aktiniy"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   2
//--- plot ABCTB
#property indicator_label1  "ABCTB"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrBlue,clrRed,clrGreenYellow
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot LINE_TLB
#property indicator_label2  "LINE_TLB"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- тип цены для расчёта
enum type_price
  {
   close=0, // Close
   open=1,  // Open
   high=2,  // Hight
   low=3,   // Low
  };
//--- вид построения
enum type_build
  {
   classic=0,  // Классический
   modified=1, // Модифицированный
  };
//--- уровень приоритета
enum priority
  {
   highest_t=4, // Наивысший
   high_t=3,    // Высокий
   medium_t=2,  // Средний
   low_t=1,     // Низкий
  };
//--- input parameters
input long               magic_numb=65758473787389;                // Магический номер
input ENUM_TIMEFRAMES    time_frame=PERIOD_CURRENT;                // Период расчёта
input ENUM_TIMEFRAMES    time_redraw=PERIOD_M1;                    // Период обновления графика
input datetime           first_date_start=D'2013.03.13 00:00:00';  // Начальная дата
input type_price         chart_price=close;                        // Тип цены для расчёта (0-Close, 1-Open, 2-High, 3-Low)
input int                step_min_f=4;                             // Минимальный шаг для нового столбца (>0)
input int                line_to_back_f=3;                         // Количество линий для показа разворота (>0)
input type_build         chart_type=classic;                       // Вид построения (0-классический, 1-модифицированный)
input bool               chart_color_period=true;                  // Изменение цвета при переходе периода
input bool               chart_synchronization=true;               // Построение графика только по полной синхронизации
input priority           chart_priority_close=highest_t;           // Уровень приоритета цены закрытия
input priority           chart_priority_open=highest_t;            // Уровень приоритета цены открытия
input priority           chart_priority_high=highest_t;            // Уровень приоритета максимальной цены
input priority           chart_priority_low=highest_t;             // Уровень приоритета минимальной цены
input bool               ma_draw=true;                             // Рисовать среднюю
input ENUM_APPLIED_PRICE ma_price=PRICE_CLOSE;                     // Тип цены построения средней
input ENUM_MA_METHOD     ma_method=MODE_EMA;                       // Тип построения
input int                ma_period=14;                             // Период усреднения
//--- indicator buffers
//--- буфера графика
double         ABCTBBuffer1[];
double         ABCTBBuffer2[];
double         ABCTBBuffer3[];
double         ABCTBBuffer4[];
double         ABCTBColors[];
//--- буфер средней
double         LINE_TLBBuffer[];
//--- переменные
MqlRates rates_array[];// массив всех данных по барам для анализа
datetime date_stop;    // текущая дата
datetime date_start;   // переменная начальной даты, для расчётов
//+------------------------------------------------------------------+
//| Struct Line Price                                                |
//+------------------------------------------------------------------+
struct line_price// структура хранения информации о прошедших линиях
  {
   double            up;  // значение верхней цены
   double            down;// значение нижней цены
  };
//+------------------------------------------------------------------+
//| Struct Line Information                                          |
//+------------------------------------------------------------------+
struct line_info// структура хранения информации о линиях для общего пользования
  {
   double            up;
   double            down;
   char              type;
   datetime          time;
  };
line_info line_main_open[];  // данные о графике по ценам открытия
line_info line_main_high[];  // данные о графике по максимальным ценам
line_info line_main_low[];   // данные о графике по минимальным ценам
line_info line_main_close[]; // данные о графике по ценам закрытия
//+------------------------------------------------------------------+
//| Struct Buffer Info                                               |
//+------------------------------------------------------------------+
struct buffer_info// структура хранения данных для заполнения буфера
  {
   double            open;
   double            high;
   double            low;
   double            close;
   char              type;
   datetime          time;
  };
buffer_info data_for_buffer[];// данные для заполнения буфера модифицированного построения
datetime array_datetime[];    // массив для хранения информации о времени, по каждой линии
int time_array[3];            // массив для функции func_date_color
datetime time_variable;       // переменная для функции func_date_color
bool latch=false;             // переменная-защёлка для функции func_date_color
int handle;                   // хендл индикатора iMA
int step_min;                 // переменная минимального шага
int line_to_back;             // переменная количества линий для разворота

2. Функция OnInit 

В функции OnInit объявляются все индикаторные буферы и устанавливается индикация массивов как в таймсерии.

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

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//--- буфера для графика
   SetIndexBuffer(0,ABCTBBuffer1,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer1,true);
   SetIndexBuffer(1,ABCTBBuffer2,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer2,true);
   SetIndexBuffer(2,ABCTBBuffer3,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer3,true);
   SetIndexBuffer(3,ABCTBBuffer4,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer4,true);
   SetIndexBuffer(4,ABCTBColors,INDICATOR_COLOR_INDEX);
   ArraySetAsSeries(ABCTBColors,true);
//--- буфер для построения средней
   SetIndexBuffer(5,LINE_TLBBuffer,INDICATOR_DATA);
   ArraySetAsSeries(LINE_TLBBuffer,true);
//--- задаём какие значения не будут прорисовываться
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0); // для графика
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0); // для средней
//--- устанавливаем внешний вид индикатора
   IndicatorSetString(INDICATOR_SHORTNAME,"ABCTB "+IntegerToString(magic_numb)); // имя индикатора
//--- точность отображения
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- запрещяем показ результатов текущих значений для индикатора
   PlotIndexSetInteger(0,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(1,PLOT_SHOW_DATA,false);
//---
   handle=iMA(_Symbol,time_frame,ma_period,0,ma_method,ma_price);
   if(step_min_f<1)
     {
      step_min=1;
      Alert("Минимальный шаг для нового столбца должен быть больше нуля");
     }
   else step_min=step_min_f;
//---
   if(line_to_back_f<1)
     {
      line_to_back=1;
      Alert("Количество линий для показа разворота должно быть больше нуля");
     }
   else line_to_back=line_to_back_f;
//---
   return(INIT_SUCCEEDED);
  }

3. Функция копирования данных

Так как работа индикатора предусмотрена со всеми четырьмя видами цен, необходимо произвести копирование всех данных, включая и время. В языке MQL5 существует структура для хранения информации о времени начала сессии, ценах, объемах и спреде - MqlRates.

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

//+------------------------------------------------------------------+
//| Func All Copy                                                    |
//+------------------------------------------------------------------+
bool func_all_copy(MqlRates &result_array[],// массив ответа
                   ENUM_TIMEFRAMES period,  // таймфрейм
                   datetime data_start,     // начальная дата
                   datetime data_stop)      // конечная дата
  {
//--- объявление вспомогательных переменных
   bool x=false;       // переменная для ответа функции
   int result_copy=-1; // количество скопированных данных
//--- добавление переменных и массивов для расчёта
   static MqlRates interim_array[]; // временный динамический массив для хранения скопированных данных
   static int bars_to_copy;         // количество баров для копирования
   static int bars_copied;          // количество уже скопированных баров с начальной даты
//--- узнаём текущее количество баров на временном промежутке
   bars_to_copy=Bars(_Symbol,period,data_start,data_stop);
//--- подсчитываем количество баров которые надо скопировать
   bars_to_copy-=bars_copied;
//--- если данные копируются не в первый раз
   if(bars_copied>0)
     {
      bars_copied--;
      bars_to_copy++;
     }
//--- меняем размер приёмного массива
   ArrayResize(interim_array,bars_to_copy);
//--- копируем данные во временный массив
   result_copy=CopyRates(_Symbol,period,0,bars_to_copy,interim_array);
//--- проверяем результат копирования данных
   if(result_copy!=-1) // если копирование в промежуточный массив было успешно
     {
      ArrayCopy(result_array,interim_array,bars_copied,0,WHOLE_ARRAY); // копируем данные из временного в основной массив
      x=true;                   // присваиваем положительный ответ функции
      bars_copied+=result_copy; // увеличиваем значение скопированных данных
     }
//---
   return(x);
  }

4. Функция расчёта данных

Функция является прототипом расчёта данных для классического построения графика "Трехлинейного прорыва". Опять повторюсь, что функция только рассчитывает данные и формирует их в специальный массив типа структуры line_info, объявленный в начале кода индикатора.

В функцию так же входят две другие функции func_regrouping (перегруппировки) и func_insert (вставки), для начала разберем их:

4.1. Функция перегруппировки

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

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

//+------------------------------------------------------------------+
// Func Regrouping                                                   |
//+------------------------------------------------------------------+
void func_regrouping(line_price &input_array[],// массив для перегруппировки
                     double new_price,         // новое значение цены
                     char type)                // тип движения
  {
   int x=ArraySize(input_array);// узнаём размер перегруппировки
   for(x--; x>0; x--)           // цикл перегруппировки
     {
      input_array[x].up=input_array[x-1].up;
      input_array[x].down=input_array[x-1].down;
     }
   if(type==1)
     {
      input_array[0].up=new_price;
      input_array[0].down=input_array[1].up;
     }
   if(type==-1)
     {
      input_array[0].down=new_price;
      input_array[0].up=input_array[1].down;
     }
  }

 4.2. Функция вставки

 Функция осуществляет вставку значений в массив ответа, код прост и не требует объяснений. 

//+------------------------------------------------------------------+
// Func Insert                                                       |
//+------------------------------------------------------------------+
void func_insert(line_info &line_m[],  // массив-получатель
                 line_price &line_i[], // массив-источник
                 int index,            // вставляемый элемент массива
                 char type,            // тип получаемого столбца
                 datetime time)        // дата
  {
   line_m[index].up=line_i[0].up;
   line_m[index].down=line_i[0].down;
   line_m[index].type=type;
   line_m[index].time=time;
  }

Функция расчёта данных условно поделена на три части. Первая часть это копирование исследуемых данных в промежуточный массив, при помощи оператора-переключателя switch, копируется только исследуемая цена. Вторая часть производит предварительный прогон, для подсчёта требуемого места в массиве данных. Далее производится изменение массива данных line_main_array[], который был передан функции изначально для ответа. И третья часть заполняет уже размеренный массив данных.

//+------------------------------------------------------------------+
//| Func Build Three Line Break                                      |
//+------------------------------------------------------------------+
void func_build_three_line_break(MqlRates &input_array[],      // массив для анализа
                                 char price_type,              // вид анализируемой цены (0-Close, 1-Open, 2-High, 3-Low)
                                 int min_step,                 // минимальный шаг для образования линии
                                 int line_back,                // количество линий для разворота
                                 line_info &line_main_array[]) // массив возврата (ответа) функции
  {
//--- подсчитываем размер массива для анализа
   int array_size=ArraySize(input_array);
//--- извлекаем данные необходимые для расчёта в промежуточный массив
   double interim_array[];// промежуточный массив
   ArrayResize(interim_array,array_size);// подгоняем промежуточный массив под размер данных
   switch(price_type)
     {
      case 0: // Close
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].close;
           }
        }
      break;
      case 1: // Open
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].open;
           }
        }
      break;
      case 2: // High
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].high;
           }
        }
      break;
      case 3: // Low
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].low;
           }
        }
      break;
     }
//--- вводим переменные для хранения информации о текущем положении
   line_price passed_line[];// массив хранения информации о последних цен линий (тип структура line_price)
   ArrayResize(passed_line,line_back+1);
   int line_calc=0;// количество линий
   int line_up=0;// количество последних линий вверх
   int line_down=0;// количество последних линий вниз
   double limit_up=0;// необходимый для преодоления верхний лимит
   double limit_down=0;// необходимый для преодоления нижний лимит
/* Заполним первыми значениями информативные переменные о текущем положении */
   passed_line[0].up=interim_array[0];
   passed_line[0].down=interim_array[0];
//--- запускаем первый цикл для расчёта полученных данных для заполнения буфера от рисовки
   for(int x=0; x<array_size; x++)
     {
      if(line_calc==0)// не одна линия ещё не образованна
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// преодолена верхняя граница
           {
            func_regrouping(passed_line,interim_array[x],1);// производим перегруппировку
            line_calc++;// обновляем счётчик линий
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// преодолена нижняя граница
           {
            func_regrouping(passed_line,interim_array[x],-1);// производим перегруппировку
            line_calc++;// обновляем счётчик линий
            line_down++;
           }
        }
      if(line_up>line_down)// последняя(и) линия(и) восходящие
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[(int)MathMin(line_up,line_back-1)].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// преодолена верхняя граница
           {
            func_regrouping(passed_line,interim_array[x],1);// производим перегруппировку
            line_calc++;// обновляем счётчик линий
            line_up++;
           }
         if(interim_array[x]<limit_down)// преодолена нижняя граница
           {
            func_regrouping(passed_line,interim_array[x],-1);// производим перегруппировку
            line_calc++;// обновляем счётчик линий
            line_up=0;
            line_down++;
           }
        }
      if(line_down>line_up)// последняя(и) линия(и) нисходящие
        {
         limit_up=passed_line[(int)MathMin(line_down,line_back-1)].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>limit_up)// преодолена верхняя граница
           {
            func_regrouping(passed_line,interim_array[x],1);// производим перегруппировку
            line_calc++;// обновляем счётчик линий
            line_down=0;
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// преодолена нижняя граница
           {
            func_regrouping(passed_line,interim_array[x],-1);// производим перегруппировку
            line_calc++;// обновляем счётчик линий
            line_down++;
           }
        }
     }
   ArrayResize(line_main_array,line_calc);// меняем размер приёмного массива
//--- обнуляем переменные и заполняем начальными данными
   line_calc=0;
   line_up=0;
   line_down=0;
   passed_line[0].up=interim_array[0];
   passed_line[0].down=interim_array[0];
//--- запускаем второй цикл для заполнения буфера от рисовки
   for(int x=0; x<array_size; x++)
     {
      if(line_calc==0)// не одна линия ещё не образованна
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// преодолена верхняя граница
           {
            func_regrouping(passed_line,interim_array[x],1);// производим перегруппировку
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// обновляем счётчик линий
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// преодолена нижняя граница
           {
            func_regrouping(passed_line,interim_array[x],-1);// производим перегруппировку
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// обновляем счётчик линий
            line_down++;
           }
        }
      if(line_up>line_down)// последняя(и) линия(и) восходящие
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[(int)MathMin(line_up,line_back-1)].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// преодолена верхняя граница
           {
            func_regrouping(passed_line,interim_array[x],1);// производим перегруппировку
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// обновляем счётчик линий
            line_up++;
           }
         if(interim_array[x]<limit_down)// преодолена нижняя граница
           {
            func_regrouping(passed_line,interim_array[x],-1);// производим перегруппировку
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// обновляем счётчик линий
            line_up=0;
            line_down++;
           }
        }
      if(line_down>line_up)// последняя(и) линия(и) нисходящие
        {
         limit_up=passed_line[(int)MathMin(line_down,line_back-1)].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>limit_up)// преодолена верхняя граница
           {
            func_regrouping(passed_line,interim_array[x],1);// производим перегруппировку
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// обновляем счётчик линий
            line_down=0;
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// преодолена нижняя граница
           {
            func_regrouping(passed_line,interim_array[x],-1);// производим перегруппировку
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// обновляем счётчик линий
            line_down++;
           }
        }
     }
  }

5. Функция построения графика

Задача этой функции сходится к тому, чтобы рассчитать в зависимости от выбранного параметра построения (классический или модифицированный) график и заполнить буфера индикатора данными для показа. Как и предыдущая функция, в своём составе она имеет дополнительные три функции (цветовая, синхронизация, скользящая средняя). Разберём их подробнее.

5.1. Цветовая функция

Функция имеет всего один входящий параметр - время. Ответом служит логическая переменная, если переданная дата является границей периода, то функция вернёт true. Так как периоды зависят от выбранного таймфрейма, в функции произведено разделение по периодам условным оператором if. После того, как период был выбран, происходит проверка, наступил новый период или нет, это делается при помощи преобразования даты в структуру MqlDateTime и сравнения. Для таймфрейма до H2 включительно, изменения дня свидетельствует смене периода от H12 до D1 включительно проверяется изменение месяца, на W1 и MN проверяется смена года.

К сожалению, информацию о том, какая текущая неделя, структура MqlDateTime, не имеет. Этот вопрос решился путём создания начальной точки отсчёта в виде переменной time_variable. В дальнейшем каждый раз от этой даты отнимается количество секунд в одной неделе.

//+------------------------------------------------------------------+
// Func Date Color                                                   |
//+------------------------------------------------------------------+
bool func_date_color(datetime date_time) // входящая дата
  {
   bool x=false;// переменная для ответа
   int seconds=PeriodSeconds(time_frame);// узнаём период расчёта
   MqlDateTime date;
   TimeToStruct(date_time,date);// преобразуем данные
   if(latch==false) // проверка состояния защёлки
     {
      MqlDateTime date_0;
      date_0=date;
      date_0.hour=0;
      date_0.min=0;
      date_0.sec=0;
      int difference=date_0.day_of_week-1;
      datetime date_d=StructToTime(date_0);
      date_d=date_d-86400*difference;
      time_variable=date_d;
      latch=true;// защёлкиваем защёлку
     }
   if(seconds<=7200)// период меньше или равен H2
     {
      if(time_array[0]!=date.day)
        {
         x=true;
         time_array[0]=date.day;
        }
     }
   if(seconds>7200 && seconds<=43200)// период больше H2 но меньше или равен H12
     {
      if(time_variable>=date_time)
        {
         x=true;
         time_variable=time_variable-604800;
        }
     }
   if(seconds>43200 && seconds<=86400)// период больше H12 но меньше или равен D1
     {
      if(time_array[1]!=date.mon)
        {
         x=true;
         time_array[1]=date.mon;
        }
     }
   if(seconds>86400)// период W1 или MN
     {
      if(time_array[2]!=date.year)
        {
         x=true;
         time_array[2]=date.year;
        }
     }
   return(x);
  }

5.2. Функция синхронизации

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

Полная синхронизация проводится в 3 прохода:

  1. Подсчёт элементов массива, удовлетворяющих условию наличия данных по всем четырём видам цен,
  2. Копирование элементов в промежуточный массив, при том же условии,
  3. Копирование из промежуточного массива в переданный по параметрам массив.

Частичная синхронизация немного сложнее.

Переданный одномерный массив структур преобразуется в двумерный, где первый индекс обозначает порядок, а второй вид цены. Следом вводится одномерный массив с четырьмя элементами, в который копируются уровни приоритета цен, далее этот массив сортируется, для выявления порядка приоритета. Затем при помощи цикла for и условного оператора if, производим распределение по приоритетам. При этом если приоритеты равны, то последовательность цен такова: close, open, high, low. Как только оператор if находит первoе значение по приоритету, при помощи цикла for все нулевые данные в созданном ранее двумерном массиве заменяются на приоритетные, и т.д.

//+------------------------------------------------------------------+
// Func Synchronization                                              |
//+------------------------------------------------------------------+
void func_synchronization(buffer_info &info[],
                          bool synchronization,
                          char close,
                          char open,
                          char high,
                          char low)
  {
   if(synchronization==true)// производим полную синхронизацию
     {
      int calc=0;// переменная подсчёта
      for(int x=0; x<ArraySize(info); x++)// подсчитываем количество полных данных
        {
         if(info[x].close!=0 && info[x].high!=0 && info[x].low!=0 && info[x].open!=0)calc++;
        }
      buffer_info i_info[];    // вводим временный массив для копирования
      ArrayResize(i_info,calc);// меняем размер временного массива
      calc=0;
      for(int x=0; x<ArraySize(info); x++)// копируем данные во временный массив
        {
         if(info[x].close!=0 && info[x].high!=0 && info[x].low!=0 && info[x].open!=0)
           {
            i_info[calc]=info[x];
            calc++;
           }
        }
      ZeroMemory(info);        // очищаем приёмный массив
      ArrayResize(info,calc);  // меняем размер основного массива
      for(int x=0; x<calc; x++)// копируем данные из временного массива в основной
        {
         info[x]=i_info[x];
        }
     }
   if(synchronization==false)  // меняем нулевые значения на приоритетные
     {
      int size=ArraySize(info); // замеряем размер массива
      double buffer[][4];       // создаём временный массив для расчёта
      ArrayResize(buffer,size); // меняем размер временного массива
      for(int x=0; x<size; x++) // копируем данные во временный массив
        {
         buffer[x][0]=info[x].close;
         buffer[x][1]=info[x].open;
         buffer[x][2]=info[x].high;
         buffer[x][3]=info[x].low;
        }
      char p[4];// вводим массив для сортировки по порядку
      p[0]=close; p[1]=open; p[2]=high; p[3]=low;// присваиваем переменные для последующей сортировки
      ArraySort(p); // сортируем
      int z=0,v=0;  // инициализация часто используемых переменных
      for(int x=0; x<4; x++)// учитывая сортировку перебираем переменные и заменяем их по приоритету
        {
         if(p[x]==close)// приоритет по ценам закрытия
           {
            for(z=0; z<size; z++)
              {
               for(v=1; v<4; v++)
                 {
                  if(buffer[z][v]==0)buffer[z][v]=buffer[z][0];
                 }
              }
           }
         if(p[x]==open)// приоритет по ценам открытия
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<4; v++)
                 {
                  if(v!=1 && buffer[z][v]==0)buffer[z][v]=buffer[z][1];
                 }
              }
           }
         if(p[x]==high)// приоритет по максимальным ценам
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<4; v++)
                 {
                  if(v!=2 && buffer[z][v]==0)buffer[z][v]=buffer[z][2];
                 }
              }
           }
         if(p[x]==low)// приоритет по минимальным ценам
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<3; v++)
                 {
                  if(buffer[z][v]==0)buffer[z][v]=buffer[z][3];
                 }
              }
           }
        }
      for(int x=0; x<size; x++)// копируем данные с временного массива обратно
        {
         info[x].close=buffer[x][0];
         info[x].open=buffer[x][1];
         info[x].high=buffer[x][2];
         info[x].low=buffer[x][3];
        }
     }
  }

5.3. Функция скользящей средней

Эта самая простая функция, по хендлу индикатора, полученного в функции OnInit, производится копирование значения по переданной в параметрах функции дате, и передаётся в ответ функции.

//+------------------------------------------------------------------+
// Func MA                                                           |
//+------------------------------------------------------------------+
double func_ma(datetime date)
  {
   double x[1];
   CopyBuffer(handle,0,date,1,x);
   return(x[0]);
  }

Функция построения графика условно делится на две части: классическое построение и модифицированное. Функция имеет два входных параметра: вид цены для построения (игнорируется при модифицированном построении) и тип построения (классический или модифицированный).

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

Вторая часть (классическое построение) гораздо проще: сначала вызывается функция расчёта данных, затем заполняются буфера индикатора.

//+------------------------------------------------------------------+
//| Func Chart Build                                                 |
//+------------------------------------------------------------------+
void func_chart_build(char price, // вид цены для построения
                      char type)  // вид построения
  {
//--- Обнуляем буфера
   ZeroMemory(ABCTBBuffer1);
   ZeroMemory(ABCTBBuffer2);
   ZeroMemory(ABCTBBuffer3);
   ZeroMemory(ABCTBBuffer4);
   ZeroMemory(ABCTBColors);
   ZeroMemory(LINE_TLBBuffer);
   if(type==1)// строим модифицированное построение (по всем видам цен)
     {
      func_build_three_line_break(rates_array,0,step_min,line_to_back,line_main_close);// данные по ценам закрытия
      func_build_three_line_break(rates_array,1,step_min,line_to_back,line_main_open);// данные по ценам открытия
      func_build_three_line_break(rates_array,2,step_min,line_to_back,line_main_high);// данные по максимальным ценам
      func_build_three_line_break(rates_array,3,step_min,line_to_back,line_main_low);// данные по минимальным ценам
      //--- подсчитываем массивы данных
      int line_main_calc[4];
      line_main_calc[0]=ArraySize(line_main_close);
      line_main_calc[1]=ArraySize(line_main_open);
      line_main_calc[2]=ArraySize(line_main_high);
      line_main_calc[3]=ArraySize(line_main_low);
      //--- собираем массив дат
      int all_elements=line_main_calc[0]+line_main_calc[1]+line_main_calc[2]+line_main_calc[3];// узнаём количество всех элементов
      datetime datetime_array[];// вводим массив для копирования
      ArrayResize(datetime_array,all_elements);
      int y[4];
      ZeroMemory(y);
      for(int x=0;x<ArraySize(datetime_array);x++)// копируем в массив данные
        {
         if(x<line_main_calc[0])
           {
            datetime_array[x]=line_main_close[y[0]].time;
            y[0]++;
           }
         if(x<line_main_calc[0]+line_main_calc[1] && x>=line_main_calc[0])
           {
            datetime_array[x]=line_main_open[y[1]].time;
            y[1]++;
           }
         if(x<line_main_calc[0]+line_main_calc[1]+line_main_calc[2] && x>=line_main_calc[0]+line_main_calc[1])
           {
            datetime_array[x]=line_main_high[y[2]].time;
            y[2]++;
           }
         if(x>=line_main_calc[0]+line_main_calc[1]+line_main_calc[2])
           {
            datetime_array[x]=line_main_low[y[3]].time;
            y[3]++;
           }
        }
      ArraySort(datetime_array);// сортируем массив
      //--- производим процесс очистки массива от дублированных данных
      int good_info=1;
      for(int x=1;x<ArraySize(datetime_array);x++)// считаем количество нужной информации
        {
         if(datetime_array[x-1]!=datetime_array[x])good_info++;
        }
      ArrayResize(array_datetime,good_info);
      array_datetime[0]=datetime_array[0];// копируем первый элемент (так как он образцовый в начале сравнения)
      good_info=1;
      for(int x=1;x<ArraySize(datetime_array);x++)// заполняем новый массив нужными данными
        {
         if(datetime_array[x-1]!=datetime_array[x])
           {
            array_datetime[good_info]=datetime_array[x];
            good_info++;
           }
        }
      //--- заполняем буфер от рисовки (цветные свечи)
      int end_of_calc[4];// переменные хранения информации о последнем сравнении
      ZeroMemory(end_of_calc);
      ZeroMemory(data_for_buffer);
      ArrayResize(data_for_buffer,ArraySize(array_datetime));// меняем размер объявленного глобально массива для содержания данных пред передачи в буфер
      for(int x=0; x<ArraySize(array_datetime); x++)
        {
         data_for_buffer[x].time=array_datetime[x];
         for(int s=end_of_calc[0]; s<line_main_calc[0]; s++)
           {
            if(array_datetime[x]==line_main_close[s].time)
              {
               end_of_calc[0]=s;
               if(line_main_close[s].type==1)data_for_buffer[x].close=line_main_close[s].up;
               else data_for_buffer[x].close=line_main_close[s].down;
               break;
              }
           }
         for(int s=end_of_calc[1]; s<line_main_calc[1]; s++)
           {
            if(array_datetime[x]==line_main_open[s].time)
              {
               end_of_calc[1]=s;
               if(line_main_open[s].type==1)data_for_buffer[x].open=line_main_open[s].down;
               else data_for_buffer[x].open=line_main_open[s].up;
               break;
              }
           }
         for(int s=end_of_calc[2]; s<line_main_calc[2]; s++)
           {
            if(array_datetime[x]==line_main_high[s].time)
              {
               end_of_calc[2]=s;
               data_for_buffer[x].high=line_main_high[s].up;
               break;
              }
           }
         for(int s=end_of_calc[3]; s<line_main_calc[3]; s++)
           {
            if(array_datetime[x]==line_main_low[s].time)
              {
               end_of_calc[3]=s;
               data_for_buffer[x].low=line_main_low[s].down;
               break;
              }
           }
        }
      //--- запускаем функцию синхронизации данных
      func_synchronization(data_for_buffer,chart_synchronization,chart_priority_close,chart_priority_open,chart_priority_high,chart_priority_low);
      //--- подготовительные действия для запуска функции func_date_color
      ZeroMemory(time_array);
      time_variable=0;
      latch=false;
      //--- производим заполнение буфера от рисовки свечей
      for(int x=ArraySize(data_for_buffer)-1,z=0; x>=0; x--)
        {
         ABCTBBuffer1[z]=data_for_buffer[x].open;
         ABCTBBuffer2[z]=data_for_buffer[x].high;
         ABCTBBuffer3[z]=data_for_buffer[x].low;
         ABCTBBuffer4[z]=data_for_buffer[x].close;
         if(ABCTBBuffer1[z]<=ABCTBBuffer4[z])ABCTBColors[z]=0;
         if(ABCTBBuffer1[z]>=ABCTBBuffer4[z])ABCTBColors[z]=1;
         if(func_date_color(data_for_buffer[x].time)==true && chart_color_period==true)ABCTBColors[z]=2;
         if(ma_draw==true)LINE_TLBBuffer[z]=func_ma(data_for_buffer[x].time);
         z++;
        }
     }
   else// строим классическое построение (по одному виду цены)
     {
      func_build_three_line_break(rates_array,price,step_min,line_to_back,line_main_close);// находим данные по выбранным ценам
      ArrayResize(array_datetime,ArraySize(line_main_close));
      //--- подготовительные действия для запуска функции func_date_color
      ZeroMemory(time_array);
      time_variable=0;
      latch=false;
      //--- производим заполнение буфера от рисовки свечей
      for(int x=ArraySize(line_main_close)-1,z=0; x>=0; x--)
        {
         ABCTBBuffer1[z]=line_main_close[x].up;
         ABCTBBuffer2[z]=line_main_close[x].up;
         ABCTBBuffer3[z]=line_main_close[x].down;
         ABCTBBuffer4[z]=line_main_close[x].down;
         if(line_main_close[x].type==1)ABCTBColors[z]=0;
         else ABCTBColors[z]=1;
         if(func_date_color(line_main_close[x].time)==true && chart_color_period==true)ABCTBColors[z]=2;
         if(ma_draw==true)LINE_TLBBuffer[z]=func_ma(line_main_close[x].time);
         z++;
        }
     }
  }

6. Функция консолидации

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

//+------------------------------------------------------------------+
//| Func Consolidation                                               |
//+------------------------------------------------------------------+
void func_consolidation()
  {
//--- определение текущей даты
   date_stop=TimeCurrent();
//--- копирование данных для анализа
   func_all_copy(rates_array,time_frame,first_date_start,date_stop);
//--- основное построение графика
   func_chart_build(chart_price,chart_type);
   ChartRedraw();
  }

7. Функция кнопочного и автоматического управления построением

Данные функции предназначены для перерисовки индикатора при помощи нажатия клавиши "R" (OnChartEvent) на клавиатуре, а также автоматически через указанный промежуток времени (OnCalculate), анализируемый при помощи функции нового бара (func_new_bar) представляющей собой упрощённый вид функции, описанной в IsNewBar.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   if(func_new_bar(time_redraw)==true)
     {
      func_consolidation();
     };
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- событие нажатия клавиши на клавиатуре
   if(id==CHARTEVENT_KEYDOWN)
     {
      if(lparam==82) //--- была нажата клавиша "R"
        {
         func_consolidation();
        }
     }
  }
//+------------------------------------------------------------------+
//| Func New Bar                                                     |
//+------------------------------------------------------------------+
bool func_new_bar(ENUM_TIMEFRAMES period_time)
  {
//---
   static datetime old_times; // переменная хранения старых значений
   bool res=false;            // переменная результата анализа  
   datetime new_time[1];      // время нового бара
//---
   int copied=CopyTime(_Symbol,period_time,0,1,new_time); // скопируем время последнего бара в ячейку new_time  
//---
   if(copied>0) // все ок. данные скопированы
     {
      if(old_times!=new_time[0]) // если старое время бара не равно новому
        {
         if(old_times!=0) res=true; // если это не первый запуск, то истина = новый бар
         old_times=new_time[0];     // запоминаем время бара
        }
     }
//---
   return(res);
  }

На этом описание кода индикатора заканчивается, теперь рассмотрим, как можно его применять.


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

Начнём с основных стратегий анализа, основанных на классическом построении графика.

1. Белые и чёрные линии в качестве сигналов к покупке и продаже

Условно можно сказать о двух правилах:

  1. Правило №1: Покупай, когда есть три последовательные линии вверх, продавай когда есть три последовательные линии вниз. Три последовательные линии служат подтверждением к появлению тенденции.
  2. Правило №2: Продавай, когда образуется падение линии разворота ниже трех последовательных восходящих линей, покупай когда подъём линии разворота будет больше трёх последовательных нисходящих линей.

Разберём рис.6, на котором представлено классическое построение EURUSD H1 с начала 2013 года (сам период анализа показан на рис.5).

Рис.5 Исследуемый период EURUSD H1

Рис.5 Исследуемый период EURUSD H1

Рис.6 Классическое построение графика "Трехлинейного прорыва", EURUSD H1, начало 2013 года, цены закрытия

Рис.6 Классическое построение графика "Трехлинейного прорыва", EURUSD H1, начало 2013 года, цены закрытия

На графике (рис. 6) явно виден сигнал (правило №1) между точками 1 и 2, что служит отправной точкой на продажу. Как видно, заработок в данном случае составляет более 200 пунктов на четырёх знаках. Следующая точка 4 уже на покупку (по правилу №2). При закрытии в точке 5 прибыль составит 40 пунктов, при закрытии в точке 6 получим безубыток.

В точке 6 сигнал на продажу (правило №2), при закрытии в точке 7 получим 10 пунктов прибыли, закрытии в точке 8 получим безубыток. Точки 8 и 9 не могут рассматриваться как сигнал, так как ни первому, ни второму правилу они не удовлетворяют. В точке 10 можно покупать (правило №1), при закрытии в точке 11 получим прибыль в 20 пунктов (или безубыток в точке 12). Все числа были округлены.

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

Стоит также отметить, что после появления модели по первому или второму правилу ждут еще подтверждения разворота тенденции в виде одной линии в сторону тенденции.

2. Равноудалённый канал, линии поддержки и сопротивления

Ещё одной торговой стратегией является подключение технического анализа к графику "Трехлинейного прорыва", рассмотрим рис. 7:

 Рис.7 Равноудалённый канал и линии поддержки и сопротивления, GBPUSD H1, период с 01.03.2014 по 01.05.2014

Рис. 7 Равноудалённый канал и линии поддержки и сопротивления, GBPUSD H1, период с 01.03.2014 по 01.05.2014

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

3. Модели японских свечей

Довольно интересный результат дал график в модифицированном построении (двухлинейный прорыв) на таймфрейме M30 пара USDCAD начало 2013 года.

Отчётливо стали видны модели японских свечей, которые оправдали свои сигналы (рису. 8).

Рис.8 Модифицированный график "трёхлинейного прорыва", USDCAD M30, начало 2013, двухлинейный прорыв

Рис. 8 Модифицированный график "Трёхлинейного прорыва", USDCAD M30, начало 2013, прорыв двухлинейный

В начале графика под номером 1 показана разворотная модель "поглощения", она состоит из двух свечей: красной и предыдущей синей. После поднимающегося тренда рынок дальше опускается до цифры 2, что представляет собой односвечную разворотную модель "молот", и рынок меняет своё направление. Аналогичная ситуация с моделью под номером 3 ("Волчок"). Следующую разворотную модель "Харами" (номер 4) образует свеча под номером 4 и следующая перед ней большая восходящая. Модель под номером 6 также состоит из двух свечей (модель "Поглощение"), но в отличии от первой, она разворачивает рынок уже в другую сторону.

Из этого можно сделать вывод о том, что применение индикатора в данном виде анализа приемлемо, но имеет недостатки (сигналы появляются достаточно редко и возможна большая просадка). Стратегия требует доработки.

4. Скользящая средняя

Своеобразная модификация индикатора (добавление скользящей средней от основного графика только по построенным линиям) добавляет новые возможности для анализа.

Рассмотрим рис. 9:

Рис.9 Анализ по скользящей средней, EURUSD H4, график "трехлинейного прорыва", классическое построение, от 01.01.2014 до 01.07.2014

Рис.9 Анализ по скользящей средней, EURUSD H4, график "Трехлинейного прорыва", классическое построение, от 01.01.2014 до 01.07.2014

В верхней части рис. 9 приведено классическое построение по наибольшим ценам (high) со скользящей средней (период усреднения 90, тип цены low, сглаженное усреднение). В нижней части показано классическое построение по наименьшим ценам (low) со скользящей средней (период усреднения 90, тип цены high, сглаженное усреднение).

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


Заключение

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

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


Прикрепленные файлы |
ABCTB.mq5 (34.52 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
Oksana Berenko
Oksana Berenko | 11 сент. 2014 в 13:16

Не понимаю этот индикатор. Скрин прикрепила

Dmitriy Zabudskiy
Dmitriy Zabudskiy | 28 сент. 2014 в 18:59
cat7:

Не понимаю этот индикатор. Скрин прикрепила

Индикатор игнорирует шкалу времени. Для более точной диагностики происходящего необходимо проанализировать используемые настройки. Могу предположить что отмеченный верхний фрагмент стоит на нижнем графике в самом правом секторе, а не по середине как отмечено. Фрагмент графика который отмечен на нижней части имеет ценовой уровень в районе 1.330, а на верхней отмеченной части пик составляет примерно 1.315.
Michail Smikov
Michail Smikov | 19 окт. 2017 в 20:39
Dmitriy Zabudskiy:
Индикатор игнорирует шкалу времени. Для более точной диагностики происходящего необходимо проанализировать используемые настройки. Могу предположить что отмеченный верхний фрагмент стоит на нижнем графике в самом правом секторе, а не по середине как отмечено. Фрагмент графика который отмечен на нижней части имеет ценовой уровень в районе 1.330, а на верхней отмеченной части пик составляет примерно 1.315.

Есть ли что то подобное для МТ4?

Поставщики сигнала Johnpaul77: "Наша стратегия более трех лет дает отличный результат, с какой стати нам ее менять?" Поставщики сигнала Johnpaul77: "Наша стратегия более трех лет дает отличный результат, с какой стати нам ее менять?"
Раскроем небольшой секрет: посетители сайта MQL5.com больше всего времени проводят на странице сигнала Johnpaul77. Это лидер нашего рейтинга, на него подписаны около 900 трейдеров с $5.7 млн совокупных средств на реальных счетах. Мы взяли интервью с провайдерами этого сигнала - их оказалось четверо! Каким образом простые индонезийские геймеры стали поставщиками топового сигнала, какими инструмента теханализа они пользуются и как распределяются роли в их коллективе — читайте здесь.
Инфографика "MQL5.com Freelance: Можно ли тут заработать?" Инфографика "MQL5.com Freelance: Можно ли тут заработать?"
К четырехлетию «Фриланса» мы подготовили инфографику, которая наглядно демонстрирует итоги деятельности сервиса за все время его существования. Цифры говорят сами за себя: на данный момент выполнено более 10 000 работ общей стоимостью около $600 000, а услугами сервиса воспользовались уже 3 000 заказчиков и 300 разработчиков.
Мастер MQL5: Расширение стандартной библиотеки для установки ордеров, стопов и целей по вычисляемым ценам Мастер MQL5: Расширение стандартной библиотеки для установки ордеров, стопов и целей по вычисляемым ценам
В статье описывается расширение стандартной библиотеки MQL5, позволяющее с помощью Мастера создавать советники, размещающие ордера, стоп-лоссы и тейк-профиты по ценам, получаемым от подключенных модулей. Данный подход не накладывает дополнительных ограничений на количество модулей и не вызывает конфликтов в их совместной работе.
Как заработать, выполняя заказы трейдеров в сервисе "Фриланс" Как заработать, выполняя заказы трейдеров в сервисе "Фриланс"
MQL5 Фриланс - это онлайн-сервис, в котором разработчики за денежное вознаграждение пишут для трейдеров-заказчиков торговые приложения. Трейдеры к 2014 году прекрасно поняли: если хочешь получить готового торгового робота - идешь в MetaTrader Market, а если нужен уникальный советник, торгующий по заданной стратегии, следует заглянуть во "Фриланс". На одну заявку здесь претендуют сразу несколько опытных разработчиков, из которых трейдер выбирает наиболее подходящего и поручает ему эту задачу.