complete ZigZag Analyser using Linear Regression in TestRegression Expert ...

This commit is contained in:
2025-09-22 15:39:58 +03:30
parent 0a102fb01b
commit 3d762aa81b
2 changed files with 699 additions and 128 deletions
+1 -1
View File
@@ -1279,7 +1279,7 @@ class XCBoxObject : public XCBaseObject
double lower,
datetime from,
datetime to,
datetime at,
datetime at = NULL,
string prefix = NULL //
)
{
+698 -127
View File
@@ -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 <ChartObjects/ChartObjectsLines.mqh>
//
@@ -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 &times[] //
)
{
//
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 &times[] //
)
{
//
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;
}
//