diff --git a/Experts/x-zigzag.analyser.ea.mq5 b/Experts/x-zigzag.analyser.ea.mq5 new file mode 100644 index 00000000..c66d88eb --- /dev/null +++ b/Experts/x-zigzag.analyser.ea.mq5 @@ -0,0 +1,383 @@ +//+------------------------------------------------------------------+ +//| ZigZag Analyzer.mq5| +//| Copyright 2025, MetaQuotes Ltd.| +//| https://www.mql5.com/en/users/lynnchris| +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, MetaQuotes Ltd." +#property link "https://www.mql5.com/en/users/lynnchris" +#property version "1.0" +#property strict + +// +#include "../Libraries/x-saherelm.common.lib.mq5" + +// Input parameters +input ENUM_TIMEFRAMES InpTimeFrame = PERIOD_CURRENT; // Timeframe to analyze +input int ZZ_Depth = 12; // ZigZag depth +input int ZZ_Deviation = 5; // ZigZag deviation +input int ZZ_Backstep = 3; // ZigZag backstep +input int LookBackBars = 500; // Bars to search for pivots +input int ExtendFutureBars = 100; // Bars to extend trendlines into the future + +// Global indicator handle for ZigZag +int zzHandle; + +// Arrays for ZigZag data and pivot storage +double zzBuffer[]; +bool pivotIsPeak[]; +double pivotPrices[]; +datetime pivotTimes[]; + +// Variable to detect new bars +datetime lastBarTime = 0; + +//+------------------------------------------------------------------+ +//| Initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + // + zzHandle = iCustom( + _Symbol, + InpTimeFrame, + "x-saherelm.xzg", + "", + ZZ_Depth, + ZZ_Deviation, + ZZ_Backstep, + X_PRICE_HIGH, + X_PRICE_LOW, + "", + false, + false, + true, + 159, + 159 // + ); + if (zzHandle == INVALID_HANDLE) + { + Print("Error creating ZigZag handle"); + return (INIT_FAILED); + } + return (INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + ObjectDelete(0, "Downtrend_HighLine"); + ObjectDelete(0, "Downtrend_LowLine"); + ObjectDelete(0, "Major_Resistance"); + ObjectDelete(0, "Major_Support"); + ObjectDelete(0, "Minor_Resistance"); + ObjectDelete(0, "Minor_Support"); + IndicatorRelease(zzHandle); +} + +//+------------------------------------------------------------------+ +//| Tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + datetime currentBarTime = iTime(_Symbol, InpTimeFrame, 0); + if (currentBarTime == lastBarTime) + return; + lastBarTime = currentBarTime; + + // Remove previous objects + ObjectDelete(0, "Downtrend_HighLine"); + ObjectDelete(0, "Downtrend_LowLine"); + ObjectDelete(0, "Major_Resistance"); + ObjectDelete(0, "Major_Support"); + ObjectDelete(0, "Minor_Resistance"); + ObjectDelete(0, "Minor_Support"); + + if (CopyBuffer(zzHandle, 0, 0, LookBackBars, zzBuffer) <= 0) + { + Print("Failed to copy ZigZag data"); + return; + } + ArraySetAsSeries(zzBuffer, true); + + DrawZigZagTrendlines(); + DrawSupportResistance(); +} + +//+------------------------------------------------------------------+ +//| Draw ZigZag-based Trendlines | +//+------------------------------------------------------------------+ +void DrawZigZagTrendlines() +{ + double highPrices[10], lowPrices[10]; + datetime highTimes[10], lowTimes[10]; + int highCount = 0, lowCount = 0; + + // Extract swing points from the ZigZag buffer + for (int i = 0; i < LookBackBars - 1; i++) + { + if (zzBuffer[i] != 0) + { + if (iHigh(_Symbol, InpTimeFrame, i) == zzBuffer[i] && highCount < 10) + { + highPrices[highCount] = zzBuffer[i]; + highTimes[highCount] = iTime(_Symbol, InpTimeFrame, i); + highCount++; + } + else if (iLow(_Symbol, InpTimeFrame, i) == zzBuffer[i] && lowCount < 10) + { + lowPrices[lowCount] = zzBuffer[i]; + lowTimes[lowCount] = iTime(_Symbol, InpTimeFrame, i); + lowCount++; + } + } + } + + // Exclude the most recent swing if possible + int usedHighCount = (highCount >= 4) ? highCount - 1 : highCount; + int usedLowCount = (lowCount >= 4) ? lowCount - 1 : lowCount; + + double mHigh = 0, bHigh = 0, mLow = 0, bLow = 0; + bool validHigh = false, validLow = false; + + // Regression for highs + if (usedHighCount >= 3) + { + double sumT = 0, sumP = 0, sumTP = 0, sumT2 = 0; + for (int i = 0; i < usedHighCount; i++) + { + double t = (double)highTimes[i]; + double p = highPrices[i]; + sumT += t; + sumP += p; + sumTP += t * p; + sumT2 += t * t; + } + int N = usedHighCount; + double denominator = N * sumT2 - sumT * sumT; + if (denominator != 0) + { + mHigh = (N * sumTP - sumT * sumP) / denominator; + bHigh = (sumP - mHigh * sumT) / N; + } + else + bHigh = sumP / N; + validHigh = true; + } + + // Regression for lows + if (usedLowCount >= 3) + { + double sumT = 0, sumP = 0, sumTP = 0, sumT2 = 0; + for (int i = 0; i < usedLowCount; i++) + { + double t = (double)lowTimes[i]; + double p = lowPrices[i]; + sumT += t; + sumP += p; + sumTP += t * p; + sumT2 += t * t; + } + int N = usedLowCount; + double denominator = N * sumT2 - sumT * sumT; + if (denominator != 0) + { + mLow = (N * sumTP - sumT * sumP) / denominator; + bLow = (sumP - mLow * sumT) / N; + } + else + bLow = sumP / N; + validLow = true; + } + + // Define time limits for trendlines + datetime pastTime = iTime(_Symbol, InpTimeFrame, LookBackBars - 1); + datetime futureTime = lastBarTime + ExtendFutureBars * PeriodSeconds(); + + // Draw trendlines if both regressions are valid + if (validHigh && validLow) + { + // When slopes have the same sign, use average slope + if (mHigh * mLow > 0) + { + double mParallel = (mHigh + mLow) / 2.0; + double bHighParallel = highPrices[0] - mParallel * (double)highTimes[0]; + double bLowParallel = lowPrices[0] - mParallel * (double)lowTimes[0]; + + datetime highStartTime = pastTime; + double highStartPrice = mParallel * (double)highStartTime + bHighParallel; + double highEndPrice = mParallel * (double)futureTime + bHighParallel; + if (!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) + Print("Failed to create High Trendline"); + else + { + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); + } + + datetime lowStartTime = pastTime; + double lowStartPrice = mParallel * (double)lowStartTime + bLowParallel; + double lowEndPrice = mParallel * (double)futureTime + bLowParallel; + if (!ObjectCreate(0, "Downtrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) + Print("Failed to create Low Trendline"); + else + { + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_COLOR, clrGreen); + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_RAY_LEFT, true); + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_RAY_RIGHT, true); + } + } + else + { + datetime highStartTime = pastTime; + double highStartPrice = mHigh * (double)highStartTime + bHigh; + double highEndPrice = mHigh * (double)futureTime + bHigh; + if (!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) + Print("Failed to create High Trendline"); + else + { + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); + } + + datetime lowStartTime = pastTime; + double lowStartPrice = mLow * (double)lowStartTime + bLow; + double lowEndPrice = mLow * (double)futureTime + bLow; + if (!ObjectCreate(0, "Downtrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) + Print("Failed to create Low Trendline"); + else + { + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_COLOR, clrGreen); + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_RAY_LEFT, true); + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_RAY_RIGHT, true); + } + } + } + else + { + if (validHigh) + { + datetime highStartTime = pastTime; + double highStartPrice = mHigh * (double)highStartTime + bHigh; + double highEndPrice = mHigh * (double)futureTime + bHigh; + if (!ObjectCreate(0, "Downtrend_HighLine", OBJ_TREND, 0, highStartTime, highStartPrice, futureTime, highEndPrice)) + Print("Failed to create High Trendline"); + else + { + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_COLOR, clrRed); + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_LEFT, true); + ObjectSetInteger(0, "Downtrend_HighLine", OBJPROP_RAY_RIGHT, true); + } + } + if (validLow) + { + datetime lowStartTime = pastTime; + double lowStartPrice = mLow * (double)lowStartTime + bLow; + double lowEndPrice = mLow * (double)futureTime + bLow; + if (!ObjectCreate(0, "Downtrend_LowLine", OBJ_TREND, 0, lowStartTime, lowStartPrice, futureTime, lowEndPrice)) + Print("Failed to create Low Trendline"); + else + { + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_COLOR, clrGreen); + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_RAY_LEFT, true); + ObjectSetInteger(0, "Downtrend_LowLine", OBJPROP_RAY_RIGHT, true); + } + } + } +} + +//+------------------------------------------------------------------+ +//| Draw Support and Resistance Levels | +//+------------------------------------------------------------------+ +void DrawSupportResistance() +{ + double confirmedHighs[10], confirmedLows[10]; + int confHighCount = 0, confLowCount = 0; + + for (int i = 0; i < LookBackBars - 1; i++) + { + if (zzBuffer[i] != 0) + { + if (iHigh(_Symbol, InpTimeFrame, i) == zzBuffer[i] && confHighCount < 10) + { + confirmedHighs[confHighCount] = zzBuffer[i]; + confHighCount++; + } + else if (iLow(_Symbol, InpTimeFrame, i) == zzBuffer[i] && confLowCount < 10) + { + confirmedLows[confLowCount] = zzBuffer[i]; + confLowCount++; + } + } + } + + int usedHighCount = (confHighCount >= 4) ? confHighCount - 1 : confHighCount; + int usedLowCount = (confLowCount >= 4) ? confLowCount - 1 : confLowCount; + + double majorResistance = -1e9, majorSupport = 1e9; + double minorResistance = -1e9, minorSupport = 1e9; + double tempHigh = -1e9, tempLow = -1e9; + for (int i = 0; i < usedHighCount; i++) + { + if (confirmedHighs[i] > majorResistance) + { + tempHigh = majorResistance; + majorResistance = confirmedHighs[i]; + } + else if (confirmedHighs[i] > tempHigh) + { + tempHigh = confirmedHighs[i]; + } + } + if (tempHigh > -1e9) + minorResistance = tempHigh; + for (int i = 0; i < usedLowCount; i++) + { + if (confirmedLows[i] < majorSupport) + { + tempLow = majorSupport; + majorSupport = confirmedLows[i]; + } + else if (confirmedLows[i] < tempLow) + { + tempLow = confirmedLows[i]; + } + } + if (tempLow < 1e9) + minorSupport = tempLow; + + if (usedHighCount > 0) + { + if (!ObjectCreate(0, "Major_Resistance", OBJ_HLINE, 0, 0, majorResistance)) + Print("Failed to create Major Resistance"); + else + ObjectSetInteger(0, "Major_Resistance", OBJPROP_COLOR, clrMagenta); + + if (minorResistance > -1e9 && minorResistance < majorResistance) + { + if (!ObjectCreate(0, "Minor_Resistance", OBJ_HLINE, 0, 0, minorResistance)) + Print("Failed to create Minor Resistance"); + else + ObjectSetInteger(0, "Minor_Resistance", OBJPROP_COLOR, clrFuchsia); + } + } + if (usedLowCount > 0) + { + if (!ObjectCreate(0, "Major_Support", OBJ_HLINE, 0, 0, majorSupport)) + Print("Failed to create Major Support"); + else + ObjectSetInteger(0, "Major_Support", OBJPROP_COLOR, clrAqua); + + if (minorSupport < 1e9 && minorSupport > majorSupport) + { + if (!ObjectCreate(0, "Minor_Support", OBJ_HLINE, 0, 0, minorSupport)) + Print("Failed to create Minor Support"); + else + ObjectSetInteger(0, "Minor_Support", OBJPROP_COLOR, clrBlue); + } + } +} +//+------------------------------------------------------------------+ diff --git a/Indicators/x-saherelm.xfima.mq5 b/Indicators/x-saherelm.xfima.mq5 index 6dcbae32..d13d3ff6 100644 --- a/Indicators/x-saherelm.xfima.mq5 +++ b/Indicators/x-saherelm.xfima.mq5 @@ -99,6 +99,7 @@ input bool showMAFast = true; // Show MA Fast input bool showMASlow = true; // Show MA Slow input bool showMABand = true; // Show MA Band input bool showZigZag = true; // Show ZigZag +input bool showZigZagPV = true; // Show ZigZag PV input bool showFiboZone = true; // Show Fibo Zone // @@ -133,8 +134,8 @@ input int valeArrowCode = 159; // Vales Arrow Code #property indicator_chart_window // -#property indicator_buffers 29 -#property indicator_plots 11 +#property indicator_buffers 31 +#property indicator_plots 13 // // Plot Buffers ... @@ -277,39 +278,67 @@ double zigzagBuffer[]; #property indicator_color9 clrYellow #property indicator_width9 2 +// +// ZG Peak ... + +// +#define zigzagPeakBufferIndex 12 +#define zigzagPeakBufferPlotIndex 9 +double zigzagPeakBuffer[]; + +// +#property indicator_label10 "XZG Peak" +#property indicator_type10 DRAW_ARROW +#property indicator_color10 clrYellow +#property indicator_width10 3 + +// +// ZG Vale ... + +// +#define zigzagValeBufferIndex 13 +#define zigzagValeBufferPlotIndex 10 +double zigzagValeBuffer[]; + +// +#property indicator_label11 "XZG Peak" +#property indicator_type11 DRAW_ARROW +#property indicator_color11 clrDarkOrange +#property indicator_width11 3 + // // MA Upper Boundary ... // -#define maUpperBufferIndex 12 -#define maUpperBufferPlotIndex 9 +#define maUpperBufferIndex 14 +#define maUpperBufferPlotIndex 11 double maUpperBuffer[]; // -#property indicator_label10 "UpperMA" -#property indicator_type10 DRAW_LINE -#property indicator_color10 clrAqua -#property indicator_width10 3 +#property indicator_label12 "UpperMA" +#property indicator_type12 DRAW_LINE +#property indicator_color12 clrAqua +#property indicator_width12 3 // // MA Lower Boundary ... // -#define maLowerBufferIndex 13 -#define maLowerBufferPlotIndex 10 +#define maLowerBufferIndex 15 +#define maLowerBufferPlotIndex 12 double maLowerBuffer[]; // -#property indicator_label11 "LowerMA" -#property indicator_type11 DRAW_LINE -#property indicator_color11 clrMagenta -#property indicator_width11 3 +#property indicator_label13 "LowerMA" +#property indicator_type13 DRAW_LINE +#property indicator_color13 clrMagenta +#property indicator_width13 3 // // Data Buffers ... // -#define mLastBufferIndex 13 +#define mLastBufferIndex 15 // // MA ... @@ -446,6 +475,14 @@ int zigzagLowersCount = 0; int zigzagUppersCount = 0; int zigzagRecalc = 3; // Number of last extremes for recalculation +// +double lastZigZagPeak = 0; +datetime lastZigZagPeakTime = NULL; + +// +double lastZigZagVale = 0; +datetime lastZigZagValeTime = NULL; + // // Event Handlers ... @@ -585,6 +622,32 @@ int OnCalculate( return prev_calculated; } + // + // 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++) + { + // + Print("Calculating Bar Index: ", i); + + // + // Calculate Peaks and Vales ... + CalculateZigZagPV( + i, + prev_calculated, + rates_total // + ); + } + // // Prepare Buffers ... ArraySetAsSeries(time, true); @@ -921,6 +984,38 @@ void DefineBuffers() PlotIndexSetInteger(zigzagBufferPlotIndex, PLOT_DRAW_BEGIN, 0); PlotIndexSetDouble(zigzagBufferPlotIndex, PLOT_EMPTY_VALUE, 0); + // + // ZigZag PV ... + + // + ENUM_DRAW_TYPE zigzagPVDrawType = showZigZagPV ? DRAW_ARROW : DRAW_NONE; + + // + // ZG Peak ... + + // + SetIndexBuffer(zigzagPeakBufferIndex, zigzagPeakBuffer, INDICATOR_DATA); + + // + PlotIndexSetInteger(zigzagPeakBufferPlotIndex, PLOT_SHOW_DATA, showZigZagPV); + PlotIndexSetInteger(zigzagPeakBufferPlotIndex, PLOT_DRAW_TYPE, zigzagPVDrawType); + + PlotIndexSetInteger(zigzagPeakBufferPlotIndex, PLOT_DRAW_BEGIN, 0); + PlotIndexSetDouble(zigzagPeakBufferPlotIndex, PLOT_EMPTY_VALUE, 0); + + // + // ZG Vale ... + + // + SetIndexBuffer(zigzagValeBufferIndex, zigzagValeBuffer, INDICATOR_DATA); + + // + PlotIndexSetInteger(zigzagValeBufferPlotIndex, PLOT_SHOW_DATA, showZigZagPV); + PlotIndexSetInteger(zigzagValeBufferPlotIndex, PLOT_DRAW_TYPE, zigzagPVDrawType); + + PlotIndexSetInteger(zigzagValeBufferPlotIndex, PLOT_DRAW_BEGIN, 0); + PlotIndexSetDouble(zigzagValeBufferPlotIndex, PLOT_EMPTY_VALUE, 0); + // // MA Band ... @@ -1912,6 +2007,94 @@ int CalculateZigZag( 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 CalculateZigZagPV( + int barIndex, + const int prevCalculated, + const int ratesTotal // +) +{ + // + double iZigZagValue = zigzagBuffer[barIndex]; + + // + // Print("Calculate Bar Index: ", barIndex, ", ZG: ", iZigZagValue, ", ", prevCalculated, ", ", ratesTotal); + + // + // 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; + } + + // + // ChartRedraw(); +} + // void CalculateCycleRanges( int barIndex, diff --git a/Indicators/x-saherelm.xzg.mq5 b/Indicators/x-saherelm.xzg.mq5 new file mode 100644 index 00000000..a31407e0 --- /dev/null +++ b/Indicators/x-saherelm.xzg.mq5 @@ -0,0 +1,1110 @@ +/////////////////////////////////////////////////////// +// +// 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 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; +} diff --git a/XFIMAEA/Classes/x-saherelm.xfima.x-poi.detector.class.mq5 b/XFIMAEA/Classes/x-saherelm.xfima.x-poi.detector.class.mq5 index b91890b6..c37dd641 100644 --- a/XFIMAEA/Classes/x-saherelm.xfima.x-poi.detector.class.mq5 +++ b/XFIMAEA/Classes/x-saherelm.xfima.x-poi.detector.class.mq5 @@ -246,6 +246,42 @@ class XCXFIMAPOIDetector : public XCBase return result; } + // + bool AnalyseMarketUsingZigZag( + XOHCL &bar, + ENUM_TIMEFRAMES forPeriod, + XZigZagAnalysis &analysis // + ) + { + // + bool result = false; + + // + // Prepare ... + analysis.Clean(); + + // + // Validate ... + result = bar.IsValid(); + if (!result) + { + return result; + } + + // + // Normalize ... + if (!IsSpecifiedValid(forPeriod)) + { + forPeriod = bar.period; + } + + // + // Reading XZigZag Points ... + + // + return result; + } + // // Implementing Patrren Detector ... diff --git a/XFIMAEA/Libraries/x-saherelm.xfima.lib.mq5 b/XFIMAEA/Libraries/x-saherelm.xfima.lib.mq5 index 5f6e99ed..5149a4ad 100644 --- a/XFIMAEA/Libraries/x-saherelm.xfima.lib.mq5 +++ b/XFIMAEA/Libraries/x-saherelm.xfima.lib.mq5 @@ -411,4 +411,199 @@ struct XZigZagPoint } }; +/** + * Specified ZigZag based Market Analysis ... + **/ +struct XZigZagAnalysis +{ + // + // Props ... + string symbol; // Symbol ... + datetime time; // Time ... + ENUM_TIMEFRAMES period; // Period ... + XZigZagPoint pivots[]; // ZigZag Pivots ... + + // + ENUM_X_DIRECTION dir; // Direction ... + + // + // Constructor ... + XZigZagAnalysis() + { + Clean(); + } + + // + // Tools ... + + /** + * Cleanup Model ... + */ + void Clean() + { + // + time = NULL; + symbol = NULL; + period = NULL; + dir = X_DIRECTION_NONE; + SpecifiedClean(pivots); + + // + ZeroMemory(this); + } + + /** + * Validate Model ... + * + * @return ( bool ) + */ + bool IsValid() + { + // + bool result = false; + + // + result = + HasChild(pivots) && + HasDirection(dir) && + IsSpecifiedValid(time) && + IsSpecifiedValid(symbol) && + IsSpecifiedValid(period); + + // + return result; + } + + /** + * Calculate Analysis Age, based on Specified Time Frame ... + * + * @param forPeriod: ENUM_TIMEFRAMES, Specified Period ... + * + * @return ( int ) + */ + int GetAge( + ENUM_TIMEFRAMES forPeriod = NULL // + ) + { + // + int result = -1; + + // + // Validate ... + if (!IsValid()) + { + return result; + } + + // + // Normalize ... + if (!IsSpecifiedValid(forPeriod)) + { + forPeriod = period; + } + + // + result = GetBarIndex( + symbol, + forPeriod, + time // + ); + + // + return result; + } + + /** + * Check two item is Same or not ... + * + * @param item: XZigZagAnalysis, dest model for Checking ... + * + * @return ( bool ) + */ + bool IsSameAs(XZigZagAnalysis &item) + { + // + bool result = false; + + // + result = + IsValid() && + item.IsValid() && + time == item.time && + IsSameMarketAs(item); + + // + return result; + } + + /** + * Check to model Has Same Symbol ... + * + * @param item: XZigZagAnalysis, dest model for Checking ... + * + * @return ( bool ) + */ + bool IsSameSymbolAs(XZigZagAnalysis &item) + { + // + bool result = false; + + // + result = + IsValid() && + item.IsValid() && + symbol == item.symbol; + + // + return result; + } + + /** + * Check to model Has Same Period ... + * + * @param item: XZigZagAnalysis, dest model for Checking ... + * + * @return ( bool ) + */ + bool IsSamePeriodAs(XZigZagAnalysis &item) + { + // + bool result = false; + + // + result = + IsValid() && + item.IsValid() && + period == item.period; + + // + return result; + } + + /** + * Check to model Has Same Symbol/Period ... + * + * @param item: XZigZagAnalysis, dest model for Checking ... + * + * @return ( bool ) + */ + bool IsSameMarketAs(XZigZagAnalysis &item) + { + // + bool result = false; + + // + result = + IsValid() && + item.IsValid() && + IsSameSymbolAs(item) && + IsSamePeriodAs(item); + + // + return result; + } + + // +}; + //