/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Indicator // --------------------------------------------- // Name: XZG // Description: ZigZag Implementation ... // // // Maintainer: // ------------ // Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) // ////////////////////////////////////////////////////// // // Global Properties ... #property copyright "Copyright 2023, SaherElm IT Center" #property link "https://saherelm.ir" #property version "1.00" #property description "SaherElm XZG Indicator" #property icon "../Images/SaherElm_Logo_Color.ico" #property strict // #define ShortName "XZG" // // Imports ... #include "../Libraries/x-saherelm.common.lib.mq5" // // Definitions ... enum ENUM_X_ZG_SEARCH_MODE { X_ZG_SEARCH_EXTREMUM = 0, // searching for the first extremum X_ZG_SEARCH_PEAK = 1, // searching for the next ZigZag peak X_ZG_SEARCH_VALE = -1 // searching for the next ZigZag vale }; // // Inputs ... // input group "Calculation"; input int depth = 12; // Depth input int deviation = 5; // Deviation input int backStep = 3; // Back Step input ENUM_X_PRICE zigzagUppersMode = X_PRICE_HIGH; // High Detect Mode input ENUM_X_PRICE zigzagLowersMode = X_PRICE_LOW; // Low Detect Mode // // Presentation ... input group "Presentation"; input bool showPeaks = true; // Show Peaks input bool showVales = true; // Show Vales input bool showZigZag = true; // Show ZigZag input int peakArrowCode = 159; // Peaks Arrow Code input int valeArrowCode = 159; // Vales Arrow Code // // Non Inputs ... // #define hideColorIDX 0 #define bullishColorIDX 1 #define bearishColorIDX 2 #define neuturalColorIDX 3 // #define bullishState 1 #define neuturalState 0 #define bearishState -1 // #define emptyValue 0.0 // // Buffers ... // #property indicator_chart_window // #property indicator_buffers 7 #property indicator_plots 3 // // ZigZag ... // #define zigzagBufferIndex 0 #define zigzagBufferPlotIndex 0 double zigzagBuffer[]; // #property indicator_label1 "XZG" #property indicator_type1 DRAW_SECTION #property indicator_color1 clrYellow #property indicator_width1 2 // // Peaks ... // #define peakBufferIndex 1 #define peakBufferPlotIndex 1 double peakBuffer[]; // #property indicator_label2 "PEAK" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrMagenta #property indicator_width2 3 // // Vales ... // #define valeBufferIndex 2 #define valeBufferPlotIndex 2 double valeBuffer[]; // #property indicator_label3 "VALE" #property indicator_type3 DRAW_ARROW #property indicator_color3 clrAqua #property indicator_width3 3 // // Data Buffers ... // #define mLastBufferIndex 2 // #define highsBufferIndex mLastBufferIndex + 1 double highsBuffer[]; // #define highsTimeBufferIndex mLastBufferIndex + 2 double highsTimeBuffer[]; // #define lowsBufferIndex mLastBufferIndex + 3 double lowsBuffer[]; // #define lowsTimeBufferIndex mLastBufferIndex + 4 double lowsTimeBuffer[]; // // Variables, Properties and etc ... // int mRecalc = 3; // Number of last extremes for recalculation // int limit; // int maxLength; // int firstBarIndex; // double zigzagUppers[]; double zigzagLowers[]; int zigzagLowersCount = 0; int zigzagUppersCount = 0; // double lastZigZagPeak = 0; datetime lastZigZagPeakTime = NULL; // double lastZigZagVale = 0; datetime lastZigZagValeTime = NULL; // // Event Handlers ... /** * Initialize Indicator ... * * @return ( int ) */ int OnInit() { // bool has = false; // // Validate Inputs ... has = ValidateInputs(); if (!has) { return INIT_PARAMETERS_INCORRECT; } // // because in some cases we may have more than one input for // calculation and we must prevent any calculation // untill we pass the biggest input length, here we get max Input length // and then wait until pass it ... maxLength = ExtractMaxLengthOfInputs(); // // Initial Requirements ... // // Define Index Buffers ... DefineBuffers(); // // Set Indicator ShortName ... SetIndicatorName(); // // Init Succeed ... return INIT_SUCCEEDED; // } /** * De Initialize Indicator ... * * @param reason: Integer, De Initialization Reason ... */ void OnDeinit(const int reason) { // // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function // REASON_REMOVE 1 Program removed from a chart // REASON_RECOMPILE 2 Program recompiled // REASON_CHARTCHANGE 3 A symbol or a chart period is changed // REASON_CHARTCLOSE 4 Chart closed // REASON_PARAMETERS 5 Inputs changed by a user // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings // REASON_TEMPLATE 7 Another chart template applied // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value // REASON_CLOSE 9 Terminal closed } /** * Calculate Bars ... * * @param rates_total: Integer, Total Bars on Chart ... * @param prev_calculated: Integer, Total Calculated Bars on Charts ... * @param time: DateTime Array, History of Open Time ... * @param open: Double Array, History of Open Prices ... * @param high: Double Array, History of High Prices ... * @param low: Double Array, History of Low Prices ... * @param close: Double Array, History of Close Prices ... * @param tick_volume: Long, History of Tick Volumes on Bar ... * @param volume: Long, History of Trade Volumes ... * @param spread: Double, History of Spread Price ... * * @return ( int ) */ int OnCalculate( const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[] // ) { // // Do ZigZag Calculations Before ArraySet As Series ... int calculatedZigZags = CalculateZigZag( 0, prev_calculated, rates_total, open, high, close, low, tick_volume // ); if (calculatedZigZags != rates_total) { return prev_calculated; } // // Checking PrevCalculated ... if (prev_calculated == rates_total) { return rates_total; } // // Calculating Limit ... limit = prev_calculated == 0 ? rates_total - 1 : rates_total - prev_calculated; if (limit <= 0) { limit = rates_total - 1; } // // Main Loop ... for (int i = limit; i < rates_total - 1 && !IsStopped(); i++) { // // Calculate Peaks and Vales ... CalculatePeaksAndVales( i, prev_calculated, rates_total, // open, high, close, low, tick_volume // ); } // return rates_total; } // // Custom Functions ... /** * Set Indicator Short Name and also we can define Buffers Labels ... */ void SetIndicatorName() { // IndicatorSetInteger(INDICATOR_DIGITS, _Digits); IndicatorSetString(INDICATOR_SHORTNAME, ShortName); } /** * Validate Input Args for Initialization ... * * @return ( bool ) */ bool ValidateInputs() { // bool result = false; // result = // // Inputs ... depth > 0 && deviation > 0 && backStep > 0 && // // Validate Upper Mode Detection ... IsXValid(zigzagUppersMode) && // // Validate Lower Mode Detection ... IsXValid(zigzagLowersMode) // ; // return result; } /** * Extract Max Length of Inputs ... * * @return ( int ) */ int ExtractMaxLengthOfInputs() { // int result = 0; // return result; } /** * Define Required Buffers ... */ void DefineBuffers() { // // Plot Buffers ... // // ZigZag ... // ENUM_DRAW_TYPE zigzagDrawType = showZigZag ? DRAW_SECTION : DRAW_NONE; // SetIndexBuffer(zigzagBufferIndex, zigzagBuffer, INDICATOR_DATA); // PlotIndexSetInteger(zigzagBufferPlotIndex, PLOT_SHOW_DATA, showZigZag); PlotIndexSetInteger(zigzagBufferPlotIndex, PLOT_DRAW_TYPE, zigzagDrawType); PlotIndexSetInteger(zigzagBufferPlotIndex, PLOT_DRAW_BEGIN, 0); PlotIndexSetDouble(zigzagBufferPlotIndex, PLOT_EMPTY_VALUE, 0); // // PEAK ... // ENUM_DRAW_TYPE peaksDrawType = showPeaks ? DRAW_ARROW : DRAW_NONE; // SetIndexBuffer(peakBufferIndex, peakBuffer, INDICATOR_DATA); // PlotIndexSetInteger(peakBufferPlotIndex, PLOT_SHOW_DATA, showPeaks); PlotIndexSetInteger(peakBufferPlotIndex, PLOT_DRAW_TYPE, peaksDrawType); // PlotIndexSetDouble(peakBufferPlotIndex, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(peakBufferPlotIndex, PLOT_ARROW, peakArrowCode); // // VALE ... // ENUM_DRAW_TYPE valesDrawType = showVales ? DRAW_ARROW : DRAW_NONE; // SetIndexBuffer(valeBufferIndex, valeBuffer, INDICATOR_DATA); // PlotIndexSetInteger(valeBufferPlotIndex, PLOT_SHOW_DATA, showVales); PlotIndexSetInteger(valeBufferPlotIndex, PLOT_DRAW_TYPE, valesDrawType); // PlotIndexSetDouble(valeBufferPlotIndex, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(valeBufferPlotIndex, PLOT_ARROW, valeArrowCode); // // Data Buffers ... // // Highs ... SetIndexBuffer(highsBufferIndex, highsBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(highsTimeBufferIndex, highsTimeBuffer, INDICATOR_CALCULATIONS); // // Lows ... SetIndexBuffer(lowsBufferIndex, lowsBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(lowsTimeBufferIndex, lowsTimeBuffer, INDICATOR_CALCULATIONS); // } /** * Calculate Values ... * * @param barIndex: int, Specified Bar Index ... * @param prevCalculated: int, Provides Previous Calculated Bars ... * @param ratesTotal: int, Provides All Availabled Bars ... * @param open: double Collection, Provides Open Prices Time Series ... * @param high: double Collection, Provides High Prices Time Series ... * @param close: double Collection, Provides Close Prices Time Series ... * @param low: double Collection, Provides Low Prices Time Series ... * @param tickVolume: long Collection, Provides Ticks Volume Prices Time Series ... * * @return ( int ) */ int CalculateZigZag( int barIndex, const int prevCalculated, const int ratesTotal, const double &open[], const double &high[], const double &close[], const double &low[], const long &tickVolume[] // ) { // int result = 0; // // Define Variables ... int i = 0; // int start = 0; int extreme_counter = 0; int extreme_search = X_ZG_SEARCH_EXTREMUM; int shift = 0; int back = 0; int last_high_pos = 0; int last_low_pos = 0; // double val = 0; double res = 0; double curlow = 0; double curhigh = 0; double last_high = 0; double last_low = 0; // // Initializing ... if (prevCalculated == 0) { // ArrayInitialize(lowsBuffer, 0); ArrayInitialize(highsBuffer, 0); ArrayInitialize(zigzagBuffer, 0); ArrayInitialize(lowsTimeBuffer, 0); ArrayInitialize(highsTimeBuffer, 0); // start = depth; } // // Preparing ZigZag Calculation High and Lows ... // if (zigzagLowersCount != zigzagUppersCount || zigzagLowersCount != ratesTotal) { // // Filling Upper ... zigzagUppersCount = FillPrice( zigzagUppersMode, zigzagUppers, open, high, close, low // ); // // Filling Lower ... zigzagLowersCount = FillPrice( zigzagLowersMode, zigzagLowers, open, high, close, low // ); } // // Already Calculated Before ... if (prevCalculated > 0) { // i = ratesTotal - 1; // // Searching for the third X_ZG_SEARCH_EXTREMUM from the last UnComplete Bar ... while (extreme_counter < mRecalc && i > ratesTotal - 100) { // res = zigzagBuffer[i]; if (res != 0) { extreme_counter++; } // i--; } // i++; start = i; // // What type of X_ZG_SEARCH_EXTREMUM we Search for ... if (lowsBuffer[i] != 0) { // curlow = lowsBuffer[i]; extreme_search = X_ZG_SEARCH_PEAK; } else { // curhigh = highsBuffer[i]; extreme_search = X_ZG_SEARCH_VALE; } // // Clear Values ... for (i = start + 1; i < ratesTotal && !IsStopped(); i++) { // lowsBuffer[i] = 0; highsBuffer[i] = 0; zigzagBuffer[i] = 0; lowsTimeBuffer[i] = 0; highsTimeBuffer[i] = 0; } } // // Search for High and Low Extremes ... for (shift = start; shift < ratesTotal && !IsStopped(); shift++) { // // Low ... val = zigzagLowers[Lowest(zigzagLowers, depth, shift)]; if (val == last_low) { val = 0; } else { // last_low = val; // if ((zigzagLowers[shift] - val) > deviation * _Point) { val = 0; } else { // for (back = 1; back <= backStep; back++) { // res = lowsBuffer[shift - back]; if ((res != 0) && (res > val)) { // lowsBuffer[shift - back] = 0; lowsTimeBuffer[shift - back] = 0; } } } } // if (zigzagLowers[shift] == val) { // lowsBuffer[shift] = val; lowsTimeBuffer[shift] = GetZigZagBarTime(shift); } else { // lowsBuffer[shift] = 0; lowsTimeBuffer[shift] = 0; } // // High ... val = zigzagUppers[Highest(zigzagUppers, depth, shift)]; if (val == last_high) { val = 0; } else { // last_high = val; // if ((val - zigzagUppers[shift]) > deviation * _Point) { val = 0; } else { // for (back = 1; back <= backStep; back++) { // res = highsBuffer[shift - back]; if ((res != 0) && (res < val)) { // highsBuffer[shift - back] = 0; highsTimeBuffer[shift - back] = 0; } } } } // if (zigzagUppers[shift] == val) { // highsBuffer[shift] = val; highsTimeBuffer[shift] = GetZigZagBarTime(shift); } else { highsBuffer[shift] = 0; highsTimeBuffer[shift] = 0; } } // // Set Last Value ... if (extreme_search == 0) // Undefined Values ... { // last_low = 0; last_high = 0; } else { // last_low = curlow; last_high = curhigh; } // // Final Selection of Extreme Points for ZigZag ... for (shift = start; shift < ratesTotal && !IsStopped(); shift++) { // res = 0; // switch (extreme_search) { // case X_ZG_SEARCH_EXTREMUM: // if (last_low == 0 && last_high == 0) { // if (highsBuffer[shift] != 0) { // last_high_pos = shift; last_high = zigzagUppers[shift]; // extreme_search = X_ZG_SEARCH_VALE; // zigzagBuffer[shift] = last_high; // res = 1; } // if (lowsBuffer[shift] != 0) { // last_low_pos = shift; last_low = zigzagLowers[shift]; // extreme_search = X_ZG_SEARCH_PEAK; // zigzagBuffer[shift] = last_low; // res = 1; } } break; // case X_ZG_SEARCH_PEAK: // if (lowsBuffer[shift] != 0 && lowsBuffer[shift] < last_low && highsBuffer[shift] == 0) { // zigzagBuffer[last_low_pos] = 0; // last_low_pos = shift; last_low = lowsBuffer[shift]; // zigzagBuffer[shift] = last_low; // res = 1; } // if (highsBuffer[shift] != 0 && lowsBuffer[shift] == 0) { // last_high_pos = shift; last_high = highsBuffer[shift]; // zigzagBuffer[shift] = last_high; // extreme_search = X_ZG_SEARCH_VALE; // res = 1; } break; // case X_ZG_SEARCH_VALE: // if (highsBuffer[shift] != 0 && highsBuffer[shift] > last_high && lowsBuffer[shift] == 0) { // zigzagBuffer[last_high_pos] = 0; // last_high_pos = shift; last_high = highsBuffer[shift]; // zigzagBuffer[shift] = last_high; } // if (lowsBuffer[shift] != 0 && highsBuffer[shift] == 0) { // last_low_pos = shift; last_low = lowsBuffer[shift]; // zigzagBuffer[shift] = last_low; // extreme_search = X_ZG_SEARCH_PEAK; } break; } } // result = ratesTotal; // return result; } /** * Calculate Peaks and Vales ... * * @param barIndex: Integer, Represent Current Bar ... * @param prevCalculated: Integer, Represent Previous Calculated Bars ... * @param ratesTotal: Integer, Represents All Available Bars ... * @param open: Double Array, History of Open Prices ... * @param high: Double Array, History of High Prices ... * @param close: Double Array, History of Close Prices ... * @param low: Double Array, History of Low Prices ... * @param tickVolume: Long, History of Tick Volumes on Bar ... */ void CalculatePeaksAndVales( int barIndex, const int prevCalculated, const int ratesTotal, const double &open[], const double &high[], const double &close[], const double &low[], const long &tickVolume[] // ) { // double iZigZagValue = zigzagBuffer[barIndex]; // // Peak ... double iHighValue = highsBuffer[barIndex]; double iHighTimeDouble = highsTimeBuffer[barIndex]; datetime iHighTime = (datetime)((int)iHighTimeDouble); if (iHighValue != 0 && iHighValue != EMPTY_VALUE && iZigZagValue == iHighValue) { // peakBuffer[barIndex] = iHighValue; // lastZigZagPeak = iHighValue; lastZigZagPeakTime = iHighTime; } else { // double iValue = emptyValue; if ( lastZigZagPeak != emptyValue && lastZigZagPeak != EMPTY_VALUE && IsSpecifiedValid(lastZigZagPeakTime)) { iValue = lastZigZagPeak; } // peakBuffer[barIndex] = iValue; } // // Vale ... double iLowValue = lowsBuffer[barIndex]; double iLowTimeDouble = lowsTimeBuffer[barIndex]; datetime iLowTime = (datetime)((int)iLowTimeDouble); if (iLowValue != 0 && iLowValue != EMPTY_VALUE && iZigZagValue == iLowValue) { // valeBuffer[barIndex] = iLowValue; // lastZigZagVale = iLowValue; lastZigZagValeTime = iLowTime; } else { // double iValue = emptyValue; if ( lastZigZagVale != emptyValue && lastZigZagVale != EMPTY_VALUE && IsSpecifiedValid(lastZigZagValeTime)) { iValue = lastZigZagVale; } // valeBuffer[barIndex] = iValue; } } // // Get Specified Bar Time as Double ... double GetZigZagBarTime(int barIndex) { // if (barIndex <= 0) { barIndex = 0; } // datetime time = iTime(_Symbol, _Period, barIndex); // double result = (double)((int)time); // return result; } // // Filling Required Prices Based on MODE ... int FillPrice( ENUM_X_PRICE mode, double &dest[], const double &open[], const double &high[], const double &close[], const double &low[] // ) { // int result = 0; // SpecifiedClean(dest); // if (!IsXValid(mode)) { return result; } // switch (mode) { // case X_PRICE_HIGH: // result = ArrayCopy( dest, high // ); break; // case X_PRICE_LOW: // result = ArrayCopy( dest, low // ); break; // case X_PRICE_OPEN: // result = ArrayCopy( dest, open // ); break; // case X_PRICE_CLOSE: // result = ArrayCopy( dest, close // ); break; // default: // result = ArrayCopy( dest, close // ); break; } // return result; } // // Search for the index of the highest bar ... int Highest( const double &mArray[], const int mDepth, const int mStart // ) { // int result = 0; // if (mStart < 0) { return result; } // double max = mArray[mStart]; result = mStart; // // Start searching ... for (int i = mStart - 1; i > mStart - mDepth && i >= 0; i--) { // if (mArray[i] > max) { // result = i; max = mArray[i]; } } // // Return index of the highest bar return result; } // // Search for the index of the lowest bar ... int Lowest( const double &mArray[], const int mDepth, const int mStart // ) { // int result = 0; // if (mStart < 0) { return result; } // double min = mArray[mStart]; result = mStart; // // Start searching ... for (int i = mStart - 1; i > mStart - mDepth && i >= 0; i--) { // if (mArray[i] < min) { // result = i; min = mArray[i]; } } // // Return index of the lowest bar return result; }