1899 lines
42 KiB
Plaintext
1899 lines
42 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Seriallize Library
|
|
// ---------------------------------------
|
|
// Name: XOBFVGLib
|
|
// Description: All models related to POIs ...
|
|
//
|
|
//
|
|
// Maintainer:
|
|
// ------------
|
|
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
|
//
|
|
//////////////////////////////////////////////////////
|
|
//
|
|
// Global Properties ...
|
|
#property library
|
|
#property copyright "Copyright `023, SaherElm IT Center"
|
|
#property link "https://www.saherelm.ir"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../Libraries/x-saherelm.x-poi.lib.mq5"
|
|
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"
|
|
|
|
//
|
|
struct XOBFVGPivot
|
|
{
|
|
//
|
|
// Props ...
|
|
double value;
|
|
datetime at;
|
|
|
|
//
|
|
// Constructor ...
|
|
XOBFVGPivot()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
void Clean()
|
|
{
|
|
//
|
|
value = 0;
|
|
at = NULL;
|
|
|
|
//
|
|
ZeroMemory(this);
|
|
}
|
|
|
|
//
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
value > 0 &&
|
|
IsValid(at);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
struct XOBFVG
|
|
{
|
|
//
|
|
// Props ...
|
|
XCFVG *fvg;
|
|
XCOrderBlock *ob;
|
|
|
|
//
|
|
XCSupplyZone *supplyZones[];
|
|
XCDemandZone *demandZones[];
|
|
|
|
//
|
|
XCSupportZone *supportZones[];
|
|
XCResistanceZone *resistanceZones[];
|
|
|
|
//
|
|
// Constructor ...
|
|
XOBFVG()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
void Clean()
|
|
{
|
|
//
|
|
ZeroMemory(ob);
|
|
ZeroMemory(fvg);
|
|
|
|
//
|
|
Clean(supplyZones);
|
|
Clean(demandZones);
|
|
Clean(supportZones);
|
|
Clean(resistanceZones);
|
|
|
|
//
|
|
ZeroMemory(this);
|
|
}
|
|
|
|
//
|
|
bool IsValid()
|
|
{
|
|
return HasOB() &&
|
|
HasFVG();
|
|
}
|
|
|
|
//
|
|
bool HasOB()
|
|
{
|
|
return ob != NULL &&
|
|
ob.IsValid();
|
|
}
|
|
|
|
//
|
|
bool HasFVG()
|
|
{
|
|
return fvg != NULL &&
|
|
fvg.IsValid();
|
|
}
|
|
|
|
//
|
|
bool HasSupplyZones()
|
|
{
|
|
return HasChild(supplyZones);
|
|
}
|
|
|
|
//
|
|
int CountSupplyZones()
|
|
{
|
|
return ArraySize(supplyZones);
|
|
}
|
|
|
|
//
|
|
bool HasDemandZones()
|
|
{
|
|
return HasChild(demandZones);
|
|
}
|
|
|
|
//
|
|
int CountDemandZones()
|
|
{
|
|
return ArraySize(demandZones);
|
|
}
|
|
|
|
//
|
|
bool HasSupportZones()
|
|
{
|
|
return HasChild(supportZones);
|
|
}
|
|
|
|
//
|
|
int CountSupportZones()
|
|
{
|
|
return ArraySize(supportZones);
|
|
}
|
|
|
|
//
|
|
bool HasResistanceZones()
|
|
{
|
|
return HasChild(resistanceZones);
|
|
}
|
|
|
|
//
|
|
int CountResistanceZones()
|
|
{
|
|
return ArraySize(resistanceZones);
|
|
}
|
|
|
|
//
|
|
ENUM_X_DIRECTION GetDirection()
|
|
{
|
|
//
|
|
ENUM_X_DIRECTION result = X_DIRECTION_NONE;
|
|
|
|
//
|
|
bool isValid = IsValid();
|
|
if (!isValid)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = ob.GetDirection();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool IsBullish()
|
|
{
|
|
return IsBullish(GetDirection());
|
|
}
|
|
|
|
//
|
|
bool IsBearish()
|
|
{
|
|
return IsBearish(GetDirection());
|
|
}
|
|
|
|
//
|
|
int GetSwings(
|
|
XOBFVGPivot &pivots[] //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(pivots);
|
|
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_DIRECTION forDir = GetDirection();
|
|
bool isBullish = IsBullish(forDir);
|
|
|
|
//
|
|
XOHCL passedBar;
|
|
XOBFVGPivot iPivot;
|
|
bool isPassed = false;
|
|
double appliedPrice = 0;
|
|
bool isBodyState = false;
|
|
|
|
//
|
|
// Start ...
|
|
isPassed = ob.IsStarted(
|
|
forDir,
|
|
passedBar,
|
|
isBodyState //
|
|
);
|
|
if (isPassed)
|
|
{
|
|
//
|
|
appliedPrice =
|
|
isBullish
|
|
? passedBar.low
|
|
: passedBar.high;
|
|
|
|
//
|
|
iPivot.Clean();
|
|
iPivot.value = appliedPrice;
|
|
iPivot.at = passedBar.time;
|
|
|
|
//
|
|
AddRef(
|
|
iPivot,
|
|
pivots //
|
|
);
|
|
|
|
//
|
|
iPivot.Clean();
|
|
}
|
|
|
|
//
|
|
// Activation ...
|
|
isPassed = ob.IsActivated(
|
|
forDir,
|
|
passedBar,
|
|
isBodyState //
|
|
);
|
|
if (isPassed)
|
|
{
|
|
//
|
|
appliedPrice =
|
|
isBullish
|
|
? passedBar.low
|
|
: passedBar.high;
|
|
|
|
//
|
|
iPivot.Clean();
|
|
iPivot.value = appliedPrice;
|
|
iPivot.at = passedBar.time;
|
|
|
|
//
|
|
AddRef(
|
|
iPivot,
|
|
pivots //
|
|
);
|
|
|
|
//
|
|
iPivot.Clean();
|
|
}
|
|
|
|
//
|
|
// Worked ...
|
|
isPassed = ob.IsWorked(
|
|
forDir,
|
|
passedBar,
|
|
isBodyState //
|
|
);
|
|
if (isPassed)
|
|
{
|
|
//
|
|
appliedPrice =
|
|
isBullish
|
|
? passedBar.low
|
|
: passedBar.high;
|
|
|
|
//
|
|
iPivot.Clean();
|
|
iPivot.value = appliedPrice;
|
|
iPivot.at = passedBar.time;
|
|
|
|
//
|
|
AddRef(
|
|
iPivot,
|
|
pivots //
|
|
);
|
|
|
|
//
|
|
iPivot.Clean();
|
|
}
|
|
|
|
//
|
|
passedBar.Clean();
|
|
|
|
//
|
|
result = ArraySize(pivots);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void UpdateZones(
|
|
XPOIState &state //
|
|
)
|
|
{
|
|
//
|
|
Clean(supplyZones);
|
|
Clean(demandZones);
|
|
Clean(supportZones);
|
|
Clean(resistanceZones);
|
|
|
|
//
|
|
bool isValid =
|
|
IsValid() &&
|
|
state.HasChild();
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int count = 0;
|
|
bool has = false;
|
|
bool isPassed = false;
|
|
|
|
//
|
|
// Supply Zones ...
|
|
has = state.HasSupplyZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = state.CountSupplyZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCSupplyZone *iZone = state.supplyZones[i];
|
|
|
|
//
|
|
isPassed = iZone.From() > ob.From();
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
isPassed = IsInsideDiff(
|
|
iZone,
|
|
ob //
|
|
);
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iZone,
|
|
supplyZones //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Demand Zones ...
|
|
has = state.HasDemandZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = state.CountDemandZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCDemandZone *iZone = state.demandZones[i];
|
|
|
|
//
|
|
isPassed = iZone.From() > ob.From();
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
isPassed = IsInsideDiff(
|
|
iZone,
|
|
ob //
|
|
);
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iZone,
|
|
demandZones //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Support Zones ...
|
|
has = state.HasSupportZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = state.CountSupportZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCSupportZone *iZone = state.supportZones[i];
|
|
|
|
//
|
|
isPassed = iZone.From() > ob.From();
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
isPassed = IsInsideDiff(
|
|
iZone,
|
|
ob //
|
|
);
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iZone,
|
|
supportZones //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Resistance Zones ...
|
|
has = state.HasResistanceZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = state.CountResistanceZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCResistanceZone *iZone = state.resistanceZones[i];
|
|
|
|
//
|
|
isPassed = iZone.From() > ob.From();
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
isPassed = IsInsideDiff(
|
|
iZone,
|
|
ob //
|
|
);
|
|
if (!isPassed)
|
|
{
|
|
//
|
|
ZeroMemory(iZone);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iZone,
|
|
resistanceZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void UpdateTo(datetime value = NULL)
|
|
{
|
|
//
|
|
value = NormalizeTime(value);
|
|
|
|
//
|
|
int count = 0;
|
|
bool has = false;
|
|
|
|
//
|
|
has = HasOB();
|
|
if (has)
|
|
{
|
|
ob.To(value);
|
|
}
|
|
|
|
//
|
|
has = HasFVG();
|
|
if (has)
|
|
{
|
|
fvg.To(value);
|
|
}
|
|
|
|
//
|
|
// Supply Zones ...
|
|
has = HasSupplyZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = CountSupplyZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
supplyZones[i].To(value);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Demand Zones ...
|
|
has = HasDemandZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = CountDemandZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
demandZones[i].To(value);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Support Zones ...
|
|
has = HasSupportZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = CountSupportZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
supportZones[i].To(value);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Resistance Zones ...
|
|
has = HasResistanceZones();
|
|
if (has)
|
|
{
|
|
//
|
|
count = CountResistanceZones();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
resistanceZones[i].To(value);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
/**
|
|
* Extract XOBFVG Collection of Market ...
|
|
*
|
|
* @param htfState: Higher Time Frame POI State (Used For Detecting Order Blocks) ...
|
|
* @param mtfState: Medium Time Frame POI State (Used For Detecting Fair Value Gaps) ...
|
|
* @param stfState: Short Time Frame POI State (Used For Detecting Other POI(s)) ...
|
|
* @param dest: Result Collection ...
|
|
* @param forDir: Market Direction for Detecting POI(s) ...
|
|
* @param forPeriod: Time Frame which Used for Zones actions (NULL for Default) ...
|
|
* @param startState: Zones Start State ...
|
|
* @param forceStartedBodyOut: Zones Started Bar's Body State ...
|
|
* @param activeState: Zones Activation State ...
|
|
* @param forceActivatedBodyIn: Zones Activated Bar's Body State ...
|
|
* @param breakState: Zones Breaked State ...
|
|
* @param forceBreakedBodyOut: Zone Breaker Bar's Body State ...
|
|
* @param workState: Zones Worked State ...
|
|
* @param forceWorkedBodyOut: Zones Worked Bar's Body State ...
|
|
* @param applySort: Specify Applying Sorting on Collections ...
|
|
* @param applyFromFilter: Filter Sorted Zones based on Occured Date ...
|
|
* @param applyPlaceFilter: Filter Sorted Zones based on Occured Place ...
|
|
* @param ignoreInside: Ignore Inside Sides of POI(s) Zones in Related to Order Block ...
|
|
* @param applyFromFilterForPOIs: Apply From Filter for Other POI(s) ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int ExtractOBFVGZones(
|
|
XPOIState &htfState,
|
|
XPOIState &mtfState,
|
|
XPOIState &stfState,
|
|
XOBFVG &dest[],
|
|
//
|
|
ENUM_X_DIRECTION forDir,
|
|
ENUM_TIMEFRAMES forPeriod = NULL,
|
|
//
|
|
// Start ...
|
|
ENUM_XZONE_STATE startState = XZONE_STATE_PASSED,
|
|
bool forceStartedBodyOut = false,
|
|
//
|
|
// Activation ...
|
|
ENUM_XZONE_STATE activeState = XZONE_STATE_FAILED,
|
|
bool forceActivatedBodyIn = false,
|
|
//
|
|
// Breaktion ...
|
|
ENUM_XZONE_STATE breakState = XZONE_STATE_FAILED,
|
|
bool forceBreakedBodyOut = false,
|
|
//
|
|
// Works ...
|
|
ENUM_XZONE_STATE workState = XZONE_STATE_FAILED,
|
|
bool forceWorkedBodyOut = false,
|
|
//
|
|
bool applySort = true,
|
|
bool applyFromFilter = true,
|
|
bool applyPlaceFilter = true,
|
|
//
|
|
bool ignoreInside = true,
|
|
//
|
|
bool detectInsideOBPOIs = true,
|
|
bool detectInsideFVGPOIs = true,
|
|
bool applyFromFilterForPOIs = true
|
|
//
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(dest);
|
|
|
|
//
|
|
bool isValid =
|
|
//
|
|
htfState.HasChild() &&
|
|
mtfState.HasChild() &&
|
|
stfState.HasChild() &&
|
|
HasDirection(forDir)
|
|
//
|
|
;
|
|
if (!isValid)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Implement Logic ...
|
|
|
|
//
|
|
int idx = -1;
|
|
int count = 0;
|
|
bool has = false;
|
|
XCFVG *tmpFVGs[];
|
|
XOBFVG tmpOBFVGs[];
|
|
XCOrderBlock *tmpOBs[];
|
|
XCOrderBlock *tmpWorkingOBs[];
|
|
datetime cTime = TimeCurrent();
|
|
bool isBullish = IsBullish(forDir);
|
|
|
|
//
|
|
if (isBullish)
|
|
{
|
|
//
|
|
has = htfState.HasBullishOrderBlocks();
|
|
if (has)
|
|
{
|
|
//
|
|
Copy(
|
|
htfState.bullishOrderBlocks,
|
|
tmpOBs //
|
|
);
|
|
|
|
//
|
|
has = mtfState.HasBullishFairValueGaps();
|
|
if (has)
|
|
{
|
|
//
|
|
Copy(
|
|
mtfState.bullishFairValueGaps,
|
|
tmpFVGs //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
has = htfState.HasBearishOrderBlocks();
|
|
if (has)
|
|
{
|
|
//
|
|
Copy(
|
|
htfState.bearishOrderBlocks,
|
|
tmpOBs //
|
|
);
|
|
|
|
//
|
|
has = mtfState.HasBearishFairValueGaps();
|
|
if (has)
|
|
{
|
|
//
|
|
Copy(
|
|
mtfState.bearishFairValueGaps,
|
|
tmpFVGs //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
count = ArraySize(tmpOBs);
|
|
has = IsValidSize(count);
|
|
if (has)
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCOrderBlock *iOB = tmpOBs[i];
|
|
|
|
//
|
|
// Check Started State ...
|
|
if (startState == XZONE_STATE_PASSED ||
|
|
startState == XZONE_STATE_FAILED)
|
|
{
|
|
//
|
|
XOHCL passedBar;
|
|
bool isBodyOut = false;
|
|
bool isStarted = iOB.IsStarted(
|
|
forDir,
|
|
passedBar,
|
|
isBodyOut,
|
|
forPeriod //
|
|
);
|
|
|
|
//
|
|
bool isPassed =
|
|
startState == XZONE_STATE_FAILED
|
|
? !isStarted
|
|
: !forceStartedBodyOut
|
|
? isStarted
|
|
: isStarted &&
|
|
isBodyOut;
|
|
|
|
//
|
|
passedBar.Clean();
|
|
if (!isPassed)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Activation State ...
|
|
if (activeState == XZONE_STATE_PASSED ||
|
|
activeState == XZONE_STATE_FAILED)
|
|
{
|
|
//
|
|
XOHCL passedBar;
|
|
bool isBodyIn = false;
|
|
bool isActivated = iOB.IsActivated(
|
|
forDir,
|
|
passedBar,
|
|
isBodyIn,
|
|
forceStartedBodyOut,
|
|
forPeriod //
|
|
);
|
|
|
|
//
|
|
bool isPassed =
|
|
activeState == XZONE_STATE_FAILED
|
|
? !isActivated
|
|
: !forceActivatedBodyIn
|
|
? isActivated
|
|
: isActivated &&
|
|
isBodyIn;
|
|
|
|
//
|
|
passedBar.Clean();
|
|
if (!isPassed)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Break State ...
|
|
if (breakState == XZONE_STATE_PASSED ||
|
|
breakState == XZONE_STATE_FAILED)
|
|
{
|
|
//
|
|
XOHCL passedBar;
|
|
bool isBodyOut = false;
|
|
bool isBreaked = iOB.IsBreaked(
|
|
forDir,
|
|
passedBar,
|
|
isBodyOut,
|
|
forceStartedBodyOut,
|
|
forPeriod //
|
|
);
|
|
|
|
//
|
|
bool isPassed =
|
|
breakState == XZONE_STATE_FAILED
|
|
? !isBreaked
|
|
: !forceBreakedBodyOut
|
|
? isBreaked
|
|
: isBreaked &&
|
|
isBodyOut;
|
|
|
|
//
|
|
passedBar.Clean();
|
|
if (!isPassed)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Work State ...
|
|
if (workState == XZONE_STATE_PASSED ||
|
|
workState == XZONE_STATE_FAILED)
|
|
{
|
|
//
|
|
bool ignoreActivation = activeState != XZONE_STATE_PASSED;
|
|
|
|
//
|
|
XOHCL passedBar;
|
|
bool isBodyOut = false;
|
|
bool isWorked = iOB.IsWorked(
|
|
forDir,
|
|
passedBar,
|
|
isBodyOut,
|
|
ignoreActivation,
|
|
forceStartedBodyOut,
|
|
forceActivatedBodyIn,
|
|
forPeriod //
|
|
);
|
|
|
|
//
|
|
bool isPassed =
|
|
workState == XZONE_STATE_FAILED
|
|
? !isWorked
|
|
: !forceWorkedBodyOut
|
|
? isWorked
|
|
: isWorked &&
|
|
isBodyOut;
|
|
|
|
//
|
|
passedBar.Clean();
|
|
if (!isPassed)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
has = FindIndex(
|
|
idx,
|
|
iOB,
|
|
tmpWorkingOBs //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
ZeroMemory(iOB);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iOB,
|
|
tmpWorkingOBs //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean ...
|
|
Clean(tmpOBs);
|
|
has = HasChild(tmpWorkingOBs);
|
|
if (has)
|
|
{
|
|
//
|
|
Clean(tmpOBs);
|
|
Copy(
|
|
tmpWorkingOBs,
|
|
tmpOBs //
|
|
);
|
|
|
|
//
|
|
Clean(tmpWorkingOBs);
|
|
}
|
|
|
|
//
|
|
// Sorting ...
|
|
if (applySort)
|
|
{
|
|
//
|
|
Clean(tmpWorkingOBs);
|
|
has = HasChild(tmpOBs);
|
|
count = ArraySize(tmpOBs);
|
|
|
|
//
|
|
// Sorting Applied Only when there is more than one item ...
|
|
if (has && count > 1)
|
|
{
|
|
//
|
|
bool hasLast = false;
|
|
XCOrderBlock *last = NULL;
|
|
while (HasChild(tmpOBs))
|
|
{
|
|
//
|
|
// Extract First Last Item ...
|
|
if (!hasLast)
|
|
{
|
|
//
|
|
idx =
|
|
isBullish
|
|
? GetLowest(tmpOBs)
|
|
: GetHighest(tmpOBs);
|
|
hasLast = IsValidIndex(idx);
|
|
if (hasLast)
|
|
{
|
|
//
|
|
last = tmpOBs[idx];
|
|
ArrayRemove(
|
|
tmpOBs,
|
|
idx,
|
|
1 //
|
|
);
|
|
|
|
//
|
|
Add(
|
|
last,
|
|
tmpWorkingOBs //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
has = false;
|
|
XCOrderBlock *iOB = NULL;
|
|
idx =
|
|
isBullish
|
|
? GetLowest(tmpOBs)
|
|
: GetHighest(tmpOBs);
|
|
has = IsValidIndex(idx);
|
|
if (has)
|
|
{
|
|
//
|
|
iOB = tmpOBs[idx];
|
|
ArrayRemove(
|
|
tmpOBs,
|
|
idx,
|
|
1 //
|
|
);
|
|
}
|
|
|
|
//
|
|
if (!has)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Apply From Filter ...
|
|
if (applyFromFilter)
|
|
{
|
|
//
|
|
bool isFromPassed =
|
|
hasLast &&
|
|
last.From() < iOB.From();
|
|
if (!isFromPassed)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Apply Place Filter ...
|
|
if (applyPlaceFilter)
|
|
{
|
|
//
|
|
bool isAbove =
|
|
hasLast &&
|
|
IsAbove(
|
|
iOB,
|
|
last,
|
|
ignoreInside //
|
|
);
|
|
|
|
//
|
|
bool isBelow =
|
|
hasLast &&
|
|
IsBelow(
|
|
iOB,
|
|
last,
|
|
ignoreInside //
|
|
);
|
|
|
|
//
|
|
bool isPlacePassed =
|
|
isBullish
|
|
? isAbove
|
|
: isBelow;
|
|
if (!isPlacePassed)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
last = iOB;
|
|
Add(
|
|
iOB,
|
|
tmpWorkingOBs //
|
|
);
|
|
}
|
|
|
|
//
|
|
if (hasLast &&
|
|
!HasChild(tmpWorkingOBs))
|
|
{
|
|
//
|
|
Add(
|
|
last,
|
|
tmpWorkingOBs //
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
Copy(
|
|
tmpOBs,
|
|
tmpWorkingOBs //
|
|
);
|
|
}
|
|
|
|
//
|
|
if (HasChild(tmpWorkingOBs))
|
|
{
|
|
//
|
|
Copy(
|
|
tmpWorkingOBs,
|
|
tmpOBs //
|
|
);
|
|
}
|
|
//
|
|
}
|
|
|
|
//
|
|
count = ArraySize(tmpOBs);
|
|
has = IsValidSize(count);
|
|
|
|
//
|
|
// Converts to XOBFVG ...
|
|
if (has)
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XOBFVG iOBFVG;
|
|
|
|
//
|
|
iOBFVG.ob = tmpOBs[i];
|
|
|
|
//
|
|
AddRef(
|
|
iOBFVG,
|
|
tmpOBFVGs //
|
|
);
|
|
|
|
//
|
|
iOBFVG.Clean();
|
|
}
|
|
}
|
|
|
|
//
|
|
Clean(dest);
|
|
Clean(tmpOBs);
|
|
Clean(tmpWorkingOBs);
|
|
|
|
//
|
|
// Here we Going to Complete XOBFVGs ...
|
|
has = HasChild(tmpOBFVGs);
|
|
if (has && HasChild(tmpFVGs))
|
|
{
|
|
//
|
|
count = ArraySize(tmpOBFVGs);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XOBFVG iOBFVG = tmpOBFVGs[i];
|
|
|
|
//
|
|
// Detecting FVG ...
|
|
XCFVG *tmpWorkingFVGs[];
|
|
for (int j = 0; j < ArraySize(tmpFVGs); j++)
|
|
{
|
|
//
|
|
XCFVG *jFVG = tmpFVGs[j];
|
|
|
|
//
|
|
// Check Start Date ...
|
|
bool isFromPassed = jFVG.From() > iOBFVG.ob.From();
|
|
if (!isFromPassed)
|
|
{
|
|
//
|
|
ZeroMemory(jFVG);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Place ...
|
|
|
|
//
|
|
bool isAbove = IsAboveDiff(
|
|
jFVG,
|
|
iOBFVG.ob,
|
|
ignoreInside //
|
|
);
|
|
|
|
//
|
|
bool isBelow = IsBelowDiff(
|
|
jFVG,
|
|
iOBFVG.ob,
|
|
ignoreInside //
|
|
);
|
|
|
|
//
|
|
bool isInside = IsInsideDiff(
|
|
jFVG,
|
|
iOBFVG.ob,
|
|
false //
|
|
);
|
|
|
|
//
|
|
bool isPlacePassed =
|
|
isInside ||
|
|
(isBullish
|
|
? isAbove
|
|
: isBelow);
|
|
if (!isPlacePassed)
|
|
{
|
|
//
|
|
ZeroMemory(jFVG);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
jFVG,
|
|
tmpWorkingFVGs //
|
|
);
|
|
}
|
|
|
|
//
|
|
int fvgsCount = ArraySize(tmpWorkingFVGs);
|
|
has = IsValidSize(fvgsCount);
|
|
if (has)
|
|
{
|
|
//
|
|
idx =
|
|
isBullish
|
|
? GetLowest(tmpWorkingFVGs)
|
|
: GetHighest(tmpWorkingFVGs);
|
|
|
|
//
|
|
if (IsValidIndex(idx))
|
|
{
|
|
//
|
|
XCFVG *selectedFVG = tmpWorkingFVGs[idx];
|
|
|
|
//
|
|
iOBFVG.fvg = selectedFVG;
|
|
|
|
//
|
|
bool isExists = FindIndex(
|
|
idx,
|
|
selectedFVG,
|
|
tmpFVGs //
|
|
);
|
|
if (isExists)
|
|
{
|
|
//
|
|
ArrayRemove(
|
|
tmpFVGs,
|
|
idx,
|
|
1 //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
Clean(tmpWorkingFVGs);
|
|
|
|
//
|
|
bool isFVGPassed = iOBFVG.HasFVG();
|
|
if (!isFVGPassed)
|
|
{
|
|
//
|
|
iOBFVG.Clean();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Validate Model ...
|
|
bool isValid = iOBFVG.IsValid();
|
|
if (!isValid)
|
|
{
|
|
//
|
|
iOBFVG.Clean();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
iOBFVG.UpdateTo(cTime);
|
|
|
|
//
|
|
AddRef(
|
|
iOBFVG,
|
|
dest //
|
|
);
|
|
|
|
//
|
|
iOBFVG.Clean();
|
|
}
|
|
|
|
//
|
|
Clean(tmpOBFVGs);
|
|
}
|
|
|
|
//
|
|
// Detecting Other POI(s) ...
|
|
has = HasChild(dest);
|
|
bool canDetectPOIs =
|
|
detectInsideOBPOIs ||
|
|
detectInsideFVGPOIs;
|
|
if (has &&
|
|
canDetectPOIs)
|
|
{
|
|
//
|
|
count = ArraySize(dest);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
// Supply Zones ...
|
|
has = stfState.HasSupplyZones();
|
|
if (has)
|
|
{
|
|
//
|
|
for (int j = 0; j < stfState.CountSupplyZones(); j++)
|
|
{
|
|
//
|
|
bool isInsideOB = IsInsideDiff(
|
|
stfState.supplyZones[j],
|
|
dest[i].ob //
|
|
);
|
|
|
|
//
|
|
bool isInsideFVG = IsInsideDiff(
|
|
stfState.supplyZones[j],
|
|
dest[i].fvg //
|
|
);
|
|
|
|
//
|
|
bool isInside = isInsideOB ||
|
|
isInsideFVG;
|
|
if (isInside)
|
|
{
|
|
//
|
|
bool isPassed =
|
|
detectInsideOBPOIs
|
|
? isInsideOB
|
|
: detectInsideFVGPOIs
|
|
? isInsideFVG
|
|
: false;
|
|
if (isPassed)
|
|
{
|
|
//
|
|
isPassed =
|
|
!applyFromFilterForPOIs
|
|
? isPassed
|
|
: isInsideOB
|
|
? stfState.supplyZones[j].From() > dest[i].ob.From()
|
|
: isInsideFVG
|
|
? stfState.supplyZones[j].From() > dest[i].fvg.From()
|
|
: false;
|
|
}
|
|
|
|
//
|
|
if (isPassed)
|
|
{
|
|
//
|
|
Add(
|
|
stfState.supplyZones[j],
|
|
dest[i].supplyZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Demand Zones ...
|
|
has = stfState.HasDemandZones();
|
|
if (has)
|
|
{
|
|
//
|
|
for (int j = 0; j < stfState.CountDemandZones(); j++)
|
|
{
|
|
//
|
|
bool isInsideOB = IsInsideDiff(
|
|
stfState.demandZones[j],
|
|
dest[i].ob //
|
|
);
|
|
|
|
//
|
|
bool isInsideFVG = IsInsideDiff(
|
|
stfState.demandZones[j],
|
|
dest[i].fvg //
|
|
);
|
|
|
|
//
|
|
bool isInside = isInsideOB ||
|
|
isInsideFVG;
|
|
if (isInside)
|
|
{
|
|
//
|
|
bool isPassed =
|
|
detectInsideOBPOIs
|
|
? isInsideOB
|
|
: detectInsideFVGPOIs
|
|
? isInsideFVG
|
|
: false;
|
|
if (isPassed)
|
|
{
|
|
//
|
|
isPassed =
|
|
!applyFromFilterForPOIs
|
|
? isPassed
|
|
: isInsideOB
|
|
? stfState.demandZones[j].From() > dest[i].ob.From()
|
|
: isInsideFVG
|
|
? stfState.demandZones[j].From() > dest[i].fvg.From()
|
|
: false;
|
|
}
|
|
|
|
//
|
|
if (isPassed)
|
|
{
|
|
//
|
|
Add(
|
|
stfState.demandZones[j],
|
|
dest[i].demandZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Support Zones ...
|
|
has = stfState.HasSupportZones();
|
|
if (has)
|
|
{
|
|
//
|
|
for (int j = 0; j < stfState.CountSupportZones(); j++)
|
|
{
|
|
//
|
|
bool isInsideOB = IsInsideDiff(
|
|
stfState.supportZones[j],
|
|
dest[i].ob //
|
|
);
|
|
|
|
//
|
|
bool isInsideFVG = IsInsideDiff(
|
|
stfState.supportZones[j],
|
|
dest[i].fvg //
|
|
);
|
|
|
|
//
|
|
bool isInside = isInsideOB ||
|
|
isInsideFVG;
|
|
if (isInside)
|
|
{
|
|
//
|
|
bool isPassed =
|
|
detectInsideOBPOIs
|
|
? isInsideOB
|
|
: detectInsideFVGPOIs
|
|
? isInsideFVG
|
|
: false;
|
|
if (isPassed)
|
|
{
|
|
//
|
|
isPassed =
|
|
!applyFromFilterForPOIs
|
|
? isPassed
|
|
: isInsideOB
|
|
? stfState.supportZones[j].From() > dest[i].ob.From()
|
|
: isInsideFVG
|
|
? stfState.supportZones[j].From() > dest[i].fvg.From()
|
|
: false;
|
|
}
|
|
|
|
//
|
|
if (isPassed)
|
|
{
|
|
//
|
|
Add(
|
|
stfState.supportZones[j],
|
|
dest[i].supportZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Resistance Zones ...
|
|
has = stfState.HasResistanceZones();
|
|
if (has)
|
|
{
|
|
//
|
|
for (int j = 0; j < stfState.CountResistanceZones(); j++)
|
|
{
|
|
//
|
|
bool isInsideOB = IsInsideDiff(
|
|
stfState.resistanceZones[j],
|
|
dest[i].ob //
|
|
);
|
|
|
|
//
|
|
bool isInsideFVG = IsInsideDiff(
|
|
stfState.resistanceZones[j],
|
|
dest[i].fvg //
|
|
);
|
|
|
|
//
|
|
bool isInside = isInsideOB ||
|
|
isInsideFVG;
|
|
if (isInside)
|
|
{
|
|
//
|
|
bool isPassed =
|
|
detectInsideOBPOIs
|
|
? isInsideOB
|
|
: detectInsideFVGPOIs
|
|
? isInsideFVG
|
|
: false;
|
|
if (isPassed)
|
|
{
|
|
//
|
|
isPassed =
|
|
!applyFromFilterForPOIs
|
|
? isPassed
|
|
: isInsideOB
|
|
? stfState.resistanceZones[j].From() > dest[i].ob.From()
|
|
: isInsideFVG
|
|
? stfState.resistanceZones[j].From() > dest[i].fvg.From()
|
|
: false;
|
|
}
|
|
|
|
//
|
|
if (isPassed)
|
|
{
|
|
//
|
|
Add(
|
|
stfState.resistanceZones[j],
|
|
dest[i].resistanceZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
Clean(tmpOBs);
|
|
Clean(tmpFVGs);
|
|
Clean(tmpOBFVGs);
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Find Highest Order Block of XOBFVG Collection ...
|
|
*
|
|
* @param source: XOBFVG Collection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int GetHighest(
|
|
XOBFVG &source[] //
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int count = ArraySize(source);
|
|
bool has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOBFVG item;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XOBFVG iOBFVG = source[i];
|
|
|
|
//
|
|
bool canSet =
|
|
!item.IsValid()
|
|
? true
|
|
: item.ob.Upper() < iOBFVG.ob.Upper();
|
|
if (canSet)
|
|
{
|
|
//
|
|
item = iOBFVG;
|
|
result = i;
|
|
}
|
|
|
|
//
|
|
iOBFVG.Clean();
|
|
}
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Find Lowest Order Block of XOBFVG Collection ...
|
|
*
|
|
* @param source: XOBFVG Collection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int GetLowest(
|
|
XOBFVG &source[] //
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int count = ArraySize(source);
|
|
bool has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOBFVG item;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XOBFVG iOBFVG = source[i];
|
|
|
|
//
|
|
bool canSet =
|
|
!item.IsValid()
|
|
? true
|
|
: item.ob.Lower() > iOBFVG.ob.Lower();
|
|
if (canSet)
|
|
{
|
|
//
|
|
item = iOBFVG;
|
|
result = i;
|
|
}
|
|
|
|
//
|
|
iOBFVG.Clean();
|
|
}
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Find Youngest Order Block of XOBFVG Collection ...
|
|
*
|
|
* @param source: XOBFVG Collection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int GetYoungest(
|
|
XOBFVG &source[] //
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int count = ArraySize(source);
|
|
bool has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOBFVG item;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XOBFVG iOBFVG = source[i];
|
|
|
|
//
|
|
bool canSet =
|
|
!item.IsValid()
|
|
? true
|
|
: item.ob.From() < iOBFVG.ob.From();
|
|
if (canSet)
|
|
{
|
|
//
|
|
item = iOBFVG;
|
|
result = i;
|
|
}
|
|
|
|
//
|
|
iOBFVG.Clean();
|
|
}
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Find Oldest Order Block of XOBFVG Collection ...
|
|
*
|
|
* @param source: XOBFVG Collection ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int GetOldest(
|
|
XOBFVG &source[] //
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int count = ArraySize(source);
|
|
bool has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOBFVG item;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XOBFVG iOBFVG = source[i];
|
|
|
|
//
|
|
bool canSet =
|
|
!item.IsValid()
|
|
? true
|
|
: item.ob.From() > iOBFVG.ob.From();
|
|
if (canSet)
|
|
{
|
|
//
|
|
item = iOBFVG;
|
|
result = i;
|
|
}
|
|
|
|
//
|
|
iOBFVG.Clean();
|
|
}
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Sort XOBFVG Collection based on OB Place ...
|
|
*
|
|
* @param source: XOBFVG instance Collection ...
|
|
* @param forDir: ENUM_X_DIRECTION member ...
|
|
* @param ignoreInside: Specified Ignoring price inside in Sorting ...
|
|
*/
|
|
void Sort(
|
|
XOBFVG &source[],
|
|
ENUM_X_DIRECTION forDir,
|
|
bool ignoreInside = true //
|
|
)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
int count = 0;
|
|
bool has = false;
|
|
|
|
//
|
|
has = HasDirection(forDir);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool isBullish = IsBullish(forDir);
|
|
|
|
//
|
|
count = ArraySize(source);
|
|
has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XOBFVG last;
|
|
XOBFVG tmpItems[];
|
|
bool hasLast = false;
|
|
while (HasChild(source))
|
|
{
|
|
//
|
|
if (!hasLast)
|
|
{
|
|
//
|
|
idx = GetOldest(source);
|
|
hasLast = IsValidIndex(idx);
|
|
if (hasLast)
|
|
{
|
|
//
|
|
last = source[idx];
|
|
ArrayRemove(
|
|
source,
|
|
idx,
|
|
1 //
|
|
);
|
|
|
|
//
|
|
AddRef(
|
|
last,
|
|
tmpItems //
|
|
);
|
|
}
|
|
|
|
//
|
|
count = ArraySize(source);
|
|
has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
//
|
|
last.Clean();
|
|
break;
|
|
}
|
|
}
|
|
if (!hasLast)
|
|
{
|
|
//
|
|
last.Clean();
|
|
break;
|
|
}
|
|
|
|
//
|
|
XOBFVG item;
|
|
idx = GetOldest(source);
|
|
has = IsValidIndex(idx);
|
|
if (!has)
|
|
{
|
|
//
|
|
item.Clean();
|
|
last.Clean();
|
|
break;
|
|
}
|
|
item = source[idx];
|
|
ArrayRemove(
|
|
source,
|
|
idx,
|
|
1 //
|
|
);
|
|
|
|
//
|
|
bool isAbove = IsAbove(
|
|
item.ob,
|
|
last.ob,
|
|
ignoreInside //
|
|
);
|
|
|
|
//
|
|
bool isBelow = IsBelow(
|
|
item.ob,
|
|
last.ob,
|
|
ignoreInside //
|
|
);
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? isAbove
|
|
: isBelow;
|
|
if (has)
|
|
{
|
|
//
|
|
AddRef(
|
|
item,
|
|
tmpItems //
|
|
);
|
|
|
|
//
|
|
last = item;
|
|
hasLast = item.IsValid();
|
|
}
|
|
}
|
|
|
|
//
|
|
Clean(source);
|
|
Copy(
|
|
tmpItems,
|
|
source //
|
|
);
|
|
|
|
//
|
|
Clean(tmpItems);
|
|
}
|
|
|
|
//
|