Optimize setup SL/TP base on spread onl, no atr for stable
This commit is contained in:
+29
-2
@@ -3,8 +3,8 @@
|
||||
//| BOS = Continue | CHoCH = Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "ICT 2026"
|
||||
#property version "1.151"
|
||||
#property description "ICT2026 MSS | SL = swing + ATR + spread | TP = iL0/iH0 + ATR − 2×spread (dễ khớp)"
|
||||
#property version "1.163"
|
||||
#property description "ICT2026 MSS | OnlyStatsMode: skip render + in stats mỗi lệnh close + tổng kết deinit"
|
||||
|
||||
#include <ICT2026/Config.mqh>
|
||||
#include <ICT2026/DailyBias.mqh>
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <ICT2026/LowTfTrend.mqh>
|
||||
#include <ICT2026/Panel.mqh>
|
||||
#include <ICT2026/Draw.mqh>
|
||||
#include <ICT2026/Stats.mqh>
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
@@ -29,6 +30,12 @@ int OnInit()
|
||||
|
||||
IctMssStats_Recompute(_Symbol, InpMssMagic);
|
||||
|
||||
if(InpOnlyStatsMode)
|
||||
{
|
||||
PrintFormat("[ICT2026] OnlyStatsMode = ON — skip render/draw, chỉ in stats khi deinit");
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
if(biasOk || intraOk)
|
||||
{
|
||||
IctEaState_Refresh(_Symbol);
|
||||
@@ -49,6 +56,13 @@ int OnInit()
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
IctMssStats_Recompute(_Symbol, InpMssMagic);
|
||||
PrintFormat("[ICT2026] === FINAL %s | %s ===",
|
||||
IctMssStats_LineCounts(), IctMssStats_LinePerf());
|
||||
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
|
||||
IctPanel_Clear();
|
||||
IctDraw_Clear();
|
||||
IctFvgDraw_DeleteAll();
|
||||
@@ -71,6 +85,9 @@ void OnTick()
|
||||
|
||||
IctLowTfTrend_TickRefresh(_Symbol);
|
||||
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
|
||||
if(refresh || !panelBoot)
|
||||
{
|
||||
IctPanel_Render(_Symbol);
|
||||
@@ -81,6 +98,8 @@ void OnTick()
|
||||
|
||||
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
|
||||
{
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
if(id == CHARTEVENT_CHART_CHANGE)
|
||||
{
|
||||
IctPanel_Render(_Symbol);
|
||||
@@ -117,6 +136,14 @@ void OnTradeTransaction(const MqlTradeTransaction &trans,
|
||||
|
||||
IctMss_OnPositionClosed(_Symbol, reason, net);
|
||||
IctMssStats_Recompute(_Symbol, InpMssMagic);
|
||||
|
||||
if(InpOnlyStatsMode)
|
||||
{
|
||||
PrintFormat("[ICT2026] %s | %s",
|
||||
IctMssStats_LineCounts(), IctMssStats_LinePerf());
|
||||
return;
|
||||
}
|
||||
|
||||
IctEaState_Refresh(_Symbol);
|
||||
IctPanel_Render(_Symbol);
|
||||
IctMssDraw_Render(_Symbol);
|
||||
|
||||
@@ -19,6 +19,9 @@ input int InpIntradaySwingRange = 2; // Pivot intraday
|
||||
input int InpIntradaySwingLookback = 120; // Lookback quét pivot H1
|
||||
input int InpIntradayRecentBars = 80; // Chỉ dùng pivot trong N bar gần nhất
|
||||
|
||||
input group "══ Test mode ══"
|
||||
input bool InpOnlyStatsMode = false; // true = skip toàn bộ render/draw + journal verbose (chạy tester nhanh, chỉ in stats cuối)
|
||||
|
||||
input group "══ Hiển thị ══"
|
||||
input bool InpDrawPanel = true; // Panel bias góc trên trái
|
||||
input int InpPanelX = 12;
|
||||
@@ -77,13 +80,10 @@ input group "══ MSS Trade ══"
|
||||
input bool InpMssTradeEnabled = true; // Đặt lệnh limit MSS
|
||||
input ulong InpMssMagic = 202604; // Magic number
|
||||
input double InpMssRiskPct = 1.0; // R % balance mỗi lệnh
|
||||
input ENUM_TIMEFRAMES InpMssSlAtrTf = PERIOD_M5; // TF tính ATR cho buffer SL
|
||||
input ENUM_TIMEFRAMES InpMssTpAtrTf = PERIOD_M5; // TF tính ATR cho buffer TP
|
||||
input int InpMssBufferAtrPeriod = 14; // Period ATR cho buffer SL/TP
|
||||
input double InpMssSlAtrMult = 2.0; // SL: buffer ngoài max(H0,H1)/min(L0,L1) (× ATR M5) — tránh spike đỉnh
|
||||
input double InpMssMinRR = 1.5; // TP tối thiểu (× risk entry→SL) — block trade nếu iL0/iH0 không đạt
|
||||
input double InpMssTpAtrBuffer = 2.0; // TP buffer trước iL0/iH0 (× ATR M5) — chốt trước vùng cản
|
||||
input int InpMssPendingExpireHours = 24; // Hết hạn pending (giờ, 0=không)
|
||||
input int InpMssSlSpreadMult = 8; // SL buffer = N × spread (cộng ra ngoài max(H0,H1)/min(L0,L1))
|
||||
input int InpMssTpSpreadMult = 16; // TP buffer = N × spread (chốt trước iL0/iH0 để dễ khớp)
|
||||
input double InpMssMinRR = 2.0; // Ngưỡng RR: nếu TP@iL0/iH0 < ngưỡng → dùng TP cố định = entry ± risk × InpMssMinRR (= 2R), ngược lại TP tại iL0/iH0
|
||||
input int InpMssPendingExpireHours = 4; // Hết hạn pending limit (giờ): hủy + reset → WAIT_FVG_TOUCH (0=không timeout)
|
||||
input bool InpMssOnePosition = true; // Một position/pending MSS
|
||||
input bool InpMssCancelPendingEod = true; // Hủy pending cuối phiên Mỹ (nếu chưa khớp)
|
||||
input int InpMssEodHour = 23; // Giờ EOD theo SERVER time (24h, vd EET broker = 23h ≈ 16:00 ET DST)
|
||||
|
||||
@@ -96,6 +96,8 @@ bool IctDraw_BuildConfirmSwings(const string sym, IctSwingSet &sw)
|
||||
|
||||
void IctDraw_Render(const string sym)
|
||||
{
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
if(!InpDrawChartLabels)
|
||||
{
|
||||
IctDraw_Clear();
|
||||
|
||||
@@ -59,6 +59,8 @@ void IctFvgDraw_HLine(const string name, const datetime t1, const datetime t2,
|
||||
|
||||
void IctFvgDraw_Render(const string sym)
|
||||
{
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
if(!InpDrawFvgZones)
|
||||
{
|
||||
IctFvgDraw_DeleteAll();
|
||||
|
||||
@@ -22,6 +22,8 @@ bool IctMss_IsEntryReadyReason(const string reason)
|
||||
|
||||
void IctMss_JournalPipeline(const string reason)
|
||||
{
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
if(!InpMssLogJournal)
|
||||
return;
|
||||
if(IctMss_IsEntryReadyReason(reason))
|
||||
@@ -35,6 +37,8 @@ void IctMss_JournalPipeline(const string reason)
|
||||
|
||||
void IctMss_JournalEntryBlock(const string reason)
|
||||
{
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
if(!InpMssLogJournal)
|
||||
return;
|
||||
if(reason == g_ictMssEntryJournalLast)
|
||||
|
||||
@@ -92,6 +92,8 @@ void IctMssDraw_ResolveChoch(const IctMssState &mss,
|
||||
|
||||
void IctMssDraw_Render(const string sym)
|
||||
{
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
if(!InpDrawMssChoch)
|
||||
{
|
||||
IctMssDraw_DeleteAll();
|
||||
|
||||
+127
-64
@@ -55,22 +55,6 @@ double IctMssEntry_VolumeForRisk(const string sym, const bool isBuy,
|
||||
return riskMoney / lossPerLot;
|
||||
}
|
||||
|
||||
double IctMssEntry_Atr(const string sym, const ENUM_TIMEFRAMES tf, const int period)
|
||||
{
|
||||
const int h = iATR(sym, tf, period);
|
||||
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 IctMssEntry_LimitPrice(const IctFvgZone &zone)
|
||||
{
|
||||
if(zone.side == ICT_FVG_BULL)
|
||||
@@ -81,13 +65,18 @@ double IctMssEntry_LimitPrice(const IctFvgZone &zone)
|
||||
}
|
||||
|
||||
bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone,
|
||||
double &entryOut, double &slOut, double &tpOut)
|
||||
double &entryOut, double &slOut, double &tpOut,
|
||||
string &reasonOut)
|
||||
{
|
||||
entryOut = slOut = tpOut = 0.0;
|
||||
reasonOut = "";
|
||||
|
||||
entryOut = IctMssEntry_LimitPrice(m5Zone);
|
||||
if(entryOut <= 0.0)
|
||||
{
|
||||
reasonOut = "M5 FVG side không xác định";
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool isBuy = (m5Zone.side == ICT_FVG_BULL);
|
||||
const ENUM_ICT_BIAS bias = isBuy ? ICT_BIAS_BULL : ICT_BIAS_BEAR;
|
||||
@@ -100,26 +89,22 @@ bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone,
|
||||
if(g_ictLowTf.mss.slSwingPrice > 0.0)
|
||||
swingSl = g_ictLowTf.mss.slSwingPrice;
|
||||
else
|
||||
{
|
||||
reasonOut = "Chưa có swing H0/L0 M5 cho SL";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// SL dùng ATR M5 (sát swing M5, không quá rộng để giữ risk thấp + RR đạt minRR)
|
||||
// TP dùng ATR H1 (trung bình sóng H1 đang đánh, buffer rõ ràng trên chart)
|
||||
double atrSl = IctMssEntry_Atr(sym, InpMssSlAtrTf, InpMssBufferAtrPeriod);
|
||||
if(atrSl <= 0.0)
|
||||
atrSl = IctMssEntry_Atr(sym, InpConfirmTf, InpFvgAtrPeriod);
|
||||
double atrTp = IctMssEntry_Atr(sym, InpMssTpAtrTf, InpMssBufferAtrPeriod);
|
||||
if(atrTp <= 0.0)
|
||||
atrTp = atrSl;
|
||||
|
||||
// SL: cộng spread (stop trigger theo ask/bid — bù để khỏi bị quét sớm)
|
||||
// TP: trừ 2×spread (TP gần entry hơn để dễ khớp — không cần giá hit chính xác iL0/iH0)
|
||||
// Buffer SL/TP thuần spread — ổn định, không lệ thuộc ATR động
|
||||
// SL = swing + InpMssSlSpreadMult × spread (cộng ra ngoài đỉnh/đáy)
|
||||
// TP = iL0/iH0 ± InpMssTpSpreadMult × spread (chốt trước vùng cản để dễ khớp)
|
||||
const double spreadNow = MathMax(0.0,
|
||||
SymbolInfoDouble(sym, SYMBOL_ASK) -
|
||||
SymbolInfoDouble(sym, SYMBOL_BID));
|
||||
const double spreadUnit = (spreadNow > 0.0) ? spreadNow : _Point;
|
||||
|
||||
const double bufSl = ((atrSl > 0.0) ? atrSl * InpMssSlAtrMult : 100.0 * _Point) + spreadNow;
|
||||
const double bufTp = ((atrTp > 0.0) ? atrTp * InpMssTpAtrBuffer : 50.0 * _Point) + 2.0 * spreadNow;
|
||||
const double bufSl = InpMssSlSpreadMult * spreadUnit;
|
||||
const double bufTp = InpMssTpSpreadMult * spreadUnit;
|
||||
const double minRR = MathMax(1.0, InpMssMinRR);
|
||||
|
||||
// ── TP target = iL0 (BEAR) / iH0 (BULL): sóng H1, m5 chỉ để entry
|
||||
@@ -131,47 +116,54 @@ bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone,
|
||||
else if(!isBuy && iSw.hasL0)
|
||||
tpTarget = iSw.l0.price;
|
||||
|
||||
// ── Logic TP (1 ngưỡng duy nhất):
|
||||
// RR(TP@iL0/iH0) > InpMssMinRR (= 2.0) → dùng TP tại iL0/iH0 (mục tiêu sóng H1)
|
||||
// Ngược lại (RR ≤ 2.0 hoặc iL0/iH0 không khả dụng) → TP cố định = entry ± risk × InpMssMinRR (= 2R)
|
||||
if(isBuy)
|
||||
{
|
||||
slOut = swingSl - bufSl;
|
||||
const double risk = entryOut - slOut;
|
||||
if(risk <= _Point)
|
||||
return false;
|
||||
|
||||
if(tpTarget > 0.0 && tpTarget > entryOut + bufTp + _Point)
|
||||
tpOut = tpTarget - bufTp;
|
||||
else
|
||||
tpOut = entryOut + risk * minRR; // fallback nếu iH0 không có hoặc quá gần
|
||||
|
||||
const double rrAchieved = (tpOut - entryOut) / risk;
|
||||
if(rrAchieved < minRR - 0.01)
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"RR=%.2f < min %.2f (entry=%.2f SL=%.2f TP=%.2f) — chốt iH0 không đủ",
|
||||
rrAchieved, minRR, entryOut, slOut, tpOut);
|
||||
reasonOut = StringFormat("Risk≤0 (SL %.5f ≥ entry %.5f)", slOut, entryOut);
|
||||
return false;
|
||||
}
|
||||
|
||||
double tpAtIH0 = 0.0;
|
||||
double rrAtIH0 = 0.0;
|
||||
if(tpTarget > 0.0 && tpTarget > entryOut + bufTp + _Point)
|
||||
{
|
||||
tpAtIH0 = tpTarget - bufTp;
|
||||
rrAtIH0 = (tpAtIH0 - entryOut) / risk;
|
||||
}
|
||||
|
||||
if(rrAtIH0 > minRR)
|
||||
tpOut = tpAtIH0;
|
||||
else
|
||||
tpOut = entryOut + risk * minRR;
|
||||
}
|
||||
else
|
||||
{
|
||||
slOut = swingSl + bufSl;
|
||||
const double risk = slOut - entryOut;
|
||||
if(risk <= _Point)
|
||||
return false;
|
||||
|
||||
if(tpTarget > 0.0 && tpTarget < entryOut - bufTp - _Point)
|
||||
tpOut = tpTarget + bufTp;
|
||||
else
|
||||
tpOut = entryOut - risk * minRR; // fallback nếu iL0 không có hoặc quá gần
|
||||
|
||||
const double rrAchieved = (entryOut - tpOut) / risk;
|
||||
if(rrAchieved < minRR - 0.01)
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"RR=%.2f < min %.2f (entry=%.2f SL=%.2f TP=%.2f) — chốt iL0 không đủ",
|
||||
rrAchieved, minRR, entryOut, slOut, tpOut);
|
||||
reasonOut = StringFormat("Risk≤0 (SL %.5f ≤ entry %.5f)", slOut, entryOut);
|
||||
return false;
|
||||
}
|
||||
|
||||
double tpAtIL0 = 0.0;
|
||||
double rrAtIL0 = 0.0;
|
||||
if(tpTarget > 0.0 && tpTarget < entryOut - bufTp - _Point)
|
||||
{
|
||||
tpAtIL0 = tpTarget + bufTp;
|
||||
rrAtIL0 = (entryOut - tpAtIL0) / risk;
|
||||
}
|
||||
|
||||
if(rrAtIL0 > minRR)
|
||||
tpOut = tpAtIL0;
|
||||
else
|
||||
tpOut = entryOut - risk * minRR;
|
||||
}
|
||||
|
||||
entryOut = IctMssEntry_NormalizePrice(sym, entryOut);
|
||||
@@ -179,9 +171,17 @@ bool IctMssEntry_ComputeLevels(const string sym, const IctFvgZone &m5Zone,
|
||||
tpOut = IctMssEntry_NormalizePrice(sym, tpOut);
|
||||
|
||||
if(isBuy && (slOut >= entryOut - _Point || tpOut <= entryOut + _Point))
|
||||
{
|
||||
reasonOut = StringFormat("SL/TP sai phía (BUY entry=%.5f SL=%.5f TP=%.5f)",
|
||||
entryOut, slOut, tpOut);
|
||||
return false;
|
||||
}
|
||||
if(!isBuy && (slOut <= entryOut + _Point || tpOut >= entryOut - _Point))
|
||||
{
|
||||
reasonOut = StringFormat("SL/TP sai phía (SELL entry=%.5f SL=%.5f TP=%.5f)",
|
||||
entryOut, slOut, tpOut);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -267,6 +267,59 @@ void IctMss_OnEodCancel(const string sym, const ulong ticket)
|
||||
ticket, h1Id, m5Id);
|
||||
}
|
||||
|
||||
void IctMss_OnPendingTimeout(const string sym, const ulong ticket, const int hours)
|
||||
{
|
||||
const ulong m5Id = g_ictLowTf.mss.m5FvgId;
|
||||
const ulong h1Id = g_ictLowTf.mss.h1FvgId;
|
||||
|
||||
if(ticket > 0 && OrderSelect(ticket))
|
||||
g_ictMssTrade.OrderDelete(ticket);
|
||||
|
||||
IctMssEntry_MarkM5FvgUsed(sym, m5Id);
|
||||
IctMss_ResetState();
|
||||
|
||||
// Guard giống OnPositionClosed: chỉ accept H1 FVG có touch sau timeout
|
||||
g_ictMssAfterCloseGuard = TimeCurrent();
|
||||
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"Timeout %dh pending #%I64u | M5 FVG #%I64u → Used | chờ touch FVG mới",
|
||||
hours, ticket, m5Id);
|
||||
|
||||
if(InpMssLogJournal)
|
||||
PrintFormat("[ICT2026/MSS] Pending timeout %dh #%I64u | H1 #%I64u | M5 #%I64u → Used | guard=%s | reset → WAIT_FVG_TOUCH",
|
||||
hours, ticket, h1Id, m5Id,
|
||||
TimeToString(g_ictMssAfterCloseGuard, TIME_DATE | TIME_MINUTES));
|
||||
}
|
||||
|
||||
void IctMssEntry_CheckPendingTimeout(const string sym)
|
||||
{
|
||||
if(InpMssPendingExpireHours <= 0)
|
||||
return;
|
||||
if(g_ictLowTf.mss.pendingTicket == 0)
|
||||
return;
|
||||
if(g_ictLowTf.mss.pendingPlacedTime == 0)
|
||||
return;
|
||||
if(IctMssEntry_HasOpenPositionMagic(sym))
|
||||
return;
|
||||
|
||||
const ulong ticket = g_ictLowTf.mss.pendingTicket;
|
||||
if(!OrderSelect(ticket))
|
||||
{
|
||||
// Pending đã biến mất (broker expire / cancel manual) mà chưa thành position
|
||||
// → vẫn coi như timeout, reset state để chờ FVG mới
|
||||
IctMss_OnPendingTimeout(sym, 0, InpMssPendingExpireHours);
|
||||
return;
|
||||
}
|
||||
|
||||
const datetime placed = g_ictLowTf.mss.pendingPlacedTime;
|
||||
const int elapsedSec = (int)(TimeCurrent() - placed);
|
||||
const int timeoutSec = InpMssPendingExpireHours * 3600;
|
||||
if(elapsedSec < timeoutSec)
|
||||
return;
|
||||
|
||||
IctMss_OnPendingTimeout(sym, ticket, InpMssPendingExpireHours);
|
||||
}
|
||||
|
||||
void IctMssEntry_CheckEodCancel(const string sym)
|
||||
{
|
||||
if(!InpMssCancelPendingEod)
|
||||
@@ -316,13 +369,19 @@ void IctMss_OnPositionClosed(const string sym, const long reason, const double n
|
||||
IctMssEntry_MarkM5FvgUsed(sym, m5Id);
|
||||
|
||||
IctMss_ResetState();
|
||||
|
||||
// Guard: chặn pipeline pick H1 FVG có touch trước thời điểm này.
|
||||
// Phải đợi touch mới (FVG mới hoặc re-touch) thì mới setup MSS + entry mới.
|
||||
g_ictMssAfterCloseGuard = TimeCurrent();
|
||||
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"Đóng %s (net %.2f) | M5 FVG #%I64u → Used | chờ POI mới",
|
||||
"Đóng %s (net %.2f) | M5 FVG #%I64u → Used | chờ touch FVG mới",
|
||||
rt, netProfit, m5Id);
|
||||
|
||||
if(InpMssLogJournal)
|
||||
PrintFormat("[ICT2026/MSS] Close (%s, net=%.2f) | H1 #%I64u | M5 #%I64u → Used | reset → WAIT_FVG_TOUCH",
|
||||
rt, netProfit, h1Id, m5Id);
|
||||
PrintFormat("[ICT2026/MSS] Close (%s, net=%.2f) | H1 #%I64u | M5 #%I64u → Used | guard=%s | reset → WAIT_FVG_TOUCH",
|
||||
rt, netProfit, h1Id, m5Id,
|
||||
TimeToString(g_ictMssAfterCloseGuard, TIME_DATE | TIME_MINUTES));
|
||||
}
|
||||
|
||||
bool IctMssEntry_CancelTicket(const ulong ticket)
|
||||
@@ -387,6 +446,7 @@ void IctMssEntry_Update(const string sym)
|
||||
s_hadPosition = nowPosition;
|
||||
|
||||
IctMssEntry_CheckEodCancel(sym);
|
||||
IctMssEntry_CheckPendingTimeout(sym);
|
||||
|
||||
if(g_ictLowTf.mss.phase == ICT_MSS_IDLE && g_ictLowTf.mss.pendingTicket > 0)
|
||||
{
|
||||
@@ -437,10 +497,12 @@ void IctMssEntry_Update(const string sym)
|
||||
|
||||
const IctFvgZone m5 = g_ictConfirmFvgZones[mIdx];
|
||||
double entry = 0.0, sl = 0.0, tp = 0.0;
|
||||
if(!IctMssEntry_ComputeLevels(sym, m5, entry, sl, tp))
|
||||
string lvlReason = "";
|
||||
if(!IctMssEntry_ComputeLevels(sym, m5, entry, sl, tp, lvlReason))
|
||||
{
|
||||
g_ictLowTf.mss.displayReason = StringFormat("%s | chờ H0/L0 M5 cho SL",
|
||||
IctMssPhaseText(g_ictLowTf.mss.phase));
|
||||
g_ictLowTf.mss.displayReason = StringFormat("%s | %s",
|
||||
IctMssPhaseText(g_ictLowTf.mss.phase),
|
||||
lvlReason);
|
||||
IctMss_JournalEntryBlock(g_ictLowTf.mss.displayReason);
|
||||
return;
|
||||
}
|
||||
@@ -483,10 +545,11 @@ void IctMssEntry_Update(const string sym)
|
||||
return;
|
||||
}
|
||||
|
||||
g_ictLowTf.mss.pendingTicket = ticket;
|
||||
g_ictLowTf.mss.pendingEntry = entry;
|
||||
g_ictLowTf.mss.pendingSl = sl;
|
||||
g_ictLowTf.mss.pendingTp = tp;
|
||||
g_ictLowTf.mss.pendingTicket = ticket;
|
||||
g_ictLowTf.mss.pendingPlacedTime = TimeCurrent();
|
||||
g_ictLowTf.mss.pendingEntry = entry;
|
||||
g_ictLowTf.mss.pendingSl = sl;
|
||||
g_ictLowTf.mss.pendingTp = tp;
|
||||
g_ictLowTf.mss.displayReason = StringFormat("Sell/Buy limit %.2f | SL %.2f (H0/L0) | TP %.2f RR%.1f",
|
||||
entry, sl, tp, rr);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
#include <ICT2026/StructureCore.mqh>
|
||||
#include <ICT2026/Swing.mqh>
|
||||
|
||||
// Guard: sau khi 1 lệnh đóng (TP/SL/manual), pipeline KHÔNG được pick H1 FVG
|
||||
// nào có firstTouch trước thời điểm này. Phải đợi 1 touch mới (FVG mới hoặc
|
||||
// FVG cũ được re-touched) thì mới tiếp tục setup MSS + entry.
|
||||
datetime g_ictMssAfterCloseGuard = 0;
|
||||
|
||||
string IctMssPhaseText(const ENUM_ICT_MSS_PHASE ph)
|
||||
{
|
||||
switch(ph)
|
||||
@@ -143,6 +148,18 @@ bool IctMss_HasFvgPriceTouch(const string sym, IctFvgZone &h1)
|
||||
return (IctMss_GetFvgTouchTime(sym, h1) > 0);
|
||||
}
|
||||
|
||||
// Touch hợp lệ cho POI mới = touch xảy ra SAU thời điểm lệnh trước đóng.
|
||||
// Tránh pick H1 FVG đã có firstTouch từ trước khi lệnh cũ TP/SL.
|
||||
bool IctMss_HasFreshFvgTouch(const string sym, IctFvgZone &h1)
|
||||
{
|
||||
const datetime tTouch = IctMss_GetFvgTouchTime(sym, h1);
|
||||
if(tTouch <= 0)
|
||||
return false;
|
||||
if(g_ictMssAfterCloseGuard > 0 && tTouch < g_ictMssAfterCloseGuard)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IctMss_HasH1FvgRetest(const string sym, IctFvgZone &h1)
|
||||
{
|
||||
return IctMss_HasFvgPriceTouch(sym, h1);
|
||||
@@ -741,14 +758,21 @@ void IctMss_Update(const string sym)
|
||||
|
||||
g_ictLowTf.mss.h1FvgId = g_ictFvgZones[hIdx].id;
|
||||
|
||||
if(!IctMss_HasFvgPriceTouch(sym, g_ictFvgZones[hIdx]))
|
||||
if(!IctMss_HasFreshFvgTouch(sym, g_ictFvgZones[hIdx]))
|
||||
{
|
||||
const double px = SymbolInfoDouble(sym, SYMBOL_BID);
|
||||
const double dist = IctMss_DistancePriceToFvg(px, g_ictFvgZones[hIdx]);
|
||||
g_ictLowTf.mss.displayReason = StringFormat(
|
||||
"POI #%I64u [%.0f–%.0f] dist %.0f pts — chờ chạm",
|
||||
g_ictFvgZones[hIdx].id,
|
||||
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper, dist / _Point);
|
||||
const datetime tTouch = IctMss_GetFvgTouchTime(sym, g_ictFvgZones[hIdx]);
|
||||
const bool staleTouch = (tTouch > 0 && g_ictMssAfterCloseGuard > 0 &&
|
||||
tTouch < g_ictMssAfterCloseGuard);
|
||||
g_ictLowTf.mss.displayReason = staleTouch ?
|
||||
StringFormat("POI #%I64u [%.0f–%.0f] — chờ touch mới (sau lệnh trước)",
|
||||
g_ictFvgZones[hIdx].id,
|
||||
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper) :
|
||||
StringFormat("POI #%I64u [%.0f–%.0f] dist %.0f pts — chờ chạm",
|
||||
g_ictFvgZones[hIdx].id,
|
||||
g_ictFvgZones[hIdx].lower, g_ictFvgZones[hIdx].upper,
|
||||
dist / _Point);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,8 @@ void IctPanel_SetLine(const long ch, const int index, const int yOffset,
|
||||
|
||||
void IctPanel_Render(const string sym)
|
||||
{
|
||||
if(InpOnlyStatsMode)
|
||||
return;
|
||||
if(!InpDrawPanel)
|
||||
{
|
||||
IctPanel_Clear();
|
||||
|
||||
@@ -582,6 +582,14 @@ ENUM_ICT_BIAS ICT2026_GetDailyBias();
|
||||
- Hiển thị 2 dòng cuối panel: counts + WR/Ravg/Net
|
||||
- Recompute trên `OnInit` và sau mỗi `OnTradeTransaction` close
|
||||
|
||||
### v1.160 — Pending timeout 4h → reset chờ FVG mới
|
||||
|
||||
- Input: `InpMssPendingExpireHours = 4` (mặc định, 0 = không timeout)
|
||||
- Tracked `pendingPlacedTime` trong `IctMssState`
|
||||
- Mỗi tick (`IctMssEntry_CheckPendingTimeout`): nếu `TimeCurrent() - pendingPlacedTime ≥ N×3600s` hoặc order biến mất (broker expire) → `OrderDelete` + `MarkM5FvgUsed` + `IctMss_ResetState` + set `g_ictMssAfterCloseGuard`
|
||||
- Hành vi giống TP/SL close: reset về `WAIT_FVG_TOUCH`, chỉ accept H1 FVG có touch sau timeout
|
||||
- Bỏ qua nếu đang có position magic (chỉ tác động lên pending)
|
||||
|
||||
### v1.140 — Hủy pending cuối phiên Mỹ (EOD)
|
||||
|
||||
- Input: `InpMssCancelPendingEod` (true), `InpMssEodHour` (23), `InpMssEodMinute` (0) — **server time**
|
||||
@@ -721,7 +729,7 @@ ENUM_ICT_BIAS ICT2026_GetDailyBias();
|
||||
### v1.117 — MSS entry: SL = H0/L0 M5, TP min 2R
|
||||
|
||||
- Sell limit @ **lower** M5 FVG | Buy limit @ **upper**
|
||||
- SL: trên **H0** M5 (bear) / dưới **L0** M5 (bull) + `InpMssSlAtrMult`×ATR
|
||||
- SL: trên **max(H0,H1)** M5 (bear) / dưới **min(L0,L1)** M5 (bull) + `InpMssSlSpreadMult`×spread
|
||||
- TP: tối thiểu `InpMssMinRR` (mặc định 2R), không phụ thuộc swing H1
|
||||
|
||||
### v1.116 — MSS limit entry: FVG edge, SL swing CHoCH, TP trước iH0/iL0
|
||||
|
||||
@@ -225,6 +225,7 @@ struct IctMssState
|
||||
double slSwingPrice;
|
||||
bool chochLocked;
|
||||
ulong pendingTicket;
|
||||
datetime pendingPlacedTime;
|
||||
double pendingEntry;
|
||||
double pendingSl;
|
||||
double pendingTp;
|
||||
@@ -252,10 +253,11 @@ struct IctMssState
|
||||
m5FvgTime = 0;
|
||||
slSwingPrice = 0.0;
|
||||
chochLocked = false;
|
||||
pendingTicket = 0;
|
||||
pendingEntry = 0.0;
|
||||
pendingSl = 0.0;
|
||||
pendingTp = 0.0;
|
||||
pendingTicket = 0;
|
||||
pendingPlacedTime = 0;
|
||||
pendingEntry = 0.0;
|
||||
pendingSl = 0.0;
|
||||
pendingTp = 0.0;
|
||||
liveL0Price = 0.0;
|
||||
liveH0Price = 0.0;
|
||||
liveL0Time = 0;
|
||||
|
||||
Reference in New Issue
Block a user