From 0f6a9b3e6a89f2bc46d1893d5c7c9339c9516cc6 Mon Sep 17 00:00:00 2001 From: Bell Date: Sat, 16 May 2026 21:42:46 +0700 Subject: [PATCH] State EA --- Experts/MyICT.mq5 | 516 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 445 insertions(+), 71 deletions(-) diff --git a/Experts/MyICT.mq5 b/Experts/MyICT.mq5 index 7164785..eaaf3f9 100644 --- a/Experts/MyICT.mq5 +++ b/Experts/MyICT.mq5 @@ -1,11 +1,12 @@ //+------------------------------------------------------------------+ //| MyICT.mq5 | -//| Cùng 1 timeframe: BigTrend (swing 12) + SmallTrend (swing 2) | -//| HH+HL = Up | LH+LL = Down | in panel + vẽ swing | +//| Big/Small structure + State machine (NO_TREND → PULLBACK → …) | //+------------------------------------------------------------------+ #property copyright "MyICT" -#property version "1.01" -#property description "Big/Small structure trên chart TF — không đa timeframe" +#property version "1.04" +#property description "ICT states: pullback discount/premium | limit/trade tracking" + +#include //+------------------------------------------------------------------+ enum ENUM_ICT_TREND @@ -16,19 +17,43 @@ enum ENUM_ICT_TREND ICT_TREND_NEUTRAL = 3 }; +enum ENUM_ICT_STATE +{ + ICT_STATE_NO_TREND = 0, + ICT_STATE_PULLBACK = 1, + ICT_STATE_WAITING_TRIGGER = 2, + ICT_STATE_LIMIT_ORDERED = 3, + ICT_STATE_ON_TRADE = 4, + ICT_STATE_NONE = 5 +}; + //+------------------------------------------------------------------+ input group "══ Swing (nến trước/sau pivot) ══" -input int InpBigSwingRange = 12; // BigTrend -input int InpSmallSwingRange = 2; // SmallTrend +input int InpBigSwingRange = 24; +input int InpSmallSwingRange = 2; input int InpSwingLookback = 400; +input group "══ Discount / Premium (sóng Big) ══" +input double InpZoneEqPct = 50.0; // % từ đáy/đỉnh sóng → equilibrium +input double InpZoneTolPoints = 5.0; // dung sai (point) + +input group "══ Trade (limit — trigger bổ sung sau) ══" +input ulong InpMagic = 20260620; +input bool InpAutoLimitOnZone = false; // true: đặt limit khi WAITING_TRIGGER + input group "══ Hiển thị chart ══" input bool InpDrawSwings = true; input bool InpDrawTrendLines = true; +input bool InpDrawZones = true; +input uchar InpZoneFillAlpha = 68; // 0=đặc, 255=trong suốt (nhỏ=hơn=mờ vừa) +input uchar InpZoneActiveAlpha = 50; // vùng bias active (đậm hơn) input color InpClrBigHigh = clrDodgerBlue; input color InpClrBigLow = clrDeepSkyBlue; input color InpClrSmallHigh = clrOrange; input color InpClrSmallLow = clrGold; +input color InpClrDiscount = C'0,110,45'; // xanh đậm (dễ thấy trên nền đen) +input color InpClrPremium = C'150,45,55'; // đỏ burgundy +input color InpClrZoneLabel = clrWhite; input color InpClrUp = clrLime; input color InpClrDown = clrTomato; input color InpClrNeutral = clrSilver; @@ -54,19 +79,37 @@ struct TrendSnapshot bool hasH0, hasH1, hasL0, hasL1; }; +struct BigWaveZone +{ + bool valid; + double waveHigh; + double waveLow; + double equilibrium; + double discountTop; // buy: giá <= discountTop = trong discount + double premiumBottom; // sell: giá >= premiumBottom = trong premium +}; + const string OBJ_PFX = "MYICT_"; -TrendSnapshot g_big; -TrendSnapshot g_small; -datetime g_lastBar = 0; +TrendSnapshot g_big; +TrendSnapshot g_small; +BigWaveZone g_zone; +ENUM_ICT_STATE g_state = ICT_STATE_NO_TREND; +ENUM_ICT_STATE g_statePrev = ICT_STATE_NO_TREND; +bool g_biasBuy = false; +datetime g_lastBar = 0; + +CTrade g_trade; //+------------------------------------------------------------------+ long ActChart() { return ChartID(); } +ENUM_TIMEFRAMES ChartTf() { return (ENUM_TIMEFRAMES)Period(); } + //+------------------------------------------------------------------+ -ENUM_TIMEFRAMES ChartTf() +color WithAlpha(const color clr, const uchar alpha) { - return (ENUM_TIMEFRAMES)Period(); + return (color)((long)clr | ((long)alpha << 24)); } //+------------------------------------------------------------------+ @@ -93,21 +136,39 @@ string TrendText(const ENUM_ICT_TREND t) { switch(t) { - case ICT_TREND_UP: return "TREND UP (HH + HL)"; - case ICT_TREND_DOWN: return "TREND DOWN (LH + LL)"; + case ICT_TREND_UP: return "UP (HH+HL)"; + case ICT_TREND_DOWN: return "DOWN (LH+LL)"; case ICT_TREND_NEUTRAL: return "NEUTRAL"; - default: return "— (thiếu swing)"; + default: return "—"; } } //+------------------------------------------------------------------+ -color TrendColor(const ENUM_ICT_TREND t) +string StateText(const ENUM_ICT_STATE s) { - switch(t) + switch(s) { - case ICT_TREND_UP: return InpClrUp; - case ICT_TREND_DOWN: return InpClrDown; - default: return InpClrNeutral; + case ICT_STATE_NO_TREND: return "NO_TREND"; + case ICT_STATE_PULLBACK: return "PULLBACK"; + case ICT_STATE_WAITING_TRIGGER: return "WAITING_TRIGGER"; + case ICT_STATE_LIMIT_ORDERED: return "LIMIT_ORDERED"; + case ICT_STATE_ON_TRADE: return "ON_TRADE"; + case ICT_STATE_NONE: return "NONE"; + default: return "?"; + } +} + +//+------------------------------------------------------------------+ +color StateColor(const ENUM_ICT_STATE s) +{ + switch(s) + { + case ICT_STATE_WAITING_TRIGGER: return clrGold; + case ICT_STATE_LIMIT_ORDERED: return clrOrange; + case ICT_STATE_ON_TRADE: return clrLime; + case ICT_STATE_PULLBACK: return clrAqua; + case ICT_STATE_NONE: return clrGray; + default: return InpClrNeutral; } } @@ -200,12 +261,10 @@ void PickLastTwo(const SwingPoint &pts[], const int count, SwingPoint &s0, SwingPoint &s1, bool &has0, bool &has1) { has0 = has1 = false; - if(count < 1) - return; + if(count < 1) return; s0 = pts[count - 1]; has0 = true; - if(count < 2) - return; + if(count < 2) return; s1 = pts[count - 2]; has1 = true; } @@ -218,7 +277,6 @@ ENUM_ICT_TREND ClassifyTrend(const bool hasH0, const bool hasH1, { if(!hasH0 || !hasH1 || !hasL0 || !hasL1) return ICT_TREND_UNKNOWN; - if(h0.price > h1.price && l0.price > l1.price) return ICT_TREND_UP; if(h0.price < h1.price && l0.price < l1.price) @@ -235,15 +293,218 @@ bool AnalyzeLayer(const string sym, const ENUM_TIMEFRAMES tf, SwingPoint highs[], lows[]; CollectSwings(sym, tf, swingRange, highs, lows); - PickLastTwo(highs, ArraySize(highs), out.h0, out.h1, out.hasH0, out.hasH1); PickLastTwo(lows, ArraySize(lows), out.l0, out.l1, out.hasL0, out.hasL1); - out.trend = ClassifyTrend(out.hasH0, out.hasH1, out.hasL0, out.hasL1, out.h0, out.h1, out.l0, out.l1); return true; } +//+------------------------------------------------------------------+ +bool BigTrendIsClear() +{ + return (g_big.trend == ICT_TREND_UP || g_big.trend == ICT_TREND_DOWN); +} + +//+------------------------------------------------------------------+ +bool IsPullbackStructure() +{ + if(!BigTrendIsClear()) + return false; + if(g_big.trend == ICT_TREND_UP && g_small.trend == ICT_TREND_DOWN) + { + g_biasBuy = true; + return true; + } + if(g_big.trend == ICT_TREND_DOWN && g_small.trend == ICT_TREND_UP) + { + g_biasBuy = false; + return true; + } + return false; +} + +//+------------------------------------------------------------------+ +void CalcBigWaveZone() +{ + ZeroMemory(g_zone); + if(!g_big.hasH0 || !g_big.hasL0) + return; + + g_zone.waveHigh = g_big.h0.price; + g_zone.waveLow = g_big.l0.price; + if(g_zone.waveHigh < g_zone.waveLow) + { + const double t = g_zone.waveHigh; + g_zone.waveHigh = g_zone.waveLow; + g_zone.waveLow = t; + } + + const double range = g_zone.waveHigh - g_zone.waveLow; + if(range <= 0.0) + return; + + const double pct = MathMax(1.0, MathMin(99.0, InpZoneEqPct)) / 100.0; + g_zone.equilibrium = g_zone.waveLow + range * pct; + g_zone.discountTop = g_zone.equilibrium; + g_zone.premiumBottom = g_zone.equilibrium; + g_zone.valid = true; +} + +//+------------------------------------------------------------------+ +bool PriceInDiscount(const double price) +{ + if(!g_zone.valid) + return false; + const double tol = InpZoneTolPoints * _Point; + return (price <= g_zone.discountTop + tol); +} + +//+------------------------------------------------------------------+ +bool PriceInPremium(const double price) +{ + if(!g_zone.valid) + return false; + const double tol = InpZoneTolPoints * _Point; + return (price >= g_zone.premiumBottom - tol); +} + +//+------------------------------------------------------------------+ +bool HasMyPosition(const string sym) +{ + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + if(!PositionSelectByTicket(PositionGetTicket(i))) + continue; + if(PositionGetString(POSITION_SYMBOL) != sym) + continue; + if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic) + continue; + return true; + } + return false; +} + +//+------------------------------------------------------------------+ +bool HasMyPendingLimit(const string sym) +{ + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + const ulong ticket = OrderGetTicket(i); + if(ticket == 0 || !OrderSelect(ticket)) + continue; + if(OrderGetString(ORDER_SYMBOL) != sym) + continue; + if((ulong)OrderGetInteger(ORDER_MAGIC) != InpMagic) + continue; + const ENUM_ORDER_TYPE t = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); + if(t == ORDER_TYPE_BUY_LIMIT || t == ORDER_TYPE_SELL_LIMIT) + return true; + } + return false; +} + +//+------------------------------------------------------------------+ +void SetTradeFilling() +{ + const long fm = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE); + if((fm & SYMBOL_FILLING_IOC) != 0) + g_trade.SetTypeFilling(ORDER_FILLING_IOC); + else if((fm & SYMBOL_FILLING_FOK) != 0) + g_trade.SetTypeFilling(ORDER_FILLING_FOK); + else + g_trade.SetTypeFilling(ORDER_FILLING_RETURN); +} + +//+------------------------------------------------------------------+ +// Placeholder: trigger chi tiết sẽ bổ sung — tạm đặt limit tại equilibrium +bool TryPlaceLimitFromState(const string sym) +{ + if(!InpAutoLimitOnZone || g_state != ICT_STATE_WAITING_TRIGGER) + return false; + if(!g_zone.valid || HasMyPosition(sym) || HasMyPendingLimit(sym)) + return false; + + const int dig = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS); + const double px = NormalizeDouble(g_zone.equilibrium, dig); + + g_trade.SetExpertMagicNumber(InpMagic); + SetTradeFilling(); + + bool ok = false; + if(g_biasBuy) + ok = g_trade.BuyLimit(0.01, sym, px, 0.0, 0.0, ORDER_TIME_GTC, 0, "MyICT limit buy"); + else + ok = g_trade.SellLimit(0.01, sym, px, 0.0, 0.0, ORDER_TIME_GTC, 0, "MyICT limit sell"); + + if(ok) + Dbg("Đặt limit tại equilibrium (placeholder)"); + return ok; +} + +//+------------------------------------------------------------------+ +ENUM_ICT_STATE ComputeStructureState(const string sym, const ENUM_TIMEFRAMES tf) +{ + if(!BigTrendIsClear()) + return ICT_STATE_NO_TREND; + + if(!IsPullbackStructure()) + return ICT_STATE_NO_TREND; + + CalcBigWaveZone(); + + const double refPrice = iClose(sym, tf, 1); + + if(g_biasBuy) + { + if(PriceInDiscount(refPrice)) + return ICT_STATE_WAITING_TRIGGER; + return ICT_STATE_PULLBACK; + } + + if(PriceInPremium(refPrice)) + return ICT_STATE_WAITING_TRIGGER; + return ICT_STATE_PULLBACK; +} + +//+------------------------------------------------------------------+ +void UpdateStateMachine(const string sym, const ENUM_TIMEFRAMES tf) +{ + g_statePrev = g_state; + + if(HasMyPosition(sym)) + { + g_state = ICT_STATE_ON_TRADE; + return; + } + + if(HasMyPendingLimit(sym)) + { + g_state = ICT_STATE_LIMIT_ORDERED; + return; + } + + if(g_statePrev == ICT_STATE_ON_TRADE || g_statePrev == ICT_STATE_LIMIT_ORDERED) + { + g_state = ICT_STATE_NONE; + Dbg("NONE — lệnh đã đóng (SL/TP hoặc hủy)"); + return; + } + + if(g_statePrev == ICT_STATE_NONE) + { + // một nến NONE rồi quay lại đánh giá cấu trúc + } + + g_state = ComputeStructureState(sym, tf); + + if(g_state == ICT_STATE_WAITING_TRIGGER) + TryPlaceLimitFromState(sym); + + if(g_state != g_statePrev && InpDebug) + Dbg(StringFormat("State %s → %s", StateText(g_statePrev), StateText(g_state))); +} + //+------------------------------------------------------------------+ void DrawArrowTag(const string tag, const datetime t, const double price, const bool isHigh, const string label, const color clr, @@ -266,11 +527,10 @@ void DrawArrowTag(const string tag, const datetime t, const double price, const int dig = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); const double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT); const double yOff = pt * (isHigh ? (width > 1 ? 150 : 90) : (width > 1 ? -150 : -90)); - const double yLbl = NormalizeDouble(price + yOff, dig); if(ObjectFind(ch, txt) < 0) - ObjectCreate(ch, txt, OBJ_TEXT, 0, t, yLbl); - ObjectMove(ch, txt, 0, t, yLbl); + ObjectCreate(ch, txt, OBJ_TEXT, 0, t, NormalizeDouble(price + yOff, dig)); + ObjectMove(ch, txt, 0, t, NormalizeDouble(price + yOff, dig)); ObjectSetString(ch, txt, OBJPROP_TEXT, label); ObjectSetInteger(ch, txt, OBJPROP_COLOR, clr); ObjectSetInteger(ch, txt, OBJPROP_FONTSIZE, InpFontSize); @@ -299,6 +559,50 @@ void DrawSegLine(const string name, const datetime t1, const double p1, ObjectSetInteger(ch, name, OBJPROP_HIDDEN, true); } +//+------------------------------------------------------------------+ +void DrawZoneRect(const string name, const datetime t1, const datetime t2, + const double pTop, const double pBot, const color clr, + const uchar fillAlpha, const string tip) +{ + const long ch = ActChart(); + const int dig = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); + const double y1 = NormalizeDouble(MathMax(pTop, pBot), dig); + const double y2 = NormalizeDouble(MathMin(pTop, pBot), dig); + + if(ObjectFind(ch, name) < 0) + ObjectCreate(ch, name, OBJ_RECTANGLE, 0, t1, y1, t2, y2); + ObjectMove(ch, name, 0, t1, y1); + ObjectMove(ch, name, 1, t2, y2); + ObjectSetInteger(ch, name, OBJPROP_COLOR, WithAlpha(clr, fillAlpha)); + ObjectSetInteger(ch, name, OBJPROP_STYLE, STYLE_SOLID); + ObjectSetInteger(ch, name, OBJPROP_WIDTH, 1); + ObjectSetInteger(ch, name, OBJPROP_FILL, true); + ObjectSetInteger(ch, name, OBJPROP_BACK, true); + ObjectSetInteger(ch, name, OBJPROP_SELECTABLE, false); + ObjectSetInteger(ch, name, OBJPROP_HIDDEN, true); + ObjectSetString(ch, name, OBJPROP_TOOLTIP, tip); +} + +//+------------------------------------------------------------------+ +void DrawZoneTag(const string name, const datetime t, const double price, + const string text, const color clr) +{ + const long ch = ActChart(); + const int dig = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); + + if(ObjectFind(ch, name) < 0) + ObjectCreate(ch, name, OBJ_TEXT, 0, t, NormalizeDouble(price, dig)); + ObjectMove(ch, name, 0, t, NormalizeDouble(price, dig)); + ObjectSetString(ch, name, OBJPROP_TEXT, text); + ObjectSetInteger(ch, name, OBJPROP_COLOR, clr); + ObjectSetInteger(ch, name, OBJPROP_FONTSIZE, MathMax(7, InpFontSize - 1)); + ObjectSetString(ch, name, OBJPROP_FONT, "Arial"); + ObjectSetInteger(ch, name, OBJPROP_ANCHOR, ANCHOR_LEFT); + ObjectSetInteger(ch, name, OBJPROP_SELECTABLE, false); + ObjectSetInteger(ch, name, OBJPROP_HIDDEN, true); + ObjectSetInteger(ch, name, OBJPROP_BACK, false); +} + //+------------------------------------------------------------------+ void DrawLayer(const string tag, const TrendSnapshot &snap, const color clrHi, const color clrLo, const int lineWidth) @@ -307,45 +611,83 @@ void DrawLayer(const string tag, const TrendSnapshot &snap, return; if(snap.hasH0) - DrawArrowTag(tag + "_H0", snap.h0.time, snap.h0.price, true, - tag + " H0", clrHi, lineWidth); + DrawArrowTag(tag + "_H0", snap.h0.time, snap.h0.price, true, tag + " H0", clrHi, lineWidth); if(snap.hasH1) - DrawArrowTag(tag + "_H1", snap.h1.time, snap.h1.price, true, - tag + " H1", clrHi, lineWidth); + DrawArrowTag(tag + "_H1", snap.h1.time, snap.h1.price, true, tag + " H1", clrHi, lineWidth); if(snap.hasL0) - DrawArrowTag(tag + "_L0", snap.l0.time, snap.l0.price, false, - tag + " L0", clrLo, lineWidth); + DrawArrowTag(tag + "_L0", snap.l0.time, snap.l0.price, false, tag + " L0", clrLo, lineWidth); if(snap.hasL1) - DrawArrowTag(tag + "_L1", snap.l1.time, snap.l1.price, false, - tag + " L1", clrLo, lineWidth); + DrawArrowTag(tag + "_L1", snap.l1.time, snap.l1.price, false, tag + " L1", clrLo, lineWidth); if(!InpDrawTrendLines) return; - if(snap.hasH0 && snap.hasH1) - DrawSegLine(OBJ_PFX + tag + "_LNH", - snap.h1.time, snap.h1.price, snap.h0.time, snap.h0.price, clrHi, lineWidth); + DrawSegLine(OBJ_PFX + tag + "_LNH", snap.h1.time, snap.h1.price, + snap.h0.time, snap.h0.price, clrHi, lineWidth); if(snap.hasL0 && snap.hasL1) - DrawSegLine(OBJ_PFX + tag + "_LNL", - snap.l1.time, snap.l1.price, snap.l0.time, snap.l0.price, clrLo, lineWidth); + DrawSegLine(OBJ_PFX + tag + "_LNL", snap.l1.time, snap.l1.price, + snap.l0.time, snap.l0.price, clrLo, lineWidth); } //+------------------------------------------------------------------+ -string LayerBlock(const string title, const int swingRange, - const TrendSnapshot &snap) +void DrawZones(const string sym, const ENUM_TIMEFRAMES tf) { - string s = StringFormat("%s (swing %d): %s", - title, swingRange, TrendText(snap.trend)); + if(!InpDrawZones || !g_zone.valid) + return; + + const datetime t2 = iTime(sym, tf, 0); + const datetime t1 = iTime(sym, tf, MathMin(60, Bars(sym, tf) - 1)); + if(t1 == 0 || t2 == 0) + return; + + const bool activePullback = (g_state == ICT_STATE_PULLBACK + || g_state == ICT_STATE_WAITING_TRIGGER); + const uchar discAlpha = (activePullback && g_biasBuy) + ? InpZoneActiveAlpha : InpZoneFillAlpha; + const uchar premAlpha = (activePullback && !g_biasBuy) + ? InpZoneActiveAlpha : InpZoneFillAlpha; + + DrawZoneRect(OBJ_PFX + "Z_DISC", t1, t2, + g_zone.discountTop, g_zone.waveLow, + InpClrDiscount, discAlpha, + "Discount — nửa dưới sóng Big (Buy)"); + + DrawZoneRect(OBJ_PFX + "Z_PREM", t1, t2, + g_zone.waveHigh, g_zone.premiumBottom, + InpClrPremium, premAlpha, + "Premium — nửa trên sóng Big (Sell)"); + + DrawSegLine(OBJ_PFX + "Z_EQ", t1, g_zone.equilibrium, t2, g_zone.equilibrium, + clrGold, 1); + ObjectSetInteger(ActChart(), OBJ_PFX + "Z_EQ", OBJPROP_STYLE, STYLE_DOT); + ObjectSetInteger(ActChart(), OBJ_PFX + "Z_EQ", OBJPROP_WIDTH, 2); + + const double midDisc = (g_zone.waveLow + g_zone.discountTop) * 0.5; + const double midPrem = (g_zone.premiumBottom + g_zone.waveHigh) * 0.5; + DrawZoneTag(OBJ_PFX + "LBL_DISC", t2, midDisc, " DISCOUNT ", InpClrZoneLabel); + DrawZoneTag(OBJ_PFX + "LBL_PREM", t2, midPrem, " PREMIUM ", InpClrZoneLabel); +} + +//+------------------------------------------------------------------+ +string LayerBlock(const string title, const int swingRange, const TrendSnapshot &snap) +{ + string s = StringFormat("%s (sw %d): %s", title, swingRange, TrendText(snap.trend)); if(snap.hasH0 && snap.hasH1 && snap.hasL0 && snap.hasL1) - { - s += StringFormat("\n H0=%.2f H1=%.2f | L0=%.2f L1=%.2f", + s += StringFormat("\n H0=%.2f H1=%.2f L0=%.2f L1=%.2f", snap.h0.price, snap.h1.price, snap.l0.price, snap.l1.price); - s += StringFormat(" | HH:%s HL:%s LH:%s LL:%s", - (snap.h0.price > snap.h1.price ? "Y" : "N"), - (snap.l0.price > snap.l1.price ? "Y" : "N"), - (snap.h0.price < snap.h1.price ? "Y" : "N"), - (snap.l0.price < snap.l1.price ? "Y" : "N")); - } + return s; +} + +//+------------------------------------------------------------------+ +string StateBlock() +{ + string s = StringFormat("STATE: %s", StateText(g_state)); + if(g_state == ICT_STATE_PULLBACK || g_state == ICT_STATE_WAITING_TRIGGER) + s += StringFormat(" | Bias %s", g_biasBuy ? "BUY" : "SELL"); + if(g_zone.valid) + s += StringFormat("\n Eq=%.2f (%.0f%%) | %s", + g_zone.equilibrium, InpZoneEqPct, + g_biasBuy ? "Discount" : "Premium"); return s; } @@ -364,43 +706,46 @@ void DrawPanel(const string body) ObjectSetInteger(ch, name, OBJPROP_YDISTANCE, InpPanelY); ObjectSetInteger(ch, name, OBJPROP_FONTSIZE, MathMax(8, InpFontSize + 1)); ObjectSetString(ch, name, OBJPROP_FONT, "Consolas"); - ObjectSetInteger(ch, name, OBJPROP_COLOR, clrWhite); + ObjectSetInteger(ch, name, OBJPROP_COLOR, StateColor(g_state)); ObjectSetInteger(ch, name, OBJPROP_SELECTABLE, false); ObjectSetString(ch, name, OBJPROP_TEXT, body); } //+------------------------------------------------------------------+ -void RedrawAll(const string sym, const ENUM_TIMEFRAMES tf) +void RedrawChart(const string sym, const ENUM_TIMEFRAMES tf) { DeleteObjectsByPrefix(OBJ_PFX); - string panel = StringFormat("MyICT | %s %s\n", _Symbol, EnumToString(tf)); + string panel = StringFormat("MyICT | %s %s\n", sym, EnumToString(tf)); + panel += StateBlock() + "\n"; panel += LayerBlock("BigTrend", InpBigSwingRange, g_big) + "\n"; panel += LayerBlock("SmallTrend", InpSmallSwingRange, g_small); DrawPanel(panel); - DrawLayer("BIG", g_big, InpClrBigHigh, InpClrBigLow, 2); DrawLayer("SML", g_small, InpClrSmallHigh, InpClrSmallLow, 1); - + DrawZones(sym, tf); ChartRedraw(ActChart()); } //+------------------------------------------------------------------+ -void UpdateStructure(const string sym) +void OnNewBar(const string sym) { const ENUM_TIMEFRAMES tf = ChartTf(); AnalyzeLayer(sym, tf, InpBigSwingRange, g_big); AnalyzeLayer(sym, tf, InpSmallSwingRange, g_small); - Dbg(StringFormat("%s Big=%s Small=%s", - EnumToString(tf), TrendText(g_big.trend), TrendText(g_small.trend))); + if(g_big.hasH0 && g_big.hasL0) + CalcBigWaveZone(); - RedrawAll(sym, tf); + UpdateStateMachine(sym, tf); + RedrawChart(sym, tf); - Comment(StringFormat("MyICT %s\nBig: %s\nSmall: %s", - EnumToString(tf), + Comment(StringFormat("MyICT | %s\n%s\nBig: %s | Small: %s", + StateText(g_state), + g_biasBuy && (g_state == ICT_STATE_PULLBACK || g_state == ICT_STATE_WAITING_TRIGGER) + ? "Bias BUY" : (!g_biasBuy && IsPullbackStructure() ? "Bias SELL" : ""), TrendText(g_big.trend), TrendText(g_small.trend))); } @@ -409,9 +754,12 @@ void UpdateStructure(const string sym) int OnInit() { g_lastBar = 0; - UpdateStructure(_Symbol); - Print("[MyICT] Init | TF=", EnumToString(ChartTf()), - " Big=", InpBigSwingRange, " Small=", InpSmallSwingRange); + g_state = ICT_STATE_NO_TREND; + g_statePrev = ICT_STATE_NO_TREND; + g_trade.SetExpertMagicNumber(InpMagic); + SetTradeFilling(); + OnNewBar(_Symbol); + Print("[MyICT] Init state=", StateText(g_state)); return INIT_SUCCEEDED; } @@ -422,6 +770,31 @@ void OnDeinit(const int reason) Comment(""); } +//+------------------------------------------------------------------+ +void OnTradeTransaction(const MqlTradeTransaction &trans, + const MqlTradeRequest &request, + const MqlTradeResult &result) +{ + if(trans.type != TRADE_TRANSACTION_DEAL_ADD) + return; + + const ulong deal = trans.deal; + if(deal == 0 || !HistoryDealSelect(deal)) + return; + if(HistoryDealGetString(deal, DEAL_SYMBOL) != _Symbol) + return; + if((ulong)HistoryDealGetInteger(deal, DEAL_MAGIC) != InpMagic) + return; + + const long entry = HistoryDealGetInteger(deal, DEAL_ENTRY); + if(entry != DEAL_ENTRY_OUT) + return; + + g_state = ICT_STATE_NONE; + Dbg("OnTradeTransaction → NONE (đóng lệnh)"); + RedrawChart(_Symbol, ChartTf()); +} + //+------------------------------------------------------------------+ void OnTick() { @@ -431,9 +804,10 @@ void OnTick() if(t0 == 0 || t0 == g_lastBar) return; g_lastBar = t0; - UpdateStructure(sym); + OnNewBar(sym); } //+------------------------------------------------------------------+ -ENUM_ICT_TREND GetBigTrend() { return g_big.trend; } -ENUM_ICT_TREND GetSmallTrend() { return g_small.trend; } +ENUM_ICT_STATE GetIctState() { return g_state; } +ENUM_ICT_TREND GetBigTrend() { return g_big.trend; } +ENUM_ICT_TREND GetSmallTrend() { return g_small.trend; }