2231 lines
55 KiB
Plaintext
2231 lines
55 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class
|
|
// -------------------------------------------------
|
|
// Name: XCXFIMAPoiDetector ...
|
|
// Description: XFIMA POI Detector Class ...
|
|
//
|
|
//
|
|
// Maintainer:
|
|
// ------------
|
|
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
|
//
|
|
//////////////////////////////////////////////////////
|
|
//
|
|
// Global Properties ...
|
|
#property library
|
|
#property copyright "Copyright 2023, SaherElm IT Center"
|
|
#property link "https://saherelm.ir"
|
|
#property version "1.00"
|
|
#property description "SaherElm XFIMA POI Detector Class"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../../Classes/x-saherelm.base.class.mq5"
|
|
#include "../../Classes/x-saherelm.x-bar.analyser.class.mq5"
|
|
#include "../../Classes/x-saherelm.x-market.pattern.class.mq5"
|
|
#include "../Libraries/x-saherelm.xfima.lib.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
enum ENUM_XFIMA_PIVOTS
|
|
{
|
|
XFIMA_PIVOT_NONE = 0,
|
|
XFIMA_PIVOT_PV = 1,
|
|
XFIMA_PIVOT_ATR = 2,
|
|
XFIMA_PIVOT_ZIGZAG = 3,
|
|
XFIMA_PIVOT_MA_CROSS = 4,
|
|
XFIMA_PIVOT_HK_SWITCH = 5,
|
|
XFIMA_PIVOT_SAR_SWITCH = 6,
|
|
XFIMA_PIVOT_BOUNDARY_SWITCH = 7,
|
|
};
|
|
|
|
bool IsXValid(ENUM_XFIMA_PIVOTS value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = value != XFIMA_PIVOT_NONE;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
string ToXString(ENUM_XFIMA_PIVOTS value)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsXValid(value))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
switch (value)
|
|
{
|
|
//
|
|
case XFIMA_PIVOT_NONE:
|
|
result = "NONE";
|
|
break;
|
|
|
|
//
|
|
case XFIMA_PIVOT_PV:
|
|
result = "XPV";
|
|
break;
|
|
|
|
//
|
|
case XFIMA_PIVOT_ATR:
|
|
result = "XATR";
|
|
break;
|
|
|
|
//
|
|
case XFIMA_PIVOT_ZIGZAG:
|
|
result = "XZGP";
|
|
break;
|
|
|
|
//
|
|
case XFIMA_PIVOT_MA_CROSS:
|
|
result = "XMACRS";
|
|
break;
|
|
|
|
//
|
|
case XFIMA_PIVOT_HK_SWITCH:
|
|
result = "XHKSWCH";
|
|
break;
|
|
|
|
//
|
|
case XFIMA_PIVOT_SAR_SWITCH:
|
|
result = "XSARSWCH";
|
|
break;
|
|
|
|
//
|
|
case XFIMA_PIVOT_BOUNDARY_SWITCH:
|
|
result = "XBNDSWCH";
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
ENUM_XFIMA_PIVOTS ToXFIMAPivots(string value)
|
|
{
|
|
//
|
|
ENUM_XFIMA_PIVOTS result = XFIMA_PIVOT_NONE;
|
|
|
|
//
|
|
if (!IsXValid(value))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (value == ToXString(XFIMA_PIVOT_NONE))
|
|
{
|
|
result = XFIMA_PIVOT_NONE;
|
|
}
|
|
else if (value == ToXString(XFIMA_PIVOT_PV))
|
|
{
|
|
result = XFIMA_PIVOT_PV;
|
|
}
|
|
else if (value == ToXString(XFIMA_PIVOT_ATR))
|
|
{
|
|
result = XFIMA_PIVOT_ATR;
|
|
}
|
|
else if (value == ToXString(XFIMA_PIVOT_ZIGZAG))
|
|
{
|
|
result = XFIMA_PIVOT_ZIGZAG;
|
|
}
|
|
else if (value == ToXString(XFIMA_PIVOT_MA_CROSS))
|
|
{
|
|
result = XFIMA_PIVOT_MA_CROSS;
|
|
}
|
|
else if (value == ToXString(XFIMA_PIVOT_HK_SWITCH))
|
|
{
|
|
result = XFIMA_PIVOT_HK_SWITCH;
|
|
}
|
|
else if (value == ToXString(XFIMA_PIVOT_SAR_SWITCH))
|
|
{
|
|
result = XFIMA_PIVOT_SAR_SWITCH;
|
|
}
|
|
else if (value == ToXString(XFIMA_PIVOT_BOUNDARY_SWITCH))
|
|
{
|
|
result = XFIMA_PIVOT_BOUNDARY_SWITCH;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Implementation ...
|
|
class XCXFIMAPOIDetector : public XCMarketPatternDetector
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
string symbol;
|
|
ENUM_TIMEFRAMES period;
|
|
|
|
//
|
|
XCBarAnalyser *barAnalyser; // Bar Analyser ...
|
|
XCXFIMAHelper *fimaHelper; // XFIMA Helper ...
|
|
XCXFIMAHelperParser *fimaParser; // XFIMA Helper Parser ...
|
|
|
|
//
|
|
// Constructor ...
|
|
XCXFIMAPOIDetector(
|
|
XCXFIMAHelper *_fimaHelper,
|
|
XCXFIMAHelperParser *_fimaParser //
|
|
) : XCMarketPatternDetector()
|
|
{
|
|
//
|
|
barAnalyser = new XCBarAnalyser();
|
|
fimaHelper = _fimaHelper;
|
|
fimaParser = _fimaParser;
|
|
|
|
//
|
|
symbol = fimaHelper.GetSymbol();
|
|
period = fimaHelper.GetPeriod();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCXFIMAPOIDetector()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
//
|
|
// Actions and Tools ...
|
|
|
|
//
|
|
// Detectors ...
|
|
|
|
//
|
|
// XPV ...
|
|
|
|
/**
|
|
* Check Conditions to Detecte XPV based Consolidation Zone ...
|
|
*
|
|
* @param zone: XBoxZone, reference to holds detected zone ...
|
|
* @param barIndex: int, Specified Bar Index ...
|
|
* @param forcePVBreak: bool, force to has PV Side Break ...
|
|
* @param forceHasSarSwitch: bool, force to has SAR Switche ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool DetectPVConsolidationZone(
|
|
XBoxZone &zone,
|
|
int barIndex = 0,
|
|
bool forcePVBreak = false,
|
|
bool forceHasSarSwitch = false //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
zone.Clean();
|
|
|
|
//
|
|
// Normalize ...
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
result = fimaParser.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
|
|
//
|
|
bool isPeakSame = fimaParser.IsPeakSame(barIndex);
|
|
bool isValeSame = fimaParser.IsValeSame(barIndex);
|
|
result = !(isPeakSame &&
|
|
isValeSame);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double iPeak = EMPTY_VALUE;
|
|
double iVale = EMPTY_VALUE;
|
|
|
|
//
|
|
int idx = barIndex;
|
|
bool canLookup = true;
|
|
while (canLookup)
|
|
{
|
|
//
|
|
idx++;
|
|
isPeakSame = fimaParser.IsPeakSame(idx);
|
|
isValeSame = fimaParser.IsValeSame(idx);
|
|
result = isPeakSame &&
|
|
isValeSame;
|
|
if (!result)
|
|
{
|
|
//
|
|
zone.from = GetBarTime(symbol, period, idx);
|
|
break;
|
|
}
|
|
|
|
//
|
|
if (idx == barIndex + 1)
|
|
{
|
|
//
|
|
zone.to = GetBarTime(
|
|
symbol,
|
|
period,
|
|
idx //
|
|
);
|
|
}
|
|
|
|
//
|
|
zone.from = GetBarTime(
|
|
symbol,
|
|
period,
|
|
idx //
|
|
);
|
|
|
|
//
|
|
iPeak = fimaHelper.GetPeak(idx);
|
|
iVale = fimaHelper.GetVale(idx);
|
|
|
|
//
|
|
zone.lower =
|
|
!NotEmptyZero(zone.lower)
|
|
? iVale
|
|
: MathMin(zone.lower, iVale);
|
|
|
|
//
|
|
zone.upper =
|
|
!NotEmptyZero(zone.upper)
|
|
? iPeak
|
|
: MathMax(zone.upper, iPeak);
|
|
}
|
|
|
|
//
|
|
// Validating Zone Params ...
|
|
result =
|
|
//
|
|
IsXValid(zone.to) &&
|
|
IsXValid(zone.from) &&
|
|
zone.from < zone.to &&
|
|
//
|
|
NotEmptyZero(zone.lower) &&
|
|
NotEmptyZero(zone.upper) &&
|
|
zone.upper > zone.lower
|
|
//
|
|
;
|
|
|
|
//
|
|
// Prepare Zone ...
|
|
if (result)
|
|
{
|
|
//
|
|
iPeak = fimaHelper.GetPeak(barIndex);
|
|
iVale = fimaHelper.GetVale(barIndex);
|
|
bool isPeakOverLast = fimaParser.IsPeakOverLast(barIndex);
|
|
bool isValeUnderLast = fimaParser.IsValeUnderLast(barIndex);
|
|
bool isValeBreak = iVale < zone.lower;
|
|
bool isPeakBreak = iPeak > zone.upper;
|
|
bool isPVBreak = isPeakBreak ||
|
|
isValeBreak;
|
|
|
|
//
|
|
zone.at = GetBarTime(symbol, period, barIndex);
|
|
zone.symbol = symbol;
|
|
zone.period = period;
|
|
zone.dir = (isPeakOverLast && !isValeUnderLast && isPVBreak)
|
|
? X_DIRECTION_BULLISH
|
|
: (isValeUnderLast && !isPeakOverLast && isPVBreak)
|
|
? X_DIRECTION_BEARISH
|
|
: X_DIRECTION_NONE;
|
|
zone.type = ToXString(X_BAR_PATTERN_CONSOLIDATION);
|
|
|
|
//
|
|
// Refine Zone Upper and Lower ...
|
|
if (zone.IsValid())
|
|
{
|
|
//
|
|
XOHCL iBar;
|
|
int zoneLoopback = zone.FromIndex() - zone.ToIndex();
|
|
result = zone.ToBar(iBar) &&
|
|
IsValidSize(zoneLoopback);
|
|
if (result)
|
|
{
|
|
//
|
|
double zoneLL = iBar.FindLowest(zoneLoopback, MODE_LOW);
|
|
double zoneHH = iBar.FindHighest(zoneLoopback, MODE_HIGH);
|
|
result = NotEmptyZero(zoneLL) &&
|
|
NotEmptyZero(zoneHH);
|
|
if (result)
|
|
{
|
|
//
|
|
zone.lower = zone.lower == zoneLL
|
|
? zone.lower
|
|
: MathMax(zone.lower, zoneLL);
|
|
|
|
//
|
|
zone.upper = zone.upper == zoneHH
|
|
? zone.upper
|
|
: MathMin(zone.upper, zoneHH);
|
|
}
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Validate ...
|
|
result = zone.IsValid();
|
|
}
|
|
|
|
//
|
|
// Validate Zones From ...
|
|
if (result)
|
|
{
|
|
//
|
|
int idx = zone.FromIndex();
|
|
|
|
//
|
|
iPeak = fimaHelper.GetPeak(idx);
|
|
iVale = fimaHelper.GetVale(idx);
|
|
|
|
//
|
|
bool isValeOverLast = fimaParser.IsValeOverLast(idx);
|
|
bool isPeakUnderLast = fimaParser.IsPeakUnderLast(idx);
|
|
|
|
//
|
|
result =
|
|
result &&
|
|
(zone.IsBullish()
|
|
? !isPeakUnderLast
|
|
: zone.IsBearish()
|
|
? !isValeOverLast
|
|
: false);
|
|
}
|
|
|
|
//
|
|
// Apply Forces ...
|
|
|
|
//
|
|
// PV Break ...
|
|
// Peak or Vale must Breaked at Bar ...
|
|
if (result && forcePVBreak)
|
|
{
|
|
//
|
|
bool isPVBreaked =
|
|
(zone.IsBullish() && fimaParser.IsPeakOverLast(barIndex)) ||
|
|
(zone.IsBearish() && fimaParser.IsValeUnderLast(barIndex));
|
|
|
|
//
|
|
result = result &&
|
|
isPVBreaked;
|
|
}
|
|
|
|
//
|
|
// Other Loop based Forces ...
|
|
if (result && (forceHasSarSwitch))
|
|
{
|
|
|
|
//
|
|
bool hasSarSwitch = false;
|
|
|
|
//
|
|
int start = zone.ToIndex();
|
|
int end = zone.FromIndex();
|
|
for (int i = start; i <= end; i++)
|
|
{
|
|
//
|
|
// Check Sar Switches ...
|
|
if (forceHasSarSwitch && !hasSarSwitch)
|
|
{
|
|
//
|
|
hasSarSwitch =
|
|
zone.IsBullish()
|
|
? fimaParser.IsSARSwitchedToBullish(i)
|
|
: fimaParser.IsSARSwitchedToBearish(i);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate Forces ...
|
|
if (forceHasSarSwitch)
|
|
{
|
|
//
|
|
result =
|
|
result &&
|
|
hasSarSwitch;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
if (!result)
|
|
{
|
|
zone.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Detect XPV Zone ...
|
|
*
|
|
* @param zone: XBoxZone, reference to Holds detected Zone ...
|
|
* @param barIndex: int, Specified Bar Index ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool DetectXPVZone(
|
|
XBoxZone &zone,
|
|
int barIndex = 0 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
zone.Clean();
|
|
|
|
//
|
|
// Normalize ...
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
result = fimaParser.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
|
|
//
|
|
XOHCL bar;
|
|
result = bar.Init(fimaParser.symbol, fimaParser.period, barIndex);
|
|
if (!result)
|
|
{
|
|
//
|
|
bar.Clean();
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Vale ...
|
|
bool isValeOverLast = fimaParser.IsValeOverLast(barIndex);
|
|
bool isValeUnderLast = fimaParser.IsValeUnderLast(barIndex);
|
|
bool isNewVale = isValeOverLast || isValeUnderLast;
|
|
|
|
//
|
|
// Peak ...
|
|
bool isPeakOverLast = fimaParser.IsPeakOverLast(barIndex);
|
|
bool isPeakUnderLast = fimaParser.IsPeakUnderLast(barIndex);
|
|
bool isNewPeak = isPeakOverLast || isPeakUnderLast;
|
|
|
|
//
|
|
// Detect new Peak or Vale ...
|
|
result = isNewVale || isNewPeak;
|
|
|
|
//
|
|
// Count Same Peaks and Same Vales from Index of Prev ...
|
|
int pIDX = barIndex + 1;
|
|
int samePeaks = fimaParser.CountSamePeaks(pIDX);
|
|
int sameVales = fimaParser.CountSameVales(pIDX);
|
|
result =
|
|
result &&
|
|
IsValidSize(samePeaks) &&
|
|
IsValidSize(sameVales);
|
|
|
|
//
|
|
// Try to Evaluate Zone ...
|
|
int peaksStartIndex = pIDX + samePeaks;
|
|
int valesStartIndex = pIDX + sameVales;
|
|
int fromIDX = MathMin(peaksStartIndex, valesStartIndex);
|
|
if (result)
|
|
{
|
|
//
|
|
// Assign Zone Symbol / Period ...
|
|
zone.symbol = fimaParser.symbol;
|
|
zone.period = fimaParser.period;
|
|
|
|
//
|
|
// Calculate Zone Times ...
|
|
zone.to = GetBarTime(
|
|
zone.symbol,
|
|
zone.period,
|
|
barIndex //
|
|
);
|
|
zone.from = GetBarTime(
|
|
zone.symbol,
|
|
zone.period,
|
|
fromIDX //
|
|
);
|
|
zone.at = zone.to;
|
|
|
|
//
|
|
// Calculate Upper and Lower ...
|
|
zone.upper = fimaHelper.GetPeak(fromIDX);
|
|
zone.lower = fimaHelper.GetVale(fromIDX);
|
|
|
|
//
|
|
// Validate Zone Upper and Lower ...
|
|
for (int i = pIDX; i <= fromIDX; i++)
|
|
{
|
|
//
|
|
if (isValeUnderLast)
|
|
{
|
|
zone.lower = MathMin(zone.lower, fimaHelper.GetVale(i));
|
|
}
|
|
|
|
//
|
|
if (isPeakOverLast)
|
|
{
|
|
zone.upper = MathMax(zone.upper, fimaHelper.GetPeak(i));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Calculate Zone Direction ...
|
|
zone.dir =
|
|
isValeOverLast
|
|
? X_DIRECTION_BULLISH
|
|
: isValeUnderLast
|
|
? X_DIRECTION_BEARISH
|
|
: isPeakOverLast
|
|
? X_DIRECTION_BULLISH
|
|
: isPeakUnderLast
|
|
? X_DIRECTION_BEARISH
|
|
: X_DIRECTION_NONE;
|
|
|
|
//
|
|
// Refine Zone Upper / Lower based on Direction and Bars ...
|
|
|
|
//
|
|
// Detect Highest High / Lowest Low ...
|
|
double ll = bar.FindLowest(fromIDX - barIndex, MODE_LOW);
|
|
double hh = bar.FindHighest(fromIDX - barIndex, MODE_HIGH);
|
|
|
|
//
|
|
if (isNewVale)
|
|
{
|
|
zone.upper = MathMin(zone.upper, hh);
|
|
}
|
|
|
|
//
|
|
if (isNewPeak)
|
|
{
|
|
zone.lower = MathMax(zone.lower, ll);
|
|
}
|
|
|
|
//
|
|
// Set Zone Type ...
|
|
zone.type = ToXString(XFIMA_PIVOT_PV);
|
|
|
|
//
|
|
// Summarize result ...
|
|
result = zone.IsValid();
|
|
}
|
|
|
|
//
|
|
result = zone.IsValid();
|
|
|
|
//
|
|
// Cleanup ...
|
|
if (!result)
|
|
{
|
|
zone.Clean();
|
|
}
|
|
bar.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Analysors ...
|
|
|
|
/**
|
|
* Analyse Market ...
|
|
*
|
|
* @param structure: XFIMAMarketStructure, reference to holds Market Strucutre ...
|
|
* @param config: XFIMAMarketStructureConfig, reference to Provide Structure Analyse Config ...
|
|
* @param barIndex: int, Specified Bar Index ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool AnalyseMarket(
|
|
XFIMAMarketStructure &structure,
|
|
XFIMAMarketStructureConfig &config,
|
|
int barIndex = 0 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
structure.Clean();
|
|
|
|
//
|
|
// Normalize ...
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
result = fimaParser.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
int idx = -1;
|
|
XOHCL tmpBar;
|
|
datetime fromTime = NULL;
|
|
XPatternAnalysis tmpBarAnalysis;
|
|
|
|
//
|
|
// Initialize Indexed Bar ...
|
|
result = structure.bar.Init(symbol, period, barIndex);
|
|
|
|
//
|
|
// Analyse Bar Patterns ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = AnalyseBarPatterns(
|
|
structure.bar,
|
|
structure.barAnalysis,
|
|
config.barsAnalysisConfig //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Analyse Pivots ...
|
|
|
|
//
|
|
// XPV ...
|
|
CollectPVPivots(
|
|
structure.pvPivots,
|
|
barIndex,
|
|
config.requiredPivots,
|
|
config.loopback //
|
|
);
|
|
|
|
//
|
|
// XMA ...
|
|
CollectMAPivots(
|
|
structure.maPivots,
|
|
barIndex,
|
|
config.requiredPivots,
|
|
config.loopback //
|
|
);
|
|
|
|
//
|
|
// XZG ...
|
|
CollectZGPivots(
|
|
structure.zgPivots,
|
|
barIndex,
|
|
config.requiredPivots,
|
|
config.loopback //
|
|
);
|
|
|
|
//
|
|
// XHK ...
|
|
CollectHKPivots(
|
|
structure.hkPivots,
|
|
barIndex,
|
|
config.requiredPivots,
|
|
config.loopback //
|
|
);
|
|
|
|
//
|
|
// XSAR ...
|
|
CollectSARPivots(
|
|
structure.sarPivots,
|
|
barIndex,
|
|
config.requiredPivots,
|
|
config.loopback //
|
|
);
|
|
|
|
//
|
|
// XPV Analysis ...
|
|
if (result && config.analysePVPivots)
|
|
{
|
|
//
|
|
result = HasChild(structure.pvPivots);
|
|
|
|
//
|
|
// Check Oldest ...
|
|
idx = GetOldest(structure.pvPivots);
|
|
result = IsValidIndex(idx);
|
|
if (result)
|
|
{
|
|
//
|
|
fromTime =
|
|
!IsXValid(fromTime)
|
|
? structure.pvPivots[idx].time
|
|
: MathMin(fromTime, structure.pvPivots[idx].time);
|
|
}
|
|
|
|
//
|
|
// Initialize Analysis ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = structure.pvAnalysis.Init(
|
|
ToXString(XFIMA_PIVOT_PV),
|
|
symbol,
|
|
period,
|
|
structure.pvPivots //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// XZG Analysis ...
|
|
if (result && config.analyseZGPivots)
|
|
{
|
|
//
|
|
result = HasChild(structure.zgPivots);
|
|
|
|
//
|
|
// Check Oldest ...
|
|
idx = GetOldest(structure.zgPivots);
|
|
result = IsValidIndex(idx);
|
|
if (result)
|
|
{
|
|
//
|
|
fromTime =
|
|
!IsXValid(fromTime)
|
|
? structure.zgPivots[idx].time
|
|
: MathMin(fromTime, structure.zgPivots[idx].time);
|
|
}
|
|
|
|
//
|
|
// Initialize Analysis ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = structure.zgAnalysis.Init(
|
|
ToXString(XFIMA_PIVOT_ZIGZAG),
|
|
symbol,
|
|
period,
|
|
structure.zgPivots //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// XHK Analysis ...
|
|
if (result && config.analyseHKPivots)
|
|
{
|
|
//
|
|
result = HasChild(structure.hkPivots);
|
|
|
|
//
|
|
// Check Oldest ...
|
|
idx = GetOldest(structure.hkPivots);
|
|
result = IsValidIndex(idx);
|
|
if (result)
|
|
{
|
|
//
|
|
fromTime =
|
|
!IsXValid(fromTime)
|
|
? structure.hkPivots[idx].time
|
|
: MathMin(fromTime, structure.hkPivots[idx].time);
|
|
}
|
|
|
|
//
|
|
// Initialize Analysis ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = structure.hkAnalysis.Init(
|
|
ToXString(XFIMA_PIVOT_HK_SWITCH),
|
|
symbol,
|
|
period,
|
|
structure.hkPivots //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// XMA Analysis ...
|
|
if (result && config.analyseMAPivots)
|
|
{
|
|
result = HasChild(structure.maPivots);
|
|
|
|
//
|
|
// Check Oldest ...
|
|
idx = GetOldest(structure.maPivots);
|
|
result = IsValidIndex(idx);
|
|
if (result)
|
|
{
|
|
//
|
|
fromTime =
|
|
!IsXValid(fromTime)
|
|
? structure.maPivots[idx].time
|
|
: MathMin(fromTime, structure.maPivots[idx].time);
|
|
}
|
|
|
|
//
|
|
// Initialize Analysis ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = structure.maAnalysis.Init(
|
|
ToXString(XFIMA_PIVOT_MA_CROSS),
|
|
symbol,
|
|
period,
|
|
structure.maPivots //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// XSAR Analysis ...
|
|
if (result && config.analyseSARPivots)
|
|
{
|
|
//
|
|
result = HasChild(structure.sarPivots);
|
|
|
|
//
|
|
// Check Oldest ...
|
|
idx = GetOldest(structure.sarPivots);
|
|
result = IsValidIndex(idx);
|
|
if (result)
|
|
{
|
|
//
|
|
fromTime =
|
|
!IsXValid(fromTime)
|
|
? structure.sarPivots[idx].time
|
|
: MathMin(fromTime, structure.sarPivots[idx].time);
|
|
}
|
|
|
|
//
|
|
// Initialize Analysis ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = structure.sarAnalysis.Init(
|
|
ToXString(XFIMA_PIVOT_SAR_SWITCH),
|
|
symbol,
|
|
period,
|
|
structure.sarPivots //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Order Flow ...
|
|
if (result && config.detectOrderFlow)
|
|
{
|
|
//
|
|
HasOrderFlow(
|
|
structure.bar,
|
|
structure.orderFlowDir,
|
|
structure.orderFlow,
|
|
config.orderFlowValidation,
|
|
config.orderFlowUseOB,
|
|
config.orderFlowUseFVG,
|
|
config.orderFlowUseSupportAndResistance,
|
|
config.barsAnalysisConfig.supportAndResistanceLoopbackLength,
|
|
config.barsAnalysisConfig.supportAndResistanceValidationLength,
|
|
config.loopback //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Analyse Bar Patterns ...
|
|
if (result && config.analyseBarPatterns)
|
|
{
|
|
//
|
|
// Collect Pivots ...
|
|
XPivot tmps[];
|
|
Copy(structure.pvPivots, tmps, false);
|
|
Copy(structure.zgPivots, tmps, false);
|
|
Copy(structure.hkPivots, tmps, false);
|
|
Copy(structure.maPivots, tmps, false);
|
|
Copy(structure.sarPivots, tmps, false);
|
|
|
|
//
|
|
XPivot sames[];
|
|
XPivot tmpPivot;
|
|
int samesCount = 0;
|
|
while (HasChild(tmps))
|
|
{
|
|
//
|
|
tmpPivot.Clean();
|
|
tmpPivot = tmps[0];
|
|
ArrayRemove(tmps, 0, 1);
|
|
|
|
//
|
|
XClean(sames);
|
|
samesCount = ExtractSameTimePivots(tmpPivot, sames, tmps);
|
|
if (HasChild(sames))
|
|
{
|
|
//
|
|
tmpBar.Clean();
|
|
if (sames[0].GetBar(tmpBar))
|
|
{
|
|
//
|
|
tmpBarAnalysis.Clean();
|
|
if (AnalyseBarPatterns(tmpBar, tmpBarAnalysis, config.barsAnalysisConfig))
|
|
{
|
|
//
|
|
AddRef(tmpBarAnalysis, structure.barsAnalysis);
|
|
}
|
|
}
|
|
|
|
//
|
|
Removes(sames, tmps);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bars Analysis Exists ...
|
|
result = HasChild(structure.barsAnalysis);
|
|
|
|
//
|
|
// Cleanup ...
|
|
XClean(tmps);
|
|
XClean(sames);
|
|
tmpPivot.Clean();
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
if (!result)
|
|
{
|
|
structure.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Collectors ...
|
|
|
|
/**
|
|
* Collect ZigZag Pivots ...
|
|
*
|
|
* @param pivots: XPivot, reference collection to holds Detected Pivots ...
|
|
* @param barIndex: int, Specified Start Bar Index ...
|
|
* @param count: int, Specified number of required Pivots ...
|
|
* @param loopback: int, Max Allowed Loopback length for Detection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int CollectZGPivots(
|
|
XPivot &pivots[],
|
|
int barIndex = 0,
|
|
int count = 50,
|
|
int loopback = 500 //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Prepare ...
|
|
XClean(pivots);
|
|
|
|
//
|
|
// Normalize ...
|
|
count = NormalizeInt(count, 0);
|
|
loopback = NormalizeInt(loopback, 50);
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
bool has = fimaParser.IsValid();
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
XOHCL bar;
|
|
XPivot pivot;
|
|
int start = barIndex;
|
|
bool isBullish = false;
|
|
bool isBearish = false;
|
|
int end = start + loopback;
|
|
ENUM_X_DIRECTION dir = X_DIRECTION_NONE;
|
|
for (int i = start; i < end; i++)
|
|
{
|
|
//
|
|
isBullish = fimaParser.IsZGLow(i);
|
|
isBearish = fimaParser.IsZGHigh(i);
|
|
|
|
//
|
|
has = isBullish || isBearish;
|
|
if (has)
|
|
{
|
|
//
|
|
// Specified Direction ...
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Initialize Indexed Bar ...
|
|
// Validate it, and Converts it to Pivot ...
|
|
bar.Clean();
|
|
has = bar.Init(symbol, period, i);
|
|
has = has &&
|
|
ToPivot(
|
|
bar,
|
|
pivot,
|
|
dir,
|
|
ToXString(XFIMA_PIVOT_ZIGZAG) //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Add Detected Pivot ...
|
|
AddIfNotExists(pivot, pivots);
|
|
}
|
|
|
|
//
|
|
has = count <= 0
|
|
? false
|
|
: ArraySize(pivots) >= count;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
}
|
|
|
|
//
|
|
result = ArraySize(pivots);
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Collect MA Pivots ...
|
|
*
|
|
* @param pivots: XPivot, reference collection to holds Detected Pivots ...
|
|
* @param barIndex: int, Specified Start Bar Index ...
|
|
* @param count: int, Specified number of required Pivots ...
|
|
* @param loopback: int, Max Allowed Loopback length for Detection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int CollectMAPivots(
|
|
XPivot &pivots[],
|
|
int barIndex = 0,
|
|
int count = 50,
|
|
int loopback = 500 //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Prepare ...
|
|
XClean(pivots);
|
|
|
|
//
|
|
// Normalize ...
|
|
count = NormalizeInt(count, 0);
|
|
loopback = NormalizeInt(loopback, 50);
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
bool has = fimaParser.IsValid();
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
XOHCL bar;
|
|
XPivot pivot;
|
|
int start = barIndex;
|
|
bool isBullish = false;
|
|
bool isBearish = false;
|
|
int end = start + loopback;
|
|
ENUM_X_DIRECTION dir = X_DIRECTION_NONE;
|
|
for (int i = start; i < end; i++)
|
|
{
|
|
//
|
|
isBullish = fimaParser.IsMAFastCrossedOverSlow(i);
|
|
isBearish = fimaParser.IsMAFastCrossedUnderSlow(i);
|
|
|
|
//
|
|
has = isBullish || isBearish;
|
|
if (has)
|
|
{
|
|
//
|
|
// Specified Direction ...
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Initialize Indexed Bar ...
|
|
// Validate it, and Converts it to Pivot ...
|
|
bar.Clean();
|
|
has = bar.Init(symbol, period, i);
|
|
has = has &&
|
|
ToPivot(
|
|
bar,
|
|
pivot,
|
|
dir,
|
|
ToXString(XFIMA_PIVOT_MA_CROSS) //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Replace MA Values ...
|
|
pivot.value =
|
|
isBullish
|
|
? fimaHelper.GetMASlow(barIndex)
|
|
: fimaHelper.GetMAFast(barIndex);
|
|
|
|
//
|
|
// Add Detected Pivot ...
|
|
AddIfNotExists(pivot, pivots);
|
|
}
|
|
|
|
//
|
|
has = count <= 0
|
|
? false
|
|
: ArraySize(pivots) >= count;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
}
|
|
|
|
//
|
|
result = ArraySize(pivots);
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Collect HK Pivots ...
|
|
*
|
|
* @param pivots: XPivot, reference collection to holds Detected Pivots ...
|
|
* @param barIndex: int, Specified Start Bar Index ...
|
|
* @param count: int, Specified number of required Pivots ...
|
|
* @param loopback: int, Max Allowed Loopback length for Detection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int CollectHKPivots(
|
|
XPivot &pivots[],
|
|
int barIndex = 0,
|
|
int count = 50,
|
|
int loopback = 500 //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Prepare ...
|
|
XClean(pivots);
|
|
|
|
//
|
|
// Normalize ...
|
|
count = NormalizeInt(count, 0);
|
|
loopback = NormalizeInt(loopback, 50);
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
bool has = fimaParser.IsValid();
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
XOHCL bar;
|
|
XPivot pivot;
|
|
int start = barIndex;
|
|
bool isBullish = false;
|
|
bool isBearish = false;
|
|
int end = start + loopback;
|
|
ENUM_X_DIRECTION dir = X_DIRECTION_NONE;
|
|
for (int i = start; i < end; i++)
|
|
{
|
|
//
|
|
isBullish = fimaParser.IsHKSwitchedToBullish(i);
|
|
isBearish = fimaParser.IsHKSwitchedToBearish(i);
|
|
|
|
//
|
|
has = isBullish || isBearish;
|
|
if (has)
|
|
{
|
|
//
|
|
// Specified Direction ...
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Initialize Indexed Bar ...
|
|
// Validate it, and Converts it to Pivot ...
|
|
bar.Clean();
|
|
has = bar.Init(symbol, period, i);
|
|
has = has &&
|
|
ToPivot(
|
|
bar,
|
|
pivot,
|
|
dir,
|
|
ToXString(XFIMA_PIVOT_HK_SWITCH) //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Replace Value ...
|
|
pivot.value =
|
|
isBullish
|
|
? fimaHelper.GetHKLow(i)
|
|
: fimaHelper.GetHKHigh(i);
|
|
|
|
//
|
|
// Add Detected Pivot ...
|
|
AddIfNotExists(pivot, pivots);
|
|
}
|
|
|
|
//
|
|
has = count <= 0
|
|
? false
|
|
: ArraySize(pivots) >= count;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
}
|
|
|
|
//
|
|
result = ArraySize(pivots);
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Collect PV Pivots ...
|
|
*
|
|
* @param pivots: XPivot, reference collection to holds Detected Pivots ...
|
|
* @param barIndex: int, Specified Start Bar Index ...
|
|
* @param count: int, Specified number of required Pivots ...
|
|
* @param loopback: int, Max Allowed Loopback length for Detection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int CollectPVPivots(
|
|
XPivot &pivots[],
|
|
int barIndex = 0,
|
|
int count = 50,
|
|
int loopback = 500 //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Prepare ...
|
|
XClean(pivots);
|
|
|
|
//
|
|
// Normalize ...
|
|
count = NormalizeInt(count, 0);
|
|
loopback = NormalizeInt(loopback, 50);
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
bool has = fimaParser.IsValid();
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
XOHCL bar;
|
|
XPivot pivot;
|
|
int start = barIndex;
|
|
bool isBullish = false;
|
|
bool isBearish = false;
|
|
int end = start + loopback;
|
|
ENUM_X_DIRECTION dir = X_DIRECTION_NONE;
|
|
for (int i = start; i < end; i++)
|
|
{
|
|
//
|
|
isBullish = fimaParser.IsPeakOverLast(i);
|
|
isBearish = fimaParser.IsValeUnderLast(i);
|
|
|
|
//
|
|
has = isBullish || isBearish;
|
|
if (has)
|
|
{
|
|
//
|
|
// Specified Direction ...
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Initialize Indexed Bar ...
|
|
// Validate it, and Converts it to Pivot ...
|
|
bar.Clean();
|
|
has = bar.Init(symbol, period, i);
|
|
has = has &&
|
|
ToPivot(
|
|
bar,
|
|
pivot,
|
|
dir,
|
|
ToXString(XFIMA_PIVOT_PV) //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Replace Value ...
|
|
pivot.value =
|
|
isBullish
|
|
? fimaHelper.GetHKLow(i)
|
|
: fimaHelper.GetHKHigh(i);
|
|
|
|
//
|
|
// Add Detected Pivot ...
|
|
AddIfNotExists(pivot, pivots);
|
|
}
|
|
|
|
//
|
|
has = count <= 0
|
|
? false
|
|
: ArraySize(pivots) >= count;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
}
|
|
|
|
//
|
|
result = ArraySize(pivots);
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Collect SAR Pivots ...
|
|
*
|
|
* @param pivots: XPivot, reference collection to holds Detected Pivots ...
|
|
* @param barIndex: int, Specified Start Bar Index ...
|
|
* @param count: int, Specified number of required Pivots ...
|
|
* @param loopback: int, Max Allowed Loopback length for Detection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int CollectSARPivots(
|
|
XPivot &pivots[],
|
|
int barIndex = 0,
|
|
int count = 50,
|
|
int loopback = 500 //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Prepare ...
|
|
XClean(pivots);
|
|
|
|
//
|
|
// Normalize ...
|
|
count = NormalizeInt(count, 0);
|
|
loopback = NormalizeInt(loopback, 50);
|
|
barIndex = fimaParser.NormalizeBarIndex(barIndex);
|
|
|
|
//
|
|
// Validate ...
|
|
bool has = fimaParser.IsValid();
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
XOHCL bar;
|
|
XPivot pivot;
|
|
int start = barIndex;
|
|
bool isBullish = false;
|
|
bool isBearish = false;
|
|
int end = start + loopback;
|
|
ENUM_X_DIRECTION dir = X_DIRECTION_NONE;
|
|
for (int i = start; i < end; i++)
|
|
{
|
|
//
|
|
isBullish = fimaParser.IsSARSwitchedToBullish(i);
|
|
isBearish = fimaParser.IsSARSwitchedToBearish(i);
|
|
|
|
//
|
|
has = isBullish || isBearish;
|
|
if (has)
|
|
{
|
|
//
|
|
// Specified Direction ...
|
|
dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Initialize Indexed Bar ...
|
|
// Validate it, and Converts it to Pivot ...
|
|
bar.Clean();
|
|
has = bar.Init(symbol, period, i);
|
|
has = has &&
|
|
ToPivot(
|
|
bar,
|
|
pivot,
|
|
dir,
|
|
ToXString(XFIMA_PIVOT_SAR_SWITCH) //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Add Detected Pivot ...
|
|
AddIfNotExists(pivot, pivots);
|
|
}
|
|
|
|
//
|
|
has = count <= 0
|
|
? false
|
|
: ArraySize(pivots) >= count;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
}
|
|
|
|
//
|
|
result = ArraySize(pivots);
|
|
|
|
//
|
|
// Cleanup ...
|
|
bar.Clean();
|
|
pivot.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Collect Specified ZOnes Boundary Min / Max Values ...
|
|
*
|
|
* @param uppers: XPivot, collection reference to holds Peaks of Zone's Boundary ...
|
|
* @param lowers: XPivot, collection reference to holds Vales of Zone's Boundary ...
|
|
* @param zone: XBoxZone, reference to Provide Specified Zone for Boundary Detection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int CollectZonesBoundary(
|
|
XPivot &uppers[],
|
|
XPivot &lowers[],
|
|
XBoxZone &zone //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Prepare ...
|
|
XClean(uppers);
|
|
XClean(lowers);
|
|
|
|
//
|
|
// Validate ...
|
|
bool has = zone.IsValid() &&
|
|
fimaParser.IsValid();
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double iUpper;
|
|
double iLower;
|
|
datetime iTime;
|
|
XPivot upperPivot;
|
|
XPivot lowerPivot;
|
|
int start = zone.ToIndex();
|
|
int end = zone.FromIndex();
|
|
for (int i = start; i <= end; i++)
|
|
{
|
|
//
|
|
iUpper = fimaParser.GetBoundaryMax(i);
|
|
iLower = fimaParser.GetBoundaryMin(i);
|
|
iTime = GetBarTime(zone.symbol, zone.period, i);
|
|
|
|
//
|
|
has =
|
|
IsXValid(iTime) &&
|
|
NotEmptyZero(iUpper) &&
|
|
NotEmptyZero(iLower);
|
|
if (has)
|
|
{
|
|
//
|
|
// Upper Pivot ...
|
|
upperPivot.Clean();
|
|
has = upperPivot.Init(
|
|
iUpper,
|
|
iTime,
|
|
zone.symbol,
|
|
zone.dir,
|
|
zone.period,
|
|
X_PIVOT_TYPE_PEAK //
|
|
);
|
|
if (has)
|
|
{
|
|
upperPivot.prefix = "XBNDUP";
|
|
}
|
|
|
|
//
|
|
// Lower Pivot ...
|
|
lowerPivot.Clean();
|
|
has = lowerPivot.Init(
|
|
iLower,
|
|
iTime,
|
|
zone.symbol,
|
|
zone.dir,
|
|
zone.period,
|
|
X_PIVOT_TYPE_VALE //
|
|
);
|
|
if (has)
|
|
{
|
|
lowerPivot.prefix = "XBNDLO";
|
|
}
|
|
|
|
//
|
|
has = upperPivot.IsValid() &&
|
|
lowerPivot.IsValid();
|
|
if (has)
|
|
{
|
|
//
|
|
AddRef(upperPivot, uppers);
|
|
AddRef(lowerPivot, lowers);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
result = MathMin(ArraySize(uppers), ArraySize(lowers));
|
|
|
|
//
|
|
// Cleanup ...
|
|
upperPivot.Clean();
|
|
lowerPivot.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Zone Has Boundary Trend or not ...
|
|
*
|
|
* @param zone: XBoxZOne, reference to Specified Zone for Checking Trend ...
|
|
* @param trend: XTrend, reference to holds Detected Trend Result ...
|
|
* @param basedOnToTime: bool, if set true, do based on to and it's prev bar ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasBoundaryTrend(
|
|
XBoxZone &zone,
|
|
XTrend &trend,
|
|
bool basedOnToTime = false //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
trend.Clean();
|
|
|
|
//
|
|
// Validate ...
|
|
result =
|
|
zone.IsValid() &&
|
|
fimaParser.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do ...
|
|
|
|
//
|
|
int toIDX = zone.ToIndex();
|
|
int fromIDX =
|
|
basedOnToTime
|
|
? toIDX + 1
|
|
: zone.FromIndex();
|
|
datetime fromTime = GetBarTime(zone.symbol, zone.period, fromIDX);
|
|
|
|
//
|
|
double toMin = fimaParser.GetBoundaryMin(toIDX);
|
|
double toMax = fimaParser.GetBoundaryMax(toIDX);
|
|
|
|
//
|
|
double fromMin = fimaParser.GetBoundaryMin(fromIDX);
|
|
double fromMax = fimaParser.GetBoundaryMax(fromIDX);
|
|
|
|
//
|
|
bool isMinBullish = fromMin < toMin;
|
|
bool isMaxBullish = fromMax < toMax;
|
|
bool isBullish = isMinBullish && isMaxBullish;
|
|
|
|
//
|
|
bool isMinBearish = fromMin > toMin;
|
|
bool isMaxBearish = fromMax > toMax;
|
|
bool isBearish = isMinBearish && isMaxBearish;
|
|
|
|
//
|
|
result = isBullish || isBearish;
|
|
if (result)
|
|
{
|
|
//
|
|
result = trend.Init(
|
|
zone.symbol,
|
|
zone.period,
|
|
TimeCurrent(),
|
|
isBullish
|
|
? fromMin
|
|
: fromMax,
|
|
fromTime,
|
|
isBullish
|
|
? toMin
|
|
: toMax,
|
|
zone.to //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Validate Result ...
|
|
result = trend.IsValid() &&
|
|
trend.HasTrend();
|
|
if (!result)
|
|
{
|
|
trend.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get Cycle Based PV Zone ...
|
|
*
|
|
* @param bar: XOHCL, reference to Specified Zone ...
|
|
* @param zone: XBoxZone, reference to holds PV Zone ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool GetSCZone(
|
|
XOHCL &bar,
|
|
XBoxZone &zone //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
zone.Clean();
|
|
|
|
//
|
|
// Validate ...
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int barIndex = bar.Index();
|
|
|
|
//
|
|
double upper = fimaHelper.GetSHH(barIndex);
|
|
double lower = fimaHelper.GetSLL(barIndex);
|
|
double pHH = fimaHelper.GetSHH(barIndex + 1);
|
|
double pLL = fimaHelper.GetSLL(barIndex + 1);
|
|
int sameLLs = fimaParser.CountSCSameLLs(barIndex);
|
|
int sameHHs = fimaParser.CountSCSameHHs(barIndex);
|
|
int fromIDX = barIndex + MathMax(sameLLs, sameHHs);
|
|
|
|
//
|
|
zone.to = bar.time;
|
|
zone.at = bar.time;
|
|
zone.upper = upper;
|
|
zone.lower = lower;
|
|
zone.symbol = bar.symbol;
|
|
zone.period = bar.period;
|
|
zone.dir =
|
|
(upper > pHH ||
|
|
lower > pLL)
|
|
? X_DIRECTION_BULLISH
|
|
: (upper < pHH ||
|
|
lower < pLL)
|
|
? X_DIRECTION_BEARISH
|
|
: X_DIRECTION_NONE;
|
|
zone.type = ToXString(XFIMA_PIVOT_PV) + "_SC";
|
|
zone.from = GetBarTime(bar.symbol, bar.period, fromIDX);
|
|
|
|
//
|
|
result = zone.IsValid();
|
|
if (!result)
|
|
{
|
|
zone.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get Cycle Based PV Zone ...
|
|
*
|
|
* @param bar: XOHCL, reference to Specified Zone ...
|
|
* @param zone: XBoxZone, reference to holds PV Zone ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool GetMCZone(
|
|
XOHCL &bar,
|
|
XBoxZone &zone //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
zone.Clean();
|
|
|
|
//
|
|
// Validate ...
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int barIndex = bar.Index();
|
|
|
|
//
|
|
double upper = fimaHelper.GetMHH(barIndex);
|
|
double lower = fimaHelper.GetMLL(barIndex);
|
|
double pHH = fimaHelper.GetMHH(barIndex + 1);
|
|
double pLL = fimaHelper.GetMLL(barIndex + 1);
|
|
int sameLLs = fimaParser.CountMCSameLLs(barIndex);
|
|
int sameHHs = fimaParser.CountMCSameHHs(barIndex);
|
|
int fromIDX = barIndex + MathMax(sameLLs, sameHHs);
|
|
|
|
//
|
|
zone.to = bar.time;
|
|
zone.at = bar.time;
|
|
zone.upper = upper;
|
|
zone.lower = lower;
|
|
zone.symbol = bar.symbol;
|
|
zone.period = bar.period;
|
|
zone.dir =
|
|
(upper > pHH ||
|
|
lower > pLL)
|
|
? X_DIRECTION_BULLISH
|
|
: (upper < pHH ||
|
|
lower < pLL)
|
|
? X_DIRECTION_BEARISH
|
|
: X_DIRECTION_NONE;
|
|
zone.type = ToXString(XFIMA_PIVOT_PV) + "_MC";
|
|
zone.from = GetBarTime(bar.symbol, bar.period, fromIDX);
|
|
|
|
//
|
|
result = zone.IsValid();
|
|
if (!result)
|
|
{
|
|
zone.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get Cycle Based PV Zone ...
|
|
*
|
|
* @param bar: XOHCL, reference to Specified Zone ...
|
|
* @param zone: XBoxZone, reference to holds PV Zone ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool GetLCZone(
|
|
XOHCL &bar,
|
|
XBoxZone &zone //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
zone.Clean();
|
|
|
|
//
|
|
// Validate ...
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int barIndex = bar.Index();
|
|
|
|
//
|
|
double upper = fimaHelper.GetLHH(barIndex);
|
|
double lower = fimaHelper.GetLLL(barIndex);
|
|
double pHH = fimaHelper.GetLHH(barIndex + 1);
|
|
double pLL = fimaHelper.GetLLL(barIndex + 1);
|
|
int sameLLs = fimaParser.CountLCSameLLs(barIndex);
|
|
int sameHHs = fimaParser.CountLCSameHHs(barIndex);
|
|
int fromIDX = barIndex + MathMax(sameLLs, sameHHs);
|
|
|
|
//
|
|
zone.to = bar.time;
|
|
zone.at = bar.time;
|
|
zone.upper = upper;
|
|
zone.lower = lower;
|
|
zone.symbol = bar.symbol;
|
|
zone.period = bar.period;
|
|
zone.dir =
|
|
(upper > pHH ||
|
|
lower > pLL)
|
|
? X_DIRECTION_BULLISH
|
|
: (upper < pHH ||
|
|
lower < pLL)
|
|
? X_DIRECTION_BEARISH
|
|
: X_DIRECTION_NONE;
|
|
zone.type = ToXString(XFIMA_PIVOT_PV) + "_LC";
|
|
zone.from = GetBarTime(bar.symbol, bar.period, fromIDX);
|
|
|
|
//
|
|
result = zone.IsValid();
|
|
if (!result)
|
|
{
|
|
zone.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get Cycle Based PV Zone ...
|
|
*
|
|
* @param bar: XOHCL, reference to Specified Zone ...
|
|
* @param zone: XBoxZone, reference to holds PV Zone ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool GetHCZone(
|
|
XOHCL &bar,
|
|
XBoxZone &zone //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Prepare ...
|
|
zone.Clean();
|
|
|
|
//
|
|
// Validate ...
|
|
result = bar.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int barIndex = bar.Index();
|
|
|
|
//
|
|
double upper = fimaHelper.GetHHH(barIndex);
|
|
double lower = fimaHelper.GetHLL(barIndex);
|
|
double pHH = fimaHelper.GetHHH(barIndex + 1);
|
|
double pLL = fimaHelper.GetHLL(barIndex + 1);
|
|
int sameLLs = fimaParser.CountHCSameLLs(barIndex);
|
|
int sameHHs = fimaParser.CountHCSameHHs(barIndex);
|
|
int fromIDX = barIndex + MathMax(sameLLs, sameHHs);
|
|
|
|
//
|
|
zone.to = bar.time;
|
|
zone.at = bar.time;
|
|
zone.upper = upper;
|
|
zone.lower = lower;
|
|
zone.symbol = bar.symbol;
|
|
zone.period = bar.period;
|
|
zone.dir =
|
|
(upper > pHH ||
|
|
lower > pLL)
|
|
? X_DIRECTION_BULLISH
|
|
: (upper < pHH ||
|
|
lower < pLL)
|
|
? X_DIRECTION_BEARISH
|
|
: X_DIRECTION_NONE;
|
|
zone.type = ToXString(XFIMA_PIVOT_PV) + "_HC";
|
|
zone.from = GetBarTime(bar.symbol, bar.period, fromIDX);
|
|
|
|
//
|
|
result = zone.IsValid();
|
|
if (!result)
|
|
{
|
|
zone.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Specified Bar is Rejected Specified XFIMA Values or not ...
|
|
*
|
|
* @param bar: XOHCL, reference to Specified Bar ...
|
|
* @param rejecteds: double, collection reference to holds Rejected Values ...
|
|
* @param forDir: ENUM_X_DIRECTION, Specified required Rejection Direction ...
|
|
* @param bullishRejectUpperPriceType: ENUM_X_PRICE, Specified Bullish Rejection Upper Price Type ...
|
|
* @param bullishRejectLowerPriceType: ENUM_X_PRICE, Specified Bullish Rejection Lower Price Type ...
|
|
* @param bearishRejectUpperPriceType: ENUM_X_PRICE, Specified Bearish Rejection Upper Price Type ...
|
|
* @param bearishRejectLowerPriceType: ENUM_X_PRICE, Specified Bearish Rejection Lower Price Type ...
|
|
* @param ignoreBoundary: bool, ignoring XFIMA Max and Min Values Rejections ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int IsRejectXFIMA(
|
|
XOHCL &bar,
|
|
double &rejecteds[],
|
|
ENUM_X_DIRECTION forDir,
|
|
ENUM_X_PRICE bullishRejectUpperPriceType = X_PRICE_DOWN,
|
|
ENUM_X_PRICE bullishRejectLowerPriceType = X_PRICE_LOW,
|
|
ENUM_X_PRICE bearishRejectUpperPriceType = X_PRICE_HIGH,
|
|
ENUM_X_PRICE bearishRejectLowerPriceType = X_PRICE_UP,
|
|
bool ignoreBoundary = true //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Prepare ...
|
|
XClean(rejecteds);
|
|
|
|
//
|
|
// Validate ...
|
|
bool has =
|
|
bar.IsValid() &&
|
|
fimaParser.IsValid() &&
|
|
HasDirection(forDir) &&
|
|
IsXValid(bullishRejectLowerPriceType) &&
|
|
IsXValid(bullishRejectUpperPriceType) &&
|
|
IsXValid(bearishRejectUpperPriceType) &&
|
|
IsXValid(bearishRejectLowerPriceType);
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int pBarIndex = bar.Index() + 1;
|
|
bool isBullish = IsXBullish(forDir);
|
|
|
|
//
|
|
// Reading Values ...
|
|
double values[];
|
|
int count = fimaParser.ReadXFIMAValues(values, pBarIndex);
|
|
has = HasChild(values);
|
|
|
|
//
|
|
// Detect Rejected Prices ...
|
|
if (has)
|
|
{
|
|
//
|
|
double min = GetMin(values);
|
|
double max = GetMax(values);
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
// Detect Rejection ...
|
|
has =
|
|
isBullish
|
|
? (bar.GetPrice(bullishRejectLowerPriceType) < values[i] &&
|
|
bar.GetPrice(bullishRejectUpperPriceType) > values[i])
|
|
: (bar.GetPrice(bearishRejectUpperPriceType) > values[i] &&
|
|
bar.GetPrice(bearishRejectLowerPriceType) < values[i]);
|
|
if (has)
|
|
{
|
|
//
|
|
// Validate Rejection ...
|
|
if (ignoreBoundary)
|
|
{
|
|
//
|
|
has =
|
|
has &&
|
|
(values[i] != min &&
|
|
values[i] != max);
|
|
}
|
|
|
|
//
|
|
// Add Result ...
|
|
if (has)
|
|
{
|
|
Add(values[i], rejecteds);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(rejecteds);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Others ...
|
|
|
|
/**
|
|
* Destroy ...
|
|
*/
|
|
void Destroy()
|
|
{
|
|
//
|
|
delete barAnalyser;
|
|
ZeroMemory(barAnalyser);
|
|
|
|
//
|
|
ZeroMemory(fimaHelper);
|
|
ZeroMemory(fimaParser);
|
|
}
|
|
|
|
//
|
|
// Testers ...
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
|
|
//
|
|
};
|
|
|
|
// |