This commit is contained in:
Bell
2026-05-16 21:42:46 +07:00
parent 2b335a8c42
commit 0f6a9b3e6a
+445 -71
View File
@@ -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 <Trade/Trade.mqh>
//+------------------------------------------------------------------+
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; }