5119 lines
122 KiB
Plaintext
5119 lines
122 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;
|
|
}
|
|
|
|
/**
|
|
* Check a Bar has Same Sequence Directionalo Bar or Not ...
|
|
*
|
|
* @param bar: XOHCL, Specified Bar ...
|
|
* @param dir: ENUM_X_DIRECTION, Detected Bars Direction ...
|
|
* @param count: int, Number of Same Bars ...
|
|
* @param validation: int, Validation of Same Bars ...
|
|
* @param loopback: int, Loopback for Detection ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasSameBars(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
int &count,
|
|
int validation = 3 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Normalize ...
|
|
count = 0;
|
|
dir = X_DIRECTION_NONE;
|
|
|
|
//
|
|
// Validate ...
|
|
result =
|
|
bar.IsValid() &&
|
|
validation > 1;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOHCL tmpBar;
|
|
bar.GetPreviousBar(tmpBar);
|
|
while ((tmpBar.GetDirection() == bar.GetDirection()) ||
|
|
((bar.IsBullish() && !tmpBar.IsBearish()) ||
|
|
(bar.IsBearish() && !tmpBar.IsBullish())))
|
|
{
|
|
//
|
|
count++;
|
|
tmpBar.GetPreviousBar(tmpBar);
|
|
}
|
|
|
|
//
|
|
// Validate Count ...
|
|
result = count >= validation;
|
|
if (result)
|
|
{
|
|
dir = bar.GetDirection();
|
|
}
|
|
|
|
//
|
|
result =
|
|
result &&
|
|
count > 0 &&
|
|
HasDirection(dir) &&
|
|
count >= validation;
|
|
|
|
//
|
|
tmpBar.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Detect Confluence Statck based on Given Bar ...
|
|
*
|
|
* @param bar: XOHCL, Specified Bar for Start Detection ...
|
|
* @param zone: XBoxZone, confluence Zone, if exists ...
|
|
* @param forDir: ENUM_X_DIRECTION, Specified Required Zone Confluence ...
|
|
* @param retests: int, Number of Zone retesting ...
|
|
* @param loopback: int, Max allowed Loopback for Confluence Detection ...
|
|
* @param rangeLength: int, Loopback for Range Detecttion ...
|
|
* @param upperPriceType: ENUM_X_PRICE, zone upper Price Type ...
|
|
* @param lowerPriceType: ENUM_X_PRICE,
|
|
* @return ( bool )
|
|
*/
|
|
bool HasConfluence(
|
|
XOHCL &bar,
|
|
XBoxZone &confluence,
|
|
ENUM_X_DIRECTION forDir,
|
|
int retests = 2,
|
|
int loopback = 15,
|
|
int rangeLength = 5,
|
|
ENUM_X_PRICE upperPriceType = X_PRICE_HIGH,
|
|
ENUM_X_PRICE lowerPriceType = X_PRICE_LOW //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Normalize ...
|
|
confluence.Clean();
|
|
if (retests < 0)
|
|
{
|
|
retests = 0;
|
|
}
|
|
if (loopback < 2)
|
|
{
|
|
loopback = 2;
|
|
}
|
|
if (rangeLength < 2)
|
|
{
|
|
rangeLength = 2;
|
|
}
|
|
|
|
//
|
|
// Validate ...
|
|
result =
|
|
bar.IsValid() &&
|
|
HasDirection(forDir) &&
|
|
IsXValid(upperPriceType) &&
|
|
IsXValid(lowerPriceType);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Upper and Lower ...
|
|
int mIndex = -1;
|
|
double upper = bar.FindHighest(
|
|
mIndex,
|
|
rangeLength,
|
|
upperPriceType //
|
|
);
|
|
double lower = bar.FindLowest(
|
|
mIndex,
|
|
rangeLength,
|
|
lowerPriceType //
|
|
);
|
|
|
|
//
|
|
// Validate Upper/Lower ...
|
|
result =
|
|
NotEmptyZero(upper) &&
|
|
NotEmptyZero(lower) &&
|
|
upper > lower;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate Confluence ...
|
|
XOHCL tmpBar;
|
|
bool has = false;
|
|
int tmpCount = 0;
|
|
double tmpPrice = EMPTY_VALUE;
|
|
for (int i = bar.Index() + 1; i < bar.Index() + loopback; i++)
|
|
{
|
|
//
|
|
has = tmpBar.Init(bar.symbol, bar.period, i);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
tmpPrice =
|
|
IsXBullish(forDir)
|
|
? tmpBar.GetPrice(upperPriceType)
|
|
: tmpBar.GetPrice(lowerPriceType);
|
|
has =
|
|
NotEmptyZero(tmpPrice) &&
|
|
(IsXBullish(forDir)
|
|
? tmpPrice < upper
|
|
: tmpPrice > lower);
|
|
if (!has)
|
|
{
|
|
//
|
|
tmpBar.Clean();
|
|
|
|
//
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Counting retests ...
|
|
has = IsXBullish(forDir)
|
|
? tmpPrice > lower
|
|
: tmpPrice > upper;
|
|
if (has)
|
|
{
|
|
tmpCount++;
|
|
}
|
|
|
|
//
|
|
tmpBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Validating Retests ...
|
|
if (result && retests > 0)
|
|
{
|
|
//
|
|
result =
|
|
result &&
|
|
tmpCount >= retests;
|
|
}
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = confluence.Init(
|
|
bar.symbol,
|
|
bar.period,
|
|
upper,
|
|
lower,
|
|
GetBarTime(bar.symbol, bar.period, bar.Index() + loopback + 1),
|
|
bar.time,
|
|
forDir,
|
|
"XConfluence",
|
|
NULL // At ...
|
|
);
|
|
|
|
//
|
|
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;
|
|
}
|
|
|
|
bool HasCandlestickDirection(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION forDir,
|
|
bool checkStarPattern = true,
|
|
bool checkPinBarPattern = true,
|
|
bool checkPiercingPattern = true,
|
|
bool checkEngulfedPattern = true,
|
|
bool checkMomentumPattern = true,
|
|
bool checkSignalKeyBarPattern = true //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate ...
|
|
result = bar.IsValid() &&
|
|
HasDirection(forDir);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_DIRECTION tmpDir = X_DIRECTION_NONE;
|
|
|
|
//
|
|
// Star ...
|
|
bool isStarPattern = false;
|
|
if (checkStarPattern)
|
|
{
|
|
//
|
|
isStarPattern =
|
|
IsStarPattern(bar, tmpDir) &&
|
|
tmpDir == forDir;
|
|
}
|
|
|
|
//
|
|
bool isPiercingPattern = false;
|
|
if (checkPiercingPattern)
|
|
{
|
|
//
|
|
isPiercingPattern =
|
|
IsPiercingPattern(bar, tmpDir) &&
|
|
tmpDir == forDir;
|
|
}
|
|
|
|
//
|
|
// Engulfed ...
|
|
bool isEngulfedPattern = false;
|
|
if (checkEngulfedPattern)
|
|
{
|
|
//
|
|
isEngulfedPattern =
|
|
IsEngulfedBar(bar, tmpDir) &&
|
|
tmpDir == forDir;
|
|
}
|
|
|
|
//
|
|
// PinBar ...
|
|
bool isPinBarPattern = true;
|
|
if (checkPinBarPattern)
|
|
{
|
|
//
|
|
isPinBarPattern =
|
|
IsPinnedBar(bar, tmpDir) &&
|
|
tmpDir == forDir;
|
|
}
|
|
|
|
//
|
|
// Signal Key Bar ...
|
|
bool isSignalKeyBarPattern = false;
|
|
if (checkSignalKeyBarPattern)
|
|
{
|
|
//
|
|
isSignalKeyBarPattern =
|
|
IsSignalKeyBarPattern(bar, tmpDir) &&
|
|
tmpDir == forDir;
|
|
}
|
|
|
|
//
|
|
// Momentum Bar ...
|
|
bool isMomentumBarPattern = false;
|
|
if (checkMomentumPattern)
|
|
{
|
|
//
|
|
isMomentumBarPattern =
|
|
IsMomentumBar(bar, tmpDir) &&
|
|
tmpDir == forDir;
|
|
}
|
|
|
|
//
|
|
result =
|
|
isStarPattern ||
|
|
isPinBarPattern ||
|
|
isPiercingPattern ||
|
|
isEngulfedPattern ||
|
|
isMomentumBarPattern ||
|
|
isSignalKeyBarPattern;
|
|
|
|
//
|
|
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,
|
|
int loopback = 500,
|
|
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 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;
|
|
}
|
|
};
|
|
|
|
// |