Files
MQL5Data/Classes/x-saherelm.x-market.pattern.class.mq5
T
2025-11-03 01:49:22 +03:30

4783 lines
114 KiB
Plaintext

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XCMarketPatternDetector
// Description: provides all Alerts requirements ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://www.saherelm.ir"
#property version "1.00"
#property strict
//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-bar.analyser.class.mq5"
#include "../Libraries/x-saherelm.x-pivot.analysis.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.lib.mq5"
//
// Definitions ...
//
// Implementation ...
class XCMarketPatternDetector : public XCBase
{
//
// Public ...
public:
//
// Props ...
//
// Constructor ...
XCMarketPatternDetector()
{
barAnalyser = new XCBarAnalyser();
}
//
// Deconstructor ...
~XCMarketPatternDetector()
{
//
delete barAnalyser;
ZeroMemory(barAnalyser);
}
//
// Analysers ...
/**
* Check for Price Touch Support Level ...
*
* @param bar: XOHCL, Specified Bar ...
* @param supportBar: XOHCL, reference to holds Zone Bar ...
* @param supportBox: XBoxZone, reference to holds Zone Box ...
* @param validationLength: int, Validation Length ...
* @param maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
*
* @return ( bool )
*/
bool IsSupportTouched(
XOHCL &bar,
XOHCL &supportBar,
XBoxZone &supportBox,
int validationLength = 21,
int maxAllowedLoopback = 1500 //
)
{
//
bool result = false;
//
// Prepare ...
supportBar.Clean();
supportBox.Clean();
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Check Support Exists ...
result = HasSupport(
bar,
supportBar,
validationLength,
maxAllowedLoopback //
);
if (!result)
{
//
supportBar.Clean();
return result;
}
//
// Extract Support Box ...
result = ToSupportBox(
supportBar,
supportBox //
);
//
// Retrieve Previous Bar ...
XOHCL pBar;
result =
result &&
bar.GetPreviousBar(pBar);
//
// Chekc Price of Bar ...
result =
result &&
pBar.low < supportBox.upper;
//
// Cleanup Resources ...
if (!result)
{
//
supportBar.Clean();
supportBox.Clean();
}
pBar.Clean();
//
return result;
}
/**
* Check for Price Touch Resistance Level ...
*
* @param bar: XOHCL, Specified Bar ...
* @param supportBar: XOHCL, reference to holds Zone Bar ...
* @param supportBox: XBoxZone, reference to holds Zone Box ...
* @param validationLength: int, Validation Length ...
* @param maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
*
* @return ( bool )
*/
bool IsResistanceTouched(
XOHCL &bar,
XOHCL &resistanceBar,
XBoxZone &resistanceBox,
int validationLength = 21,
int maxAllowedLoopback = 1500 //
)
{
//
bool result = false;
//
// Prepare ...
resistanceBar.Clean();
resistanceBox.Clean();
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Check Resistance Exists ...
result = HasResistance(
bar,
resistanceBar,
validationLength,
maxAllowedLoopback //
);
if (!result)
{
//
resistanceBar.Clean();
return result;
}
//
// Extract Resistance Box ...
result = ToResistanceBox(
resistanceBar,
resistanceBox //
);
//
// Retrieve Previous Bar ...
XOHCL pBar;
result =
result &&
bar.GetPreviousBar(pBar);
//
// Chekc Price of Bar ...
result =
result &&
pBar.high > resistanceBox.lower;
//
// Cleanup Resources ...
if (!result)
{
//
resistanceBar.Clean();
resistanceBox.Clean();
}
pBar.Clean();
//
return result;
}
//
// Detectors ...
/**
* Check Specified Bar is Peak or Vale ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param validationLength: int, Validation Length ...
*
* @return ( bool )
*/
bool IsPV(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
int validationLength = 7,
ENUM_X_PRICE peakPriceType = X_PRICE_HIGH, //
ENUM_X_PRICE valePriceType = X_PRICE_LOW //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Normalize ...
validationLength = NormalizeInt(validationLength, 2);
//
// Validate ...
result = bar.IsValid() &&
IsXValid(peakPriceType) &&
IsXValid(valePriceType);
if (!result)
{
return result;
}
//
int valeIndex = -1;
double vale = bar.FindLowest(
valeIndex,
validationLength,
valePriceType //
);
double barVale = bar.GetPrice(valePriceType);
bool isVale =
NotEmptyZero(vale) &&
NotEmptyZero(barVale) &&
IsValidIndex(valeIndex) &&
vale >= barVale;
//
int peakIndex = -1;
double peak = bar.FindHighest(
peakIndex,
validationLength,
peakPriceType //
);
double barPeak = bar.GetPrice(peakPriceType);
bool isPeak =
NotEmptyZero(peak) &&
NotEmptyZero(barPeak) &&
IsValidIndex(peakIndex) &&
peak <= barPeak;
//
result = (isVale && !isPeak) ||
(isPeak && !isVale);
if (result)
{
//
dir =
isVale
? X_DIRECTION_BULLISH
: isPeak
? X_DIRECTION_BEARISH
: X_DIRECTION_NONE;
}
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
//
return result;
}
/**
* Check Specified Bar is Momentum Bar or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param requiredBars: int, bar range for approve Momentum ...
* @param approvedMultiplier: double, a Multipler for approve Momentum ...
*
* @return ( bool )
*/
bool IsMomentumBar(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
int requiredBars = 3,
double approvedMultiplier = 2 //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Normalizing ...
requiredBars = NormalizeInt(requiredBars, 3, 10);
approvedMultiplier = NormalizeDouble(approvedMultiplier, 2, 5);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Calculate Requirements ...
//
XOHCL iBar;
bool has = false;
double rangeBody = 0;
int start = bar.Index() + 1;
int end = start + requiredBars;
for (int i = start; i < end; i++)
{
//
// Initialized Bar ...
has = iBar.Init(
bar.symbol,
bar.period,
i //
);
//
// Summarize Body Sizes ...
if (has)
{
rangeBody += iBar.GetBody();
}
//
iBar.Clean();
}
//
// Calculate Body Approvement ...
result = bar.GetBody() >= (approvedMultiplier * rangeBody);
if (!result)
{
return result;
}
//
// Check Direction ...
dir = bar.GetDirection();
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
iBar.Clean();
//
return result;
}
/**
* Check Specified Bar is Engulfed Bar or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
*
* @return ( bool )
*/
bool IsEngulfedBar(
XOHCL &bar,
ENUM_X_DIRECTION &dir //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Calculate Requirements ...
//
XOHCL iBar;
result = bar.GetPreviousBar(iBar);
if (!result)
{
//
iBar.Clean();
return result;
}
//
// Check Prev Bar Must Inside Bar ...
result = bar.GetUp() > iBar.GetUp() &&
bar.GetDown() < iBar.GetDown();
if (!result)
{
//
iBar.Clean();
return result;
}
//
// Check Directions ...
bool isBullish =
iBar.IsBearish() &&
bar.IsBullish();
bool isBearish =
iBar.IsBullish() &&
bar.IsBearish();
result = isBullish || isBearish;
if (!result)
{
//
iBar.Clean();
return result;
}
//
dir = bar.GetDirection();
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
iBar.Clean();
//
return result;
}
/**
* Check Specified Bar is True Gapped Bar or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param approvedPointMultiplier: double, Gap Validation Multiplier in Point ...
*
* @return ( bool )
*/
bool IsTrueGapedBar(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
double approvedPointMultiplier = 1 //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Normalizing ...
approvedPointMultiplier = NormalizeDouble(approvedPointMultiplier, 1, 100);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Calculate Requirements ...
//
XOHCL iBar;
result = bar.GetPreviousBar(iBar);
if (!result)
{
//
iBar.Clean();
return result;
}
//
// Check Directions ...
bool isBullish =
iBar.IsBullish() &&
bar.IsBullish();
bool isBearish =
iBar.IsBearish() &&
bar.IsBearish();
result = isBullish || isBearish;
if (!result)
{
//
iBar.Clean();
return result;
}
//
double point = GetPoints(bar.symbol);
double approvedGapSize = approvedPointMultiplier * point;
//
// Check Gap Exists ...
result =
isBullish
? bar.low > iBar.high
: iBar.low > bar.high;
if (!result)
{
//
iBar.Clean();
return result;
}
//
// Validate Gap Size ...
double gapSize = isBullish
? bar.low - iBar.high
: iBar.low - bar.high;
result = gapSize >= approvedGapSize;
if (!result)
{
//
iBar.Clean();
return result;
}
//
dir = bar.GetDirection();
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
iBar.Clean();
//
return result;
}
/**
* Check Specified Bar is Pinned Bar or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param shadowApprovedMultiplier: double, Shadow Size Approve Multiplier ...
* @param minimumPinBarRangeMultiplier: doubble, Range Size Approve Multiplier in Point ...
* @param forceDirection: bool, Apply Force Bar Type ...
*
* @return ( bool )
*/
bool IsPinnedBar(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
double shadowApprovedMultiplier = 3,
double minimumPinBarRangeMultiplier = 4,
bool forceDirection = false //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Normalizing ...
shadowApprovedMultiplier = NormalizeDouble(shadowApprovedMultiplier, 2, 5);
minimumPinBarRangeMultiplier = NormalizeDouble(minimumPinBarRangeMultiplier, 2, 100);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Calculate Requirements ...
double body = bar.GetBody();
double lowShadow = bar.GetLowShadow();
double highShadow = bar.GetHighShadow();
//
// Check Directions ...
bool isBullish =
lowShadow > body &&
lowShadow > highShadow &&
lowShadow >= (body * shadowApprovedMultiplier);
bool isBearish =
highShadow > body &&
highShadow > lowShadow &&
highShadow > (body * shadowApprovedMultiplier);
result = isBullish || isBearish;
if (!result)
{
return result;
}
//
// Checking Force Bar Type ...
if (forceDirection)
{
//
result =
isBullish
? bar.IsBullish()
: bar.IsBearish();
}
if (!result)
{
return result;
}
//
// Check Range Validation ...
double point = GetPoints(bar.symbol);
double approvedRange = minimumPinBarRangeMultiplier * point;
result = bar.GetRange() >= approvedRange;
if (!result)
{
return result;
}
//
// Setting Direction ...
dir =
isBullish
? X_DIRECTION_BULLISH
: X_DIRECTION_BEARISH;
//
result = HasDirection(dir);
//
return result;
}
/**
* Check Specified Bar has Star Pattern or not ...
* Morning Star => Bullish
* Evening Star => Bearish
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
*
* @return ( bool )
*/
bool IsStarPattern(
XOHCL &bar,
ENUM_X_DIRECTION &dir //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Get Requirements ...
XOHCL pBar, p2Bar;
result = bar.GetPreviousBar(pBar);
result = result &&
pBar.GetPreviousBar(p2Bar);
if (!result)
{
//
pBar.Clean();
p2Bar.Clean();
//
return result;
}
//
// Check Directions ...
bool isBullish =
bar.IsBullish() &&
p2Bar.IsBearish();
bool isBearish =
bar.IsBearish() &&
p2Bar.IsBullish();
result = isBullish || isBearish;
if (!result)
{
//
pBar.Clean();
p2Bar.Clean();
//
return result;
}
//
// Checking Gap Exists Between Bars ...
result =
isBullish
? p2Bar.GetDown() > pBar.GetUp() &&
pBar.GetUp() < bar.GetDown()
: p2Bar.GetUp() < pBar.GetDown() &&
pBar.GetDown() > bar.GetUp();
if (!result)
{
//
pBar.Clean();
p2Bar.Clean();
//
return result;
}
//
// Check Bar Close Validation ...
double barClosedApprovedPrice = p2Bar.GetDown() + ((p2Bar.GetUp() - p2Bar.GetDown()) / 2);
result =
isBullish
? bar.close > barClosedApprovedPrice
: bar.close < barClosedApprovedPrice;
if (!result)
{
//
pBar.Clean();
p2Bar.Clean();
//
return result;
}
//
// Validating PBar Body and Shadow ...
double body = pBar.GetBody();
double lowShadow = pBar.GetLowShadow();
double highShadow = pBar.GetHighShadow();
result =
body < bar.GetBody() &&
body < p2Bar.GetBody() &&
(isBullish
? lowShadow > body &&
lowShadow > highShadow
: highShadow > body &&
highShadow > lowShadow);
if (!result)
{
//
pBar.Clean();
p2Bar.Clean();
//
return result;
}
//
dir = bar.GetDirection();
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
pBar.Clean();
p2Bar.Clean();
//
return result;
}
/**
* Check Specified Bar has Pirecing Pattern or not ...
* Piercing => Bullish
* Dark Cloud Cover => Bearish
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
*
* @return ( bool )
*/
bool IsPiercingPattern(
XOHCL &bar,
ENUM_X_DIRECTION &dir //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
XOHCL pBar;
result = bar.GetPreviousBar(pBar);
if (!result)
{
//
pBar.Clean();
return result;
}
//
// Check Directions ...
bool isBullish =
bar.IsBullish() &&
pBar.IsBearish();
bool isBearish =
bar.IsBearish() &&
pBar.IsBullish();
result = isBullish || isBearish;
if (!result)
{
//
pBar.Clean();
return result;
}
//
// Check Bar Close ...
double barCloseApprovedPrice = pBar.GetDown() + ((pBar.GetUp() - pBar.GetDown()) / 2);
result =
isBullish
? bar.close > barCloseApprovedPrice
: bar.close < barCloseApprovedPrice;
if (!result)
{
//
pBar.Clean();
return result;
}
//
dir = bar.GetDirection();
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
pBar.Clean();
//
return result;
}
/**
* Check Specified Bar has Rising Pattern or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
*
* @return ( bool )
*/
bool IsRisingPattern(
XOHCL &bar,
ENUM_X_DIRECTION &dir //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Reading Required Bars ...
XOHCL iBar;
XOHCL bars[];
bool has = false;
int start = bar.Index();
int end = start + 5;
for (int i = start; i < end; i++)
{
//
// Initialized Bar ...
has = iBar.Init(
bar.symbol,
bar.period,
i //
);
if (has)
{
//
AddRef(
iBar,
bars //
);
}
//
iBar.Clean();
}
result = ArraySize(bars) == 5;
if (!result)
{
//
iBar.Clean();
SpecifiedClean(bars);
//
return result;
}
//
// Check Directions ...
bool isBullish =
bars[0].IsBullish() &&
bars[4].IsBullish() &&
bars[1].IsBearish() &&
bars[2].IsBearish() &&
bars[3].IsBearish();
bool isBearish =
bars[0].IsBearish() &&
bars[4].IsBearish() &&
bars[1].IsBullish() &&
bars[2].IsBullish() &&
bars[3].IsBullish();
result = isBullish || isBearish;
if (!result)
{
//
iBar.Clean();
SpecifiedClean(bars);
//
return result;
}
//
// Check Range Validation ...
double upper = bars[4].GetUp();
double lower = bars[4].GetDown();
//
double max = MathMax(bars[1].GetUp(), bars[2].GetUp());
max = MathMax(max, bars[3].GetUp());
//
double min = MathMin(bars[1].GetDown(), bars[2].GetDown());
min = MathMin(min, bars[3].GetDown());
//
result =
max < upper &&
min > lower &&
(isBullish
? bars[0].close > bars[4].GetUp()
: bars[0].close < bars[4].GetDown());
if (!result)
{
//
iBar.Clean();
SpecifiedClean(bars);
//
return result;
}
//
dir = bar.GetDirection();
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
iBar.Clean();
SpecifiedClean(bars);
//
return result;
}
/**
* Check Specified Bar has Flag Pattern or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param swingBar: XOHCL, Detected Swing Bar ...
* @param minimumPullbackBars: int, Minimum Shoulders of Swing Bar ...
*
* @return ( bool )
*/
bool IsFlagPattern(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
XOHCL &swingBar,
int minimumPullbackBars = 3 //
)
{
//
bool result = false;
//
// Prepare ...
swingBar.Clean();
CleanDirection(dir);
//
// Normalizing ...
minimumPullbackBars = NormalizeInt(minimumPullbackBars, 3, 10);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Reading Previous Bar ...
XOHCL pBar;
result = bar.GetPreviousBar(pBar);
if (!result)
{
//
pBar.Clean();
return result;
}
//
// Check Directions ...
bool isBullish =
bar.IsBullish() &&
bar.close > pBar.GetUp();
bool isBearish =
bar.IsBearish() &&
bar.close < pBar.GetDown();
result = isBullish || isBearish;
if (!result)
{
//
pBar.Clean();
return result;
}
//
// Detect Swing Bar ...
ENUM_X_SWING_TYPE swingType =
isBullish
? X_SWING_HIGH
: X_SWING_LOW;
result =
result &&
IsXValid(swingType) &&
bar.FindNextSwingBar(
swingType,
swingBar,
minimumPullbackBars //
) &&
(isBullish
? swingBar.IsBullish() &&
swingBar.high > pBar.high
: swingBar.IsBearish() &&
swingBar.low < pBar.low);
if (!result)
{
//
pBar.Clean();
swingBar.Clean();
//
return result;
}
//
dir = bar.GetDirection();
//
// Get Result ...
result = HasDirection(dir);
//
// Cleanup Resources ...
pBar.Clean();
if (!result)
{
swingBar.Clean();
}
//
return result;
}
/**
* Check Specified Bar has SignalKey Bar Pattern or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param shadowApprovedMultiplier: double, Shadow Size Approve Multiplier ...
* @param minimumPinBarRangeMultiplier: doubble, Range Size Approve Multiplier in Point ...
* @param forceDirection: bool, Apply Force Bar Type ...
*
* @return ( bool )
*/
bool IsSignalKeyBarPattern(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
double shadowApprovedMultiplier = 3,
double minimumPinBarRangeMultiplier = 4,
bool forceDirection = false //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Get Requirements ...
XOHCL pBar;
result = bar.GetPreviousBar(pBar);
if (!result)
{
return result;
}
//
// Chck Previous Bar must Pinned ...
result = IsPinnedBar(
pBar,
dir,
shadowApprovedMultiplier,
minimumPinBarRangeMultiplier,
forceDirection //
);
if (!result)
{
//
pBar.Clean();
return result;
}
//
// Check Direction ...
bool isBullish =
bar.IsBullish() &&
IsSpecifiedBullish(dir) &&
bar.close > pBar.high;
bool isBearish =
bar.IsBearish() &&
IsSpecifiedBearish(dir) &&
bar.close < pBar.low;
result = isBullish || isBearish;
if (!result)
{
//
pBar.Clean();
return result;
}
//
dir = bar.GetDirection();
//
result = HasDirection(dir);
//
// Cleanup Resources ...
pBar.Clean();
//
return result;
}
/**
* Check Specified Bar has Pullback Bar Pattern or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param zone: XBoxZone, refrence to hold detected zone ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param pullbackLength: int, min required indirectional bars to confirm pullback ...
* @param forceBreak: bool, force pullback bar breaks peak or vale of Zone ...
*
* @return ( bool )
*/
bool IsPullbackPattern(
XOHCL &bar,
XBoxZone &zone,
ENUM_X_DIRECTION &dir,
int pullbackLength = 2,
bool forceBreak = false //
)
{
//
bool result = false;
//
// Prepare ...
zone.Clean();
CleanDirection(dir);
//
// Normalize ...
pullbackLength = NormalizeInt(pullbackLength, 2);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// First Bar Direction ...
dir = bar.GetDirection();
result = HasDirection(dir);
if (!result)
{
return result;
}
//
// Loop through Pullback Length ...
XOHCL iBar;
datetime from = NULL;
double max = EMPTY_VALUE;
double min = EMPTY_VALUE;
int start = bar.Index() + 1;
int end = start + pullbackLength;
for (int i = start; i < end; i++)
{
//
// Initialize Indexed Bar ...
iBar.Clean();
result = bar.BarAt(i, iBar) &&
iBar.GetDirection() == Opposit(dir);
if (!result)
{
break;
}
//
// Calculate Max and Min ...
max =
!NotEmptyZero(max)
? iBar.high
: MathMax(max, iBar.high);
min =
!NotEmptyZero(min)
? iBar.low
: MathMin(min, iBar.low);
from = iBar.time;
}
//
// Apply Force Break ...
if (result &&
forceBreak)
{
//
result =
result &&
NotEmptyZero(min) &&
NotEmptyZero(max) &&
(IsXBullish(dir)
? bar.close > max
: bar.close < min);
}
//
// Prepare Zone ...
if (result)
{
//
ToBox(
bar,
zone,
dir,
ToXString(X_BAR_PATTERN_PULLBACK) //
);
zone.upper = max;
zone.lower = min;
zone.from = from;
zone.at = bar.time;
zone.to = bar.time;
//
result = zone.IsValid();
}
//
// Cleanup ...
if (!result)
{
//
zone.Clean();
CleanDirection(dir);
}
iBar.Clean();
//
return result;
}
/**
* Check Market Has Consolidation Pattern or not ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param zone: XBoxZone, refrence to hold detected zone ...
* @param dir: ENUM_X_DIRECTION, reference to holds Detected Order Flow Direction ...
* @param validationLength: int, Validation Length ...
* @param breakPriceType: ENUM_X_PRICE, Specified Price Type to Breake Consolidation ...
* @param upperPriceType: ENUM_X_PRICE, Specified Price Type to Detected Upper of Consolidation ...
* @param lowerPriceType: ENUM_X_PRICE, Specified Price Type to Detected Lower of Consolidation ...
*
* @return ( bool )
*/
bool IsConsolidationPattern(
XOHCL &bar,
XBoxZone &zone,
ENUM_X_DIRECTION &dir,
int validationLength = 5,
ENUM_X_PRICE breakPriceType = X_PRICE_CLOSE,
ENUM_X_PRICE upperPriceType = X_PRICE_HIGH,
ENUM_X_PRICE lowerPriceType = X_PRICE_LOW //
)
{
//
// Descriptions:
// ===============
// Consolidation Zones Happens by Highest High and Lowest Low
// of Specified Range which Breakes From one Side ...
//
//
bool result = false;
//
// Prepare ...
zone.Clean();
CleanDirection(dir);
//
// Normalize ...
validationLength = NormalizeInt(validationLength, 3);
//
// Validate ...
result = bar.IsValid() &&
IsXValid(lowerPriceType) &&
IsXValid(upperPriceType) &&
IsXValid(breakPriceType);
if (!result)
{
return result;
}
//
// Define Requirements ...
XOHCL iBar;
bool has = false;
datetime to = NULL;
datetime from = NULL;
double iLow = EMPTY_VALUE;
bool hasLowShadow = false;
double lower = EMPTY_VALUE;
double upper = EMPTY_VALUE;
double iHigh = EMPTY_VALUE;
bool hasHighShadow = false;
double iPrice = EMPTY_VALUE;
bool isLowerBreaked = false;
bool isUpperBreaked = false;
//
// Loop Through Bars ...
int barIndex = bar.Index();
int idx = barIndex;
bool canLookup = IsValidIndex(idx);
while (canLookup)
{
//
// Initialize Indexed Bar ...
idx++;
iBar.Clean();
result = bar.BarAt(idx, iBar);
if (!result)
{
break;
}
//
// Update Time ...
if (idx == barIndex + 1)
{
to = iBar.time;
}
//
// Extract Low and High Price ...
iLow = iBar.GetPrice(lowerPriceType);
iHigh = iBar.GetPrice(upperPriceType);
result = NotEmptyZero(iLow) &&
NotEmptyZero(iHigh);
if (!result)
{
break;
}
//
// Check Bar Has Low/High Shadow ...
hasLowShadow = iBar.GetLowShadow() > iBar.GetHighShadow();
hasHighShadow = iBar.GetHighShadow() > iBar.GetLowShadow();
//
// Update Range Lower and Upper based on Shadows ...
//
// Lower ...
if (hasLowShadow)
{
//
// Check Can Update ...
has =
!NotEmptyZero(lower)
? true
: iLow < lower;
if (has)
{
lower = iLow;
}
}
//
// Upper ...
if (hasHighShadow)
{
//
// Check Can Update ...
has =
!NotEmptyZero(upper)
? true
: iHigh > upper;
if (has)
{
upper = iHigh;
}
}
//
// Check Price Breakes ...
iPrice = iBar.GetPrice(breakPriceType);
result = NotEmptyZero(iPrice);
if (!result)
{
break;
}
//
// Check Upper/Lower breakes ...
isLowerBreaked = iPrice < lower;
isUpperBreaked = iPrice > upper;
//
canLookup = (!isLowerBreaked && !isUpperBreaked);
if (!canLookup)
{
break;
}
//
from = iBar.time;
}
//
// Validate Calculations ...
result =
//
IsXValid(to) &&
IsXValid(from) &&
from < to &&
//
NotEmptyZero(lower) &&
NotEmptyZero(upper) &&
upper > lower
//
;
//
// Validate To Edge Breakes ...
if (result)
{
//
iPrice = bar.GetPrice(breakPriceType);
result = NotEmptyZero(iPrice);
//
// Validate To Edge Breakes ...
if (result)
{
//
isUpperBreaked = iPrice > upper;
isLowerBreaked = iPrice < lower;
//
// Validate To Break ...
result = isUpperBreaked || isLowerBreaked;
}
//
// Prepare Direction ...
if (result)
{
//
dir =
isUpperBreaked
? X_DIRECTION_BULLISH
: X_DIRECTION_BEARISH;
}
//
// Validate Direction ...
result = HasDirection(dir);
}
//
// Prepare Zone ...
if (result)
{
//
zone.to = to;
zone.dir = dir;
zone.from = from;
zone.at = bar.time;
zone.upper = upper;
zone.lower = lower;
zone.symbol = bar.symbol;
zone.period = bar.period;
zone.type = ToXString(X_BAR_PATTERN_CONSOLIDATION);
//
// Validate Zone ...
result = zone.IsValid();
//
// Validate Zone Length ...
if (result)
{
result = zone.FromIndex() - zone.ToIndex() >= validationLength;
}
}
//
// Cleanup ...
if (!result)
{
//
zone.Clean();
CleanDirection(dir);
}
iBar.Clean();
//
return result;
}
/**
* Detect Order Flow ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dir: ENUM_X_DIRECTION, reference to holds Detected Order Flow Direction ...
* @param orderFlow: XBoxZone, collection reference to holds Detected Order Flow ...
* @param orderFlowValidation: int, number of sequential Zones for Validate Order Flow ...
* @param containsOBs: bool, Specified to Use Order Blocks in Order Flow Detection ...
* @param containsFVGs: bool, Specified to Use Fair Value Gaps in Order Flow Detection ...
* @param containsSupportResistances: bool, Specified to Use Support and Resistances in Order Flow Detection ...
* @param zonesLoopback: int, Zone Detection Loopback ...
* @param zonesValidationLength: int, Zone Validation Length ...
* @param loopback: int, Max Allowed Loopback Bars for Collecting ...
*
* @return ( bool )
*/
bool HasOrderFlow(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
XBoxZone &orderFlow[],
int orderFlowValidation = 2,
bool containsOBs = false,
bool containsFVGs = true,
bool containsSupportResistances = false,
int zonesLoopback = 5,
int zonesValidationLength = 21,
int loopback = 100 //
)
{
//
bool result = false;
//
// Prepare ...
XClean(orderFlow);
CleanDirection(dir);
int maxRequiredZone = 0;
//
// Normalize ...
loopback = NormalizeInt(loopback, 10);
zonesLoopback = NormalizeInt(zonesLoopback, 3);
maxRequiredZone = NormalizeInt(maxRequiredZone, 0);
orderFlowValidation = NormalizeInt(orderFlowValidation, 1);
zonesValidationLength = NormalizeInt(zonesValidationLength, 3);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Do ...
//
// Collectiong Zones ...
XBoxZone tmp;
XBoxZone tmps[];
XBoxZone _zones[];
XBoxZone bullishZones[];
XBoxZone bearishZones[];
int bullishZonesCount = 0;
int bearishZonesCount = 0;
//
// Order Blocks ...
if (containsOBs)
{
//
XClean(tmps);
CollectOBs(bar, tmps, zonesLoopback, maxRequiredZone, loopback);
FillNotExists(_zones, tmps);
}
//
// Fair Value Gaps ...
if (containsFVGs)
{
//
XClean(tmps);
CollectFVGs(bar, tmps, zonesLoopback, maxRequiredZone, loopback);
FillNotExists(_zones, tmps);
}
//
// Support and Resistances ...
if (containsSupportResistances)
{
//
XClean(tmps);
CollectZones(bar, tmps, zonesLoopback, maxRequiredZone, loopback);
FillNotExists(_zones, tmps);
}
//
// Validate Zones ...
result = HasChild(_zones);
//
// Extract Only Validated Zones ...
if (result)
{
//
ValidateBoxes(
_zones,
bar.Index() // Bars Length ...
);
//
UpdateToTime(_zones, bar.time);
RemoveBreakedZones(_zones, X_PRICE_CLOSE);
//
result = HasChild(_zones);
}
//
// Separate Zones Direction ...
if (result)
{
//
bullishZonesCount = ExtractByDirection(bullishZones, _zones, X_DIRECTION_BULLISH);
bearishZonesCount = ExtractByDirection(bearishZones, _zones, X_DIRECTION_BEARISH);
//
result = IsValidSize(bullishZonesCount) ||
IsValidSize(bearishZonesCount);
}
//
// Looking for Sequences of Zones ...
if (result)
{
//
int idx = -1;
bool has = false;
XBoxZone bullOrderFlow[];
XBoxZone bearOrderFlow[];
//
// Bullish OrderFlow ...
//
// Sorting ...
Copy(bullishZones, bullOrderFlow);
Sort(
bullOrderFlow,
X_SORT_BY_DATE,
X_DIRECTION_BEARISH,
orderFlowValidation + 1 //
);
bool hasBullishOrderFlow = ArraySize(bullOrderFlow) >= orderFlowValidation;
if (hasBullishOrderFlow)
{
//
// Validate Order Flow ...
Copy(bullOrderFlow, tmps);
XClean(bullOrderFlow);
while (HasChild(tmps))
{
//
tmp.Clean();
tmp = tmps[0];
ArrayRemove(tmps, 0, 1);
//
idx = GetHighest(tmps);
has = !IsValidIndex(idx)
? true
: tmps[idx].lower < tmp.lower;
if (has)
{
AddIfNotExists(tmp, bullOrderFlow);
}
}
//
hasBullishOrderFlow = ArraySize(bullOrderFlow) >= orderFlowValidation;
}
//
// Bearish OrderFlow ...
//
// Sorting ...
Copy(bearishZones, bearOrderFlow);
Sort(
bearOrderFlow,
X_SORT_BY_DATE,
X_DIRECTION_BULLISH,
orderFlowValidation + 1 //
);
bool hasBearishOrderFlow = ArraySize(bearOrderFlow) >= orderFlowValidation;
if (hasBearishOrderFlow)
{
//
// Validate Order Flow ...
Copy(bearOrderFlow, tmps);
XClean(bearOrderFlow);
while (HasChild(tmps))
{
//
tmp.Clean();
tmp = tmps[0];
ArrayRemove(tmps, 0, 1);
//
idx = GetLowest(tmps);
has = !IsValidIndex(idx)
? true
: tmps[idx].upper > tmp.upper;
if (has)
{
AddIfNotExists(tmp, bearOrderFlow);
}
}
//
hasBearishOrderFlow = ArraySize(bearOrderFlow) >= orderFlowValidation;
}
//
result = hasBullishOrderFlow ||
hasBearishOrderFlow;
//
// Validate Order Flows ...
if (result)
{
//
has = (hasBullishOrderFlow && !hasBearishOrderFlow) ||
(hasBearishOrderFlow && !hasBullishOrderFlow);
//
// When only Have One Direction ...
if (has)
{
//
if (hasBullishOrderFlow)
{
//
dir = X_DIRECTION_BULLISH;
Copy(bullOrderFlow, orderFlow);
}
else if (hasBearishOrderFlow)
{
//
dir = X_DIRECTION_BEARISH;
Copy(bearOrderFlow, orderFlow);
}
}
//
// When Two Direction Exists ...
else
{
//
// Retrieve Oldest Of Bullish / Bearish ...
int oldestBullIDX = GetOldest(bullOrderFlow);
int oldestBearIDX = GetOldest(bearOrderFlow);
result = IsValidIndex(oldestBullIDX) &&
IsValidIndex(oldestBearIDX);
if (result)
{
//
dir =
bullOrderFlow[oldestBullIDX].from > bearOrderFlow[oldestBearIDX].from
? X_DIRECTION_BULLISH
: bearOrderFlow[oldestBearIDX].from > bullOrderFlow[oldestBullIDX].from
? X_DIRECTION_BEARISH
: X_DIRECTION_NONE;
}
//
result = HasDirection(dir);
if (result)
{
//
if (IsXBullish(dir))
{
Copy(bullOrderFlow, orderFlow);
}
else if (IsXBearish(dir))
{
Copy(bearOrderFlow, orderFlow);
}
}
}
}
//
XClean(bullOrderFlow);
XClean(bearOrderFlow);
//
result = HasChild(orderFlow) &&
HasDirection(dir);
}
//
// Validate Price ...
if (result)
{
//
int idx = -1;
if (IsXBullish(dir))
{
//
idx = GetHighest(orderFlow);
result = IsValidIndex(idx) &&
bar.low > orderFlow[idx].upper;
}
else
{
//
idx = GetLowest(orderFlow);
result = IsValidIndex(idx) &&
bar.high < orderFlow[idx].lower;
}
}
//
// Update To Time of Order Flow ...
if (result)
{
UpdateToTime(orderFlow, bar.time);
}
//
// Cleanup ...
if (!result)
{
//
XClean(orderFlow);
CleanDirection(dir);
}
tmp.Clean();
XClean(tmps);
XClean(_zones);
XClean(bullishZones);
XClean(bearishZones);
//
return result;
}
//
// FVG ...
/**
* Check For FVG Exists in Specified Loopback Period ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param fvg: XBoxZone, reference for Holding FVG ...
* @param maxLoopback: int, Max Allowed Loopback to Lookup FVG ...
*
* @return ( bool )
*/
bool HasFVG(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
XBoxZone &fvg,
int maxLoopback = 5 //
)
{
//
bool result = false;
//
// Prepare ...
fvg.Clean();
CleanDirection(dir);
//
// Normalize ...
maxLoopback = NormalizeInt(maxLoopback, 3);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
XOHCL iBar;
bool has = false;
int start = bar.Index();
int end = start + maxLoopback;
for (int i = start; i < end; i++)
{
//
// Initialize iBar ...
has = iBar.Init(
bar.symbol,
bar.period,
i //
);
if (!has)
{
continue;
}
//
// Check iBar is FVG or not ...
has = barAnalyser.IsFVG(
iBar,
fvg,
true // Force Bar Type ...
);
if (!has)
{
//
fvg.Clean();
iBar.Clean();
CleanDirection(dir);
//
continue;
}
//
iBar.Clean();
dir = fvg.dir;
//
break;
}
//
// Check FVG Exists ...
result = fvg.IsValid();
//
// Check Direction Exists ...
result =
result &&
HasDirection(dir);
//
// Cleanup Resources ...
if (!result)
{
//
fvg.Clean();
CleanDirection(dir);
}
iBar.Clean();
//
return result;
}
//
// OB ...
/**
* Check For OB Exists in Specified Loopback Period ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param ob: XBoxZone, reference for Holding OB ...
* @param maxLoopback: int, Max Allowed Loopback to Lookup OB ...
*
* @return ( bool )
*/
bool HasOB(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
XBoxZone &ob,
int maxLoopback = 5 //
)
{
//
bool result = false;
//
// Prepare ...
ob.Clean();
CleanDirection(dir);
//
// Normalize ...
maxLoopback = NormalizeInt(maxLoopback, 3);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
XOHCL iBar;
bool has = false;
int start = bar.Index();
int end = start + maxLoopback;
for (int i = start; i < end; i++)
{
//
// Initialize iBar ...
has = iBar.Init(
bar.symbol,
bar.period,
i //
);
if (!has)
{
continue;
}
//
// Check iBar is OB or not ...
has = barAnalyser.IsOB(
iBar,
ob,
true, // Force FVG Bar Type ...
true // Force OB First Two Bar InDirection ...
);
if (!has)
{
//
ob.Clean();
iBar.Clean();
CleanDirection(dir);
//
continue;
}
//
iBar.Clean();
dir = ob.dir;
//
break;
}
//
// Check OB Exists ...
result = ob.IsValid();
//
// Check Direction Exists ...
result =
result &&
HasDirection(dir);
//
// Cleanup Resources ...
if (!result)
{
//
ob.Clean();
CleanDirection(dir);
}
iBar.Clean();
//
return result;
}
//
// Complex ...
/**
* Check Double Pinned Bar ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dir: ENUM_X_DIRECTION, reference to holds Pattern Direction ...
* @param shadowApprovedMultiplier: double, Shadow Size Approve Multiplier ...
* @param minimumPinBarRangeMultiplier: doubble, Range Size Approve Multiplier in Point ...
* @param forceDirection: bool, Apply Force Bar Type ...
* @param loopback: int, Max Allowed Detecting Loopback lenght ...
*
* @return ( bool )
*/
bool IsDoublePinned(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
double shadowApprovedMultiplier = 3,
double minimumPinBarRangeMultiplier = 4,
bool forceDirection = false,
int loopback = 5 //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Normalize ...
loopback = NormalizeInt(loopback, 1);
shadowApprovedMultiplier = NormalizeDouble(shadowApprovedMultiplier, 2, 5);
minimumPinBarRangeMultiplier = NormalizeDouble(minimumPinBarRangeMultiplier, 2, 100);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Do ...
//
// Current Bar Must be a Pinned Bar ...
result = IsPinnedBar(
bar,
dir,
shadowApprovedMultiplier,
minimumPinBarRangeMultiplier,
forceDirection //
);
if (!result)
{
//
CleanDirection(dir);
//
return result;
}
//
XOHCL iBar;
ENUM_X_DIRECTION iDir;
int start = bar.Index() + 1;
int end = start + loopback;
for (int i = start; i < end; i++)
{
//
iBar.Clean();
result = bar.BarAt(i, iBar);
result = result && IsPinnedBar(
iBar,
iDir,
shadowApprovedMultiplier,
minimumPinBarRangeMultiplier,
forceDirection //
);
result = result &&
iDir == dir;
if (result)
{
break;
}
}
//
// Cleanup ...
if (!result)
{
CleanDirection(dir);
}
iBar.Clean();
CleanDirection(iDir);
//
return result;
}
/**
* Check Double Engulfed Bar ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dir: ENUM_X_DIRECTION, reference to holds Pattern Direction ...
* @param loopback: int, Max Allowed Detecting Loopback lenght ...
*
* @return ( bool )
*/
bool IsDoubleEngulfed(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
int loopback = 5 //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Normalize ...
loopback = NormalizeInt(loopback, 1);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Do ...
//
// Current Bar Must be a Pinned Bar ...
result = IsEngulfedBar(
bar,
dir //
);
if (!result)
{
//
CleanDirection(dir);
//
return result;
}
//
XOHCL iBar;
ENUM_X_DIRECTION iDir;
int start = bar.Index() + 1;
int end = start + loopback;
for (int i = start; i < end; i++)
{
//
iBar.Clean();
result = bar.BarAt(i, iBar);
result = result && IsEngulfedBar(
iBar,
iDir //
);
result = result &&
iDir == dir;
if (result)
{
break;
}
}
//
// Cleanup ...
if (!result)
{
CleanDirection(dir);
}
iBar.Clean();
CleanDirection(iDir);
//
return result;
}
/**
* Check Double Momentum Bar ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dir: ENUM_X_DIRECTION, reference to holds Pattern Direction ...
* @param requiredBars: int, bar range for approve Momentum ...
* @param approvedMultiplier: double, a Multipler for approve Momentum ...
* @param loopback: int, Max Allowed Detecting Loopback lenght ...
*
* @return ( bool )
*/
bool IsDoubleMomentum(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
int requiredBars = 3,
double approvedMultiplier = 2,
int loopback = 5 //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
//
// Normalize ...
loopback = NormalizeInt(loopback, 1);
requiredBars = NormalizeInt(requiredBars, 3, 10);
approvedMultiplier = NormalizeDouble(approvedMultiplier, 2, 5);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Do ...
//
// Current Bar Must be a Pinned Bar ...
result = IsMomentumBar(
bar,
dir,
requiredBars,
approvedMultiplier //
);
if (!result)
{
//
CleanDirection(dir);
//
return result;
}
//
XOHCL iBar;
ENUM_X_DIRECTION iDir;
int start = bar.Index() + 1;
int end = start + loopback;
for (int i = start; i < end; i++)
{
//
iBar.Clean();
result = bar.BarAt(i, iBar);
result = result && IsMomentumBar(
iBar,
iDir,
requiredBars,
approvedMultiplier //
);
result = result &&
iDir == dir;
if (result)
{
break;
}
}
//
// Cleanup ...
if (!result)
{
CleanDirection(dir);
}
iBar.Clean();
CleanDirection(iDir);
//
return result;
}
/**
* Check Has Double Patterns ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dir: ENUM_X_DIRECTION, reference to holds Specified Patterns ...
* @param barPatterns: XPatternAnalysis, reference to Provides Bar's Pattern Analysis ...
* @param pBarPatterns: XPatternAnalysis, reference to Provides Previous Bar's Pattern Analysis ...
* @param allowedPatterns: ENUM_X_BAR_PATTERN, collection to Provide Allowed Bar Patterns for Detection ...
* @param config: XPatternAnalysisConfig, Provides Patterns Detection Config ...
* @param loopback: int, Max Allowed Loopback for Detection ...
* @param forDir: ENUM_X_DIRECTION, Specified Patterns Detection Direction ...
*
* @return ( bool )
*/
bool HasDoublePatterns(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
XPatternAnalysis &barPatterns,
XPatternAnalysis &pBarPatterns,
ENUM_X_BAR_PATTERN &allowedPatterns[],
XPatternAnalysisConfig &config,
int loopback = 5,
ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
//
bool result = false;
//
// Prepare ...
barPatterns.Clean();
CleanDirection(dir);
pBarPatterns.Clean();
XPatternAnalysisConfig iConfig = config;
//
// Normalize ...
loopback = NormalizeInt(loopback, 2);
//
// Validate ...
result = bar.IsValid() &&
HasChild(allowedPatterns);
if (!result)
{
return result;
}
//
// Prepare Config ...
iConfig.CleanPatterns();
if (HasChild(allowedPatterns))
{
//
for (int i = 0; i < ArraySize(allowedPatterns); i++)
{
iConfig.AddPattern(allowedPatterns[i]);
}
}
//
// Detect Bar Patterns ...
result = AnalyseBarPatterns(bar, barPatterns, config);
if (!result)
{
//
CleanDirection(dir);
barPatterns.Clean();
pBarPatterns.Clean();
//
return result;
}
//
// Check Direction ...
int bullishCount = barPatterns.Count(X_DIRECTION_BULLISH);
int bearishCount = barPatterns.Count(X_DIRECTION_BEARISH);
dir =
bullishCount > bearishCount
? X_DIRECTION_BULLISH
: bearishCount > bullishCount
? X_DIRECTION_BEARISH
: X_DIRECTION_NONE;
result = HasDirection(dir);
//
// Check for Dir ...
if (HasDirection(forDir))
{
//
result =
result &&
dir == forDir;
}
//
// Looping Back ...
if (result)
{
//
XOHCL iBar;
ENUM_X_DIRECTION iDir;
int start = bar.Index() + 1;
int end = start + loopback;
for (int i = start; i < end; i++)
{
//
iBar.Clean();
pBarPatterns.Clean();
result = bar.BarAt(i, iBar);
result = result &&
AnalyseBarPatterns(iBar, pBarPatterns, iConfig);
if (!result)
{
continue;
}
//
bullishCount = pBarPatterns.Count(X_DIRECTION_BULLISH);
bearishCount = pBarPatterns.Count(X_DIRECTION_BEARISH);
iDir =
bullishCount > bearishCount
? X_DIRECTION_BULLISH
: bearishCount > bullishCount
? X_DIRECTION_BEARISH
: X_DIRECTION_NONE;
result = HasDirection(iDir);
result = result &&
dir == iDir;
if (result)
{
break;
}
}
//
iBar.Clean();
CleanDirection(iDir);
}
//
// Cleanup ...
if (!result)
{
//
CleanDirection(dir);
barPatterns.Clean();
pBarPatterns.Clean();
}
iConfig.Clean();
//
return result;
}
/**
* Count Same Bars ...
*
* @param bar: XOHCL, Specified Bar ...
* @param sameBars: XOHCL, collection reference to holds same Bars ...
* @param dir: ENUM_X_DIRECTION, refrence to Founded Bars Direction holding ...
* @param forDir: ENUM_X_DIRECTION, Specified Looking Bar ...
*
* @return ( int )
*/
int CountSameBars(
XOHCL &bar,
XOHCL &sameBars[],
ENUM_X_DIRECTION &dir,
ENUM_X_DIRECTION forDir //
)
{
//
int result = 0;
//
// Prepare ...
XClean(sameBars);
//
// Validate ...
bool has = bar.IsValid();
if (!has)
{
return result;
}
//
XOHCL tmpBar;
ENUM_X_DIRECTION tmpDir = forDir;
int idx = bar.Index();
while (has)
{
//
idx++;
tmpBar.Clean();
has = bar.BarAt(idx, tmpBar);
if (has && !HasDirection(tmpDir))
{
tmpDir = tmpBar.GetDirection();
}
has = has &&
tmpBar.GetDirection() == tmpDir;
if (!has)
{
break;
}
//
AddIfNotExists(tmpBar, sameBars);
}
//
result = ArraySize(sameBars);
if (IsValidSize(result))
{
dir = tmpDir;
}
//
// Cleanup ...
tmpBar.Clean();
//
return result;
}
//
// Trending ...
/**
* Check Trend Exists Based on Swings ...
*
* @param bar: XOHCL, Specified Bar ...
* @param dir: ENUM_X_DIRECTION a reference for Direction ...
* @param swings: XOHCL Array, Holds Swings ...
* @param numberOfSwings: int, Number of Swing Points for Detect Trend ...
* @param swingShoulders: int, Swing Shoulders Approvement Length ...
*
* @return ( bool )
*/
bool HasSwingTrend(
XOHCL &bar,
ENUM_X_DIRECTION &dir,
XOHCL &swings[],
int numberOfSwings = 3,
int swingShoulders = 7 //
)
{
//
bool result = false;
//
// Prepare ...
CleanDirection(dir);
SpecifiedClean(swings);
//
// Normalizing ...
numberOfSwings = NormalizeInt(numberOfSwings, 3, 10);
swingShoulders = NormalizeInt(swingShoulders, 3);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Check Trend Direction ...
XOHCL iSwing;
int idx = -1;
bool has = false;
XOHCL swingLows[];
XOHCL swingHighs[];
int lastSwingLowIndex = bar.Index();
int lastSwingHighIndex = bar.Index();
while (ArraySize(swingLows) < numberOfSwings || ArraySize(swingHighs) < numberOfSwings)
{
//
bool canLookupSwingLow = ArraySize(swingLows) < numberOfSwings;
bool canLookupSwingHigh = ArraySize(swingHighs) < numberOfSwings;
if (!canLookupSwingLow && !canLookupSwingHigh)
{
break;
}
//
// Handle Swing Lows ...
if (canLookupSwingLow)
{
//
iSwing.Clean();
idx = FindXSwing(
X_SWING_LOW,
bar.symbol,
bar.period,
lastSwingLowIndex + 1,
swingShoulders //
);
has = IsValidIndex(idx);
if (has)
{
//
has = iSwing.Init(
bar.symbol,
bar.period,
idx //
);
if (has)
{
//
AddRef(
iSwing,
swingLows //
);
//
lastSwingLowIndex = idx;
}
}
}
//
// Handle Swing Highs ...
if (canLookupSwingHigh)
{
//
iSwing.Clean();
idx = FindXSwing(
X_SWING_HIGH,
bar.symbol,
bar.period,
lastSwingHighIndex + 1,
swingShoulders //
);
has = IsValidIndex(idx);
if (has)
{
//
has = iSwing.Init(
bar.symbol,
bar.period,
idx //
);
if (has)
{
//
AddRef(
iSwing,
swingHighs //
);
//
lastSwingHighIndex = idx;
}
}
}
}
//
// Check Trend Based On Swings ...
//
// Check Directions ...
//
bool isBullish = false;
bool isBearish = false;
//
datetime oldestSwingLow = NULL;
datetime oldestSwingHigh = NULL;
//
// Checking Bullish Trend based on Swing Lows ...
int count = ArraySize(swingLows);
for (int i = 0; i < count - 1; i++)
{
//
has = swingLows[i].low > swingLows[i + 1].low;
isBullish =
!isBullish
? has
: isBullish && has;
if (!isBullish)
{
break;
}
//
datetime oldeTime = MathMin(swingLows[i].time, swingLows[i + 1].time);
oldestSwingLow =
!IsSpecifiedValid(oldestSwingLow)
? oldeTime
: MathMin(oldestSwingLow, oldeTime);
}
//
// Checking Bearish Trend based on Swing Highs ...
count = ArraySize(swingHighs);
for (int i = 0; i < count - 1; i++)
{
//
has = swingHighs[i].high < swingHighs[i + 1].high;
isBearish =
!isBearish
? has
: isBearish && has;
if (!isBearish)
{
break;
}
//
datetime oldeTime = MathMin(swingHighs[i].time, swingHighs[i + 1].time);
oldestSwingHigh =
!IsSpecifiedValid(oldestSwingHigh)
? oldeTime
: MathMin(oldestSwingHigh, oldeTime);
}
//
result = isBullish || isBearish;
if (!result)
{
//
iSwing.Clean();
SpecifiedClean(swingLows);
SpecifiedClean(swingHighs);
//
return result;
}
//
if (isBullish &&
isBearish &&
IsSpecifiedValid(oldestSwingLow) &&
IsSpecifiedValid(oldestSwingHigh))
{
//
// Here we Have to Detect Newest Trend or Oldest Trend ...
isBullish = oldestSwingLow < oldestSwingHigh;
isBearish = oldestSwingHigh < oldestSwingLow;
}
//
result = (isBullish && !isBearish) ||
(isBearish && !isBullish);
if (!result)
{
//
iSwing.Clean();
SpecifiedClean(swingLows);
SpecifiedClean(swingHighs);
//
return result;
}
//
if (isBullish)
{
//
Copy(
swingLows,
swings //
);
//
dir = X_DIRECTION_BULLISH;
}
else
{
//
Copy(
swingHighs,
swings //
);
//
dir = X_DIRECTION_BEARISH;
}
//
iSwing.Clean();
SpecifiedClean(swingLows);
SpecifiedClean(swingHighs);
//
return result;
}
/**
* Check Specified Bar Has Support or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param support: XOHCL, Supported Bar ...
* @param validationLength: int, Validation Length ...
* @param maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
*
* @return ( bool )
*/
bool HasSupport(
XOHCL &bar,
XOHCL &support,
int validationLength = 21,
int maxAllowedLoopback = 1500 //
)
{
//
bool result = false;
//
// Prepare ...
support.Clean();
//
// Normalizing ...
validationLength = NormalizeInt(validationLength, 7);
maxAllowedLoopback = NormalizeInt(maxAllowedLoopback, 21, 1500);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Find Support Swing ...
int idx = -1;
bool has = false;
int lookupIDX = bar.Index();
while (!IsValidIndex(idx) &&
lookupIDX < maxAllowedLoopback)
{
//
// Detect Swing ...
idx = FindXSwing(
X_SWING_LOW,
bar.symbol,
bar.period,
lookupIDX,
validationLength //
);
has = IsValidIndex(idx);
if (!has)
{
//
lookupIDX++;
continue;
}
//
// Retrieve Swing Bar ...
has = support.Init(
bar.symbol,
bar.period,
idx //
);
if (!has)
{
//
idx = -1;
lookupIDX++;
support.Clean();
continue;
}
//
// Check Suppor Low Shadow ...
has = support.GetDown() < bar.low;
if (!has)
{
//
idx = -1;
lookupIDX++;
support.Clean();
continue;
}
//
if (IsValidIndex(idx))
{
break;
}
}
//
result = support.IsValid();
//
return result;
}
/**
* Check Specified Bar Has Resistance or not ...
*
* @param bar: XOHCL, Specified Bar ...
* @param resistence: XOHCL, Resistanced Bar ...
* @param validationLength: int, Validation Length ...
* @param maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
*
* @return ( bool )
*/
bool HasResistance(
XOHCL &bar,
XOHCL &resistence,
int validationLength = 21,
int maxAllowedLoopback = 1500 //
)
{
//
bool result = false;
//
// Prepare ...
resistence.Clean();
//
// Normalizing ...
validationLength = NormalizeInt(validationLength, 7);
maxAllowedLoopback = NormalizeInt(maxAllowedLoopback, 21, 1500);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
// Find Support Swing ...
int idx = -1;
bool has = false;
int lookupIDX = bar.Index();
while (!IsValidIndex(idx) &&
lookupIDX < maxAllowedLoopback)
{
//
// Detect Swing ...
idx = FindXSwing(
X_SWING_HIGH,
bar.symbol,
bar.period,
lookupIDX,
validationLength //
);
has = IsValidIndex(idx);
if (!has)
{
//
lookupIDX++;
continue;
}
//
// Retrieve Swing Bar ...
has = resistence.Init(
bar.symbol,
bar.period,
idx //
);
if (!has)
{
//
idx = -1;
lookupIDX++;
resistence.Clean();
continue;
}
//
// Check Resistance High Shadow ...
has = resistence.GetUp() > bar.high;
if (!has)
{
//
idx = -1;
lookupIDX++;
resistence.Clean();
continue;
}
//
if (IsValidIndex(idx))
{
break;
}
}
//
result = resistence.IsValid();
//
return result;
}
/**
* Check Specified Bar Has Support or Resistance and Extract them ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param support: XBoxZone, reference to holds Detected Support ...
* @param resistance: XBoxZone, reference to holds Detected Resistance ...
* @param validationLength: int, Specified Validation Length ...
* @param loopback: int, Max Allowed Loopback Bars for Detection ...
*
* @return ( bool )
*/
bool HasSupportResistance(
XOHCL &bar,
XBoxZone &support,
XBoxZone &resistance,
int validationLength = 7,
int loopback = 100 //
)
{
//
bool result = false;
//
// Prepare ...
support.Clean();
resistance.Clean();
//
// Normalize ...
loopback = NormalizeInt(loopback, 50);
validationLength = NormalizeInt(validationLength, 3);
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
XOHCL iZBar;
//
// Support ...
iZBar.Clean();
bool hasSupport = HasSupport(
bar,
iZBar,
validationLength,
loopback //
);
if (hasSupport)
{
//
hasSupport = ToSupportBox(
iZBar,
support,
bar.time //
);
}
//
// Resistance ...
iZBar.Clean();
bool hasResistance = HasResistance(
bar,
iZBar,
validationLength,
loopback //
);
if (hasResistance)
{
//
hasResistance = ToResistanceBox(
iZBar,
resistance,
bar.time //
);
}
//
// Summarize Result ...
result = hasSupport ||
hasResistance;
//
// Cleanup ...
iZBar.Clean();
//
return result;
}
//
// Analysers ...
/**
* Analyse Specified Bar ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param analysis: XPatternAnalysis, reference to holds Analysis result ...
* @param config: XPatternAnalysisConfig, reference to Provides Detector Configs ...
*
* @return ( bool )
*/
bool AnalyseBarPatterns(
XOHCL &bar,
XPatternAnalysis &analysis,
XPatternAnalysisConfig &config //
)
{
//
bool result = false;
//
// Prepare ...
analysis.Clean();
//
// Validate ...
result = bar.IsValid();
if (!result)
{
return result;
}
//
analysis.bar = bar;
analysis.time = TimeCurrent();
//
XOHCL iSwing;
XBoxZone iZone;
ENUM_X_DIRECTION iDir;
ENUM_X_BAR_PATTERN iPattern;
//
bool has;
bool isBullish;
bool isBearish;
//
bool canDetect;
bool canDetectBullish;
bool canDetectBearish;
//
// Bars Detectors ...
//
// XPV High Low Detection ...
canDetect = config.CanDetect(X_BAR_PATTERN_HIGH) ||
config.CanDetect(X_BAR_PATTERN_LOW);
if (canDetect)
{
//
has = IsPV(
bar,
iDir,
config.pvValidationLength,
config.peakPriceType,
config.valePriceType //
);
//
canDetectBullish = config.CanDetect(X_BAR_PATTERN_LOW);
canDetectBearish = config.CanDetect(X_BAR_PATTERN_HIGH);
//
isBullish =
has &&
IsXBullish(iDir) &&
canDetectBullish;
//
isBearish =
has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
iPattern =
isBearish
? X_BAR_PATTERN_HIGH
: X_BAR_PATTERN_LOW;
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Momentum ...
iPattern = X_BAR_PATTERN_MOMENTUM;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsMomentumBar(
bar,
iDir,
config.momentumBarValidationLength,
config.momentumBarApprovedMultiplier //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Engulfed ...
iPattern = X_BAR_PATTERN_ENGULFED;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsEngulfedBar(
bar,
iDir //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// TrueGap ...
iPattern = X_BAR_PATTERN_TRUE_GAPED;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsTrueGapedBar(
bar,
iDir,
config.trueGapApprovedStrength //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Pinned ...
iPattern = X_BAR_PATTERN_PINNED;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsPinnedBar(
bar,
iDir,
config.pinBarShadowApprovedMultiplier,
config.pinBarRangeMultiplier,
config.pinBarForceDirection //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Pattern Detectors ...
//
// Star ...
iPattern = X_BAR_PATTERN_STAR;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsStarPattern(
bar,
iDir //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Piercing ...
iPattern = X_BAR_PATTERN_PIERCING;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsPiercingPattern(
bar,
iDir //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Rising ...
iPattern = X_BAR_PATTERN_RISING;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsRisingPattern(
bar,
iDir //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Flag ...
iPattern = X_BAR_PATTERN_FLAG;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsFlagPattern(
bar,
iDir,
iSwing,
config.flagPatternPullbackLength //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
//
analysis.flagSwing = iSwing;
}
}
//
// SignalKey ...
iPattern = X_BAR_PATTERN_SIGNALKEY;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = IsSignalKeyBarPattern(
bar,
iDir,
config.pinBarShadowApprovedMultiplier,
config.pinBarRangeMultiplier,
config.pinBarForceDirection //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
IsXBullish(iDir) &&
canDetectBullish;
isBearish = has &&
IsXBearish(iDir) &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iDir //
);
}
}
//
// Zone Detectors ...
//
// OB ...
iPattern = X_BAR_PATTERN_OB;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = barAnalyser.IsOB(
bar,
iZone,
config.fvgForceType,
config.obForceTwoBar //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
iZone.IsBullish() &&
canDetectBullish;
isBearish = has &&
iZone.IsBearish() &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iZone.dir //
);
//
analysis.ob = iZone;
}
}
//
// FVG ...
iPattern = X_BAR_PATTERN_FVG;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = barAnalyser.IsFVG(
bar,
iZone,
config.fvgForceType //
);
//
canDetectBullish = config.CanDetect(
iPattern,
X_DIRECTION_BULLISH //
);
canDetectBearish = config.CanDetect(
iPattern,
X_DIRECTION_BEARISH //
);
//
isBullish = has &&
iZone.IsBullish() &&
canDetectBullish;
isBearish = has &&
iZone.IsBearish() &&
canDetectBearish;
//
canDetect = isBullish ||
isBearish;
if (canDetect)
{
//
analysis.AddPattern(
iPattern,
iZone.dir //
);
//
analysis.fvg = iZone;
}
}
//
// Support ...
iPattern = X_BAR_PATTERN_SUPPORT;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = HasSupport(
bar,
iSwing,
config.supportAndResistanceValidationLength,
config.supportAndResistanceLoopbackLength //
);
if (has)
{
//
has = ToSupportBox(
iSwing,
iZone,
bar.time,
config.supportAndResistanceValidationLength //
);
if (has)
{
//
analysis.AddPattern(
iPattern,
iZone.dir //
);
//
analysis.support = iZone;
}
}
}
//
// Resistance ...
iPattern = X_BAR_PATTERN_RESISTANCE;
canDetect = config.CanDetect(iPattern);
if (canDetect)
{
//
has = HasResistance(
bar,
iSwing,
config.supportAndResistanceValidationLength,
config.supportAndResistanceLoopbackLength //
);
if (has)
{
//
has = ToResistanceBox(
iSwing,
iZone,
bar.time,
config.supportAndResistanceValidationLength //
);
if (has)
{
//
analysis.AddPattern(
iPattern,
iZone.dir //
);
//
analysis.resistance = iZone;
}
}
}
//
// Validate Analysis ...
result = analysis.IsValid();
//
// Cleanup Resources ...
iZone.Clean();
iSwing.Clean();
CleanDirection(iDir);
//
return result;
}
/**
* Collect Required Order Blocks ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dest: XBoxZone, collection reference to holds detected zones ...
* @param zonesLoopback: int, zones detection loopback ...
* @param maxRequiredZone: int, number of required zones ...
* @param loopback: int, max allowed loopback lenght for zone Detection ...
*
* @return ( int )
*/
int CollectOBs(
XOHCL &bar,
XBoxZone &dest[],
int zonesLoopback = 5,
int maxRequiredZone = 0,
int loopback = 100 //
)
{
//
int result = 0;
//
// Prepare ...
XClean(dest);
//
// Normalize ...
loopback = NormalizeInt(loopback, 10);
zonesLoopback = NormalizeInt(zonesLoopback, 3);
maxRequiredZone = NormalizeInt(maxRequiredZone, 0);
//
// Validate ...
bool has = bar.IsValid();
if (!has)
{
return result;
}
//
XOHCL iBar;
XBoxZone iBox;
ENUM_X_DIRECTION iDir;
int start = bar.Index();
int end = start + loopback;
for (int i = start; i < end; i++)
{
//
iBar.Clean();
iBox.Clean();
iDir = X_DIRECTION_NONE;
//
has = bar.BarAt(i, iBar);
has = has &&
HasOB(iBar, iDir, iBox, zonesLoopback);
if (has)
{
//
AddIfNotExists(
iBox,
dest //
);
}
//
has = maxRequiredZone <= 0 ||
(maxRequiredZone > 0 && ArraySize(dest) < maxRequiredZone);
if (!has)
{
break;
}
}
//
result = ArraySize(dest);
//
// Cleanup ...
iBar.Clean();
iBox.Clean();
iDir = X_DIRECTION_NONE;
//
return result;
}
/**
* Collect Required Fair Value Gaps ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dest: XBoxZone, collection reference to holds detected zones ...
* @param zonesLoopback: int, zones detection loopback ...
* @param maxRequiredZone: int, number of required zones ...
* @param loopback: int, max allowed loopback lenght for zone Detection ...
*
* @return ( int )
*/
int CollectFVGs(
XOHCL &bar,
XBoxZone &dest[],
int zonesLoopback = 5,
int maxRequiredZone = 0,
int loopback = 100 //
)
{
//
int result = 0;
//
// Prepare ...
XClean(dest);
//
// Normalize ...
loopback = NormalizeInt(loopback, 10);
zonesLoopback = NormalizeInt(zonesLoopback, 3);
maxRequiredZone = NormalizeInt(maxRequiredZone, 0);
//
// Validate ...
bool has = bar.IsValid();
if (!has)
{
return result;
}
//
XOHCL iBar;
XBoxZone iBox;
ENUM_X_DIRECTION iDir;
int start = bar.Index();
int end = start + loopback;
for (int i = start; i < end; i++)
{
//
iBar.Clean();
iBox.Clean();
iDir = X_DIRECTION_NONE;
//
has = bar.BarAt(i, iBar);
has = has &&
HasFVG(iBar, iDir, iBox, zonesLoopback);
if (has)
{
//
AddIfNotExists(
iBox,
dest //
);
}
//
has = maxRequiredZone <= 0 ||
(maxRequiredZone > 0 && ArraySize(dest) < maxRequiredZone);
if (!has)
{
break;
}
}
//
result = ArraySize(dest);
//
// Cleanup ...
iBar.Clean();
iBox.Clean();
iDir = X_DIRECTION_NONE;
//
return result;
}
/**
* Collect Required Support and Resistance Zones ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param dest: XBoxZone, collection reference to holds detected zones ...
* @param zonesLoopback: int, zones detection loopback ...
* @param zonesValidationLength: int, zones validation length ...
* @param maxRequiredZone: int, number of required zones ...
* @param loopback: int, max allowed loopback lenght for zone Detection ...
*
* @return ( int )
*/
int CollectZones(
XOHCL &bar,
XBoxZone &dest[],
int zonesLoopback = 5,
int zonesValidationLength = 21,
int maxRequiredZone = 0,
int loopback = 100 //
)
{
//
int result = 0;
//
// Prepare ...
XClean(dest);
//
// Normalize ...
loopback = NormalizeInt(loopback, 10);
zonesLoopback = NormalizeInt(zonesLoopback, 3);
maxRequiredZone = NormalizeInt(maxRequiredZone, 0);
zonesValidationLength = NormalizeInt(zonesValidationLength, 3);
//
// Validate ...
bool has = bar.IsValid();
if (!has)
{
return result;
}
//
XOHCL iBar;
XBoxZone iBox;
XOHCL iZoneBar;
int start = bar.Index();
int end = start + loopback;
for (int i = start; i < end; i++)
{
//
iBar.Clean();
iBox.Clean();
iZoneBar.Clean();
//
has = bar.BarAt(i, iBar);
if (!has)
{
continue;
}
//
// Support ...
has = HasSupport(iBar, iZoneBar, zonesValidationLength, zonesLoopback);
if (has)
{
//
has = ToSupportBox(iZoneBar, iBox, bar.time, zonesValidationLength);
if (has)
{
//
AddIfNotExists(
iBox,
dest //
);
}
}
//
// Resistance ...
has = HasResistance(iBar, iZoneBar, zonesValidationLength, zonesLoopback);
if (has)
{
//
has = ToResistanceBox(iZoneBar, iBox, bar.time, zonesValidationLength);
if (has)
{
//
AddIfNotExists(
iBox,
dest //
);
}
}
//
has = maxRequiredZone <= 0 ||
(maxRequiredZone > 0 && ArraySize(dest) < maxRequiredZone);
if (!has)
{
break;
}
}
//
result = ArraySize(dest);
//
// Cleanup ...
iBar.Clean();
iBox.Clean();
iZoneBar.Clean();
//
return result;
}
/**
* Detect and Collect Required Zones ...
*
* @param bar: XOHCL, reference to Specified Bar ...
* @param zones: XBoxZone, collection reference to holds detected zones ...
* @param validateZones: bool, Specified to Detect only Valid Zones ...
* @param containsOBs: bool, Flag to use Order Block Zones ...
* @param containsFVGs: bool, Flag to use Fair Value Gap Zones ...
* @param containsSupports: bool, Flag to use Support Zones ...
* @param containsResistancess: bool, Flag to use Resistance Zones ...
* @param forDir: ENUM_X_DIRECTION, Specified Zones Direction for Detecting ...
* @param sortby: ENUM_X_SORT_BY, Specified Zones Sorting Factor ...
* @param sortDir: ENUM_X_DIRECTION, Specified Zones Sorting Direction ...
* @param zonesRangeValidationFactor: double, Specified Zones Range Validation in Points ...
* @param zonesLoopback: int, loop back for Zones Detection ...
* @param zonesValidationLength: int, zones Validation Length ...
* @param count: int, number of required Zones ...
* @param loopback: int, max allowed looping back lenght for detection ...
*
* @return ( int )
*/
int DetectZones(
XOHCL &bar,
XBoxZone &zones[],
bool validateZones = true,
bool containsOBs = true,
bool containsFVGs = true,
bool containsSupports = true,
bool containsResistancess = true,
ENUM_X_DIRECTION forDir = X_DIRECTION_NONE,
ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
ENUM_X_DIRECTION sortDir = X_DIRECTION_BEARISH,
double zonesRangeValidationFactor = 10,
int zonesLoopback = 5,
int zonesValidationLength = 5,
int count = 0,
int loopback = 500 //
)
{
//
int result = 0;
//
// Prepare ...
XClean(zones);
//
// Normalize ...
count = NormalizeInt(count, 0);
loopback = NormalizeInt(loopback, 50);
zonesLoopback = NormalizeInt(zonesLoopback, 2);
zonesValidationLength = NormalizeInt(zonesValidationLength, 3);
//
// Validate ...
bool has = bar.IsValid() &&
IsXValid(sortDir) &&
IsXValid(sortBy) &&
(containsOBs || containsFVGs || containsSupports || containsResistancess);
if (!has)
{
return result;
}
//
XOHCL iBar;
XOHCL tmpBar;
XPivot tmpPivot;
XBoxZone tmpZone;
ENUM_X_DIRECTION tmpDir;
int idx = bar.Index();
while (idx < bar.Index() + loopback)
{
//
idx++;
//
iBar.Clean();
tmpBar.Clean();
tmpZone.Clean();
tmpPivot.Clean();
//
has = bar.BarAt(idx, iBar);
if (!has)
{
continue;
}
//
// Collecting Zones ...
//
// Support ...
if (containsSupports)
{
//
if (!HasDirection(forDir) || IsXBullish(forDir))
{
//
has = HasSupport(iBar, tmpBar, zonesValidationLength, zonesLoopback);
if (has)
{
//
has = ToBox(tmpBar, tmpZone, X_DIRECTION_BULLISH, X_BAR_PATTERN_SUPPORT);
if (has)
{
AddIfNotExists(tmpZone, zones);
}
}
}
}
//
// Resistance ...
if (containsResistancess)
{
//
if (!HasDirection(forDir) || IsXBearish(forDir))
{
//
has = HasResistance(iBar, tmpBar, zonesValidationLength, zonesLoopback);
if (has)
{
//
has = ToBox(tmpBar, tmpZone, X_DIRECTION_BEARISH, X_BAR_PATTERN_RESISTANCE);
if (has)
{
AddIfNotExists(tmpZone, zones);
}
}
}
}
//
// Order Block ...
if (containsOBs)
{
//
has = HasOB(iBar, tmpDir, tmpZone, zonesLoopback);
if (has && (!HasDirection(forDir) || tmpDir == forDir))
{
AddIfNotExists(tmpZone, zones);
}
}
//
// Fair Value Gaps ...
if (containsFVGs)
{
//
has = HasFVG(iBar, tmpDir, tmpZone, zonesLoopback);
if (has && (!HasDirection(forDir) || tmpDir == forDir))
{
AddIfNotExists(tmpZone, zones);
}
}
}
//
// Validate Zones Exists ...
result = ArraySize(zones);
has = IsValidSize(result);
if (!has)
{
return result;
}
//
// Update Zones Time ...
UpdateToTime(zones, bar.time);
//
// Validate Zones ...
if (validateZones)
{
//
// Apply Zones Validation Here ...
RemoveBreakedZones(zones, true);
}
//
// Validate Zones Range ...
if (zonesRangeValidationFactor > 0)
{
//
double points = GetPoints(bar.symbol);
double minAllowedRange = points * zonesRangeValidationFactor;
ValidateZonesRange(zones, minAllowedRange);
}
//
// Apply Sorting ...
Sort(zones, sortBy, sortDir);
//
// Validate Zones Count ...
if (count > 0)
{
CleanupArray(zones, count);
}
//
result = ArraySize(zones);
//
// Cleanup ...
iBar.Clean();
tmpBar.Clean();
tmpZone.Clean();
tmpPivot.Clean();
//
return result;
}
/**
* Filter Zones ...
*
* @param bar: XOHCL, reference to Specified Filtering Bar ...
* @param source: XBoxZone, reference collection to hold's filtered zones ...
* @param filterDir: ENUM_X_DIRECTION, specified Filter Direction ...
* @param priceType: ENUM_X_PRICE, Specified Bar's Price type to Use for Filtering Zones ...
* @param forDir: ENUM_X_DIRECTION, Specified Zone's Direction to Handle Filtering ...
*
* @return ( int )
*/
int FilterZones(
XOHCL &bar,
XBoxZone &source[],
ENUM_X_DIRECTION filterDir,
ENUM_X_PRICE priceType = X_PRICE_CLOSE,
ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
//
int result = 0;
//
// Prepare ...
XBoxZone tmp[];
Copy(
source,
tmp //
);
XClean(source);
//
// Validate ...
bool has = bar.IsValid() &&
HasChild(tmp) &&
IsXValid(priceType) &&
HasDirection(filterDir);
if (!has)
{
//
XClean(tmp);
return result;
}
//
XBoxZone iBox;
double price = bar.GetPrice(priceType);
bool isBullish = IsXBullish(filterDir);
while (HasChild(tmp))
{
//
iBox = tmp[0];
ArrayRemove(tmp, 0, 1);
//
has =
//
// Direction Pass ...
(!HasDirection(forDir)
? true
: forDir == iBox.dir)
//
&&
//
// Price Pass ...
(isBullish
? price > iBox.lower
: price < iBox.upper);
if (has)
{
AddIfNotExists(iBox, source);
}
//
iBox.Clean();
}
//
result = ArraySize(source);
//
// Cleanup ...
XClean(tmp);
iBox.Clean();
//
return result;
}
//
// Protected ...
protected:
//
//
// Tools / Actions ...
/**
* Check Zones and Manage them ...
*
* @param forBar: XOHCL, reference to Specified Bar ...
* @param _zones: XBoxZone, reference to Specified Zone Collection ...
* @param sortBy: X_SORT_BY_DATE, Sort Zones by ...
* @param sortDir: ENUM_X_DIRECTION, Sort Zones in Direction ...
* @param maxAllowed: int, Specified Max Allowed Zones ...
*/
void CheckZones(
XOHCL &forBar,
XBoxZone &_zones[],
ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
ENUM_X_DIRECTION sortDir = X_DIRECTION_BEARISH,
int maxAllowed = 50 //
)
{
//
// Normalize ...
maxAllowed = NormalizeInt(maxAllowed, 0);
//
// Validate ...
bool has = forBar.IsValid() &&
HasChild(_zones);
if (!has)
{
return;
}
//
// Looping Through Zones ...
int count = ArraySize(_zones);
for (int i = 0; i < count; i++)
{
//
// Check Break ...
has = IsXValid(_zones[i].breakAt);
if (has)
{
continue;
}
//
_zones[i].to = forBar.time;
//
// Check for Test ...
has = IsTested(
forBar,
_zones[i] //
);
if (has)
{
//
Add(
forBar.time,
_zones[i].tests //
);
}
//
// Check for Inside ...
has = IsInside(
forBar,
_zones[i] //
);
if (has)
{
//
Add(
forBar.time,
_zones[i].insides //
);
}
//
// Check for Break ...
has = IsBreaked(
forBar,
_zones[i] //
);
if (has)
{
//
Add(
forBar.time,
_zones[i].breaks //
);
}
//
// Check for Act ...
has = IsActed(
forBar,
_zones[i] //
);
if (has)
{
//
Add(
forBar.time,
_zones[i].acteds //
);
}
//
// Check for FakeBreakes ...
has = IsFakeBreaked(
forBar,
_zones[i] //
);
if (has)
{
//
Add(
forBar.time,
_zones[i].fakeBreaks //
);
}
//
// Check for Valid Breakes ...
has = IsValidBreaked(
forBar,
_zones[i] //
);
if (has)
{
_zones[i].breakAt = forBar.time;
}
}
//
// Apply Sort and Cleanup ...
Sort(
_zones,
sortBy,
sortDir,
maxAllowed //
);
}
//
// Private ...
private:
//
// Props ...
XCBarAnalyser *barAnalyser;
//
// Helpers ...
/**
* Clean Specified Referenced Direction ...
*
* @param dir: ENUM_X_DIRECTION
*/
void CleanDirection(ENUM_X_DIRECTION &dir)
{
dir = X_DIRECTION_NONE;
}
};
//