1367 lines
28 KiB
Plaintext
1367 lines
28 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// --------------------------------------
|
|
// Name: XPOI
|
|
// Description: Bar Analysing ...
|
|
//
|
|
// 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 "../Libraries/x-saherelm.x-poi.lib.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
//
|
|
// Implementations ...
|
|
class XCBarAnalyser : public XCBase
|
|
{
|
|
//
|
|
public:
|
|
//
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
void XCBarAnalyser()
|
|
{
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XCBarAnalyser()
|
|
{
|
|
}
|
|
|
|
//
|
|
// Getter(s) / Setter(s) ...
|
|
|
|
//
|
|
// Providers ...
|
|
|
|
/**
|
|
* Calculate Loopback Data for Specific Bar ...
|
|
*
|
|
* @param index: Integer, Bar Current Index ...
|
|
* @param start: Integer, Start of Loopback ...
|
|
* @param end: Integer, End of Loopback ...
|
|
* @param count: Integer, Number of Calculating Bars ...
|
|
* @param bar: XOHCL instance, Specified Bar ...
|
|
* @param loopback: Integer, Loopback Length ...
|
|
* @return ( bool )
|
|
*/
|
|
bool CalculateLoopbackData(
|
|
int &index,
|
|
int &start,
|
|
int &end,
|
|
int &count,
|
|
XOHCL &bar,
|
|
int loopback = 1 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (loopback < 1)
|
|
{
|
|
loopback = 1;
|
|
}
|
|
|
|
//
|
|
index = -1;
|
|
start = -1;
|
|
end = -1;
|
|
count = -1;
|
|
|
|
//
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
index = bar.Index();
|
|
start = index + 1;
|
|
end = start + loopback;
|
|
count = end - start;
|
|
result = count >= 1;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieve Loopback HH & LL Data ...
|
|
*
|
|
* @param hhIDX: Integer, HH Index ...
|
|
* @param hh: Double, HH Value ...
|
|
* @param llIDX: Integer, LL Index ...
|
|
* @param ll: Double, LL Value ...
|
|
* @param bar: XOHCL instance ...
|
|
* @param loopback: Integer, Loopback Length ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool CalculateLoopbackHLData(
|
|
int &hhIDX,
|
|
double &hh,
|
|
int &llIDX,
|
|
double &ll,
|
|
XOHCL &bar,
|
|
int loopback //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (loopback < 1)
|
|
{
|
|
loopback = 1;
|
|
}
|
|
|
|
//
|
|
int index = -1;
|
|
int start = -1;
|
|
int end = -1;
|
|
int count = -1;
|
|
result = CalculateLoopbackData(
|
|
index,
|
|
start,
|
|
end,
|
|
count,
|
|
bar,
|
|
loopback //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
hhIDX = iHighest(
|
|
bar.symbol,
|
|
bar.period,
|
|
MODE_HIGH,
|
|
count,
|
|
start //
|
|
);
|
|
result = IsValidIndex(hhIDX);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
llIDX = iLowest(
|
|
bar.symbol,
|
|
bar.period,
|
|
MODE_LOW,
|
|
count,
|
|
start //
|
|
);
|
|
result = IsValidIndex(llIDX);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
hh = iHigh(
|
|
bar.symbol,
|
|
bar.period,
|
|
hhIDX //
|
|
);
|
|
ll = iLow(
|
|
bar.symbol,
|
|
bar.period,
|
|
llIDX //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Fill Prices ...
|
|
*
|
|
* @param prices: double collection
|
|
* @param bar: XOHCL instance reference, Provides Start ...
|
|
* @param loopback: int, Specified How Many Bars Process ...
|
|
* @param type: ENUM_X_PRICE member, Specified Price Type ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool FillPrice(
|
|
double &prices[],
|
|
XOHCL &bar,
|
|
int loopback = 14,
|
|
ENUM_X_PRICE type = X_PRICE_CLOSE //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean(prices);
|
|
|
|
//
|
|
loopback = NormalizeInt(loopback, 1);
|
|
|
|
//
|
|
result = IsValid(type) &&
|
|
bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string symbol = bar.symbol;
|
|
ENUM_TIMEFRAMES period = bar.period;
|
|
|
|
//
|
|
// Calculate Loopback Data ...
|
|
int index = -1;
|
|
int start = -1;
|
|
int end = -1;
|
|
int count = -1;
|
|
result = CalculateLoopbackData(
|
|
index,
|
|
start,
|
|
end,
|
|
count,
|
|
bar,
|
|
loopback //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = index; i < count; i++)
|
|
{
|
|
//
|
|
XOHCL iBar;
|
|
bool isInited = iBar.Init(
|
|
symbol,
|
|
period,
|
|
i //
|
|
);
|
|
if (!isInited)
|
|
{
|
|
//
|
|
iBar.Clean();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double iPrice = iBar.GetPrice(type);
|
|
|
|
//
|
|
isInited = NotEmptyZero(iPrice);
|
|
if (isInited)
|
|
{
|
|
//
|
|
Add(
|
|
iPrice,
|
|
prices //
|
|
);
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
result = ArraySize(prices) == count;
|
|
if (!result)
|
|
{
|
|
Clean(prices);
|
|
}
|
|
|
|
//
|
|
ArrayReverse(prices);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Calculate Price Range ...
|
|
*
|
|
* @param ranges: double Collection, Calculated Ranges ...
|
|
* @param bar: XOHCL instance Reference, Start Bar ...
|
|
* @param forceBody: bool, Force to Calculate Range using Body ...
|
|
* @param loopback: int, loopback period for Range Calculated ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int GetPriceRange(
|
|
double &ranges[],
|
|
XOHCL &bar,
|
|
bool forceBody = false,
|
|
int loopback = 14 //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(ranges);
|
|
|
|
//
|
|
loopback = NormalizeInt(loopback, 1);
|
|
|
|
//
|
|
if (!bar.IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool has = false;
|
|
|
|
//
|
|
// Select Uppers Prices ...
|
|
double uppers[];
|
|
int uppersCount = 0;
|
|
if (forceBody)
|
|
{
|
|
//
|
|
has = FillPrice(
|
|
uppers,
|
|
bar,
|
|
loopback,
|
|
X_PRICE_UP //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
has = FillPrice(
|
|
uppers,
|
|
bar,
|
|
loopback,
|
|
X_PRICE_HIGH //
|
|
);
|
|
}
|
|
if (has)
|
|
{
|
|
uppersCount = ArraySize(uppers);
|
|
}
|
|
|
|
//
|
|
// Select Lower Prices ...
|
|
double lowers[];
|
|
int lowersCount = 0;
|
|
if (forceBody)
|
|
{
|
|
//
|
|
has = FillPrice(
|
|
lowers,
|
|
bar,
|
|
loopback,
|
|
X_PRICE_DOWN //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
has = FillPrice(
|
|
lowers,
|
|
bar,
|
|
loopback,
|
|
X_PRICE_LOW //
|
|
);
|
|
}
|
|
if (has)
|
|
{
|
|
lowersCount = ArraySize(lowers);
|
|
}
|
|
|
|
//
|
|
// Validate Filled Prices ...
|
|
has =
|
|
IsValidSize(uppersCount) &&
|
|
IsValidSize(lowersCount) &&
|
|
uppersCount == lowersCount;
|
|
if (!has)
|
|
{
|
|
//
|
|
Clean(uppers);
|
|
Clean(lowers);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < uppersCount; i++)
|
|
{
|
|
//
|
|
double iValue = lowers[i] + (uppers[i] - lowers[i]);
|
|
|
|
//
|
|
Add(
|
|
iValue,
|
|
ranges //
|
|
);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(ranges);
|
|
ArrayReverse(ranges);
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
Clean(uppers);
|
|
Clean(lowers);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Calculate Price Change Percent ...
|
|
*
|
|
* @param bar: XOHCL instance Reference, Start Bar ...
|
|
* @param type: ENUM_X_PRICE member, Specified Price Type ...
|
|
* @param loopback: int, loopback period for Range Calculated ...
|
|
*
|
|
* @return ( double )
|
|
*/
|
|
double GetPriceChangeRate(
|
|
XOHCL &bar,
|
|
ENUM_X_PRICE type = X_PRICE_CLOSE,
|
|
int loopback = 14 //
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
bool has = false;
|
|
|
|
//
|
|
loopback = NormalizeInt(loopback, 1);
|
|
|
|
//
|
|
has =
|
|
IsValid(type) &&
|
|
bar.IsValid();
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOHCL oldBar;
|
|
has = oldBar.Init(
|
|
bar.symbol,
|
|
bar.period,
|
|
bar.Index() + loopback //
|
|
);
|
|
if (!has)
|
|
{
|
|
//
|
|
oldBar.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
double newPrice = bar.GetPrice(type);
|
|
double oldPrice = oldBar.GetPrice(type);
|
|
|
|
//
|
|
result = ((newPrice - oldPrice) / oldPrice) / 100;
|
|
|
|
//
|
|
oldBar.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Detect Same Bars ...
|
|
*
|
|
* @param bar: XOHCL instance, Start Bar ...
|
|
* @param dir: ENUM_X_DIRECTION member, Holds Result Direction ...
|
|
* @param index: Integer, Holds Result Index ...
|
|
* @param sameBars: Iteger, Required Same Bar ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasSameBars(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
int &index,
|
|
int sameBars = 3 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
|
|
//
|
|
if (sameBars < 1)
|
|
{
|
|
sameBars = 1;
|
|
}
|
|
|
|
//
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
index = bar.Index() + 1;
|
|
bool canContinue = true;
|
|
int sameBullishBarsFounded = 0;
|
|
int sameBearishBarsFounded = 0;
|
|
bool canContinueForBullish = true;
|
|
bool canContinueForBearish = true;
|
|
while (canContinue)
|
|
{
|
|
//
|
|
XOHCL iBar;
|
|
result = iBar.Init(
|
|
bar.symbol,
|
|
bar.period,
|
|
index //
|
|
);
|
|
if (!result)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
bool isBullish = iBar.IsBullish();
|
|
bool isBearish = iBar.IsBearish();
|
|
result = isBullish ||
|
|
isBearish;
|
|
if (!result)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
if (isBullish)
|
|
{
|
|
//
|
|
sameBullishBarsFounded++;
|
|
canContinueForBearish = false;
|
|
}
|
|
else if (isBearish)
|
|
{
|
|
//
|
|
sameBearishBarsFounded++;
|
|
canContinueForBullish = false;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
sameBullishBarsFounded = 0;
|
|
sameBearishBarsFounded = 0;
|
|
canContinueForBullish = false;
|
|
canContinueForBearish = false;
|
|
}
|
|
|
|
//
|
|
result = canContinueForBullish ||
|
|
canContinueForBearish;
|
|
if (!result)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
canContinueForBullish =
|
|
canContinueForBullish &&
|
|
sameBullishBarsFounded > 0;
|
|
|
|
//
|
|
canContinueForBearish =
|
|
canContinueForBearish &&
|
|
sameBearishBarsFounded > 0;
|
|
|
|
//
|
|
canContinue =
|
|
canContinueForBullish ||
|
|
canContinueForBearish;
|
|
if (canContinue)
|
|
{
|
|
index++;
|
|
}
|
|
}
|
|
|
|
//
|
|
bool hasBullishSameBars =
|
|
sameBullishBarsFounded >= sameBars;
|
|
|
|
//
|
|
bool hasBearishSameBars =
|
|
sameBearishBarsFounded >= sameBars;
|
|
|
|
//
|
|
result = hasBullishSameBars ||
|
|
hasBearishSameBars;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
index--;
|
|
|
|
//
|
|
dir =
|
|
hasBullishSameBars
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Fibonacci Level 382 Pressure ...
|
|
*
|
|
* @param bar: XOHCL instance ...
|
|
* @param dir: ENUM_X_DIRECTION member ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasFiboPressure(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION forDir,
|
|
ENUM_X_FIBO_LEVELS level //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = bar.IsValid() &&
|
|
HasDirection(forDir);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isBullish = IsBullish(forDir);
|
|
bool isBearish = IsBearish(forDir);
|
|
|
|
//
|
|
double fiboLevelValue = GetFibonacciLevel(
|
|
bar.high,
|
|
bar.low,
|
|
level,
|
|
forDir //
|
|
);
|
|
|
|
//
|
|
bool isBullishPressured =
|
|
isBullish &&
|
|
bar.GetDown() > fiboLevelValue;
|
|
bool isBearishPressured =
|
|
isBearish &&
|
|
bar.GetUp() < fiboLevelValue;
|
|
|
|
//
|
|
result = isBullishPressured ||
|
|
isBearishPressured;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Detect Big Price Change Happens or not ...
|
|
*
|
|
* @param bar: XOHCL instance reference, Start Bar ...
|
|
* @param dir: ENUM_X_DIRECTION member, Event Direction ...
|
|
* @param type: ENUM_X_PRICE member, Price Type ...
|
|
* @param forcBodyInRange: bool, Use Body in Range Detection ...
|
|
* @param rangeExtendMultiplier: double, a Multiplier for Checking Price Range ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsBigChange(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
ENUM_X_PRICE type = X_PRICE_CLOSE,
|
|
bool forcBodyInRange = true,
|
|
double rangeExtendMultiplier = 1.0 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
rangeExtendMultiplier = NormalizeDouble(rangeExtendMultiplier, 1);
|
|
|
|
//
|
|
result =
|
|
bar.IsValid() &&
|
|
IsValid(type);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double minPriceChangePercent = 0;
|
|
int loopback = GetPeriodSharpData(
|
|
minPriceChangePercent,
|
|
bar.period //
|
|
);
|
|
|
|
//
|
|
double ranges[];
|
|
int rangesCount = GetPriceRange(
|
|
ranges,
|
|
bar,
|
|
forcBodyInRange,
|
|
loopback //
|
|
);
|
|
result = IsValidSize(rangesCount);
|
|
if (!result)
|
|
{
|
|
//
|
|
Clean(ranges);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Ranges ...
|
|
double rangeMax = GetMax(ranges);
|
|
double rangeMin = GetMin(ranges);
|
|
double rangeAverage = GetAverage(ranges);
|
|
double priceChangePercent = GetPriceChangeRate(
|
|
bar,
|
|
type,
|
|
loopback //
|
|
);
|
|
|
|
//
|
|
// Calculate HH and LL ...
|
|
double hh = 0;
|
|
double ll = 0;
|
|
int hhIDX = -1;
|
|
int llIDX = -1;
|
|
result = CalculateLoopbackHLData(
|
|
hhIDX,
|
|
hh,
|
|
llIDX,
|
|
ll,
|
|
bar,
|
|
loopback //
|
|
);
|
|
result =
|
|
result &&
|
|
ll > 0 &&
|
|
hh > ll &&
|
|
IsValidIndex(llIDX) &&
|
|
IsValidIndex(hhIDX);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int hhDiff = (hhIDX - bar.Index());
|
|
int llDiff = (llIDX - bar.Index());
|
|
|
|
//
|
|
bool isBullish =
|
|
//
|
|
hhDiff <= 1 &&
|
|
llIDX > hhIDX &&
|
|
bar.close >= hh &&
|
|
priceChangePercent > 0 &&
|
|
priceChangePercent >= minPriceChangePercent &&
|
|
bar.high > (rangeAverage * rangeExtendMultiplier)
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isBearish =
|
|
//
|
|
llDiff <= 1 &&
|
|
hhIDX > llIDX &&
|
|
bar.close <= ll &&
|
|
priceChangePercent < 0 &&
|
|
bar.low < (rangeAverage * rangeExtendMultiplier) &&
|
|
MathAbs(priceChangePercent) >= minPriceChangePercent
|
|
//
|
|
;
|
|
|
|
//
|
|
result = isBullish ||
|
|
isBearish;
|
|
if (result)
|
|
{
|
|
//
|
|
dir = isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
Clean(ranges);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Actions ...
|
|
|
|
/**
|
|
* Detect Spike ...
|
|
*
|
|
* @param bar: XOHCL instance reference, Start Bar ...
|
|
* @param dir: ENUM_X_DIRECTION member, Event Direction ...
|
|
* @param type: ENUM_X_PRICE member, Price Type ...
|
|
* @param forcBodyInRange: bool, Use Body in Range Detection ...
|
|
* @param rangeExtendMultiplier: double, a Multiplier for Checking Price Range ...
|
|
* @param sameBars: int, Specified How Many Same Bars Required for an Spike ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsSpike(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
ENUM_X_PRICE type = X_PRICE_CLOSE,
|
|
bool forcBodyInRange = true,
|
|
double rangeExtendMultiplier = 1.0,
|
|
int sameBars = 3 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
sameBars = NormalizeInt(sameBars, 2);
|
|
|
|
//
|
|
result = IsValid(type) &&
|
|
bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// First We Have to Has a Big Change ...
|
|
result = IsBigChange(
|
|
bar,
|
|
dir,
|
|
type,
|
|
forcBodyInRange,
|
|
rangeExtendMultiplier //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Has Same Bars ...
|
|
int sameBarsIndex = -1;
|
|
ENUM_X_DIRECTION sameBarsDir;
|
|
result = HasSameBars(
|
|
bar,
|
|
sameBarsDir,
|
|
sameBarsIndex,
|
|
sameBars //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Same Bars Direction sames To Price Change ...
|
|
result = sameBarsDir == dir;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Candlestick ...
|
|
|
|
/**
|
|
* Check Specified Bar is Doji Pattern or not ...
|
|
*
|
|
* @param bar: XOHCL instance reference, Specified Bar to Check ...
|
|
* @param dir: ENUM_X_DIRECTION member, Specified Doji Direction ...
|
|
* @param shadowMultiplier: double, Specified Shadow Multiplier ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsDoji(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
double shadowMultiplier = 1.5 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
shadowMultiplier = NormalizeDouble(shadowMultiplier, 1.5, 3);
|
|
|
|
//
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double body = bar.GetBody();
|
|
double range = bar.GetRange();
|
|
|
|
//
|
|
result =
|
|
body > 0 &&
|
|
range > 0
|
|
? (body / range < 0.1)
|
|
: false;
|
|
if (result)
|
|
{
|
|
//
|
|
dir =
|
|
bar.GetLowShadow() >= (bar.GetHighShadow() * shadowMultiplier)
|
|
? X_DIRECTION_BULLISH
|
|
: bar.GetHighShadow() >= (bar.GetLowShadow() * shadowMultiplier)
|
|
? X_DIRECTION_BEARISH
|
|
: X_DIRECTION_NONE;
|
|
//
|
|
result = HasDirection(dir);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Specified Bar is Hammer Pattern or not ...
|
|
*
|
|
* @param bar: XOHCL instance reference, Specified Bar to Check ...
|
|
* @param dir: ENUM_X_DIRECTION member, Specified Doji Direction ...
|
|
* @param bodyMultiplier: double, Specified Body Multiplier ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsHammer(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
double bodyMultiplier = 2.0 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
bodyMultiplier = NormalizeDouble(bodyMultiplier, 1.5, 3);
|
|
|
|
//
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double body = bar.GetBody();
|
|
double lShadow = bar.GetLowShadow();
|
|
double hShadow = bar.GetHighShadow();
|
|
|
|
//
|
|
bool isBullish =
|
|
hShadow < body &&
|
|
lShadow > (bodyMultiplier * body);
|
|
|
|
//
|
|
bool isBearish =
|
|
lShadow < body &&
|
|
hShadow > (bodyMultiplier * body);
|
|
|
|
//
|
|
result = isBullish ||
|
|
isBearish;
|
|
if (result)
|
|
{
|
|
//
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Specified Bar is Engulfed Prev Bar or not ...
|
|
*
|
|
* @param bar: XOHCL instance reference, Specified Bar to Check ...
|
|
* @param dir: ENUM_X_DIRECTION member, Specified Doji Direction ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsEngulfed(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
|
|
//
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOHCL pBar;
|
|
result = bar.GetPreviousBar(pBar);
|
|
if (!result)
|
|
{
|
|
//
|
|
pBar.Clean();
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = bar.GetBody() > pBar.GetBody();
|
|
if (!result)
|
|
{
|
|
//
|
|
pBar.Clean();
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isBullish =
|
|
bar.IsBullish() &&
|
|
pBar.IsBearish() &&
|
|
bar.GetDown() <= pBar.GetDown() &&
|
|
bar.GetUp() > pBar.GetUp();
|
|
|
|
//
|
|
bool isBearish =
|
|
bar.IsBearish() &&
|
|
pBar.IsBullish() &&
|
|
bar.GetUp() >= pBar.GetUp() &&
|
|
bar.GetDown() < pBar.GetDown();
|
|
|
|
//
|
|
result = isBullish ||
|
|
isBearish;
|
|
if (result)
|
|
{
|
|
//
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
pBar.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Specified Bar is Momentum Bar or not ...
|
|
*
|
|
* @param bar: XOHCL instance reference, Specified Bar to Check ...
|
|
* @param dir: ENUM_X_DIRECTION member, Specified Doji Direction ...
|
|
* @param loopback: int, Specified Loopback ...
|
|
* @param rangeExtendMultiplier: double, a Multiplier for Checking Price Range ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsMomentum(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
int loopback = 2,
|
|
double rangeExtendMultiplier = 1.5 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
loopback = NormalizeInt(loopback, 1);
|
|
rangeExtendMultiplier = NormalizeDouble(rangeExtendMultiplier, 1);
|
|
|
|
//
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Bar Must Engulfed ...
|
|
result = IsEngulfed(
|
|
bar,
|
|
dir //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate HH and LL ...
|
|
double hh = 0;
|
|
double ll = 0;
|
|
int hhIDX = -1;
|
|
int llIDX = -1;
|
|
result = CalculateLoopbackHLData(
|
|
hhIDX,
|
|
hh,
|
|
llIDX,
|
|
ll,
|
|
bar,
|
|
loopback //
|
|
);
|
|
result =
|
|
result &&
|
|
ll > 0 &&
|
|
hh > ll &&
|
|
IsValidIndex(llIDX) &&
|
|
IsValidIndex(hhIDX);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isBullish =
|
|
bar.low > ll &&
|
|
IsBullish(dir) &&
|
|
bar.close >= hh;
|
|
|
|
//
|
|
bool isBearish =
|
|
bar.high < hh &&
|
|
IsBearish(dir) &&
|
|
bar.close <= ll;
|
|
|
|
//
|
|
result = isBullish ||
|
|
isBearish;
|
|
if (result)
|
|
{
|
|
//
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Specified Bar is Pullback or not ...
|
|
*
|
|
* @param bar: XOHCL instance reference, Specified Bar to Check ...
|
|
* @param dir: ENUM_X_DIRECTION member, Specified Doji Direction ...
|
|
* @param loopback: int, Specified Loopback ...
|
|
* @param forceBody: bool, Force Using Body ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsPullback(
|
|
XOHCL &bar,
|
|
ENUM_X_DIRECTION &dir,
|
|
int loopback = 3,
|
|
bool forceBody = false //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
loopback = NormalizeInt(loopback, 3);
|
|
|
|
//
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Bar Must Engulfed ...
|
|
result = IsEngulfed(
|
|
bar,
|
|
dir //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate HH and LL ...
|
|
double hh = 0;
|
|
double ll = 0;
|
|
int hhIDX = -1;
|
|
int llIDX = -1;
|
|
result = CalculateLoopbackHLData(
|
|
hhIDX,
|
|
hh,
|
|
llIDX,
|
|
ll,
|
|
bar,
|
|
loopback //
|
|
);
|
|
result =
|
|
result &&
|
|
ll > 0 &&
|
|
hh > ll &&
|
|
IsValidIndex(llIDX) &&
|
|
IsValidIndex(hhIDX);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isBullish =
|
|
bar.high > hh &&
|
|
bar.low > ll;
|
|
|
|
//
|
|
bool isBearish =
|
|
bar.high < hh &&
|
|
bar.low < ll;
|
|
|
|
//
|
|
result = isBullish ||
|
|
isBearish;
|
|
if (result)
|
|
{
|
|
//
|
|
if (forceBody)
|
|
{
|
|
//
|
|
isBullish =
|
|
isBullish &&
|
|
bar.GetUp() > hh &&
|
|
bar.GetDown() > ll &&
|
|
llIDX == bar.Index() + 1;
|
|
|
|
//
|
|
isBearish =
|
|
isBearish &&
|
|
bar.GetUp() < hh &&
|
|
bar.GetDown() < ll &&
|
|
hhIDX == bar.Index() + 1;
|
|
|
|
//
|
|
result = isBullish ||
|
|
isBearish;
|
|
}
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
//
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
protected:
|
|
//
|
|
|
|
//
|
|
private:
|
|
//
|
|
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Actions ...
|
|
|
|
//
|
|
};
|
|
|
|
// |