diff --git a/Classes/x-saherelm.x-cobject.class.mq5 b/Classes/x-saherelm.x-cobject.class.mq5 index 28356ad0..cfe78766 100644 --- a/Classes/x-saherelm.x-cobject.class.mq5 +++ b/Classes/x-saherelm.x-cobject.class.mq5 @@ -1279,7 +1279,7 @@ class XCBoxObject : public XCBaseObject double lower, datetime from, datetime to, - datetime at, + datetime at = NULL, string prefix = NULL // ) { diff --git a/Experts/TestRegresser.mq5 b/Experts/TestRegresser.mq5 index e0ae56fb..0a6a63a4 100644 --- a/Experts/TestRegresser.mq5 +++ b/Experts/TestRegresser.mq5 @@ -9,7 +9,9 @@ // // Imports ... +#include "../Classes/x-saherelm.x-cobject.class.mq5" #include "../Libraries/x-saherelm.common.lib.mq5" +#include "../Libraries/x-saherelm.x-poi.lib.mq5" #include // @@ -17,13 +19,11 @@ // // Calculation ... -input int zigzagDepth = 12; // Depth -input int zigzagDeviation = 5; // Deviation -input int zigzagBackStep = 3; // Back Step - -// -input int mLoopbackLength = 200; -input int mExtendFutureLength = 100; // Bars to extend trendlines into the future +input int zigzagDepth = 12; // Depth +input int zigzagDeviation = 5; // Deviation +input int zigzagBackStep = 3; // Back Step +input int mLoopbackLength = 200; // Loopback Length for Calculation +input int mRequiredPivots = 50; // Required Peaks for Calculation (0 > use loopback ) // // Variables ... @@ -53,6 +53,38 @@ double mZigZagVales[]; double mZigZagValeIndexes[]; datetime mZigZagValeTimes[]; +// +datetime mToTime; +datetime mFromTime; + +// +// Predictions ... + +// +double mPeakTo; +double mValeTo; +double mPeakFrom; +double mValeFrom; + +// +ENUM_X_DIRECTION mUpperTrendDir; +ENUM_X_DIRECTION mLowerTrendDir; + +// +// Support and Resistances ... + +// +XBoxZone mSupportBox; +double mMinorSupport; +double mMajorSupport; +XCBoxObject *mSupportObj; + +// +XBoxZone mResistanceBox; +double mMinorResistance; +double mMajorResistance; +XCBoxObject *mResistanceObj; + //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ @@ -65,14 +97,6 @@ int OnInit() _Period // ); - // - double xData[]; - double yData[]; - int xDataCount = CollectData( - xData, - yData // - ); - // // Initialize ZigZag Handler ... mZigZagHandler = iCustom( @@ -112,14 +136,56 @@ void OnTick() } // - int readedZigZagsCount = ReadZigZagValues(); + int readedZigZagsCount = 0; + bool readZigZagsBasedOnPivots = mRequiredPivots > 0; + + // + // Reading ZigZag Values Based on Loopback Length ... + if (!readZigZagsBasedOnPivots) + { + readedZigZagsCount = ReadZigZagValues(); + } + // + // Reading ZigZag Pivots based on required Pivots ... + else + { + // + readedZigZagsCount = ReadingZigZagPivots( + 0, // BarIndex ... + mRequiredPivots, // Required Pivots ... + 1500 // Max Allowed Loopback Length ... + ); + } + + // + // Validate Reading Succeed ... if (readedZigZagsCount <= 0) { return; } + + // + // Try to Detect ZigZag Peaks and Vales ... ProcessZigZagsPeaksAndVales(); - CalculateZigZagLinearRegressionParameters(); - DrawZigZagPredictedTrendLines(); + + // + // Try to Calculate ZigZag Supports and Resistances ... + ProcessZigZagsSupportsAnsResistances(); + + // + // Try to Calculate Peaks and Vales Linear Regression Based + // Slope and Intercept (s) and also Detect From and To Times then + // Predict Peaks and Vales Values for them then ... + // Prepare Support and Resistance Boxes based on Calculated Support + // and Resistances ... + if (!CalculateZigZagLinearRegressionParameters()) + { + return; + } + + // + // Draw Peaks and Vales Based Trend Lines ... + DrawAnalysis(); // // Process ZigZag Values ... @@ -129,63 +195,6 @@ void OnTick() } //+------------------------------------------------------------------+ -// -int CollectData( - double &xData[], - double &yData[], - int barIndex = 0 // -) -{ - // - int result = false; - - // - SpecifiedClean(xData); - SpecifiedClean(yData); - - // - barIndex = NormalizeInt(barIndex, 0); - - // - int start = barIndex; - int end = start + mLoopbackLength; - for (int i = start; i < end; i++) - { - // - double iY = GetAppliedPrice( - _Symbol, - _Period, - i // - ); - - // - datetime iTime = iTime( - _Symbol, - _Period, - i // - ); - double iX = i + 1; // (double)TimeToSeconds(iTime); - - // - Add( - iX, - xData // - ); - - // - Add( - iY, - yData // - ); - } - - // - result = ArraySize(xData); - - // - return result; -} - // int ReadZigZagValues(int barIndex = 0) { @@ -212,14 +221,13 @@ int ReadZigZagValues(int barIndex = 0) ArraySetAsSeries(mZigZagBuffer, true); // - return result; -} + if (result <= 0) + { + return result; + } -// -void ProcessZigZagsPeaksAndVales() -{ // - // Here we Parse ZigZag Values to Detect Peak or Vale ... + // Filling ZigZag Pivots ... // // Clean Buffers ... @@ -295,6 +303,160 @@ void ProcessZigZagsPeaksAndVales() iBar.Clean(); } + // + return result; +} + +// +int ReadingZigZagPivots( + int barIndex = 0, + int requiredPivots = 15, + int maxAllowedLoopback = 1500 // +) +{ + // + int result = 0; + + // + // Cleanup Resources ... + SpecifiedClean(mZigZagPivotTimes); + SpecifiedClean(mZigZagPivotPrices); + SpecifiedClean(mZigZagPivotIsPeaks); + SpecifiedClean(mZigZagPivotIndexes); + + // + // Normalize ... + barIndex = NormalizeInt(barIndex, 0); + requiredPivots = NormalizeInt(requiredPivots, 2); + maxAllowedLoopback = NormalizeInt(maxAllowedLoopback, 100); + + // + XOHCL iBar; + bool has = false; + double iZigZag = 0; + bool isPeak = false; + int idx = barIndex - 1; + double tmpZigZagBuffer[]; + int maxAllowed = barIndex + maxAllowedLoopback; + while (idx < maxAllowed && + ArraySize(mZigZagPivotPrices) < requiredPivots) + { + // + idx++; + + // + // Initializing Bar ... + has = iBar.Init( + _Symbol, + _Period, + idx // + ); + if (!has) + { + continue; + } + + // + // Reading ZigZag Value ... + if (CopyBuffer(mZigZagHandler, MAIN_LINE, idx, 1, tmpZigZagBuffer) < 1) + { + // + iBar.Clean(); + continue; + } + + // + // Read ZigZag Value and Validate it ... + iZigZag = tmpZigZagBuffer[0]; + has = NotEmptyZero(iZigZag); + if (!has) + { + // + iBar.Clean(); + SpecifiedClean(tmpZigZagBuffer); + + // + continue; + } + + // + isPeak = iZigZag == iBar.high; + + // + // Validate ZigZag Value for Ignore Zero ... + if (iBar.Index() == barIndex) + { + // + iBar.Clean(); + SpecifiedClean(tmpZigZagBuffer); + + // + continue; + } + + // + // Filling Pivots ... + + // + Add( + iZigZag, + mZigZagPivotPrices // + ); + + // + Add( + isPeak, + mZigZagPivotIsPeaks // + ); + + // + Add( + iBar.time, + mZigZagPivotTimes // + ); + + // + Add( + (double)iBar.Index(), + mZigZagPivotIndexes // + ); + + // + // Cleanup Resources ... + iBar.Clean(); + SpecifiedClean(tmpZigZagBuffer); + + // + // Check Can Break Loop ... + has = idx >= maxAllowed || + ArraySize(mZigZagPivotPrices) == requiredPivots; + if (has) + { + break; + } + } + + // + // Cleanup Resources ... + SpecifiedClean(tmpZigZagBuffer); + + // + result = ArraySize(mZigZagPivotPrices); + + // + return result; +} + +// +void ProcessZigZagsPeaksAndVales() +{ + // + // Here we Parse ZigZag Values to Detect Peak or Vale ... + + // + int count = 0; + bool has = false; + // // Extract mZigZag Peaks and Vales ... SpecifiedClean(mZigZagPeaks); @@ -311,7 +473,9 @@ void ProcessZigZagsPeaksAndVales() } // + double iZigZag = 0; double iZigZagIDX = -1; + bool isIZigZagPeak = false; datetime iZigZagTime = NULL; for (int i = 0; i < count; i++) { @@ -364,15 +528,126 @@ void ProcessZigZagsPeaksAndVales() ); } } - - // - // TODO: - // - [] Add Support for Reading Specified Number of Swings instead of Specified Loopback ... } // -void CalculateZigZagLinearRegressionParameters() +void ProcessZigZagsSupportsAnsResistances() { + // + int count = 0; + bool has = false; + double iValue = EMPTY_VALUE; + double tmpValue = EMPTY_VALUE; + + // + // Support ... + // Supports Calculated based on Vales ... + + // + tmpValue = EMPTY_VALUE; + mMinorSupport = EMPTY_VALUE; + mMajorSupport = EMPTY_VALUE; + count = ArraySize(mZigZagVales); + has = IsValidSize(count); + if (has) + { + // + for (int i = 0; i < count; i++) + { + // + // Reading Indexed Value ... + iValue = mZigZagVales[i]; + + // + // Check Can Change Major Support ... + has = !NotEmpty(mMajorSupport) || + mMajorSupport > iValue; + if (has) + { + // + tmpValue = mMajorSupport; + mMajorSupport = iValue; + } + else + { + // + // Check Minor Support ... + has = !NotEmpty(tmpValue) || + tmpValue > iValue; + if (has) + { + tmpValue = iValue; + } + } + } + + // + has = NotEmpty(tmpValue); + if (has) + { + mMinorSupport = tmpValue; + } + } + + // + // Resistance ... + // Resistances Calculated based on Peaks ... + + // + tmpValue = EMPTY_VALUE; + mMinorResistance = EMPTY_VALUE; + mMajorResistance = EMPTY_VALUE; + count = ArraySize(mZigZagPeaks); + has = IsValidSize(count); + if (has) + { + // + for (int i = 0; i < count; i++) + { + // + // Reading Indexed Value ... + iValue = mZigZagPeaks[i]; + + // + // Check Can Change Major Resistance ... + has = !NotEmpty(mMajorResistance) || + mMajorResistance < iValue; + if (has) + { + // + tmpValue = mMajorResistance; + mMajorResistance = iValue; + } + else + { + // + // Check Minor Resistance ... + has = !NotEmpty(tmpValue) || + tmpValue < iValue; + if (has) + { + tmpValue = iValue; + } + } + } + + // + has = NotEmpty(tmpValue); + if (has) + { + mMinorResistance = tmpValue; + } + } +} + +// +bool CalculateZigZagLinearRegressionParameters() +{ + // + bool result = false; + mUpperTrendDir = X_DIRECTION_NONE; + mLowerTrendDir = X_DIRECTION_NONE; + // // Peaks ... CalculateSlopeAndIntercept( @@ -390,74 +665,180 @@ void CalculateZigZagLinearRegressionParameters() mZigZagValeIndexes, mZigZagVales // ); -} - -// -void DrawZigZagPredictedTrendLines() -{ - // - // This Variables are Same in Peaks and Vales ... // - // TODO: - // - [] Add Support for Extending Lines betwen Oldest and Youngest Pivots ... - // - [] Add Support for Styling Lines ... + result = + (NotEmpty(mPeakSlope) && + NotEmpty(mPeakIntercept)) || + (NotEmpty(mValeSlope) && + NotEmpty(mValeIntercept)); + if (!result) + { + return result; + } // - // FromTime ... - datetime fromTime = iTime( - _Symbol, - _Period, - mLoopbackLength - 1 // + // Detect Oldest and Youngest Time of Pivots ... + + // + int idx = -1; + bool has = false; + + // + // Oldest ... + has = FindOldestIndex( + idx, + mZigZagPivotTimes // ); - int fromIDX = iBarShift( + if (has) + { + mFromTime = mZigZagPivotTimes[idx]; + } + + // + // Youngest ... + has = FindYoungesttIndex( + idx, + mZigZagPivotTimes // + ); + if (has) + { + mToTime = mZigZagPivotTimes[idx]; + } + + // + // Validate Extracted Times ... + result = + IsSpecifiedValid(mToTime) && + IsSpecifiedValid(mFromTime); + if (!result) + { + return result; + } + + // + // Detect Extracted Times Bar Indexes ... + + // + int mToIDX = iBarShift( _Symbol, _Period, - fromTime // + mToTime // + ); + int mFromIDX = iBarShift( + _Symbol, + _Period, + mFromTime // ); // - // To Time ... - // this time Calculated using From Time and mExtendFutureLength ... - datetime toTime = fromTime + (mExtendFutureLength * PeriodSeconds(_Period)); - int toIDX = iBarShift( - _Symbol, - _Period, - toTime // - ); + // Validate Detected Bar Indexes ... + result = + IsValidIndex(mToIDX) && + IsValidIndex(mFromIDX); + if (!result) + { + return result; + } // - // Predict Values ... + // Calculate Predictions ... // // Peak ... - double mPeakTo = PredictValue( + mPeakTo = PredictValue( mPeakSlope, mPeakIntercept, - toIDX // + mToIDX // ); - double mPeakFrom = PredictValue( + mPeakFrom = PredictValue( mPeakSlope, mPeakIntercept, - fromIDX // + mFromIDX // ); // // Vale ... - double mValeTo = PredictValue( + mValeTo = PredictValue( mValeSlope, mValeIntercept, - toIDX // + mToIDX // ); - double mValeFrom = PredictValue( + mValeFrom = PredictValue( mValeSlope, mValeIntercept, - fromIDX // + mFromIDX // ); // - // Draw Predicted Lines ... + mUpperTrendDir = + mPeakFrom < mPeakTo + ? X_DIRECTION_BULLISH + : mPeakFrom > mPeakTo + ? X_DIRECTION_BEARISH + : X_DIRECTION_NONE; + mLowerTrendDir = + mValeFrom < mValeTo + ? X_DIRECTION_BULLISH + : mValeFrom > mValeTo + ? X_DIRECTION_BEARISH + : X_DIRECTION_NONE; + // + // Prepare Support and Resistance Boxes ... + + // + // Support ... + + // + mSupportBox.Clean(); + has = NotEmptyZero(mMinorSupport) && + NotEmptyZero(mMajorSupport); + if (has) + { + // + mSupportBox.to = mToTime; + mSupportBox.from = mFromTime; + mSupportBox.symbol = _Symbol; + mSupportBox.period = _Period; + mSupportBox.type = "Support"; + mSupportBox.upper = mMinorSupport; + mSupportBox.lower = mMajorSupport; + mSupportBox.dir = X_DIRECTION_BULLISH; + } + + // + // Resistance ... + + // + mResistanceBox.Clean(); + has = NotEmptyZero(mMinorResistance) && + NotEmptyZero(mMajorResistance); + if (has) + { + // + mResistanceBox.to = mToTime; + mResistanceBox.from = mFromTime; + mResistanceBox.symbol = _Symbol; + mResistanceBox.period = _Period; + mResistanceBox.type = "Resistance"; + mResistanceBox.upper = mMajorResistance; + mResistanceBox.lower = mMinorResistance; + mResistanceBox.dir = X_DIRECTION_BEARISH; + } + + // + result = + mSupportBox.IsValid() || + mResistanceBox.IsValid(); + + // + return result; +} + +// +void DrawAnalysis() +{ // bool isCreated = false; @@ -481,9 +862,9 @@ void DrawZigZagPredictedTrendLines() ChartID(), "UpperTrend", 0, - fromTime, + mFromTime, mPeakFrom, - toTime, + mToTime, mPeakTo // ); if (isCreated) @@ -491,10 +872,18 @@ void DrawZigZagPredictedTrendLines() // // Apply Styles ... + // + color upperColor = + IsSpecifiedBullish(mUpperTrendDir) + ? clrAqua + : IsSpecifiedBearish(mUpperTrendDir) + ? clrMagenta + : clrGray; // Neutural ... + // mUpperTrend.Width(2); mUpperTrend.RayRight(true); - mUpperTrend.Color(clrAqua); + mUpperTrend.Color(upperColor); } // @@ -517,9 +906,9 @@ void DrawZigZagPredictedTrendLines() ChartID(), "LowerTrend", 0, - fromTime, + mFromTime, mValeFrom, - toTime, + mToTime, mValeTo // ); if (isCreated) @@ -527,11 +916,193 @@ void DrawZigZagPredictedTrendLines() // // Apply Styles ... + // + color lowerColor = + IsSpecifiedBullish(mLowerTrendDir) + ? clrAqua + : IsSpecifiedBearish(mLowerTrendDir) + ? clrMagenta + : clrGray; // Neutural ... + // mLowerTrend.Width(2); mLowerTrend.RayRight(true); - mLowerTrend.Color(clrMagenta); + mLowerTrend.Color(lowerColor); + } + + // + // Support ... + + // + // Cleanup Support Object ... + if (mSupportObj != NULL) + { + // + mSupportObj.Delete(); + mSupportObj.Detach(); + delete mSupportObj; + ZeroMemory(mSupportObj); + } + + // + isCreated = mSupportBox.IsValid(); + if (isCreated) + { + // + datetime to = TimeCurrent(); + mSupportObj = new XCBoxObject(); + isCreated = mSupportObj.Create( + ChartID(), + 0, + "Support", + mSupportBox.upper, + mSupportBox.lower, + mSupportBox.from, + to // + ); + if (isCreated) + { + // + // Apply Style ... + mSupportObj.BoxWidth(2); + mSupportObj.BoxColor(clrAqua); + } + } + + // + // Resistance ... + + // + // Cleanup Resistance Object ... + if (mResistanceObj != NULL) + { + // + mResistanceObj.Delete(); + mResistanceObj.Detach(); + delete mResistanceObj; + ZeroMemory(mResistanceObj); + } + + // + isCreated = mResistanceBox.IsValid(); + if (isCreated) + { + // + datetime to = TimeCurrent(); + mResistanceObj = new XCBoxObject(); + isCreated = mResistanceObj.Create( + ChartID(), + 0, + "Resistance", + mResistanceBox.upper, + mResistanceBox.lower, + mResistanceBox.from, + to // + ); + if (isCreated) + { + // + // Apply Style ... + mResistanceObj.BoxWidth(2); + mResistanceObj.BoxColor(clrMagenta); + } } } +// +int FindOldestIndex( + int &index, + datetime ×[] // +) +{ + // + bool result = false; + + // + // Prepare ... + index = -1; + + // + // Validate ... + if (!HasChild(times)) + { + return result; + } + + // + bool has = false; + datetime tmpTime = NULL; + for (int i = 0; i < ArraySize(times); i++) + { + // + has = + !IsSpecifiedValid(tmpTime) + ? true + : tmpTime > times[i]; + if (has) + { + // + index = i; + tmpTime = times[i]; + } + } + + // + // Validate result ... + result = + IsValidIndex(index) && + IsSpecifiedValid(tmpTime); + + // + return result; +} + +// +int FindYoungesttIndex( + int &index, + datetime ×[] // +) +{ + // + bool result = false; + + // + // Prepare ... + index = -1; + + // + // Validate ... + if (!HasChild(times)) + { + return result; + } + + // + bool has = false; + datetime tmpTime = NULL; + for (int i = 0; i < ArraySize(times); i++) + { + // + has = + !IsSpecifiedValid(tmpTime) + ? true + : tmpTime < times[i]; + if (has) + { + // + index = i; + tmpTime = times[i]; + } + } + + // + // Validate result ... + result = + IsValidIndex(index) && + IsSpecifiedValid(tmpTime); + + // + return result; +} + // \ No newline at end of file