Files
Bell-PriceActionWithEma-EA/Experts/XAU_H1_M5_Pullback.mq5
T
2025-12-20 05:28:49 +07:00

333 lines
10 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.
//+------------------------------------------------------------------+
//| ICT Early Trend Detection EA |
//| Structure + Early Key-Level Break (MSS-style) |
//| Clean Architecture Meaningful Naming |
//| Author: Bell CW |
//+------------------------------------------------------------------+
#property strict
//====================================================
// INPUT PARAMETERS
//====================================================
input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H1; // Timeframe xác định trend
input ENUM_TIMEFRAMES TriggerTimeframe = PERIOD_M5; // Timeframe kích hoạt logic
input int SwingDetectionRange = 2; // Số nến trái/phải xác định swing
input int StructureScanLookbackBars = 150; // Số nến quét structure
//====================================================
// DATA STRUCTURE
//====================================================
struct TrendState
{
int trendDirection; // 1 = uptrend, -1 = downtrend, 0 = neutral
double latestSwingHigh; // Swing High mới nhất
double previousSwingHigh; // Swing High trước đó
double latestSwingLow; // Swing Low mới nhất
double previousSwingLow; // Swing Low trước đó
int latestSwingHighIndex;
int previousSwingHighIndex;
int latestSwingLowIndex;
int previousSwingLowIndex;
double currentKeyLevel; // Key level theo ICT structure
bool earlyFlipUsedThisBar; // Đã dùng early flip trong HTF bar này chưa
datetime lastStructureUpdateTime; // Thời gian HTF bar cuối cùng đã xử lý
};
//====================================================
// GLOBAL STATE
//====================================================
TrendState g_TrendState;
datetime g_LastTriggerBarTime = 0;
//====================================================
// INITIALIZATION
//====================================================
int OnInit()
{
ZeroMemory(g_TrendState);
g_TrendState.trendDirection = 0;
g_TrendState.lastStructureUpdateTime = 0;
Print("ICT Early Trend Detection EA initialized successfully");
return INIT_SUCCEEDED;
}
//====================================================
// BAR UTILITY
//====================================================
bool IsNewBarFormed(ENUM_TIMEFRAMES timeframe, datetime &lastBarTime)
{
datetime currentBarTime = iTime(_Symbol, timeframe, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//====================================================
// SWING DETECTION (GENERIC)
//====================================================
bool IsSwingHighAtBar(
ENUM_TIMEFRAMES timeframe,
int barIndex
)
{
double highPrice = iHigh(_Symbol, timeframe, barIndex);
for(int offset = 1; offset <= SwingDetectionRange; offset++)
{
if(iHigh(_Symbol, timeframe, barIndex - offset) >= highPrice) return false;
if(iHigh(_Symbol, timeframe, barIndex + offset) >= highPrice) return false;
}
return true;
}
bool IsSwingLowAtBar(
ENUM_TIMEFRAMES timeframe,
int barIndex
)
{
double lowPrice = iLow(_Symbol, timeframe, barIndex);
for(int offset = 1; offset <= SwingDetectionRange; offset++)
{
if(iLow(_Symbol, timeframe, barIndex - offset) <= lowPrice) return false;
if(iLow(_Symbol, timeframe, barIndex + offset) <= lowPrice) return false;
}
return true;
}
//====================================================
// STRUCTURE VALIDATION
//====================================================
bool HasConfirmedMarketStructure(const TrendState &state)
{
return (
state.latestSwingHigh > 0 &&
state.previousSwingHigh > 0 &&
state.latestSwingLow > 0 &&
state.previousSwingLow > 0
);
}
//====================================================
// STRUCTURE UPDATE (TIMEFRAME AGNOSTIC)
//====================================================
void UpdateMarketStructure(
ENUM_TIMEFRAMES timeframe,
TrendState &state
)
{
datetime currentStructureBarTime = iTime(_Symbol, timeframe, 0);
if(currentStructureBarTime == state.lastStructureUpdateTime)
return;
state.lastStructureUpdateTime = currentStructureBarTime;
state.earlyFlipUsedThisBar = false;
double detectedSwingHighs[2];
double detectedSwingLows[2];
int detectedSwingHighIndices[2];
int detectedSwingLowIndices[2];
int highCount = 0;
int lowCount = 0;
for(int barIndex = SwingDetectionRange + 1;
barIndex < StructureScanLookbackBars;
barIndex++)
{
if(highCount < 2 && IsSwingHighAtBar(timeframe, barIndex))
{
detectedSwingHighs[highCount] = iHigh(_Symbol, timeframe, barIndex);
detectedSwingHighIndices[highCount] = barIndex;
highCount++;
}
if(lowCount < 2 && IsSwingLowAtBar(timeframe, barIndex))
{
detectedSwingLows[lowCount] = iLow(_Symbol, timeframe, barIndex);
detectedSwingLowIndices[lowCount] = barIndex;
lowCount++;
}
if(highCount == 2 && lowCount == 2)
break;
}
if(highCount < 2 || lowCount < 2)
return;
state.latestSwingHigh = detectedSwingHighs[0];
state.previousSwingHigh = detectedSwingHighs[1];
state.latestSwingLow = detectedSwingLows[0];
state.previousSwingLow = detectedSwingLows[1];
state.latestSwingHighIndex = detectedSwingHighIndices[0];
state.previousSwingHighIndex = detectedSwingHighIndices[1];
state.latestSwingLowIndex = detectedSwingLowIndices[0];
state.previousSwingLowIndex = detectedSwingLowIndices[1];
// ===== STRUCTURE INTERPRETATION =====
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;
}
}
//====================================================
// EARLY TREND FLIP (MSS-STYLE)
//====================================================
void CheckForEarlyTrendFlip(
TrendState &state,
double currentPrice
)
{
if(!HasConfirmedMarketStructure(state)) return;
if(state.earlyFlipUsedThisBar) return;
if(state.trendDirection == 1 &&
currentPrice < state.currentKeyLevel)
{
state.trendDirection = -1;
state.currentKeyLevel = state.latestSwingHigh;
state.earlyFlipUsedThisBar = true;
}
else if(state.trendDirection == -1 &&
currentPrice > state.currentKeyLevel)
{
state.trendDirection = 1;
state.currentKeyLevel = state.latestSwingLow;
state.earlyFlipUsedThisBar = true;
}
}
//====================================================
// DRAWING UTILITIES
//====================================================
void DrawSwingLabel(
string objectName,
string labelText,
ENUM_TIMEFRAMES timeframe,
int barIndex,
double price,
color labelColor
)
{
if(barIndex < 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);
ObjectSetString (0, objectName, OBJPROP_TEXT, labelText);
}
void DrawMarketStructure(
ENUM_TIMEFRAMES timeframe,
const TrendState &state
)
{
DrawSwingLabel("SWING_HIGH_LATEST", "H0", timeframe,
state.latestSwingHighIndex,
state.latestSwingHigh, clrLime);
DrawSwingLabel("SWING_HIGH_PREVIOUS", "H1", timeframe,
state.previousSwingHighIndex,
state.previousSwingHigh, clrGreen);
DrawSwingLabel("SWING_LOW_LATEST", "L0", timeframe,
state.latestSwingLowIndex,
state.latestSwingLow, clrRed);
DrawSwingLabel("SWING_LOW_PREVIOUS", "L1", timeframe,
state.previousSwingLowIndex,
state.previousSwingLow, clrMaroon);
}
void DrawTrendSummaryLabel(const TrendState &state)
{
string labelName = "TREND_SUMMARY";
ObjectDelete(0, labelName);
string labelText = "TREND: NEUTRAL";
color labelColor = clrGray;
if(state.trendDirection == 1)
{
labelText = StringFormat(
"UPTREND - Key Level: %.2f%s",
state.currentKeyLevel,
state.earlyFlipUsedThisBar ? " -> EARLY FLIP" : ""
);
labelColor = state.earlyFlipUsedThisBar ? clrOrange : clrLime;
}
else if(state.trendDirection == -1)
{
labelText = StringFormat(
"DOWNTREND - Key Level: %.2f%s",
state.currentKeyLevel,
state.earlyFlipUsedThisBar ? " -> EARLY FLIP" : ""
);
labelColor = state.earlyFlipUsedThisBar ? clrOrange : clrRed;
}
ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20);
ObjectSetInteger(0, labelName, OBJPROP_COLOR, labelColor);
ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 9);
ObjectSetString (0, labelName, OBJPROP_TEXT, labelText);
}
//====================================================
// MAIN LOOP
//====================================================
void OnTick()
{
if(!IsNewBarFormed(TriggerTimeframe, g_LastTriggerBarTime))
return;
UpdateMarketStructure(TrendTimeframe, g_TrendState);
double currentBidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
CheckForEarlyTrendFlip(g_TrendState, currentBidPrice);
DrawMarketStructure(TrendTimeframe, g_TrendState);
DrawTrendSummaryLabel(g_TrendState);
// === Entry logic sẽ gắn tại đây ===
}
//+------------------------------------------------------------------+