Update all

This commit is contained in:
Bell
2026-05-25 02:01:26 +07:00
parent b9c03196f6
commit f966c5ba2c
11 changed files with 1028 additions and 197 deletions
+263 -24
View File
@@ -1,11 +1,12 @@
//+------------------------------------------------------------------+
//| EmaPullbackBasic.mq5 — EMA50 pullback EA (phased build) |
//| Phase 4: breakout khỏi nến touch (cửa sổ N nến) |
//| Phase 5: risk 1% R, TP 1.5R |
//| Phase 5: risk & TP theo R |
//| Phase 6: spread/session/DD + chốt 50% @ 2R rồi BE (không cap lệnh/ngày)|
//+------------------------------------------------------------------+
#property copyright "EMA Pullback Basic"
#property version "0.52"
#property description "EMA pullback + reject entry, 1% risk, TP 1.5R"
#property version "0.58"
#property description "Phase 6: filters + partial 2R + breakeven"
#include <Trade\Trade.mqh>
@@ -54,10 +55,24 @@ input bool InpRequireBreakoutCandleColor = true; // Buy: bullish, Sel
input group "=== Risk (Phase 5) ==="
input bool InpTradeEnabled = true;
input double InpRiskPercent = 1.0; // R = % balance
input double InpTpRR = 1.5; // TP = InpTpRR × SL distance
input double InpTpRR = 3; // TP = InpTpRR × SL distance
input double InpSlBufferAtr = 0.10; // SL dưới đáy/ trên đỉnh + buffer × ATR
input int InpMaxBarsInTrade = 0; // 0 = không đóng theo thời gian
input group "=== Phase 6 — An toàn & quản lý lệnh ==="
input bool InpUseSpreadFilter = true;
input int InpMaxSpreadPoints = 50; // Không vào lệnh nếu spread > (points)
input bool InpUseSessionFilter = true;
input int InpSessionStartHour = 8; // Giờ server (bắt đầu)
input int InpSessionEndHour = 22; // Giờ server (kết thúc, có thể qua đêm)
input bool InpUseDdRiskScale = true;
input double InpDdHalveRiskPct = 5.0; // DD từ đỉnh equity → risk × 0.5
input bool InpUsePartialAt2R = true; // Đạt 2R: chốt 50% + SL → BE
input double InpPartialCloseRR = 2.0; // Ngưỡng R để chốt một phần
input double InpPartialCloseFrac = 0.50; // Phần volume đóng (0.5 = một nửa)
input int InpBeOffsetPoints = 2; // BE = entry ± offset (points)
// Ghi chú: không giới hạn số lệnh / ngày
input group "=== EA ==="
input ulong InpMagic = 20260522;
input int InpSlippage = 10;
@@ -87,6 +102,14 @@ int g_statTotal = 0;
int g_statTP = 0;
int g_statSL = 0;
double g_equityPeak = 0.0;
double g_riskScale = 1.0;
ulong g_trackTicket = 0;
double g_trackEntry = 0.0;
double g_trackRiskDist = 0.0;
bool g_trackIsBuy = false;
bool g_trackPartialDone = false;
const string OBJ_PREFIX = "EPB_";
string StatPfx() { return "EPB_ST_" + IntegerToString((int)InpMagic) + "_"; }
@@ -496,9 +519,125 @@ bool HasOurPosition() {
return false;
}
ulong FindOurPositionTicket() {
for(int i = PositionsTotal() - 1; i >= 0; i--) {
const ulong t = PositionGetTicket(i);
if(t == 0 || !PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
return t;
}
return 0;
}
void ResetPositionTrack() {
g_trackTicket = 0;
g_trackEntry = 0.0;
g_trackRiskDist = 0.0;
g_trackIsBuy = false;
g_trackPartialDone = false;
}
void SyncPositionTrack() {
const ulong t = FindOurPositionTicket();
if(t == 0) {
ResetPositionTrack();
return;
}
if(t == g_trackTicket && g_trackRiskDist > 0.0)
return;
if(!PositionSelectByTicket(t))
return;
g_trackTicket = t;
g_trackEntry = PositionGetDouble(POSITION_PRICE_OPEN);
const double sl = PositionGetDouble(POSITION_SL);
g_trackRiskDist = MathAbs(g_trackEntry - sl);
if(g_trackRiskDist < _Point)
g_trackRiskDist = _Point;
g_trackIsBuy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
g_trackPartialDone = false;
}
void StartPositionTrack(const ulong ticket, const bool isBuy,
const double entry, const double sl) {
g_trackTicket = ticket;
g_trackEntry = entry;
g_trackRiskDist = MathAbs(entry - sl);
if(g_trackRiskDist < _Point)
g_trackRiskDist = _Point;
g_trackIsBuy = isBuy;
g_trackPartialDone = false;
}
int CurrentSpreadPoints() {
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(ask <= 0.0 || bid <= 0.0 || _Point <= 0.0)
return 0;
return (int)MathRound((ask - bid) / _Point);
}
bool IsSpreadAllowed() {
if(!InpUseSpreadFilter)
return true;
return CurrentSpreadPoints() <= InpMaxSpreadPoints;
}
bool IsSessionAllowed() {
if(!InpUseSessionFilter)
return true;
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
const int h = dt.hour;
if(InpSessionStartHour == InpSessionEndHour)
return true;
if(InpSessionStartHour < InpSessionEndHour)
return (h >= InpSessionStartHour && h < InpSessionEndHour);
return (h >= InpSessionStartHour || h < InpSessionEndHour);
}
void UpdateRiskScale() {
if(!InpUseDdRiskScale) {
g_riskScale = 1.0;
return;
}
const double eq = AccountInfoDouble(ACCOUNT_EQUITY);
if(g_equityPeak <= 0.0)
g_equityPeak = eq;
if(eq > g_equityPeak)
g_equityPeak = eq;
g_riskScale = 1.0;
if(g_equityPeak > 0.0) {
const double ddPct = (g_equityPeak - eq) / g_equityPeak * 100.0;
if(ddPct >= InpDdHalveRiskPct)
g_riskScale = 0.5;
}
}
bool IsAllowedNewEntry(string &why) {
why = "";
if(!IsSpreadAllowed()) {
why = StringFormat("spread %d > %d pts", CurrentSpreadPoints(), InpMaxSpreadPoints);
return false;
}
if(!IsSessionAllowed()) {
why = StringFormat("ngoài session %02d%02d server", InpSessionStartHour, InpSessionEndHour);
return false;
}
return true;
}
double VolumeForRisk(const bool isBuy, const double entry, const double sl) {
if(MathAbs(entry - sl) < _Point) return 0.0;
const double riskMoney = AccountInfoDouble(ACCOUNT_BALANCE) * (InpRiskPercent / 100.0);
const double riskMoney = AccountInfoDouble(ACCOUNT_BALANCE)
* (InpRiskPercent / 100.0)
* g_riskScale;
double profit = 0.0;
if(!OrderCalcProfit(isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, _Symbol, 1.0, entry, sl, profit))
return 0.0;
@@ -596,26 +735,111 @@ bool OpenMarketFromReject(const bool isBuy,
const datetime t = iTime(_Symbol, _Period, rejectShift);
const double px = isBuy ? iHigh(_Symbol, _Period, rejectShift) : iLow(_Symbol, _Period, rejectShift);
DrawSetupMarker("SIG", t, px, isBuy ? clrLime : clrOrangeRed, isBuy ? "Entry Buy" : "Entry Sell");
const ulong posTicket = FindOurPositionTicket();
if(posTicket > 0)
StartPositionTrack(posTicket, isBuy, entry, sl);
return true;
}
bool TryPartialCloseAndBreakeven(const ulong ticket) {
if(!InpUsePartialAt2R || g_trackPartialDone || g_trackRiskDist <= 0.0)
return false;
if(ticket == 0 || !PositionSelectByTicket(ticket))
return false;
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
const double px = g_trackIsBuy ? bid : ask;
double profitR = 0.0;
if(g_trackIsBuy)
profitR = (px - g_trackEntry) / g_trackRiskDist;
else
profitR = (g_trackEntry - px) / g_trackRiskDist;
if(profitR < InpPartialCloseRR - 1e-8)
return false;
const double vol = PositionGetDouble(POSITION_VOLUME);
const double vmin = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
const double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(vol <= vmin + 1e-12)
return false;
double closeVol = vol * InpPartialCloseFrac;
if(step > 0.0)
closeVol = MathFloor(closeVol / step) * step;
closeVol = NormalizeLots(closeVol);
if(closeVol < vmin) {
if(vol <= vmin * 2.0 + 1e-12)
return false;
closeVol = vmin;
}
if(vol - closeVol < vmin - 1e-12)
closeVol = NormalizeLots(vol - vmin);
if(closeVol < vmin - 1e-12)
return false;
if(!g_trade.PositionClosePartial(ticket, closeVol)) {
PrintFormat("[EPB] Partial close fail ticket=%I64u vol=%.2f ret=%d",
ticket, closeVol, g_trade.ResultRetcode());
return false;
}
const double beOff = InpBeOffsetPoints * _Point;
const double tp = PositionGetDouble(POSITION_TP);
double newSl = g_trackIsBuy
? NormalizePrice(g_trackEntry + beOff)
: NormalizePrice(g_trackEntry - beOff);
const double minDist = (double)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
if(g_trackIsBuy) {
if(bid - newSl < MathMax(_Point, minDist))
newSl = NormalizePrice(bid - MathMax(_Point, minDist));
} else {
if(newSl - ask < MathMax(_Point, minDist))
newSl = NormalizePrice(ask + MathMax(_Point, minDist));
}
if(!g_trade.PositionModify(ticket, newSl, tp)) {
PrintFormat("[EPB] BE modify fail ticket=%I64u SL=%.*f ret=%d",
ticket, _Digits, newSl, g_trade.ResultRetcode());
}
g_trackPartialDone = true;
PrintFormat("[EPB] 2R manage: chốt %.2f lot (%.0f%%) @ %.2fR | SL→BE %.*f",
closeVol, InpPartialCloseFrac * 100.0, profitR, _Digits, newSl);
return true;
}
void ManageOpenPosition() {
if(InpMaxBarsInTrade <= 0 || !HasOurPosition())
if(!HasOurPosition()) {
ResetPositionTrack();
return;
}
SyncPositionTrack();
const ulong ticket = FindOurPositionTicket();
if(ticket == 0)
return;
for(int i = PositionsTotal() - 1; i >= 0; i--) {
const ulong t = PositionGetTicket(i);
if(t == 0 || !PositionSelectByTicket(t)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
TryPartialCloseAndBreakeven(ticket);
const datetime openT = (datetime)PositionGetInteger(POSITION_TIME);
const int barsIn = iBarShift(_Symbol, _Period, openT, false);
if(barsIn >= 0 && barsIn >= InpMaxBarsInTrade) {
g_trade.PositionClose(t);
Print("[EPB] Đóng lệnh — quá ", InpMaxBarsInTrade, " bar");
}
break;
if(InpMaxBarsInTrade <= 0)
return;
if(!PositionSelectByTicket(ticket))
return;
const datetime openT = (datetime)PositionGetInteger(POSITION_TIME);
const int barsIn = iBarShift(_Symbol, _Period, openT, false);
if(barsIn >= 0 && barsIn >= InpMaxBarsInTrade) {
g_trade.PositionClose(ticket);
Print("[EPB] Đóng lệnh — quá ", InpMaxBarsInTrade, " bar");
ResetPositionTrack();
}
}
@@ -704,6 +928,12 @@ bool TryEntryOnBreakout(const int signalShift, const double atr) {
return false;
}
string allowWhy = "";
if(!IsAllowedNewEntry(allowWhy)) {
g_setupDetail = allowWhy;
return false;
}
const int touchShift = FindTouchBarShift();
string why = "";
const bool isBuy = (g_setupDir > 0);
@@ -923,7 +1153,10 @@ void UpdateChartComment(const int shift,
" shift=", touchShift, "\n",
"Trade: ", (InpTradeEnabled ? "ON" : "OFF"),
" Pos: ", (HasOurPosition() ? "YES" : "NO"),
" Risk: ", DoubleToString(InpRiskPercent, 1), "% TP=", DoubleToString(InpTpRR, 1), "R\n",
" Risk: ", DoubleToString(InpRiskPercent * g_riskScale, 2), "% (×", DoubleToString(g_riskScale, 1),
") TP=", DoubleToString(InpTpRR, 1), "R\n",
"Spread: ", CurrentSpreadPoints(), " pts",
InpUsePartialAt2R ? StringFormat(" | 2R: chốt %.0f%%+BE", InpPartialCloseFrac * 100.0) : "", "\n",
"Close/EMA/ADX: ", DoubleToString(iClose(_Symbol, _Period, shift), _Digits),
" / ", DoubleToString(ema, _Digits),
" / ", DoubleToString(adx, 1)
@@ -931,8 +1164,6 @@ void UpdateChartComment(const int shift,
}
void OnNewClosedBar() {
ManageOpenPosition();
const int shift = 1;
double ema = 0.0, adx = 0.0, atr = 0.0;
@@ -985,6 +1216,9 @@ int OnInit() {
}
ResetSetupState("init");
ResetPositionTrack();
g_equityPeak = AccountInfoDouble(ACCOUNT_EQUITY);
g_riskScale = 1.0;
double probe = 0.0;
string err = "";
@@ -1002,12 +1236,14 @@ int OnInit() {
}
PrintFormat(
"[EPB] Init — trade=%s risk=%.1f%% TP=%.1fR | breakout %d bar, %s High/Low touch",
"[EPB] Init Phase 6 — trade=%s risk=%.1f%%×DD TP=%.1fR | spread<=%d | session %02d-%02dh | 2R: %.0f%%+BE | no daily cap",
InpTradeEnabled ? "ON" : "OFF",
InpRiskPercent,
InpTpRR,
InpMaxBarsWaitBreakout,
InpBreakoutUseTouchHighLow ? "Close>" : "Close>Close"
InpMaxSpreadPoints,
InpSessionStartHour,
InpSessionEndHour,
InpPartialCloseFrac * 100.0
);
return INIT_SUCCEEDED;
}
@@ -1033,6 +1269,9 @@ void OnTradeTransaction(const MqlTradeTransaction &trans,
}
void OnTick() {
UpdateRiskScale();
ManageOpenPosition();
const datetime barOpen = iTime(_Symbol, _Period, 0);
if(barOpen == g_lastBarTime)
return;
+1 -1
View File
@@ -17,7 +17,7 @@
//| CHƯA CÓ TRONG DỰ ÁN: đặt lệnh, LTF, session, alert |
//+------------------------------------------------------------------+
#property copyright "HyperICT"
#property version "1.11"
#property version "1.13"
#property description "HTF structure | Fib=init only | pivot confirm=InpSwingRange"
#include <HyperICT/Config.mqh>
Binary file not shown.
+365 -54
View File
@@ -1,16 +1,25 @@
//+------------------------------------------------------------------+
//| RsiMomentumEA.mq5 |
//| EA tự động — logic độc lập (không đọc RsiMomentumIndicator). |
//| RSI×WMA45 + 5phase entry | ATR↑ EMA200 phiên | Limit 50% body |
//| RSI×WMA45 + 5phase | ATR↑ ADX trend EMA200 phiên | Limit 50% body |
//+------------------------------------------------------------------+
#property copyright "RsiMomentumEA"
#property version "4.27"
#property version "4.31"
#include <Trade/Trade.mqh>
#include <RsiMom/TradeJournal.mqh>
#include <RsiMom/PhaseEntry.mqh>
#include <RsiMom/AdxFilter.mqh>
#include <RsiMom/SwingStructure.mqh>
#include <RsiMom/SignalDebug.mqh>
//--- Cơ chế vào lệnh (switch test — logic Limit giữ nguyên trong TradeExecuteLimitOrder)
enum ENUM_RSI_MOM_ENTRY_MODE
{
RSI_MOM_ENTRY_LIMIT_BODY50 = 0, // Limit @ 50% thân nến tín hiệu (mặc định)
RSI_MOM_ENTRY_MARKET = 1 // Market ngay khi nến mới sau tín hiệu
};
//--- Input (khớp Indicators/RsiMomentumIndicator/Lib/Inputs.mqh)
input group "Chỉ báo"
input int InpRSIPeriod = 14;
@@ -19,14 +28,29 @@ input int InpWMA45Period = 45;
input int InpEMATrendPeriod = 200;
input group "Bộ lọc tín hiệu (RSI + ATR + trend EMA200)"
input bool InpTrendFilterEnabled = false; // BUY: close > EMA200 | SELL: close < EMA200
input bool InpTrendFilterEnabled = true; // BUY: close > EMA200 | SELL: close < EMA200
input int InpTrendConfirmBars = 1; // BUY: N close > EMA200 | SELL: N close < EMA200
input bool InpAtrExpFilterEnabled = true; // ATR tăng vs N bar + liên tiếp (lọc sideway)
input bool InpAtrExpFilterEnabled = false; // tắt tạm — test riêng ADX
input int InpAtrExpPeriod = 14;
input int InpAtrExpCompareBars = 3; // so ATR[shift] vs ATR[shift+N]
input double InpAtrExpMinRatio = 1.005; // ≥1.005 = +0.5% (tối ưu 1.0031.02)
input int InpAtrExpRiseBars = 2; // ATR tăng liên tiếp N nến (1 = lỏng hơn)
input group "ADX — trend mạnh (pullback hiệu quả)"
input bool InpAdxFilterEnabled = true; // ADX + hướng +DI/-DI tại nến tín hiệu
input int InpAdxPeriod = 14;
input double InpAdxMinLevel = 22.0; // ADX >= ngưỡng (sideway ~<20, trend 2235+)
input double InpAdxMaxLevel = 0.0; // 0=tắt; ví dụ 45 tránh trend quá già
input bool InpAdxRequireDiDirection = true; // BUY +DI>-DI | SELL -DI>+DI
input double InpAdxMinDiSpread = 0.0; // |+DI(-DI)| tối thiểu (510 = chặt hơn)
input int InpAdxRiseBars = 0; // ADX tăng vs N nến trước (0=tắt, 12 bật)
input group "2 swing — đáy tăng / đỉnh giảm (thân nến)"
input bool InpSwingStructFilterEnabled = true;
input int InpSwingStructRange = 2; // pivot: N nến mỗi bên mỗi đáy/đỉnh
input int InpSwingStructLookback = 120; // quét tối đa N nến trước tín hiệu
input double InpSwingStructTolPts = 0.0; // cho phép 2 đáy/đỉnh bằng nhau (points)
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
input double InpRSIOverbought = 70.0; // RSI ≥ ngưỡng → bỏ BUY
@@ -47,17 +71,20 @@ input double InpPhaseWmaFlatMaxSlope = 0.55; // P4: |slope| WMA45 gần 0 tạ
input bool InpPhaseWmaRelaxPrior = true; // P4: chỉ cần WMA45 từng đi đúng hướng, không cần dốc mạnh
input double InpPhaseMaxEma9WmaGap = 16.0; // P5: EMA9WMA45 tối đa khi cắt (lớn hơn = lỏng)
input group "Entry — Limit 50% thân nến tín hiệu"
input group "Cơ chế vào lệnh (switch test)"
input ENUM_RSI_MOM_ENTRY_MODE InpEntryMode = RSI_MOM_ENTRY_MARKET;
input group "Entry — Limit 50% thân nến (chỉ InpEntryMode=Limit)"
input int InpSignalBarShift = 1; // nến tín hiệu (1 = nến vừa đóng)
input group "Quản lý Limit pending"
input int InpLimitExpireBars = 40; // hủy Limit nếu không khớp sau N nến (trước: 20)
input group "Quản lý Limit pending (chỉ InpEntryMode=Limit)"
input int InpLimitExpireBars = 40; // hủy Limit nếu không khớp sau N nến
input group "Debug — đánh dấu RSI×WMA45 (hợp lệ / skip + lý do)"
input bool InpDebugMarkSignals = true; // mọi cross: OK xanh | SIG vàng | SKIP đỏ + nhãn phase fail
input int InpDebugMarkMaxBars = 400; // chỉ tạo mới trong N nến gần nhất (dấu cũ vẫn giữ)
input bool InpDebugLogExperts = true; // 1 dòng Experts / nến tín hiệu (không lặp mỗi tick)
input bool InpDebugHoverHint = true; // rê chuột lên dấu X: panel góc dưới-trái + tooltip
input group "Debug — đánh dấu RSI×WMA45 (tắt = backtest nhanh)"
input bool InpDebugMarkSignals = false; // mọi cross: OK xanh | SIG vàng | SKIP đỏ
input int InpDebugMarkMaxBars = 400; // chỉ tạo mới trong N nến gần nhất
input bool InpDebugLogExperts = false; // 1 dòng Experts / nến tín hiệu
input bool InpDebugHoverHint = false; // rê chuột lên dấu X
input group "Mũi tên giao cắt"
input color InpArrowUpColor = clrLime;
@@ -66,7 +93,7 @@ input int InpArrowOffsetPts = 30;
input int InpArrowSize = 1;
input group "Panel trạng thái (góc trên-trái)"
input bool InpShowPanel = true;
input bool InpShowPanel = false; // tắt = tester nhanh hơn
input int InpPanelFontSize = 8;
input int InpPanelLinePad = 14; // khoảng cách dọc giữa các dòng
input int InpPanelLeftMargin = 8;
@@ -75,9 +102,9 @@ input color InpPanelColorEMA9 = clrGold;
input color InpPanelColorWMA45 = clrDodgerBlue;
input group "Cảnh báo / Notification (khi có entry mới)"
input bool InpAlertPush = true;
input bool InpAlertPopup = true;
input bool InpAlertSound = true;
input bool InpAlertPush = false;
input bool InpAlertPopup = false;
input bool InpAlertSound = false;
input string InpSoundBuy = "alert.wav";
input string InpSoundSell = "alert2.wav";
input bool InpAlertEmail = false;
@@ -87,7 +114,7 @@ input group "Giao dịch tự động"
input bool InpTradeEnabled = true;
input ulong InpMagic = 202602;
input double InpRiskPercent = 1; // % balance mất nếu SL khớp (theo lot tính từ SL)
input double InpRewardRiskRatio = 1.1; // R:R — TP = tỷ lệ × khoảng SL (2.0 = 1:2)
input double InpRewardRiskRatio = 1.05; // R:R — TP = tỷ lệ × khoảng SL (2.0 = 1:2)
input int InpSwingMaxBars = 30; // quét swing pivot / fallback min-max
input int InpSlippagePoints = 30;
input bool InpOnePositionFlat = true;
@@ -111,7 +138,7 @@ input bool InpSpreadSkipInTester = true; // Tester: bỏ lọc spread (
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 = true; // @1R: chốt một phần + dời SL về entry
input bool InpManageAt1R = false; // @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)
@@ -122,11 +149,11 @@ input double InpSlAtrMultiplier = 0.5; // khoảng cách thêm = ATR(shift
input bool InpSlAtrAddSpread = true; // cộng thêm buffer spread vào SL
input group "Xuất CSV thống kê (FILE_COMMON)"
input bool InpExportTradeJournal = true; // journal từng lệnh + summary theo tháng khi kết tc test/EA
input bool InpExportTradeJournal = false; // bật lại khi cần phân tích CSV (chậm hơn một ct)
input bool InpJournalResetOnInit = true; // Tester: xóa CSV cũ mỗi lần chạy backtest mới
input group "Thống kê (góc dưới-trái chart)"
input bool InpShowStats = true;
input bool InpShowStats = true; // tắt = tester nhanh hơn
input int InpStatFontSize = 9;
input int InpStatLinePad = 26; // khoảng cách dọc giữa các dòng (pixel)
input int InpStatBottomMargin = 28; // lề dưới block thống kê
@@ -146,13 +173,14 @@ int h_WMA45 = INVALID_HANDLE;
int h_EMA200 = INVALID_HANDLE;
int h_ATR = INVALID_HANDLE;
int h_ATR_Regime = INVALID_HANDLE;
int h_ADX = INVALID_HANDLE;
const string OBJ_PREFIX = "RsiMomEA_";
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)
#define PANEL_LINE_COUNT 25
#define PANEL_IDX_MARKET 19 // dòng 19+ = RSI / signal (sau block trạng thái)
const string PNL_PREFIX = OBJ_PREFIX + "pnl_";
const string EA_VERSION_STR = "4.27";
const string EA_VERSION_STR = "4.31";
datetime g_dbgLogBarTime = 0; // chống spam Experts: 1 dòng / (nến, BUY|SELL)
int g_dbgLogSide = 0; // 1=BUY, -1=SELL
@@ -221,6 +249,11 @@ double NormalizeLots(double v);
double VolumeForRiskPercent(const bool isBuy, const double entryRef, const double slPrice);
void TradeTryOnBarOpen(const int calcRet);
void TradeExecuteOrder(const bool isBuy);
void TradeExecuteLimitOrder(const bool isBuy);
void TradeExecuteMarketOrder(const bool isBuy);
bool EntryModeIsLimit();
bool EntryModeIsMarket();
string EntryModeLabel();
bool Signal_BodyMidPrice(const int shift, double &midOut);
bool LimitPriceValid(const bool isBuy, const double limitPx);
void Pending_ManageExpiry();
@@ -233,6 +266,11 @@ bool AtrExp_GetAt(const int shift, double &atr);
bool AtrExp_IsExpandingAt(const int shift);
bool AtrExp_AllowsAt(const int shift);
bool AtrExp_AllowsNow();
bool Adx_AllowsBuyAt(const int shift, string &why);
bool Adx_AllowsSellAt(const int shift, string &why);
void Adx_GetAtBar(const int shift, double &adx, double &plusDi, double &minusDi);
bool SwingStruct_AllowsBuyAt(const int shift, string &why, double &bodyOld, double &bodyNew);
bool SwingStruct_AllowsSellAt(const int shift, string &why, double &bodyOld, double &bodyNew);
int Env_CurrentSpreadPts();
bool Env_SpreadAllows();
bool Env_AllowsSessionAt(const datetime t, string &why);
@@ -291,6 +329,12 @@ bool Handles_CreateAll()
Print("[RsiMomEA] Không tạo được handle ATR (regime)");
return false;
}
h_ADX = iADX(_Symbol, _Period, MathMax(2, InpAdxPeriod));
if (h_ADX == INVALID_HANDLE)
{
Print("[RsiMomEA] Không tạo được handle ADX");
return false;
}
return true;
}
@@ -303,7 +347,8 @@ void Handles_ReleaseAll()
if (h_EMA200 != INVALID_HANDLE) IndicatorRelease(h_EMA200);
if (h_ATR != INVALID_HANDLE) IndicatorRelease(h_ATR);
if (h_ATR_Regime != INVALID_HANDLE) IndicatorRelease(h_ATR_Regime);
h_RSI = h_EMA9 = h_WMA45 = h_EMA200 = h_ATR = h_ATR_Regime = INVALID_HANDLE;
if (h_ADX != INVALID_HANDLE) IndicatorRelease(h_ADX);
h_RSI = h_EMA9 = h_WMA45 = h_EMA200 = h_ATR = h_ATR_Regime = h_ADX = INVALID_HANDLE;
}
//+------------------------------------------------------------------+
@@ -434,8 +479,12 @@ void Panel_UpdateStatus()
MathMax(1, InpSignalBarShift)),
Panel_ClrOnOff(InpTradeEnabled));
Panel_SetLine(ln++, StringFormat("Risk %.2f%% | R:R 1:%.2f | Limit 50%% body Exp %d bar",
InpRiskPercent, InpRewardRiskRatio, InpLimitExpireBars),
Panel_SetLine(ln++, StringFormat("Entry: %s", EntryModeLabel()),
InpEntryMode == RSI_MOM_ENTRY_MARKET ? clrGold : clrSilver);
Panel_SetLine(ln++, StringFormat("Risk %.2f%% | R:R 1:%.2f%s",
InpRiskPercent, InpRewardRiskRatio,
EntryModeIsLimit()
? StringFormat(" | Limit exp %d bar", InpLimitExpireBars) : ""),
clrSilver);
Panel_SetLine(ln++, StringFormat("Manage @1R: %s (partial %.0f%%)",
@@ -453,8 +502,19 @@ void Panel_UpdateStatus()
MathMax(1, InpTrendConfirmBars)),
InpPhaseFilterEnabled ? clrWhite : clrDimGray);
Panel_SetLine(ln++, StringFormat("ATR expand: %s | RSI OB/OS: %s (%.0f / %.0f)",
Panel_SetLine(ln++, StringFormat("ATR expand: %s | ADX: %s >=%.0f DI: %s",
Panel_FmtOnOff(InpAtrExpFilterEnabled),
Panel_FmtOnOff(InpAdxFilterEnabled),
InpAdxMinLevel,
Panel_FmtOnOff(InpAdxRequireDiDirection)),
clrSilver);
Panel_SetLine(ln++, StringFormat("Swing2 body: %s range=%d LB=%d",
Panel_FmtOnOff(InpSwingStructFilterEnabled),
InpSwingStructRange, InpSwingStructLookback),
clrSilver);
Panel_SetLine(ln++, StringFormat("RSI OB/OS: %s (%.0f / %.0f)",
Panel_FmtOnOff(InpRsiObOsFilterEnabled),
InpRSIOverbought, InpRSIOversold),
clrSilver);
@@ -570,6 +630,17 @@ void Panel_Update(const double &closeArr[], const double &ema200Arr[],
Panel_SetLine(ln++, StringFormat("RSI %6.2f", buf_RSI[1]), clrMediumOrchid);
Panel_SetLine(ln++, StringFormat("EMA9 %6.2f", buf_EMA9[1]), InpPanelColorEMA9);
Panel_SetLine(ln++, StringFormat("WMA45 %6.2f", buf_WMA45[1]), InpPanelColorWMA45);
if(InpAdxFilterEnabled)
{
double adx = 0.0, pdi = 0.0, mdi = 0.0;
Adx_GetAtBar(1, adx, pdi, mdi);
string adxWhy = "";
const bool adxOk = panelTrendUp ? Adx_AllowsBuyAt(1, adxWhy)
: (panelTrendDown ? Adx_AllowsSellAt(1, adxWhy) : true);
Panel_SetLine(ln++, StringFormat("ADX %5.1f +DI=%.1f -DI=%.1f %s",
adx, pdi, mdi, adxOk ? "OK" : "FAIL"),
adxOk ? clrSilver : clrOrangeRed);
}
Panel_SetLine(ln++, StringFormat("EMA200 close[1] %s %.5f",
panelTrendUp ? ">" : (panelTrendDown ? "<" : "~"),
ema200Arr[1]), trendClr);
@@ -707,6 +778,84 @@ bool AtrExp_AllowsNow()
return AtrExp_AllowsAt(1);
}
//+------------------------------------------------------------------+
AdxFilterConfig GetAdxFilterConfig()
{
AdxFilterConfig c;
c.enabled = InpAdxFilterEnabled;
c.period = MathMax(2, InpAdxPeriod);
c.minLevel = MathMax(0.0, InpAdxMinLevel);
c.maxLevel = MathMax(0.0, InpAdxMaxLevel);
c.requireDiDir = InpAdxRequireDiDirection;
c.minDiSpread = MathMax(0.0, InpAdxMinDiSpread);
c.riseBars = MathMax(0, InpAdxRiseBars);
return c;
}
void Adx_GetAtBar(const int shift, double &adx, double &plusDi, double &minusDi)
{
adx = plusDi = minusDi = 0.0;
if(!InpAdxFilterEnabled || h_ADX == INVALID_HANDLE)
return;
AdxFilter_GetAt(h_ADX, shift, adx, plusDi, minusDi);
}
bool Adx_AllowsBuyAt(const int shift, string &why)
{
why = "";
if(!InpAdxFilterEnabled)
return true;
return AdxFilter_PassesBuy(h_ADX, shift, GetAdxFilterConfig(), why);
}
bool Adx_AllowsSellAt(const int shift, string &why)
{
why = "";
if(!InpAdxFilterEnabled)
return true;
return AdxFilter_PassesSell(h_ADX, shift, GetAdxFilterConfig(), why);
}
SwingStructConfig SwingStruct_BuildConfig()
{
SwingStructConfig c;
c.enabled = InpSwingStructFilterEnabled;
c.pivotRange = MathMax(1, InpSwingStructRange);
c.lookback = MathMax(20, InpSwingStructLookback);
c.tolPts = MathMax(0.0, InpSwingStructTolPts);
return c;
}
bool SwingStruct_AllowsBuyAt(const int shift, string &why, double &bodyOld, double &bodyNew)
{
why = "";
bodyOld = bodyNew = 0.0;
if(!InpSwingStructFilterEnabled)
return true;
SwingStructPoint older, newer;
const SwingStructConfig cfg = SwingStruct_BuildConfig();
const bool ok = SwingStruct_PassesBuyAt(_Symbol, _Period, shift, cfg, why, older, newer);
bodyOld = older.bodyPrice;
bodyNew = newer.bodyPrice;
return ok;
}
bool SwingStruct_AllowsSellAt(const int shift, string &why, double &bodyOld, double &bodyNew)
{
why = "";
bodyOld = bodyNew = 0.0;
if(!InpSwingStructFilterEnabled)
return true;
SwingStructPoint older, newer;
const SwingStructConfig cfg = SwingStruct_BuildConfig();
const bool ok = SwingStruct_PassesSellAt(_Symbol, _Period, shift, cfg, why, older, newer);
bodyOld = older.bodyPrice;
bodyNew = newer.bodyPrice;
return ok;
}
//+------------------------------------------------------------------+
//| Phiên London/NY + chặn giao phiên + spread tối đa (trade env) |
//+------------------------------------------------------------------+
@@ -919,16 +1068,21 @@ bool Signal_RsiOkSellAt(const int shift)
//+------------------------------------------------------------------+
string Signal_ReasonBuy(const bool trendUp, const bool ema9CoreOk, const bool phaseOk, const string phaseFail,
const bool atrExpOk, const bool rsiObOsOk, const bool envOk, const bool valid)
const bool atrExpOk, const bool adxOk, const bool swingOk,
const bool rsiObOsOk, const bool envOk, const bool valid)
{
if(valid)
{
string s = InpPhaseFilterEnabled
? "RSI↑WMA45 | 5phase OK | EMA200 UP"
: "RSI↑WMA45 EMA9<WMA45 | EMA200 UP";
s += StringFormat(" (%d bar) | Limit 50%% body", MathMax(1, InpTrendConfirmBars));
s += StringFormat(" (%d bar) | %s", MathMax(1, InpTrendConfirmBars), EntryModeLabel());
if(InpAtrExpFilterEnabled)
s += StringFormat(" | ATR↑ x%.0f%% %d bar", (InpAtrExpMinRatio - 1.0) * 100.0, InpAtrExpRiseBars);
if(InpAdxFilterEnabled)
s += StringFormat(" | ADX>=%.0f", InpAdxMinLevel);
if(InpSwingStructFilterEnabled)
s += " | 2đáy↑";
return s;
}
@@ -941,6 +1095,10 @@ string Signal_ReasonBuy(const bool trendUp, const bool ema9CoreOk, const bool ph
f += "ngược EMA200 ";
if(InpAtrExpFilterEnabled && !atrExpOk)
f += "ATR co ";
if(InpAdxFilterEnabled && !adxOk)
f += "ADX yếu ";
if(InpSwingStructFilterEnabled && !swingOk)
f += "2 đáy ";
if(InpRsiObOsFilterEnabled && !rsiObOsOk)
f += "RSI quá mua ";
if(!envOk)
@@ -951,16 +1109,21 @@ string Signal_ReasonBuy(const bool trendUp, const bool ema9CoreOk, const bool ph
}
string Signal_ReasonSell(const bool trendDown, const bool ema9CoreOk, const bool phaseOk, const string phaseFail,
const bool atrExpOk, const bool rsiObOsOk, const bool envOk, const bool valid)
const bool atrExpOk, const bool adxOk, const bool swingOk,
const bool rsiObOsOk, const bool envOk, const bool valid)
{
if(valid)
{
string s = InpPhaseFilterEnabled
? "RSI↓WMA45 | 5phase OK | EMA200 DOWN"
: "RSI↓WMA45 EMA9>WMA45 | EMA200 DOWN";
s += StringFormat(" (%d bar) | Limit 50%% body", MathMax(1, InpTrendConfirmBars));
s += StringFormat(" (%d bar) | %s", MathMax(1, InpTrendConfirmBars), EntryModeLabel());
if(InpAtrExpFilterEnabled)
s += StringFormat(" | ATR↑ x%.0f%% %d bar", (InpAtrExpMinRatio - 1.0) * 100.0, InpAtrExpRiseBars);
if(InpAdxFilterEnabled)
s += StringFormat(" | ADX>=%.0f", InpAdxMinLevel);
if(InpSwingStructFilterEnabled)
s += " | 2đỉnh↓";
return s;
}
@@ -973,6 +1136,10 @@ string Signal_ReasonSell(const bool trendDown, const bool ema9CoreOk, const bool
f += "ngược EMA200 ";
if(InpAtrExpFilterEnabled && !atrExpOk)
f += "ATR co ";
if(InpAdxFilterEnabled && !adxOk)
f += "ADX yếu ";
if(InpSwingStructFilterEnabled && !swingOk)
f += "2 đỉnh ";
if(InpRsiObOsFilterEnabled && !rsiObOsOk)
f += "RSI quá bán ";
if(!envOk)
@@ -1038,12 +1205,27 @@ void Signal_DebugMarkCross(const long ch, const int shift, const datetime barTim
if(DebugMarksEffective() && Signal_DebugMarkExists(ch, DBG_PREFIX, barTime, isBuy))
return;
double adxVal = 0.0, plusDi = 0.0, minusDi = 0.0;
string adxWhy = "";
Adx_GetAtBar(shift, adxVal, plusDi, minusDi);
const bool adxOkBar = isBuy ? Adx_AllowsBuyAt(shift, adxWhy) : Adx_AllowsSellAt(shift, adxWhy);
string swingWhy = "";
double swingOld = 0.0, swingNew = 0.0;
const bool swingOkBar = isBuy
? SwingStruct_AllowsBuyAt(shift, swingWhy, swingOld, swingNew)
: SwingStruct_AllowsSellAt(shift, swingWhy, swingOld, swingNew);
SignalEvalResult ev;
if(isBuy)
ev = Signal_EvaluateBuyAt(shift, rates_total, trendN,
buf_RSI, buf_EMA9, buf_WMA45, closeArr, ema200Arr,
phaseCfg, InpPhaseFilterEnabled, InpTrendFilterEnabled,
InpAtrExpFilterEnabled, InpRsiObOsFilterEnabled,
InpAtrExpFilterEnabled, InpAdxFilterEnabled, adxOkBar,
adxVal, plusDi, minusDi, adxWhy,
InpSwingStructFilterEnabled, swingOkBar,
swingOld, swingNew, swingWhy,
InpRsiObOsFilterEnabled,
InpRSIOverbought, InpRSIOversold,
InpSessionFilterEnabled,
atrExpOkBar, sessionAtBar, sessionFailWhy);
@@ -1051,7 +1233,11 @@ void Signal_DebugMarkCross(const long ch, const int shift, const datetime barTim
ev = Signal_EvaluateSellAt(shift, rates_total, trendN,
buf_RSI, buf_EMA9, buf_WMA45, closeArr, ema200Arr,
phaseCfg, InpPhaseFilterEnabled, InpTrendFilterEnabled,
InpAtrExpFilterEnabled, InpRsiObOsFilterEnabled,
InpAtrExpFilterEnabled, InpAdxFilterEnabled, adxOkBar,
adxVal, plusDi, minusDi, adxWhy,
InpSwingStructFilterEnabled, swingOkBar,
swingOld, swingNew, swingWhy,
InpRsiObOsFilterEnabled,
InpRSIOverbought, InpRSIOversold,
InpSessionFilterEnabled,
atrExpOkBar, sessionAtBar, sessionFailWhy);
@@ -1127,6 +1313,13 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
const bool coreBuyOk = InpPhaseFilterEnabled ? phaseBuyOk : ema9BuyOk;
const bool coreSellOk = InpPhaseFilterEnabled ? phaseSellOk : ema9SellOk;
const bool atrExpOkBar = AtrExp_AllowsAt(i);
string adxWhyB = "", adxWhyS = "";
const bool adxOkBuy = Adx_AllowsBuyAt(i, adxWhyB);
const bool adxOkSell = Adx_AllowsSellAt(i, adxWhyS);
string swingWhyB = "", swingWhyS = "";
double swOld = 0.0, swNew = 0.0;
const bool swingOkBuy = SwingStruct_AllowsBuyAt(i, swingWhyB, swOld, swNew);
const bool swingOkSell = SwingStruct_AllowsSellAt(i, swingWhyS, swOld, swNew);
const bool rsiOkBuy = Signal_RsiOkBuyAt(i);
const bool rsiOkSell = Signal_RsiOkSellAt(i);
string envWhy = "";
@@ -1134,7 +1327,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
if(crossUpWma45)
{
const bool validBuy = coreBuyOk && trendUp && atrExpOkBar && rsiOkBuy && envOkBar;
const bool validBuy = coreBuyOk && trendUp && atrExpOkBar && adxOkBuy
&& swingOkBuy && rsiOkBuy && envOkBar;
if(dbgMarks && i <= dbgMax)
{
@@ -1148,7 +1342,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
if(validBuy)
{
const string reason = Signal_ReasonBuy(trendUp, ema9BuyOk, phaseBuyOk, phaseFailBuy,
atrExpOkBar, rsiOkBuy, envOkBar, true);
atrExpOkBar, adxOkBuy, swingOkBuy,
rsiOkBuy, envOkBar, true);
const double arrowPrice = lowArr[i] - arrowOffset;
const string arrowName = OBJ_PREFIX + "CR_" + IntegerToString((int)timeArr[i]);
if(ObjectFind(ch, arrowName) < 0)
@@ -1168,7 +1363,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
if(crossDownWma45)
{
const bool validSell = coreSellOk && trendDown && atrExpOkBar && rsiOkSell && envOkBar;
const bool validSell = coreSellOk && trendDown && atrExpOkBar && adxOkSell
&& swingOkSell && rsiOkSell && envOkBar;
if(dbgMarks && i <= dbgMax)
{
@@ -1182,7 +1378,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
if(validSell)
{
const string reason = Signal_ReasonSell(trendDown, ema9SellOk, phaseSellOk, phaseFailSell,
atrExpOkBar, rsiOkSell, envOkBar, true);
atrExpOkBar, adxOkSell, swingOkSell,
rsiOkSell, envOkBar, true);
const double arrowPrice = highArr[i] + arrowOffset;
const string arrowName = OBJ_PREFIX + "CR_" + IntegerToString((int)timeArr[i]);
if(ObjectFind(ch, arrowName) < 0)
@@ -1227,11 +1424,14 @@ void Diagnostics_FirstPass(const int rates_total, const int copyN, const int tre
{
int crossUp = 0, crossDn = 0, passTrendUp = 0, passTrendDn = 0, passAtrUp = 0, passAtrDn = 0;
int passAdxUp = 0, passAdxDn = 0;
int passSwingUp = 0, passSwingDn = 0;
int passEnvUp = 0, passEnvDn = 0;
int passEma9Up = 0, passEma9Dn = 0;
int passPhaseUp = 0, passPhaseDn = 0;
int validBuy = 0, validSell = 0;
const int needSh = MathMax(1, InpAtrExpCompareBars) + MathMax(1, InpAtrExpRiseBars);
const int needSh = MathMax(1, InpAtrExpCompareBars) + MathMax(1, InpAtrExpRiseBars)
+ (InpAdxFilterEnabled ? MathMax(0, InpAdxRiseBars) : 0);
const int phaseLb = InpPhaseFilterEnabled
? MathMax(InpPhaseExpandLookback, InpPhaseCoilLookback) + InpPhaseWmaFlatBars * 2 + 3
: 0;
@@ -1253,6 +1453,12 @@ void Diagnostics_FirstPass(const int rates_total, const int copyN, const int tre
passTrendDn++;
if(up && AtrExp_AllowsAt(i)) passAtrUp++;
if(dn && AtrExp_AllowsAt(i)) passAtrDn++;
string adxW = "";
if(up && Adx_AllowsBuyAt(i, adxW)) passAdxUp++;
if(dn && Adx_AllowsSellAt(i, adxW)) passAdxDn++;
double swO = 0.0, swN = 0.0;
if(up && SwingStruct_AllowsBuyAt(i, adxW, swO, swN)) passSwingUp++;
if(dn && SwingStruct_AllowsSellAt(i, adxW, swO, swN)) passSwingDn++;
if(up && Signal_Ema9CoreBuyOkAt(i)) passEma9Up++;
if(dn && Signal_Ema9CoreSellOkAt(i)) passEma9Dn++;
string pfB = "", pfS = "";
@@ -1265,16 +1471,20 @@ void Diagnostics_FirstPass(const int rates_total, const int copyN, const int tre
if(buf_Signal[i] > 0.5) validBuy++;
if(buf_Signal[i] < -0.5) validSell++;
}
PrintFormat("[RsiMomEA] last %d bars: cross UP=%d DN=%d | EMA9 UP=%d DN=%d | 5phase UP=%d DN=%d | EMA200 UP=%d DOWN=%d | ATR↑ UP=%d DN=%d | phiên UP=%d DN=%d | signal BUY=%d SELL=%d",
PrintFormat("[RsiMomEA] last %d bars: cross UP=%d DN=%d | EMA9 UP=%d DN=%d | 5phase UP=%d DN=%d | EMA200 UP=%d DOWN=%d | ATR↑ UP=%d DN=%d | ADX UP=%d DN=%d | Swing2 UP=%d DN=%d | phiên UP=%d DN=%d | signal BUY=%d SELL=%d",
n, crossUp, crossDn, passEma9Up, passEma9Dn, passPhaseUp, passPhaseDn,
passTrendUp, passTrendDn, passAtrUp, passAtrDn,
passEnvUp, passEnvDn, validBuy, validSell);
passTrendUp, passTrendDn, passAtrUp, passAtrDn, passAdxUp, passAdxDn,
passSwingUp, passSwingDn, passEnvUp, passEnvDn, validBuy, validSell);
if(crossUp > 0 && passPhaseUp == 0 && InpPhaseFilterEnabled)
Print("[RsiMomEA] Gợi ý: cross UP bị 5phase — xem P1-P5 hoặc hạ InpPhaseMinExpandSpread / InpPhaseMinRsiEma9Cross");
else if(crossUp > 0 && passEma9Up == 0)
Print("[RsiMomEA] Gợi ý: cross UP nhưng EMA9>=WMA45 — không đủ điều kiện lõi BUY");
if(InpAtrExpFilterEnabled && crossUp > 0 && passAtrUp == 0)
Print("[RsiMomEA] Gợi ý: cross bị chặn ATR — hạ InpAtrExpMinRatio / InpAtrExpRiseBars hoặc tắt InpAtrExpFilterEnabled");
if(InpAdxFilterEnabled && crossUp > 0 && passAdxUp == 0)
Print("[RsiMomEA] Gợi ý: cross bị chặn ADX — hạ InpAdxMinLevel hoặc tắt InpAdxRequireDiDirection");
if(InpSwingStructFilterEnabled && crossUp > 0 && passSwingUp == 0)
Print("[RsiMomEA] Gợi ý: cross bị chặn 2 đáy — tăng lookback hoặc tắt InpSwingStructFilterEnabled");
if(InpSessionFilterEnabled && crossUp > 0 && passEnvUp == 0)
Print("[RsiMomEA] Gợi ý: cross bị chặn PHIÊN — chỉnh giờ London/NY (server) hoặc tắt InpSessionFilterEnabled");
}
@@ -1302,11 +1512,14 @@ void EnsureBuffers(const int rates_total)
int RsiMomentum_OnCalculate(const int rates_total, const int prev_calculated)
{
const int atrNeed = MathMax(1, InpAtrExpCompareBars) + MathMax(1, InpAtrExpRiseBars) + 5;
const int adxNeed = InpAdxFilterEnabled
? MathMax(InpAdxPeriod, 5) + MathMax(0, InpAdxRiseBars) + 3
: 0;
const int phaseNeed = InpPhaseFilterEnabled
? MathMax(InpPhaseExpandLookback, InpPhaseCoilLookback) + InpPhaseWmaFlatBars * 2 + 10
: 0;
const int minBars = MathMax(InpWMA45Period + InpRSIPeriod + 5 + phaseNeed,
MathMax(InpAtrExpPeriod, InpSlAtrPeriod) + atrNeed);
MathMax(MathMax(InpAtrExpPeriod, InpSlAtrPeriod) + atrNeed, adxNeed));
if (rates_total < minBars) return 0;
EnsureBuffers(rates_total);
@@ -1316,19 +1529,23 @@ int RsiMomentum_OnCalculate(const int rates_total, const int prev_calculated)
const int wmaBars = BarsCalculated(h_WMA45);
const int ema200Bars = BarsCalculated(h_EMA200);
const int atrRegBars = BarsCalculated(h_ATR_Regime);
if (rsiBars <= 0 || ema9Bars <= 0 || wmaBars <= 0 || ema200Bars <= 0 || atrRegBars <= 0)
const int adxBars = InpAdxFilterEnabled ? BarsCalculated(h_ADX) : 1;
if (rsiBars <= 0 || ema9Bars <= 0 || wmaBars <= 0 || ema200Bars <= 0 || atrRegBars <= 0
|| adxBars <= 0)
{
static datetime lastWarn = 0;
if (TimeCurrent() - lastWarn > 30)
{
PrintFormat("[RsiMomEA] Source not ready: RSI=%d EMA9=%d WMA45=%d EMA200=%d ATRreg=%d (rates=%d)",
rsiBars, ema9Bars, wmaBars, ema200Bars, atrRegBars, rates_total);
PrintFormat("[RsiMomEA] Source not ready: RSI=%d EMA9=%d WMA45=%d EMA200=%d ATRreg=%d ADX=%d (rates=%d)",
rsiBars, ema9Bars, wmaBars, ema200Bars, atrRegBars, adxBars, rates_total);
lastWarn = TimeCurrent();
}
return 0;
}
const int srcMin = MathMin(MathMin(MathMin(MathMin(rsiBars, ema9Bars), wmaBars), ema200Bars), atrRegBars);
int srcMin = MathMin(MathMin(MathMin(MathMin(rsiBars, ema9Bars), wmaBars), ema200Bars), atrRegBars);
if(InpAdxFilterEnabled)
srcMin = MathMin(srcMin, adxBars);
const int copyN = MathMin(srcMin, rates_total);
if (copyN < minBars) return 0;
@@ -1405,11 +1622,17 @@ int OnInit()
const long chInit = ActChart();
ObjectsDeleteAll(chInit, OBJ_PREFIX + "RSN_");
Signal_DebugConfigureChart(chInit);
ObjectsDeleteAll(chInit, DBG_PREFIX);
if(DebugMarksEffective())
Signal_DebugConfigureChart(chInit);
Panel_CreateAll();
Stats_CreateObjects();
Stats_UpdateDisplay();
if(InpShowPanel)
Panel_CreateAll();
if(InpShowStats)
{
Stats_CreateObjects();
Stats_UpdateDisplay();
}
const int spr = Env_CurrentSpreadPts();
string envWhy = "";
const bool envNow = Env_AllowsTradeAtBar(MathMax(1, InpSignalBarShift), envWhy);
@@ -1417,13 +1640,17 @@ int OnInit()
Print("[RsiMomEA] Journal CSV: ", RsiMomJournal_TradesPath(_Symbol, _Period),
" | summary: ", RsiMomJournal_SummaryPath(_Symbol, _Period), " (FILE_COMMON)");
Print("[RsiMomEA] Init OK v4.14 — RSI×WMA45 + ", InpPhaseFilterEnabled ? "5phase" : "core",
Print("[RsiMomEA] Init OK v", EA_VERSION_STR, " — entry=", EntryModeLabel(),
" | RSI×WMA45 + ", InpPhaseFilterEnabled ? "5phase" : "core",
" | EMA200=", InpTrendFilterEnabled ? "on" : "OFF",
" | session=", InpSessionFilterEnabled ? "on" : "OFF",
" | debugMarks=", InpDebugMarkSignals ? "on" : "off",
" | ATR+EMA200+phiên | trade=", InpTradeEnabled ? "on" : "off", " risk%=", InpRiskPercent,
" ATRexp=", InpAtrExpFilterEnabled
? StringFormat("ratio>=%.2f rise%d cmp%d", InpAtrExpMinRatio, InpAtrExpRiseBars, InpAtrExpCompareBars) : "OFF",
" ADX=", InpAdxFilterEnabled
? StringFormat(">=%.0f DI=%s rise%d", InpAdxMinLevel,
InpAdxRequireDiDirection ? "on" : "off", InpAdxRiseBars) : "OFF",
" session=", InpSessionFilterEnabled
? StringFormat("L%d-%d NY%d-%d avoid%d%s", InpLondonStartHour, InpLondonEndHour,
InpNYStartHour, InpNYEndHour, InpSessionAvoidLastMin,
@@ -1458,8 +1685,11 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
void OnTick()
{
Pending_EnvCancelIfBad();
Pending_ManageExpiry();
if(EntryModeIsLimit())
{
Pending_EnvCancelIfBad();
Pending_ManageExpiry();
}
Position_ManageAt1R();
const datetime t0 = iTime(_Symbol, _Period, 0);
@@ -1650,8 +1880,33 @@ bool LimitPriceValid(const bool isBuy, const double limitPx)
return (limitPx > tk.bid + md);
}
//+------------------------------------------------------------------+
bool EntryModeIsLimit()
{
return (InpEntryMode == RSI_MOM_ENTRY_LIMIT_BODY50);
}
bool EntryModeIsMarket()
{
return (InpEntryMode == RSI_MOM_ENTRY_MARKET);
}
string EntryModeLabel()
{
return EntryModeIsMarket() ? "Market" : "Limit 50% body";
}
//+------------------------------------------------------------------+
void TradeExecuteOrder(const bool isBuy)
{
if(EntryModeIsMarket())
TradeExecuteMarketOrder(isBuy);
else
TradeExecuteLimitOrder(isBuy);
}
//+------------------------------------------------------------------+
void TradeExecuteLimitOrder(const bool isBuy)
{
if (InpOnePositionFlat && HasMyMagicPositionOrPending())
return;
@@ -1720,6 +1975,61 @@ void TradeExecuteOrder(const bool isBuy)
}
}
//+------------------------------------------------------------------+
void TradeExecuteMarketOrder(const bool isBuy)
{
if(InpOnePositionFlat && CountMyMagicPositions() > 0)
return;
string envWhy = "";
if(!Env_AllowsTradeNow(envWhy))
{
Print("[RsiMomEA] Trade skip ", isBuy ? "BUY" : "SELL", ": ", envWhy,
"(spread=", Env_CurrentSpreadPts(), " pts)");
return;
}
MqlTick tk;
if(!SymbolInfoTick(_Symbol, tk))
{
Print("[RsiMomEA] Trade skip: không lấy được tick");
return;
}
const int dig = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const double entryPx = NormalizeDouble(isBuy ? tk.ask : tk.bid, dig);
double sl = 0.0, tp = 0.0;
if(!NearestSwingSlTp(isBuy, entryPx, dig, sl, tp))
{
Print("[RsiMomEA] Trade skip: SL/TP swing không hợp lệ");
return;
}
if(!StopsValid(isBuy, entryPx, sl, tp))
{
Print("[RsiMomEA] Trade skip: STOPS_LEVEL / FREEZE");
return;
}
double vol = VolumeForRiskPercent(isBuy, entryPx, sl);
vol = NormalizeLots(vol);
if(vol <= 0.0)
{
Print("[RsiMomEA] Trade skip: volume=0");
return;
}
const bool ok = isBuy
? g_trade.Buy(vol, _Symbol, 0.0, sl, tp, "RsiMom BUY mkt")
: g_trade.Sell(vol, _Symbol, 0.0, sl, tp, "RsiMom SELL mkt");
if(!ok)
Print("[RsiMomEA] Market fail ", g_trade.ResultRetcode(), " ", g_trade.ResultComment());
else
Print("[RsiMomEA] Market OK #", g_trade.ResultDeal(), " ", isBuy ? "BUY" : "SELL",
" @~", DoubleToString(entryPx, dig),
" vol=", vol, " SL=", DoubleToString(sl, dig), " TP=", DoubleToString(tp, dig));
}
//+------------------------------------------------------------------+
void TradeTryOnBarOpen(const int calcRet)
{
@@ -2063,7 +2373,8 @@ void Stats_UpdateDisplay()
ObjectSetString(ch, STAT_L1, OBJPROP_TEXT, "");
ObjectSetString(ch, STAT_L2, OBJPROP_TEXT, "");
ObjectSetString(ch, STAT_L3, OBJPROP_TEXT, "");
ChartRedraw(ch);
if(ChartRedrawEffective())
ChartRedraw(ch);
return;
}
+20 -1
View File
@@ -94,6 +94,7 @@ Không có cross → không xét tiếp.
|-------|------------------|------------------------|
| `InpTrendFilterEnabled` | `false` | BUY: `InpTrendConfirmBars` nến liên tiếp có `Close > EMA200`. SELL: `Close < EMA200`. |
| `InpAtrExpFilterEnabled` | `true` | ATR(shift) / ATR(shift+cmp) ≥ `InpAtrExpMinRatio` **và** ATR tăng liên tiếp `InpAtrExpRiseBars` nến. |
| `InpAdxFilterEnabled` | `true` | ADX ≥ `InpAdxMinLevel`; BUY +DI>DI, SELL ngược; tùy chọn ADX tăng / max / spread DI. |
| `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`):**
@@ -458,4 +459,22 @@ Debug: SKIP `ATR co` hoặc tooltip `ATR:FAIL`. Nếu **0 lệnh** sau bật fil
---
*Tài liệu đồng bộ với mã nguồn v4.25.*
---
## 17. Lọc ADX (trend mạnh)
**Mục tiêu:** Chỉ pullback khi thị trường **có trend** (ADX đủ cao) và **đúng hướng** (+DI/DI).
| Input | Mặc định | Ý nghĩa |
|-------|----------|---------|
| `InpAdxMinLevel` | 22 | ADX &lt; 20 thường sideway; 2235 vùng trend ổn |
| `InpAdxRequireDiDirection` | true | BUY: +DI &gt; DI; SELL: DI &gt; +DI |
| `InpAdxMinDiSpread` | 0 | Chênh +DI(DI) tối thiểu (thử 510) |
| `InpAdxRiseBars` | 0 | ADX tăng vs N nến (12 = trend đang mạnh lên) |
| `InpAdxMaxLevel` | 0 | 0=tắt; ví dụ 45 tránh vào khi trend quá già |
Debug: `ADX:OK(28.5 +DI=32.0 -DI=18.0)` hoặc SKIP `ADX yếu`.
---
*Tài liệu đồng bộ với mã nguồn v4.29.*