3268 lines
78 KiB
Plaintext
3268 lines
78 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Indicator
|
|
// -------------------------------------------------
|
|
// Name: XCATBEA Lib
|
|
// Description: Library for XCATBEA ...
|
|
//
|
|
//
|
|
// 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 Library for XCATBEA Signaller"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../../Classes/x-saherelm.x-bar.analyser.class.mq5"
|
|
#include "../../Classes/x-saherelm.x-poi.drawer.class.mq5"
|
|
#include "../../Helpers/x-saherelm.x121.xcatb.helper.mq5"
|
|
|
|
//
|
|
// Definiions ...
|
|
|
|
//
|
|
#define XLiquidityToken "XLQ";
|
|
#define XTriggerBlockToken "XTRG";
|
|
|
|
//
|
|
struct XTriggerBlock
|
|
{
|
|
//
|
|
// Props ...
|
|
XBoxZone ob; // Order Block
|
|
XBoxZone fvg; // Fair Value Gap
|
|
XOHCL swingBar; // Swing of Trigger
|
|
XBoxZone trigger; // Trigger Block
|
|
XBoxZone rLiquidity; // Reversal Liquidity
|
|
XBoxZone fLiquidity; // Following Liquidity
|
|
|
|
//
|
|
XSignal signal; // Pepared Signal
|
|
|
|
//
|
|
// Constructor ...
|
|
XTriggerBlock()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Cleanup ...
|
|
void Clean()
|
|
{
|
|
//
|
|
ob.Clean();
|
|
fvg.Clean();
|
|
signal.Clean();
|
|
trigger.Clean();
|
|
swingBar.Clean();
|
|
rLiquidity.Clean();
|
|
fLiquidity.Clean();
|
|
|
|
//
|
|
ZeroMemory(this);
|
|
}
|
|
|
|
//
|
|
// Validate ...
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = ob.IsValid() &&
|
|
fvg.IsValid() &&
|
|
trigger.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool IsBullish()
|
|
{
|
|
return IsValid() &&
|
|
ob.IsBullish();
|
|
}
|
|
|
|
//
|
|
bool IsBearish()
|
|
{
|
|
return IsValid() &&
|
|
ob.IsBearish();
|
|
}
|
|
|
|
//
|
|
int ToIndex()
|
|
{
|
|
return trigger.ToIndex();
|
|
}
|
|
|
|
//
|
|
int FromIndex()
|
|
{
|
|
return trigger.FromIndex();
|
|
}
|
|
|
|
//
|
|
ENUM_X_DIRECTION GetDirection()
|
|
{
|
|
//
|
|
ENUM_X_DIRECTION result = X_DIRECTION_NONE;
|
|
|
|
//
|
|
if (IsValid())
|
|
{
|
|
result = ob.dir;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
//
|
|
// Trigger Block Validation Rules ...
|
|
struct XTriggerBlockValidationRules
|
|
{
|
|
//
|
|
// Props ...
|
|
bool forceHasSwing; // Force Block to Has Propper Swing ...
|
|
bool forceOBBarType; // Force Order Block to Has InDirectional Bars ...
|
|
int minRequiredOBBar; // Min Required OB Bar ...
|
|
bool forceFVGBarType; // Force Fair Value Gaps Bars Type ...
|
|
int liquidityLoopback; // Liquidity Detection Loopback ...
|
|
double maxAllowedRange; // Max Allowed Order Block Range ...
|
|
double minAllowedRange; // Min Allowed Order Block Range ...
|
|
bool forceHasFLiquidity; // Force Order Block To Has Reversal Liquidity ...
|
|
bool forceHasRLiquidity; // Force Order Block to Has Following Liquidity ...
|
|
bool validateGapSequence; // Validate Fair Value Gaps Bar Sequences ...
|
|
int minAllowedBlockLength; // Min Allowed Block Length ...
|
|
bool validateBlockEdgeBreakout; // Validate Order Block Edge Breakeout ...
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
XTriggerBlockValidationRules()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Cleanup ...
|
|
void Clean()
|
|
{
|
|
//
|
|
maxAllowedRange = 0;
|
|
minAllowedRange = 0;
|
|
minRequiredOBBar = 0;
|
|
liquidityLoopback = 0;
|
|
forceHasSwing = false;
|
|
forceOBBarType = false;
|
|
forceFVGBarType = false;
|
|
minAllowedBlockLength = 0;
|
|
forceHasFLiquidity = false;
|
|
forceHasRLiquidity = false;
|
|
validateGapSequence = false;
|
|
validateBlockEdgeBreakout = false;
|
|
|
|
//
|
|
ZeroMemory(this);
|
|
}
|
|
};
|
|
|
|
//
|
|
// Trigger Block Filters ...
|
|
struct XTriggerBlockFilters
|
|
{
|
|
//
|
|
// Props ...
|
|
bool filterBasedOnKI; // Filter Based On KI Flats ...
|
|
bool filterBasedOnPV; // Filter Based On PV ...
|
|
bool filterBasedOnSar; // Filter Based On SAR ...
|
|
bool filterBasedOnRSI; // Filter Based On RSI ...
|
|
bool filterBasedOnADX; // Filter Based On ADX ...
|
|
bool filterBasedOnATR; // Filter Based On ATR ...
|
|
bool filterBasedOnTrend; // Filter Based On Trend ...
|
|
bool filterBasedOnDelta; // Filter Based On Delta ...
|
|
bool filterBasedOnVolume; // Filter Based On Volume ...
|
|
bool filterBasedOnSignalBar; // Filter Based on Signal Bar ...
|
|
bool filterBasedOnHKSignalBar; // Filter Based On HK Signal Bar ...
|
|
|
|
//
|
|
int bosLoopback; // PV Based Filtering BOS Lookup ...
|
|
|
|
//
|
|
bool forceSarSwitched; // SAR Switched on Sides for Filtering ...
|
|
bool forceSarOnBothSide; // SAR Check on Both Sides for Filtering ...
|
|
bool forceSarHasBreakout; // SAR Must SAR Must Breakout ...
|
|
bool forceSarHasReversalSide; // SAR Must SAR Must Has Reversal Direction ...
|
|
|
|
//
|
|
bool forceHasRSITrending; // RSI Must Has Trending ...
|
|
bool forceHasRSIVPattern; // RSI Must Has Pattern ...
|
|
bool forceHasRSICrossing; // RSI Must Has Crossing Levels ...
|
|
|
|
//
|
|
bool forceHasADXTrending; // ADX Must Has Trending ...
|
|
bool forceHasADXCrossing; // ADX Must Has Crossing +DI/-DI ...
|
|
|
|
//
|
|
bool forceHasATRTrending; // ATR Must Has Trending ...
|
|
bool forceHasATRBreakout; // ATR Must Has Breakout ...
|
|
|
|
//
|
|
bool forceHasTrendDir; // TREND Must Has Following Direction ...
|
|
bool forceHasTrendPlace; // TREND Place Based Filtering ...
|
|
bool forceHasTrendChange; // TREND Change Filtering ...
|
|
bool forceHasTrendTrending; // TREND Must Has Trending ...
|
|
bool forceHasTrendRejection; // TREND Line Must Rejected ...
|
|
bool forceHasTrendLineInside; // TREND Line has Inside ...
|
|
bool forceHasTrendDirOnBothSide; // TREND Must Has Following Direction in Both Side ...
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
XTriggerBlockFilters()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Cleanup ...
|
|
void Clean()
|
|
{
|
|
//
|
|
filterBasedOnKI = false;
|
|
filterBasedOnPV = false;
|
|
filterBasedOnSar = false;
|
|
filterBasedOnRSI = false;
|
|
filterBasedOnADX = false;
|
|
filterBasedOnATR = false;
|
|
filterBasedOnTrend = false;
|
|
filterBasedOnDelta = false;
|
|
filterBasedOnVolume = false;
|
|
filterBasedOnSignalBar = false;
|
|
filterBasedOnHKSignalBar = false;
|
|
|
|
//
|
|
bosLoopback = 0;
|
|
|
|
//
|
|
forceSarSwitched = false;
|
|
forceSarOnBothSide = false;
|
|
forceSarHasBreakout = false;
|
|
forceSarHasReversalSide = false;
|
|
|
|
//
|
|
forceHasRSITrending = false;
|
|
forceHasRSIVPattern = false;
|
|
forceHasRSICrossing = false;
|
|
|
|
//
|
|
forceHasADXTrending = false;
|
|
forceHasADXCrossing = false;
|
|
|
|
//
|
|
forceHasATRTrending = false;
|
|
forceHasATRBreakout = false;
|
|
|
|
//
|
|
forceHasTrendDir = false;
|
|
forceHasTrendPlace = false;
|
|
forceHasTrendChange = false;
|
|
forceHasTrendTrending = false;
|
|
forceHasTrendRejection = false;
|
|
forceHasTrendLineInside = false;
|
|
forceHasTrendDirOnBothSide = false;
|
|
|
|
//
|
|
ZeroMemory(this);
|
|
}
|
|
};
|
|
|
|
//
|
|
// Extensions ...
|
|
|
|
/**
|
|
* Detect Trigger Block ...
|
|
*
|
|
* @param helper: XCX121XCatbHelper instance pointer ...
|
|
* @param barAnalyser: XCBarAnalyser instance pointer ...
|
|
* @param triggerBlock: XTriggerBlock Refrence, holds Detected Trigger Block ...
|
|
* @param conditions: X121XCatbConditions Refrence, holds Trigger Block Conditions ...
|
|
* @param validations: XTriggerBlockValidationRules Refrence, Provides Trigger Block Validation Rules ...
|
|
* @param filters: XTriggerBlockFilters Refrence, Provides Trigger Block Filters ...
|
|
* @param barIndex: int, Bar Index ...
|
|
* @param loopback: int, Loopback for Conditions reading ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool DetectTriggerBlock1(
|
|
XCX121XCatbHelper *helper,
|
|
XCBarAnalyser *barAnalyser,
|
|
XTriggerBlock &triggerBlock,
|
|
X121XCatbConditions &conditions,
|
|
XTriggerBlockValidationRules &validations,
|
|
XTriggerBlockFilters &filters,
|
|
int barIndex = 0,
|
|
int loopback = 10 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Normalize Args ...
|
|
conditions.Clean();
|
|
triggerBlock.Clean();
|
|
barIndex = NormalizeInt(barIndex, 0);
|
|
loopback = NormalizeInt(loopback, 0);
|
|
|
|
//
|
|
// Validate Args ...
|
|
result =
|
|
helper != NULL &&
|
|
barAnalyser != NULL;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int zIDX = 0;
|
|
int cIDX = zIDX + 1;
|
|
int pIDX = cIDX + 1;
|
|
int p2IDX = pIDX + 1;
|
|
int p3IDX = p2IDX + 1;
|
|
int p4IDX = p3IDX + 1;
|
|
|
|
//
|
|
bool has = false;
|
|
string symbol = helper.GetSymbol();
|
|
ENUM_TIMEFRAMES period = helper.GetPeriod();
|
|
|
|
//
|
|
// Initial Requirements ...
|
|
|
|
//
|
|
// Required Bars ...
|
|
XOHCL zBar;
|
|
XOHCL cBar;
|
|
result =
|
|
result &&
|
|
zBar.Init(
|
|
symbol,
|
|
period,
|
|
barIndex //
|
|
);
|
|
result =
|
|
result &&
|
|
zBar.GetPreviousBar(cBar);
|
|
|
|
//
|
|
// Here we Must Detect Order Block ...
|
|
// then Detect FVG ...
|
|
// then Prepare Trigger Box ...
|
|
|
|
//
|
|
// Select Application Bar ...
|
|
XOHCL iBar;
|
|
if (barIndex == 0)
|
|
{
|
|
iBar = cBar;
|
|
}
|
|
else if (barIndex > 0)
|
|
{
|
|
iBar = zBar;
|
|
}
|
|
iBar = cBar;
|
|
result =
|
|
result &&
|
|
iBar.IsValid();
|
|
|
|
//
|
|
// Detect Order Block ...
|
|
result =
|
|
result &&
|
|
barAnalyser
|
|
.IsOB(
|
|
iBar,
|
|
triggerBlock.ob,
|
|
false,
|
|
false //
|
|
);
|
|
|
|
//
|
|
// Detect Fair Value Gap ...
|
|
result =
|
|
result &&
|
|
barAnalyser
|
|
.IsFVG(
|
|
iBar,
|
|
triggerBlock.fvg,
|
|
false //
|
|
);
|
|
|
|
//
|
|
// Check Result and Create Trigger Box based on it ...
|
|
if (result)
|
|
{
|
|
//
|
|
triggerBlock.trigger = triggerBlock.ob;
|
|
triggerBlock.trigger.type = XTriggerBlockToken;
|
|
|
|
//
|
|
// Update Upper and Lower ...
|
|
int toIDX = triggerBlock.ob.ToIndex();
|
|
int fromIDX = triggerBlock.ob.FromIndex();
|
|
|
|
//
|
|
iBar.Clean();
|
|
result = iBar.Init(
|
|
symbol,
|
|
period,
|
|
toIDX //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
double iLL = iBar.FindLowest(fromIDX - toIDX, MODE_LOW);
|
|
double iHH = iBar.FindHighest(fromIDX - toIDX, MODE_HIGH);
|
|
|
|
//
|
|
triggerBlock.trigger.upper = MathMax(iHH, triggerBlock.trigger.upper);
|
|
triggerBlock.trigger.lower = MathMin(iLL, triggerBlock.trigger.lower);
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Apply Validations ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = ValidateTriggerBlock(
|
|
helper,
|
|
barAnalyser,
|
|
triggerBlock,
|
|
conditions,
|
|
validations,
|
|
barIndex,
|
|
loopback //
|
|
);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
triggerBlock.Clean();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Apply Filtering ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = FilterTriggerBlock(
|
|
helper,
|
|
barAnalyser,
|
|
triggerBlock,
|
|
conditions,
|
|
filters,
|
|
barIndex,
|
|
loopback //
|
|
);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
triggerBlock.Clean();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Summarizing Result ...
|
|
result =
|
|
result &&
|
|
triggerBlock.IsValid();
|
|
|
|
//
|
|
// Preparing Signal ...
|
|
if (result)
|
|
{
|
|
//
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
iBar.Clean();
|
|
zBar.Clean();
|
|
cBar.Clean();
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
triggerBlock.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Validate a Trigger Block ...
|
|
*
|
|
* @param helper: XCX121XCatbHelper instance pointer ...
|
|
* @param barAnalyser: XCBarAnalyser instance pointer ...
|
|
* @param triggerBlock: XTriggerBlock Refrence, Trigger Block ...
|
|
* @param conditions: X121XCatbConditions Refrence, Trigger Block Conditions ...
|
|
* @param validations: XTriggerBlockValidationRules Refrence, Provides Trigger Block Validation Rules ...
|
|
* @param barIndex: int, Bar Index ...
|
|
* @param loopback: int, Loopback for Conditions reading ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool ValidateTriggerBlock(
|
|
XCX121XCatbHelper *helper,
|
|
XCBarAnalyser *barAnalyser,
|
|
XTriggerBlock &triggerBlock,
|
|
X121XCatbConditions &conditions,
|
|
XTriggerBlockValidationRules &validations,
|
|
int barIndex = 0,
|
|
int loopback = 10 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Checking Conditions ...
|
|
|
|
//
|
|
// Normalize Args ...
|
|
conditions.Clean();
|
|
barIndex = NormalizeInt(barIndex, 0);
|
|
loopback = NormalizeInt(loopback, 0);
|
|
|
|
//
|
|
// Validate Args ...
|
|
result =
|
|
helper != NULL &&
|
|
barAnalyser != NULL &&
|
|
triggerBlock.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reading Market Conditions ...
|
|
result = helper.GetConditions(
|
|
conditions,
|
|
barIndex,
|
|
loopback //
|
|
);
|
|
if (!result)
|
|
{
|
|
//
|
|
conditions.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int zIDX = 0;
|
|
int cIDX = zIDX + 1;
|
|
int pIDX = cIDX + 1;
|
|
int p2IDX = pIDX + 1;
|
|
int p3IDX = p2IDX + 1;
|
|
int p4IDX = p3IDX + 1;
|
|
|
|
//
|
|
XOHCL iBar;
|
|
int count = 0;
|
|
int toIDX = -1;
|
|
int fromIDX = -1;
|
|
bool has = false;
|
|
bool isBullish = triggerBlock.IsBullish();
|
|
bool isBearish = triggerBlock.IsBearish();
|
|
double points = GetPoints(triggerBlock.ob.symbol);
|
|
ENUM_X_DIRECTION iDir = triggerBlock.GetDirection();
|
|
|
|
//
|
|
// Checking Conditions ...
|
|
|
|
//
|
|
// Range Validation ...
|
|
|
|
//
|
|
// Max Allowed OB Range ...
|
|
if (result &&
|
|
validations.maxAllowedRange > 0)
|
|
{
|
|
//
|
|
double iRange = triggerBlock.ob.GetRange();
|
|
result = iRange <= (validations.maxAllowedRange * points);
|
|
}
|
|
|
|
//
|
|
// Min Allowed OB Range ...
|
|
if (result &&
|
|
validations.minAllowedRange > 0)
|
|
{
|
|
//
|
|
double iRange = triggerBlock.ob.GetRange();
|
|
result = iRange >= (validations.maxAllowedRange * points);
|
|
}
|
|
|
|
//
|
|
// Has Swing ...
|
|
if (result &&
|
|
validations.forceHasSwing)
|
|
{
|
|
//
|
|
bool isSwing = false;
|
|
toIDX = triggerBlock.ToIndex();
|
|
fromIDX = triggerBlock.FromIndex();
|
|
ENUM_X_DIRECTION swingDir = X_DIRECTION_NONE;
|
|
for (int i = toIDX; i <= fromIDX; i++)
|
|
{
|
|
//
|
|
// Initialize iBar ...
|
|
has = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
i //
|
|
);
|
|
isSwing =
|
|
has &&
|
|
barAnalyser
|
|
.IsSimpleSwing(
|
|
iBar,
|
|
swingDir //
|
|
);
|
|
if (isSwing &&
|
|
swingDir == iDir)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
result =
|
|
isSwing &&
|
|
iBar.IsValid() &&
|
|
swingDir == iDir;
|
|
if (result)
|
|
{
|
|
//
|
|
// Initialize Swing Bar ...
|
|
result = triggerBlock
|
|
.swingBar
|
|
.Init(
|
|
iBar.symbol,
|
|
iBar.period,
|
|
iBar.Index() + 2 //
|
|
);
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Force FVG Bar Types ...
|
|
if (result &&
|
|
validations.forceFVGBarType)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.fvg.ToIndex();
|
|
fromIDX = triggerBlock.fvg.FromIndex();
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
// Initialize iBar ...
|
|
result = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
i //
|
|
);
|
|
result =
|
|
result &&
|
|
iDir == iBar.GetDirection();
|
|
|
|
//
|
|
iBar.Clean();
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Force OB Start By InDirectional Bars ...
|
|
if (result &&
|
|
validations.forceOBBarType)
|
|
{
|
|
//
|
|
count = 0;
|
|
fromIDX = triggerBlock.fvg.FromIndex() + 1;
|
|
int sIDX = fromIDX;
|
|
has = true;
|
|
while (has)
|
|
{
|
|
//
|
|
// Initialize iBar ..
|
|
has = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
fromIDX //
|
|
);
|
|
has = has &&
|
|
iDir == Opposit(iBar.GetDirection());
|
|
if (has)
|
|
{
|
|
count++;
|
|
}
|
|
|
|
//
|
|
fromIDX++;
|
|
}
|
|
|
|
//
|
|
int required =
|
|
validations.minRequiredOBBar > 0
|
|
? validations.minRequiredOBBar
|
|
: 1;
|
|
|
|
//
|
|
result = count >= required;
|
|
|
|
//
|
|
// Update Trigger Block Box based on OB Bar Types ...
|
|
if (result)
|
|
{
|
|
//
|
|
result = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
sIDX //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
datetime iFrom = GetBarTime(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
sIDX + count //
|
|
);
|
|
double iLL = iBar.FindLowest(count, MODE_LOW);
|
|
double iHH = iBar.FindHighest(count, MODE_HIGH);
|
|
|
|
//
|
|
triggerBlock.trigger.from = iFrom;
|
|
triggerBlock.trigger.upper = MathMax(iHH, triggerBlock.trigger.upper);
|
|
triggerBlock.trigger.lower = MathMin(iLL, triggerBlock.trigger.lower);
|
|
}
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Force Fair Value Gap to Following Sequence ...
|
|
if (result &&
|
|
validations.validateGapSequence)
|
|
{
|
|
//
|
|
XOHCL iPBar;
|
|
toIDX = triggerBlock.fvg.ToIndex();
|
|
fromIDX = triggerBlock.fvg.FromIndex() - 1;
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
result = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
i //
|
|
);
|
|
result =
|
|
result &&
|
|
iBar.GetPreviousBar(iPBar);
|
|
result =
|
|
result &&
|
|
(isBullish
|
|
? iBar.low > iPBar.low
|
|
: iBar.high < iPBar.high);
|
|
if (!result)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
iPBar.Clean();
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
iPBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Validate OB Edge Breakeout Using Gaps End Bar ...
|
|
if (result &&
|
|
validations.validateBlockEdgeBreakout)
|
|
{
|
|
//
|
|
// Initialize Gap End Bar ...
|
|
toIDX = triggerBlock.fvg.ToIndex();
|
|
result = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
toIDX //
|
|
);
|
|
|
|
//
|
|
result =
|
|
result &&
|
|
(isBullish
|
|
? iBar.open < triggerBlock.ob.upper &&
|
|
iBar.close > triggerBlock.ob.upper
|
|
: iBar.open > triggerBlock.ob.lower &&
|
|
iBar.close < triggerBlock.ob.lower);
|
|
|
|
//
|
|
// Here we Can Apply Edge Breakout Using Fibo ...
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Force Block to Has Liquidity ...
|
|
if (result &&
|
|
validations.liquidityLoopback > 0 &&
|
|
(validations.forceHasFLiquidity ||
|
|
validations.forceHasRLiquidity))
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ToIndex();
|
|
fromIDX = triggerBlock.FromIndex();
|
|
ENUM_X_DIRECTION iLiqDir = X_DIRECTION_NONE;
|
|
for (int i = toIDX; i < fromIDX + validations.liquidityLoopback; i++)
|
|
{
|
|
//
|
|
// Initialize iBar ...
|
|
has = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
i //
|
|
);
|
|
has =
|
|
has &&
|
|
barAnalyser
|
|
.IsRejected(
|
|
iBar,
|
|
iLiqDir,
|
|
false, // Force Bar Type ...
|
|
true // Force Fibo Pressure ...
|
|
);
|
|
|
|
//
|
|
bool isFLiq =
|
|
has &&
|
|
iLiqDir == iDir;
|
|
if (isFLiq &&
|
|
validations.forceHasFLiquidity &&
|
|
!triggerBlock.fLiquidity.IsValid())
|
|
{
|
|
//
|
|
FillLiquidity(
|
|
iBar,
|
|
iLiqDir,
|
|
triggerBlock.fLiquidity,
|
|
triggerBlock.trigger.to //
|
|
);
|
|
}
|
|
|
|
//
|
|
bool isRLiq =
|
|
has &&
|
|
iLiqDir == Opposit(iDir);
|
|
if (isRLiq &&
|
|
validations.forceHasRLiquidity &&
|
|
!triggerBlock.rLiquidity.IsValid())
|
|
{
|
|
//
|
|
FillLiquidity(
|
|
iBar,
|
|
iLiqDir,
|
|
triggerBlock.rLiquidity,
|
|
triggerBlock.trigger.to //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Here We Can Apply Liquidity Validations ...
|
|
ValidateTriggerBlockLiquidities(
|
|
helper,
|
|
barAnalyser,
|
|
triggerBlock,
|
|
conditions,
|
|
validations //
|
|
);
|
|
|
|
//
|
|
// Check Liquidities ...
|
|
has = (!validations.forceHasFLiquidity
|
|
? true
|
|
: triggerBlock.fLiquidity.IsValid()) &&
|
|
(!validations.forceHasRLiquidity
|
|
? true
|
|
: triggerBlock.rLiquidity.IsValid());
|
|
|
|
//
|
|
iBar.Clean();
|
|
|
|
//
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Result ...
|
|
result = (!validations.forceHasFLiquidity
|
|
? true
|
|
: triggerBlock.fLiquidity.IsValid()) &&
|
|
(!validations.forceHasRLiquidity
|
|
? true
|
|
: triggerBlock.rLiquidity.IsValid());
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
// Validating Block Length Based on Direction ...
|
|
if (result &&
|
|
validations.minAllowedBlockLength)
|
|
{
|
|
//
|
|
count = 0;
|
|
fromIDX = triggerBlock.FromIndex();
|
|
int sIDX = fromIDX;
|
|
has = true;
|
|
while (has)
|
|
{
|
|
//
|
|
// Initialize iBar ...
|
|
has = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
sIDX //
|
|
);
|
|
|
|
//
|
|
has =
|
|
has &&
|
|
(isBullish
|
|
? iBar.low > triggerBlock.trigger.lower
|
|
: iBar.high < triggerBlock.trigger.upper);
|
|
|
|
//
|
|
if (!has)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
sIDX++;
|
|
count++;
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
result =
|
|
iBar.IsValid() &&
|
|
count >= validations.minAllowedBlockLength;
|
|
if (result)
|
|
{
|
|
//
|
|
// Update Trigger Block ...
|
|
bool usaMinAllowedForBlockLength = false;
|
|
if (usaMinAllowedForBlockLength)
|
|
{
|
|
fromIDX = fromIDX + validations.minAllowedBlockLength;
|
|
}
|
|
else
|
|
{
|
|
fromIDX = sIDX - 1;
|
|
}
|
|
|
|
//
|
|
triggerBlock.trigger.from = GetBarTime(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
fromIDX //
|
|
);
|
|
}
|
|
|
|
//
|
|
iBar.Clean();
|
|
}
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
triggerBlock.Clean();
|
|
}
|
|
|
|
//
|
|
// Summarize result ...
|
|
result = triggerBlock.IsValid();
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
iBar.Clean();
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
//
|
|
conditions.Clean();
|
|
triggerBlock.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Filter a Trigger Block ...
|
|
*
|
|
* @param helper: XCX121XCatbHelper instance pointer ...
|
|
* @param barAnalyser: XCBarAnalyser instance pointer ...
|
|
* @param triggerBlock: XTriggerBlock Refrence, Trigger Block ...
|
|
* @param conditions: X121XCatbConditions Refrence, Trigger Block Conditions ...
|
|
* @param filters: XTriggerBlockFilters Refrence, Provides Trigger Block Filters ...
|
|
* @param barIndex: int, Bar Index ...
|
|
* @param loopback: int, Loopback for Conditions reading ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool FilterTriggerBlock(
|
|
XCX121XCatbHelper *helper,
|
|
XCBarAnalyser *barAnalyser,
|
|
XTriggerBlock &triggerBlock,
|
|
X121XCatbConditions &conditions,
|
|
XTriggerBlockFilters &filters,
|
|
int barIndex = 0,
|
|
int loopback = 10 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Checking Conditions ...
|
|
|
|
//
|
|
// Normalize Args ...
|
|
conditions.Clean();
|
|
barIndex = NormalizeInt(barIndex, 0);
|
|
loopback = NormalizeInt(loopback, 0);
|
|
|
|
//
|
|
// Validate Args ...
|
|
result =
|
|
helper != NULL &&
|
|
barAnalyser != NULL &&
|
|
triggerBlock.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reading Market Conditions ...
|
|
result = helper.GetConditions(
|
|
conditions,
|
|
barIndex,
|
|
loopback //
|
|
);
|
|
if (!result)
|
|
{
|
|
//
|
|
conditions.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int zIDX = 0;
|
|
int cIDX = zIDX + 1;
|
|
int pIDX = cIDX + 1;
|
|
int p2IDX = pIDX + 1;
|
|
int p3IDX = p2IDX + 1;
|
|
int p4IDX = p3IDX + 1;
|
|
|
|
//
|
|
XOHCL iBar;
|
|
int toIDX = -1;
|
|
int fromIDX = -1;
|
|
bool has = false;
|
|
bool isBullish = triggerBlock.IsBullish();
|
|
bool isBearish = triggerBlock.IsBearish();
|
|
double points = GetPoints(triggerBlock.ob.symbol);
|
|
ENUM_X_DIRECTION iDir = triggerBlock.GetDirection();
|
|
|
|
//
|
|
// Checking Conditions ...
|
|
|
|
//
|
|
// Filter Based KI ...
|
|
if (result &&
|
|
filters.filterBasedOnKI)
|
|
{
|
|
//
|
|
int count = 0;
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
double lKI = conditions.kiBuffer[zIDX];
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iKI = helper.GetKI(i);
|
|
if (iKI == lKI)
|
|
{
|
|
count++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = count >= 3;
|
|
}
|
|
|
|
//
|
|
// Filter Based On PV ...
|
|
if (result &&
|
|
filters.filterBasedOnPV)
|
|
{
|
|
//
|
|
// For PV Filtering ...
|
|
|
|
//
|
|
// Detect BOS ...
|
|
|
|
//
|
|
// When Loopback for BOS has Specified Value ...
|
|
// we Lookup for BOS on Specific Period ...
|
|
if (filters.bosLoopback > 0)
|
|
{
|
|
//
|
|
has = true;
|
|
int fBOSIDX = -1;
|
|
int rBOSIDX = -1;
|
|
bool isBOS = false;
|
|
bool isFBOS = false;
|
|
bool isRBOS = false;
|
|
bool isPeakOverLast = false;
|
|
bool isValeUnderLast = false;
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
int sIDX = fromIDX;
|
|
while (has)
|
|
{
|
|
//
|
|
double iPeak = helper.GetPeak(sIDX);
|
|
double iPPeak = helper.GetPeak(sIDX + 1);
|
|
|
|
//
|
|
double iVale = helper.GetVale(sIDX);
|
|
double iPVale = helper.GetVale(sIDX + 1);
|
|
|
|
//
|
|
isPeakOverLast =
|
|
iPeak > 0 &&
|
|
iPeak > iPPeak;
|
|
|
|
//
|
|
isValeUnderLast =
|
|
iVale > 0 &&
|
|
iVale < iPVale;
|
|
|
|
//
|
|
isFBOS =
|
|
isBullish
|
|
? isPeakOverLast
|
|
: isValeUnderLast;
|
|
if (isFBOS)
|
|
{
|
|
fBOSIDX = sIDX;
|
|
}
|
|
|
|
//
|
|
isRBOS =
|
|
isBullish
|
|
? isValeUnderLast
|
|
: isPeakOverLast;
|
|
if (isRBOS)
|
|
{
|
|
rBOSIDX = sIDX;
|
|
}
|
|
|
|
//
|
|
isBOS =
|
|
(isBullish &&
|
|
isPeakOverLast) ||
|
|
(isBearish &&
|
|
isValeUnderLast);
|
|
if (isBOS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
sIDX++;
|
|
has = !isBOS &&
|
|
sIDX < fromIDX + filters.bosLoopback;
|
|
}
|
|
|
|
//
|
|
isFBOS = IsValidIndex(fBOSIDX);
|
|
isRBOS = IsValidIndex(rBOSIDX);
|
|
|
|
//
|
|
result = isBOS &&
|
|
isFBOS &&
|
|
!isRBOS;
|
|
}
|
|
|
|
//
|
|
// When Loopback for BOS has not Specified Value ...
|
|
// we Lookup for BOS on GAP End Bar ...
|
|
if (filters.bosLoopback <= 0)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ToIndex();
|
|
|
|
//
|
|
// Initialize iBar ...
|
|
result = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
toIDX //
|
|
);
|
|
result =
|
|
result &&
|
|
(isBullish ? iBar.high > helper.GetPeak(toIDX + 1)
|
|
: iBar.low < helper.GetVale(toIDX + 1));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Based on SAR ...
|
|
if (result &&
|
|
filters.filterBasedOnSar)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ToIndex();
|
|
fromIDX = triggerBlock.FromIndex();
|
|
|
|
//
|
|
double iSarState = helper.GetSarState(toIDX);
|
|
double iPSarState = helper.GetSarState(toIDX + 1);
|
|
|
|
//
|
|
double iFSarState = helper.GetSarState(fromIDX);
|
|
double iPFSarState = helper.GetSarState(fromIDX + 1);
|
|
|
|
//
|
|
bool isSarBullish =
|
|
iSarState > 0;
|
|
|
|
//
|
|
bool isSarBearish =
|
|
iSarState < 0;
|
|
|
|
//
|
|
bool isFSarBullish =
|
|
iFSarState > 0;
|
|
|
|
//
|
|
bool isFSarBearish =
|
|
iFSarState < 0;
|
|
|
|
//
|
|
bool isSarSwitchedToBullish =
|
|
iSarState > 0 &&
|
|
iPSarState <= 0;
|
|
|
|
//
|
|
bool isSarSwitchedToBearish =
|
|
iSarState < 0 &&
|
|
iPSarState >= 0;
|
|
|
|
//
|
|
bool isFSarSwitchedToBullish =
|
|
iFSarState > 0 &&
|
|
iPFSarState <= 0;
|
|
|
|
//
|
|
bool isFSarSwitchedToBearish =
|
|
iFSarState < 0 &&
|
|
iPFSarState >= 0;
|
|
|
|
//
|
|
// Check SAR Direction ...
|
|
|
|
//
|
|
if (result &&
|
|
!filters.forceSarSwitched)
|
|
{
|
|
//
|
|
result = isBullish
|
|
? isSarBullish
|
|
: isSarBearish;
|
|
}
|
|
|
|
//
|
|
if (result &&
|
|
filters.forceSarSwitched)
|
|
{
|
|
//
|
|
result = isBullish
|
|
? isSarSwitchedToBullish
|
|
: isSarSwitchedToBearish;
|
|
}
|
|
|
|
//
|
|
if (result &&
|
|
filters.forceSarOnBothSide)
|
|
{
|
|
//
|
|
if (result &&
|
|
!filters.forceSarSwitched)
|
|
{
|
|
//
|
|
result = isBullish
|
|
? isFSarBullish
|
|
: isFSarBearish;
|
|
}
|
|
|
|
//
|
|
if (result &&
|
|
filters.forceSarSwitched)
|
|
{
|
|
//
|
|
result = isBullish
|
|
? isFSarSwitchedToBullish
|
|
: isFSarSwitchedToBearish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Sar Has Reversal Direction inside Trigger Block ...
|
|
if (result &&
|
|
filters.forceSarHasReversalSide)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ToIndex();
|
|
fromIDX = triggerBlock.FromIndex();
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iSarState = helper.GetSarState(i);
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? iSarState > 0
|
|
: iSarState < 0;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Force Sar Breakouts ...
|
|
// in this Senario Sar must in following Direction and
|
|
// there is not any Switches ...
|
|
// but must breakout Bar the last in Reversal Direction
|
|
// of Sar ...
|
|
if (result &&
|
|
filters.forceSarHasBreakout)
|
|
{
|
|
//
|
|
bool isSarBullishStart =
|
|
//
|
|
(
|
|
//
|
|
// Current SAR is Directional ...
|
|
conditions.isSarBullish &&
|
|
//
|
|
// Previous InDirectional Sar Exists ...
|
|
helper.lastSarBearish.IsValid() &&
|
|
//
|
|
// Current Close is Over previous Sar ...
|
|
conditions.bars[cIDX].close > helper.lastSarBearish.after &&
|
|
//
|
|
// Before Close is not Over previous Sar ...
|
|
conditions.bars[pIDX].close <= helper.lastSarBearish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isSarBearishStart =
|
|
//
|
|
(
|
|
//
|
|
// Current SAR is Directional ...
|
|
conditions.isSarBearish &&
|
|
//
|
|
// Previous InDirectional Sar Exists ...
|
|
helper.lastSarBullish.IsValid() &&
|
|
//
|
|
// Current Close is Under previous ...
|
|
conditions.bars[cIDX].close < helper.lastSarBullish.after &&
|
|
//
|
|
// Before Close is not Under previous ...
|
|
conditions.bars[pIDX].close >= helper.lastSarBullish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
result =
|
|
isBullish
|
|
? isSarBullishStart
|
|
: isSarBearishStart;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Based On RSI ...
|
|
if (result &&
|
|
filters.filterBasedOnRSI)
|
|
{
|
|
//
|
|
// Here We Can Apply RSI Filters ...
|
|
// Since RSI Filters must be Check on FVG Bars ...
|
|
// we Have to Loop back Based on FVG Bars to Detect Conditions ...
|
|
double rsiTrend = (conditions.rsiOBLevel + conditions.rsiOSLevel) / 2;
|
|
|
|
//
|
|
toIDX = triggerBlock.fvg.ToIndex();
|
|
fromIDX = triggerBlock.fvg.FromIndex();
|
|
|
|
//
|
|
// Filter Using RSI Trending ...
|
|
if (result &&
|
|
filters.forceHasRSITrending)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iRSI = helper.GetRSI(i);
|
|
double iPRSI = helper.GetRSI(i + 1);
|
|
|
|
//
|
|
has =
|
|
(isBullish
|
|
? (iRSI > rsiTrend &&
|
|
iRSI > iPRSI)
|
|
: (iRSI < rsiTrend &&
|
|
iRSI < iPRSI));
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using RSI V Pattern ...
|
|
if (result &&
|
|
filters.forceHasRSIVPattern)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iRSI = helper.GetRSI(i);
|
|
double iPRSI = helper.GetRSI(i + 1);
|
|
double iP2RSI = helper.GetRSI(i + 2);
|
|
|
|
//
|
|
bool isRSIVBullish =
|
|
iRSI > iPRSI &&
|
|
iP2RSI > iPRSI;
|
|
|
|
//
|
|
bool isRSIVBearish =
|
|
iRSI < iPRSI &&
|
|
iP2RSI < iPRSI;
|
|
|
|
//
|
|
has =
|
|
(isBullish
|
|
? isRSIVBullish
|
|
: isRSIVBearish);
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using RSI Crossing on OB/OS Levels ...
|
|
if (result &&
|
|
filters.forceHasRSICrossing)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iRSI = helper.GetRSI(i);
|
|
double iPRSI = helper.GetRSI(i + 1);
|
|
|
|
//
|
|
bool isRSICrossedOverOS =
|
|
iRSI > conditions.rsiOSLevel &&
|
|
iPRSI <= conditions.rsiOSLevel;
|
|
|
|
//
|
|
bool isRSICrossedUnderOB =
|
|
iRSI < conditions.rsiOBLevel &&
|
|
iPRSI >= conditions.rsiOBLevel;
|
|
|
|
//
|
|
has =
|
|
(isBullish
|
|
? isRSICrossedOverOS
|
|
: isRSICrossedUnderOB);
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Based On ADX ...
|
|
if (result &&
|
|
filters.filterBasedOnADX)
|
|
{
|
|
//
|
|
// Here We Can Apply ADX Filters ...
|
|
// Since ADX Filters must be Check on FVG Bars ...
|
|
// we Have to Loop back Based on FVG Bars to Detect Conditions ...
|
|
|
|
//
|
|
toIDX = triggerBlock.fvg.ToIndex();
|
|
fromIDX = triggerBlock.fvg.FromIndex();
|
|
|
|
//
|
|
// Filter Using ADX Trending ...
|
|
if (result &&
|
|
filters.forceHasADXTrending)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iADX = helper.GetADX(i);
|
|
double iPADX = helper.GetADX(i + 1);
|
|
|
|
//
|
|
has = iADX > conditions.adxThreshold &&
|
|
iADX > iPADX;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using ADX Crossing on +DI/-DI ...
|
|
if (result &&
|
|
filters.forceHasADXCrossing)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iDIPlus = helper.GetADXP(i);
|
|
double iPDIPlus = helper.GetADXP(i + 1);
|
|
|
|
//
|
|
double iDIMinus = helper.GetADXN(i);
|
|
double iPDIMinus = helper.GetADXN(i + 1);
|
|
|
|
//
|
|
bool isADXPlusCrossedOverMinus =
|
|
iDIPlus > iDIMinus &&
|
|
iPDIPlus <= iDIMinus;
|
|
|
|
//
|
|
bool isADXMinusCrossedOverPlus =
|
|
iDIMinus > iDIPlus &&
|
|
iPDIMinus <= iDIPlus;
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? isADXPlusCrossedOverMinus
|
|
: isADXMinusCrossedOverPlus;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Based On ATR Band ...
|
|
if (result &&
|
|
filters.filterBasedOnATR)
|
|
{
|
|
//
|
|
// Here We Can Apply ATR Filters ...
|
|
// Since ATR Filters must be Check on OB Bars ...
|
|
// we Have to Loop back Based on OB Bars to Detect Conditions ...
|
|
|
|
//
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
|
|
//
|
|
// Filter Using ATR Trending ...
|
|
if (result &&
|
|
filters.forceHasATRTrending)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iATRUpper = helper.GetATRUpper(i);
|
|
double iPATRUpper = helper.GetATRUpper(i + 1);
|
|
|
|
//
|
|
double iATRLower = helper.GetATRLower(i);
|
|
double iPATRLower = helper.GetATRLower(i + 1);
|
|
|
|
//
|
|
bool isATRUp =
|
|
iATRLower > iPATRLower;
|
|
|
|
//
|
|
bool isATRDown =
|
|
iATRUpper < iPATRUpper;
|
|
|
|
//
|
|
has = isBullish
|
|
? isATRUp
|
|
: isATRDown;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using ATR Breakout ...
|
|
if (result &&
|
|
filters.forceHasATRBreakout)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iATRUpper = helper.GetATRUpper(i);
|
|
double iATRLower = helper.GetATRLower(i);
|
|
|
|
//
|
|
has = isBullish
|
|
? triggerBlock.ob.lower < iATRLower
|
|
: triggerBlock.ob.upper > iATRUpper;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Based On Trend ...
|
|
if (result &&
|
|
filters.filterBasedOnTrend)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.fvg.ToIndex();
|
|
fromIDX = triggerBlock.fvg.FromIndex();
|
|
|
|
//
|
|
// Filter Using Trend Direction ...
|
|
if (result &&
|
|
filters.forceHasTrendDir)
|
|
{
|
|
//
|
|
result =
|
|
result &&
|
|
isBullish
|
|
? conditions.isTrendBullish
|
|
: conditions.isTrendBearish;
|
|
}
|
|
|
|
//
|
|
// Filter Using Trend Direction On Both Side ...
|
|
if (result &&
|
|
filters.forceHasTrendDirOnBothSide)
|
|
{
|
|
//
|
|
// Side To ...
|
|
result =
|
|
result &&
|
|
isBullish
|
|
? conditions.isTrendBullish
|
|
: conditions.isTrendBearish;
|
|
|
|
//
|
|
// Side From ...
|
|
int sFIDX = triggerBlock.ob.FromIndex();
|
|
double sFTrendState = helper.GetTrendState(sFIDX);
|
|
result =
|
|
result &&
|
|
isBullish
|
|
? sFTrendState > 0
|
|
: sFTrendState < 0;
|
|
}
|
|
|
|
//
|
|
// Filter Using Trend Change ...
|
|
if (result &&
|
|
filters.forceHasTrendChange)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iTrendState = helper.GetTrendState(i);
|
|
double iPTrendState = helper.GetTrendState(i + 1);
|
|
|
|
//
|
|
bool isTrendSwitchedToBullish =
|
|
iTrendState > 0 &&
|
|
iPTrendState <= 0;
|
|
|
|
//
|
|
bool isTrendSwitchedToBearish =
|
|
iTrendState < 0 &&
|
|
iPTrendState >= 0;
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? isTrendSwitchedToBullish
|
|
: isTrendSwitchedToBearish;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using Trend Trending ...
|
|
if (result &&
|
|
filters.forceHasTrendTrending)
|
|
{
|
|
//
|
|
result =
|
|
result &&
|
|
(isBullish
|
|
? conditions.trendBuffer[cIDX] > conditions.trendBuffer[pIDX]
|
|
: conditions.trendBuffer[cIDX] < conditions.trendBuffer[pIDX]);
|
|
}
|
|
|
|
//
|
|
// Filter Using Trend Rejection ...
|
|
if (result &&
|
|
filters.forceHasTrendRejection)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
// Initialize iBar ...
|
|
has = iBar.Init(
|
|
conditions.symbol,
|
|
conditions.period,
|
|
i //
|
|
);
|
|
|
|
//
|
|
// Read Trend Value ...
|
|
double iTrend = helper.GetTrend(i);
|
|
|
|
//
|
|
bool isTrendBullishRejected =
|
|
has &&
|
|
iBar.low < iTrend &&
|
|
iBar.GetDown() > iTrend;
|
|
|
|
//
|
|
bool isTrendBearishRejected =
|
|
has &&
|
|
iBar.high > iTrend &&
|
|
iBar.GetUp() < iTrend;
|
|
|
|
//
|
|
has =
|
|
has &&
|
|
(isBullish
|
|
? isTrendBullishRejected
|
|
: isTrendBearishRejected);
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using Trend Inside ...
|
|
if (result &&
|
|
filters.forceHasTrendLineInside)
|
|
{
|
|
//
|
|
result =
|
|
result &&
|
|
conditions.trendBuffer[cIDX] > triggerBlock.ob.lower &&
|
|
conditions.trendBuffer[cIDX] < triggerBlock.ob.upper;
|
|
}
|
|
|
|
//
|
|
// Filter Using Trend Place ...
|
|
if (result &&
|
|
filters.forceHasTrendPlace)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
double mult =
|
|
isBullish
|
|
? 1
|
|
: -1;
|
|
double edge = isBullish
|
|
? triggerBlock.ob.lower
|
|
: triggerBlock.ob.upper;
|
|
double factor = edge + (mult * (triggerBlock.ob.GetRange() / 4));
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iTrend = helper.GetTrend(i);
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? factor >= iTrend
|
|
: factor <= iTrend;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Using Delta ...
|
|
if (result &&
|
|
filters.filterBasedOnDelta)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iDelta = helper.GetDelta(i);
|
|
double iPDelta = helper.GetDelta(i + 1);
|
|
|
|
//
|
|
double iDeltaSignal = helper.GetDeltaSignal(i);
|
|
double iPDeltaSignal = helper.GetDeltaSignal(i + 1);
|
|
|
|
//
|
|
bool isDeltaUp =
|
|
iDelta > iPDelta;
|
|
|
|
//
|
|
bool isDeltaDown =
|
|
iDelta < iPDelta;
|
|
|
|
//
|
|
bool isDeltaSignalUp =
|
|
iDeltaSignal > iPDeltaSignal;
|
|
|
|
//
|
|
bool isDeltaSignalDown =
|
|
iDeltaSignal < iPDeltaSignal;
|
|
|
|
//
|
|
bool isDeltaCrossedOverSignal =
|
|
iDelta > iDeltaSignal &&
|
|
iPDelta <= iPDeltaSignal;
|
|
|
|
//
|
|
bool isDeltaCrossedUnderSignal =
|
|
iDelta < iDeltaSignal &&
|
|
iPDelta >= iPDeltaSignal;
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? (isDeltaUp &&
|
|
isDeltaSignalUp &&
|
|
isDeltaCrossedOverSignal)
|
|
: (isDeltaDown &&
|
|
isDeltaSignalDown &&
|
|
isDeltaCrossedUnderSignal);
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using Volume ...
|
|
if (result &&
|
|
filters.filterBasedOnVolume)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
double iBullVolume = helper.GetBullishVolume(i);
|
|
double iPBullVolume = helper.GetBullishVolume(i + 1);
|
|
|
|
//
|
|
double iBullVolumeSignal = helper.GetBullishVolumeSignal(i);
|
|
double iPBullVolumeSignal = helper.GetBullishVolumeSignal(i + 1);
|
|
|
|
//
|
|
double iBearVolume = helper.GetBearishVolume(i);
|
|
double iPBearVolume = helper.GetBearishVolume(i + 1);
|
|
|
|
//
|
|
double iBearVolumeSignal = helper.GetBearishVolumeSignal(i);
|
|
double iPBearVolumeSignal = helper.GetBearishVolumeSignal(i + 1);
|
|
|
|
//
|
|
bool isBullVolumeUp =
|
|
iBullVolume > iPBullVolume;
|
|
|
|
//
|
|
bool isBullVolumeSignalUp =
|
|
iBullVolumeSignal > iPBullVolumeSignal;
|
|
|
|
//
|
|
bool isBearVolumeUp =
|
|
iBearVolume > iPBearVolume;
|
|
|
|
//
|
|
bool isBearVolumeSignalUp =
|
|
iBearVolumeSignal > iPBearVolumeSignal;
|
|
|
|
//
|
|
bool isVolumeSwitchedToBullish =
|
|
iBullVolumeSignal > iBearVolumeSignal &&
|
|
iPBullVolumeSignal <= iPBearVolumeSignal;
|
|
|
|
//
|
|
bool isVolumeSwitchedToBearish =
|
|
iBearVolumeSignal > iBullVolumeSignal &&
|
|
iPBearVolumeSignal <= iPBullVolumeSignal;
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? (isBullVolumeUp &&
|
|
isBullVolumeSignalUp &&
|
|
isVolumeSwitchedToBullish)
|
|
: (isBearVolumeUp &&
|
|
isBearVolumeSignalUp &&
|
|
isVolumeSwitchedToBearish);
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
|
|
//
|
|
// Filter Using Signal Bar ...
|
|
if (result &&
|
|
filters.filterBasedOnSignalBar)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
|
|
//
|
|
double iSOpen = helper.GetSBarOpen(toIDX);
|
|
double iPSOpen = helper.GetSBarOpen(toIDX + 1);
|
|
|
|
//
|
|
double iSClose = helper.GetSBarClose(toIDX);
|
|
double iPSClose = helper.GetSBarClose(toIDX + 1);
|
|
|
|
//
|
|
double iSPow = MathAbs(iSOpen - iSClose);
|
|
double iPSPow = MathAbs(iPSOpen - iPSClose);
|
|
|
|
//
|
|
result =
|
|
result &&
|
|
iSPow > iPSPow &&
|
|
(isBullish
|
|
? iSClose > iSOpen
|
|
: iSClose < iSOpen);
|
|
|
|
//
|
|
// Now Looking for Dir Change ...
|
|
if (result)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
iSOpen = helper.GetSBarOpen(toIDX);
|
|
iPSOpen = helper.GetSBarOpen(toIDX + 1);
|
|
|
|
//
|
|
iSClose = helper.GetSBarClose(toIDX);
|
|
iPSClose = helper.GetSBarClose(toIDX + 1);
|
|
|
|
//
|
|
bool isSBullish =
|
|
iSClose > iSOpen;
|
|
|
|
//
|
|
bool isPSBullish =
|
|
iPSClose > iPSOpen;
|
|
|
|
//
|
|
bool isSBearish =
|
|
iSClose < iSOpen;
|
|
|
|
//
|
|
bool isPSBearish =
|
|
iPSClose < iPSOpen;
|
|
|
|
//
|
|
bool isSSwitchedToBullish =
|
|
isSBullish &&
|
|
!isPSBullish;
|
|
|
|
//
|
|
bool isSSwitchedToBearish =
|
|
isSBearish &&
|
|
!isPSBearish;
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? isSSwitchedToBullish
|
|
: isSSwitchedToBearish;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Using Signal Bar ...
|
|
if (result &&
|
|
filters.filterBasedOnHKSignalBar)
|
|
{
|
|
//
|
|
toIDX = triggerBlock.ob.ToIndex();
|
|
fromIDX = triggerBlock.ob.FromIndex();
|
|
|
|
//
|
|
double iSOpen = helper.GetHKSBarOpen(toIDX);
|
|
double iPSOpen = helper.GetHKSBarOpen(toIDX + 1);
|
|
|
|
//
|
|
double iSClose = helper.GetHKSBarClose(toIDX);
|
|
double iPSClose = helper.GetHKSBarClose(toIDX + 1);
|
|
|
|
//
|
|
double iSPow = MathAbs(iSOpen - iSClose);
|
|
double iPSPow = MathAbs(iPSOpen - iPSClose);
|
|
|
|
//
|
|
result =
|
|
result &&
|
|
iSPow > iPSPow &&
|
|
(isBullish
|
|
? iSClose > iSOpen
|
|
: iSClose < iSOpen);
|
|
|
|
//
|
|
// Now Looking for Dir Change ...
|
|
if (result)
|
|
{
|
|
//
|
|
for (int i = toIDX; i < fromIDX; i++)
|
|
{
|
|
//
|
|
iSOpen = helper.GetHKSBarOpen(toIDX);
|
|
iPSOpen = helper.GetHKSBarOpen(toIDX + 1);
|
|
|
|
//
|
|
iSClose = helper.GetHKSBarClose(toIDX);
|
|
iPSClose = helper.GetHKSBarClose(toIDX + 1);
|
|
|
|
//
|
|
bool isSBullish =
|
|
iSClose > iSOpen;
|
|
|
|
//
|
|
bool isPSBullish =
|
|
iPSClose > iPSOpen;
|
|
|
|
//
|
|
bool isSBearish =
|
|
iSClose < iSOpen;
|
|
|
|
//
|
|
bool isPSBearish =
|
|
iPSClose < iPSOpen;
|
|
|
|
//
|
|
bool isSSwitchedToBullish =
|
|
isSBullish &&
|
|
!isPSBullish;
|
|
|
|
//
|
|
bool isSSwitchedToBearish =
|
|
isSBearish &&
|
|
!isPSBearish;
|
|
|
|
//
|
|
has =
|
|
isBullish
|
|
? isSSwitchedToBullish
|
|
: isSSwitchedToBearish;
|
|
if (has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = has;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
triggerBlock.Clean();
|
|
}
|
|
|
|
//
|
|
// Summarize result ...
|
|
result = triggerBlock.IsValid();
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
iBar.Clean();
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
//
|
|
conditions.Clean();
|
|
triggerBlock.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void ValidateTriggerBlockLiquidities(
|
|
XCX121XCatbHelper *helper,
|
|
XCBarAnalyser *barAnalyser,
|
|
XTriggerBlock &triggerBlock,
|
|
X121XCatbConditions &conditions,
|
|
XTriggerBlockValidationRules &validations //
|
|
)
|
|
{
|
|
//
|
|
bool has = false;
|
|
|
|
//
|
|
has =
|
|
helper != NULL &&
|
|
barAnalyser != NULL &&
|
|
triggerBlock.IsValid() &&
|
|
(triggerBlock.fLiquidity.IsValid() ||
|
|
triggerBlock.rLiquidity.IsValid());
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool hasFLiq = triggerBlock.fLiquidity.IsValid();
|
|
bool hasRLiq = triggerBlock.rLiquidity.IsValid();
|
|
|
|
//
|
|
// TODO: Implement Liquidity Validation ...
|
|
}
|
|
|
|
//
|
|
bool DetectSignalZone(
|
|
XCPOIDrawer *&drawer,
|
|
XCX121XCatbHelper *helper,
|
|
XCBarAnalyser *barAnalyser,
|
|
XBoxZone &signalZone,
|
|
X121XCatbConditions &conditions,
|
|
int barIndex = 0,
|
|
int loopback = 10 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Checking Conditions ...
|
|
|
|
//
|
|
// Normalize Args ...
|
|
signalZone.Clean();
|
|
conditions.Clean();
|
|
barIndex = NormalizeInt(barIndex, 0);
|
|
loopback = NormalizeInt(loopback, 0);
|
|
|
|
//
|
|
// Validate Args ...
|
|
result = helper != NULL &&
|
|
barAnalyser != NULL;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reading Market Conditions ...
|
|
result = helper.GetConditions(
|
|
conditions,
|
|
barIndex,
|
|
loopback //
|
|
);
|
|
if (!result)
|
|
{
|
|
//
|
|
signalZone.Clean();
|
|
conditions.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int zIDX = 0;
|
|
int cIDX = zIDX + 1;
|
|
int pIDX = cIDX + 1;
|
|
int p2IDX = pIDX + 1;
|
|
int p3IDX = p2IDX + 1;
|
|
int p4IDX = p3IDX + 1;
|
|
|
|
//
|
|
double sl = 0;
|
|
double tp = 0;
|
|
double upper = 0;
|
|
double lower = 0;
|
|
datetime to = NULL;
|
|
datetime from = NULL;
|
|
string signalProvider = NULL;
|
|
|
|
//
|
|
// Checking Conditions ...
|
|
|
|
//
|
|
// Detect Bar Verifications ...
|
|
|
|
//
|
|
ENUM_X_DIRECTION iDir;
|
|
|
|
//
|
|
// Rejected ...
|
|
bool isRejected =
|
|
barAnalyser
|
|
.IsRejected(
|
|
conditions.bars[cIDX],
|
|
iDir //
|
|
);
|
|
bool isBullishRejected =
|
|
isRejected &&
|
|
IsBullish(iDir);
|
|
bool isBearishRejected =
|
|
isRejected &&
|
|
IsBearish(iDir);
|
|
|
|
//
|
|
// Engulfed ...
|
|
bool isEngulfed =
|
|
barAnalyser
|
|
.IsEngulfed(
|
|
conditions.bars[cIDX],
|
|
iDir //
|
|
);
|
|
bool isBullishEngulfed =
|
|
isEngulfed &&
|
|
IsBullish(iDir);
|
|
bool isBearishEngulfed =
|
|
isEngulfed &&
|
|
IsBearish(iDir);
|
|
|
|
//
|
|
// Momentum ...
|
|
bool isMomentum =
|
|
barAnalyser
|
|
.IsMomentum(
|
|
conditions.bars[cIDX],
|
|
iDir //
|
|
);
|
|
bool isBullishMomentum =
|
|
isMomentum &&
|
|
IsBullish(iDir);
|
|
bool isBearishMomentum =
|
|
isMomentum &&
|
|
IsBearish(iDir);
|
|
|
|
//
|
|
bool isBarBullishPressured =
|
|
isBullishRejected ||
|
|
isBullishEngulfed ||
|
|
isBullishMomentum;
|
|
|
|
//
|
|
bool isBarBearishPressured =
|
|
isBearishRejected ||
|
|
isBearishEngulfed ||
|
|
isBearishMomentum;
|
|
|
|
//
|
|
// Detect Indicator Base Start Directional Movement ...
|
|
|
|
//
|
|
// SAR ...
|
|
|
|
//
|
|
bool isSarBullishStart =
|
|
//
|
|
(
|
|
//
|
|
// Current SAR is Directional ...
|
|
conditions.isSarBullish &&
|
|
//
|
|
// Previous InDirectional Sar Exists ...
|
|
helper.lastSarBearish.IsValid() &&
|
|
//
|
|
// Current Close is Over previous Sar ...
|
|
conditions.bars[cIDX].close > helper.lastSarBearish.after &&
|
|
//
|
|
// Before Close is not Over previous Sar ...
|
|
conditions.bars[pIDX].close <= helper.lastSarBearish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isSarBearishStart =
|
|
//
|
|
(
|
|
//
|
|
// Current SAR is Directional ...
|
|
conditions.isSarBearish &&
|
|
//
|
|
// Previous InDirectional Sar Exists ...
|
|
helper.lastSarBullish.IsValid() &&
|
|
//
|
|
// Current Close is Under previous ...
|
|
conditions.bars[cIDX].close < helper.lastSarBullish.after &&
|
|
//
|
|
// Before Close is not Under previous ...
|
|
conditions.bars[pIDX].close >= helper.lastSarBullish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
// TREND ...
|
|
|
|
//
|
|
bool isTrendBullishStart =
|
|
//
|
|
(
|
|
//
|
|
// Current Trend is Directional ...
|
|
conditions.isTrendBullish &&
|
|
//
|
|
// Previous InDirectiona; Trend Exists ...
|
|
helper.lastTrendBearish.IsValid() &&
|
|
//
|
|
// Current Close is Over previous ...
|
|
conditions.bars[cIDX].close > helper.lastTrendBearish.after &&
|
|
//
|
|
// Before close is not Over previous ...
|
|
conditions.bars[pIDX].close <= helper.lastTrendBearish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isTrendBearishStart =
|
|
//
|
|
(
|
|
//
|
|
// Current Trend is Directional ...
|
|
conditions.isTrendBearish &&
|
|
//
|
|
// Previous InDirectiona; Trend Exists ...
|
|
helper.lastTrendBullish.IsValid() &&
|
|
//
|
|
// Current Close is Under previous ...
|
|
conditions.bars[cIDX].close < helper.lastTrendBullish.after &&
|
|
//
|
|
// Before close is not Under previous ...
|
|
conditions.bars[pIDX].close >= helper.lastTrendBullish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
// ADX ...
|
|
|
|
//
|
|
bool isADXBullishStart =
|
|
//
|
|
(
|
|
//
|
|
// ADX Value is Switched to Strong ...
|
|
conditions.adxBuffer[cIDX] > conditions.adxThreshold &&
|
|
conditions.adxBuffer[pIDX] <= conditions.adxThreshold &&
|
|
//
|
|
// ADX must Up ...
|
|
conditions.adxBuffer[cIDX] > conditions.adxBuffer[pIDX] &&
|
|
//
|
|
// ADX +DI is Bigger than -DI ...
|
|
conditions.adxpBuffer[cIDX] > conditions.adxnBuffer[cIDX] &&
|
|
//
|
|
// ADX +DI is Up ...
|
|
conditions.adxpBuffer[cIDX] > conditions.adxpBuffer[pIDX]
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isADXBearishStart =
|
|
//
|
|
(
|
|
//
|
|
// ADX Value is Switched to Strong ...
|
|
conditions.adxBuffer[cIDX] > conditions.adxThreshold &&
|
|
conditions.adxBuffer[pIDX] <= conditions.adxThreshold &&
|
|
//
|
|
// ADX must Up ...
|
|
conditions.adxBuffer[cIDX] > conditions.adxBuffer[pIDX] &&
|
|
//
|
|
// ADX -DI is Bigger than +DI ...
|
|
conditions.adxnBuffer[cIDX] > conditions.adxpBuffer[cIDX] &&
|
|
//
|
|
// ADX -DI is Up ...
|
|
conditions.adxnBuffer[cIDX] > conditions.adxnBuffer[pIDX]
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
// DELTA ...
|
|
|
|
//
|
|
bool isDeltaBullishStart =
|
|
//
|
|
// Check State ...
|
|
(
|
|
//
|
|
// Delta is Up ...
|
|
conditions.deltaBuffer[cIDX] > conditions.deltaBuffer[pIDX] &&
|
|
//
|
|
// Delta Signal is Up ...
|
|
conditions.deltaSignalBuffer[cIDX] > conditions.deltaSignalBuffer[pIDX] &&
|
|
//
|
|
// Delta Crossed Over Delta Signal ...
|
|
conditions.deltaBuffer[cIDX] > conditions.deltaSignalBuffer[cIDX]
|
|
//
|
|
)
|
|
//
|
|
&&
|
|
//
|
|
// Check Last InDirectional Exists ...
|
|
helper.lastDeltaBearish.IsValid() &&
|
|
//
|
|
// Current Close is Over last ...
|
|
conditions.bars[cIDX].close > helper.lastDeltaBearish.after &&
|
|
//
|
|
// Before Close is not Over last ...
|
|
conditions.bars[pIDX].close <= helper.lastDeltaBearish.after
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isDeltaBearishStart =
|
|
//
|
|
// Check State ...
|
|
(
|
|
//
|
|
// Delta is Down ...
|
|
conditions.deltaBuffer[cIDX] < conditions.deltaBuffer[pIDX] &&
|
|
//
|
|
// Delta Signal is Down ...
|
|
conditions.deltaSignalBuffer[cIDX] < conditions.deltaSignalBuffer[pIDX] &&
|
|
//
|
|
// Delta Crossed Under Delta Signal ...
|
|
conditions.deltaBuffer[cIDX] < conditions.deltaSignalBuffer[cIDX]
|
|
//
|
|
)
|
|
//
|
|
&&
|
|
//
|
|
// Check Last InDirectional Exists ...
|
|
helper.lastDeltaBullish.IsValid() &&
|
|
//
|
|
// Current Close is Under last ...
|
|
conditions.bars[cIDX].close < helper.lastDeltaBullish.after &&
|
|
//
|
|
// Before Close is not Under last ...
|
|
conditions.bars[pIDX].close >= helper.lastDeltaBullish.after
|
|
//
|
|
;
|
|
|
|
//
|
|
// VOLUME ...
|
|
|
|
//
|
|
bool isVolumeBullishStart =
|
|
//
|
|
(
|
|
//
|
|
//
|
|
// Volume is Up ...
|
|
conditions.bullishVolumeBuffer[1] > conditions.bullishVolumeBuffer[2] &&
|
|
//
|
|
// Volume Signal is Up ...
|
|
conditions.bullishVolumeSignalBuffer[1] > conditions.bullishVolumeSignalBuffer[2] &&
|
|
//
|
|
// Volume Signals Crossed Over ...
|
|
conditions.bullishVolumeSignalBuffer[1] > conditions.bearishVolumeSignalBuffer[1]
|
|
//
|
|
)
|
|
//
|
|
&&
|
|
//
|
|
// Check Last InDirectional Exists ...
|
|
helper.lastVolumeBearish.IsValid() &&
|
|
//
|
|
// Current Close is Over last ...
|
|
conditions.bars[cIDX].close > helper.lastVolumeBearish.after &&
|
|
//
|
|
// Before Close is not Over last ...
|
|
conditions.bars[pIDX].close <= helper.lastVolumeBearish.after
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isVolumeBearishStart =
|
|
//
|
|
(
|
|
//
|
|
//
|
|
// Volume is Up ...
|
|
conditions.bearishVolumeBuffer[1] > conditions.bearishVolumeBuffer[2] &&
|
|
//
|
|
// Volume Signal is Up ...
|
|
conditions.bearishVolumeSignalBuffer[1] > conditions.bearishVolumeSignalBuffer[2] &&
|
|
//
|
|
// Volume Signals Crossed Over ...
|
|
conditions.bearishVolumeSignalBuffer[1] > conditions.bullishVolumeSignalBuffer[1]
|
|
//
|
|
)
|
|
//
|
|
&&
|
|
//
|
|
// Check Last InDirectional Exists ...
|
|
helper.lastVolumeBullish.IsValid() &&
|
|
//
|
|
// Current Close is Over last ...
|
|
conditions.bars[cIDX].close < helper.lastVolumeBullish.after &&
|
|
//
|
|
// Before Close is not Over last ...
|
|
conditions.bars[pIDX].close >= helper.lastVolumeBullish.after
|
|
//
|
|
;
|
|
|
|
//
|
|
// SIGNAL Bar ...
|
|
|
|
//
|
|
bool isSBarBullishStart =
|
|
//
|
|
(
|
|
//
|
|
// Check Direction ...
|
|
conditions.isSBarBullish &&
|
|
//
|
|
// Previous InDirectional Exists ...
|
|
helper.lastSignalBarBearish.IsValid() &&
|
|
//
|
|
// Current Signal Bar close over prev ...
|
|
conditions.sBarCloseBuffer[cIDX] > helper.lastSignalBarBearish.after &&
|
|
//
|
|
// Prev Signal Bar not close over prev ...
|
|
conditions.sBarCloseBuffer[pIDX] <= helper.lastSignalBarBearish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isSBarBearishStart =
|
|
//
|
|
(
|
|
//
|
|
// Check Direction ...
|
|
conditions.isSBarBearish &&
|
|
//
|
|
// Previous InDirectional Exists ...
|
|
helper.lastSignalBarBullish.IsValid() &&
|
|
//
|
|
// Current Signal Bar close over prev ...
|
|
conditions.sBarCloseBuffer[cIDX] < helper.lastSignalBarBullish.after &&
|
|
//
|
|
// Prev Signal Bar not close over prev ...
|
|
conditions.sBarCloseBuffer[pIDX] >= helper.lastSignalBarBullish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
// HKSIGNAL Bar ...
|
|
|
|
//
|
|
bool isHKSBarBullishStart =
|
|
//
|
|
(
|
|
//
|
|
// Check Direction ...
|
|
conditions.isHKSBarBullish &&
|
|
//
|
|
// Previous InDirectional Exists ...
|
|
helper.lastHKSignalBarBearish.IsValid() &&
|
|
//
|
|
// Current Signal Bar close over prev ...
|
|
conditions.hkSBarCloseBuffer[cIDX] > helper.lastHKSignalBarBearish.after &&
|
|
//
|
|
// Prev Signal Bar not close over prev ...
|
|
conditions.hkSBarCloseBuffer[pIDX] <= helper.lastHKSignalBarBearish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isHKSBarBearishStart =
|
|
//
|
|
(
|
|
//
|
|
// Check Direction ...
|
|
conditions.isHKSBarBearish &&
|
|
//
|
|
// Previous InDirectional Exists ...
|
|
helper.lastHKSignalBarBullish.IsValid() &&
|
|
//
|
|
// Current Signal Bar close over prev ...
|
|
conditions.hkSBarCloseBuffer[cIDX] < helper.lastHKSignalBarBullish.after &&
|
|
//
|
|
// Prev Signal Bar not close over prev ...
|
|
conditions.hkSBarCloseBuffer[pIDX] >= helper.lastHKSignalBarBullish.after
|
|
//
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
// Check for Vale and Peak ...
|
|
|
|
//
|
|
bool isPeakOverLast =
|
|
//
|
|
(conditions.peakBuffer[cIDX] > conditions.peakBuffer[pIDX] &&
|
|
conditions.peakBuffer[pIDX] <= conditions.peakBuffer[p2IDX] &&
|
|
conditions.bars[cIDX].close > conditions.peakBuffer[pIDX])
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isValeUnderLast =
|
|
//
|
|
(conditions.valeBuffer[cIDX] < conditions.valeBuffer[pIDX] &&
|
|
conditions.valeBuffer[pIDX] >= conditions.valeBuffer[p2IDX] &&
|
|
conditions.bars[cIDX].close < conditions.valeBuffer[pIDX])
|
|
//
|
|
;
|
|
|
|
//
|
|
// Start Different Conditions Implemntation ...
|
|
|
|
//
|
|
// Condition 1:
|
|
// ---------------------------
|
|
// Descriptions:
|
|
//
|
|
//
|
|
// ---------------------------
|
|
|
|
//
|
|
// Bullish ...
|
|
bool isCond1Bullish = false;
|
|
|
|
//
|
|
// Bearish ...
|
|
bool isCond1Bearish = false;
|
|
|
|
//
|
|
bool isCond1 =
|
|
isCond1Bullish ||
|
|
isCond1Bearish;
|
|
if (isCond1)
|
|
{
|
|
//
|
|
// Prepare Cond1 Based Requirements ...
|
|
|
|
//
|
|
to = NULL;
|
|
upper = 0;
|
|
lower = 0;
|
|
from = NULL;
|
|
|
|
//
|
|
// Provider Definition ...
|
|
signalProvider = "Cond1";
|
|
}
|
|
|
|
//
|
|
// Try to Summarize Conditions ...
|
|
|
|
//
|
|
bool isBullish =
|
|
//
|
|
isCond1Bullish
|
|
//
|
|
;
|
|
|
|
//
|
|
bool isBearish =
|
|
//
|
|
isCond1Bearish
|
|
//
|
|
;
|
|
|
|
//
|
|
// Summarize Result ...
|
|
result =
|
|
isBullish ||
|
|
isBearish;
|
|
if (result)
|
|
{
|
|
//
|
|
// Prepare Signal Zone ...
|
|
|
|
//
|
|
signalZone.to = to;
|
|
signalZone.from = from;
|
|
signalZone.upper = upper;
|
|
signalZone.lower = lower;
|
|
signalZone.type = signalProvider;
|
|
signalZone.symbol = conditions.symbol;
|
|
signalZone.period = conditions.period;
|
|
|
|
//
|
|
signalZone.dir =
|
|
isBullish
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Validate Signal Zone ...
|
|
result = signalZone.IsValid();
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
//
|
|
signalZone.Clean();
|
|
conditions.Clean();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Draw Specific Trigger Block ...
|
|
bool DrawTriggerBlock(
|
|
XTriggerBlock &trigger,
|
|
XCPOIDrawer *drawer,
|
|
CArrayObj &objects,
|
|
datetime to = NULL,
|
|
//
|
|
bool _drawSignals = false,
|
|
int rrZoneLength = 10,
|
|
// Styles ...
|
|
//
|
|
// Trigger Block ...
|
|
int tbWidth = 1, // Trigger Block Width
|
|
color tbBullishColor = clrAqua, // Trigger Block Bullish Color
|
|
color tbBearishColor = clrMagenta, // Trigger Block Bearish Color
|
|
ENUM_LINE_STYLE tbStyle = STYLE_DASHDOTDOT, // Trigger Block Style
|
|
//
|
|
// OB ...
|
|
int obWidth = 1, // OB Width
|
|
color obBullishColor = clrLime, // OB Bullish Color
|
|
color obBearishColor = clrRed, // OB Bearish Color
|
|
ENUM_LINE_STYLE obStyle = STYLE_DOT, // OB Style
|
|
//
|
|
// FVG ...
|
|
int fvgWidth = 2, // FVG Width
|
|
color fvgBullishColor = clrLime, // FVG Bullish Color
|
|
color fvgBearishColor = clrRed, // FVG Bearish Color
|
|
ENUM_LINE_STYLE fvgStyle = STYLE_SOLID // FVG Style
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
result = trigger.IsValid() &&
|
|
drawer != NULL;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XTriggerBlock _trigger = trigger;
|
|
|
|
//
|
|
// Update To ...
|
|
bool canUpdateTo = IsValid(to);
|
|
if (canUpdateTo)
|
|
{
|
|
//
|
|
_trigger.trigger.to = to;
|
|
_trigger.rLiquidity.to = to;
|
|
_trigger.fLiquidity.to = to;
|
|
}
|
|
|
|
//
|
|
bool isBullish = _trigger.IsBullish();
|
|
|
|
//
|
|
// Draw OB ...
|
|
if (_trigger.ob.IsValid())
|
|
{
|
|
//
|
|
XCBoxObject *iOBObj;
|
|
result = drawer.DrawBox(
|
|
_trigger.ob,
|
|
iOBObj //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
// Style Object ...
|
|
|
|
//
|
|
color iClr =
|
|
isBullish
|
|
? obBullishColor
|
|
: obBearishColor;
|
|
|
|
//
|
|
iOBObj.BoxColor(iClr);
|
|
iOBObj.BoxWidth(obWidth);
|
|
iOBObj.BoxStyle(obStyle);
|
|
|
|
//
|
|
// Store Object ...
|
|
objects.Add(iOBObj);
|
|
}
|
|
ZeroMemory(iOBObj);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw FVG ...
|
|
if (_trigger.fvg.IsValid())
|
|
{
|
|
//
|
|
XCBoxObject *iFVGObj;
|
|
result = drawer.DrawBox(
|
|
_trigger.fvg,
|
|
iFVGObj //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
// Style Object ...
|
|
|
|
//
|
|
color iClr =
|
|
isBullish
|
|
? fvgBullishColor
|
|
: fvgBearishColor;
|
|
|
|
//
|
|
iFVGObj.BoxColor(iClr);
|
|
iFVGObj.BoxWidth(fvgWidth);
|
|
iFVGObj.BoxStyle(fvgStyle);
|
|
|
|
//
|
|
// Store Object ...
|
|
objects.Add(iFVGObj);
|
|
}
|
|
ZeroMemory(iFVGObj);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw Trigger ...
|
|
if (_trigger.trigger.IsValid())
|
|
{
|
|
//
|
|
XCBoxObject *iTriggerObj;
|
|
result = drawer.DrawBox(
|
|
_trigger.trigger,
|
|
iTriggerObj //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
// Style Object ...
|
|
|
|
//
|
|
color iClr =
|
|
isBullish
|
|
? tbBullishColor
|
|
: tbBearishColor;
|
|
|
|
//
|
|
iTriggerObj.BoxColor(iClr);
|
|
iTriggerObj.BoxWidth(tbWidth);
|
|
iTriggerObj.BoxStyle(tbStyle);
|
|
|
|
//
|
|
// Store Object ...
|
|
objects.Add(iTriggerObj);
|
|
}
|
|
ZeroMemory(iTriggerObj);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw Liquidities ...
|
|
|
|
//
|
|
// Reversla Liquidity ...
|
|
if (_trigger.rLiquidity.IsValid())
|
|
{
|
|
//
|
|
XCBoxObject *iLiqObj;
|
|
result = drawer.DrawBox(
|
|
_trigger.rLiquidity,
|
|
iLiqObj //
|
|
);
|
|
if (result)
|
|
{
|
|
objects.Add(iLiqObj);
|
|
}
|
|
ZeroMemory(iLiqObj);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Following Liquidity ...
|
|
if (_trigger.fLiquidity.IsValid())
|
|
{
|
|
//
|
|
XCBoxObject *iLiqObj;
|
|
result = drawer.DrawBox(
|
|
_trigger.fLiquidity,
|
|
iLiqObj //
|
|
);
|
|
if (result)
|
|
{
|
|
objects.Add(iLiqObj);
|
|
}
|
|
ZeroMemory(iLiqObj);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw Swing Bar ...
|
|
if (_trigger.swingBar.IsValid())
|
|
{
|
|
//
|
|
color swingColor =
|
|
_trigger.fvg.IsBullish()
|
|
? clrAqua
|
|
: clrMagenta;
|
|
ENUM_X_PRICE swingPType =
|
|
_trigger.fvg.IsBullish()
|
|
? X_PRICE_LOW
|
|
: X_PRICE_HIGH;
|
|
|
|
//
|
|
XCBarArrowObject *swingObj;
|
|
result = drawer.CreateBarArrow(
|
|
_trigger.swingBar,
|
|
swingObj,
|
|
swingPType,
|
|
159,
|
|
swingColor //
|
|
);
|
|
if (result)
|
|
{
|
|
objects.Add(swingObj);
|
|
}
|
|
ZeroMemory(swingObj);
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw RR of Signal ...
|
|
if (_drawSignals &&
|
|
_trigger.signal.IsValid() &&
|
|
rrZoneLength > 0)
|
|
{
|
|
//
|
|
XCRRObject *iRRObj;
|
|
iRRObj = new XCRRObject();
|
|
result = iRRObj.Create(
|
|
drawer.ChartIdentification(),
|
|
drawer.SubWindowIdentification(),
|
|
_trigger.signal,
|
|
rrZoneLength //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
int iWidth = 2;
|
|
ENUM_LINE_STYLE iStyle = STYLE_SOLID;
|
|
|
|
//
|
|
iRRObj.TPWidth(iWidth);
|
|
iRRObj.SLWidth(iWidth);
|
|
iRRObj.EntryWidth(iWidth);
|
|
iRRObj.TargetWidth(iWidth);
|
|
|
|
//
|
|
iRRObj.TPStyle(iStyle);
|
|
iRRObj.SLStyle(iStyle);
|
|
iRRObj.EntryStyle(iStyle);
|
|
iRRObj.TargetStyle(iStyle);
|
|
|
|
//
|
|
iRRObj.TPColor(clrLime);
|
|
iRRObj.SLColor(clrRed);
|
|
iRRObj.EntryColor(clrYellow);
|
|
iRRObj.TargetColor(clrLightBlue);
|
|
|
|
//
|
|
objects.Add(iRRObj);
|
|
}
|
|
ZeroMemory(iRRObj);
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|