//+------------------------------------------------------------------+ //| AlexSTAL_ZigZagProf.mq4 | //| AlexSTAL & PPC | //| Professional Zig-Zag | //+------------------------------------------------------------------+ #property copyright "Aleksandr Chugunov & George Tischenko" #include #property indicator_chart_window #property indicator_buffers 2 #property indicator_color1 Red #property indicator_color2 Red // Количество баров для расчета экстремумов // не может быть меньше 2 extern int ExtPeriod = 12; // Минимальное расстояние цены между соседними пиком и впадиной (иначе не регистрируется) extern int MinAmplitude = 10; // Минимальное движение цены в пунктах на нулевом баре для перерасчета индикатора extern int MinMotion = 0; // Использовать более точный алгоритм вычисления порядка формирования High/Low бара extern bool UseSmallerTFforEB = true; // Буферы индикатора double UP[], DN[]; // Буфер для кэширования внешнего бара double OB[]; // Время открытия последнего обсчитанного бара datetime LastBarTime; // Защита от докачки истории int LastBarsCount, LastBarNum; // Для оптимизации расчетов double LastBarLastHigh, LastBarLastLow; // Время первого экстремума (необходимо для алгоритма внешнего бара) datetime TimeFirstExtBar; // Статические переменные для ускорения расчетов double MP, MM; // Вспомогательная переменная bool DownloadHistory; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { if (ExtPeriod < 2) ExtPeriod = 2; if (MinAmplitude < 0) MinAmplitude = 0; MP = NormalizeDouble(MinAmplitude * Point, Digits); if (MinMotion < 1) MinMotion = 1; MM = NormalizeDouble(MinMotion * Point, Digits); //---- indicators IndicatorDigits(Digits); IndicatorBuffers(3); SetIndexBuffer(0, UP); SetIndexStyle(0, DRAW_ZIGZAG); SetIndexBuffer(1, DN); SetIndexStyle(1, DRAW_ZIGZAG); SetIndexBuffer(2, OB); LastBarsCount = 0; DownloadHistory = true; //---- return(0); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ int deinit() { //---- return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { // Блок полной инициализации при подкачке истории // ---------------------------------------------- int counted_bars = IndicatorCounted(), i; // Проверим и обнулим при необходимости индикаторные буферы if ( counted_bars != 0 ) for (i = (Bars-counted_bars-2); i >= 0; i--) { if (UP[i] != EMPTY_VALUE) UP[i] = EMPTY_VALUE; if (DN[i] != EMPTY_VALUE) DN[i] = EMPTY_VALUE; if (OB[i] != EMPTY_VALUE) OB[i] = EMPTY_VALUE; } // Бережёного... как говорится сами знаете что // При поиске бага MT4 добавил... if (LastBarsCount == 0) LastBarsCount = Bars; if (Bars < LastBarsCount) DownloadHistory = true; LastBarsCount = Bars; // IndicatorCounted() может обнулится при восстановлении связи if ( counted_bars == 0 ) DownloadHistory = true; // Защита от докачки пропущенной истории внутрь расчета if ( (counted_bars != 0) && (LastBarNum != 0) ) if ( (Bars - iBarShift(NULL, 0, LastBarTime, true)) != LastBarNum ) DownloadHistory = true; // Полная инициализация if (DownloadHistory) { ArrayInitialize(UP, EMPTY_VALUE); ArrayInitialize(DN, EMPTY_VALUE); ArrayInitialize(OB, EMPTY_VALUE); TimeFirstExtBar = 0; counted_bars = 0; LastBarTime = 0; LastBarNum = 0; DownloadHistory = false; } // Блок определения формирования нового бара (первый дошедший тик) bool NewBar = false; if (LastBarTime != Time[0]) { NewBar = true; LastBarTime = Time[0]; LastBarNum = Bars; // новый бар - обнулим переменные для оптимизации расчетов LastBarLastHigh = High[0]; LastBarLastLow = Low[0]; } // Вычислим необходимое количество баров для пересчета int BarsForRecalculation; if ( counted_bars != 0 ) { BarsForRecalculation = Bars - counted_bars - 1; // Блок оптимизации расчетов if (!NewBar) { if ( (NormalizeDouble(High[0]-LastBarLastHigh, Digits) >= MM) || (NormalizeDouble(LastBarLastLow-Low[0], Digits) >= MM) ) { LastBarLastHigh = High[0]; LastBarLastLow = Low[0]; } else { // На данном тике расчет производить не будем, так как цена изменялась внутри // нулевого бара или же её изменения не были больше минимального порога, // заданного в переменной MinMotion return(0); } } } else { BarsForRecalculation = Bars - ExtPeriod; } //====================================================== //======== основной цикл =============================== //====================================================== int LET; double H, L, Fup, Fdn; int lastUPbar, lastDNbar; double lastUP, lastDN; int m, n; // Для поиска последнего экстремума for (i = BarsForRecalculation; i >= 0; i--) { // Поиск последнего экстремума // --------------------------- lastUP = 0; lastDN = 0; lastUPbar = i; lastDNbar = i; LET = 0; m = 0; n = 0; while ( UP[lastUPbar] == EMPTY_VALUE ) { if ( lastUPbar > (Bars-ExtPeriod) ) break; lastUPbar++; } lastUP = UP[lastUPbar]; //возможно нашли последний пик while ( DN[lastDNbar] == EMPTY_VALUE) { if ( lastDNbar > (Bars-ExtPeriod) ) break; lastDNbar++; } lastDN = DN[lastDNbar]; //возможно нашли последнюю впадину if ( lastUPbar < lastDNbar) LET = 1; if ( lastUPbar > lastDNbar) LET = -1; if ( lastUPbar == lastDNbar) { //lastUPbar==lastDNbar надо узнать, какой одиночный экстремум был последним: m = lastUPbar; n = m; while ( m == n ) { m++; n++; while ( UP[m] == EMPTY_VALUE ) { if ( m > (Bars-ExtPeriod) ) break; m++; } //возможно нашли последний пик while ( DN[n] == EMPTY_VALUE ) { if ( n > (Bars-ExtPeriod) ) break; n++; } //возможно нашли последнюю впадину if ( MathMax(m, n) > (Bars-ExtPeriod) ) break; } if (m < n) LET = 1; //базовый отсчет - пик else if (m > n) LET = -1; //базовый отсчет - впадина } // если LET==0 - значит это начало отсчета или в самом начале зафиксирован внешний бар с 2 экстремумами // Конец поиска последнего экстремума // ---------------------------------- //---- рассмотрим ценовые экстремумы за расчетный период: H = High[iHighest(NULL, 0, MODE_HIGH, ExtPeriod, i)]; L = Low[iLowest(NULL, 0, MODE_LOW, ExtPeriod, i)]; Fup = High[i]; Fdn = Low[i]; //---- проанализируем ситуацию и рассмотрим возможность регистрации новых экстремумов: switch(Comb(i,H,L,Fup,Fdn)) { //---- на расчетном баре потенциальный пик (Comb) case 1 : switch(LET) { case 1 : //предыдущий экстремум тоже пик, выбираем больший: if ( lastUP < Fup ) { UP[lastUPbar] = EMPTY_VALUE; UP[i] = Fup; } break; case -1 : if ( (Fup-lastDN) > MP ) //предыдущий экстремум - впадина UP[i] = Fup; break; default : UP[i] = Fup; TimeFirstExtBar = iTime(NULL, 0, i); //0 - значит это начало расчета break; } break; //---- на расчетном баре потенциальная впадина (Comb) case -1 : switch(LET) { case 1 : if ( (lastUP-Fdn) > MP ) //предыдущий экстремум - пик DN[i] = Fdn; break; case -1 : //предыдущий экстремум тоже впадина, выбираем меньшую: if ( lastDN > Fdn ) { DN[lastDNbar] = EMPTY_VALUE; DN[i] = Fdn; } break; default : DN[i] = Fdn; TimeFirstExtBar = iTime(NULL, 0, i); //0 - значит это начало расчета break; } break; //---- на расчетном баре потенциальный пик и потенциальная впадина (Comb) case 2 : //предположительно сначала сформировался LOW потом HIGH (бычий бар) switch(LET) { case 1 : //предыдущий экстремум - пик if ( (Fup-Fdn) > MP ) { if ( (lastUP-Fdn) > MP ) { UP[i] = Fup; DN[i] = Fdn; } else { if ( lastUP < Fup ) { UP[lastUPbar] = EMPTY_VALUE; UP[i] = Fup; } } } else { if ( (lastUP-Fdn) > MP ) DN[i] = Fdn; else { if ( lastUP < Fup ) { UP[lastUPbar] = EMPTY_VALUE; UP[i] = Fup; } } } break; case -1 : //предыдущий экстремум - впадина if ( (Fup-Fdn) > MP ) { UP[i] = Fup; if ( (Fdn < lastDN) && (iTime(NULL, 0, lastDNbar) > TimeFirstExtBar) ) { DN[lastDNbar] = EMPTY_VALUE; DN[i] = Fdn; } } else { if ( (Fup-lastDN) > MP ) UP[i] = Fup; else { if ( lastDN > Fdn ) { DN[lastDNbar] = EMPTY_VALUE; DN[i] = Fdn; } } } break; default: break; } //switch LET break; //---- на расчетном баре потенциальный пик и потенциальная впадина (Comb) case -2 : //предположительно сначала сформировался HIGH потом LOW (медвежий бар) switch(LET) { case 1 : //предыдущий экстремум - пик if ( (Fup-Fdn) > MP ) { DN[i] = Fdn; if ( (lastUP < Fup) && (iTime(NULL, 0, lastUPbar) > TimeFirstExtBar) ) { UP[lastUPbar] = EMPTY_VALUE; UP[i] = Fup; } } else { if ( (lastUP-Fdn) > MP) DN[i] = Fdn; else { if ( lastUP < Fup ) { UP[lastUPbar] = EMPTY_VALUE; UP[i] = Fup; } } } break; case -1 : //предыдущий экстремум - впадина if ( (Fup-Fdn) > MP ) { if ( (Fup-lastDN) > MP ) { UP[i] = Fup; DN[i] = Fdn; } else { if (lastDN > Fdn) { DN[lastDNbar] = EMPTY_VALUE; DN[i] = Fdn; } } } else { if ( (Fup-lastDN) > MP ) UP[i] = Fup; else { if ( lastDN > Fdn) { DN[lastDNbar] = EMPTY_VALUE; DN[i] = Fdn; } } } break; default: break; } //switch LET break; default: break; } // switch (главный) } // for (главный) //---- return(0); } //+------------------------------------------------------------------+ //| функция анализа текущей ситуации | //+------------------------------------------------------------------+ int Comb(int i, double H, double L, double Fup, double Fdn) { //---- if (Fup==H && (Fdn==0 || (Fdn>0 && Fdn>L))) return(1); //на расчетном баре потенциальный пик if (Fdn==L && (Fup==0 || (Fup>0 && Fup