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, double lower,
datetime from, datetime from,
datetime to, datetime to,
datetime at, datetime at = NULL,
string prefix = NULL // string prefix = NULL //
) )
{ {
+698 -127
View File
@@ -9,7 +9,9 @@
// //
// Imports ... // Imports ...
#include "../Classes/x-saherelm.x-cobject.class.mq5"
#include "../Libraries/x-saherelm.common.lib.mq5" #include "../Libraries/x-saherelm.common.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.lib.mq5"
#include <ChartObjects/ChartObjectsLines.mqh> #include <ChartObjects/ChartObjectsLines.mqh>
// //
@@ -17,13 +19,11 @@
// //
// Calculation ... // Calculation ...
input int zigzagDepth = 12; // Depth input int zigzagDepth = 12; // Depth
input int zigzagDeviation = 5; // Deviation input int zigzagDeviation = 5; // Deviation
input int zigzagBackStep = 3; // Back Step 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 )
input int mLoopbackLength = 200;
input int mExtendFutureLength = 100; // Bars to extend trendlines into the future
// //
// Variables ... // Variables ...
@@ -53,6 +53,38 @@ double mZigZagVales[];
double mZigZagValeIndexes[]; double mZigZagValeIndexes[];
datetime mZigZagValeTimes[]; 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 | //| Expert initialization function |
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
@@ -65,14 +97,6 @@ int OnInit()
_Period // _Period //
); );
//
double xData[];
double yData[];
int xDataCount = CollectData(
xData,
yData //
);
// //
// Initialize ZigZag Handler ... // Initialize ZigZag Handler ...
mZigZagHandler = iCustom( 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) if (readedZigZagsCount <= 0)
{ {
return; return;
} }
//
// Try to Detect ZigZag Peaks and Vales ...
ProcessZigZagsPeaksAndVales(); 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 ... // 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) int ReadZigZagValues(int barIndex = 0)
{ {
@@ -212,14 +221,13 @@ int ReadZigZagValues(int barIndex = 0)
ArraySetAsSeries(mZigZagBuffer, true); 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 ... // Clean Buffers ...
@@ -295,6 +303,160 @@ void ProcessZigZagsPeaksAndVales()
iBar.Clean(); 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 ... // Extract mZigZag Peaks and Vales ...
SpecifiedClean(mZigZagPeaks); SpecifiedClean(mZigZagPeaks);
@@ -311,7 +473,9 @@ void ProcessZigZagsPeaksAndVales()
} }
// //
double iZigZag = 0;
double iZigZagIDX = -1; double iZigZagIDX = -1;
bool isIZigZagPeak = false;
datetime iZigZagTime = NULL; datetime iZigZagTime = NULL;
for (int i = 0; i < count; i++) 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 ... // Peaks ...
CalculateSlopeAndIntercept( CalculateSlopeAndIntercept(
@@ -390,74 +665,180 @@ void CalculateZigZagLinearRegressionParameters()
mZigZagValeIndexes, mZigZagValeIndexes,
mZigZagVales // mZigZagVales //
); );
}
//
void DrawZigZagPredictedTrendLines()
{
//
// This Variables are Same in Peaks and Vales ...
// //
// TODO: result =
// - [] Add Support for Extending Lines betwen Oldest and Youngest Pivots ... (NotEmpty(mPeakSlope) &&
// - [] Add Support for Styling Lines ... NotEmpty(mPeakIntercept)) ||
(NotEmpty(mValeSlope) &&
NotEmpty(mValeIntercept));
if (!result)
{
return result;
}
// //
// FromTime ... // Detect Oldest and Youngest Time of Pivots ...
datetime fromTime = iTime(
_Symbol, //
_Period, int idx = -1;
mLoopbackLength - 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, _Symbol,
_Period, _Period,
fromTime // mToTime //
);
int mFromIDX = iBarShift(
_Symbol,
_Period,
mFromTime //
); );
// //
// To Time ... // Validate Detected Bar Indexes ...
// this time Calculated using From Time and mExtendFutureLength ... result =
datetime toTime = fromTime + (mExtendFutureLength * PeriodSeconds(_Period)); IsValidIndex(mToIDX) &&
int toIDX = iBarShift( IsValidIndex(mFromIDX);
_Symbol, if (!result)
_Period, {
toTime // return result;
); }
// //
// Predict Values ... // Calculate Predictions ...
// //
// Peak ... // Peak ...
double mPeakTo = PredictValue( mPeakTo = PredictValue(
mPeakSlope, mPeakSlope,
mPeakIntercept, mPeakIntercept,
toIDX // mToIDX //
); );
double mPeakFrom = PredictValue( mPeakFrom = PredictValue(
mPeakSlope, mPeakSlope,
mPeakIntercept, mPeakIntercept,
fromIDX // mFromIDX //
); );
// //
// Vale ... // Vale ...
double mValeTo = PredictValue( mValeTo = PredictValue(
mValeSlope, mValeSlope,
mValeIntercept, mValeIntercept,
toIDX // mToIDX //
); );
double mValeFrom = PredictValue( mValeFrom = PredictValue(
mValeSlope, mValeSlope,
mValeIntercept, 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; bool isCreated = false;
@@ -481,9 +862,9 @@ void DrawZigZagPredictedTrendLines()
ChartID(), ChartID(),
"UpperTrend", "UpperTrend",
0, 0,
fromTime, mFromTime,
mPeakFrom, mPeakFrom,
toTime, mToTime,
mPeakTo // mPeakTo //
); );
if (isCreated) if (isCreated)
@@ -491,10 +872,18 @@ void DrawZigZagPredictedTrendLines()
// //
// Apply Styles ... // Apply Styles ...
//
color upperColor =
IsSpecifiedBullish(mUpperTrendDir)
? clrAqua
: IsSpecifiedBearish(mUpperTrendDir)
? clrMagenta
: clrGray; // Neutural ...
// //
mUpperTrend.Width(2); mUpperTrend.Width(2);
mUpperTrend.RayRight(true); mUpperTrend.RayRight(true);
mUpperTrend.Color(clrAqua); mUpperTrend.Color(upperColor);
} }
// //
@@ -517,9 +906,9 @@ void DrawZigZagPredictedTrendLines()
ChartID(), ChartID(),
"LowerTrend", "LowerTrend",
0, 0,
fromTime, mFromTime,
mValeFrom, mValeFrom,
toTime, mToTime,
mValeTo // mValeTo //
); );
if (isCreated) if (isCreated)
@@ -527,11 +916,193 @@ void DrawZigZagPredictedTrendLines()
// //
// Apply Styles ... // Apply Styles ...
//
color lowerColor =
IsSpecifiedBullish(mLowerTrendDir)
? clrAqua
: IsSpecifiedBearish(mLowerTrendDir)
? clrMagenta
: clrGray; // Neutural ...
// //
mLowerTrend.Width(2); mLowerTrend.Width(2);
mLowerTrend.RayRight(true); 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;
}
// //