This commit is contained in:
Bell
2025-12-24 05:37:38 +07:00
parent 537e67bdaf
commit 73ca47a0cb
2 changed files with 596 additions and 187 deletions
+592 -185
View File
@@ -6,6 +6,9 @@
//+------------------------------------------------------------------+
#property strict
#define OBJ_OB_RECT "OB_RECT"
#define OBJ_OB_LABEL "OB_LABEL"
//====================================================
// DEBUG CONFIG
//====================================================
@@ -17,6 +20,28 @@ input bool Debug_TriggerTF = false;
input bool Debug_Order = true;
input bool Debug_DrawOnChart = true;
input int TriggerMaxScanBars = 30;
enum ENUM_OB_STATE
{
OB_NONE = 0, // chưa có OB nào
OB_CANDIDATE, // tìm được OB hợp lệ, CHƯA CHẠM
OB_TOUCHED, // đã chạm (mitigated), chờ trigger
OB_USED, // đã trade HOẶC bị invalidate (KẾT THÚC)
};
enum ENUM_BLOCK_REASON
{
BLOCK_NONE,
BLOCK_NO_OB,
BLOCK_OB_NOT_TOUCHED,
BLOCK_BIAS_MISMATCH,
BLOCK_SESSION,
BLOCK_DAILY_LOSS
};
ENUM_BLOCK_REASON g_BlockReason = BLOCK_NONE;
enum ENUM_OB_RESET_REASON
{
OB_RESET_NONE,
@@ -127,10 +152,7 @@ struct OBWatchState
{
int direction; // 1 = buy, -1 = sell
bool active; // đang theo dõi OB
bool used; // OB đã được sử dụng (entry xong)
bool touched; // 🔥 GIÁ ĐÃ CHẠM OB
ENUM_OB_STATE state;
datetime touchTime; // 🔥 thời điểm chạm đầu tiên
double obHigh;
@@ -296,26 +318,216 @@ void UpdateDailyRiskState(DailyRiskState &risk)
}
}
void BootstrapHTFBias(
ENUM_TIMEFRAMES biasTf,
HTFBiasState &state)
{
// ép reset
state.lastUpdateTime = 0;
// đảm bảo đủ data
if (Bars(_Symbol, biasTf) < 5)
return;
UpdateHTFBias(biasTf, state);
}
void BootstrapMarketStructure(
ENUM_TIMEFRAMES tf,
TrendState &state)
{
ZeroMemory(state);
state.trendDirection = 0;
state.lastStructureUpdateTime = 0;
int maxBar =
MathMin(StructureScanLookbackBars,
Bars(_Symbol, tf) - SwingDetectionRange - 2);
double highs[2], lows[2];
int hiIdx[2], loIdx[2];
int hc = 0, lc = 0;
for (int i = SwingDetectionRange + 1; i <= maxBar; i++)
{
if (hc < 2 && IsSwingHighAtBar(tf, i))
{
highs[hc] = iHigh(_Symbol, tf, i);
hiIdx[hc] = i;
hc++;
}
if (lc < 2 && IsSwingLowAtBar(tf, i))
{
lows[lc] = iLow(_Symbol, tf, i);
loIdx[lc] = i;
lc++;
}
if (hc == 2 && lc == 2)
break;
}
if (hc < 2 || lc < 2)
return;
state.latestSwingHigh = highs[0];
state.previousSwingHigh = highs[1];
state.latestSwingLow = lows[0];
state.previousSwingLow = lows[1];
state.latestSwingHighIndex = hiIdx[0];
state.previousSwingHighIndex = hiIdx[1];
state.latestSwingLowIndex = loIdx[0];
state.previousSwingLowIndex = loIdx[1];
// interpret structure
if (state.previousSwingHigh < state.latestSwingHigh &&
state.previousSwingLow < state.latestSwingLow)
{
state.trendDirection = 1;
state.currentKeyLevel = state.latestSwingLow;
}
else if (state.previousSwingHigh > state.latestSwingHigh &&
state.previousSwingLow > state.latestSwingLow)
{
state.trendDirection = -1;
state.currentKeyLevel = state.latestSwingHigh;
}
}
bool BootstrapOBCandidate(
ENUM_TIMEFRAMES tf,
const TrendState &trend,
OBWatchState &ob)
{
double h, l;
int idx;
if (!FindTrendOrderBlock(tf, trend, h, l, idx))
return false;
ob.state = OB_CANDIDATE; // candidate
ob.direction = trend.trendDirection;
ob.obHigh = h;
ob.obLow = l;
ob.createdTime = iTime(_Symbol, tf, idx);
ob.barsAlive = 0;
ob.triggerStartBar = 1;
return true;
}
void DrawInitialContext()
{
DrawMarketStructure(TrendTimeframe, g_TrendState);
DrawTrendSummaryLabel(g_TrendState);
DrawEAStateOverlay();
// OB candidate (chưa chạm)
if (g_OBWatch.obHigh > 0 && g_OBWatch.state == OB_CANDIDATE)
DrawCandidateOB(g_OBWatch);
}
//====================================================
// INITIALIZATION
//====================================================
int OnInit()
{
// ===============================
// 0️⃣ RESET TOÀN BỘ STATE
// ===============================
ZeroMemory(g_TrendState);
g_TrendState.trendDirection = 0;
g_TrendState.lastStructureUpdateTime = 0;
ZeroMemory(g_HTFBias);
ZeroMemory(g_OBWatch);
ZeroMemory(g_TriggerTF);
ZeroMemory(g_DailyRisk);
g_HTFBias.bias = HTF_BIAS_NONE;
g_HTFBias.rangeHigh = 0;
g_HTFBias.rangeLow = 0;
g_HTFBias.lastUpdateTime = 0;
g_EAStep = EA_STEP_IDLE;
g_LastTriggerBarTime = 0;
g_OBWatch.active = false;
// ===============================
// 1️⃣ ENSURE HISTORY READY
// ===============================
if (Bars(_Symbol, TrendTimeframe) < StructureScanLookbackBars + 10 ||
Bars(_Symbol, BiasTimeframe) < 5)
{
Print("⏳ Not enough historical data to initialize EA");
return INIT_FAILED;
}
// ===============================
// 2️⃣ BOOTSTRAP HTF BIAS
// ===============================
BootstrapHTFBias(BiasTimeframe, g_HTFBias);
if (Debug_HTFBias)
PrintFormat("INIT | HTF Bias = %d", g_HTFBias.bias);
// ===============================
// 3️⃣ BOOTSTRAP TREND STRUCTURE
// ===============================
BootstrapMarketStructure(TrendTimeframe, g_TrendState);
if (!HasConfirmedMarketStructure(g_TrendState))
{
Print("⚠ INIT | Market structure not confirmed yet");
return INIT_SUCCEEDED; // vẫn cho EA chạy, nhưng không trade
}
if (Debug_Trend)
{
PrintFormat(
"INIT | Trend=%d | H0=%.2f | H1=%.2f | L0=%.2f | L1=%.2f",
g_TrendState.trendDirection,
g_TrendState.latestSwingHigh,
g_TrendState.previousSwingHigh,
g_TrendState.latestSwingLow,
g_TrendState.previousSwingLow
);
}
// ===============================
// 4️⃣ BOOTSTRAP OB CANDIDATE
// ===============================
if (IsHTFBiasAligned(g_HTFBias.bias, g_TrendState.trendDirection))
{
bool obFound = BootstrapOBCandidate(
TrendTimeframe,
g_TrendState,
g_OBWatch);
if (obFound)
{
g_EAStep = EA_STEP_WAIT_OB;
if (Debug_OB)
PrintFormat(
"INIT | OB Candidate [%0.2f - %0.2f]",
g_OBWatch.obLow,
g_OBWatch.obHigh
);
}
else
{
if (Debug_OB)
Print("INIT | No valid OB candidate found");
}
}
else
{
if (Debug_HTFBias)
Print("INIT | Bias not aligned with trend → skip OB bootstrap");
}
Print("✅ ICT Early Trend Detection EA initialized (BOOTSTRAPPED)");
DrawInitialContext();
Print("ICT Early Trend Detection EA initialized successfully");
return INIT_SUCCEEDED;
}
//====================================================
// BAR UTILITY
//====================================================
@@ -556,72 +768,90 @@ bool FindTrendOrderBlock(
double &obLow,
int &obCandleIndex)
{
obHigh = 0;
obLow = 0;
obCandleIndex = -1;
obHigh = 0;
obLow = 0;
obCandleIndex = -1;
// ===== UP TREND =====
if (trend.trendDirection == 1)
{
// Bắt đầu từ swing low gần nhất (key level)
for (int i = trend.latestSwingLowIndex - 1; i >= 1; i--)
// ===== XÁC ĐỊNH VÙNG QUÉT =====
int scanFrom, scanTo;
if (trend.trendDirection == 1)
{
double open = iOpen(_Symbol, timeframe, i);
double close = iClose(_Symbol, timeframe, i);
// Nến giảm
if (close < open)
{
obHigh = iHigh(_Symbol, timeframe, i);
obLow = iLow(_Symbol, timeframe, i);
obCandleIndex = i;
double range = MathAbs(obHigh - obLow);
double minRange = 10 * _Point;
if (range < minRange)
continue;
return true;
}
// Uptrend: OB phải nằm giữa L1 -> L0
scanFrom = trend.latestSwingLowIndex - 1;
scanTo = trend.previousSwingLowIndex + 1;
}
}
// ===== DOWN TREND =====
if (trend.trendDirection == -1)
{
// Bắt đầu từ swing high gần nhất (key level)
for (int i = trend.latestSwingHighIndex - 1; i >= 1; i--)
else if (trend.trendDirection == -1)
{
double open = iOpen(_Symbol, timeframe, i);
double close = iClose(_Symbol, timeframe, i);
// Nến tăng
if (close > open)
{
obHigh = iHigh(_Symbol, timeframe, i);
obLow = iLow(_Symbol, timeframe, i);
obCandleIndex = i;
double range = MathAbs(obHigh - obLow);
double minRange = 10 * _Point;
if (range < minRange)
continue;
return true;
}
// Downtrend: OB phải nằm giữa H1 -> H0
scanFrom = trend.latestSwingHighIndex - 1;
scanTo = trend.previousSwingHighIndex + 1;
}
}
else
return false;
return false;
scanFrom = MathMax(scanFrom, 1);
scanTo = MathMax(scanTo, 1);
// ===== QUÉT OB =====
for (int i = scanFrom; i >= scanTo; i--)
{
double open = iOpen(_Symbol, timeframe, i);
double close = iClose(_Symbol, timeframe, i);
// ===== NẾN NGƯỢC TREND =====
bool isBear = close < open;
bool isBull = close > open;
if ((trend.trendDirection == 1 && !isBear) ||
(trend.trendDirection == -1 && !isBull))
continue;
double h = iHigh(_Symbol, timeframe, i);
double l = iLow(_Symbol, timeframe, i);
// ===== RANGE FILTER =====
if (MathAbs(h - l) < 10 * _Point)
continue;
// ===== FILTER 1: OB PHẢI NẰM TRONG CON SÓNG =====
if (trend.trendDirection == 1 && l < trend.latestSwingLow)
continue;
if (trend.trendDirection == -1 && h > trend.latestSwingHigh)
continue;
// ===== FILTER 2: CHƯA BỊ MITIGATION =====
bool mitigated = false;
for (int j = i - 1; j >= 1; j--)
{
double price = iClose(_Symbol, timeframe, j);
if (HasPriceTouchedOB(trend.trendDirection, h, l, price))
{
mitigated = true;
break;
}
}
if (mitigated)
continue;
// ===== FILTER 3: CHƯA BỊ CLOSE BREAK (HTF) =====
if (IsOBCloseBreak(trend.trendDirection, h, l))
continue;
// ===== OK → OB HỢP LỆ =====
obHigh = h;
obLow = l;
obCandleIndex = i;
return true;
}
return false;
}
void ResetOBWatch()
{
g_OBWatch.active = false;
g_OBWatch.used = false;
g_OBWatch.touched = false;
g_OBWatch.state = OB_NONE;
g_OBWatch.touchTime = 0;
g_OBWatch.direction = 0;
@@ -691,7 +921,7 @@ void UpdateTriggerTFStructure(
bool foundLow = false;
for (int i = startBar + SwingDetectionRange;
i < startBar + StructureScanLookbackBars;
i < startBar + TriggerMaxScanBars;
i++)
{
if (!foundHigh && IsSwingHighAtBar(tf, i))
@@ -735,41 +965,35 @@ void UpdateTriggerTFStructure(
}
}
void TryActivateOBWatch(
void CheckOBTouchAndActivate(
const TrendState &trend,
double currentPrice)
{
if (g_OBWatch.used)
return;
double obHigh, obLow;
int obIndex;
// Chỉ xử lý khi có OB candidate đang chờ active
if (g_OBWatch.state != OB_CANDIDATE) return;
if (!FindTrendOrderBlock(TrendTimeframe,
trend,
obHigh,
obLow,
obIndex))
return;
// Kiểm tra giá chạm OB đang theo dõi
if (!HasPriceTouchedOB(
g_OBWatch.direction,
g_OBWatch.obHigh,
g_OBWatch.obLow,
currentPrice))
{
g_BlockReason = BLOCK_OB_NOT_TOUCHED;
return;
}
g_OBWatch.active = true;
g_OBWatch.touched = false;
g_OBWatch.used = false;
// ===== OB BỊ MITIGATE =====
MarkOBAsTouched(g_OBWatch, TriggerTimeframe);
ResetTriggerTFStructure();
g_OBWatch.direction = trend.trendDirection;
g_OBWatch.obHigh = obHigh;
g_OBWatch.obLow = obLow;
g_OBWatch.createdTime = iTime(_Symbol, TriggerTimeframe, 0);
g_OBWatch.barsAlive = 0;
g_OBWatch.triggerStartBar = 1;
// Debug (optional)
PrintFormat(
"OB WATCH ACTIVATED | dir=%d | OB[%.2f - %.2f]",
g_OBWatch.direction,
g_OBWatch.obLow,
g_OBWatch.obHigh);
if (Debug_OB)
{
PrintFormat(
"🟦 OB MITIGATED | dir=%d | OB[%.2f - %.2f]",
g_OBWatch.direction,
g_OBWatch.obLow,
g_OBWatch.obHigh);
}
}
void HandleOBWatching(
@@ -790,7 +1014,7 @@ void HandleOBWatching(
g_OBWatch.obLow))
{
g_LastOBResetReason = OB_RESET_CLOSE_BREAK; // DEBUG
ResetOBWatch();
MarkOBAsUsed(); // đánh dấu đã dùng
return;
}
@@ -801,7 +1025,7 @@ void HandleOBWatching(
currentPrice))
{
g_LastOBResetReason = OB_RESET_INVALIDATED; // DEBUG
ResetOBWatch();
MarkOBAsUsed(); // đánh dấu đã dùng
return;
}
@@ -822,21 +1046,6 @@ void HandleOBWatching(
return;
}
// DEBUG: Touch -> Draw on chart
if(!g_OBWatch.touched &&
HasPriceTouchedOB(g_OBWatch.direction,
g_OBWatch.obHigh,
g_OBWatch.obLow,
currentPrice))
{
g_OBWatch.touched = true;
g_OBWatch.touchTime = iTime(_Symbol, TriggerTimeframe, 1);
if(Debug_OB)
Print("🟦 OB TOUCHED");
}
// =================================================
// 🔥 TRIGGER TF STRUCTURE LOGIC (CHỖ QUAN TRỌNG)
// =================================================
@@ -875,8 +1084,8 @@ void HandleOBWatching(
bool IsOBExpired(const OBWatchState &ob)
{
if (!ob.active)
return true;
if (ob.createdTime <= 0)
return false;
int aliveSec = (int)(TimeCurrent() - ob.createdTime);
return aliveSec > OBMaxAliveMinutes * 60;
@@ -884,16 +1093,45 @@ bool IsOBExpired(const OBWatchState &ob)
void MarkOBAsUsed()
{
g_OBWatch.used = true;
g_OBWatch.active = false;
g_OBWatch.state = OB_USED;
}
void MarkOBAsTouched(
OBWatchState &ob,
ENUM_TIMEFRAMES triggerTf)
{
if (ob.state != OB_CANDIDATE) return;
ob.state = OB_TOUCHED;
// Thời điểm chạm đầu tiên (bar đã đóng)
ob.touchTime = iTime(_Symbol, triggerTf, 1);
// Reset trigger waiting
ob.barsAlive = 0;
ob.triggerStartBar = 1;
// Reset trigger TF structure
ResetTriggerTFStructure();
if (Debug_OB)
{
PrintFormat(
"🟦 OB TOUCHED | dir=%d | OB[%.2f - %.2f]",
ob.direction,
ob.obLow,
ob.obHigh
);
}
}
bool IsOBCloseBreak(
int direction,
double obHigh,
double obLow)
{
double close = iClose(_Symbol, TriggerTimeframe, 1);
double close = iClose(_Symbol, TrendTimeframe, 1);
if (direction == 1) // buy OB
return close < obLow;
@@ -1049,82 +1287,157 @@ void ExecuteOrder(const OrderPlan &plan)
}
}
bool IsOBCandidateStillValid(
const OBWatchState &ob,
const TrendState &trend)
{
// OB phải cùng direction
if (ob.direction != trend.trendDirection)
return false;
// OB phải nằm trong con sóng hiện tại
if (trend.trendDirection == 1) // uptrend
{
if (ob.obLow < trend.previousSwingLow ||
ob.obLow > trend.latestSwingLow)
return false;
}
else // downtrend
{
if (ob.obHigh > trend.previousSwingHigh ||
ob.obHigh < trend.latestSwingHigh)
return false;
}
return true;
}
void OnTick()
{
if (!IsNewBarFormed(TriggerTimeframe, g_LastTriggerBarTime))
return;
// ===== RUN ON NEW BAR (TRIGGER TF) =====
if (!IsNewBarFormed(TriggerTimeframe, g_LastTriggerBarTime))
return;
// STEP HOLD LOGIC (DEBUG PURPOSE)
if(g_EAStep == EA_STEP_ORDER_SENT)
{
int currentBar = Bars(_Symbol, TriggerTimeframe);
// =============================
// STEP HOLD AFTER ORDER SENT
// =============================
if (g_EAStep == EA_STEP_ORDER_SENT)
{
int currentBar = Bars(_Symbol, TriggerTimeframe);
if (currentBar <= g_OrderSentBarIndex)
{
DrawEAStateOverlay();
return;
}
g_OrderSentBarIndex = -1;
}
// giữ STEP_ORDER_SENT cho tới khi sang bar mới
if(currentBar <= g_OrderSentBarIndex) {
DrawEAStateOverlay(); // DEBUG
g_EAStep = EA_STEP_IDLE;
// =============================
// SESSION & RISK GUARD
// =============================
if (!IsTradingSessionAllowed())
{
g_BlockReason = BLOCK_SESSION;
DrawEAStateOverlay();
return;
}
// sang bar mới → cho phép reset to IDLE
g_OrderSentBarIndex = -1;
}
UpdateDailyRiskState(g_DailyRisk);
if (IsDailyLossExceeded(g_DailyRisk, MaxDailyLossPercent))
{
g_BlockReason = BLOCK_DAILY_LOSS;
DrawEAStateOverlay();
return;
}
g_EAStep = EA_STEP_IDLE; // DEBUG
// =============================
// CONTEXT UPDATE
// =============================
g_EAStep = EA_STEP_CONTEXT;
if (!IsTradingSessionAllowed())
{
DrawEAStateOverlay(); // DEBUG
return;
}
UpdateHTFBias(BiasTimeframe, g_HTFBias);
UpdateMarketStructure(TrendTimeframe, g_TrendState);
UpdateDailyRiskState(g_DailyRisk);
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
CheckForEarlyTrendFlip(g_TrendState, currentBid);
if (IsDailyLossExceeded(g_DailyRisk, MaxDailyLossPercent))
{
DrawEAStateOverlay(); // DEBUG
return;
}
if (!IsHTFBiasAligned(g_HTFBias.bias, g_TrendState.trendDirection))
{
g_BlockReason = BLOCK_BIAS_MISMATCH;
DrawEAStateOverlay();
return;
}
g_EAStep = EA_STEP_CONTEXT; // DEBUG
// =====================================================
// 🔥 PHASE 0.5: VALIDATE EXISTING OB CANDIDATE
// =====================================================
if (g_OBWatch.state == OB_CANDIDATE)
{
if (!IsOBCandidateStillValid(g_OBWatch, g_TrendState))
{
g_LastOBResetReason = OB_RESET_INVALIDATED; // hoặc OB_RESET_STRUCTURE_CHANGED
ResetOBWatch();
// ===== HTF BIAS =====
UpdateHTFBias(BiasTimeframe, g_HTFBias);
if (Debug_OB)
Print("🔴 OB INVALIDATED → STRUCTURE CHANGED");
}
}
// ===== TREND STRUCTURE =====
UpdateMarketStructure(TrendTimeframe, g_TrendState);
// =====================================================
// 🔥 PHASE 0: NO OB → FIND NEW OB CANDIDATE
// =====================================================
if (g_OBWatch.state != OB_CANDIDATE && g_OBWatch.state != OB_TOUCHED)
{
g_EAStep = EA_STEP_WAIT_OB;
double currentBidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
CheckForEarlyTrendFlip(g_TrendState, currentBidPrice);
bool found = BootstrapOBCandidate(
TrendTimeframe,
g_TrendState,
g_OBWatch
);
// ===== ENTRY FILTER =====
if (!IsHTFBiasAligned(g_HTFBias.bias, g_TrendState.trendDirection))
{
DrawEAStateOverlay(); // DEBUG
return;
}
if (found && Debug_OB)
{
PrintFormat(
"🟧 NEW OB CANDIDATE | dir=%d | [%.2f - %.2f]",
g_OBWatch.direction,
g_OBWatch.obLow,
g_OBWatch.obHigh
);
}
}
// ===== ORDER BLOCK FLOW =====
if (!g_OBWatch.active)
{
g_EAStep = EA_STEP_WAIT_OB; // DEBUG
TryActivateOBWatch(g_TrendState, currentBidPrice);
}
else
{
g_EAStep = EA_STEP_WAIT_TRIGGER; // DEBUG
HandleOBWatching(g_TrendState, currentBidPrice);
}
// =====================================================
// 🔵 PHASE 1: OB CANDIDATE → WAIT FOR TOUCH
// =====================================================
else if (g_OBWatch.state == OB_CANDIDATE)
{
g_EAStep = EA_STEP_WAIT_OB;
CheckOBTouchAndActivate(g_TrendState, currentBid);
}
// =====================================================
// 🟦 PHASE 2: OB TOUCHED → WAIT TRIGGER
// =====================================================
else if (g_OBWatch.state == OB_TOUCHED)
{
g_EAStep = EA_STEP_WAIT_TRIGGER;
HandleOBWatching(g_TrendState, currentBid);
}
DrawMarketStructure(TrendTimeframe, g_TrendState);
DrawTrendSummaryLabel(g_TrendState);
DrawEAStateOverlay();
// OB đang sống (chưa bị chạm)
if(g_OBWatch.active && !g_OBWatch.touched)
DrawActiveOB(g_OBWatch);
// =============================
// DRAW
// =============================
DrawMarketStructure(TrendTimeframe, g_TrendState);
DrawTrendSummaryLabel(g_TrendState);
DrawEAStateOverlay();
// OB đã chạm → rectangle kết thúc
if(g_OBWatch.touched)
DrawTouchedOB(g_OBWatch);
if (g_OBWatch.state == OB_CANDIDATE && g_OBWatch.state != OB_TOUCHED)
DrawCandidateOB(g_OBWatch);
if (g_OBWatch.state == OB_TOUCHED)
DrawTouchedOB(g_OBWatch);
}
void DebugPrint(bool enabled, string message)
@@ -1195,7 +1508,7 @@ void DrawEAStateOverlay()
g_HTFBias.bias),
70, clrWhite);
if (g_OBWatch.active)
if (g_OBWatch.state == OB_CANDIDATE)
{
DebugLabel("EA_OB",
StringFormat("OB [%0.2f - %0.2f] alive=%d",
@@ -1214,16 +1527,30 @@ void DrawSwingLabel(
double price,
color labelColor)
{
if (barIndex < 0)
if (barIndex < 0 || price <= 0)
return;
datetime barTime = iTime(_Symbol, timeframe, barIndex);
ObjectDelete(0, objectName);
ObjectCreate(0, objectName, OBJ_TEXT, 0, barTime, price);
ObjectSetInteger(0, objectName, OBJPROP_COLOR, labelColor);
ObjectSetInteger(0, objectName, OBJPROP_FONTSIZE, 8);
ObjectSetInteger(0, objectName, OBJPROP_FONTSIZE, 9);
ObjectSetString(0, objectName, OBJPROP_TEXT, labelText);
// ===== PIXEL OFFSET =====
int SwingLabelYOffsetPx = 20;
if (labelText == "H0" || labelText == "H1")
{
ObjectSetInteger(0, objectName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
ObjectSetInteger(0, objectName, OBJPROP_YDISTANCE, SwingLabelYOffsetPx);
}
else if (labelText == "L0" || labelText == "L1")
{
ObjectSetInteger(0, objectName, OBJPROP_ANCHOR, ANCHOR_TOP);
ObjectSetInteger(0, objectName, OBJPROP_YDISTANCE, SwingLabelYOffsetPx);
}
}
void DrawMarketStructure(
@@ -1304,9 +1631,9 @@ void DrawOBRectangle(
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
}
void DrawActiveOB(const OBWatchState &ob)
void DrawCandidateOB(const OBWatchState &ob)
{
if(!ob.active) return;
if(ob.state != OB_CANDIDATE) return;
datetime now = iTime(_Symbol, TriggerTimeframe, 0);
@@ -1315,7 +1642,7 @@ void DrawActiveOB(const OBWatchState &ob)
: clrTomato; // sell OB
DrawOBRectangle(
"OB_ACTIVE",
"OB_CANDIDATE",
ob.createdTime,
now,
ob.obHigh,
@@ -1326,7 +1653,7 @@ void DrawActiveOB(const OBWatchState &ob)
void DrawTouchedOB(const OBWatchState &ob)
{
if(!ob.touched) return;
if(ob.state != OB_TOUCHED) return;
color clr = (ob.direction == 1)
? clrDeepSkyBlue
@@ -1340,4 +1667,84 @@ void DrawTouchedOB(const OBWatchState &ob)
ob.obLow,
clr
);
}
}
void DrawOrUpdateOBRectangle(const OBWatchState &ob)
{
if (ob.state == OB_NONE)
return;
datetime endTime;
if (ob.state == OB_CANDIDATE)
endTime = iTime(_Symbol, TriggerTimeframe, 0); // kéo tới hiện tại
else
endTime = ob.touchTime; // cố định khi touched / used
color clr;
switch (ob.state)
{
case OB_CANDIDATE: clr = clrDodgerBlue; break;
case OB_TOUCHED: clr = clrDeepSkyBlue; break;
case OB_USED: clr = clrGray; break;
default: return;
}
if (!ObjectFind(0, OBJ_OB_RECT))
{
ObjectCreate(0, OBJ_OB_RECT, OBJ_RECTANGLE, 0,
ob.createdTime, ob.obHigh,
endTime, ob.obLow);
ObjectSetInteger(0, OBJ_OB_RECT, OBJPROP_BACK, true);
ObjectSetInteger(0, OBJ_OB_RECT, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, OBJ_OB_RECT, OBJPROP_FILL, true);
}
ObjectSetInteger(0, OBJ_OB_RECT, OBJPROP_COLOR, clr);
ObjectMove(0, OBJ_OB_RECT, 1, endTime, ob.obLow);
}
void DrawOrUpdateOBLabel(const OBWatchState &ob)
{
if (ob.state == OB_NONE)
return;
string text;
color clr;
switch (ob.state)
{
case OB_CANDIDATE:
text = "OB: CANDIDATE";
clr = clrDodgerBlue;
break;
case OB_TOUCHED:
text = "OB: TOUCHED";
clr = clrDeepSkyBlue;
break;
case OB_USED:
text = "OB: USED";
clr = clrGray;
break;
default:
return;
}
datetime labelTime = ob.createdTime;
double labelPrice = ob.obHigh;
if (!ObjectFind(0, OBJ_OB_LABEL))
{
ObjectCreate(0, OBJ_OB_LABEL, OBJ_TEXT, 0,
labelTime, labelPrice);
}
ObjectSetString(0, OBJ_OB_LABEL, OBJPROP_TEXT, text);
ObjectSetInteger(0, OBJ_OB_LABEL, OBJPROP_COLOR, clr);
ObjectSetInteger(0, OBJ_OB_LABEL, OBJPROP_FONTSIZE, 9);
}
+4 -2
View File
@@ -1,5 +1,7 @@
Issues: Còn nhiều bug, EA không vào lệnh
1. Draw OB -> Chưa thành công.
2. Draw label, trend ngay khi khởi tạo. -> đang bị chậm
3. OB đang được active ngay khi tìm thấy -> xem câu trả lời gần nhất của GPT để giải quyết vấn đề Logic và Draw
2. Draw label, trend ngay khi khởi tạo. -> đang bị chậm -> Đã fix
3. OB đang được active ngay khi tìm thấy -> xem câu trả lời gần nhất của GPT để giải quyết vấn đề Logic và Draw -> Done logic, còn draw