SimplerICt
This commit is contained in:
Binary file not shown.
Binary file not shown.
+6
-6
@@ -39,9 +39,9 @@ input double trailingStopStartR = 2; // Advanced moving SL: bắt đầu kích
|
||||
input double trailOffsetR = 1.5; // Advanced moving SL: khoảng cách SL so với giá hiện tại
|
||||
|
||||
// Cấu hình Swing
|
||||
input int htfSwingRange = 3; // D1/H4: chắc trend
|
||||
input int mtfSwingRange = 3; // H1: BOS rõ
|
||||
input int ltfSwingRange = 4; // M5: giảm fake MSS
|
||||
input int htfSwingRange = 1; // D1/H4: chắc trend
|
||||
input int mtfSwingRange = 2; // H1: BOS rõ
|
||||
input int ltfSwingRange = 2; // M5: giảm fake MSS
|
||||
input int MaxSwingKeep = 2; // Số đỉnh/đáy gần nhất cần lưu (bạn yêu cầu 2)
|
||||
|
||||
// Struct pending entry (single slot)
|
||||
@@ -86,9 +86,9 @@ input bool PrintToExperts = false; // không dùng — logs đã được c
|
||||
// slot 0 = HTF, 1 = MTF, 2 = LTF
|
||||
int TrendTF[3]; // 1 = up, -1 = down, 0 = sideway / unknown
|
||||
|
||||
input ENUM_TIMEFRAMES HighTF = PERIOD_H4; // Khung thời gian cao
|
||||
input ENUM_TIMEFRAMES MiddleTF = PERIOD_M15; // Khung thời gian trung bình (dùng để tìm FVG)
|
||||
input ENUM_TIMEFRAMES LowTF = PERIOD_M1; // Khung thời gian thấp (Tìm điểm vào lệnh)
|
||||
input ENUM_TIMEFRAMES HighTF = PERIOD_W1; // Khung thời gian cao
|
||||
input ENUM_TIMEFRAMES MiddleTF = PERIOD_H4; // Khung thời gian trung bình (dùng để tìm FVG)
|
||||
input ENUM_TIMEFRAMES LowTF = PERIOD_M15; // Khung thời gian thấp (Tìm điểm vào lệnh)
|
||||
|
||||
// --- multi-TF swing storage (slots) ---
|
||||
// slot 0 = HighTF, slot 1 = MiddleTF, slot 2 = LowTF
|
||||
|
||||
@@ -9,13 +9,32 @@
|
||||
//====================================================
|
||||
// INPUT PARAMETERS
|
||||
//====================================================
|
||||
|
||||
input ENUM_TIMEFRAMES BiasTimeframe = PERIOD_D1; // TF bias cao hơn Trend TF
|
||||
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
|
||||
|
||||
input int OBMaxAliveMinutes = 180; // OB sống tối đa 180 phút
|
||||
|
||||
//====================================================
|
||||
// SESSION FILTER (SERVER TIME)
|
||||
//====================================================
|
||||
|
||||
// London Session
|
||||
input int LondonStartHour = 8;
|
||||
input int LondonEndHour = 17;
|
||||
input int LondonAvoidLastMin = 60; // tránh 60 phút cuối phiên
|
||||
|
||||
// New York Session
|
||||
input int NewYorkStartHour = 13;
|
||||
input int NewYorkEndHour = 22;
|
||||
input int NewYorkAvoidLastMin = 60; // tránh 60 phút cuối phiên
|
||||
|
||||
input double RiskPercent = 1.0; // % vốn rủi ro mỗi lệnh
|
||||
input double RiskReward = 2.0; // R:R = 1:2
|
||||
|
||||
//====================================================
|
||||
// DATA STRUCTURE
|
||||
//====================================================
|
||||
@@ -45,20 +64,162 @@ struct TrendState
|
||||
//====================================================
|
||||
// GLOBAL STATE
|
||||
//====================================================
|
||||
|
||||
TrendState g_TrendState;
|
||||
datetime g_LastTriggerBarTime = 0;
|
||||
|
||||
enum ENUM_HTF_BIAS
|
||||
{
|
||||
HTF_BIAS_NONE = 0,
|
||||
HTF_BIAS_UP,
|
||||
HTF_BIAS_DOWN,
|
||||
HTF_BIAS_SIDEWAY
|
||||
};
|
||||
|
||||
struct HTFBiasState
|
||||
{
|
||||
ENUM_HTF_BIAS bias;
|
||||
|
||||
double rangeHigh;
|
||||
double rangeLow;
|
||||
|
||||
datetime lastUpdateTime;
|
||||
};
|
||||
|
||||
HTFBiasState g_HTFBias;
|
||||
|
||||
struct OBWatchState
|
||||
{
|
||||
bool active; // đang theo dõi OB
|
||||
int direction; // 1 = buy, -1 = sell
|
||||
bool used; // OB đã được sử dụng (entry xong)
|
||||
double obHigh;
|
||||
double obLow;
|
||||
datetime createdTime;
|
||||
};
|
||||
|
||||
OBWatchState g_OBWatch;
|
||||
|
||||
struct OrderPlan
|
||||
{
|
||||
bool valid;
|
||||
|
||||
int direction; // 1 = buy, -1 = sell
|
||||
double entry;
|
||||
double stopLoss;
|
||||
double takeProfit;
|
||||
double lot;
|
||||
int sourceSwingIndex; // cây nến tạo swing bị phá
|
||||
};
|
||||
|
||||
struct TriggerTFStructure
|
||||
{
|
||||
bool valid;
|
||||
|
||||
int direction; // 1 = up, -1 = down
|
||||
double lastSwingHigh;
|
||||
double lastSwingLow;
|
||||
|
||||
int lastSwingHighIndex;
|
||||
int lastSwingLowIndex;
|
||||
|
||||
double breakLevel; // level sẽ bị phá
|
||||
double newKeyLevel; // 🔥 key level mới sau break
|
||||
};
|
||||
|
||||
TriggerTFStructure g_TriggerTF;
|
||||
|
||||
void UpdateHTFBias(
|
||||
ENUM_TIMEFRAMES biasTf,
|
||||
HTFBiasState &state
|
||||
) {
|
||||
datetime tf0Time = iTime(_Symbol, biasTf, 0);
|
||||
if(tf0Time == state.lastUpdateTime)
|
||||
return;
|
||||
|
||||
state.lastUpdateTime = tf0Time;
|
||||
|
||||
// --- Bar 1 và Bar 2 đã đóng
|
||||
double b1Open = iOpen (_Symbol, biasTf, 1);
|
||||
double b1Close = iClose(_Symbol, biasTf, 1);
|
||||
double b1High = iHigh (_Symbol, biasTf, 1);
|
||||
double b1Low = iLow (_Symbol, biasTf, 1);
|
||||
|
||||
double b2High = iHigh (_Symbol, biasTf, 2);
|
||||
double b2Low = iLow (_Symbol, biasTf, 2);
|
||||
|
||||
state.rangeHigh = 0;
|
||||
state.rangeLow = 0;
|
||||
|
||||
// ===== UP BIAS =====
|
||||
if(b1Close > b2High)
|
||||
{
|
||||
state.bias = HTF_BIAS_UP;
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== DOWN BIAS =====
|
||||
if(b1Close < b2Low)
|
||||
{
|
||||
state.bias = HTF_BIAS_DOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== SIDEWAY: inside bar =====
|
||||
bool insideBar =
|
||||
b1High <= b2High &&
|
||||
b1Low >= b2Low;
|
||||
|
||||
if(insideBar)
|
||||
{
|
||||
state.bias = HTF_BIAS_SIDEWAY;
|
||||
state.rangeHigh = b2High;
|
||||
state.rangeLow = b2Low;
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== SIDEWAY: indecision =====
|
||||
bool indecision =
|
||||
b1Close < b2High &&
|
||||
b1Close > b2Low;
|
||||
|
||||
if(indecision)
|
||||
{
|
||||
state.bias = HTF_BIAS_SIDEWAY;
|
||||
state.rangeHigh = b1High;
|
||||
state.rangeLow = b1Low;
|
||||
return;
|
||||
}
|
||||
|
||||
state.bias = HTF_BIAS_NONE;
|
||||
}
|
||||
|
||||
bool IsHTFBiasAligned(
|
||||
ENUM_HTF_BIAS bias,
|
||||
int trendDirection
|
||||
)
|
||||
{
|
||||
if(bias == HTF_BIAS_UP && trendDirection == 1) return true;
|
||||
if(bias == HTF_BIAS_DOWN && trendDirection == -1) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// INITIALIZATION
|
||||
//====================================================
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
ZeroMemory(g_TrendState);
|
||||
g_TrendState.trendDirection = 0;
|
||||
g_TrendState.lastStructureUpdateTime = 0;
|
||||
|
||||
g_HTFBias.bias = HTF_BIAS_NONE;
|
||||
g_HTFBias.rangeHigh = 0;
|
||||
g_HTFBias.rangeLow = 0;
|
||||
g_HTFBias.lastUpdateTime = 0;
|
||||
|
||||
g_OBWatch.active = false;
|
||||
|
||||
Print("ICT Early Trend Detection EA initialized successfully");
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
@@ -314,11 +475,522 @@ void DrawTrendSummaryLabel(const TrendState &state)
|
||||
// MAIN LOOP
|
||||
//====================================================
|
||||
|
||||
//====================================================
|
||||
// SESSION GUARD
|
||||
//====================================================
|
||||
|
||||
bool IsWithinSession(
|
||||
int currentMinutes,
|
||||
int sessionStartHour,
|
||||
int sessionEndHour,
|
||||
int avoidLastMinutes
|
||||
)
|
||||
{
|
||||
int sessionStartMin = sessionStartHour * 60;
|
||||
int sessionEndMin = sessionEndHour * 60;
|
||||
|
||||
// Trong phiên
|
||||
if(currentMinutes < sessionStartMin ||
|
||||
currentMinutes >= sessionEndMin)
|
||||
return false;
|
||||
|
||||
// Tránh cuối phiên
|
||||
if(currentMinutes >= (sessionEndMin - avoidLastMinutes))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsTradingSessionAllowed()
|
||||
{
|
||||
datetime now = TimeCurrent();
|
||||
|
||||
MqlDateTime tm;
|
||||
TimeToStruct(now, tm);
|
||||
|
||||
int currentMinutes = tm.hour * 60 + tm.min;
|
||||
|
||||
if(IsWithinSession(currentMinutes,
|
||||
LondonStartHour,
|
||||
LondonEndHour,
|
||||
LondonAvoidLastMin))
|
||||
return true;
|
||||
|
||||
if(IsWithinSession(currentMinutes,
|
||||
NewYorkStartHour,
|
||||
NewYorkEndHour,
|
||||
NewYorkAvoidLastMin))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasPriceTouchedOB(
|
||||
int direction,
|
||||
double obHigh,
|
||||
double obLow,
|
||||
double price
|
||||
) {
|
||||
if(direction == 1) // buy
|
||||
return price <= obHigh && price >= obLow;
|
||||
|
||||
if(direction == -1) // sell
|
||||
return price >= obLow && price <= obHigh;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindTrendOrderBlock(
|
||||
ENUM_TIMEFRAMES timeframe,
|
||||
const TrendState &trend,
|
||||
double &obHigh,
|
||||
double &obLow,
|
||||
int &obCandleIndex
|
||||
)
|
||||
{
|
||||
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--)
|
||||
{
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 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--)
|
||||
{
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResetOBWatch()
|
||||
{
|
||||
g_OBWatch.active = false;
|
||||
g_OBWatch.used = false;
|
||||
g_OBWatch.direction = 0;
|
||||
g_OBWatch.obHigh = 0;
|
||||
g_OBWatch.obLow = 0;
|
||||
g_OBWatch.createdTime = 0;
|
||||
}
|
||||
|
||||
bool IsOBInvalidated(
|
||||
int direction,
|
||||
double obHigh,
|
||||
double obLow,
|
||||
double price
|
||||
) {
|
||||
if(direction == 1) // buy
|
||||
return price < obLow;
|
||||
|
||||
if(direction == -1) // sell
|
||||
return price > obHigh;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResetTriggerTFStructure()
|
||||
{
|
||||
ZeroMemory(g_TriggerTF);
|
||||
}
|
||||
|
||||
bool IsTriggerTFStructureBreak(
|
||||
const TriggerTFStructure &ts,
|
||||
int trendDirection,
|
||||
ENUM_TIMEFRAMES tf
|
||||
)
|
||||
{
|
||||
if(!ts.valid) return false;
|
||||
|
||||
double close = iClose(_Symbol, tf, 1);
|
||||
|
||||
if(trendDirection == 1)
|
||||
return close > ts.breakLevel;
|
||||
|
||||
if(trendDirection == -1)
|
||||
return close < ts.breakLevel;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateTriggerTFStructure(
|
||||
ENUM_TIMEFRAMES tf,
|
||||
TriggerTFStructure &ts
|
||||
) {
|
||||
double sh, sl;
|
||||
int shi, sli;
|
||||
|
||||
bool foundHigh = false;
|
||||
bool foundLow = false;
|
||||
|
||||
for(int i = SwingDetectionRange + 1; i < StructureScanLookbackBars; i++)
|
||||
{
|
||||
if(!foundHigh && IsSwingHighAtBar(tf, i))
|
||||
{
|
||||
sh = iHigh(_Symbol, tf, i);
|
||||
shi = i;
|
||||
foundHigh = true;
|
||||
}
|
||||
|
||||
if(!foundLow && IsSwingLowAtBar(tf, i))
|
||||
{
|
||||
sl = iLow(_Symbol, tf, i);
|
||||
sli = i;
|
||||
foundLow = true;
|
||||
}
|
||||
|
||||
if(foundHigh && foundLow) break;
|
||||
}
|
||||
|
||||
if(!foundHigh || !foundLow) return;
|
||||
|
||||
ts.lastSwingHigh = sh;
|
||||
ts.lastSwingLow = sl;
|
||||
ts.lastSwingHighIndex = shi;
|
||||
ts.lastSwingLowIndex = sli;
|
||||
|
||||
ts.direction = g_TrendState.trendDirection;
|
||||
ts.valid = true;
|
||||
|
||||
if(ts.direction == 1)
|
||||
{
|
||||
ts.breakLevel = ts.lastSwingHigh;
|
||||
ts.newKeyLevel = ts.lastSwingLow;
|
||||
}
|
||||
else
|
||||
{
|
||||
ts.breakLevel = ts.lastSwingLow;
|
||||
ts.newKeyLevel = ts.lastSwingHigh;
|
||||
}
|
||||
}
|
||||
|
||||
void TryActivateOBWatch(
|
||||
const TrendState &trend,
|
||||
double currentPrice
|
||||
) {
|
||||
if(g_OBWatch.used) return;
|
||||
double obHigh, obLow;
|
||||
int obIndex;
|
||||
|
||||
if(!FindTrendOrderBlock(TrendTimeframe,
|
||||
trend,
|
||||
obHigh,
|
||||
obLow,
|
||||
obIndex))
|
||||
return;
|
||||
|
||||
if(!HasPriceTouchedOB(trend.trendDirection,
|
||||
obHigh,
|
||||
obLow,
|
||||
currentPrice))
|
||||
return;
|
||||
|
||||
g_OBWatch.active = true;
|
||||
ResetTriggerTFStructure();
|
||||
g_OBWatch.direction = trend.trendDirection;
|
||||
g_OBWatch.obHigh = obHigh;
|
||||
g_OBWatch.obLow = obLow;
|
||||
g_OBWatch.createdTime = TimeCurrent();
|
||||
|
||||
// Debug (optional)
|
||||
PrintFormat(
|
||||
"OB WATCH ACTIVATED | dir=%d | OB[%.2f - %.2f]",
|
||||
g_OBWatch.direction,
|
||||
g_OBWatch.obLow,
|
||||
g_OBWatch.obHigh
|
||||
);
|
||||
}
|
||||
|
||||
void HandleOBWatching(
|
||||
const TrendState &trend,
|
||||
double currentPrice
|
||||
)
|
||||
{
|
||||
// 1️⃣ Bias hoặc trend không còn hợp lệ
|
||||
if(!IsHTFBiasAligned(g_HTFBias.bias, trend.trendDirection))
|
||||
{
|
||||
ResetOBWatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2️⃣ OB quá hạn
|
||||
if(IsOBExpired(g_OBWatch))
|
||||
{
|
||||
ResetOBWatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// 3️⃣ Giá đóng nến xuyên qua OB
|
||||
if(IsOBCloseBreak(g_OBWatch.direction,
|
||||
g_OBWatch.obHigh,
|
||||
g_OBWatch.obLow))
|
||||
{
|
||||
ResetOBWatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// 4️⃣ OB bị phá sâu (tick safety)
|
||||
if(IsOBInvalidated(g_OBWatch.direction,
|
||||
g_OBWatch.obHigh,
|
||||
g_OBWatch.obLow,
|
||||
currentPrice))
|
||||
{
|
||||
ResetOBWatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// =================================================
|
||||
// 🔥 TRIGGER TF STRUCTURE LOGIC (CHỖ QUAN TRỌNG)
|
||||
// =================================================
|
||||
if(!g_TriggerTF.valid)
|
||||
{
|
||||
UpdateTriggerTFStructure(TriggerTimeframe, g_TriggerTF);
|
||||
}
|
||||
|
||||
if(IsTriggerTFStructureBreak(g_TriggerTF,
|
||||
trend.trendDirection,
|
||||
TriggerTimeframe))
|
||||
{
|
||||
OrderPlan plan =
|
||||
BuildOrderPlanFromTriggerTF(g_TriggerTF,
|
||||
TriggerTimeframe);
|
||||
|
||||
if(plan.valid)
|
||||
{
|
||||
plan.lot = CalculateRiskLot(plan.entry, plan.stopLoss);
|
||||
|
||||
if(plan.lot <= 0)
|
||||
return;
|
||||
|
||||
ExecuteOrder(plan);
|
||||
|
||||
PrintFormat(
|
||||
"🎯 ENTRY CONFIRMED | dir=%d | entry=%.2f | SL=%.2f | TP=%.2f | RR=1:%.1f",
|
||||
plan.direction,
|
||||
plan.entry,
|
||||
plan.stopLoss,
|
||||
plan.takeProfit,
|
||||
RiskReward
|
||||
);
|
||||
|
||||
// === SEND ORDER Ở ĐÂY ===
|
||||
// SendMarketOrder(plan) hoặc PlacePending(plan)
|
||||
|
||||
MarkOBAsUsed();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsOBExpired(const OBWatchState &ob)
|
||||
{
|
||||
if(!ob.active)
|
||||
return true;
|
||||
|
||||
int aliveSec = (int)(TimeCurrent() - ob.createdTime);
|
||||
return aliveSec > OBMaxAliveMinutes * 60;
|
||||
}
|
||||
|
||||
void MarkOBAsUsed()
|
||||
{
|
||||
g_OBWatch.used = true;
|
||||
g_OBWatch.active = false;
|
||||
}
|
||||
|
||||
bool IsOBCloseBreak(
|
||||
int direction,
|
||||
double obHigh,
|
||||
double obLow
|
||||
) {
|
||||
double close = iClose(_Symbol, TriggerTimeframe, 1);
|
||||
|
||||
if(direction == 1) // buy OB
|
||||
return close < obLow;
|
||||
|
||||
if(direction == -1) // sell OB
|
||||
return close > obHigh;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsTriggerKeyLevelBreak(
|
||||
const TrendState &trend,
|
||||
ENUM_TIMEFRAMES triggerTf
|
||||
) {
|
||||
double close = iClose(_Symbol, triggerTf, 1);
|
||||
|
||||
if(trend.trendDirection == 1)
|
||||
return close > trend.currentKeyLevel;
|
||||
|
||||
if(trend.trendDirection == -1)
|
||||
return close < trend.currentKeyLevel;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
OrderPlan BuildOrderPlanFromTriggerTF(
|
||||
const TriggerTFStructure &ts,
|
||||
ENUM_TIMEFRAMES tf
|
||||
)
|
||||
{
|
||||
OrderPlan plan;
|
||||
ZeroMemory(plan);
|
||||
|
||||
double entryPrice = iClose(_Symbol, tf, 1);
|
||||
|
||||
plan.valid = true;
|
||||
plan.direction = ts.direction;
|
||||
plan.entry = entryPrice;
|
||||
|
||||
double riskPoints;
|
||||
|
||||
// ===== BUY =====
|
||||
if(ts.direction == 1)
|
||||
{
|
||||
plan.stopLoss = MathMin(ts.newKeyLevel, g_OBWatch.obLow);
|
||||
|
||||
riskPoints = plan.entry - plan.stopLoss;
|
||||
if(riskPoints <= 0)
|
||||
{
|
||||
plan.valid = false;
|
||||
return plan;
|
||||
}
|
||||
|
||||
plan.takeProfit = plan.entry + riskPoints * RiskReward;
|
||||
}
|
||||
// ===== SELL =====
|
||||
else
|
||||
{
|
||||
plan.stopLoss = MathMax(ts.newKeyLevel, g_OBWatch.obHigh);
|
||||
|
||||
riskPoints = plan.stopLoss - plan.entry;
|
||||
if(riskPoints <= 0)
|
||||
{
|
||||
plan.valid = false;
|
||||
return plan;
|
||||
}
|
||||
|
||||
plan.takeProfit = plan.entry - riskPoints * RiskReward;
|
||||
}
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
||||
double CalculateRiskLot(
|
||||
double entryPrice,
|
||||
double stopLossPrice
|
||||
) {
|
||||
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double riskMoney = balance * RiskPercent / 100.0;
|
||||
|
||||
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||||
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
||||
|
||||
if(tickValue <= 0 || tickSize <= 0)
|
||||
return 0;
|
||||
|
||||
double slPoints = MathAbs(entryPrice - stopLossPrice) / _Point;
|
||||
if(slPoints <= 0)
|
||||
return 0;
|
||||
|
||||
double costPerLot = slPoints * (tickValue / tickSize);
|
||||
double lot = riskMoney / costPerLot;
|
||||
|
||||
// ===== Normalize theo broker =====
|
||||
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
||||
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
lot = MathMax(minLot, MathMin(lot, maxLot));
|
||||
lot = NormalizeDouble(lot, (int)MathLog10(1.0 / lotStep));
|
||||
|
||||
return lot;
|
||||
}
|
||||
|
||||
void ExecuteOrder(const OrderPlan &plan)
|
||||
{
|
||||
if(!plan.valid) return;
|
||||
|
||||
MqlTradeRequest req;
|
||||
MqlTradeResult res;
|
||||
ZeroMemory(req);
|
||||
ZeroMemory(res);
|
||||
|
||||
req.action = TRADE_ACTION_DEAL;
|
||||
req.symbol = _Symbol;
|
||||
req.volume = plan.lot;
|
||||
req.type = plan.direction == 1 ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
|
||||
req.price = plan.direction == 1 ?
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
req.sl = plan.stopLoss;
|
||||
req.tp = plan.takeProfit;
|
||||
req.deviation= 5;
|
||||
req.magic = 202501;
|
||||
req.comment = "ICT_OB_Trigger";
|
||||
|
||||
OrderSend(req, res);
|
||||
|
||||
if(res.retcode != TRADE_RETCODE_DONE)
|
||||
{
|
||||
Print("❌ OrderSend failed: ", res.retcode);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintFormat(
|
||||
"✅ ORDER SENT | lot=%.2f | entry=%.2f | SL=%.2f | TP=%.2f",
|
||||
plan.lot,
|
||||
plan.entry,
|
||||
plan.stopLoss,
|
||||
plan.takeProfit
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewBarFormed(TriggerTimeframe, g_LastTriggerBarTime))
|
||||
return;
|
||||
|
||||
if(!IsTradingSessionAllowed())
|
||||
return;
|
||||
|
||||
// ===== HTF BIAS =====
|
||||
UpdateHTFBias(BiasTimeframe, g_HTFBias);
|
||||
|
||||
// ===== TREND STRUCTURE =====
|
||||
UpdateMarketStructure(TrendTimeframe, g_TrendState);
|
||||
|
||||
double currentBidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
@@ -327,6 +999,17 @@ void OnTick()
|
||||
DrawMarketStructure(TrendTimeframe, g_TrendState);
|
||||
DrawTrendSummaryLabel(g_TrendState);
|
||||
|
||||
// === Entry logic sẽ gắn tại đây ===
|
||||
// ===== ENTRY FILTER =====
|
||||
if(!IsHTFBiasAligned(g_HTFBias.bias,
|
||||
g_TrendState.trendDirection))
|
||||
return;
|
||||
|
||||
// ===== ORDER BLOCK FLOW =====
|
||||
if(!g_OBWatch.active) {
|
||||
TryActivateOBWatch(g_TrendState, currentBidPrice);
|
||||
} else {
|
||||
HandleOBWatching(g_TrendState, currentBidPrice);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
Reference in New Issue
Block a user