diff --git a/Experts/RsiMomentumEA.mq5 b/Experts/RsiMomentumEA.mq5 index f4e9791..3e3ae6b 100644 --- a/Experts/RsiMomentumEA.mq5 +++ b/Experts/RsiMomentumEA.mq5 @@ -4,7 +4,7 @@ //| RSI×WMA45 + 5phase entry | ATR↑ EMA200 phiên | Limit 50% body | //+------------------------------------------------------------------+ #property copyright "RsiMomentumEA" -#property version "4.24" +#property version "4.27" #include #include @@ -19,13 +19,13 @@ input int InpWMA45Period = 45; input int InpEMATrendPeriod = 200; input group "Bộ lọc tín hiệu (RSI + ATR + trend EMA200)" -input bool InpTrendFilterEnabled = true; // BUY: close > EMA200 | SELL: close < EMA200 +input bool InpTrendFilterEnabled = false; // BUY: close > EMA200 | SELL: close < EMA200 input int InpTrendConfirmBars = 1; // BUY: N close > EMA200 | SELL: N close < EMA200 -input bool InpAtrExpFilterEnabled = false; // tắt = bỏ lọc ATR mở rộng (debug) +input bool InpAtrExpFilterEnabled = true; // ATR tăng vs N bar + liên tiếp (lọc sideway) input int InpAtrExpPeriod = 14; -input int InpAtrExpCompareBars = 3; -input double InpAtrExpMinRatio = 1.005; -input int InpAtrExpRiseBars = 2; +input int InpAtrExpCompareBars = 3; // so ATR[shift] vs ATR[shift+N] +input double InpAtrExpMinRatio = 1.005; // ≥1.005 = +0.5% (tối ưu 1.003–1.02) +input int InpAtrExpRiseBars = 2; // ATR tăng liên tiếp N nến (1 = lỏng hơn) input group "Lọc RSI quá mua / quá bán (nến tín hiệu)" input bool InpRsiObOsFilterEnabled = true; // BUY khi RSI<70 | SELL khi RSI>30 @@ -35,7 +35,7 @@ input double InpRSIOversold = 30.0; // RSI ≤ ngưỡng → bỏ SELL input group "Entry 5 phase (mở rộng → cuộn EMA9 → EMA9 hướng → WMA45 phẳng → cắt gần)" input bool InpPhaseFilterEnabled = true; input int InpPhaseExpandLookback = 25; // P1: quét mở rộng 3 đường -input double InpPhaseMinExpandSpread = 16.0; // P1: min max(WMA45-RSI) pt RSI [tối ưu ~6–18, step 1] +input double InpPhaseMinExpandSpread = 10.0; // P1: min max(WMA45-RSI) pt RSI [tối ưu ~6–18, step 1] input int InpPhaseCoilLookback = 12; // P2: quét cuộn trước nến tín hiệu input int InpPhaseMinRsiEma9Cross = 2; // P2: RSI cắt EMA9 ≥ N lần (chống xuyên 1 lần) input double InpPhaseCoilBand = 6.0; // P2: |RSI-EMA9| ≤ band = quanh EMA9 @@ -108,10 +108,11 @@ input int InpTransition2EndHour = 22; input bool InpSpreadFilterEnabled = true; input int InpMaxSpreadPoints = 60; // SYMBOL_SPREAD (points) tối đa input bool InpSpreadSkipInTester = true; // Tester: bỏ lọc spread (spread cố định thường quá cao) +input bool InpTesterCalcOnNewBarOnly = true; // Tester: OnCalculate chỉ khi nến mới (tránh chậm dần) input group "Quản lý lệnh mở @ 1R" -input bool InpManageAt1R = false; // tắt tạm: chốt 50% @1R + dời SL về entry -input double InpPartialCloseRatio = 0.5; // tỷ lệ volume chốt khi đạt 1R +input bool InpManageAt1R = true; // @1R: chốt một phần + dời SL về entry +input double InpPartialCloseRatio = 0.5; // tỷ lệ volume chốt khi đạt 1R (0.5 = 50%) input int InpBreakevenOffsetPts = 0; // SL tại entry ± point (0 = đúng entry) input group "SL buffer theo ATR (đẩy SL xa đáy/đỉnh swing)" @@ -151,7 +152,7 @@ const string DBG_PREFIX = OBJ_PREFIX + "DBG_"; #define PANEL_LINE_COUNT 22 #define PANEL_IDX_MARKET 16 // dòng 16+ = RSI / signal (sau block trạng thái) const string PNL_PREFIX = OBJ_PREFIX + "pnl_"; -const string EA_VERSION_STR = "4.24"; +const string EA_VERSION_STR = "4.27"; datetime g_dbgLogBarTime = 0; // chống spam Experts: 1 dòng / (nến, BUY|SELL) int g_dbgLogSide = 0; // 1=BUY, -1=SELL @@ -184,6 +185,31 @@ CTrade g_trade; long ActChart() { return ChartID(); } +bool IsStrategyTester() +{ + return (bool)MQLInfoInteger(MQL_TESTER); +} + +bool IsTesterVisualMode() +{ + return IsStrategyTester() && (bool)MQLInfoInteger(MQL_VISUAL_MODE); +} + +bool DebugMarksEffective() +{ + if(!InpDebugMarkSignals) + return false; + // Tester không visual: không vẽ object (tránh hàng nghìn object làm chậm) + if(IsStrategyTester() && !IsTesterVisualMode()) + return false; + return true; +} + +bool ChartRedrawEffective() +{ + return !IsStrategyTester() || IsTesterVisualMode(); +} + void SetTradeFillingFromSymbol(); double Sl_GetBufferDistance(const int atrShift = 1); bool NearestSwingSlTp(const bool isBuy, const double entry, const int dig, double &sl, double &tp); @@ -1009,6 +1035,9 @@ void Signal_DebugMarkCross(const long ch, const int shift, const datetime barTim const bool atrExpOkBar, const bool sessionAtBar, const string sessionFailWhy) { + if(DebugMarksEffective() && Signal_DebugMarkExists(ch, DBG_PREFIX, barTime, isBuy)) + return; + SignalEvalResult ev; if(isBuy) ev = Signal_EvaluateBuyAt(shift, rates_total, trendN, @@ -1054,8 +1083,12 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need, const double arrowOffset = InpArrowOffsetPts * _Point; const int dbgMax = MathMax(50, InpDebugMarkMaxBars); - if(InpDebugMarkSignals) + const bool dbgMarks = DebugMarksEffective(); + if(dbgMarks) + { Signal_DebugConfigureChart(ch); + Signal_DebugPruneOlderThan(ch, DBG_PREFIX, MathMax(50, InpDebugMarkMaxBars) + 5); + } buf_Signal[0] = 0.0; buf_Trend[0] = 0.0; @@ -1103,7 +1136,7 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need, { const bool validBuy = coreBuyOk && trendUp && atrExpOkBar && rsiOkBuy && envOkBar; - if(InpDebugMarkSignals && i <= dbgMax) + if(dbgMarks && i <= dbgMax) { string envDbg = envWhy; Signal_DebugMarkCross(ch, i, timeArr[i], highArr[i], lowArr[i], @@ -1137,7 +1170,7 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need, { const bool validSell = coreSellOk && trendDown && atrExpOkBar && rsiOkSell && envOkBar; - if(InpDebugMarkSignals && i <= dbgMax) + if(dbgMarks && i <= dbgMax) { string envDbg = envWhy; Signal_DebugMarkCross(ch, i, timeArr[i], highArr[i], lowArr[i], @@ -1329,7 +1362,8 @@ int RsiMomentum_OnCalculate(const int rates_total, const int prev_calculated) Alerts_CheckAndFire(timeArr, closeArr, ema200Arr, need, rates_total); Panel_Update(closeArr, ema200Arr, trendN, rates_total); - ChartRedraw(ActChart()); + if(ChartRedrawEffective()) + ChartRedraw(ActChart()); Diagnostics_FirstPass(rates_total, copyN, trendN, need, closeArr); return copyN; @@ -1428,16 +1462,26 @@ void OnTick() Pending_ManageExpiry(); Position_ManageAt1R(); - const int rates_total = Bars(_Symbol, _Period); - const int ret = RsiMomentum_OnCalculate(rates_total, g_prevCalculated); - if (ret != 0) - g_prevCalculated = ret; - const datetime t0 = iTime(_Symbol, _Period, 0); - if (t0 != 0 && t0 != g_tradeBarAnchor) + const bool newBar = (t0 != 0 && t0 != g_tradeBarAnchor); + const bool runCalc = (g_prevCalculated == 0) + || newBar + || !IsStrategyTester() + || !InpTesterCalcOnNewBarOnly; + + int calcRet = g_prevCalculated; + if(runCalc) + { + const int rates_total = Bars(_Symbol, _Period); + calcRet = RsiMomentum_OnCalculate(rates_total, g_prevCalculated); + if(calcRet != 0) + g_prevCalculated = calcRet; + } + + if(newBar) { g_tradeBarAnchor = t0; - TradeTryOnBarOpen(ret); + TradeTryOnBarOpen(calcRet); } if(InpShowPanel) @@ -1447,7 +1491,7 @@ void OnTick() //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { - if(!InpDebugMarkSignals || !InpDebugHoverHint) + if(!DebugMarksEffective() || !InpDebugHoverHint) return; const long ch = ActChart(); diff --git a/Experts/RsiMomentumEA_README.md b/Experts/RsiMomentumEA_README.md index a92e005..eeca64f 100644 --- a/Experts/RsiMomentumEA_README.md +++ b/Experts/RsiMomentumEA_README.md @@ -93,7 +93,7 @@ Không có cross → không xét tiếp. | Input | Mặc định (debug) | Điều kiện khi **bật** | |-------|------------------|------------------------| | `InpTrendFilterEnabled` | `false` | BUY: `InpTrendConfirmBars` nến liên tiếp có `Close > EMA200`. SELL: `Close < EMA200`. | -| `InpAtrExpFilterEnabled` | `false` | ATR(shift) / ATR(shift+cmp) ≥ `InpAtrExpMinRatio` **và** ATR tăng liên tiếp `InpAtrExpRiseBars` nến. | +| `InpAtrExpFilterEnabled` | `true` | ATR(shift) / ATR(shift+cmp) ≥ `InpAtrExpMinRatio` **và** ATR tăng liên tiếp `InpAtrExpRiseBars` nến. | | `InpSessionFilterEnabled` | `false` | Thời gian **nến tín hiệu** nằm London hoặc NY; không trong cửa sổ giao phiên; trừ `InpSessionAvoidLastMin` phút cuối phiên. | **Tín hiệu hợp lệ (ghi `buf_Signal`):** @@ -381,7 +381,7 @@ Include/RsiMom/ ``` InpPhaseFilterEnabled = true InpTrendFilterEnabled = false -InpAtrExpFilterEnabled = false +InpAtrExpFilterEnabled = true InpSessionFilterEnabled = false InpDebugMarkSignals = true ``` @@ -442,4 +442,20 @@ EA **không** đọc file indicator. Indicator `Indicators/RsiMomentumIndicator` --- -*Tài liệu đồng bộ với mã nguồn v4.23.* +--- + +## 16. Tối ưu ATR mở rộng + +**Mục tiêu:** Bỏ entry khi biến động “ngủ” (ATR không tăng) — thường giảm chop, có thể tăng PF. + +| Input | Mặc định | Gợi ý sweep | +|-------|----------|-------------| +| `InpAtrExpMinRatio` | 1.005 | 1.003–1.015 (step 0.001) | +| `InpAtrExpRiseBars` | 2 | 1 (lỏng) hoặc 3 (chặt) | +| `InpAtrExpCompareBars` | 3 | 2–5 | + +Debug: SKIP `ATR co` hoặc tooltip `ATR:FAIL`. Nếu **0 lệnh** sau bật filter → hạ `MinRatio` hoặc `RiseBars=1`. + +--- + +*Tài liệu đồng bộ với mã nguồn v4.25.* diff --git a/Include/RsiMom/SignalDebug.mqh b/Include/RsiMom/SignalDebug.mqh index c3b5b03..ee2b405 100644 --- a/Include/RsiMom/SignalDebug.mqh +++ b/Include/RsiMom/SignalDebug.mqh @@ -467,6 +467,40 @@ void Signal_DebugOnMouseMove(const long chartId, const string prefix, const int Signal_DebugHideHoverHint(chartId); } +bool Signal_DebugMarkExists(const long chartId, const string prefix, + const datetime barTime, const bool isBuy) +{ + const string arrowName = prefix + IntegerToString((int)barTime) + (isBuy ? "_B" : "_S") + "_ar"; + return (ObjectFind(chartId, arrowName) >= 0); +} + +void Signal_DebugPruneOlderThan(const long chartId, const string prefix, const int keepBars) +{ + if(keepBars < 1) + return; + + const datetime cutoff = iTime(_Symbol, _Period, keepBars); + if(cutoff == 0) + return; + + const int total = ObjectsTotal(chartId, 0, -1); + for(int i = total - 1; i >= 0; i--) + { + const string name = ObjectName(chartId, i, 0, -1); + if(!Signal_DebugObjectHasPrefix(name, prefix)) + continue; + + const int pfxLen = StringLen(prefix); + const int und = StringFind(name, "_", pfxLen); + if(und <= pfxLen) + continue; + + const datetime bt = (datetime)StringToInteger(StringSubstr(name, pfxLen, und - pfxLen)); + if(bt > 0 && bt < cutoff) + ObjectDelete(chartId, name); + } +} + void Signal_DebugDrawMark(const long chartId, const string prefix, const datetime barTime, const double barHigh, const double barLow, const double markPrice, const bool isBuy, const SignalEvalResult &ev)