diff --git a/Experts/BellMainPriceAction.mq5 b/Experts/BellMainPriceAction.mq5 index 7eef36c..e832ba0 100644 --- a/Experts/BellMainPriceAction.mq5 +++ b/Experts/BellMainPriceAction.mq5 @@ -1,146 +1,154 @@ //+------------------------------------------------------------------+ -//| EA_Trend_EMA50_EMA200.mq5 | -//| Phiên bản: 1.0 | +//| EA_Trend_EMA50_EMA200_v1.1.mq5 | +//| Tác giả: Bell Hyperpush | //| Mục đích: | -//| - Xác định xu hướng bằng EMA50 & EMA200 (m15) | -//| - Vào lệnh trên M5 khi có tín hiệu: 2 đáy / 2 đỉnh / nến nhấn chìm | -//| - Rủi ro cố định: 1% Equity mỗi lệnh | -//| - Giới hạn tối đa 2 lệnh mở cùng lúc | -//| - Tùy chọn hiển thị EMA lên biểu đồ | +//| - Xác định xu hướng bằng EMA50 & EMA200 (theo TrendTF) | +//| - In ra khi xu hướng thay đổi, vẽ marker lên chart | +//| - Tùy chọn hiển thị EMA trên biểu đồ | //+------------------------------------------------------------------+ #property copyright "Bell Hyperpush" -#property version "1.00" +#property version "1.10" #property strict #include CTrade trade; + +#define TrendTF PERIOD_M5 // Test +#define EntryTF PERIOD_M5 // Test + int handleEMAFast = INVALID_HANDLE; int handleEMASlow = INVALID_HANDLE; // ================== INPUTS ========================================= -input ENUM_TIMEFRAMES TrendTF = PERIOD_H1; // khung thời gian dùng để lọc xu hướng -input ENUM_TIMEFRAMES EntryTF = PERIOD_M5; // khung thời gian để tìm điểm vào +// input ENUM_TIMEFRAMES TrendTF = PERIOD_M15; // khung thời gian dùng để lọc xu hướng +// input ENUM_TIMEFRAMES EntryTF = PERIOD_M5; // khung thời gian để tìm điểm vào input int EMA_Fast = 50; // EMA nhanh input int EMA_Slow = 200; // EMA chậm -input double RiskPercent = 1.0; // rủi ro % equity cho mỗi lệnh +input double RiskPercent = 1.0; // rủi ro % equity mỗi lệnh input double TP_Multiplier = 2.0; // tỉ lệ TP = Risk * multiplier -input int MaxOpenPositions = 2; // tối đa 2 lệnh mở -input double DoubleTolerancePoints = 30; // dung sai giữa 2 đáy/đỉnh -input int MinBarsBetweenDouble = 4; // khoảng cách tối thiểu giữa 2 đáy/đỉnh -input int SL_Pips_Default = 40; // SL mặc định (points) -input int Slippage = 10; // trượt giá tối đa +input int MaxOpenPositions = 2; // tối đa số lệnh mở input bool VisualizeEMAs = true; // có vẽ EMA lên chart không -input int TrendShift = 1; // shift dùng để đọc nến đóng (tránh nến đang hình thành) -input int StructureRecentOffset = 1; // offset cho nến recent khi kiểm tra cấu trúc (high1/low1) -input int StructurePrevOffset = 2; // offset cho nến trước đó khi kiểm tra cấu trúc (high2/low2) +input int TrendShift = 1; // đọc nến đóng (tránh nến đang chạy) -// ================== FUNCTIONS ===================================== - -// ----- Enums for EMA and trend result (avoid hard-coded strings) ----- +// ================== ENUMS & UTILITIES ============================== enum EMA_COND { EMA_INSUFFICIENT = 0, EMA_UPTREND, EMA_DOWNTREND, EMA_NEUTRAL }; enum TREND_RESULT { TREND_NEUTRAL = 0, TREND_UPTREND, TREND_DOWNTREND, TREND_SIDEWAY_RESULT }; -string EmaConditionToString(const int e) { - switch(e) { - case EMA_UPTREND: return "EMA Uptrend"; - case EMA_DOWNTREND: return "EMA Downtrend"; - case EMA_NEUTRAL: return "EMA Neutral"; - default: return "EMA InsufficientData"; +string TrendResultToString(const TREND_RESULT t) { + switch(t) { + case TREND_UPTREND: return "UPTREND"; + case TREND_DOWNTREND: return "DOWNTREND"; + case TREND_SIDEWAY_RESULT:return "SIDEWAY"; + default: return "NEUTRAL"; } } -string TrendResultToString(const int r) { - switch(r) { - case TREND_UPTREND: return "UPTREND"; - case TREND_DOWNTREND: return "DOWNTREND"; - case TREND_SIDEWAY_RESULT: return "SIDEWAY"; - default: return "NEUTRAL"; - } -} - -// Xác định xu hướng hiện tại -string GetTrendDirection() { - int shift = TrendShift; // tránh dùng nến đang chạy (mặc định = 1) - double emaFast = 0.0; - double emaSlow = 0.0; +// ================== XÁC ĐỊNH XU HƯỚNG ============================= +TREND_RESULT GetTrendDirection() +{ + int shift = TrendShift; + double emaFast = 0.0, emaSlow = 0.0; double buf[]; - // read EMA50 value by copying buffer from handle - if(handleEMAFast != INVALID_HANDLE) { + // --- Copy EMA buffers từ khung TrendTF --- + if(handleEMAFast != INVALID_HANDLE) if(CopyBuffer(handleEMAFast, 0, shift, 1, buf) > 0) emaFast = buf[0]; - } - - // read EMA200 value by copying buffer from handle - if(handleEMASlow != INVALID_HANDLE) { + if(handleEMASlow != INVALID_HANDLE) if(CopyBuffer(handleEMASlow, 0, shift, 1, buf) > 0) emaSlow = buf[0]; - } + double priceClose = iClose(_Symbol, TrendTF, shift); + TREND_RESULT trendEnum = TREND_NEUTRAL; - int emaEnum = EMA_INSUFFICIENT; - int trendEnum = TREND_NEUTRAL; - - // --- B2: Kiểm tra EMA (an toàn: đảm bảo dữ liệu hợp lệ) - if(priceClose <= 0.0 || emaFast <= 0.0 || emaSlow <= 0.0) { - emaEnum = EMA_INSUFFICIENT; + if(priceClose <= 0.0 || emaFast <= 0.0 || emaSlow <= 0.0) trendEnum = TREND_NEUTRAL; - } - else if(emaFast > emaSlow ) { // && priceClose > emaFast - emaEnum = EMA_UPTREND; + else if(emaFast > emaSlow) trendEnum = TREND_UPTREND; - } - else if(emaFast < emaSlow) { // && priceClose < emaFast - emaEnum = EMA_DOWNTREND; + else if(emaFast < emaSlow) trendEnum = TREND_DOWNTREND; - } - else { - emaEnum = EMA_NEUTRAL; + else trendEnum = TREND_SIDEWAY_RESULT; - } - // --- Log chi tiết (debug) - PrintFormat("[TREND DEBUG] === %s ===", TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS)); - // print the EMA periods and their current values instead of hard-coded labels - PrintFormat("EMA%d=%.5f | EMA%d=%.5f | PriceClose=%.5f", EMA_Fast, emaFast, EMA_Slow, emaSlow, priceClose); - PrintFormat("EMA: %s | => TREND: %s", EmaConditionToString(emaEnum), TrendResultToString(trendEnum)); + // --- Debug log --- + PrintFormat("[TREND DEBUG] %s | EMA%d=%.5f | EMA%d=%.5f | Close=%.5f | => %s", + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS), + EMA_Fast, emaFast, EMA_Slow, emaSlow, priceClose, + TrendResultToString(trendEnum)); - return TrendResultToString(trendEnum); + return trendEnum; } +// ================== VẼ MARKER XU HƯỚNG ============================= +void DrawTrendChangeMarker(TREND_RESULT trend) +{ + string name = "TrendChange_" + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS); + double price = iClose(_Symbol, TrendTF, 1); + datetime barTime = iTime(_Symbol, TrendTF, 1); -// ================== VÒNG LẶP CHÍNH ================================ + color c = clrWhite; + string label = ""; + + if(trend == TREND_UPTREND) { c = clrLime; label = "↑ UPTREND"; } + else if(trend == TREND_DOWNTREND){ c = clrRed; label = "↓ DOWNTREND"; } + else if(trend == TREND_SIDEWAY_RESULT) { 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_SELECTABLE, false); + ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); + // ObjectSetDouble(0, name, OBJPROP_YDISTANCE, 10); // tránh chữ đè lên nến +} + +// ================== VÒNG LẶP CHÍNH ================================= void OnTick() { - static datetime lastCheck = 0; + static TREND_RESULT lastTrend = TREND_NEUTRAL; + static datetime lastLogTime = 0; - // chỉ check 1 lần mỗi phút - if(TimeCurrent() - lastCheck >= 60) { - string trend = GetTrendDirection(); - Comment("Current Trend (", EnumToString(TrendTF), "): ", trend); - lastCheck = TimeCurrent(); + TREND_RESULT trend = GetTrendDirection(); + + // Nếu trend mới khác trend cũ → in log & đánh dấu + if(trend != lastTrend && lastTrend != TREND_NEUTRAL) + { + string msg = StringFormat("TREND CHANGED: %s → %s", + TrendResultToString(lastTrend), + TrendResultToString(trend)); + Print(msg); + Comment("Current Trend: ", TrendResultToString(trend)); + DrawTrendChangeMarker(trend); + } + + lastTrend = trend; + + // Cập nhật comment mỗi phút + if(TimeCurrent() - lastLogTime >= 60) { + Comment("Current Trend: ", TrendResultToString(trend)); + lastLogTime = TimeCurrent(); } } - // ================== KHỞI TẠO & KẾT THÚC =========================== -int OnInit() { - Sleep(500); // đợi chart visual load hoàn tất +int OnInit() +{ + Sleep(500); - handleEMAFast = iMA(_Symbol, _Period, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE); - handleEMASlow = iMA(_Symbol, _Period, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE); + handleEMAFast = iMA(_Symbol, TrendTF, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE); + handleEMASlow = iMA(_Symbol, TrendTF, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE); if(handleEMAFast == INVALID_HANDLE || handleEMASlow == INVALID_HANDLE) { Print("Failed to create EMA handles. Err=", GetLastError()); return(INIT_FAILED); } - // Diagnostic: print which EA file is running and the active EMA periods (helps detect .set overrides) - PrintFormat("Init: EA=%s EMA_Fast=%d EMA_Slow=%d handles=(%d,%d)", __FILE__, EMA_Fast, EMA_Slow, handleEMAFast, handleEMASlow); + PrintFormat("Init: EA=%s | EMA_Fast=%d | EMA_Slow=%d | TF=%s", + __FILE__, EMA_Fast, EMA_Slow, EnumToString(TrendTF)); if(VisualizeEMAs) { long chart_id = ChartID(); bool ok1 = ChartIndicatorAdd(chart_id, 0, handleEMAFast); bool ok2 = ChartIndicatorAdd(chart_id, 0, handleEMASlow); - if(!ok1 || !ok2) Print("Warning: ChartIndicatorAdd failed. Err=", GetLastError()); } @@ -148,7 +156,9 @@ int OnInit() { return(INIT_SUCCEEDED); } -void OnDeinit(const int reason) { - if(handleEMAFast != INVALID_HANDLE) IndicatorRelease(handleEMAFast); +void OnDeinit(const int reason) +{ + if(handleEMAFast != INVALID_HANDLE) IndicatorRelease(handleEMAFast); if(handleEMASlow != INVALID_HANDLE) IndicatorRelease(handleEMASlow); + Comment(""); // clear chart comment }