1844 lines
46 KiB
Plaintext
1844 lines
46 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 X121 SMC Signal Class
|
|
// -------------------------------------------------
|
|
// Name: XWZ
|
|
// Description: provide all Signalling 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-121.smc.market.cycle.helper.class.mq5"
|
|
#include "../Libraries/x-121.smc.guard.lib.mq5"
|
|
#include "../Libraries/x-121.smc.lib.mq5"
|
|
#include "./x-121.smc.xwz.signal.lib.mq5"
|
|
|
|
//
|
|
// Definition ...
|
|
class XCX121SMCXWZSignalDetector : public XCBase
|
|
{
|
|
//
|
|
public:
|
|
//
|
|
// Props ...
|
|
bool useGuards;
|
|
double rfRewar;
|
|
bool useQuarterTargets;
|
|
X121SMCStrategyXWZWorkingZone workingZone;
|
|
X121SMCStrategyXWZSignalConditions mConditions;
|
|
X121SMCStrategyXWZSignalConditions mConditionsCollection[];
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
XCX121SMCXWZSignalDetector(
|
|
string _symbol,
|
|
ENUM_TIMEFRAMES _period //
|
|
)
|
|
{
|
|
//
|
|
symbol = _symbol;
|
|
period = _period;
|
|
Init();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCX121SMCXWZSignalDetector()
|
|
{
|
|
DeInit();
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// Detect Setup Conditions ...
|
|
bool DetectSignalSetup(
|
|
//
|
|
X121SMCStrategySignalProviderData &provider,
|
|
//
|
|
double maxAllowedSLDistanceInPoint = 300
|
|
//
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Cycle Helpers ...
|
|
result = provider.IsValid();
|
|
if (!result)
|
|
{
|
|
//
|
|
mConditions.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isBullish = false;
|
|
bool isBearish = false;
|
|
|
|
//
|
|
int zIndex = 0;
|
|
int cIndex = zIndex + 1;
|
|
int pIndex = cIndex + 1;
|
|
int ppIndex = pIndex + 1;
|
|
|
|
//
|
|
int loopback = 10;
|
|
|
|
//
|
|
mConditions.symbol = symbol;
|
|
mConditions.period = period;
|
|
|
|
//
|
|
double ask = GetAsk(symbol);
|
|
double bid = GetBid(symbol);
|
|
datetime cTime = TimeCurrent();
|
|
double points = GetPoints(symbol);
|
|
|
|
//
|
|
// Cycles Conditions ...
|
|
result = provider.Prepare(
|
|
zIndex,
|
|
loopback,
|
|
true, // Trigger ...
|
|
true, // Decision ...
|
|
true, // Analyse ...
|
|
true, // Verification ...
|
|
true, // Consolidation ...
|
|
true // Vision ...
|
|
);
|
|
if (!result)
|
|
{
|
|
//
|
|
mConditions.Clean();
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Select Drawer Class ...
|
|
XCPOIDrawer *drawer = provider.visionCycleHelper.mPOIDrawer;
|
|
|
|
//
|
|
XOHCL consolidationZBar;
|
|
result = provider.consolidationCycleHelper.GetBar(
|
|
consolidationZBar,
|
|
zIndex //
|
|
);
|
|
if (!result)
|
|
{
|
|
//
|
|
consolidationZBar.Clean();
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool drawBar = false;
|
|
if (drawBar)
|
|
{
|
|
//
|
|
DrawBar(
|
|
consolidationZBar,
|
|
drawer //
|
|
);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// Setup Conditions ...
|
|
double sls[];
|
|
double sl = 0;
|
|
double pivot = 0;
|
|
double point = 0;
|
|
double targets[];
|
|
double reward = 0;
|
|
XSignalBox signalBox;
|
|
bool hasTrend = false;
|
|
bool hasPriority = false;
|
|
double amountPercent = -1;
|
|
bool hasSignalBox = false;
|
|
bool hasWaitingBoxes = false;
|
|
bool hasRewardAmount = false;
|
|
ENUM_X_DIRECTION trend = X_DIRECTION_NONE;
|
|
ENUM_X_DIRECTION signalBoxDir = X_DIRECTION_NONE;
|
|
ENUM_X121SMC_XWZ_PRIORITY priority = X121SMC_XWZ_PRIORITY_NONE;
|
|
ENUM_X121SMC_XWZ_PROVIDERS signalProvider = X121SMC_XWZ_PROVIDER_NONE;
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// First We Have to Has Working Zone ...
|
|
// bool hasWorkingZone = workingZone.IsValid();
|
|
// if (!hasWorkingZone)
|
|
// {
|
|
// //
|
|
// hasWorkingZone = workingZone.Init(
|
|
// symbol,
|
|
// period,
|
|
// zIndex,
|
|
// provider //
|
|
// );
|
|
// }
|
|
|
|
// //
|
|
// // Update Working Zone ...
|
|
// if (hasWorkingZone)
|
|
// {
|
|
// //
|
|
// // Reset POI Detector Required POI(s) ...
|
|
// provider.MaxRequiredPOIs(50);
|
|
|
|
// //
|
|
// // Reset POI Detector Allowed Loopback ...
|
|
// provider.MaxAllowedLoopbackForInit(578);
|
|
|
|
// //
|
|
// // Check Working Zone Has Trend ...
|
|
// hasTrend = workingZone.DetectTreand(
|
|
// X_CYCLE_CONSOLIDATION,
|
|
// trend,
|
|
// provider,
|
|
// zIndex //
|
|
// );
|
|
|
|
// //
|
|
// // Detectiong Signal Boxes ...
|
|
// // hasWaitingBoxes = workingZone
|
|
// // .DetectWaitingBoxes(
|
|
// // X_CYCLE_ANALYSE,
|
|
// // X_CYCLE_DECISION,
|
|
// // provider,
|
|
// // zIndex //
|
|
// // );
|
|
// if (hasWaitingBoxes)
|
|
// {
|
|
// //
|
|
// // Validate Working Zone Waiting Boxes ...
|
|
// workingZone.ValidateWaitingBoxes(zIndex);
|
|
|
|
// //
|
|
// // Double Check Has Waiting Boxes or not ...
|
|
// hasWaitingBoxes = workingZone.HasWaitingBoxes();
|
|
// }
|
|
|
|
// //
|
|
// // Checking Working Zone Destory or not ...
|
|
// bool isWorkingZoneDestroyed = workingZone.IsDestroyed(
|
|
// zIndex,
|
|
// period //
|
|
// );
|
|
|
|
// //
|
|
// // Cleanup WorkingZone and ignore all conditions ...
|
|
// if (isWorkingZoneDestroyed)
|
|
// {
|
|
// //
|
|
// result = false;
|
|
// workingZone.Clean();
|
|
// hasWorkingZone = false;
|
|
|
|
// //
|
|
// // Cleanup Resources ...
|
|
// provider.Clean();
|
|
|
|
// //
|
|
// mConditions.Clean();
|
|
// consolidationZBar.Clean();
|
|
|
|
// //
|
|
// return result;
|
|
// }
|
|
// }
|
|
|
|
//
|
|
// Update Models To Time ...
|
|
UpdateTo(cTime);
|
|
|
|
//
|
|
// Draw Working Zone ...
|
|
workingZone.Draw(
|
|
drawer,
|
|
//
|
|
false, // Draw Supply Zone ...
|
|
false, // Draw Demand Zone ...
|
|
true, // Draw Waiting Zones ...
|
|
false // Draw Trend ...
|
|
);
|
|
|
|
//
|
|
// Detect Signal Box ...
|
|
hasSignalBox =
|
|
// hasWorkingZone &&
|
|
workingZone.DetectSignalConditions(
|
|
signalBox,
|
|
signalBoxDir,
|
|
signalProvider,
|
|
provider,
|
|
zIndex //
|
|
);
|
|
|
|
//
|
|
// Remove Signal Box form
|
|
// Waiting Boxes and Breaked Boxes
|
|
// if Exists on them, then add it to
|
|
// UnAvailable Boxes ...
|
|
if (hasSignalBox)
|
|
{
|
|
//
|
|
// Remove Signal Box from UnAvailable Box ...
|
|
// if Exists ...
|
|
bool hasBreakedBoxes = workingZone
|
|
.HasBreakedBoxes();
|
|
if (hasBreakedBoxes)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
bool has = FindIndex(
|
|
idx,
|
|
signalBox,
|
|
workingZone.breakedBoxes //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
ArrayRemove(
|
|
workingZone.breakedBoxes,
|
|
idx,
|
|
1 //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add Signal Box to UnAvailable Boxes ...
|
|
// if Exists in Waiting Boxes ...
|
|
if (hasWaitingBoxes)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
bool has = FindIndex(
|
|
idx,
|
|
signalBox,
|
|
workingZone.waitingBoxes //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
ArrayRemove(
|
|
workingZone.waitingBoxes,
|
|
idx,
|
|
1 //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adding to UnAvailable Box ...
|
|
AddRef(
|
|
signalBox,
|
|
workingZone.unavailableBoxes //
|
|
);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// Cleanup Signal if UnApproved ...
|
|
if (!hasSignalBox)
|
|
{
|
|
//
|
|
signalBox.Clean();
|
|
signalBoxDir = X_DIRECTION_NONE;
|
|
}
|
|
|
|
//
|
|
// Check Priority Exists ...
|
|
// hasPriority =
|
|
// hasWorkingZone &&
|
|
// workingZone
|
|
// .DetectPriority(
|
|
// priority,
|
|
// signalBoxDir,
|
|
// zIndex,
|
|
// period //
|
|
// );
|
|
|
|
//
|
|
// Check Price Reward and Amount Exists ...
|
|
// hasRewardAmount =
|
|
// hasWorkingZone &&
|
|
// CalculateRiskReward(
|
|
// priority,
|
|
// reward,
|
|
// amountPercent //
|
|
// );
|
|
|
|
//
|
|
// Check Signal Conditions ...
|
|
if (hasSignalBox)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
bool has = false;
|
|
|
|
//
|
|
// Draw Signal Box ...
|
|
XCBaseObject *objects[];
|
|
has = drawer.DrawSignalBox(
|
|
signalBox,
|
|
objects,
|
|
clrYellow //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
int count = ArraySize(objects);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
workingZone.mDrawnObjects.Add(objects[i]);
|
|
}
|
|
}
|
|
Clean(objects);
|
|
|
|
//
|
|
// Detect Signal Direction ...
|
|
isBullish = IsBullish(signalBoxDir);
|
|
isBearish = IsBearish(signalBoxDir);
|
|
|
|
//
|
|
pivot = isBullish
|
|
? signalBox.ob.lower
|
|
: signalBox.ob.upper;
|
|
|
|
//
|
|
point = isBullish
|
|
? signalBox.ob.upper
|
|
: signalBox.ob.lower;
|
|
|
|
//
|
|
double entry = GetEntry(
|
|
signalBox.ob.symbol,
|
|
signalBoxDir //
|
|
);
|
|
|
|
//
|
|
// Selecting SL(s) ...
|
|
|
|
//
|
|
double iSL = 0;
|
|
|
|
//
|
|
// Default SL ...
|
|
iSL = pivot;
|
|
|
|
//
|
|
Add(
|
|
iSL,
|
|
sls //
|
|
);
|
|
|
|
//
|
|
// Select and Provide SL Based on ...
|
|
double iAtr = 0;
|
|
if (signalProvider == X121SMC_XWZ_PROVIDER_XREJECTED_BOX)
|
|
{
|
|
//
|
|
iAtr = provider.decisionXConditions.atrBuffer[1];
|
|
iAtr *= 0;
|
|
|
|
//
|
|
// Fix rf Reward ...
|
|
rfRewar = 2;
|
|
}
|
|
else if (signalProvider == X121SMC_XWZ_PROVIDER_XACTIVATED_BOX)
|
|
{
|
|
//
|
|
iAtr = provider.decisionXConditions.atrBuffer[1];
|
|
iAtr *= 0;
|
|
|
|
//
|
|
Clean(sls);
|
|
|
|
//
|
|
// Prepare Box fro SL ...
|
|
iSL = signalBox.sl;
|
|
Add(
|
|
iSL,
|
|
sls //
|
|
);
|
|
|
|
//
|
|
// Fix rf Reward ...
|
|
rfRewar = 1.5;
|
|
}
|
|
else if (signalProvider == X121SMC_XWZ_PROVIDER_XBREAKED_BOX)
|
|
{
|
|
//
|
|
iAtr = provider.decisionXConditions.atrBuffer[1];
|
|
iAtr *= 0;
|
|
|
|
//
|
|
Clean(sls);
|
|
|
|
//
|
|
// Prepare Box fro SL ...
|
|
iSL = pivot;
|
|
Add(
|
|
iSL,
|
|
sls //
|
|
);
|
|
|
|
//
|
|
// Fix rf Reward ...
|
|
rfRewar = 2;
|
|
}
|
|
else if (signalProvider == X121SMC_XWZ_PROVIDER_XWPVZ)
|
|
{
|
|
//
|
|
iAtr = provider.decisionXConditions.atrBuffer[1];
|
|
iAtr *= 0;
|
|
|
|
//
|
|
Clean(sls);
|
|
|
|
//
|
|
// Prepare Box fro SL ...
|
|
double risk = pivot - point;
|
|
|
|
iSL = risk <= (100 * points)
|
|
? pivot
|
|
: isBullish
|
|
? NormalizePrice(entry - (50 * points), signalBox.ob.symbol)
|
|
: NormalizePrice(entry + (50 * points), signalBox.ob.symbol);
|
|
Add(
|
|
iSL,
|
|
sls //
|
|
);
|
|
|
|
//
|
|
// Fix rf Reward ...
|
|
rfRewar = 2;
|
|
}
|
|
|
|
//
|
|
Print("XWZ ", ToString(signalBoxDir), " Signal Provided: ", EnumToString(signalProvider));
|
|
|
|
//
|
|
// Select Condition SL ...
|
|
|
|
//
|
|
double slsMin = GetMin(sls);
|
|
double slsMax = GetMax(sls);
|
|
|
|
//
|
|
double longSL = slsMax - iAtr;
|
|
double shortSL = slsMin + iAtr;
|
|
|
|
//
|
|
sl = isBullish
|
|
? longSL
|
|
: shortSL;
|
|
|
|
//
|
|
// Targets ...
|
|
|
|
//
|
|
double risk = MathAbs(entry - sl);
|
|
|
|
//
|
|
// Checking risk ...
|
|
if (risk >= maxAllowedSLDistanceInPoint)
|
|
{
|
|
// Print("Max Aloowed Risk Point Reached ...");
|
|
}
|
|
|
|
//
|
|
// Reward to RF ...
|
|
if (rfRewar > 0)
|
|
{
|
|
//
|
|
double iRFReward = rfRewar * risk;
|
|
double iRFTP = isBullish
|
|
? entry + iRFReward
|
|
: entry - iRFReward;
|
|
|
|
//
|
|
Add(
|
|
iRFTP,
|
|
targets //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Based On Priority ...
|
|
useQuarterTargets = false;
|
|
if (useQuarterTargets)
|
|
{
|
|
//
|
|
has = priority != X121SMC_XWZ_PRIORITY_NONE;
|
|
if (has)
|
|
{
|
|
//
|
|
switch (priority)
|
|
{
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_WEAK:
|
|
//
|
|
if (isBullish)
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.supplyZone.Lower(),
|
|
targets //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.demandZone.Upper(),
|
|
targets //
|
|
);
|
|
}
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_LOW:
|
|
//
|
|
if (isBullish)
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.supplyZone.Lower(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter1,
|
|
targets //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.demandZone.Upper(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter3,
|
|
targets //
|
|
);
|
|
}
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_REGULAR:
|
|
//
|
|
if (isBullish)
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.supplyZone.Lower(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter1,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Regular ...
|
|
Add(
|
|
workingZone.quarter2,
|
|
targets //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.demandZone.Upper(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter3,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Regular ...
|
|
Add(
|
|
workingZone.quarter2,
|
|
targets //
|
|
);
|
|
}
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_HIGH:
|
|
//
|
|
if (isBullish)
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.supplyZone.Lower(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter1,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Regular ...
|
|
Add(
|
|
workingZone.quarter2,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// High ...
|
|
Add(
|
|
workingZone.quarter3,
|
|
targets //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.demandZone.Upper(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter3,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Regular ...
|
|
Add(
|
|
workingZone.quarter2,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// High ...
|
|
Add(
|
|
workingZone.quarter1,
|
|
targets //
|
|
);
|
|
}
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_STRONG_HIGH:
|
|
//
|
|
if (isBullish)
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.supplyZone.Lower(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter1,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Regular ...
|
|
Add(
|
|
workingZone.quarter2,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// High ...
|
|
Add(
|
|
workingZone.quarter3,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Strong High ...
|
|
Add(
|
|
workingZone.demandZone.Upper(),
|
|
targets //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Weak ...
|
|
Add(
|
|
workingZone.demandZone.Upper(),
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Low ...
|
|
Add(
|
|
workingZone.quarter3,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Regular ...
|
|
Add(
|
|
workingZone.quarter2,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// High ...
|
|
Add(
|
|
workingZone.quarter1,
|
|
targets //
|
|
);
|
|
|
|
//
|
|
// Strong High ...
|
|
Add(
|
|
workingZone.supplyZone.Lower(),
|
|
targets //
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Summarize Results ...
|
|
// result = hasPriority &&
|
|
// hasSignalBox &&
|
|
// hasRewardAmount;
|
|
result = hasSignalBox;
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
//
|
|
Copy(
|
|
sls,
|
|
mConditions.sls //
|
|
);
|
|
Copy(
|
|
targets,
|
|
mConditions.targets //
|
|
);
|
|
|
|
//
|
|
mConditions.sl = sl;
|
|
mConditions.pivot = pivot;
|
|
mConditions.point = point;
|
|
mConditions.reward = reward;
|
|
mConditions.setupTime = cTime;
|
|
mConditions.dir = signalBoxDir;
|
|
mConditions.priority = priority;
|
|
mConditions.signalBox = signalBox.ob;
|
|
mConditions.provider = signalProvider;
|
|
mConditions.amountPercent = amountPercent;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
|
|
//
|
|
Clean(sls);
|
|
Clean(targets);
|
|
|
|
//
|
|
provider.Clean();
|
|
|
|
//
|
|
signalBox.Clean();
|
|
consolidationZBar.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Detect Trigger Conditions ...
|
|
bool DetectSignalTrigger(
|
|
//
|
|
X121SMCStrategyXWZSignalConditions &conditions,
|
|
//
|
|
X121SMCStrategySignalProviderData &provider,
|
|
//
|
|
int maxAllowedSetupAge = 60
|
|
//
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
provider.IsValid()
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = conditions.IsSetuped();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
datetime setupTime = conditions.setupTime;
|
|
ENUM_X_DIRECTION setupDir = conditions.dir;
|
|
|
|
//
|
|
bool isBullish = IsBullish(setupDir);
|
|
bool isBearish = IsBearish(setupDir);
|
|
|
|
//
|
|
int zIndex = 0;
|
|
int cIndex = zIndex + 1;
|
|
int pIndex = cIndex + 1;
|
|
int ppIndex = pIndex + 1;
|
|
|
|
//
|
|
int loopback = 10;
|
|
|
|
//
|
|
double ask = GetAsk(symbol);
|
|
double bid = GetBid(symbol);
|
|
datetime cTime = TimeCurrent();
|
|
double points = GetPoints(symbol);
|
|
|
|
//
|
|
// Cycles Conditions ...
|
|
result = provider.Prepare(
|
|
zIndex,
|
|
loopback //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double iAtr = provider.decisionXConditions.atrBuffer[1];
|
|
iAtr *= 1.5;
|
|
|
|
//
|
|
// Check Signal Validation ...
|
|
// IMPORTANT: since all Validations done in SetUp Process ...
|
|
// here we only Execute Signal ...
|
|
|
|
//
|
|
result = isBullish ||
|
|
isBearish;
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
//
|
|
conditions.triggerTime = cTime;
|
|
conditions.type = isBullish
|
|
? X_POSITION_TYPE_LONG
|
|
: X_POSITION_TYPE_SHORT;
|
|
|
|
//
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
|
|
//
|
|
provider.Clean();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Detect Guards ...
|
|
bool DetectGuards(
|
|
//
|
|
X121SMCGuard &guards[],
|
|
const XPosition &positions[],
|
|
//
|
|
X121SMCStrategySignalProviderData &provider
|
|
//
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!useGuards)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
Clean(guards);
|
|
|
|
//
|
|
// Set Provider for Common Guards ...
|
|
string signalProvider = ToString(X_121_SMC_PROVIDER_XWZ);
|
|
|
|
//
|
|
int zIndex = 0;
|
|
int cIndex = zIndex + 1;
|
|
int pIndex = cIndex + 1;
|
|
int ppIndex = pIndex + 1;
|
|
|
|
//
|
|
XOHCL cBar;
|
|
|
|
//
|
|
int loopback = 10;
|
|
|
|
//
|
|
double ask = GetAsk(symbol);
|
|
double bid = GetBid(symbol);
|
|
datetime cTime = TimeCurrent();
|
|
double points = GetPoints(symbol);
|
|
|
|
//
|
|
// Cycles Conditions ...
|
|
result = provider.IsValid();
|
|
result =
|
|
result &&
|
|
provider.Prepare(
|
|
zIndex,
|
|
loopback //
|
|
);
|
|
if (!result)
|
|
{
|
|
//
|
|
provider.Clean();
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Select Own Positions ...
|
|
int count = ArraySize(positions);
|
|
result = IsValidSize(count);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition ownPositions[];
|
|
XPosition ownLongPositions[];
|
|
XPosition ownShortPositions[];
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
bool isOwn = iPosition
|
|
.IsFiltersPassed(
|
|
symbol,
|
|
signalProvider,
|
|
NULL,
|
|
X_POSITION_TYPE_ALL,
|
|
iPosition.magic //
|
|
);
|
|
if (isOwn)
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownPositions //
|
|
);
|
|
|
|
//
|
|
bool isLong = IsLong(positions[i].type);
|
|
if (isLong)
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownLongPositions //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownShortPositions //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Extracted Positions ...
|
|
|
|
//
|
|
count = ArraySize(ownPositions);
|
|
|
|
//
|
|
int longsCount = ArraySize(ownLongPositions);
|
|
bool hasLongs = IsValidSize(longsCount);
|
|
|
|
//
|
|
int shortsCount = ArraySize(ownShortPositions);
|
|
bool hasShorts = IsValidSize(shortsCount);
|
|
|
|
//
|
|
// Validate Own Positions ...
|
|
result = IsValidSize(count);
|
|
if (!result)
|
|
{
|
|
//
|
|
Clean(ownPositions);
|
|
Clean(ownLongPositions);
|
|
Clean(ownShortPositions);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// Get Global and Common Guards ...
|
|
|
|
//
|
|
// ENUM_X_121_SMC_GUARD_ACTIONS ignoreGuards[1] = {
|
|
// X_121_SMC_GUARD_ACTION_TRAIL_STOP //
|
|
// };
|
|
// result = DetectX121SMCGuards(
|
|
// signalProvider,
|
|
// guards,
|
|
// positions,
|
|
// provider,
|
|
// //
|
|
// ignoreGuards
|
|
// //
|
|
// );
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
// Implement Custom Implemented Guards ...
|
|
|
|
//
|
|
// Here we Are Sure there is Positions to Guard ...
|
|
// based on Current Provider ...
|
|
|
|
//
|
|
// Selected Consolidation Zones ...
|
|
|
|
//
|
|
int idx = -1;
|
|
bool has = false;
|
|
|
|
//
|
|
bool hasBullishConsolidationZone = false;
|
|
XConsolidationZone bullishConsolidationZone;
|
|
has = provider.decisionState.HasBullishConsolidationZones();
|
|
if (has)
|
|
{
|
|
//
|
|
idx = GetYoungest(provider.decisionState.bullishConsolidationZones);
|
|
hasBullishConsolidationZone = IsValidIndex(idx);
|
|
if (hasBullishConsolidationZone)
|
|
{
|
|
bullishConsolidationZone = provider.decisionState.bullishConsolidationZones[idx];
|
|
}
|
|
}
|
|
|
|
//
|
|
bool hasBearishConsolidationZone = false;
|
|
XConsolidationZone bearishConsolidationZone;
|
|
has = provider.decisionState.HasBearishConsolidationZones();
|
|
if (has)
|
|
{
|
|
//
|
|
idx = GetYoungest(provider.decisionState.bearishConsolidationZones);
|
|
hasBearishConsolidationZone = IsValidIndex(idx);
|
|
if (hasBearishConsolidationZone)
|
|
{
|
|
bearishConsolidationZone = provider.decisionState.bearishConsolidationZones[idx];
|
|
}
|
|
}
|
|
|
|
//
|
|
XBoxZone bullishBox;
|
|
bool hasBullishBox = false;
|
|
bool isBullishBoxBreaked = false;
|
|
if (!hasBullishBox)
|
|
{
|
|
//
|
|
XCOrderBlock *ob = NULL;
|
|
idx = GetYoungest(provider.analyseState.bullishOrderBlocks);
|
|
has = IsValidIndex(idx);
|
|
if (has)
|
|
{
|
|
//
|
|
ob = provider.analyseState.bullishOrderBlocks[idx];
|
|
|
|
//
|
|
hasBullishBox = ToBox(
|
|
bullishBox,
|
|
ob //
|
|
);
|
|
}
|
|
|
|
//
|
|
if (!hasBullishBox)
|
|
{
|
|
bullishBox.Clean();
|
|
}
|
|
|
|
//
|
|
ZeroMemory(ob);
|
|
}
|
|
|
|
//
|
|
XBoxZone bearishBox;
|
|
bool hasBearishBox = false;
|
|
bool isBearishBoxBreaked = false;
|
|
if (!hasBearishBox)
|
|
{
|
|
//
|
|
XCOrderBlock *ob = NULL;
|
|
idx = GetYoungest(provider.analyseState.bearishOrderBlocks);
|
|
has = IsValidIndex(idx);
|
|
if (has)
|
|
{
|
|
//
|
|
ob = provider.analyseState.bearishOrderBlocks[idx];
|
|
|
|
//
|
|
hasBearishBox = ToBox(
|
|
bearishBox,
|
|
ob //
|
|
);
|
|
}
|
|
|
|
//
|
|
if (!hasBearishBox)
|
|
{
|
|
bearishBox.Clean();
|
|
}
|
|
|
|
//
|
|
ZeroMemory(ob);
|
|
}
|
|
|
|
//
|
|
// Here We Have all Requirements for Guarding Positions ...
|
|
|
|
//
|
|
// Available Guards:
|
|
// X_121_SMC_GUARD_ACTION_NONE
|
|
// X_121_SMC_GUARD_ACTION_CLOSE
|
|
// X_121_SMC_GUARD_ACTION_CLOSE_ALL
|
|
// X_121_SMC_GUARD_ACTION_CLOSE_LONGS
|
|
// X_121_SMC_GUARD_ACTION_CLOSE_SHORTS
|
|
// X_121_SMC_GUARD_ACTION_PARTIAL_CLOSE
|
|
// X_121_SMC_GUARD_ACTION_TRAIL_STOP
|
|
// X_121_SMC_GUARD_ACTION_TRAIL_TARGET
|
|
// X_121_SMC_GUARD_ACTION_HEDGE
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_CLOSE:
|
|
// Close Specified Position on Specified Conditions ...
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_CLOSE_ALL:
|
|
// Close All Positions on Specified Conditions ...
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_CLOSE_LONGS:
|
|
// Close All Long Positions on Specified Conditions ...
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_CLOSE_SHORTS:
|
|
// Close All Short Positions on Specified Conditions ...
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_PARTIAL_CLOSE:
|
|
// Partial Close Specified Position on Specified Conditions ...
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_TRAIL_STOP:
|
|
// Trail Stop Specified Position on Specified Conditions ...
|
|
bool handleStoptrailingBasedOnBoxes = true;
|
|
bool handleStoptrailingBasedOnConsolidationZones = true;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = ownPositions[i];
|
|
|
|
//
|
|
bool isInited = cBar.Init(
|
|
iPosition.symbol,
|
|
iPosition.period,
|
|
cIndex //
|
|
);
|
|
|
|
//
|
|
isBullishBoxBreaked =
|
|
isInited &&
|
|
hasBullishBox &&
|
|
cBar.high < bullishBox.lower;
|
|
|
|
//
|
|
isBearishBoxBreaked =
|
|
isInited &&
|
|
hasBearishBox &&
|
|
cBar.low > bearishBox.upper;
|
|
|
|
//
|
|
bool isLong = IsLong(iPosition.type);
|
|
bool isInProfit = iPosition.profit > 0;
|
|
|
|
//
|
|
double sl = iPosition.sl;
|
|
double tp = iPosition.tp;
|
|
|
|
//
|
|
// Trail Stops Using Consolidation Zones ...
|
|
|
|
//
|
|
bool canTraiLonglUsingCZone =
|
|
//
|
|
handleStoptrailingBasedOnConsolidationZones &&
|
|
//
|
|
(isLong &&
|
|
isInProfit &&
|
|
hasBullishConsolidationZone &&
|
|
bullishConsolidationZone.lower > iPosition.sl &&
|
|
bullishConsolidationZone.lower > iPosition.entry)
|
|
//
|
|
;
|
|
if (canTraiLonglUsingCZone)
|
|
{
|
|
sl = bullishConsolidationZone.lower;
|
|
}
|
|
|
|
//
|
|
bool canTraiShortlUsingCZone =
|
|
//
|
|
handleStoptrailingBasedOnConsolidationZones &&
|
|
//
|
|
(!isLong &&
|
|
isInProfit &&
|
|
hasBearishConsolidationZone &&
|
|
bearishConsolidationZone.upper < iPosition.sl &&
|
|
bearishConsolidationZone.upper < iPosition.entry)
|
|
//
|
|
;
|
|
if (canTraiShortlUsingCZone)
|
|
{
|
|
sl = bearishConsolidationZone.upper;
|
|
}
|
|
|
|
//
|
|
bool canTrailUsingCZone =
|
|
canTraiLonglUsingCZone ||
|
|
canTraiShortlUsingCZone;
|
|
|
|
//
|
|
// Trail Stop Using Boxes ...
|
|
|
|
//
|
|
bool canTraiLonglUsingBox =
|
|
//
|
|
handleStoptrailingBasedOnBoxes &&
|
|
//
|
|
(isLong &&
|
|
isInProfit &&
|
|
hasBullishBox &&
|
|
bullishBox.lower > iPosition.sl &&
|
|
bullishBox.lower > iPosition.entry)
|
|
//
|
|
;
|
|
if (canTraiLonglUsingBox)
|
|
{
|
|
sl = bullishBox.lower;
|
|
}
|
|
|
|
//
|
|
bool canTraiShortlUsingBox =
|
|
//
|
|
handleStoptrailingBasedOnBoxes &&
|
|
//
|
|
(!isLong &&
|
|
isInProfit &&
|
|
hasBearishBox &&
|
|
bearishBox.upper < iPosition.sl &&
|
|
bearishBox.upper < iPosition.entry)
|
|
//
|
|
;
|
|
if (canTraiShortlUsingBox)
|
|
{
|
|
sl = bearishBox.upper;
|
|
}
|
|
|
|
//
|
|
bool canTrailUsingBox =
|
|
canTraiLonglUsingBox ||
|
|
canTraiShortlUsingBox;
|
|
|
|
//
|
|
bool canTraiLonglUsingBreakedBox =
|
|
//
|
|
handleStoptrailingBasedOnBoxes &&
|
|
//
|
|
(isLong &&
|
|
isInProfit &&
|
|
isBearishBoxBreaked &&
|
|
bearishBox.upper > iPosition.sl &&
|
|
bearishBox.upper > iPosition.entry)
|
|
//
|
|
;
|
|
if (canTraiLonglUsingBreakedBox)
|
|
{
|
|
sl = bearishBox.upper;
|
|
}
|
|
|
|
//
|
|
bool canTraiShortlUsingBreakedBox =
|
|
//
|
|
handleStoptrailingBasedOnBoxes &&
|
|
//
|
|
(!isLong &&
|
|
isInProfit &&
|
|
isBullishBoxBreaked &&
|
|
bullishBox.lower < iPosition.sl &&
|
|
bullishBox.lower < iPosition.entry)
|
|
//
|
|
;
|
|
if (canTraiShortlUsingBreakedBox)
|
|
{
|
|
sl = bullishBox.lower;
|
|
}
|
|
|
|
//
|
|
bool canTrailUsingBreakedBox =
|
|
canTraiLonglUsingBreakedBox ||
|
|
canTraiShortlUsingBreakedBox;
|
|
|
|
//
|
|
// Summarize SL ...
|
|
|
|
//
|
|
bool isSLPassed =
|
|
sl > 0 &&
|
|
(isLong
|
|
? iPosition.price - sl >= points * 15
|
|
: sl - iPosition.price >= points * 15);
|
|
|
|
//
|
|
bool canTrail =
|
|
isSLPassed &&
|
|
(canTrailUsingBox ||
|
|
canTrailUsingCZone ||
|
|
canTrailUsingBreakedBox);
|
|
if (canTrail)
|
|
{
|
|
//
|
|
X121SMCGuard iGuard;
|
|
|
|
//
|
|
iGuard.time = cTime;
|
|
iGuard.symbol = symbol;
|
|
iGuard.provider = signalProvider;
|
|
iGuard.action = X_121_SMC_GUARD_ACTION_TRAIL_STOP;
|
|
|
|
//
|
|
iGuard.ticket = iPosition.ticket;
|
|
|
|
//
|
|
iGuard.sl = sl;
|
|
|
|
//
|
|
AddGuard(
|
|
iGuard,
|
|
guards //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_TRAIL_TARGET:
|
|
// Trail Target Specified Position on Specified Conditions ...
|
|
|
|
//
|
|
// X_121_SMC_GUARD_ACTION_HEDGE:
|
|
// Hedge all Positions on Specified Conditions ...
|
|
|
|
//
|
|
// Cleanup Collections ...
|
|
|
|
//
|
|
cBar.Clean();
|
|
|
|
//
|
|
provider.Clean();
|
|
|
|
//
|
|
bullishBox.Clean();
|
|
bearishBox.Clean();
|
|
|
|
//
|
|
bullishConsolidationZone.Clean();
|
|
bearishConsolidationZone.Clean();
|
|
|
|
//
|
|
count = ArraySize(guards);
|
|
result = IsValidSize(count);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add Conditions to Conditions Collection ...
|
|
int AddConditionsIfNotExists(
|
|
int maxAllowed = 10 //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
bool isSetuped = IsValid(mConditions.setupTime) &&
|
|
HasDirection(mConditions.dir);
|
|
if (!isSetuped)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(mConditionsCollection);
|
|
if (IsValidSize(count))
|
|
{
|
|
//
|
|
// Copy Original Items ...
|
|
X121SMCStrategyXWZSignalConditions tmpItems[];
|
|
Copy(
|
|
mConditionsCollection,
|
|
tmpItems //
|
|
);
|
|
|
|
//
|
|
// Prese Item Data ...
|
|
bool isBullish = IsBullish(mConditions.dir);
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
X121SMCStrategyXWZSignalConditions iItem = tmpItems[i];
|
|
|
|
//
|
|
bool canRemove = mConditions.dir == iItem.dir &&
|
|
mConditions.pivot == iItem.pivot;
|
|
if (canRemove)
|
|
{
|
|
//
|
|
ArrayRemove(
|
|
mConditionsCollection,
|
|
i,
|
|
1 //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
Clean(tmpItems);
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
mConditions,
|
|
mConditionsCollection //
|
|
);
|
|
|
|
//
|
|
// Remove Olds ...
|
|
CleanupArray(
|
|
mConditionsCollection,
|
|
maxAllowed //
|
|
);
|
|
|
|
//
|
|
result = ArraySize(mConditionsCollection);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Draw Functions ...
|
|
//
|
|
|
|
//
|
|
void DrawBar(
|
|
XOHCL &bar,
|
|
XCPOIDrawer *drawer,
|
|
bool forceClean = true //
|
|
)
|
|
{
|
|
//
|
|
if (forceClean)
|
|
{
|
|
mBarObjects.Clear();
|
|
}
|
|
|
|
//
|
|
if (drawer == NULL ||
|
|
!bar.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool isBullish = bar.IsBullish();
|
|
|
|
//
|
|
int widthBar = 2;
|
|
bool fillShadowsBar = true;
|
|
ENUM_LINE_STYLE styleBar = STYLE_SOLID;
|
|
color clrBar =
|
|
isBullish
|
|
? clrAqua
|
|
: clrMagenta;
|
|
|
|
//
|
|
datetime to = bar.NextAt();
|
|
|
|
//
|
|
XCOHCLObject *iObj;
|
|
bool isCreated = drawer
|
|
.CreateBar(
|
|
bar,
|
|
iObj,
|
|
to //
|
|
);
|
|
if (isCreated)
|
|
{
|
|
//
|
|
iObj.BarSpes(
|
|
widthBar,
|
|
clrBar,
|
|
styleBar //
|
|
);
|
|
|
|
//
|
|
iObj.LowShadowFill(fillShadowsBar);
|
|
iObj.HighShadowFill(fillShadowsBar);
|
|
|
|
//
|
|
mBarObjects.Add(iObj);
|
|
}
|
|
}
|
|
|
|
//
|
|
protected:
|
|
//
|
|
|
|
//
|
|
private:
|
|
//
|
|
// Props ...
|
|
string symbol;
|
|
ENUM_TIMEFRAMES period;
|
|
CArrayObj mObjects;
|
|
CArrayObj mBarObjects;
|
|
CArrayObj mWorkingZoneObjects;
|
|
|
|
/**
|
|
* Initialize ...
|
|
*/
|
|
void Init()
|
|
{
|
|
//
|
|
rfRewar = 2;
|
|
useGuards = false;
|
|
useQuarterTargets = false;
|
|
}
|
|
|
|
/**
|
|
* DeInitialize ...
|
|
*/
|
|
void DeInit()
|
|
{
|
|
//
|
|
mConditions.Clean();
|
|
workingZone.Clean();
|
|
Clean(mConditionsCollection);
|
|
|
|
//
|
|
mBarObjects.Clear();
|
|
}
|
|
|
|
/**
|
|
* Update To Time of Exists Components ...
|
|
*
|
|
* @param value: updated time ...
|
|
*/
|
|
void UpdateTo(
|
|
datetime value = NULL //
|
|
)
|
|
{
|
|
//
|
|
value = NormalizeTime(value);
|
|
|
|
//
|
|
workingZone.UpdateTo(value);
|
|
}
|
|
|
|
//
|
|
// Helpers ...
|
|
|
|
//
|
|
bool CalculateRiskReward(
|
|
ENUM_X121SMC_XWZ_PRIORITY priority,
|
|
double &reward,
|
|
double &amountPercent //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
reward = 0;
|
|
amountPercent = 0;
|
|
|
|
//
|
|
result = priority != X121SMC_XWZ_PRIORITY_NONE;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
switch (priority)
|
|
{
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_STRONG_HIGH:
|
|
//
|
|
reward = 6;
|
|
amountPercent = 2;
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_HIGH:
|
|
//
|
|
reward = 4;
|
|
amountPercent = 2;
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_REGULAR:
|
|
//
|
|
reward = 3;
|
|
amountPercent = 1.5;
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_LOW:
|
|
//
|
|
reward = 2;
|
|
amountPercent = 1;
|
|
break;
|
|
|
|
//
|
|
case X121SMC_XWZ_PRIORITY_WEAK:
|
|
//
|
|
reward = 1;
|
|
amountPercent = 0.5;
|
|
break;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
reward > 0 &&
|
|
amountPercent > 0
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
// |