Files
Bell-PriceActionWithEma-EA/Include/ICT2026/MssSetup.mqh
T
2026-05-24 22:48:39 +07:00

675 lines
21 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| MssSetup.mqh — MSS: retest FVG H1 (POI) → CHoCH M5 → M5 FVG → entry |
//+------------------------------------------------------------------+
#ifndef ICT2026_MSSSETUP_MQH
#define ICT2026_MSSSETUP_MQH
#include <ICT2026/ConfirmFvg.mqh>
#include <ICT2026/Journal.mqh>
#include <ICT2026/LowTfApi.mqh>
string IctMssPhaseText(const ENUM_ICT_MSS_PHASE ph)
{
switch(ph)
{
case ICT_MSS_H1_TOUCH: return "Retest FVG H1 OK";
case ICT_MSS_CHOCH: return "CHoCH OK";
case ICT_MSS_M5_FVG: return "M5 FVG";
case ICT_MSS_ENTRY_FILL: return "M5 fill 38.2%";
case ICT_MSS_READY: return "READY entry";
default: return "Idle";
}
}
int IctMss_FindH1FvgById(const ulong id)
{
for(int i = 0; i < g_ictFvgCount; i++)
if(g_ictFvgZones[i].id == id)
return i;
return -1;
}
// H1 FVG mục tiêu: Bear → Premium cao nhất; Bull → Discount thấp nhất (chờ hồi chạm, chưa cần fill)
int IctMss_SelectTargetH1Fvg()
{
int bestIdx = -1;
for(int i = 0; i < g_ictFvgCount; i++)
{
if(!IctFvg_MatchesBias(g_ictFvgZones[i]))
continue;
if(!IctFvg_IsEntryRepPd(g_ictFvgZones[i]))
continue;
if(g_ictFvgZones[i].state == ICT_FVG_USED && g_ictFvgZones[i].maxFillRatio < 0.01)
continue;
if(g_ictDailyBias.bias == ICT_BIAS_BEAR)
{
if(bestIdx < 0 || g_ictFvgZones[i].upper > g_ictFvgZones[bestIdx].upper)
bestIdx = i;
}
else if(g_ictDailyBias.bias == ICT_BIAS_BULL)
{
if(bestIdx < 0 || g_ictFvgZones[i].lower < g_ictFvgZones[bestIdx].lower)
bestIdx = i;
}
}
return bestIdx;
}
// Retest FVG H1 (POI): giá chạm gap H1 + lấp ≥ InpMssH1MinFillPct (đo trên InpFvgTf, không phải M5)
bool IctMss_HasH1FvgRetest(const string sym, IctFvgZone &h1)
{
IctFvg_UpdateZoneState(sym, InpFvgTf, h1);
if(h1.firstTouchTime <= 0)
return false;
if(!IctFvg_HasFillAtLeast(h1, InpMssH1MinFillPct))
return false;
h1.mssH1Touch382 = true;
return true;
}
double IctMss_H1Atr(const string sym)
{
const int h = iATR(sym, InpFvgTf, InpFvgAtrPeriod);
if(h == INVALID_HANDLE)
return 0.0;
double buf[];
ArraySetAsSeries(buf, true);
if(CopyBuffer(h, 0, 1, 1, buf) != 1)
{
IndicatorRelease(h);
return 0.0;
}
IndicatorRelease(h);
return buf[0];
}
double IctMss_H1ZoneBuffer(const IctFvgZone &h1, const string sym)
{
const double gapH = h1.upper - h1.lower;
double buf = (gapH > _Point && InpMssMaxDistGapPct > 0.0) ?
gapH * InpMssMaxDistGapPct / 100.0 : 0.0;
const double atr = IctMss_H1Atr(sym);
if(atr > 0.0 && InpMssMaxDistAtrMult > 0.0)
buf = MathMax(buf, atr * InpMssMaxDistAtrMult);
if(buf <= 0.0)
buf = MathMax(gapH * 0.5, 20.0 * _Point);
return buf;
}
void IctMss_GetH1ProximityBounds(const IctFvgZone &h1, const string sym,
const ENUM_ICT_BIAS bias,
double &outLo, double &outHi)
{
const double gapH = h1.upper - h1.lower;
const double buf = IctMss_H1ZoneBuffer(h1, sym);
outLo = h1.lower - buf;
outHi = h1.upper + buf;
if(gapH <= _Point)
return;
if(bias == ICT_BIAS_BEAR && InpMssExtraBelowGapPct > 0.0)
outLo -= gapH * InpMssExtraBelowGapPct / 100.0;
if(bias == ICT_BIAS_BULL && InpMssExtraAboveGapPct > 0.0)
outHi += gapH * InpMssExtraAboveGapPct / 100.0;
}
bool IctMss_PriceNearH1Fvg(const IctFvgZone &h1, const string sym,
const double price, const ENUM_ICT_BIAS bias)
{
if(price <= 0.0 || h1.upper <= h1.lower)
return false;
double lo = 0.0, hi = 0.0;
IctMss_GetH1ProximityBounds(h1, sym, bias, lo, hi);
return (price >= lo - _Point && price <= hi + _Point);
}
bool IctMss_ChochWithinTouchWindow(const IctFvgZone &h1, const datetime chochTime)
{
const datetime tTouch = (h1.firstTouchTime > 0) ? h1.firstTouchTime : h1.createdTime;
if(chochTime > 0 && chochTime < tTouch)
return false;
if(InpMssMaxM5BarsAfterTouch > 0 && chochTime > 0)
{
const int sec = (int)PeriodSeconds(InpConfirmTf);
const datetime tMax = tTouch + (datetime)(InpMssMaxM5BarsAfterTouch * sec);
if(chochTime > tMax)
return false;
}
return true;
}
int IctMss_MaxSwingShift(const IctSwingSet &sw)
{
int m = 1;
if(sw.hasH0)
m = MathMax(m, sw.h0.shift);
if(sw.hasL0)
m = MathMax(m, sw.l0.shift);
if(sw.hasH1)
m = MathMax(m, sw.h1.shift);
if(sw.hasL1)
m = MathMax(m, sw.l1.shift);
return m + 2;
}
bool IctMss_BodyBrokeLevelSince(const string sym, const ENUM_TIMEFRAMES tf,
const int maxShift, const double level,
const bool wantBelow)
{
const int lim = MathMax(1, MathMin(maxShift, InpMssConfirmLookback));
for(int sh = 1; sh <= lim; sh++)
{
if(wantBelow && IctBodyBreakBelow(sym, tf, sh, level))
return true;
if(!wantBelow && IctBodyBreakAbove(sym, tf, sh, level))
return true;
}
return false;
}
IctSwingPoint IctMss_NewestSwingLowBefore(const IctSwingPoint &lows[], const int nL,
const int barShift, const datetime afterTime)
{
IctSwingPoint empty;
empty.Clear();
int best = -1;
for(int i = 0; i < nL; i++)
{
if(lows[i].shift <= barShift)
continue;
if(lows[i].time < afterTime)
continue;
if(best < 0 || lows[i].shift < lows[best].shift)
best = i;
}
if(best < 0)
return empty;
return lows[best];
}
IctSwingPoint IctMss_NewestSwingHighBefore(const IctSwingPoint &highs[], const int nH,
const int barShift, const datetime afterTime)
{
IctSwingPoint empty;
empty.Clear();
int best = -1;
for(int i = 0; i < nH; i++)
{
if(highs[i].shift <= barShift)
continue;
if(highs[i].time < afterTime)
continue;
if(best < 0 || highs[i].shift < highs[best].shift)
best = i;
}
if(best < 0)
return empty;
return highs[best];
}
double IctMss_MaxHighSince(const string sym, const ENUM_TIMEFRAMES tf,
const int fromShift, const int toShift,
const datetime afterTime)
{
double hi = -DBL_MAX;
for(int sh = fromShift; sh >= toShift; sh--)
{
if(iTime(sym, tf, sh) < afterTime)
continue;
hi = MathMax(hi, iHigh(sym, tf, sh));
}
return (hi > -DBL_MAX * 0.5) ? hi : 0.0;
}
double IctMss_MinLowSince(const string sym, const ENUM_TIMEFRAMES tf,
const int fromShift, const int toShift,
const datetime afterTime)
{
double lo = DBL_MAX;
for(int sh = fromShift; sh >= toShift; sh--)
{
if(iTime(sym, tf, sh) < afterTime)
continue;
lo = MathMin(lo, iLow(sym, tf, sh));
}
return (lo < DBL_MAX * 0.5) ? lo : 0.0;
}
// CHoCH/MSS thời điểm: nến M5 đầu tiên (sau retest FVG H1) body phá swing — khóa, không đổi khi pivot update
bool IctMss_FindFirstChochAfterFvgRetest(const string sym, const ENUM_TIMEFRAMES tf,
const datetime tTouch, const ENUM_ICT_BIAS bias,
double &keyLevelOut, datetime &keyTimeOut,
datetime &chochBarTimeOut, double &slSwingOut)
{
keyLevelOut = 0.0;
keyTimeOut = 0;
chochBarTimeOut = 0;
slSwingOut = 0.0;
int touchSh = iBarShift(sym, tf, tTouch, true);
if(touchSh < 0)
touchSh = iBarShift(sym, tf, tTouch, false);
if(touchSh < 1)
return false;
IctSwingPoint highs[], lows[];
IctCollectSwings(sym, tf, InpConfirmSwingRange, InpConfirmSwingLookback, highs, lows);
const int nH = ArraySize(highs);
const int nL = ArraySize(lows);
if(bias == ICT_BIAS_BEAR)
{
for(int sh = touchSh; sh >= 1; sh--)
{
const datetime tBar = iTime(sym, tf, sh);
if(tBar < tTouch)
continue;
const IctSwingPoint keyLo = IctMss_NewestSwingLowBefore(lows, nL, sh, tTouch);
if(!keyLo.Valid())
continue;
if(!IctBodyBreakBelow(sym, tf, sh, keyLo.price))
continue;
const double reactHi = IctMss_MaxHighSince(sym, tf, touchSh, sh, tTouch);
if(reactHi <= 0.0)
continue;
keyLevelOut = keyLo.price;
keyTimeOut = keyLo.time;
chochBarTimeOut = tBar;
slSwingOut = reactHi;
return true;
}
}
if(bias == ICT_BIAS_BULL)
{
for(int sh = touchSh; sh >= 1; sh--)
{
const datetime tBar = iTime(sym, tf, sh);
if(tBar < tTouch)
continue;
const IctSwingPoint keyHi = IctMss_NewestSwingHighBefore(highs, nH, sh, tTouch);
if(!keyHi.Valid())
continue;
if(!IctBodyBreakAbove(sym, tf, sh, keyHi.price))
continue;
const double reactLo = IctMss_MinLowSince(sym, tf, touchSh, sh, tTouch);
if(reactLo <= 0.0)
continue;
keyLevelOut = keyHi.price;
keyTimeOut = keyHi.time;
chochBarTimeOut = tBar;
slSwingOut = reactLo;
return true;
}
}
return false;
}
bool IctMss_TryLockChoch(const string sym, const ENUM_TIMEFRAMES tf,
const IctFvgZone &h1, const ENUM_ICT_BIAS bias)
{
if(g_ictLowTf.mss.chochLocked && g_ictLowTf.mss.chochKeyLevel > 0.0)
return true;
const datetime tTouch = h1.firstTouchTime;
if(tTouch <= 0)
return false;
double keyLv = 0.0, slSwing = 0.0;
datetime keyT = 0, chochBarT = 0;
if(!IctMss_FindFirstChochAfterFvgRetest(sym, tf, tTouch, bias,
keyLv, keyT, chochBarT, slSwing))
return false;
g_ictLowTf.mss.chochKeyLevel = keyLv;
g_ictLowTf.mss.chochKeyTime = keyT;
g_ictLowTf.mss.slSwingPrice = slSwing;
g_ictLowTf.mss.chochTime = chochBarT;
g_ictLowTf.mss.chochLocked = true;
return true;
}
bool IctMss_ZonesOverlapH1(const IctFvgZone &h1, const string sym,
const ENUM_ICT_BIAS bias,
const double zoneLo, const double zoneHi)
{
if(zoneHi <= zoneLo)
return false;
double hLo = 0.0, hHi = 0.0;
IctMss_GetH1ProximityBounds(h1, sym, bias, hLo, hHi);
return !(zoneHi < hLo - _Point || zoneLo > hHi + _Point);
}
bool IctMss_IsChochNearH1Fvg(const string sym, const IctFvgZone &h1,
const ENUM_ICT_BIAS bias,
const double keyLevel, const double anchorPrice,
const datetime chochTime)
{
const bool nearAnchor = (anchorPrice > 0.0 &&
IctMss_PriceNearH1Fvg(h1, sym, anchorPrice, bias));
const bool nearKey = (keyLevel > 0.0 &&
IctMss_PriceNearH1Fvg(h1, sym, keyLevel, bias));
if(!nearAnchor && !nearKey)
return false;
return IctMss_ChochWithinTouchWindow(h1, chochTime);
}
bool IctMss_GetConfirmMssSwing(const string sym, const ENUM_ICT_BIAS bias, double &swingOut)
{
swingOut = 0.0;
IctSwingSet sw;
ENUM_ICT_STRUCT structural = ICT_STRUCT_NONE;
if(IctBuildConfirmSwingSet(sym, sw, structural))
{
if(bias == ICT_BIAS_BEAR && sw.hasH0)
{
swingOut = sw.h0.price;
return true;
}
if(bias == ICT_BIAS_BULL && sw.hasL0)
{
swingOut = sw.l0.price;
return true;
}
}
IctSwingPoint highs[], lows[];
IctCollectSwings(sym, InpConfirmTf, InpConfirmSwingRange, InpConfirmSwingLookback,
highs, lows);
const int nH = ArraySize(highs);
const int nL = ArraySize(lows);
if(bias == ICT_BIAS_BEAR && nH > 0)
{
swingOut = highs[nH - 1].price;
return true;
}
if(bias == ICT_BIAS_BULL && nL > 0)
{
swingOut = lows[nL - 1].price;
return true;
}
return false;
}
bool IctMss_FindExtremePivot(const IctSwingPoint &pts[], const int n,
const int maxShift, const bool wantHighest,
IctSwingPoint &out)
{
out.Clear();
int best = -1;
for(int i = 0; i < n; i++)
{
if(pts[i].shift > maxShift || pts[i].shift < 3)
continue;
if(best < 0)
best = i;
else if(wantHighest)
{
if(pts[i].price > pts[best].price)
best = i;
}
else
{
if(pts[i].price < pts[best].price)
best = i;
}
}
if(best < 0)
return false;
out = pts[best];
return true;
}
bool IctMss_DetectChoch(const string sym, const ENUM_TIMEFRAMES tf,
const ENUM_ICT_BIAS bias,
double &keyLevelOut, datetime &keyTimeOut,
double &slSwingOut)
{
keyLevelOut = 0.0;
keyTimeOut = 0;
slSwingOut = 0.0;
IctSwingPoint highs[], lows[];
IctCollectSwings(sym, tf, InpConfirmSwingRange, InpConfirmSwingLookback,
highs, lows);
const int nH = ArraySize(highs);
const int nL = ArraySize(lows);
const int maxSh = InpMssConfirmLookback;
if(bias == ICT_BIAS_BULL)
{
IctSwingPoint extremeLo;
if(!IctMss_FindExtremePivot(lows, nL, maxSh, false, extremeLo))
return false;
IctSwingPoint keyHi = IctFindMostRecentOlder(highs, nH, extremeLo.shift);
if(!keyHi.Valid())
return false;
keyLevelOut = keyHi.price;
keyTimeOut = keyHi.time;
slSwingOut = extremeLo.price;
return IctBodyBreakAbove(sym, tf, 1, keyLevelOut);
}
if(bias == ICT_BIAS_BEAR)
{
IctSwingPoint extremeHi;
if(!IctMss_FindExtremePivot(highs, nH, maxSh, true, extremeHi))
return false;
IctSwingPoint keyLo = IctFindMostRecentOlder(lows, nL, extremeHi.shift);
if(!keyLo.Valid())
return false;
keyLevelOut = keyLo.price;
keyTimeOut = keyLo.time;
slSwingOut = extremeHi.price;
return IctBodyBreakBelow(sym, tf, 1, keyLevelOut);
}
return false;
}
bool IctMss_PriceInEntryZone(const string sym, const ENUM_TIMEFRAMES tf,
const IctFvgZone &zone)
{
if(!IctFvg_HasFillAtLeast(zone, InpMssEntryFillPct))
return false;
const double fillPx = IctFvg_PriceAtFillPct(zone, InpMssEntryFillPct);
const double bid = SymbolInfoDouble(sym, SYMBOL_BID);
const double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
const double mid = (bid + ask) * 0.5;
if(zone.side == ICT_FVG_BULL)
return (mid <= fillPx + _Point && mid >= zone.lower - _Point);
if(zone.side == ICT_FVG_BEAR)
return (mid >= fillPx - _Point && mid <= zone.upper + _Point);
return false;
}
void IctMss_ResetState()
{
g_ictLowTf.mss.Clear();
IctMss_JournalReset();
}
void IctMss_Update(const string sym)
{
g_ictLowTf.mss.displayReason = IctMssPhaseText(g_ictLowTf.mss.phase);
if(!g_ictIntraday.isAllowTrade)
{
if(g_ictLowTf.mss.phase != ICT_MSS_IDLE)
IctMss_ResetState();
g_ictLowTf.mss.displayReason = "AllowTrade=false";
return;
}
const ENUM_TIMEFRAMES cTf = InpConfirmTf;
const ENUM_ICT_FVG_SIDE wantSide = IctFvgSideFromBias(g_ictDailyBias.bias);
if(wantSide == ICT_FVG_NONE)
{
IctMss_ResetState();
g_ictLowTf.mss.displayReason = "Bias none";
return;
}
if(g_ictLowTf.mss.phase == ICT_MSS_IDLE || g_ictLowTf.mss.h1FvgId == 0)
{
const int hIdx = IctMss_SelectTargetH1Fvg();
if(hIdx < 0)
{
g_ictLowTf.mss.displayReason = StringFormat("Chờ H1 %s FVG",
(wantSide == ICT_FVG_BEAR) ? "Premium" : "Discount");
return;
}
g_ictLowTf.mss.h1FvgId = g_ictFvgZones[hIdx].id;
if(!IctMss_HasH1FvgRetest(sym, g_ictFvgZones[hIdx]))
{
g_ictLowTf.mss.displayReason = StringFormat(
"Chờ retest FVG H1 %s [%.0f%.0f] (%.0f%% lấp trên H1)",
IctPdZoneText(IctFvg_GetRepPd(g_ictFvgZones[hIdx])),
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper,
InpMssH1MinFillPct);
return;
}
g_ictLowTf.mss.phase = ICT_MSS_H1_TOUCH;
g_ictLowTf.mss.h1TouchTime = g_ictFvgZones[hIdx].firstTouchTime;
g_ictLowTf.mss.displayReason = StringFormat("Retest FVG H1 OK | lấp %.0f%% (H1)",
g_ictFvgZones[hIdx].maxFillRatio * 100.0);
}
const int h1Idx = IctMss_FindH1FvgById(g_ictLowTf.mss.h1FvgId);
if(h1Idx < 0)
{
IctMss_ResetState();
g_ictLowTf.mss.displayReason = "H1 FVG mất";
return;
}
if(g_ictLowTf.mss.phase >= ICT_MSS_H1_TOUCH &&
!IctMss_HasH1FvgRetest(sym, g_ictFvgZones[h1Idx]))
{
IctMss_ResetState();
g_ictLowTf.mss.displayReason = "FVG H1 chưa retest đủ % — reset MSS";
return;
}
if(g_ictLowTf.mss.phase >= ICT_MSS_H1_TOUCH && g_ictLowTf.mss.h1TouchTime <= 0)
g_ictLowTf.mss.h1TouchTime = g_ictFvgZones[h1Idx].firstTouchTime;
if(g_ictLowTf.mss.phase == ICT_MSS_H1_TOUCH)
{
if(!IctMss_TryLockChoch(sym, cTf, g_ictFvgZones[h1Idx], g_ictDailyBias.bias))
{
g_ictLowTf.mss.displayReason = (g_ictDailyBias.bias == ICT_BIAS_BEAR) ?
"Retest FVG H1 OK — chờ MSS↓ phá swing M5" :
"Retest FVG H1 OK — chờ MSS↑ phá swing M5";
return;
}
const double keyLv = g_ictLowTf.mss.chochKeyLevel;
const double slSwing = g_ictLowTf.mss.slSwingPrice;
const datetime chochT = g_ictLowTf.mss.chochTime;
if(!IctMss_IsChochNearH1Fvg(sym, g_ictFvgZones[h1Idx], g_ictDailyBias.bias,
keyLv, slSwing, chochT))
{
g_ictLowTf.mss.displayReason = StringFormat(
"CHoCH khóa @ %.2f — xa H1 FVG, chờ gần hơn", keyLv);
return;
}
g_ictLowTf.mss.phase = ICT_MSS_CHOCH;
IctConfirmFvg_ScanNew(sym, cTf, wantSide, g_ictLowTf.mss.chochTime, true);
g_ictLowTf.mss.displayReason = StringFormat("CHoCH khóa @ %.2f — quét M5 FVG", keyLv);
}
if(g_ictLowTf.mss.phase == ICT_MSS_CHOCH)
{
IctConfirmFvg_ScanNew(sym, cTf, wantSide, g_ictLowTf.mss.chochTime, false);
const int mIdx = IctConfirmFvg_FindLatestAfter(g_ictLowTf.mss.chochTime, wantSide);
if(mIdx < 0)
{
g_ictLowTf.mss.displayReason = "CHoCH OK — chờ M5 FVG";
return;
}
if(!IctMss_ZonesOverlapH1(g_ictFvgZones[h1Idx], sym, g_ictDailyBias.bias,
g_ictConfirmFvgZones[mIdx].lower,
g_ictConfirmFvgZones[mIdx].upper))
{
g_ictLowTf.mss.displayReason = "M5 FVG xa H1 FVG — chờ FVG gần hơn";
return;
}
g_ictLowTf.mss.phase = ICT_MSS_M5_FVG;
g_ictLowTf.mss.m5FvgId = g_ictConfirmFvgZones[mIdx].id;
g_ictLowTf.mss.m5FvgTime = g_ictConfirmFvgZones[mIdx].createdTime;
g_ictLowTf.mss.displayReason = StringFormat("M5 FVG #%I64u [%.2f%.2f]",
g_ictLowTf.mss.m5FvgId,
g_ictConfirmFvgZones[mIdx].lower,
g_ictConfirmFvgZones[mIdx].upper);
}
if(g_ictLowTf.mss.phase >= ICT_MSS_M5_FVG && g_ictLowTf.mss.m5FvgId > 0)
{
const int mIdx = IctConfirmFvg_FindById(g_ictLowTf.mss.m5FvgId);
if(mIdx < 0)
{
g_ictLowTf.mss.phase = ICT_MSS_CHOCH;
g_ictLowTf.mss.m5FvgId = 0;
g_ictLowTf.mss.displayReason = "M5 FVG mất — quét lại";
return;
}
IctFvg_UpdateZoneState(sym, cTf, g_ictConfirmFvgZones[mIdx]);
if(IctMss_PriceInEntryZone(sym, cTf, g_ictConfirmFvgZones[mIdx]))
{
g_ictLowTf.mss.phase = ICT_MSS_READY;
g_ictLowTf.mss.displayReason = StringFormat("READY | M5 fill %.0f%% | %s %s",
g_ictConfirmFvgZones[mIdx].maxFillRatio * 100.0,
IctFvgSideText(wantSide),
IctPdZoneText(IctFvg_GetRepPd(g_ictFvgZones[h1Idx])));
}
else if(IctFvg_HasFillAtLeast(g_ictConfirmFvgZones[mIdx], InpMssEntryFillPct))
{
g_ictLowTf.mss.phase = ICT_MSS_ENTRY_FILL;
g_ictLowTf.mss.displayReason = StringFormat("M5 chạm %.0f%% — canh entry",
InpMssEntryFillPct);
}
else
{
g_ictLowTf.mss.phase = ICT_MSS_M5_FVG;
g_ictLowTf.mss.displayReason = StringFormat("Chờ hồi M5 FVG %.0f%% (fill %.0f%%)",
InpMssEntryFillPct,
g_ictConfirmFvgZones[mIdx].maxFillRatio * 100.0);
}
}
IctMss_JournalPipeline(g_ictLowTf.mss.displayReason);
}
#endif