- [] required to Complete Supply and Demand Zone Retrieve in XConsolidation Zone Struct ...
2027 lines
39 KiB
Plaintext
2027 lines
39 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Common Library
|
|
// --------------------------------------
|
|
// Name: X121SMCLib
|
|
// Description: provide all commonly used functions ...
|
|
//
|
|
//
|
|
// 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 "../../Helpers/x-saherelm.x121.helper.mq5"
|
|
#include "../../Libraries/x-saherelm.common.lib.mq5"
|
|
#include "../../Libraries/x-saherelm.x-poi.lib.mq5"
|
|
#include "../../Libraries/x-saherelm.x-trade.lib.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
const string X121SMCStrategyToken = "X121SMC";
|
|
|
|
const string X121SMCPVToken = "X121SMCPV";
|
|
const string X121SMCBOSToken = "X121SMCBOS";
|
|
const string X121SMCSARToken = "X121SMCSAR";
|
|
const string X121SMCCHOCHToken = "X121SMCCHOCH";
|
|
const string X121SMCPULLBKToken = "X121SMCPULLBK";
|
|
|
|
enum ENUM_X_121_SMC_PROVIDERS
|
|
{
|
|
//
|
|
X_121_SMC_PROVIDER_NONE,
|
|
X_121_SMC_PROVIDER_PV,
|
|
X_121_SMC_PROVIDER_BOS,
|
|
X_121_SMC_PROVIDER_SAR,
|
|
X_121_SMC_PROVIDER_CHOCH,
|
|
X_121_SMC_PROVIDER_PULLBK,
|
|
};
|
|
|
|
enum ENUM_XPV_PIVOTS
|
|
{
|
|
XPV_NONE,
|
|
XPV_PEAK,
|
|
XPV_VALE,
|
|
};
|
|
|
|
struct XPVPivot
|
|
{
|
|
//
|
|
string symbol;
|
|
ENUM_TIMEFRAMES period;
|
|
|
|
//
|
|
double upper;
|
|
double lower;
|
|
|
|
//
|
|
datetime from;
|
|
datetime to;
|
|
|
|
//
|
|
int repetition;
|
|
|
|
//
|
|
ENUM_XPV_PIVOTS type;
|
|
|
|
//
|
|
// Combined all States ...
|
|
XPOIState state;
|
|
|
|
//
|
|
XPVPivot()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
/**
|
|
* Cleanup ...
|
|
*/
|
|
void Clean()
|
|
{
|
|
//
|
|
upper = 0;
|
|
lower = 0;
|
|
|
|
//
|
|
to = NULL;
|
|
from = NULL;
|
|
symbol = NULL;
|
|
period = NULL;
|
|
|
|
//
|
|
repetition = 0;
|
|
|
|
//
|
|
type = XPV_NONE;
|
|
|
|
//
|
|
state.Clean();
|
|
}
|
|
|
|
/**
|
|
* Validation ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
IsValid(to) &&
|
|
IsValid(from) &&
|
|
IsValid(symbol) &&
|
|
IsValid(period) &&
|
|
repetition > 0 &&
|
|
type != XPV_NONE &&
|
|
(upper > 0 || lower > 0)
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Pivot is Peak ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsPeak()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsValid() &&
|
|
type == XPV_PEAK;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Pivot is Vale ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsVale()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsValid() &&
|
|
type == XPV_VALE;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Calculate Lower for Peaks ...
|
|
*
|
|
* @return ( double )
|
|
*/
|
|
double CalculateLower()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
if (!IsValid() ||
|
|
!IsPeak())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int fromIndex = iBarShift(
|
|
symbol,
|
|
period,
|
|
from //
|
|
);
|
|
|
|
//
|
|
int toIndex = iBarShift(
|
|
symbol,
|
|
period,
|
|
to //
|
|
);
|
|
|
|
//
|
|
int count =
|
|
fromIndex - toIndex;
|
|
|
|
//
|
|
int llIDX = iLowest(
|
|
symbol,
|
|
period,
|
|
MODE_LOW,
|
|
count,
|
|
toIndex //
|
|
);
|
|
|
|
//
|
|
result =
|
|
iLow(
|
|
symbol,
|
|
period,
|
|
llIDX //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Calculate Upper for Vales ...
|
|
*
|
|
* @return ( double )
|
|
*/
|
|
double CalculateUpper()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
if (!IsValid() ||
|
|
!IsVale())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int fromIndex = iBarShift(
|
|
symbol,
|
|
period,
|
|
from //
|
|
);
|
|
|
|
//
|
|
int toIndex = iBarShift(
|
|
symbol,
|
|
period,
|
|
to //
|
|
);
|
|
|
|
//
|
|
int count =
|
|
fromIndex - toIndex;
|
|
|
|
//
|
|
int hhIDX = iHighest(
|
|
symbol,
|
|
period,
|
|
MODE_HIGH,
|
|
count,
|
|
toIndex //
|
|
);
|
|
|
|
//
|
|
result =
|
|
iHigh(
|
|
symbol,
|
|
period,
|
|
hhIDX //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Calculate Range of Pivot ...
|
|
*
|
|
* @return ( double )
|
|
*/
|
|
double CalculateRange()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
if (!IsValid() ||
|
|
upper <= 0 ||
|
|
lower <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = upper - lower;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Calculate Middle of Pivot ...
|
|
* Used for
|
|
* @return ( double )
|
|
*/
|
|
double CalculateMid()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
double range = CalculateRange();
|
|
if (range <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = lower + (range / 2);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Calculate Liquidity Percent for Specified Direction ...
|
|
*
|
|
* @param forDirection: ENUM_X_DIRECTION member, Specified Liquidity ...
|
|
* @param price: Double, Specified Price ...
|
|
*
|
|
* @return ( double )
|
|
*/
|
|
double CalculateLiquidityPercent(
|
|
ENUM_X_DIRECTION forDirection,
|
|
double price = 0 //
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = state
|
|
.CalculateLiquidityPercent(
|
|
forDirection,
|
|
price //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
|
|
/**
|
|
* Get Unique Identifier ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string GetTag()
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string hash = symbol + "_" +
|
|
ToString(period) + "_" +
|
|
ToString(upper) + "," +
|
|
ToString(lower) + "_" +
|
|
ToString(from) + "," +
|
|
ToString(to);
|
|
hash = ToMD5(hash);
|
|
|
|
//
|
|
string typeStr =
|
|
type == XPV_PEAK
|
|
? "Peak"
|
|
: "Vale";
|
|
|
|
//
|
|
result =
|
|
//
|
|
"XPV_" +
|
|
typeStr + "_" +
|
|
symbol + "_" +
|
|
ToString(period) +
|
|
"_" + hash
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
//
|
|
// Each Market Cycle (Time Frame) Conditions
|
|
// Model as this Structure ...
|
|
struct X121SMCCycleConditions
|
|
{
|
|
//
|
|
string symbol;
|
|
ENUM_TIMEFRAMES period;
|
|
datetime time;
|
|
|
|
//
|
|
X121Conditions x121Conditions;
|
|
|
|
//
|
|
XPOIState state;
|
|
|
|
//
|
|
void X121SMCStrategyConditions()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
/**
|
|
* Cleanup ...
|
|
*/
|
|
void Clean()
|
|
{
|
|
//
|
|
symbol = NULL;
|
|
period = NULL;
|
|
time = NULL;
|
|
|
|
//
|
|
state.Clean();
|
|
x121Conditions.Clean();
|
|
}
|
|
|
|
/**
|
|
* Validate ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsSpecifiedValid(symbol) &&
|
|
IsSpecifiedValid(period) &&
|
|
IsSpecifiedValid(time);
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reporter Functions ...
|
|
|
|
/**
|
|
* Generate Provided Scores ...
|
|
*
|
|
* @param bullishScore: Integer, reference ...
|
|
* @param bearishScore: Integer, referenceF ...
|
|
*/
|
|
void GenerateScore(
|
|
double &bullishScore,
|
|
double &bearishScore //
|
|
)
|
|
{
|
|
//
|
|
bullishScore = 0;
|
|
bearishScore = 0;
|
|
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double bullScore = 0;
|
|
double bearScore = 0;
|
|
}
|
|
|
|
/**
|
|
* Generate Summary ...
|
|
*
|
|
* @param onlyCommons: Boolean ...
|
|
* @param onlyConditions: Boolean ...
|
|
* @param includeScores: Boolean ...
|
|
* @param ignoreFalseConditions: Boolean ...
|
|
* @param separator: String ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string GenerateSummary(
|
|
bool onlyCommons = false,
|
|
bool onlyConditions = false,
|
|
bool includeScores = true,
|
|
bool ignoreFalseConditions = true,
|
|
string separator = "\n" //
|
|
)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
double bullishScore = 0;
|
|
double bearishScore = 0;
|
|
GenerateScore(
|
|
bullishScore,
|
|
bearishScore //
|
|
);
|
|
|
|
//
|
|
string commonStr = GenerateSpecifiedCommonSummary(
|
|
this,
|
|
separator,
|
|
includeScores,
|
|
true //
|
|
);
|
|
|
|
//
|
|
string conditionsStr =
|
|
//
|
|
""
|
|
//
|
|
;
|
|
|
|
//
|
|
result =
|
|
//
|
|
"[" + GetTag() + "]" + separator +
|
|
"----------" + separator +
|
|
(onlyConditions
|
|
? ""
|
|
: commonStr) +
|
|
" " + separator +
|
|
(onlyCommons
|
|
? ""
|
|
: conditionsStr) +
|
|
""
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get Unique Identifier ...
|
|
*
|
|
* @param forObject: Boolean ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string GetTag(
|
|
bool forObject = false //
|
|
)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
string typeName = GetTypeName(this);
|
|
|
|
//
|
|
if (!forObject)
|
|
{
|
|
result = typeName;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = typeName + "_" +
|
|
symbol + "_" + ToString(period) + "_" +
|
|
ToMD5(time);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
//
|
|
// Model Signalling Conditions ...
|
|
struct X121SMCStrategyConditions
|
|
{
|
|
//
|
|
string symbol;
|
|
ENUM_TIMEFRAMES period;
|
|
datetime time;
|
|
|
|
//
|
|
// Cycles Conditions here ...
|
|
X121SMCCycleConditions triggerConditions;
|
|
X121SMCCycleConditions decisionConditions;
|
|
X121SMCCycleConditions analyseConditions;
|
|
X121SMCCycleConditions verificationConditions;
|
|
X121SMCCycleConditions consolidationConditions;
|
|
X121SMCCycleConditions visionConditions;
|
|
|
|
//
|
|
double sl;
|
|
string provider;
|
|
ENUM_X_DIRECTION marketStructureDir;
|
|
|
|
//
|
|
|
|
|
|
//
|
|
void X121SMCStrategyConditions()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
/**
|
|
* Cleanup ...
|
|
*/
|
|
void Clean()
|
|
{
|
|
//
|
|
symbol = NULL;
|
|
period = NULL;
|
|
time = NULL;
|
|
|
|
//
|
|
sl = 0;
|
|
provider = NULL;
|
|
marketStructureDir = X_DIRECTION_NONE;
|
|
|
|
//
|
|
triggerConditions.Clean();
|
|
decisionConditions.Clean();
|
|
analyseConditions.Clean();
|
|
verificationConditions.Clean();
|
|
consolidationConditions.Clean();
|
|
visionConditions.Clean();
|
|
}
|
|
|
|
/**
|
|
* Validate ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsSpecifiedValid(symbol) &&
|
|
IsSpecifiedValid(period) &&
|
|
IsSpecifiedValid(time);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reporter Functions ...
|
|
|
|
/**
|
|
* Generate Provided Scores ...
|
|
*
|
|
* @param bullishScore: Integer, reference ...
|
|
* @param bearishScore: Integer, referenceF ...
|
|
*/
|
|
void GenerateScore(
|
|
double &bullishScore,
|
|
double &bearishScore //
|
|
)
|
|
{
|
|
//
|
|
bullishScore = 0;
|
|
bearishScore = 0;
|
|
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double bullScore = 0;
|
|
double bearScore = 0;
|
|
}
|
|
|
|
/**
|
|
* Generate Summary ...
|
|
*
|
|
* @param onlyCommons: Boolean ...
|
|
* @param onlyConditions: Boolean ...
|
|
* @param includeScores: Boolean ...
|
|
* @param ignoreFalseConditions: Boolean ...
|
|
* @param separator: String ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string GenerateSummary(
|
|
bool onlyCommons = false,
|
|
bool onlyConditions = false,
|
|
bool includeScores = true,
|
|
bool ignoreFalseConditions = true,
|
|
string separator = "\n" //
|
|
)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
double bullishScore = 0;
|
|
double bearishScore = 0;
|
|
GenerateScore(
|
|
bullishScore,
|
|
bearishScore //
|
|
);
|
|
|
|
//
|
|
string commonStr = GenerateSpecifiedCommonSummary(
|
|
this,
|
|
separator,
|
|
includeScores,
|
|
true //
|
|
);
|
|
|
|
//
|
|
string conditionsStr =
|
|
//
|
|
""
|
|
//
|
|
;
|
|
|
|
//
|
|
result =
|
|
//
|
|
"[" + GetTag() + "]" + separator +
|
|
"----------" + separator +
|
|
(onlyConditions
|
|
? ""
|
|
: commonStr) +
|
|
" " + separator +
|
|
(onlyCommons
|
|
? ""
|
|
: conditionsStr) +
|
|
""
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get Unique Identifier ...
|
|
*
|
|
* @param forObject: Boolean ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string GetTag(
|
|
bool forObject = false //
|
|
)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
string typeName = GetTypeName(this);
|
|
|
|
//
|
|
if (!forObject)
|
|
{
|
|
result = typeName;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = typeName + "_" +
|
|
symbol + "_" + ToString(period) + "_" +
|
|
ToMD5(time);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
typedef void (*TOnX121SMCSignal)(
|
|
XSignal &signal,
|
|
X121SMCStrategyConditions &conditions //
|
|
);
|
|
|
|
//
|
|
// Extentions ...
|
|
|
|
/**
|
|
* Find Oldest Pivot Index ...
|
|
*
|
|
* @param pivots: XPVPivot instance ...
|
|
* @return ( int )
|
|
*/
|
|
int FindOldestPivotIndex(
|
|
XPVPivot &pivots[] //
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int count = ArraySize(pivots);
|
|
if (!IsValidSize(count))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPVPivot iPivot = pivots[i];
|
|
|
|
//
|
|
bool isOldest = true;
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
//
|
|
XPVPivot jPivot = pivots[j];
|
|
|
|
//
|
|
isOldest = iPivot.from <= jPivot.from;
|
|
if (!isOldest)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (isOldest)
|
|
{
|
|
//
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Sort Pivots Based On From Date ...
|
|
*
|
|
* @param pivots: XPVPivot instance Collection reference ...
|
|
*/
|
|
void SortPivots(
|
|
XPVPivot &pivots[] //
|
|
)
|
|
{
|
|
//
|
|
XPVPivot tmp[];
|
|
Copy(
|
|
pivots,
|
|
tmp //
|
|
);
|
|
|
|
//
|
|
Clean(pivots);
|
|
|
|
//
|
|
while (ArraySize(tmp) > 0)
|
|
{
|
|
//
|
|
if (ArraySize(tmp) == 1)
|
|
{
|
|
//
|
|
AddRef(
|
|
tmp[0],
|
|
pivots //
|
|
);
|
|
|
|
//
|
|
ArrayRemove(
|
|
tmp,
|
|
0,
|
|
1);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
int idx = FindOldestPivotIndex(tmp);
|
|
if (IsValidIndex(idx))
|
|
{
|
|
//
|
|
AddRef(
|
|
tmp[idx],
|
|
pivots //
|
|
);
|
|
|
|
//
|
|
ArrayRemove(
|
|
tmp,
|
|
idx,
|
|
1 //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
Clean(tmp);
|
|
}
|
|
|
|
bool IsPivotBreaked(
|
|
XPVPivot &pivot,
|
|
XPVPivot &pivots[] //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = pivot.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(pivots);
|
|
result = IsValidSize(pivots);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// For Peaks Highs Must not Broked ...
|
|
// For Vales Lows Must not Broked ...
|
|
bool isPeak = pivot.IsPeak();
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPVPivot iPivot = pivots[i];
|
|
|
|
//
|
|
result = isPeak
|
|
? iPivot.upper > pivot.upper
|
|
: iPivot.lower < pivot.lower;
|
|
if (result)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Filter Broken Pivots for Detect Order Flow ...
|
|
*
|
|
* @param pivots: XPVPivot instance Collection reference ...
|
|
*/
|
|
void FilterBrokenPivots(
|
|
XPVPivot &pivots[] //
|
|
)
|
|
{
|
|
//
|
|
XPVPivot tmp[];
|
|
Copy(
|
|
pivots,
|
|
tmp //
|
|
);
|
|
|
|
//
|
|
Clean(pivots);
|
|
|
|
//
|
|
while (ArraySize(tmp) > 0)
|
|
{
|
|
//
|
|
if (ArraySize(tmp) == 1)
|
|
{
|
|
//
|
|
AddRef(
|
|
tmp[0],
|
|
pivots //
|
|
);
|
|
|
|
//
|
|
ArrayRemove(
|
|
tmp,
|
|
0,
|
|
1 //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
XPVPivot pivot = tmp[0];
|
|
|
|
//
|
|
ArrayRemove(
|
|
tmp,
|
|
0,
|
|
1 //
|
|
);
|
|
|
|
//
|
|
bool isBreaked = IsPivotBreaked(
|
|
pivot,
|
|
tmp //
|
|
);
|
|
if (!isBreaked)
|
|
{
|
|
//
|
|
AddRef(
|
|
pivot,
|
|
pivots //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
Clean(tmp);
|
|
}
|
|
|
|
/**
|
|
* Calculate Order Flow Direction ...
|
|
*
|
|
* @param orderFlow: XPVPivot instance Collection reference ...
|
|
* @param dir: ENUM_X_DIRECTION member reference, Hold's Calculated Direction ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool DetectPivotDirection(
|
|
XPVPivot &orderFlow[],
|
|
ENUM_X_DIRECTION &dir //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
dir = X_DIRECTION_NONE;
|
|
|
|
//
|
|
int count = ArraySize(orderFlow);
|
|
result = IsValidSize(count);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// For Bullish Direction all Exists lowers must be Greater or Equal Prev ...
|
|
bool isBullish = false;
|
|
bool canLookupForBullish = true;
|
|
|
|
//
|
|
// For Bearish Direction all Exists uppers must be Lesser or Equal Prev ...
|
|
bool isBearish = false;
|
|
bool canLookupForBearish = true;
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPVPivot iPivot = orderFlow[i];
|
|
|
|
//
|
|
XPVPivot tmp[];
|
|
Copy(
|
|
orderFlow,
|
|
tmp //
|
|
);
|
|
ArrayRemove(
|
|
tmp,
|
|
0,
|
|
i + 1 //
|
|
);
|
|
|
|
//
|
|
int tmpCount = ArraySize(tmp);
|
|
if (IsValidSize(tmpCount))
|
|
{
|
|
//
|
|
for (int j = 0; j < tmpCount; j++)
|
|
{
|
|
//
|
|
XPVPivot jPivot = tmp[j];
|
|
|
|
//
|
|
// Check Bullish Direction ...
|
|
isBullish =
|
|
canLookupForBullish &&
|
|
jPivot.lower >= iPivot.lower;
|
|
if (!isBullish)
|
|
{
|
|
canLookupForBullish = false;
|
|
}
|
|
|
|
//
|
|
// Check Bearish Direction ...
|
|
isBearish =
|
|
canLookupForBearish &&
|
|
jPivot.upper <= iPivot.upper;
|
|
if (!isBearish)
|
|
{
|
|
canLookupForBearish = false;
|
|
}
|
|
|
|
//
|
|
if (!canLookupForBullish &&
|
|
!canLookupForBearish)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Clean(tmp);
|
|
}
|
|
|
|
//
|
|
if (isBullish &&
|
|
!isBearish)
|
|
{
|
|
dir = X_DIRECTION_BULLISH;
|
|
}
|
|
else if (isBearish &&
|
|
!isBullish)
|
|
{
|
|
dir = X_DIRECTION_BEARISH;
|
|
}
|
|
|
|
//
|
|
result = dir != X_DIRECTION_NONE;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Detect all POIs which inside a Pivot ...
|
|
*
|
|
* @param pivot: XPVPivot instance, Specified Pivot ...
|
|
* @param source: XPOIState instance, Reference to Source State ...
|
|
* @param state: XPOIState instance, Reference to Detected Pivots ...
|
|
* @param lookupSwingHighs: Boolean ...
|
|
* @param lookupSwingLows: Boolean ...
|
|
* @param lookupSupportZones: Boolean ...
|
|
* @param lookupResistanceZones: Boolean ...
|
|
* @param lookupSupplyZones: Boolean ...
|
|
* @param lookupDemandZones: Boolean ...
|
|
* @param lookupBullishOrderBlocks: Boolean ...
|
|
* @param lookupBearishOrderBlocks: Boolean ...
|
|
* @param lookupBullishFairValueGaps: Boolean ...
|
|
* @param lookupBearishFairValueGaps: Boolean ...
|
|
* @param lookupBullishRejectionBars: Boolean ...
|
|
* @param lookupBearishRejectionBars: Boolean ...
|
|
* @param lookupBullishMomentumBars: Boolean ...
|
|
* @param lookupBearishMomentumBars: Boolean ...
|
|
*/
|
|
void DetectPivotPOIs(
|
|
XPVPivot &pivot,
|
|
XPOIState &source,
|
|
XPOIState &state,
|
|
bool lookupSwingHighs = true,
|
|
bool lookupSwingLows = true,
|
|
bool lookupSupportZones = true,
|
|
bool lookupResistanceZones = true,
|
|
bool lookupSupplyZones = true,
|
|
bool lookupDemandZones = true,
|
|
bool lookupBullishOrderBlocks = true,
|
|
bool lookupBearishOrderBlocks = true,
|
|
bool lookupBullishFairValueGaps = true,
|
|
bool lookupBearishFairValueGaps = true,
|
|
bool lookupBullishRejectionBars = true,
|
|
bool lookupBearishRejectionBars = true,
|
|
bool lookupBullishMomentumBars = true,
|
|
bool lookupBearishMomentumBars = true //
|
|
)
|
|
{
|
|
//
|
|
state.Clean();
|
|
|
|
//
|
|
if (!pivot.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int count = 0;
|
|
|
|
//
|
|
state.symbol = pivot.symbol;
|
|
state.period = pivot.period;
|
|
state.time = TimeCurrent();
|
|
|
|
//
|
|
// Swings ...
|
|
|
|
//
|
|
// Swing High ...
|
|
if (lookupSwingHighs)
|
|
{
|
|
//
|
|
count = ArraySize(source.swingHighs);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCSwing *iSwing = source.swingHighs[i];
|
|
|
|
//
|
|
XOHCL iBar;
|
|
bool isFilled = iSwing.FillBar(iBar);
|
|
if (!isFilled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isInside = iBar.high <= pivot.upper;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iSwing,
|
|
state.swingHighs //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Swing Low ...
|
|
if (lookupSwingLows)
|
|
{
|
|
//
|
|
count = ArraySize(source.swingLows);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCSwing *iSwing = source.swingLows[i];
|
|
|
|
//
|
|
XOHCL iBar;
|
|
bool isFilled = iSwing.FillBar(iBar);
|
|
if (!isFilled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isInside = iBar.low >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iSwing,
|
|
state.swingLows //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Support and Resistance Zones ...
|
|
|
|
//
|
|
// Support Zones ...
|
|
if (lookupSupportZones)
|
|
{
|
|
//
|
|
count = ArraySize(source.supportZones);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCSupportZone *iZone = source.supportZones[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.supportZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Resistance Zones ...
|
|
if (lookupResistanceZones)
|
|
{
|
|
//
|
|
count = ArraySize(source.resistanceZones);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCResistanceZone *iZone = source.resistanceZones[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.resistanceZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Supply and Demand Zones ...
|
|
|
|
//
|
|
// Supply Zones ...
|
|
if (lookupSupplyZones)
|
|
{
|
|
//
|
|
count = ArraySize(source.supplyZones);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCSupplyZone *iZone = source.supplyZones[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.supplyZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Demand Zones ...
|
|
if (lookupDemandZones)
|
|
{
|
|
//
|
|
count = ArraySize(source.demandZones);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCDemandZone *iZone = source.demandZones[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.demandZones //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Order Blocks ...
|
|
|
|
//
|
|
// Bullish Order Blocks ...
|
|
if (lookupBullishOrderBlocks)
|
|
{
|
|
//
|
|
count = ArraySize(source.bullishOrderBlocks);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCOrderBlock *iZone = source.bullishOrderBlocks[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.bullishOrderBlocks //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bearish Order Blocks ...
|
|
if (lookupBearishOrderBlocks)
|
|
{
|
|
//
|
|
count = ArraySize(source.bearishOrderBlocks);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCOrderBlock *iZone = source.bearishOrderBlocks[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.bearishOrderBlocks //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fair Value Gaps ...
|
|
|
|
//
|
|
// Bullish Fair Value Gaps ...
|
|
if (lookupBullishFairValueGaps)
|
|
{
|
|
//
|
|
count = ArraySize(source.bullishFairValueGaps);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCFVG *iZone = source.bullishFairValueGaps[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.bullishFairValueGaps //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bearish Fair Value Gaps ...
|
|
if (lookupBearishFairValueGaps)
|
|
{
|
|
//
|
|
count = ArraySize(source.bearishFairValueGaps);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCFVG *iZone = source.bearishFairValueGaps[i];
|
|
|
|
//
|
|
bool isInside =
|
|
iZone.Upper() <= pivot.upper &&
|
|
iZone.Lower() >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iZone,
|
|
state.bearishFairValueGaps //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Rejection Bars ...
|
|
|
|
//
|
|
// Bullish Rejection Bars ...
|
|
if (lookupBullishRejectionBars)
|
|
{
|
|
//
|
|
count = ArraySize(source.bullishRejectionBars);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCRejectionBar *iRejection = source.bullishRejectionBars[i];
|
|
|
|
//
|
|
XOHCL iBar;
|
|
bool isFilled = iRejection.FillBar(iBar);
|
|
if (!isFilled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isInside = iBar.low <= pivot.upper &&
|
|
iBar.low >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iRejection,
|
|
state.bullishRejectionBars //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bearish Rejection Bars ...
|
|
if (lookupBearishRejectionBars)
|
|
{
|
|
//
|
|
count = ArraySize(source.bearishRejectionBars);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCRejectionBar *iRejection = source.bearishRejectionBars[i];
|
|
|
|
//
|
|
XOHCL iBar;
|
|
bool isFilled = iRejection.FillBar(iBar);
|
|
if (!isFilled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isInside = iBar.high <= pivot.upper &&
|
|
iBar.high >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iRejection,
|
|
state.bearishRejectionBars //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Momentum Bars ...
|
|
|
|
//
|
|
// Bullish Momentum Bars ...
|
|
if (lookupBullishMomentumBars)
|
|
{
|
|
//
|
|
count = ArraySize(source.bullishMomentumBars);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCMomentumBar *iMomentum = source.bullishMomentumBars[i];
|
|
|
|
//
|
|
XOHCL iBar;
|
|
bool isFilled = iMomentum.FillBar(iBar);
|
|
if (!isFilled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isInside = iBar.low <= pivot.upper &&
|
|
iBar.low >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iMomentum,
|
|
state.bullishMomentumBars //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bearish Momenum Bars ...
|
|
if (lookupBearishMomentumBars)
|
|
{
|
|
//
|
|
count = ArraySize(source.bearishMomentumBars);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCMomentumBar *iMomentum = source.bearishMomentumBars[i];
|
|
|
|
//
|
|
XOHCL iBar;
|
|
bool isFilled = iMomentum.FillBar(iBar);
|
|
if (!isFilled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isInside = iBar.high <= pivot.upper &&
|
|
iBar.high >= pivot.lower;
|
|
if (isInside)
|
|
{
|
|
//
|
|
Add(
|
|
iMomentum,
|
|
state.bearishMomentumBars //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Provider Extensions ...
|
|
|
|
/**
|
|
* Validate a Signal Provider ...
|
|
*
|
|
* @param value: ENUM_X_121_SMC_PROVIDERS member ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsValid(ENUM_X_121_SMC_PROVIDERS value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = value != X_121_SMC_PROVIDER_NONE;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Converts a Signal Provider to String ...
|
|
*
|
|
* @param value: ENUM_X_121_SMC_PROVIDERS member ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string ToString(ENUM_X_121_SMC_PROVIDERS value)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsValid(value))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
switch (value)
|
|
{
|
|
//
|
|
case X_121_SMC_PROVIDER_PV:
|
|
result = X121SMCPVToken;
|
|
break;
|
|
|
|
//
|
|
case X_121_SMC_PROVIDER_BOS:
|
|
result = X121SMCBOSToken;
|
|
break;
|
|
|
|
//
|
|
case X_121_SMC_PROVIDER_SAR:
|
|
result = X121SMCSARToken;
|
|
break;
|
|
|
|
//
|
|
case X_121_SMC_PROVIDER_CHOCH:
|
|
result = X121SMCCHOCHToken;
|
|
break;
|
|
|
|
//
|
|
case X_121_SMC_PROVIDER_PULLBK:
|
|
result = X121SMCPULLBKToken;
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Parse Specified String as a Signal Provider ...
|
|
*
|
|
* @param value: String ...
|
|
*
|
|
* @return ( ENUM_X_121_SMC_PROVIDERS )
|
|
*/
|
|
ENUM_X_121_SMC_PROVIDERS ParseProvider(string value)
|
|
{
|
|
//
|
|
ENUM_X_121_SMC_PROVIDERS result = X_121_SMC_PROVIDER_NONE;
|
|
|
|
//
|
|
if (!IsValid(value))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (Contains(X121SMCPVToken, value))
|
|
{
|
|
result = X_121_SMC_PROVIDER_PV;
|
|
}
|
|
else if (Contains(X121SMCBOSToken, value))
|
|
{
|
|
result = X_121_SMC_PROVIDER_BOS;
|
|
}
|
|
else if (Contains(X121SMCSARToken, value))
|
|
{
|
|
result = X_121_SMC_PROVIDER_SAR;
|
|
}
|
|
else if (Contains(X121SMCCHOCHToken, value))
|
|
{
|
|
result = X_121_SMC_PROVIDER_CHOCH;
|
|
}
|
|
else if (Contains(X121SMCPULLBKToken, value))
|
|
{
|
|
result = X_121_SMC_PROVIDER_PULLBK;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Model Value Changed ...
|
|
struct XValueChange
|
|
{
|
|
//
|
|
// Props ...
|
|
double before; // Before Change Value ...
|
|
double after; // After Change Value ...
|
|
datetime time; // Change Time ...
|
|
ENUM_X_DIRECTION dir; // Change Direction ...
|
|
|
|
//
|
|
// Constructor ...
|
|
XValueChange()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
/**
|
|
* Initialize ...
|
|
*
|
|
* @param _before: Double, Value Before Change ...
|
|
* @param _after: Double, Value After Change ...
|
|
* @param _time: Datetime, Change Time ...
|
|
* @param _dir: ENUM_X_DIRECTION member, Specified Changed Direction ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool Init(
|
|
double _before, // Before Change Value ...
|
|
double _after, // After Change Value ...
|
|
ENUM_X_DIRECTION _dir, // Change Direction ...
|
|
datetime _time = NULL // Change Time ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
_time = NormalizeTime(_time);
|
|
|
|
//
|
|
// Validate Inputs ...
|
|
result =
|
|
//
|
|
_after > 0 &&
|
|
_before > 0 &&
|
|
IsValid(_time) &&
|
|
_after != _before &&
|
|
HasDirection(_dir)
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Set Validated Values ...
|
|
dir = _dir;
|
|
time = _time;
|
|
after = _after;
|
|
before = _before;
|
|
|
|
//
|
|
result = IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Cleaning Up ...
|
|
*/
|
|
void Clean()
|
|
{
|
|
//
|
|
after = 0;
|
|
before = 0;
|
|
time = NULL;
|
|
dir = X_DIRECTION_NONE;
|
|
}
|
|
|
|
/**
|
|
* Validate Model ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
after > 0 &&
|
|
before > 0 &&
|
|
IsValid(time) &&
|
|
after != before &&
|
|
HasDirection(dir)
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
/**
|
|
* Add Value Change instance to Exists Collection if it's not Exists ...
|
|
*
|
|
* @param item: XValueChange instance Reference ...
|
|
* @param items: XValueChange instance Array Reference ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int AddIfNotExists(
|
|
XValueChange &item,
|
|
XValueChange &items[] //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
result = item.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool canAdd = false;
|
|
int count = ArraySize(items);
|
|
if (!IsValidSize(count))
|
|
{
|
|
canAdd = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
XValueChange last;
|
|
bool hasItem = GetLastItem(
|
|
last,
|
|
items //
|
|
);
|
|
if (!hasItem)
|
|
{
|
|
canAdd = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Compare Last Item with Current ...
|
|
canAdd = item.time > last.time;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add Item if Allowed ...
|
|
if (canAdd)
|
|
{
|
|
//
|
|
AddRef(
|
|
item,
|
|
items //
|
|
);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(items);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
// |