last ...
This commit is contained in:
@@ -0,0 +1,418 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| SMC_OrderBlock_EA.mq5|
|
||||
//| Copyright 2023, MetaQuotes Software Corp. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2023, MetaQuotes Software Corp."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade/AccountInfo.mqh>
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
//
|
||||
// Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M15; // Timeframe for analysis
|
||||
input double RiskPercent = 1.0; // Risk per trade (1-2%)
|
||||
input int ATR_Period = 14; // ATR period for volatility
|
||||
input int OrderBlockLength = 20; // Bars for consolidation
|
||||
input int ConfirmationCandles = 3; // Candles to confirm breakout
|
||||
input double StopLossMultiplier = 1.5; // Stop loss buffer
|
||||
input double TakeProfitRatio = 2.0; // Risk:Reward ratio
|
||||
input int TrailingStopPips = 50; // Trailing stop in pips
|
||||
|
||||
//
|
||||
CTrade trade;
|
||||
CAccountInfo accountInfo;
|
||||
|
||||
//
|
||||
double Ask = 0;
|
||||
double Bid = 0;
|
||||
|
||||
//
|
||||
// Order Block structure
|
||||
struct OrderBlock
|
||||
{
|
||||
datetime startTime;
|
||||
double high;
|
||||
double low;
|
||||
};
|
||||
|
||||
//
|
||||
int atrHandler = INVALID_HANDLE;
|
||||
int maFastHandler = INVALID_HANDLE;
|
||||
int maSlowHandler = INVALID_HANDLE;
|
||||
|
||||
//
|
||||
double atrs[];
|
||||
double fastMAs[];
|
||||
double slowMAs[];
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//
|
||||
trade.SetExpertMagicNumber(123456); // Unique magic number
|
||||
|
||||
//
|
||||
atrHandler = iATR(_Symbol, TimeFrame, ATR_Period);
|
||||
maFastHandler = iMA(_Symbol, TimeFrame, 20, 0, MODE_SMA, PRICE_CLOSE);
|
||||
maSlowHandler = iMA(_Symbol, TimeFrame, 50, 0, MODE_SMA, PRICE_CLOSE);
|
||||
|
||||
//
|
||||
bool isValid =
|
||||
atrHandler != INVALID_HANDLE &&
|
||||
maFastHandler != INVALID_HANDLE &&
|
||||
maSlowHandler != INVALID_HANDLE;
|
||||
if (!isValid)
|
||||
{
|
||||
return (INIT_FAILED);
|
||||
}
|
||||
|
||||
//
|
||||
return (INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//
|
||||
// Cleanup code if needed ...
|
||||
IndicatorRelease(atrHandler);
|
||||
IndicatorRelease(maFastHandler);
|
||||
IndicatorRelease(maSlowHandler);
|
||||
|
||||
//
|
||||
ZeroMemory(atrs);
|
||||
ZeroMemory(fastMAs);
|
||||
ZeroMemory(slowMAs);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Avoid multiple orders
|
||||
if (CheckOpenOrders())
|
||||
return;
|
||||
|
||||
//
|
||||
CopyBuffer(atrHandler, MAIN_LINE, 0, 200, atrs);
|
||||
|
||||
//
|
||||
CopyBuffer(maFastHandler, MAIN_LINE, 0, 2, fastMAs);
|
||||
CopyBuffer(maSlowHandler, MAIN_LINE, 0, 2, slowMAs);
|
||||
|
||||
//
|
||||
Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits); // Get the Ask Price
|
||||
Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits); // Get the Bid Price
|
||||
|
||||
//
|
||||
// Check for trend and execute trades
|
||||
if (IsUptrend())
|
||||
{
|
||||
OrderBlock block = FindBullishOrderBlock();
|
||||
if (block.startTime != 0 && IsPriceInBlock(block))
|
||||
{
|
||||
if (ConfirmBullishEntry())
|
||||
ExecuteTrade(block, ORDER_TYPE_BUY);
|
||||
}
|
||||
}
|
||||
else if (IsDowntrend())
|
||||
{
|
||||
OrderBlock block = FindBearishOrderBlock();
|
||||
if (block.startTime != 0 && IsPriceInBlock(block))
|
||||
{
|
||||
if (ConfirmBearishEntry())
|
||||
ExecuteTrade(block, ORDER_TYPE_SELL);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage trailing stops
|
||||
TrailStop();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if price is within the order block |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsPriceInBlock(OrderBlock &block)
|
||||
{
|
||||
double currentPrice = (Ask + Bid) / 2; // Mid-price
|
||||
return (currentPrice >= block.low && currentPrice <= block.high);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detect uptrend using moving averages |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsUptrend()
|
||||
{
|
||||
//
|
||||
double maFast = fastMAs[0];
|
||||
double maSlow = slowMAs[0];
|
||||
|
||||
//
|
||||
return (maFast > maSlow);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detect downtrend using moving averages |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsDowntrend()
|
||||
{
|
||||
//
|
||||
double maFast = fastMAs[0];
|
||||
double maSlow = slowMAs[0];
|
||||
|
||||
//
|
||||
return (maFast < maSlow);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Find bullish order blocks |
|
||||
//+------------------------------------------------------------------+
|
||||
OrderBlock FindBullishOrderBlock()
|
||||
{
|
||||
//
|
||||
OrderBlock block;
|
||||
block.startTime = 0;
|
||||
|
||||
//
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
double prevHigh = iHigh(NULL, TimeFrame, i);
|
||||
double prevLow = iLow(NULL, TimeFrame, i);
|
||||
double prevClose = iClose(NULL, TimeFrame, i);
|
||||
double prevOpen = iOpen(NULL, TimeFrame, i);
|
||||
|
||||
double atr = atrs[i];
|
||||
if (prevClose - prevOpen > 1.5 * atr)
|
||||
{ // Strong bullish candle
|
||||
double consolidationHigh = prevHigh;
|
||||
double consolidationLow = prevLow;
|
||||
bool isConsolidation = true;
|
||||
|
||||
for (int j = 1; j <= OrderBlockLength; j++)
|
||||
{
|
||||
double newHigh = iHigh(NULL, TimeFrame, i - j);
|
||||
double newLow = iLow(NULL, TimeFrame, i - j);
|
||||
consolidationHigh = MathMax(consolidationHigh, newHigh);
|
||||
consolidationLow = MathMin(consolidationLow, newLow);
|
||||
|
||||
if ((consolidationHigh - consolidationLow) > 0.5 * atr)
|
||||
{
|
||||
isConsolidation = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isConsolidation)
|
||||
{
|
||||
block.high = consolidationHigh;
|
||||
block.low = consolidationLow;
|
||||
block.startTime = iTime(NULL, TimeFrame, i);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Find bearish order blocks |
|
||||
//+------------------------------------------------------------------+
|
||||
OrderBlock FindBearishOrderBlock()
|
||||
{
|
||||
//
|
||||
OrderBlock block;
|
||||
block.startTime = 0;
|
||||
|
||||
//
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
double prevHigh = iHigh(NULL, TimeFrame, i);
|
||||
double prevLow = iLow(NULL, TimeFrame, i);
|
||||
double prevClose = iClose(NULL, TimeFrame, i);
|
||||
double prevOpen = iOpen(NULL, TimeFrame, i);
|
||||
|
||||
double atr = atrs[i];
|
||||
if (prevOpen - prevClose > 1.5 * atr)
|
||||
{ // Strong bearish candle
|
||||
double consolidationHigh = prevHigh;
|
||||
double consolidationLow = prevLow;
|
||||
bool isConsolidation = true;
|
||||
|
||||
for (int j = 1; j <= OrderBlockLength; j++)
|
||||
{
|
||||
double newHigh = iHigh(NULL, TimeFrame, i - j);
|
||||
double newLow = iLow(NULL, TimeFrame, i - j);
|
||||
consolidationHigh = MathMax(consolidationHigh, newHigh);
|
||||
consolidationLow = MathMin(consolidationLow, newLow);
|
||||
|
||||
if ((consolidationHigh - consolidationLow) > 0.5 * atr)
|
||||
{
|
||||
isConsolidation = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isConsolidation)
|
||||
{
|
||||
block.high = consolidationHigh;
|
||||
block.low = consolidationLow;
|
||||
block.startTime = iTime(NULL, TimeFrame, i);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Confirm bullish entry with candle pattern |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ConfirmBullishEntry()
|
||||
{
|
||||
//
|
||||
int shift = iBarShift(NULL, TimeFrame, TimeCurrent());
|
||||
double prevLow = iLow(NULL, TimeFrame, shift + 1);
|
||||
double currentClose = iClose(NULL, TimeFrame, shift);
|
||||
return (currentClose > prevLow && iOpen(NULL, TimeFrame, shift) < currentClose);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Confirm bearish entry with candle pattern |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ConfirmBearishEntry()
|
||||
{
|
||||
//
|
||||
int shift = iBarShift(NULL, TimeFrame, TimeCurrent());
|
||||
double prevHigh = iHigh(NULL, TimeFrame, shift + 1);
|
||||
double currentClose = iClose(NULL, TimeFrame, shift);
|
||||
return (currentClose < prevHigh && iOpen(NULL, TimeFrame, shift) > currentClose);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Execute trade with risk management |
|
||||
//+------------------------------------------------------------------+
|
||||
void ExecuteTrade(OrderBlock &block, ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
//
|
||||
double atr = atrs[0];
|
||||
double stopLoss, takeProfit, lotSize;
|
||||
double entryPrice = (orderType == ORDER_TYPE_BUY) ? Ask : Bid;
|
||||
|
||||
// Calculate stop loss and take profit
|
||||
if (orderType == ORDER_TYPE_BUY)
|
||||
{
|
||||
stopLoss = block.low - StopLossMultiplier * atr;
|
||||
takeProfit = entryPrice + (entryPrice - stopLoss) * TakeProfitRatio;
|
||||
}
|
||||
else
|
||||
{
|
||||
stopLoss = block.high + StopLossMultiplier * atr;
|
||||
takeProfit = entryPrice - (stopLoss - entryPrice) * TakeProfitRatio;
|
||||
}
|
||||
|
||||
// Calculate lot size based on risk
|
||||
lotSize = CalculateLotSize(stopLoss, entryPrice, orderType);
|
||||
if (lotSize <= 0)
|
||||
return;
|
||||
|
||||
// Place order
|
||||
if (orderType == ORDER_TYPE_BUY)
|
||||
{
|
||||
trade.Buy(lotSize, _Symbol, entryPrice, stopLoss, takeProfit, "SMC Bullish Order Block");
|
||||
}
|
||||
else
|
||||
{
|
||||
trade.Sell(lotSize, _Symbol, entryPrice, stopLoss, takeProfit, "SMC Bearish Order Block");
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate lot size based on risk per trade |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateLotSize(double stopLoss, double entryPrice, ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double riskAmount = AccountBalance() * RiskPercent / 100;
|
||||
double stopLossPips = MathAbs((entryPrice - stopLoss) / SymbolInfoDouble(_Symbol, SYMBOL_POINT));
|
||||
|
||||
if (stopLossPips == 0)
|
||||
return 0;
|
||||
|
||||
double pipValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||||
double lotSize = riskAmount / (stopLossPips * pipValue);
|
||||
lotSize = NormalizeDouble(lotSize, 2); // Adjust to symbol's lot precision
|
||||
|
||||
return MathMax(lotSize, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for existing open orders |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckOpenOrders()
|
||||
{
|
||||
for (int i = 0; i < PositionsTotal(); i++)
|
||||
{
|
||||
if (PositionGetSymbol(i) == _Symbol)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trailing stop logic |
|
||||
//+------------------------------------------------------------------+
|
||||
void TrailStop()
|
||||
{
|
||||
for (int i = 0; i < PositionsTotal(); i++)
|
||||
{
|
||||
if (PositionGetSymbol(i) != _Symbol)
|
||||
continue;
|
||||
|
||||
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
||||
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
double currentPrice = (type == POSITION_TYPE_BUY) ? Bid : Ask;
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double stopLoss = PositionGetDouble(POSITION_SL);
|
||||
double trailingStopPrice = (type == POSITION_TYPE_BUY)
|
||||
? currentPrice - TrailingStopPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT)
|
||||
: currentPrice + TrailingStopPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
|
||||
// Update stop loss if price moves favorably
|
||||
if ((type == POSITION_TYPE_BUY && trailingStopPrice > stopLoss) ||
|
||||
(type == POSITION_TYPE_SELL && trailingStopPrice < stopLoss))
|
||||
{
|
||||
trade.PositionModify(ticket, trailingStopPrice, PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate Account Balance |
|
||||
//+------------------------------------------------------------------+
|
||||
double AccountBalance()
|
||||
{
|
||||
return accountInfo.Balance();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1324,6 +1324,83 @@ bool HasInsideBox(
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsValidBox(
|
||||
XBoxZone &box,
|
||||
int barIndex = 0,
|
||||
ENUM_TIMEFRAMES forPeriod = NULL //
|
||||
)
|
||||
{
|
||||
//
|
||||
bool result = false;
|
||||
|
||||
//
|
||||
result = box.IsValid();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
if (barIndex < 0)
|
||||
{
|
||||
barIndex = 0;
|
||||
}
|
||||
|
||||
//
|
||||
if (!IsValid(forPeriod))
|
||||
{
|
||||
forPeriod = box.period;
|
||||
}
|
||||
|
||||
//
|
||||
int fromIndex = iBarShift(
|
||||
box.symbol,
|
||||
forPeriod,
|
||||
box.from //
|
||||
);
|
||||
result = IsValidIndex(fromIndex);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
XOHCL iBar;
|
||||
bool isBullish = box.IsBullish();
|
||||
for (int i = barIndex; i < fromIndex; i++)
|
||||
{
|
||||
//
|
||||
result = iBar.Init(
|
||||
box.symbol,
|
||||
forPeriod,
|
||||
i //
|
||||
);
|
||||
if (!result)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
result =
|
||||
isBullish
|
||||
? iBar.GetDown() > box.lower
|
||||
: iBar.GetUp() < box.upper;
|
||||
if (!result)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
iBar.Clean();
|
||||
}
|
||||
|
||||
//
|
||||
iBar.Clean();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to XBoxZone ...
|
||||
*
|
||||
@@ -2899,14 +2976,14 @@ int FilterZones(
|
||||
|
||||
/**
|
||||
* Detect Nearest Price Out of Specified Box ...
|
||||
*
|
||||
*
|
||||
* @param box: XBoxZone instance Reference ...
|
||||
* @param outSideBar: XOHCL instance Reference which Represents Out Side Bar if Exists ...
|
||||
* @param forPeriod: ENUM_TIMEFRAMES member which provides time frame of Out Bar, if not Provides used box Period as Default ...
|
||||
* @param forDir: ENUM_X_DIRECTION member which Specified outSide Direction, use Box Dir as Default ...
|
||||
* @param forDir: ENUM_X_DIRECTION member which Specified outSide Direction, use Box Dir as Default ...
|
||||
* @param forceBodyIn: bool value which Specifyed Body Breakes or not, Default is false ...
|
||||
* @param barIndex: integer value which Specified Start Bar Index, Default is 0 ...
|
||||
*
|
||||
*
|
||||
* @return ( bool )
|
||||
*/
|
||||
bool DetectBoxNearestPriceOut(
|
||||
|
||||
@@ -488,11 +488,17 @@ struct XOBFVG
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// bool isBarAbove =
|
||||
// bar.low > iFVG.Upper();
|
||||
// bool isBarBelow =
|
||||
// bar.high < iFVG.Lower();
|
||||
|
||||
//
|
||||
bool isBarAbove =
|
||||
bar.low > iFVG.Upper();
|
||||
bar.GetDown() > iFVG.Upper();
|
||||
bool isBarBelow =
|
||||
bar.high < iFVG.Lower();
|
||||
bar.GetUp() < iFVG.Lower();
|
||||
|
||||
//
|
||||
isValidFVG =
|
||||
|
||||
@@ -164,12 +164,14 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
double targets[];
|
||||
double reward = 0;
|
||||
XBoxZone signalBox;
|
||||
int activatedFVGIDX = 0;
|
||||
int activatedZoneIDX = 0;
|
||||
double amountPercent = 0;
|
||||
bool hasPriority = false;
|
||||
int activatedFVGIDX = -1;
|
||||
int activatedBoxIDX = -1;
|
||||
int activatedZoneIDX = -1;
|
||||
double amountPercent = -1;
|
||||
bool hasSignalBox = false;
|
||||
bool hasWaitingZones = false;
|
||||
bool hasWaitingBoxes = false;
|
||||
bool hasRewardAmount = false;
|
||||
bool isSignalBoxReversal = false;
|
||||
bool hasWorkingZoneOveralDirection = false;
|
||||
@@ -214,10 +216,14 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
.DetectOveralDirection(wzOveralDir);
|
||||
|
||||
//
|
||||
// Detect Zones and Boxes ...
|
||||
hasWaitingZones = workingZone.DetectZone(
|
||||
provider //
|
||||
);
|
||||
|
||||
//
|
||||
hasWaitingBoxes = workingZone.HasWaitingBoxes();
|
||||
|
||||
//
|
||||
// Checking Working Zone Destory or not ...
|
||||
bool isWorkingZoneDestroyed = workingZone.IsDestroyed(
|
||||
@@ -264,8 +270,7 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
//
|
||||
true, // Draw Supply Zone ...
|
||||
true, // Draw Demand Zone ...
|
||||
true, // Draw Waiting Zones ...
|
||||
true // Draw Waiting Zones Box Area ...
|
||||
true // Draw Waiting Zones ...
|
||||
//
|
||||
);
|
||||
}
|
||||
@@ -276,7 +281,9 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
|
||||
//
|
||||
// Detect Signal Box ...
|
||||
hasSignalBox =
|
||||
|
||||
//
|
||||
bool isZoneActivated =
|
||||
hasWorkingZone &&
|
||||
hasWaitingZones &&
|
||||
workingZone.IsZoneActivated(
|
||||
@@ -285,23 +292,38 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
provider,
|
||||
zIndex //
|
||||
);
|
||||
|
||||
//
|
||||
bool isBoxActivated =
|
||||
hasWorkingZone &&
|
||||
hasWaitingBoxes &&
|
||||
workingZone.IsBoxActivated(
|
||||
activatedBoxIDX,
|
||||
provider,
|
||||
zIndex //
|
||||
);
|
||||
|
||||
//
|
||||
hasSignalBox =
|
||||
isBoxActivated ||
|
||||
isZoneActivated;
|
||||
if (hasSignalBox)
|
||||
{
|
||||
//
|
||||
signalBox.Clean();
|
||||
|
||||
//
|
||||
XOBFVG iOBFVG = workingZone.waitingZones[activatedZoneIDX];
|
||||
XCFVG *activatedFVG = iOBFVG.fvgs[activatedFVGIDX];
|
||||
hasSignalBox = ToBox(
|
||||
signalBox,
|
||||
activatedFVG //
|
||||
);
|
||||
if (hasSignalBox)
|
||||
if (isZoneActivated)
|
||||
{
|
||||
//
|
||||
signalBox.to = cTime;
|
||||
signalBoxDir = signalBox.dir;
|
||||
XOBFVG iOBFVG = workingZone.waitingZones[activatedZoneIDX];
|
||||
XCFVG *activatedFVG = iOBFVG.fvgs[activatedFVGIDX];
|
||||
hasSignalBox = ToBox(
|
||||
signalBox,
|
||||
activatedFVG //
|
||||
);
|
||||
|
||||
//
|
||||
signalProvider = X121SMC_XWZ_PROVIDER_ACTIVATED_ZONE;
|
||||
|
||||
//
|
||||
@@ -326,13 +348,48 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
1 //
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
iOBFVG.Clean();
|
||||
ZeroMemory(activatedFVG);
|
||||
}
|
||||
|
||||
//
|
||||
iOBFVG.Clean();
|
||||
ZeroMemory(activatedFVG);
|
||||
if (isBoxActivated)
|
||||
{
|
||||
//
|
||||
signalBox = workingZone.waitingBoxes[activatedBoxIDX];
|
||||
|
||||
//
|
||||
signalProvider = X121SMC_XWZ_PROVIDER_ACTIVATED_BOX;
|
||||
|
||||
//
|
||||
AddRef(
|
||||
signalBox,
|
||||
workingZone.unavailableBoxes //
|
||||
);
|
||||
|
||||
//
|
||||
ArrayRemove(
|
||||
workingZone.waitingBoxes,
|
||||
activatedBoxIDX,
|
||||
1 //
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
if (hasSignalBox)
|
||||
{
|
||||
//
|
||||
signalBox.to = cTime;
|
||||
signalBoxDir = signalBox.dir;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
//
|
||||
// Remove Invalidate Zones ...
|
||||
if (hasWaitingZones)
|
||||
@@ -343,6 +400,16 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
hasWaitingZones = workingZone.HasWaitingZones();
|
||||
}
|
||||
|
||||
//
|
||||
// Remove InValidate Boxes ...
|
||||
if (hasWaitingBoxes)
|
||||
{
|
||||
//
|
||||
// Validate Waiting Boxes ...
|
||||
workingZone.ValidateWaitingBoxes(zIndex);
|
||||
hasWaitingBoxes = workingZone.HasWaitingBoxes();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -413,6 +480,12 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
? signalBox.upper
|
||||
: signalBox.lower;
|
||||
|
||||
//
|
||||
double entry = GetEntry(
|
||||
signalBox.symbol,
|
||||
signalBoxDir //
|
||||
);
|
||||
|
||||
//
|
||||
double iSL = 0;
|
||||
|
||||
@@ -432,6 +505,11 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
// }
|
||||
|
||||
//
|
||||
double iAtr = provider.decisionXConditions.atrBuffer[1];
|
||||
iAtr *= 1.5;
|
||||
|
||||
//
|
||||
// Default SL ...
|
||||
iSL = pivot;
|
||||
|
||||
//
|
||||
@@ -443,19 +521,13 @@ class XCX121SMCXWZSignalDetector : public XCBase
|
||||
//
|
||||
// Select Condition SL ...
|
||||
|
||||
//
|
||||
double entry = GetEntry(
|
||||
signalBox.symbol,
|
||||
signalBoxDir //
|
||||
);
|
||||
|
||||
//
|
||||
double slsMin = GetMin(sls);
|
||||
double slsMax = GetMax(sls);
|
||||
|
||||
//
|
||||
double longSL = slsMin;
|
||||
double shortSL = slsMax;
|
||||
double longSL = slsMin - iAtr;
|
||||
double shortSL = slsMax + iAtr;
|
||||
|
||||
//
|
||||
sl = isBullish
|
||||
|
||||
@@ -30,10 +30,8 @@
|
||||
enum ENUM_X121SMC_XWZ_PROVIDERS
|
||||
{
|
||||
X121SMC_XWZ_PROVIDER_NONE,
|
||||
X121SMC_XWZ_PROVIDER_PV_ZONE,
|
||||
X121SMC_XWZ_PROVIDER_ACTIVATED_BOX,
|
||||
X121SMC_XWZ_PROVIDER_ACTIVATED_ZONE,
|
||||
X121SMC_XWZ_PROVIDER_ACTIVATED_CHOCH,
|
||||
X121SMC_XWZ_PROVIDER_PRICE_INSIDE_ZONE,
|
||||
};
|
||||
|
||||
//
|
||||
@@ -73,6 +71,10 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
XOBFVG waitingZones[];
|
||||
XOBFVG unavailableZones[];
|
||||
|
||||
//
|
||||
XBoxZone waitingBoxes[];
|
||||
XBoxZone unavailableBoxes[];
|
||||
|
||||
//
|
||||
CArrayObj mDrawnObjects;
|
||||
CArrayObj mWorkingZoneObjects;
|
||||
@@ -101,6 +103,11 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
|
||||
//
|
||||
Clean(waitingZones);
|
||||
Clean(unavailableZones);
|
||||
|
||||
//
|
||||
Clean(waitingBoxes);
|
||||
Clean(unavailableBoxes);
|
||||
|
||||
//
|
||||
ZeroMemory(supplyZone);
|
||||
@@ -461,7 +468,8 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
|
||||
//
|
||||
// Detect OB FVG ...
|
||||
if (hasNewOrderBlock)
|
||||
if (false &&
|
||||
hasNewOrderBlock)
|
||||
{
|
||||
//
|
||||
XOHCL obStartedBar;
|
||||
@@ -596,6 +604,104 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
obStartedBar.Clean();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
//
|
||||
// Check New Order Block Detection Analyse ...
|
||||
XBoxZone analyseOBBox;
|
||||
XCOrderBlock *analyseOB = NULL;
|
||||
bool hasNewAnalyseOrderBlock =
|
||||
Contains(
|
||||
X_BULLISH_ORDERBLOCK_DETECTED,
|
||||
provider
|
||||
.analysePoiEvents //
|
||||
) ||
|
||||
Contains(
|
||||
X_BEARISH_ORDERBLOCK_DETECTED,
|
||||
provider
|
||||
.analysePoiEvents //
|
||||
);
|
||||
if (hasNewAnalyseOrderBlock)
|
||||
{
|
||||
//
|
||||
idx = GetYoungest(provider
|
||||
.analyseState
|
||||
.orderBlocks //
|
||||
);
|
||||
has = IsValidIndex(idx);
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
analyseOB = provider
|
||||
.analyseState
|
||||
.orderBlocks[idx];
|
||||
has = ToBox(
|
||||
analyseOBBox,
|
||||
analyseOB //
|
||||
);
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
analyseOB.To(cTime);
|
||||
analyseOBBox.to = cTime;
|
||||
hasNewAnalyseOrderBlock = analyseOBBox.IsValid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Checking Analyse Order Block ...
|
||||
if (hasNewAnalyseOrderBlock)
|
||||
{
|
||||
//
|
||||
XCBaseObject *iObj;
|
||||
has = provider
|
||||
.visionCycleHelper
|
||||
.mPOIDrawer
|
||||
.CreateOrderBlock(
|
||||
analyseOB,
|
||||
iObj //
|
||||
);
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
XOHCL obStartedBar;
|
||||
bool isOBStartedBodyOut = false;
|
||||
bool isOBStarted = analyseOB.IsStarted(
|
||||
analyseOB.GetDirection(),
|
||||
obStartedBar,
|
||||
isOBStartedBodyOut //
|
||||
);
|
||||
|
||||
//
|
||||
has = isOBStarted &&
|
||||
isOBStartedBodyOut;
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
has = IsExists(analyseOBBox);
|
||||
if (!has)
|
||||
{
|
||||
//
|
||||
AddRef(
|
||||
analyseOBBox,
|
||||
waitingBoxes //
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
analyseOBBox.Clean();
|
||||
ZeroMemory(analyseOB);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
//
|
||||
result = HasWaitingZones();
|
||||
|
||||
@@ -694,6 +800,83 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
int ValidateWaitingBoxes(
|
||||
int barIndex = 0 //
|
||||
)
|
||||
{
|
||||
//
|
||||
int result = 0;
|
||||
|
||||
//
|
||||
bool has = false;
|
||||
|
||||
//
|
||||
if (barIndex < 0)
|
||||
{
|
||||
barIndex = 0;
|
||||
}
|
||||
|
||||
//
|
||||
if (barIndex == 0)
|
||||
{
|
||||
barIndex = 1;
|
||||
}
|
||||
|
||||
//
|
||||
has = IsValid() &&
|
||||
HasWaitingBoxes();
|
||||
if (!has)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
int removeIDX[];
|
||||
result = CountWaitingBoxes();
|
||||
for (int i = 0; i < result; i++)
|
||||
{
|
||||
//
|
||||
has = IsValidBox(
|
||||
waitingBoxes[i],
|
||||
barIndex,
|
||||
period //
|
||||
);
|
||||
if (!has)
|
||||
{
|
||||
//
|
||||
Add(
|
||||
i,
|
||||
removeIDX //
|
||||
);
|
||||
|
||||
//
|
||||
AddRef(
|
||||
waitingBoxes[i],
|
||||
unavailableBoxes //
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
has = HasChild(removeIDX);
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
CleanupArray(
|
||||
removeIDX,
|
||||
waitingBoxes //
|
||||
);
|
||||
}
|
||||
Clean(removeIDX);
|
||||
|
||||
//
|
||||
result = CountWaitingBoxes();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
bool IsZoneActivated(
|
||||
int &zoneIDX,
|
||||
@@ -848,17 +1031,17 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
// Activate Zone Using PV ...
|
||||
bool isPVPassed =
|
||||
isBullish
|
||||
? vale < iBox.upper &&
|
||||
vale > iBox.lower
|
||||
: peak < iBox.upper &&
|
||||
peak > iBox.lower;
|
||||
? vale <= iBox.upper &&
|
||||
vale >= iBox.lower
|
||||
: peak <= iBox.upper &&
|
||||
peak >= iBox.lower;
|
||||
bool isPV50Passed =
|
||||
isBullish
|
||||
? vale < iBox.upper &&
|
||||
vale < iRange &&
|
||||
vale <= iRange &&
|
||||
vale > iBox.lower
|
||||
: peak < iBox.upper &&
|
||||
peak < iRange &&
|
||||
peak >= iRange &&
|
||||
peak > iBox.lower;
|
||||
|
||||
//
|
||||
@@ -872,7 +1055,8 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
|
||||
//
|
||||
// Combine Result ...
|
||||
result = isPV50Passed;
|
||||
result = isPVPassed;
|
||||
// || isPVPassed
|
||||
// || isPV50Passed
|
||||
// || isRejected;
|
||||
if (result)
|
||||
@@ -933,6 +1117,194 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
bool IsBoxActivated(
|
||||
int &boxIDX,
|
||||
X121SMCStrategySignalProviderData &provider,
|
||||
int barIndex = 0 //
|
||||
)
|
||||
{
|
||||
//
|
||||
bool result = false;
|
||||
|
||||
//
|
||||
boxIDX = -1;
|
||||
|
||||
//
|
||||
if (barIndex < 0)
|
||||
{
|
||||
barIndex = 0;
|
||||
}
|
||||
|
||||
//
|
||||
result = IsValid() &&
|
||||
HasWaitingBoxes();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
XOHCL bar;
|
||||
XOHCL cBar;
|
||||
result = bar.Init(
|
||||
symbol,
|
||||
period,
|
||||
barIndex //
|
||||
);
|
||||
result =
|
||||
result &&
|
||||
bar.GetPreviousBar(cBar);
|
||||
if (!result)
|
||||
{
|
||||
//
|
||||
bar.Clean();
|
||||
cBar.Clean();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve Some Required Data ...
|
||||
|
||||
//
|
||||
double peak = provider.decisionXConditions.peaksBuffer[1];
|
||||
double vale = provider.decisionXConditions.valesBuffer[1];
|
||||
|
||||
//
|
||||
double analysePeak = provider.analyseXConditions.peaksBuffer[1];
|
||||
double analyseVale = provider.analyseXConditions.valesBuffer[1];
|
||||
|
||||
//
|
||||
double consolidationPeak = provider.consolidationXConditions.peaksBuffer[1];
|
||||
double consolidationVale = provider.consolidationXConditions.valesBuffer[1];
|
||||
|
||||
//
|
||||
double visionPeak = provider.visionXConditions.peaksBuffer[1];
|
||||
double visionVale = provider.visionXConditions.valesBuffer[1];
|
||||
|
||||
//
|
||||
bool isPeaksSame =
|
||||
peak == analysePeak &&
|
||||
analysePeak == consolidationPeak;
|
||||
|
||||
//
|
||||
bool isValesSame =
|
||||
vale == analyseVale &&
|
||||
analyseVale == consolidationVale;
|
||||
|
||||
//
|
||||
// Calculate Scores Conditions ...
|
||||
|
||||
//
|
||||
bool isDecisionScoreBullish =
|
||||
provider.decisionBullishScore > provider.decisionBearishScore;
|
||||
|
||||
//
|
||||
bool isDecisionScoreBearish =
|
||||
provider.decisionBearishScore > provider.decisionBullishScore;
|
||||
|
||||
//
|
||||
bool isAnalyseScoreBullish =
|
||||
provider.analyseBullishScore > provider.analyseBearishScore;
|
||||
|
||||
//
|
||||
bool isAnalyseScoreBearish =
|
||||
provider.analyseBearishScore > provider.analyseBullishScore;
|
||||
|
||||
//
|
||||
bool isConsolidationScoreBullish =
|
||||
provider.consolidationBullishScore > provider.consolidationBearishScore;
|
||||
|
||||
//
|
||||
bool isConsolidationScoreBearish =
|
||||
provider.consolidationBearishScore > provider.consolidationBullishScore;
|
||||
|
||||
//
|
||||
bool isVisionScoreBullish =
|
||||
provider.visionBullishScore > provider.visionBearishScore;
|
||||
|
||||
//
|
||||
bool isVisionScoreBearish =
|
||||
provider.visionBearishScore > provider.visionBullishScore;
|
||||
|
||||
//
|
||||
int count = CountWaitingBoxes();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
//
|
||||
XBoxZone iBox = waitingBoxes[i];
|
||||
bool isBullish = iBox.IsBullish();
|
||||
|
||||
//
|
||||
// Checking Rejection ...
|
||||
|
||||
//
|
||||
// Bullish ...
|
||||
bool isBullishRejected =
|
||||
isBullish &&
|
||||
cBar.low <= iBox.upper &&
|
||||
cBar.GetDown() > iBox.upper;
|
||||
|
||||
//
|
||||
// Bearish ...
|
||||
bool isBearishRejected =
|
||||
!isBullish &&
|
||||
cBar.high >= iBox.lower &&
|
||||
cBar.GetUp() < iBox.lower;
|
||||
|
||||
//
|
||||
// Rejection ...
|
||||
bool isRejected = isBullishRejected ||
|
||||
isBearishRejected;
|
||||
|
||||
//
|
||||
// Checking Breaktion ...
|
||||
|
||||
//
|
||||
// Bullish ...
|
||||
bool isBullishBreaked =
|
||||
isBullish &&
|
||||
cBar.high > iBox.upper &&
|
||||
cBar.GetUp() > iBox.upper &&
|
||||
cBar.low >= iBox.lower;
|
||||
|
||||
//
|
||||
// Bearish ...
|
||||
bool isBearishBreaked =
|
||||
!isBullish &&
|
||||
cBar.low < iBox.lower &&
|
||||
cBar.GetDown() < iBox.lower &&
|
||||
cBar.high <= iBox.upper;
|
||||
|
||||
//
|
||||
// Breaktion ...
|
||||
bool isBreaked = isBullishBreaked ||
|
||||
isBearishBreaked;
|
||||
|
||||
//
|
||||
result = isRejected ||
|
||||
isBreaked;
|
||||
|
||||
//
|
||||
if (result)
|
||||
{
|
||||
//
|
||||
boxIDX = i;
|
||||
break;
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
//
|
||||
bar.Clean();
|
||||
cBar.Clean();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
bool IsExists(XOBFVG &zone)
|
||||
{
|
||||
@@ -941,8 +1313,9 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
|
||||
//
|
||||
// Check Validation ...
|
||||
result = HasWaitingZones() ||
|
||||
HasUnavailableZones();
|
||||
result = zone.IsValid() &&
|
||||
(HasWaitingZones() ||
|
||||
HasUnavailableZones());
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
@@ -979,6 +1352,53 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
bool IsExists(XBoxZone &box)
|
||||
{
|
||||
//
|
||||
bool result = false;
|
||||
|
||||
//
|
||||
// Check Validation ...
|
||||
result = box.IsValid() &&
|
||||
(HasWaitingBoxes() ||
|
||||
HasUnavailableBoxes());
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
int idx = -1;
|
||||
|
||||
//
|
||||
// Check Index in Waiting Zones ...
|
||||
idx = FindIndex(
|
||||
box,
|
||||
waitingBoxes //
|
||||
);
|
||||
result = IsValidIndex(idx);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Check Index in Unavailable Zones ...
|
||||
idx = FindIndex(
|
||||
box,
|
||||
unavailableBoxes //
|
||||
);
|
||||
result = IsValidIndex(idx);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
bool IsDestroyed(
|
||||
int barIndex = 0,
|
||||
@@ -1330,6 +1750,18 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
has = HasWaitingBoxes();
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
count = CountWaitingBoxes();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
waitingBoxes[i].to = value;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
@@ -1338,9 +1770,7 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
XCPOIDrawer *drawer,
|
||||
bool drawSupplyZone = false,
|
||||
bool drawDemandZone = false,
|
||||
bool drawWaitingZones = false,
|
||||
bool drawWaitingZonesBoxArea = false //
|
||||
)
|
||||
bool drawWaitingZones = false)
|
||||
{
|
||||
//
|
||||
if (drawer == NULL)
|
||||
@@ -1605,30 +2035,54 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
}
|
||||
|
||||
//
|
||||
if (drawWaitingZonesBoxArea)
|
||||
{
|
||||
//
|
||||
XBoxZone iBox;
|
||||
has = waitingZones[i].ToZone(
|
||||
iBox //
|
||||
);
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
XCBoxObject *iBoxObj;
|
||||
has = drawer.DrawBox(
|
||||
iBox,
|
||||
iBoxObj //
|
||||
);
|
||||
if (has)
|
||||
{
|
||||
mDrawnObjects.Add(iBoxObj);
|
||||
}
|
||||
}
|
||||
// if (drawWaitingZonesBoxArea)
|
||||
// {
|
||||
// //
|
||||
// XBoxZone iBox;
|
||||
// has = waitingZones[i].ToZone(
|
||||
// iBox //
|
||||
// );
|
||||
// if (has)
|
||||
// {
|
||||
// //
|
||||
// XCBoxObject *iBoxObj;
|
||||
// has = drawer.DrawBox(
|
||||
// iBox,
|
||||
// iBoxObj //
|
||||
// );
|
||||
// if (has)
|
||||
// {
|
||||
// mDrawnObjects.Add(iBoxObj);
|
||||
// }
|
||||
// }
|
||||
|
||||
//
|
||||
iBox.Clean();
|
||||
// //
|
||||
// iBox.Clean();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
has = HasWaitingBoxes();
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
count = CountWaitingBoxes();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
//
|
||||
XCBoxObject *iBoxObj;
|
||||
has = drawer.DrawBox(
|
||||
waitingBoxes[i],
|
||||
iBoxObj //
|
||||
);
|
||||
if (has)
|
||||
{
|
||||
mDrawnObjects.Add(iBoxObj);
|
||||
}
|
||||
|
||||
//
|
||||
ZeroMemory(iBoxObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1678,6 +2132,30 @@ struct X121SMCStrategyXWZWorkingZone
|
||||
return ArraySize(unavailableZones);
|
||||
}
|
||||
|
||||
//
|
||||
bool HasWaitingBoxes()
|
||||
{
|
||||
return HasChild(waitingBoxes);
|
||||
}
|
||||
|
||||
//
|
||||
int CountWaitingBoxes()
|
||||
{
|
||||
return ArraySize(waitingBoxes);
|
||||
}
|
||||
|
||||
//
|
||||
bool HasUnavailableBoxes()
|
||||
{
|
||||
return HasChild(unavailableBoxes);
|
||||
}
|
||||
|
||||
//
|
||||
int CountUnavailableBoxes()
|
||||
{
|
||||
return ArraySize(unavailableBoxes);
|
||||
}
|
||||
|
||||
//
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user