//+------------------------------------------------------------------+ //| EA_Trend_Structure_EMA50.mq5 | //| Tác giả: Bell Hyperpush | //| Mục tiêu: | //| - Xác định xu hướng theo 2 yếu tố: | //| (1) Giá nằm trên/dưới EMA50 | //| (2) Cấu trúc đỉnh-đáy HH-HL hoặc LH-LL | //| - Vẽ marker xu hướng & đỉnh đáy lên biểu đồ | //| - Có tùy chỉnh SwingDepth để điều chỉnh độ nhạy tìm swing | //+------------------------------------------------------------------+ #property strict #include CTrade trade; // ================== INPUTS ========================================= #define TrendTF PERIOD_M5 // timeframe dùng cho xu hướng (mặc định M5) // #define SwingDepth 5 // Độ sâu swing để xác định đỉnh/đáy (mặc định = 5) input int EMA_Period = 50; // Chu kỳ EMA input int LookbackBars = 20; // Số nến để quét tìm đỉnh/đáy input int SwingDepth = 5; // Độ sâu để xác định swing (mặc định = 3) input bool VisualizeEMAs = true; // Hiển thị EMA lên chart hay không // ================== ENUMS ========================================= enum TREND_RESULT { TREND_NEUTRAL = 0, TREND_UP, TREND_DOWN, TREND_SIDEWAY }; // ================== BIẾN TOÀN CỤC ================================ int handleEMA = INVALID_HANDLE; // ================== HÀM TIỆN ÍCH ================================ string TrendToStr(TREND_RESULT t) { switch(t) { case TREND_UP: return "UPTREND"; case TREND_DOWN: return "DOWNTREND"; case TREND_SIDEWAY: return "SIDEWAY"; default: return "NEUTRAL"; } } // ================================================================ // HÀM: VẼ MARKER XU HƯỚNG (vẽ text báo hướng giá trên chart) // ================================================================ void DrawTrendMarker(TREND_RESULT trend) { string name = "TrendMarker_" + TimeToString(TimeCurrent(), TIME_SECONDS); double price = iClose(_Symbol, TrendTF, 1); datetime barTime = iTime(_Symbol, TrendTF, 1); color c = clrWhite; string label = ""; if(trend == TREND_UP) { c = clrLime; label = "↑ UPTREND"; } else if(trend == TREND_DOWN) { c = clrRed; label = "↓ DOWNTREND"; } else if(trend == TREND_SIDEWAY){ c = clrYellow; label = "→ SIDEWAY"; } ObjectCreate(0, name, OBJ_TEXT, 0, barTime, price); ObjectSetString(0, name, OBJPROP_TEXT, label); ObjectSetInteger(0, name, OBJPROP_COLOR, c); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); } // ================================================================ // HÀM: VẼ MARKER ĐỈNH / ĐÁY (đánh dấu swing) // ================================================================ void DrawSwingMarker(datetime t, double price, bool isHigh) { string name = (isHigh ? "High_" : "Low_") + TimeToString(t, TIME_SECONDS); string text = isHigh ? "▲ High" : "▼ Low"; color c = isHigh ? clrRed : clrDodgerBlue; ObjectCreate(0, name, OBJ_TEXT, 0, t, price); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetInteger(0, name, OBJPROP_COLOR, c); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); } // ================================================================ // HÀM: TÌM ĐỈNH / ĐÁY GẦN NHẤT TRONG LookbackBars nến // - Sử dụng SwingDepth để quy định khoảng kiểm tra (càng lớn càng "mượt") // ================================================================ bool FindRecentSwing(double &lastHigh, datetime &timeHigh, double &lastLow, datetime &timeLow) { int bars = MathMin(LookbackBars, Bars(_Symbol, TrendTF)); if(bars < SwingDepth * 2 + 1) return false; double highPrev = 0.0, lowPrev = 0.0; datetime tHighPrev = 0, tLowPrev = 0; // Duyệt qua các nến trong vùng quan sát for(int i = SwingDepth; i < bars - SwingDepth; i++) { double h = iHigh(_Symbol, TrendTF, i); double l = iLow(_Symbol, TrendTF, i); bool isSwingHigh = true; bool isSwingLow = true; // Kiểm tra SwingDepth nến hai bên for(int j = i - SwingDepth; j <= i + SwingDepth; j++) { if(j == i) continue; if(iHigh(_Symbol, TrendTF, j) > h) isSwingHigh = false; if(iLow(_Symbol, TrendTF, j) < l) isSwingLow = false; if(!isSwingHigh && !isSwingLow) break; } // Nếu là đỉnh rõ ràng if(isSwingHigh) { highPrev = h; tHighPrev = iTime(_Symbol, TrendTF, i); DrawSwingMarker(tHighPrev, highPrev, true); // chỉ lấy đỉnh gần nhất → break break; } // Nếu là đáy rõ ràng if(isSwingLow) { lowPrev = l; tLowPrev = iTime(_Symbol, TrendTF, i); DrawSwingMarker(tLowPrev, lowPrev, false); break; } } lastHigh = highPrev; timeHigh = tHighPrev; lastLow = lowPrev; timeLow = tLowPrev; return (highPrev > 0 && lowPrev > 0); } // ================================================================ // HÀM: XÁC ĐỊNH XU HƯỚNG (dựa trên EMA và cấu trúc swing) // ================================================================ TREND_RESULT GetTrendDirection() { double ema = 0.0; double emaBuf[]; // Lấy giá trị EMA gần nhất if(handleEMA != INVALID_HANDLE) if(CopyBuffer(handleEMA, 0, 1, 1, emaBuf) > 0) ema = emaBuf[0]; double price = iClose(_Symbol, TrendTF, 1); // Tìm đỉnh/đáy gần nhất double high1 = 0.0, low1 = 0.0; datetime timeHigh1 = 0, timeLow1 = 0; FindRecentSwing(high1, timeHigh1, low1, timeLow1); // ================== // Logic xu hướng cơ bản: // - Giá > EMA và đỉnh cao hơn đáy → Uptrend // - Giá < EMA và đáy thấp hơn đỉnh → Downtrend // - Còn lại → Sideway // ================== if(price > ema && high1 > low1) return TREND_UP; else if(price < ema && low1 < high1) return TREND_DOWN; else return TREND_SIDEWAY; } // ================================================================ // HÀM: VÒNG LẶP CHÍNH // ================================================================ void OnTick() { static TREND_RESULT lastTrend = TREND_NEUTRAL; TREND_RESULT trend = GetTrendDirection(); // Chỉ báo thay đổi xu hướng khi có sự chuyển trạng thái if(trend != lastTrend && trend != TREND_NEUTRAL) { PrintFormat("[TREND] %s → %s", TrendToStr(lastTrend), TrendToStr(trend)); DrawTrendMarker(trend); Comment("Current Trend: ", TrendToStr(trend)); } lastTrend = trend; } // ================================================================ // KHỞI TẠO // ================================================================ int OnInit() { // Tạo handle EMA handleEMA = iMA(_Symbol, TrendTF, EMA_Period, 0, MODE_EMA, PRICE_CLOSE); if(handleEMA == INVALID_HANDLE) { Print("Error creating EMA handle: ", GetLastError()); return(INIT_FAILED); } if(VisualizeEMAs) ChartIndicatorAdd(ChartID(), 0, handleEMA); Print("EA Initialized."); return(INIT_SUCCEEDED); } // ================================================================ // GIẢI PHÓNG // ================================================================ void OnDeinit(const int reason) { if(handleEMA != INVALID_HANDLE) IndicatorRelease(handleEMA); Comment(""); }