Update all
This commit is contained in:
+263
-24
@@ -1,11 +1,12 @@
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| EmaPullbackBasic.mq5 — EMA50 pullback EA (phased build) |
|
//| EmaPullbackBasic.mq5 — EMA50 pullback EA (phased build) |
|
||||||
//| Phase 4: breakout khỏi nến touch (cửa sổ N nến) |
|
//| 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 copyright "EMA Pullback Basic"
|
||||||
#property version "0.52"
|
#property version "0.58"
|
||||||
#property description "EMA pullback + reject entry, 1% risk, TP 1.5R"
|
#property description "Phase 6: filters + partial 2R + breakeven"
|
||||||
|
|
||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
|
|
||||||
@@ -54,10 +55,24 @@ input bool InpRequireBreakoutCandleColor = true; // Buy: bullish, Sel
|
|||||||
input group "=== Risk (Phase 5) ==="
|
input group "=== Risk (Phase 5) ==="
|
||||||
input bool InpTradeEnabled = true;
|
input bool InpTradeEnabled = true;
|
||||||
input double InpRiskPercent = 1.0; // R = % balance
|
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 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 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 group "=== EA ==="
|
||||||
input ulong InpMagic = 20260522;
|
input ulong InpMagic = 20260522;
|
||||||
input int InpSlippage = 10;
|
input int InpSlippage = 10;
|
||||||
@@ -87,6 +102,14 @@ int g_statTotal = 0;
|
|||||||
int g_statTP = 0;
|
int g_statTP = 0;
|
||||||
int g_statSL = 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_";
|
const string OBJ_PREFIX = "EPB_";
|
||||||
|
|
||||||
string StatPfx() { return "EPB_ST_" + IntegerToString((int)InpMagic) + "_"; }
|
string StatPfx() { return "EPB_ST_" + IntegerToString((int)InpMagic) + "_"; }
|
||||||
@@ -496,9 +519,125 @@ bool HasOurPosition() {
|
|||||||
return false;
|
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) {
|
double VolumeForRisk(const bool isBuy, const double entry, const double sl) {
|
||||||
if(MathAbs(entry - sl) < _Point) return 0.0;
|
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;
|
double profit = 0.0;
|
||||||
if(!OrderCalcProfit(isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, _Symbol, 1.0, entry, sl, profit))
|
if(!OrderCalcProfit(isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, _Symbol, 1.0, entry, sl, profit))
|
||||||
return 0.0;
|
return 0.0;
|
||||||
@@ -596,26 +735,111 @@ bool OpenMarketFromReject(const bool isBuy,
|
|||||||
const datetime t = iTime(_Symbol, _Period, rejectShift);
|
const datetime t = iTime(_Symbol, _Period, rejectShift);
|
||||||
const double px = isBuy ? iHigh(_Symbol, _Period, rejectShift) : iLow(_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");
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ManageOpenPosition() {
|
void ManageOpenPosition() {
|
||||||
if(InpMaxBarsInTrade <= 0 || !HasOurPosition())
|
if(!HasOurPosition()) {
|
||||||
|
ResetPositionTrack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncPositionTrack();
|
||||||
|
const ulong ticket = FindOurPositionTicket();
|
||||||
|
if(ticket == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
TryPartialCloseAndBreakeven(ticket);
|
||||||
const ulong t = PositionGetTicket(i);
|
|
||||||
if(t == 0 || !PositionSelectByTicket(t)) continue;
|
|
||||||
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
|
||||||
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
|
|
||||||
|
|
||||||
const datetime openT = (datetime)PositionGetInteger(POSITION_TIME);
|
if(InpMaxBarsInTrade <= 0)
|
||||||
const int barsIn = iBarShift(_Symbol, _Period, openT, false);
|
return;
|
||||||
if(barsIn >= 0 && barsIn >= InpMaxBarsInTrade) {
|
|
||||||
g_trade.PositionClose(t);
|
if(!PositionSelectByTicket(ticket))
|
||||||
Print("[EPB] Đóng lệnh — quá ", InpMaxBarsInTrade, " bar");
|
return;
|
||||||
}
|
|
||||||
break;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string allowWhy = "";
|
||||||
|
if(!IsAllowedNewEntry(allowWhy)) {
|
||||||
|
g_setupDetail = allowWhy;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const int touchShift = FindTouchBarShift();
|
const int touchShift = FindTouchBarShift();
|
||||||
string why = "";
|
string why = "";
|
||||||
const bool isBuy = (g_setupDir > 0);
|
const bool isBuy = (g_setupDir > 0);
|
||||||
@@ -923,7 +1153,10 @@ void UpdateChartComment(const int shift,
|
|||||||
" shift=", touchShift, "\n",
|
" shift=", touchShift, "\n",
|
||||||
"Trade: ", (InpTradeEnabled ? "ON" : "OFF"),
|
"Trade: ", (InpTradeEnabled ? "ON" : "OFF"),
|
||||||
" Pos: ", (HasOurPosition() ? "YES" : "NO"),
|
" 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),
|
"Close/EMA/ADX: ", DoubleToString(iClose(_Symbol, _Period, shift), _Digits),
|
||||||
" / ", DoubleToString(ema, _Digits),
|
" / ", DoubleToString(ema, _Digits),
|
||||||
" / ", DoubleToString(adx, 1)
|
" / ", DoubleToString(adx, 1)
|
||||||
@@ -931,8 +1164,6 @@ void UpdateChartComment(const int shift,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnNewClosedBar() {
|
void OnNewClosedBar() {
|
||||||
ManageOpenPosition();
|
|
||||||
|
|
||||||
const int shift = 1;
|
const int shift = 1;
|
||||||
|
|
||||||
double ema = 0.0, adx = 0.0, atr = 0.0;
|
double ema = 0.0, adx = 0.0, atr = 0.0;
|
||||||
@@ -985,6 +1216,9 @@ int OnInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ResetSetupState("init");
|
ResetSetupState("init");
|
||||||
|
ResetPositionTrack();
|
||||||
|
g_equityPeak = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||||
|
g_riskScale = 1.0;
|
||||||
|
|
||||||
double probe = 0.0;
|
double probe = 0.0;
|
||||||
string err = "";
|
string err = "";
|
||||||
@@ -1002,12 +1236,14 @@ int OnInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrintFormat(
|
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",
|
InpTradeEnabled ? "ON" : "OFF",
|
||||||
InpRiskPercent,
|
InpRiskPercent,
|
||||||
InpTpRR,
|
InpTpRR,
|
||||||
InpMaxBarsWaitBreakout,
|
InpMaxSpreadPoints,
|
||||||
InpBreakoutUseTouchHighLow ? "Close>" : "Close>Close"
|
InpSessionStartHour,
|
||||||
|
InpSessionEndHour,
|
||||||
|
InpPartialCloseFrac * 100.0
|
||||||
);
|
);
|
||||||
return INIT_SUCCEEDED;
|
return INIT_SUCCEEDED;
|
||||||
}
|
}
|
||||||
@@ -1033,6 +1269,9 @@ void OnTradeTransaction(const MqlTradeTransaction &trans,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnTick() {
|
void OnTick() {
|
||||||
|
UpdateRiskScale();
|
||||||
|
ManageOpenPosition();
|
||||||
|
|
||||||
const datetime barOpen = iTime(_Symbol, _Period, 0);
|
const datetime barOpen = iTime(_Symbol, _Period, 0);
|
||||||
if(barOpen == g_lastBarTime)
|
if(barOpen == g_lastBarTime)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
//| CHƯA CÓ TRONG DỰ ÁN: đặt lệnh, LTF, session, alert |
|
//| CHƯA CÓ TRONG DỰ ÁN: đặt lệnh, LTF, session, alert |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
#property copyright "HyperICT"
|
#property copyright "HyperICT"
|
||||||
#property version "1.11"
|
#property version "1.13"
|
||||||
#property description "HTF structure | Fib=init only | pivot confirm=InpSwingRange"
|
#property description "HTF structure | Fib=init only | pivot confirm=InpSwingRange"
|
||||||
|
|
||||||
#include <HyperICT/Config.mqh>
|
#include <HyperICT/Config.mqh>
|
||||||
|
|||||||
Binary file not shown.
+365
-54
@@ -1,16 +1,25 @@
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| RsiMomentumEA.mq5 |
|
//| RsiMomentumEA.mq5 |
|
||||||
//| EA tự động — logic độc lập (không đọc RsiMomentumIndicator). |
|
//| 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 copyright "RsiMomentumEA"
|
||||||
#property version "4.27"
|
#property version "4.31"
|
||||||
|
|
||||||
#include <Trade/Trade.mqh>
|
#include <Trade/Trade.mqh>
|
||||||
#include <RsiMom/TradeJournal.mqh>
|
#include <RsiMom/TradeJournal.mqh>
|
||||||
#include <RsiMom/PhaseEntry.mqh>
|
#include <RsiMom/PhaseEntry.mqh>
|
||||||
|
#include <RsiMom/AdxFilter.mqh>
|
||||||
|
#include <RsiMom/SwingStructure.mqh>
|
||||||
#include <RsiMom/SignalDebug.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 (khớp Indicators/RsiMomentumIndicator/Lib/Inputs.mqh)
|
||||||
input group "Chỉ báo"
|
input group "Chỉ báo"
|
||||||
input int InpRSIPeriod = 14;
|
input int InpRSIPeriod = 14;
|
||||||
@@ -19,14 +28,29 @@ input int InpWMA45Period = 45;
|
|||||||
input int InpEMATrendPeriod = 200;
|
input int InpEMATrendPeriod = 200;
|
||||||
|
|
||||||
input group "Bộ lọc tín hiệu (RSI + ATR + trend EMA200)"
|
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 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 InpAtrExpPeriod = 14;
|
||||||
input int InpAtrExpCompareBars = 3; // so ATR[shift] vs ATR[shift+N]
|
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 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 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 22–35+)
|
||||||
|
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 (5–10 = chặt hơn)
|
||||||
|
input int InpAdxRiseBars = 0; // ADX tăng vs N nến trước (0=tắt, 1–2 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 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 bool InpRsiObOsFilterEnabled = true; // BUY khi RSI<70 | SELL khi RSI>30
|
||||||
input double InpRSIOverbought = 70.0; // RSI ≥ ngưỡng → bỏ BUY
|
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 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: EMA9–WMA45 tối đa khi cắt (lớn hơn = lỏng)
|
input double InpPhaseMaxEma9WmaGap = 16.0; // P5: EMA9–WMA45 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 int InpSignalBarShift = 1; // nến tín hiệu (1 = nến vừa đóng)
|
||||||
|
|
||||||
input group "Quản lý Limit pending"
|
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 (trước: 20)
|
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 group "Debug — đánh dấu RSI×WMA45 (tắt = backtest nhanh)"
|
||||||
input bool InpDebugMarkSignals = true; // mọi cross: OK xanh | SIG vàng | SKIP đỏ + nhãn phase fail
|
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 (dấu cũ vẫn giữ)
|
input int InpDebugMarkMaxBars = 400; // chỉ tạo mới trong N nến gần nhất
|
||||||
input bool InpDebugLogExperts = true; // 1 dòng Experts / nến tín hiệu (không lặp mỗi tick)
|
input bool InpDebugLogExperts = false; // 1 dòng Experts / nến tín hiệu
|
||||||
input bool InpDebugHoverHint = true; // rê chuột lên dấu X: panel góc dưới-trái + tooltip
|
input bool InpDebugHoverHint = false; // rê chuột lên dấu X
|
||||||
|
|
||||||
input group "Mũi tên giao cắt"
|
input group "Mũi tên giao cắt"
|
||||||
input color InpArrowUpColor = clrLime;
|
input color InpArrowUpColor = clrLime;
|
||||||
@@ -66,7 +93,7 @@ input int InpArrowOffsetPts = 30;
|
|||||||
input int InpArrowSize = 1;
|
input int InpArrowSize = 1;
|
||||||
|
|
||||||
input group "Panel trạng thái (góc trên-trái)"
|
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 InpPanelFontSize = 8;
|
||||||
input int InpPanelLinePad = 14; // khoảng cách dọc giữa các dòng
|
input int InpPanelLinePad = 14; // khoảng cách dọc giữa các dòng
|
||||||
input int InpPanelLeftMargin = 8;
|
input int InpPanelLeftMargin = 8;
|
||||||
@@ -75,9 +102,9 @@ input color InpPanelColorEMA9 = clrGold;
|
|||||||
input color InpPanelColorWMA45 = clrDodgerBlue;
|
input color InpPanelColorWMA45 = clrDodgerBlue;
|
||||||
|
|
||||||
input group "Cảnh báo / Notification (khi có entry mới)"
|
input group "Cảnh báo / Notification (khi có entry mới)"
|
||||||
input bool InpAlertPush = true;
|
input bool InpAlertPush = false;
|
||||||
input bool InpAlertPopup = true;
|
input bool InpAlertPopup = false;
|
||||||
input bool InpAlertSound = true;
|
input bool InpAlertSound = false;
|
||||||
input string InpSoundBuy = "alert.wav";
|
input string InpSoundBuy = "alert.wav";
|
||||||
input string InpSoundSell = "alert2.wav";
|
input string InpSoundSell = "alert2.wav";
|
||||||
input bool InpAlertEmail = false;
|
input bool InpAlertEmail = false;
|
||||||
@@ -87,7 +114,7 @@ input group "Giao dịch tự động"
|
|||||||
input bool InpTradeEnabled = true;
|
input bool InpTradeEnabled = true;
|
||||||
input ulong InpMagic = 202602;
|
input ulong InpMagic = 202602;
|
||||||
input double InpRiskPercent = 1; // % balance mất nếu SL khớp (theo lot tính từ SL)
|
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 InpSwingMaxBars = 30; // quét swing pivot / fallback min-max
|
||||||
input int InpSlippagePoints = 30;
|
input int InpSlippagePoints = 30;
|
||||||
input bool InpOnePositionFlat = true;
|
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 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 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 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 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 bool InpSlAtrAddSpread = true; // cộng thêm buffer spread vào SL
|
||||||
|
|
||||||
input group "Xuất CSV thống kê (FILE_COMMON)"
|
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 thúc test/EA
|
input bool InpExportTradeJournal = false; // bật lại khi cần phân tích CSV (chậm hơn một chút)
|
||||||
input bool InpJournalResetOnInit = true; // Tester: xóa CSV cũ mỗi lần chạy backtest mới
|
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 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 InpStatFontSize = 9;
|
||||||
input int InpStatLinePad = 26; // khoảng cách dọc giữa các dòng (pixel)
|
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ê
|
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_EMA200 = INVALID_HANDLE;
|
||||||
int h_ATR = INVALID_HANDLE;
|
int h_ATR = INVALID_HANDLE;
|
||||||
int h_ATR_Regime = INVALID_HANDLE;
|
int h_ATR_Regime = INVALID_HANDLE;
|
||||||
|
int h_ADX = INVALID_HANDLE;
|
||||||
|
|
||||||
const string OBJ_PREFIX = "RsiMomEA_";
|
const string OBJ_PREFIX = "RsiMomEA_";
|
||||||
const string DBG_PREFIX = OBJ_PREFIX + "DBG_";
|
const string DBG_PREFIX = OBJ_PREFIX + "DBG_";
|
||||||
#define PANEL_LINE_COUNT 22
|
#define PANEL_LINE_COUNT 25
|
||||||
#define PANEL_IDX_MARKET 16 // dòng 16+ = RSI / signal (sau block trạng thái)
|
#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 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)
|
datetime g_dbgLogBarTime = 0; // chống spam Experts: 1 dòng / (nến, BUY|SELL)
|
||||||
int g_dbgLogSide = 0; // 1=BUY, -1=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);
|
double VolumeForRiskPercent(const bool isBuy, const double entryRef, const double slPrice);
|
||||||
void TradeTryOnBarOpen(const int calcRet);
|
void TradeTryOnBarOpen(const int calcRet);
|
||||||
void TradeExecuteOrder(const bool isBuy);
|
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 Signal_BodyMidPrice(const int shift, double &midOut);
|
||||||
bool LimitPriceValid(const bool isBuy, const double limitPx);
|
bool LimitPriceValid(const bool isBuy, const double limitPx);
|
||||||
void Pending_ManageExpiry();
|
void Pending_ManageExpiry();
|
||||||
@@ -233,6 +266,11 @@ bool AtrExp_GetAt(const int shift, double &atr);
|
|||||||
bool AtrExp_IsExpandingAt(const int shift);
|
bool AtrExp_IsExpandingAt(const int shift);
|
||||||
bool AtrExp_AllowsAt(const int shift);
|
bool AtrExp_AllowsAt(const int shift);
|
||||||
bool AtrExp_AllowsNow();
|
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();
|
int Env_CurrentSpreadPts();
|
||||||
bool Env_SpreadAllows();
|
bool Env_SpreadAllows();
|
||||||
bool Env_AllowsSessionAt(const datetime t, string &why);
|
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)");
|
Print("[RsiMomEA] Không tạo được handle ATR (regime)");
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +347,8 @@ void Handles_ReleaseAll()
|
|||||||
if (h_EMA200 != INVALID_HANDLE) IndicatorRelease(h_EMA200);
|
if (h_EMA200 != INVALID_HANDLE) IndicatorRelease(h_EMA200);
|
||||||
if (h_ATR != INVALID_HANDLE) IndicatorRelease(h_ATR);
|
if (h_ATR != INVALID_HANDLE) IndicatorRelease(h_ATR);
|
||||||
if (h_ATR_Regime != INVALID_HANDLE) IndicatorRelease(h_ATR_Regime);
|
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)),
|
MathMax(1, InpSignalBarShift)),
|
||||||
Panel_ClrOnOff(InpTradeEnabled));
|
Panel_ClrOnOff(InpTradeEnabled));
|
||||||
|
|
||||||
Panel_SetLine(ln++, StringFormat("Risk %.2f%% | R:R 1:%.2f | Limit 50%% body Exp %d bar",
|
Panel_SetLine(ln++, StringFormat("Entry: %s", EntryModeLabel()),
|
||||||
InpRiskPercent, InpRewardRiskRatio, InpLimitExpireBars),
|
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);
|
clrSilver);
|
||||||
|
|
||||||
Panel_SetLine(ln++, StringFormat("Manage @1R: %s (partial %.0f%%)",
|
Panel_SetLine(ln++, StringFormat("Manage @1R: %s (partial %.0f%%)",
|
||||||
@@ -453,8 +502,19 @@ void Panel_UpdateStatus()
|
|||||||
MathMax(1, InpTrendConfirmBars)),
|
MathMax(1, InpTrendConfirmBars)),
|
||||||
InpPhaseFilterEnabled ? clrWhite : clrDimGray);
|
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(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),
|
Panel_FmtOnOff(InpRsiObOsFilterEnabled),
|
||||||
InpRSIOverbought, InpRSIOversold),
|
InpRSIOverbought, InpRSIOversold),
|
||||||
clrSilver);
|
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("RSI %6.2f", buf_RSI[1]), clrMediumOrchid);
|
||||||
Panel_SetLine(ln++, StringFormat("EMA9 %6.2f", buf_EMA9[1]), InpPanelColorEMA9);
|
Panel_SetLine(ln++, StringFormat("EMA9 %6.2f", buf_EMA9[1]), InpPanelColorEMA9);
|
||||||
Panel_SetLine(ln++, StringFormat("WMA45 %6.2f", buf_WMA45[1]), InpPanelColorWMA45);
|
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",
|
Panel_SetLine(ln++, StringFormat("EMA200 close[1] %s %.5f",
|
||||||
panelTrendUp ? ">" : (panelTrendDown ? "<" : "~"),
|
panelTrendUp ? ">" : (panelTrendDown ? "<" : "~"),
|
||||||
ema200Arr[1]), trendClr);
|
ema200Arr[1]), trendClr);
|
||||||
@@ -707,6 +778,84 @@ bool AtrExp_AllowsNow()
|
|||||||
return AtrExp_AllowsAt(1);
|
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) |
|
//| 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,
|
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)
|
if(valid)
|
||||||
{
|
{
|
||||||
string s = InpPhaseFilterEnabled
|
string s = InpPhaseFilterEnabled
|
||||||
? "RSI↑WMA45 | 5phase OK | EMA200 UP"
|
? "RSI↑WMA45 | 5phase OK | EMA200 UP"
|
||||||
: "RSI↑WMA45 EMA9<WMA45 | 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)
|
if(InpAtrExpFilterEnabled)
|
||||||
s += StringFormat(" | ATR↑ x%.0f%% %d bar", (InpAtrExpMinRatio - 1.0) * 100.0, InpAtrExpRiseBars);
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,6 +1095,10 @@ string Signal_ReasonBuy(const bool trendUp, const bool ema9CoreOk, const bool ph
|
|||||||
f += "ngược EMA200 ";
|
f += "ngược EMA200 ";
|
||||||
if(InpAtrExpFilterEnabled && !atrExpOk)
|
if(InpAtrExpFilterEnabled && !atrExpOk)
|
||||||
f += "ATR co ";
|
f += "ATR co ";
|
||||||
|
if(InpAdxFilterEnabled && !adxOk)
|
||||||
|
f += "ADX yếu ";
|
||||||
|
if(InpSwingStructFilterEnabled && !swingOk)
|
||||||
|
f += "2 đáy ";
|
||||||
if(InpRsiObOsFilterEnabled && !rsiObOsOk)
|
if(InpRsiObOsFilterEnabled && !rsiObOsOk)
|
||||||
f += "RSI quá mua ";
|
f += "RSI quá mua ";
|
||||||
if(!envOk)
|
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,
|
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)
|
if(valid)
|
||||||
{
|
{
|
||||||
string s = InpPhaseFilterEnabled
|
string s = InpPhaseFilterEnabled
|
||||||
? "RSI↓WMA45 | 5phase OK | EMA200 DOWN"
|
? "RSI↓WMA45 | 5phase OK | EMA200 DOWN"
|
||||||
: "RSI↓WMA45 EMA9>WMA45 | 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)
|
if(InpAtrExpFilterEnabled)
|
||||||
s += StringFormat(" | ATR↑ x%.0f%% %d bar", (InpAtrExpMinRatio - 1.0) * 100.0, InpAtrExpRiseBars);
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,6 +1136,10 @@ string Signal_ReasonSell(const bool trendDown, const bool ema9CoreOk, const bool
|
|||||||
f += "ngược EMA200 ";
|
f += "ngược EMA200 ";
|
||||||
if(InpAtrExpFilterEnabled && !atrExpOk)
|
if(InpAtrExpFilterEnabled && !atrExpOk)
|
||||||
f += "ATR co ";
|
f += "ATR co ";
|
||||||
|
if(InpAdxFilterEnabled && !adxOk)
|
||||||
|
f += "ADX yếu ";
|
||||||
|
if(InpSwingStructFilterEnabled && !swingOk)
|
||||||
|
f += "2 đỉnh ";
|
||||||
if(InpRsiObOsFilterEnabled && !rsiObOsOk)
|
if(InpRsiObOsFilterEnabled && !rsiObOsOk)
|
||||||
f += "RSI quá bán ";
|
f += "RSI quá bán ";
|
||||||
if(!envOk)
|
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))
|
if(DebugMarksEffective() && Signal_DebugMarkExists(ch, DBG_PREFIX, barTime, isBuy))
|
||||||
return;
|
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;
|
SignalEvalResult ev;
|
||||||
if(isBuy)
|
if(isBuy)
|
||||||
ev = Signal_EvaluateBuyAt(shift, rates_total, trendN,
|
ev = Signal_EvaluateBuyAt(shift, rates_total, trendN,
|
||||||
buf_RSI, buf_EMA9, buf_WMA45, closeArr, ema200Arr,
|
buf_RSI, buf_EMA9, buf_WMA45, closeArr, ema200Arr,
|
||||||
phaseCfg, InpPhaseFilterEnabled, InpTrendFilterEnabled,
|
phaseCfg, InpPhaseFilterEnabled, InpTrendFilterEnabled,
|
||||||
InpAtrExpFilterEnabled, InpRsiObOsFilterEnabled,
|
InpAtrExpFilterEnabled, InpAdxFilterEnabled, adxOkBar,
|
||||||
|
adxVal, plusDi, minusDi, adxWhy,
|
||||||
|
InpSwingStructFilterEnabled, swingOkBar,
|
||||||
|
swingOld, swingNew, swingWhy,
|
||||||
|
InpRsiObOsFilterEnabled,
|
||||||
InpRSIOverbought, InpRSIOversold,
|
InpRSIOverbought, InpRSIOversold,
|
||||||
InpSessionFilterEnabled,
|
InpSessionFilterEnabled,
|
||||||
atrExpOkBar, sessionAtBar, sessionFailWhy);
|
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,
|
ev = Signal_EvaluateSellAt(shift, rates_total, trendN,
|
||||||
buf_RSI, buf_EMA9, buf_WMA45, closeArr, ema200Arr,
|
buf_RSI, buf_EMA9, buf_WMA45, closeArr, ema200Arr,
|
||||||
phaseCfg, InpPhaseFilterEnabled, InpTrendFilterEnabled,
|
phaseCfg, InpPhaseFilterEnabled, InpTrendFilterEnabled,
|
||||||
InpAtrExpFilterEnabled, InpRsiObOsFilterEnabled,
|
InpAtrExpFilterEnabled, InpAdxFilterEnabled, adxOkBar,
|
||||||
|
adxVal, plusDi, minusDi, adxWhy,
|
||||||
|
InpSwingStructFilterEnabled, swingOkBar,
|
||||||
|
swingOld, swingNew, swingWhy,
|
||||||
|
InpRsiObOsFilterEnabled,
|
||||||
InpRSIOverbought, InpRSIOversold,
|
InpRSIOverbought, InpRSIOversold,
|
||||||
InpSessionFilterEnabled,
|
InpSessionFilterEnabled,
|
||||||
atrExpOkBar, sessionAtBar, sessionFailWhy);
|
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 coreBuyOk = InpPhaseFilterEnabled ? phaseBuyOk : ema9BuyOk;
|
||||||
const bool coreSellOk = InpPhaseFilterEnabled ? phaseSellOk : ema9SellOk;
|
const bool coreSellOk = InpPhaseFilterEnabled ? phaseSellOk : ema9SellOk;
|
||||||
const bool atrExpOkBar = AtrExp_AllowsAt(i);
|
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 rsiOkBuy = Signal_RsiOkBuyAt(i);
|
||||||
const bool rsiOkSell = Signal_RsiOkSellAt(i);
|
const bool rsiOkSell = Signal_RsiOkSellAt(i);
|
||||||
string envWhy = "";
|
string envWhy = "";
|
||||||
@@ -1134,7 +1327,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
|
|||||||
|
|
||||||
if(crossUpWma45)
|
if(crossUpWma45)
|
||||||
{
|
{
|
||||||
const bool validBuy = coreBuyOk && trendUp && atrExpOkBar && rsiOkBuy && envOkBar;
|
const bool validBuy = coreBuyOk && trendUp && atrExpOkBar && adxOkBuy
|
||||||
|
&& swingOkBuy && rsiOkBuy && envOkBar;
|
||||||
|
|
||||||
if(dbgMarks && i <= dbgMax)
|
if(dbgMarks && i <= dbgMax)
|
||||||
{
|
{
|
||||||
@@ -1148,7 +1342,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
|
|||||||
if(validBuy)
|
if(validBuy)
|
||||||
{
|
{
|
||||||
const string reason = Signal_ReasonBuy(trendUp, ema9BuyOk, phaseBuyOk, phaseFailBuy,
|
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 double arrowPrice = lowArr[i] - arrowOffset;
|
||||||
const string arrowName = OBJ_PREFIX + "CR_" + IntegerToString((int)timeArr[i]);
|
const string arrowName = OBJ_PREFIX + "CR_" + IntegerToString((int)timeArr[i]);
|
||||||
if(ObjectFind(ch, arrowName) < 0)
|
if(ObjectFind(ch, arrowName) < 0)
|
||||||
@@ -1168,7 +1363,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
|
|||||||
|
|
||||||
if(crossDownWma45)
|
if(crossDownWma45)
|
||||||
{
|
{
|
||||||
const bool validSell = coreSellOk && trendDown && atrExpOkBar && rsiOkSell && envOkBar;
|
const bool validSell = coreSellOk && trendDown && atrExpOkBar && adxOkSell
|
||||||
|
&& swingOkSell && rsiOkSell && envOkBar;
|
||||||
|
|
||||||
if(dbgMarks && i <= dbgMax)
|
if(dbgMarks && i <= dbgMax)
|
||||||
{
|
{
|
||||||
@@ -1182,7 +1378,8 @@ void SignalScan_Run(const int barsToScan, const int rates_total, const int need,
|
|||||||
if(validSell)
|
if(validSell)
|
||||||
{
|
{
|
||||||
const string reason = Signal_ReasonSell(trendDown, ema9SellOk, phaseSellOk, phaseFailSell,
|
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 double arrowPrice = highArr[i] + arrowOffset;
|
||||||
const string arrowName = OBJ_PREFIX + "CR_" + IntegerToString((int)timeArr[i]);
|
const string arrowName = OBJ_PREFIX + "CR_" + IntegerToString((int)timeArr[i]);
|
||||||
if(ObjectFind(ch, arrowName) < 0)
|
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 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 passEnvUp = 0, passEnvDn = 0;
|
||||||
int passEma9Up = 0, passEma9Dn = 0;
|
int passEma9Up = 0, passEma9Dn = 0;
|
||||||
int passPhaseUp = 0, passPhaseDn = 0;
|
int passPhaseUp = 0, passPhaseDn = 0;
|
||||||
int validBuy = 0, validSell = 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
|
const int phaseLb = InpPhaseFilterEnabled
|
||||||
? MathMax(InpPhaseExpandLookback, InpPhaseCoilLookback) + InpPhaseWmaFlatBars * 2 + 3
|
? MathMax(InpPhaseExpandLookback, InpPhaseCoilLookback) + InpPhaseWmaFlatBars * 2 + 3
|
||||||
: 0;
|
: 0;
|
||||||
@@ -1253,6 +1453,12 @@ void Diagnostics_FirstPass(const int rates_total, const int copyN, const int tre
|
|||||||
passTrendDn++;
|
passTrendDn++;
|
||||||
if(up && AtrExp_AllowsAt(i)) passAtrUp++;
|
if(up && AtrExp_AllowsAt(i)) passAtrUp++;
|
||||||
if(dn && AtrExp_AllowsAt(i)) passAtrDn++;
|
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(up && Signal_Ema9CoreBuyOkAt(i)) passEma9Up++;
|
||||||
if(dn && Signal_Ema9CoreSellOkAt(i)) passEma9Dn++;
|
if(dn && Signal_Ema9CoreSellOkAt(i)) passEma9Dn++;
|
||||||
string pfB = "", pfS = "";
|
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) validBuy++;
|
||||||
if(buf_Signal[i] < -0.5) validSell++;
|
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,
|
n, crossUp, crossDn, passEma9Up, passEma9Dn, passPhaseUp, passPhaseDn,
|
||||||
passTrendUp, passTrendDn, passAtrUp, passAtrDn,
|
passTrendUp, passTrendDn, passAtrUp, passAtrDn, passAdxUp, passAdxDn,
|
||||||
passEnvUp, passEnvDn, validBuy, validSell);
|
passSwingUp, passSwingDn, passEnvUp, passEnvDn, validBuy, validSell);
|
||||||
if(crossUp > 0 && passPhaseUp == 0 && InpPhaseFilterEnabled)
|
if(crossUp > 0 && passPhaseUp == 0 && InpPhaseFilterEnabled)
|
||||||
Print("[RsiMomEA] Gợi ý: cross UP bị 5phase — xem P1-P5 hoặc hạ InpPhaseMinExpandSpread / InpPhaseMinRsiEma9Cross");
|
Print("[RsiMomEA] Gợi ý: cross UP bị 5phase — xem P1-P5 hoặc hạ InpPhaseMinExpandSpread / InpPhaseMinRsiEma9Cross");
|
||||||
else if(crossUp > 0 && passEma9Up == 0)
|
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");
|
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)
|
if(InpAtrExpFilterEnabled && crossUp > 0 && passAtrUp == 0)
|
||||||
Print("[RsiMomEA] Gợi ý: cross bị chặn ATR — hạ InpAtrExpMinRatio / InpAtrExpRiseBars hoặc tắt InpAtrExpFilterEnabled");
|
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)
|
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");
|
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)
|
int RsiMomentum_OnCalculate(const int rates_total, const int prev_calculated)
|
||||||
{
|
{
|
||||||
const int atrNeed = MathMax(1, InpAtrExpCompareBars) + MathMax(1, InpAtrExpRiseBars) + 5;
|
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
|
const int phaseNeed = InpPhaseFilterEnabled
|
||||||
? MathMax(InpPhaseExpandLookback, InpPhaseCoilLookback) + InpPhaseWmaFlatBars * 2 + 10
|
? MathMax(InpPhaseExpandLookback, InpPhaseCoilLookback) + InpPhaseWmaFlatBars * 2 + 10
|
||||||
: 0;
|
: 0;
|
||||||
const int minBars = MathMax(InpWMA45Period + InpRSIPeriod + 5 + phaseNeed,
|
const int minBars = MathMax(InpWMA45Period + InpRSIPeriod + 5 + phaseNeed,
|
||||||
MathMax(InpAtrExpPeriod, InpSlAtrPeriod) + atrNeed);
|
MathMax(MathMax(InpAtrExpPeriod, InpSlAtrPeriod) + atrNeed, adxNeed));
|
||||||
if (rates_total < minBars) return 0;
|
if (rates_total < minBars) return 0;
|
||||||
|
|
||||||
EnsureBuffers(rates_total);
|
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 wmaBars = BarsCalculated(h_WMA45);
|
||||||
const int ema200Bars = BarsCalculated(h_EMA200);
|
const int ema200Bars = BarsCalculated(h_EMA200);
|
||||||
const int atrRegBars = BarsCalculated(h_ATR_Regime);
|
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;
|
static datetime lastWarn = 0;
|
||||||
if (TimeCurrent() - lastWarn > 30)
|
if (TimeCurrent() - lastWarn > 30)
|
||||||
{
|
{
|
||||||
PrintFormat("[RsiMomEA] Source not ready: RSI=%d EMA9=%d WMA45=%d EMA200=%d ATRreg=%d (rates=%d)",
|
PrintFormat("[RsiMomEA] Source not ready: RSI=%d EMA9=%d WMA45=%d EMA200=%d ATRreg=%d ADX=%d (rates=%d)",
|
||||||
rsiBars, ema9Bars, wmaBars, ema200Bars, atrRegBars, rates_total);
|
rsiBars, ema9Bars, wmaBars, ema200Bars, atrRegBars, adxBars, rates_total);
|
||||||
lastWarn = TimeCurrent();
|
lastWarn = TimeCurrent();
|
||||||
}
|
}
|
||||||
return 0;
|
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);
|
const int copyN = MathMin(srcMin, rates_total);
|
||||||
if (copyN < minBars) return 0;
|
if (copyN < minBars) return 0;
|
||||||
|
|
||||||
@@ -1405,11 +1622,17 @@ int OnInit()
|
|||||||
|
|
||||||
const long chInit = ActChart();
|
const long chInit = ActChart();
|
||||||
ObjectsDeleteAll(chInit, OBJ_PREFIX + "RSN_");
|
ObjectsDeleteAll(chInit, OBJ_PREFIX + "RSN_");
|
||||||
Signal_DebugConfigureChart(chInit);
|
ObjectsDeleteAll(chInit, DBG_PREFIX);
|
||||||
|
if(DebugMarksEffective())
|
||||||
|
Signal_DebugConfigureChart(chInit);
|
||||||
|
|
||||||
Panel_CreateAll();
|
if(InpShowPanel)
|
||||||
Stats_CreateObjects();
|
Panel_CreateAll();
|
||||||
Stats_UpdateDisplay();
|
if(InpShowStats)
|
||||||
|
{
|
||||||
|
Stats_CreateObjects();
|
||||||
|
Stats_UpdateDisplay();
|
||||||
|
}
|
||||||
const int spr = Env_CurrentSpreadPts();
|
const int spr = Env_CurrentSpreadPts();
|
||||||
string envWhy = "";
|
string envWhy = "";
|
||||||
const bool envNow = Env_AllowsTradeAtBar(MathMax(1, InpSignalBarShift), envWhy);
|
const bool envNow = Env_AllowsTradeAtBar(MathMax(1, InpSignalBarShift), envWhy);
|
||||||
@@ -1417,13 +1640,17 @@ int OnInit()
|
|||||||
Print("[RsiMomEA] Journal CSV: ", RsiMomJournal_TradesPath(_Symbol, _Period),
|
Print("[RsiMomEA] Journal CSV: ", RsiMomJournal_TradesPath(_Symbol, _Period),
|
||||||
" | summary: ", RsiMomJournal_SummaryPath(_Symbol, _Period), " (FILE_COMMON)");
|
" | 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",
|
" | EMA200=", InpTrendFilterEnabled ? "on" : "OFF",
|
||||||
" | session=", InpSessionFilterEnabled ? "on" : "OFF",
|
" | session=", InpSessionFilterEnabled ? "on" : "OFF",
|
||||||
" | debugMarks=", InpDebugMarkSignals ? "on" : "off",
|
" | debugMarks=", InpDebugMarkSignals ? "on" : "off",
|
||||||
" | ATR+EMA200+phiên | trade=", InpTradeEnabled ? "on" : "off", " risk%=", InpRiskPercent,
|
" | ATR+EMA200+phiên | trade=", InpTradeEnabled ? "on" : "off", " risk%=", InpRiskPercent,
|
||||||
" ATRexp=", InpAtrExpFilterEnabled
|
" ATRexp=", InpAtrExpFilterEnabled
|
||||||
? StringFormat("ratio>=%.2f rise%d cmp%d", InpAtrExpMinRatio, InpAtrExpRiseBars, InpAtrExpCompareBars) : "OFF",
|
? 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
|
" session=", InpSessionFilterEnabled
|
||||||
? StringFormat("L%d-%d NY%d-%d avoid%d%s", InpLondonStartHour, InpLondonEndHour,
|
? StringFormat("L%d-%d NY%d-%d avoid%d%s", InpLondonStartHour, InpLondonEndHour,
|
||||||
InpNYStartHour, InpNYEndHour, InpSessionAvoidLastMin,
|
InpNYStartHour, InpNYEndHour, InpSessionAvoidLastMin,
|
||||||
@@ -1458,8 +1685,11 @@ void OnDeinit(const int reason)
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
void OnTick()
|
void OnTick()
|
||||||
{
|
{
|
||||||
Pending_EnvCancelIfBad();
|
if(EntryModeIsLimit())
|
||||||
Pending_ManageExpiry();
|
{
|
||||||
|
Pending_EnvCancelIfBad();
|
||||||
|
Pending_ManageExpiry();
|
||||||
|
}
|
||||||
Position_ManageAt1R();
|
Position_ManageAt1R();
|
||||||
|
|
||||||
const datetime t0 = iTime(_Symbol, _Period, 0);
|
const datetime t0 = iTime(_Symbol, _Period, 0);
|
||||||
@@ -1650,8 +1880,33 @@ bool LimitPriceValid(const bool isBuy, const double limitPx)
|
|||||||
return (limitPx > tk.bid + md);
|
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)
|
void TradeExecuteOrder(const bool isBuy)
|
||||||
|
{
|
||||||
|
if(EntryModeIsMarket())
|
||||||
|
TradeExecuteMarketOrder(isBuy);
|
||||||
|
else
|
||||||
|
TradeExecuteLimitOrder(isBuy);
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void TradeExecuteLimitOrder(const bool isBuy)
|
||||||
{
|
{
|
||||||
if (InpOnePositionFlat && HasMyMagicPositionOrPending())
|
if (InpOnePositionFlat && HasMyMagicPositionOrPending())
|
||||||
return;
|
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)
|
void TradeTryOnBarOpen(const int calcRet)
|
||||||
{
|
{
|
||||||
@@ -2063,7 +2373,8 @@ void Stats_UpdateDisplay()
|
|||||||
ObjectSetString(ch, STAT_L1, OBJPROP_TEXT, "");
|
ObjectSetString(ch, STAT_L1, OBJPROP_TEXT, "");
|
||||||
ObjectSetString(ch, STAT_L2, OBJPROP_TEXT, "");
|
ObjectSetString(ch, STAT_L2, OBJPROP_TEXT, "");
|
||||||
ObjectSetString(ch, STAT_L3, OBJPROP_TEXT, "");
|
ObjectSetString(ch, STAT_L3, OBJPROP_TEXT, "");
|
||||||
ChartRedraw(ch);
|
if(ChartRedrawEffective())
|
||||||
|
ChartRedraw(ch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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`. |
|
| `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. |
|
| `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. |
|
| `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`):**
|
**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 < 20 thường sideway; 22–35 vùng trend ổn |
|
||||||
|
| `InpAdxRequireDiDirection` | true | BUY: +DI > −DI; SELL: −DI > +DI |
|
||||||
|
| `InpAdxMinDiSpread` | 0 | Chênh +DI−(−DI) tối thiểu (thử 5–10) |
|
||||||
|
| `InpAdxRiseBars` | 0 | ADX tăng vs N nến (1–2 = 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.*
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ Gán pivot: từ L0/H0 gần nhất, lùi từng bước tới pivot cũ **kề*
|
|||||||
#### Bull CHoCH (phá L0)
|
#### Bull CHoCH (phá L0)
|
||||||
|
|
||||||
1. **P1:** Track `newL0` (low thấp hơn).
|
1. **P1:** Track `newL0` (low thấp hơn).
|
||||||
2. **P2:** `IsConfirmedSwingLow(newL0)` → vào phase 3.
|
2. **P2:** `IsConfirmedSwingLow(newL0)` **hoặc** hồi lên đủ `InpSwingRange` (swing high sau đáy) → khóa `newL0`, vào phase 3 (không kéo `newL0` nữa).
|
||||||
3. **P3 case 1:** Phá lên H0 → `newH0` confirm → roll bull (`L1` giữ).
|
3. **P3 case 1:** Phá lên H0 → `newH0` confirm → roll bull (`L1` giữ).
|
||||||
4. **P3 case 2:** Phá xuống `newL0` → `newH0` + `newL02` confirm → roll bear.
|
4. **P3 case 2:** Body phá xuống `newL0` đã khóa → track `newH0` (confirm) → track `newL02` (confirm tuần tự) → roll bear.
|
||||||
|
|
||||||
Bear Continue / CHoCH: **đối xứng** với bull.
|
Bear Continue / CHoCH: **đối xứng** với bull.
|
||||||
|
|
||||||
|
|||||||
@@ -123,9 +123,11 @@ struct UpdateContext
|
|||||||
SwingPoint newH0;
|
SwingPoint newH0;
|
||||||
SwingPoint newL0;
|
SwingPoint newL0;
|
||||||
SwingPoint newL02; // CHoCH bull case2 → roll thành L0
|
SwingPoint newL02; // CHoCH bull case2 → roll thành L0
|
||||||
bool newH0Locked; // ngừng track high khi swing confirm
|
bool newH0Locked;
|
||||||
bool newLLocked;
|
bool newLLocked; // newL0 hoàn thiện (P2) — không kéo newL0 nữa
|
||||||
bool case1TrackingH0; // CHoCH P3 case1: đang tạo đỉnh mới
|
bool case1TrackingH0; // P3 case1: phá lên H0
|
||||||
|
bool case2Active; // P3 case2: phá xuống newL0 đã khóa
|
||||||
|
bool newH0Case2Locked; // P3 case2: newH0 (hồi) đã confirm
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
event = UPD_NONE;
|
event = UPD_NONE;
|
||||||
@@ -134,6 +136,8 @@ struct UpdateContext
|
|||||||
newH0.Clear(); newL0.Clear(); newL02.Clear();
|
newH0.Clear(); newL0.Clear(); newL02.Clear();
|
||||||
newH0Locked = newLLocked = false;
|
newH0Locked = newLLocked = false;
|
||||||
case1TrackingH0 = false;
|
case1TrackingH0 = false;
|
||||||
|
case2Active = false;
|
||||||
|
newH0Case2Locked = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+289
-108
@@ -1,18 +1,5 @@
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| UpdateEngine.mqh |
|
//| UpdateEngine.mqh — §1.4 CHoCH / Continue |
|
||||||
//| §1.4 CHoCH & Trend Continue — cập nhật incremental |
|
|
||||||
//+------------------------------------------------------------------+
|
|
||||||
//| ĐÃ GIẢI QUYẾT: |
|
|
||||||
//| • Chỉ chạy khi nến HTF mới đóng; không quét lại full lookback |
|
|
||||||
//| • Phá Key LV1 (body) → CHoCH | Phá Key LV2 → Continue |
|
|
||||||
//| • §1.4a Bull Continue P1: track newH0 (nến xanh), newL0 (đáy) |
|
|
||||||
//| • §1.4a P2: IsConfirmedSwingHigh(newH0) → roll, khóa newH0 |
|
|
||||||
//| • §1.4b Bull CHoCH P1–2: track newL0 → swing low confirm → P3 |
|
|
||||||
//| • §1.4b P3 case1: phá H0 → newH0 confirm → roll (L1 giữ) |
|
|
||||||
//| • §1.4b P3 case2: phá newL0 → newH0+newL02 confirm → bear roll|
|
|
||||||
//| • §1.4c/d Bear: đối xứng Continue & CHoCH |
|
|
||||||
//| • Xác nhận đỉnh/đáy = InpSwingRange (KHÔNG dùng Fib ở đây) |
|
|
||||||
//| CHƯA TỐI ƯU: gắn OB chính xác cho newL0 mỗi leg; edge case đa tín hiệu |
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
#ifndef HYPERICT_UPDATEENGINE_MQH
|
#ifndef HYPERICT_UPDATEENGINE_MQH
|
||||||
#define HYPERICT_UPDATEENGINE_MQH
|
#define HYPERICT_UPDATEENGINE_MQH
|
||||||
@@ -31,9 +18,9 @@ class CUpdateEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void TrackNewHigh(const string sym, const ENUM_TIMEFRAMES tf,
|
static void TrackNewHigh(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
const int shift, SwingPoint &pt, const bool onlyIfLocked)
|
const int shift, SwingPoint &pt, const bool frozen)
|
||||||
{
|
{
|
||||||
if(onlyIfLocked)
|
if(frozen)
|
||||||
return;
|
return;
|
||||||
const double h = iHigh(sym, tf, shift);
|
const double h = iHigh(sym, tf, shift);
|
||||||
if(!pt.Valid() || h > pt.price)
|
if(!pt.Valid() || h > pt.price)
|
||||||
@@ -41,27 +28,112 @@ class CUpdateEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void TrackNewLow(const string sym, const ENUM_TIMEFRAMES tf,
|
static void TrackNewLow(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
const int shift, SwingPoint &pt, const bool onlyIfLocked)
|
const int shift, SwingPoint &pt, const bool frozen)
|
||||||
{
|
{
|
||||||
if(onlyIfLocked)
|
if(frozen)
|
||||||
return;
|
return;
|
||||||
const double l = iLow(sym, tf, shift);
|
const double l = iLow(sym, tf, shift);
|
||||||
if(!pt.Valid() || l < pt.price)
|
if(!pt.Valid() || l < pt.price)
|
||||||
CSwingEngine::FillSwingLow(sym, tf, shift, pt);
|
CSwingEngine::FillSwingLow(sym, tf, shift, pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FinalizePivotHigh(const string sym, const ENUM_TIMEFRAMES tf,
|
static bool ConfirmedSwingHighAt(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
SwingPoint &pt, const int range)
|
const int shift, const int range)
|
||||||
{
|
{
|
||||||
if(pt.Valid())
|
SwingPoint pt;
|
||||||
CSwingEngine::FillSwingHigh(sym, tf, pt.shift, pt);
|
CSwingEngine::FillSwingHigh(sym, tf, shift, pt);
|
||||||
|
return CSwingEngine::IsConfirmedSwingHigh(sym, tf, pt, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FinalizePivotLow(const string sym, const ENUM_TIMEFRAMES tf,
|
static bool ConfirmedSwingLowAt(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
SwingPoint &pt, const int range)
|
const int shift, const int range)
|
||||||
{
|
{
|
||||||
if(pt.Valid())
|
SwingPoint pt;
|
||||||
CSwingEngine::FillSwingLow(sym, tf, pt.shift, pt);
|
CSwingEngine::FillSwingLow(sym, tf, shift, pt);
|
||||||
|
return CSwingEngine::IsConfirmedSwingLow(sym, tf, pt, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2 bull CHoCH: đáy confirm (swing low) HOẶC đã hồi lên có swing high sau đáy
|
||||||
|
static bool TryConfirmBullChochNewL0(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
|
const SwingPoint &newL0, const int range)
|
||||||
|
{
|
||||||
|
if(!newL0.Valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(CSwingEngine::IsConfirmedSwingLow(sym, tf, newL0, range))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for(int sh = 1; sh < newL0.shift; sh++)
|
||||||
|
{
|
||||||
|
if(iLow(sym, tf, sh) <= newL0.price + _Point)
|
||||||
|
continue;
|
||||||
|
if(ConfirmedSwingHighAt(sym, tf, sh, range))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Đỉnh hồi cao nhất (đã confirm) sau đáy newL0 — dùng seed newH0 case2
|
||||||
|
static bool FindRetraceHighAfterLow(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
|
const SwingPoint &low, const int range,
|
||||||
|
SwingPoint &outHigh)
|
||||||
|
{
|
||||||
|
outHigh.Clear();
|
||||||
|
if(!low.Valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for(int sh = 1; sh < low.shift; sh++)
|
||||||
|
{
|
||||||
|
if(iLow(sym, tf, sh) <= low.price + _Point)
|
||||||
|
continue;
|
||||||
|
if(!ConfirmedSwingHighAt(sym, tf, sh, range))
|
||||||
|
continue;
|
||||||
|
const double h = iHigh(sym, tf, sh);
|
||||||
|
if(!outHigh.Valid() || h > outHigh.price)
|
||||||
|
CSwingEngine::FillSwingHigh(sym, tf, sh, outHigh);
|
||||||
|
}
|
||||||
|
return outHigh.Valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool FindRetraceLowAfterHigh(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
|
const SwingPoint &high, const int range,
|
||||||
|
SwingPoint &outLow)
|
||||||
|
{
|
||||||
|
outLow.Clear();
|
||||||
|
if(!high.Valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for(int sh = 1; sh < high.shift; sh++)
|
||||||
|
{
|
||||||
|
if(iHigh(sym, tf, sh) >= high.price - _Point)
|
||||||
|
continue;
|
||||||
|
if(!ConfirmedSwingLowAt(sym, tf, sh, range))
|
||||||
|
continue;
|
||||||
|
const double l = iLow(sym, tf, sh);
|
||||||
|
if(!outLow.Valid() || l < outLow.price)
|
||||||
|
CSwingEngine::FillSwingLow(sym, tf, sh, outLow);
|
||||||
|
}
|
||||||
|
return outLow.Valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2 bear CHoCH: đỉnh confirm hoặc hồi xuống có swing low sau đỉnh
|
||||||
|
static bool TryConfirmBearChochNewH0(const string sym, const ENUM_TIMEFRAMES tf,
|
||||||
|
const SwingPoint &newH0, const int range)
|
||||||
|
{
|
||||||
|
if(!newH0.Valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(CSwingEngine::IsConfirmedSwingHigh(sym, tf, newH0, range))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for(int sh = 1; sh < newH0.shift; sh++)
|
||||||
|
{
|
||||||
|
if(iHigh(sym, tf, sh) >= newH0.price - _Point)
|
||||||
|
continue;
|
||||||
|
if(ConfirmedSwingLowAt(sym, tf, sh, range))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FinishRoll(HtfContext &ctx)
|
static void FinishRoll(HtfContext &ctx)
|
||||||
@@ -71,6 +143,50 @@ class CUpdateEngine
|
|||||||
Dbg(StringFormat("Roll xong — bias=%d", ctx.structBias));
|
Dbg(StringFormat("Roll xong — bias=%d", ctx.structBias));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void LockBullChochNewL0(HtfContext &ctx)
|
||||||
|
{
|
||||||
|
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
|
||||||
|
ctx.update.newLLocked = true;
|
||||||
|
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
|
||||||
|
ctx.update.case2Active = false;
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
ctx.update.newL02.Clear();
|
||||||
|
|
||||||
|
SwingPoint retrace;
|
||||||
|
if(FindRetraceHighAfterLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange, retrace))
|
||||||
|
{
|
||||||
|
ctx.update.newH0 = retrace;
|
||||||
|
Dbg(StringFormat("Bull CHoCH P2 — seed newH0 @ %.5f (hồi sau đáy)", retrace.price));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ctx.update.newH0.Clear();
|
||||||
|
|
||||||
|
Dbg(StringFormat("Bull CHoCH P2 — newL0 khóa @ %.5f sh=%d",
|
||||||
|
ctx.update.newL0.price, ctx.update.newL0.shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LockBearChochNewH0(HtfContext &ctx)
|
||||||
|
{
|
||||||
|
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
|
||||||
|
ctx.update.newH0Locked = true;
|
||||||
|
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
|
||||||
|
ctx.update.case2Active = false;
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
ctx.update.newL02.Clear();
|
||||||
|
|
||||||
|
SwingPoint retrace;
|
||||||
|
if(FindRetraceLowAfterHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange, retrace))
|
||||||
|
{
|
||||||
|
ctx.update.newL0 = retrace;
|
||||||
|
Dbg(StringFormat("Bear CHoCH P2 — seed newL0 @ %.5f (hồi sau đỉnh)", retrace.price));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ctx.update.newL0.Clear();
|
||||||
|
|
||||||
|
Dbg(StringFormat("Bear CHoCH P2 — newH0 khóa @ %.5f sh=%d",
|
||||||
|
ctx.update.newH0.price, ctx.update.newH0.shift));
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool IsNewHtfBar(HtfContext &ctx)
|
static bool IsNewHtfBar(HtfContext &ctx)
|
||||||
{
|
{
|
||||||
@@ -78,7 +194,6 @@ public:
|
|||||||
return t0 != 0 && t0 != ctx.lastHtfBar;
|
return t0 != 0 && t0 != ctx.lastHtfBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
//--- Phát hiện body phá Key LV1/L2 lần đầu → bật BUILD
|
|
||||||
static void DetectBreakEvents(HtfContext &ctx)
|
static void DetectBreakEvents(HtfContext &ctx)
|
||||||
{
|
{
|
||||||
if(ctx.update.phase != UPD_PHASE_IDLE)
|
if(ctx.update.phase != UPD_PHASE_IDLE)
|
||||||
@@ -94,7 +209,10 @@ public:
|
|||||||
ctx.update.newL0.Clear();
|
ctx.update.newL0.Clear();
|
||||||
ctx.update.newH0Locked = false;
|
ctx.update.newH0Locked = false;
|
||||||
ctx.update.newLLocked = false;
|
ctx.update.newLLocked = false;
|
||||||
Dbg("Bull CHoCH — body break L0");
|
ctx.update.case2Active = false;
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
ctx.update.case1TrackingH0 = false;
|
||||||
|
Dbg("Bull CHoCH P1 — body break L0");
|
||||||
}
|
}
|
||||||
else if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
|
else if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
|
||||||
{
|
{
|
||||||
@@ -116,7 +234,10 @@ public:
|
|||||||
ctx.update.newH0.Clear();
|
ctx.update.newH0.Clear();
|
||||||
ctx.update.newH0Locked = false;
|
ctx.update.newH0Locked = false;
|
||||||
ctx.update.newLLocked = false;
|
ctx.update.newLLocked = false;
|
||||||
Dbg("Bear CHoCH — body break H0");
|
ctx.update.case2Active = false;
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
ctx.update.case1TrackingH0 = false;
|
||||||
|
Dbg("Bear CHoCH P1 — body break H0");
|
||||||
}
|
}
|
||||||
else if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
|
else if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
|
||||||
{
|
{
|
||||||
@@ -131,7 +252,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--- §1.4a Bull Continue — P1 track + P2 swing high confirm → roll
|
|
||||||
static void ProcessBullContinueBuild(HtfContext &ctx, const int sh)
|
static void ProcessBullContinueBuild(HtfContext &ctx, const int sh)
|
||||||
{
|
{
|
||||||
if(CKeyLevels::IsBullCandle(ctx.symbol, ctx.htf, sh))
|
if(CKeyLevels::IsBullCandle(ctx.symbol, ctx.htf, sh))
|
||||||
@@ -145,12 +265,11 @@ public:
|
|||||||
ctx.update.newH0, InpSwingRange))
|
ctx.update.newH0, InpSwingRange))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
|
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
|
||||||
ctx.update.newH0Locked = true;
|
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
|
||||||
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
|
|
||||||
CSwingEngine::RollBullContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
CSwingEngine::RollBullContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
||||||
FinishRoll(ctx);
|
FinishRoll(ctx);
|
||||||
Dbg("Bull Continue — swing high confirmed, rolled");
|
Dbg("Bull Continue — roll");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcessBearContinueBuild(HtfContext &ctx, const int sh)
|
static void ProcessBearContinueBuild(HtfContext &ctx, const int sh)
|
||||||
@@ -166,144 +285,206 @@ public:
|
|||||||
ctx.update.newL0, InpSwingRange))
|
ctx.update.newL0, InpSwingRange))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
|
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
|
||||||
ctx.update.newLLocked = true;
|
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
|
||||||
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
|
|
||||||
CSwingEngine::RollBearContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
CSwingEngine::RollBearContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
||||||
FinishRoll(ctx);
|
FinishRoll(ctx);
|
||||||
Dbg("Bear Continue — swing low confirmed, rolled");
|
Dbg("Bear Continue — roll");
|
||||||
}
|
}
|
||||||
|
|
||||||
//--- §1.4b Bull CHoCH — P1 track newL0, P2 swing low confirm → CHOCH_RESOLVE
|
|
||||||
static void ProcessBullChochBuild(HtfContext &ctx, const int sh)
|
static void ProcessBullChochBuild(HtfContext &ctx, const int sh)
|
||||||
{
|
{
|
||||||
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
|
if(!ctx.update.newLLocked)
|
||||||
|
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
|
||||||
|
|
||||||
if(ctx.update.newLLocked)
|
if(ctx.update.newLLocked)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
if(TryConfirmBullChochNewL0(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange))
|
||||||
ctx.update.newL0, InpSwingRange))
|
LockBullChochNewL0(ctx);
|
||||||
return;
|
|
||||||
|
|
||||||
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
|
|
||||||
ctx.update.newLLocked = true;
|
|
||||||
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
|
|
||||||
Dbg("Bull CHoCH — newL0 swing confirmed → phase 3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcessBearChochBuild(HtfContext &ctx, const int sh)
|
static void ProcessBearChochBuild(HtfContext &ctx, const int sh)
|
||||||
{
|
{
|
||||||
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
|
if(!ctx.update.newH0Locked)
|
||||||
|
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
|
||||||
|
|
||||||
if(ctx.update.newH0Locked)
|
if(ctx.update.newH0Locked)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
if(TryConfirmBearChochNewH0(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange))
|
||||||
ctx.update.newH0, InpSwingRange))
|
LockBearChochNewH0(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// P3 case2: newL0 khóa → hồi (newH0) → phá xuống (newL02) → roll bear
|
||||||
|
static void ProcessBullChochCase2(HtfContext &ctx, const int sh)
|
||||||
|
{
|
||||||
|
if(!ctx.update.newLLocked || !ctx.update.newL0.Valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.update.newL0.price))
|
||||||
|
{
|
||||||
|
if(!ctx.update.case2Active)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(!ctx.update.case2Active)
|
||||||
|
{
|
||||||
|
ctx.update.case2Active = true;
|
||||||
|
ctx.update.newL02.Clear();
|
||||||
|
ctx.update.case1TrackingH0 = false;
|
||||||
|
if(ctx.update.newH0.Valid() &&
|
||||||
|
CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
||||||
|
ctx.update.newH0, InpSwingRange))
|
||||||
|
ctx.update.newH0Case2Locked = true;
|
||||||
|
else
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
Dbg("Bull CHoCH P3 case2 — bắt đầu (newL0 đã khóa, phá xuống)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ctx.update.case2Active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!ctx.update.newH0Case2Locked)
|
||||||
|
{
|
||||||
|
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
|
||||||
|
if(CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
||||||
|
ctx.update.newH0, InpSwingRange))
|
||||||
|
{
|
||||||
|
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
|
||||||
|
ctx.update.newH0Case2Locked = true;
|
||||||
|
ctx.update.newL02.Clear();
|
||||||
|
Dbg(StringFormat("Bull CHoCH case2 — newH0 confirm @ %.5f", ctx.update.newH0.price));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
|
||||||
|
|
||||||
|
if(!CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
||||||
|
ctx.update.newL02, InpSwingRange))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
|
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL02.shift, ctx.update.newL02);
|
||||||
ctx.update.newH0Locked = true;
|
CSwingEngine::RollBullChochCase2(ctx.swings,
|
||||||
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
|
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
|
||||||
Dbg("Bear CHoCH — newH0 swing confirmed → phase 3");
|
FinishRoll(ctx);
|
||||||
|
Dbg("Bull CHoCH case2 — roll bear (H0→H1, newH0→H0, newL0→L1, newL02→L0)");
|
||||||
}
|
}
|
||||||
|
|
||||||
//--- §1.4b Bull CHoCH P3 — case1 về bull / case2 confirm bear
|
|
||||||
static void ProcessBullChochResolve(HtfContext &ctx, const int sh)
|
static void ProcessBullChochResolve(HtfContext &ctx, const int sh)
|
||||||
{
|
{
|
||||||
if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.swings.h0.price))
|
if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.swings.h0.price))
|
||||||
{
|
{
|
||||||
|
ctx.update.case2Active = false;
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
|
||||||
if(!ctx.update.case1TrackingH0)
|
if(!ctx.update.case1TrackingH0)
|
||||||
{
|
{
|
||||||
ctx.update.case1TrackingH0 = true;
|
ctx.update.case1TrackingH0 = true;
|
||||||
ctx.update.newH0.Clear();
|
ctx.update.newH0.Clear();
|
||||||
ctx.update.newH0Locked = false;
|
ctx.update.newH0Locked = false;
|
||||||
Dbg("Bull CHoCH case1 — break H0, track newH0");
|
Dbg("Bull CHoCH case1 — phá H0, track newH0");
|
||||||
}
|
}
|
||||||
if(CKeyLevels::IsBullCandle(ctx.symbol, ctx.htf, sh))
|
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
|
||||||
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
|
|
||||||
|
|
||||||
if(ctx.update.case1TrackingH0 && !ctx.update.newH0Locked &&
|
if(!ctx.update.newH0Locked &&
|
||||||
CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
||||||
ctx.update.newH0, InpSwingRange))
|
ctx.update.newH0, InpSwingRange))
|
||||||
{
|
{
|
||||||
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
|
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
|
||||||
ctx.update.newH0Locked = true;
|
|
||||||
CSwingEngine::RollBullChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
CSwingEngine::RollBullChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
||||||
FinishRoll(ctx);
|
FinishRoll(ctx);
|
||||||
Dbg("Bull CHoCH case1 — newH0 confirmed, rolled");
|
Dbg("Bull CHoCH case1 — roll bull");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ctx.update.newL0.Valid() &&
|
ProcessBullChochCase2(ctx, sh);
|
||||||
CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.update.newL0.price))
|
}
|
||||||
{
|
|
||||||
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
|
|
||||||
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
|
|
||||||
|
|
||||||
const bool hOk = CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
static void ProcessBearChochCase2(HtfContext &ctx, const int sh)
|
||||||
ctx.update.newH0, InpSwingRange);
|
{
|
||||||
const bool lOk = CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
if(!ctx.update.newH0Locked || !ctx.update.newH0.Valid())
|
||||||
ctx.update.newL02, InpSwingRange);
|
return;
|
||||||
if(hOk && lOk)
|
|
||||||
{
|
if(!CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.update.newH0.price))
|
||||||
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
|
{
|
||||||
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL02, InpSwingRange);
|
if(!ctx.update.case2Active)
|
||||||
CSwingEngine::RollBullChochCase2(ctx.swings,
|
return;
|
||||||
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
|
|
||||||
FinishRoll(ctx);
|
|
||||||
Dbg("Bull CHoCH case2 — bear confirmed, rolled");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if(!ctx.update.case2Active)
|
||||||
|
{
|
||||||
|
ctx.update.case2Active = true;
|
||||||
|
ctx.update.newL02.Clear();
|
||||||
|
ctx.update.case1TrackingH0 = false;
|
||||||
|
if(ctx.update.newL0.Valid() &&
|
||||||
|
CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
||||||
|
ctx.update.newL0, InpSwingRange))
|
||||||
|
ctx.update.newH0Case2Locked = true;
|
||||||
|
else
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
Dbg("Bear CHoCH P3 case2 — bắt đầu");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ctx.update.case2Active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!ctx.update.newH0Case2Locked)
|
||||||
|
{
|
||||||
|
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
|
||||||
|
if(CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
||||||
|
ctx.update.newL0, InpSwingRange))
|
||||||
|
{
|
||||||
|
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
|
||||||
|
ctx.update.newH0Case2Locked = true;
|
||||||
|
ctx.update.newL02.Clear();
|
||||||
|
Dbg(StringFormat("Bear CHoCH case2 — newL0 retrace confirm @ %.5f", ctx.update.newL0.price));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
|
||||||
|
|
||||||
|
if(!CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
||||||
|
ctx.update.newL02, InpSwingRange))
|
||||||
|
return;
|
||||||
|
|
||||||
|
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newL02.shift, ctx.update.newL02);
|
||||||
|
CSwingEngine::RollBearChochCase2(ctx.swings,
|
||||||
|
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
|
||||||
|
FinishRoll(ctx);
|
||||||
|
Dbg("Bear CHoCH case2 — roll bull");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcessBearChochResolve(HtfContext &ctx, const int sh)
|
static void ProcessBearChochResolve(HtfContext &ctx, const int sh)
|
||||||
{
|
{
|
||||||
if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.swings.l0.price))
|
if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.swings.l0.price))
|
||||||
{
|
{
|
||||||
|
ctx.update.case2Active = false;
|
||||||
|
ctx.update.newH0Case2Locked = false;
|
||||||
|
|
||||||
if(!ctx.update.case1TrackingH0)
|
if(!ctx.update.case1TrackingH0)
|
||||||
{
|
{
|
||||||
ctx.update.case1TrackingH0 = true;
|
ctx.update.case1TrackingH0 = true;
|
||||||
ctx.update.newL0.Clear();
|
ctx.update.newL0.Clear();
|
||||||
ctx.update.newLLocked = false;
|
ctx.update.newLLocked = false;
|
||||||
Dbg("Bear CHoCH case1 — break L0, track newL0");
|
Dbg("Bear CHoCH case1 — phá L0");
|
||||||
}
|
}
|
||||||
if(CKeyLevels::IsBearCandle(ctx.symbol, ctx.htf, sh))
|
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
|
||||||
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
|
|
||||||
|
|
||||||
if(ctx.update.case1TrackingH0 && !ctx.update.newLLocked &&
|
if(!ctx.update.newLLocked &&
|
||||||
CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
||||||
ctx.update.newL0, InpSwingRange))
|
ctx.update.newL0, InpSwingRange))
|
||||||
{
|
{
|
||||||
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
|
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
|
||||||
ctx.update.newLLocked = true;
|
|
||||||
CSwingEngine::RollBearChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
CSwingEngine::RollBearChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
|
||||||
FinishRoll(ctx);
|
FinishRoll(ctx);
|
||||||
Dbg("Bear CHoCH case1 — newL0 confirmed, rolled");
|
Dbg("Bear CHoCH case1 — roll bear");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ctx.update.newH0.Valid() &&
|
ProcessBearChochCase2(ctx, sh);
|
||||||
CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.update.newH0.price))
|
|
||||||
{
|
|
||||||
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
|
|
||||||
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
|
|
||||||
|
|
||||||
const bool lOk = CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
|
|
||||||
ctx.update.newL0, InpSwingRange);
|
|
||||||
const bool hOk = CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
|
|
||||||
ctx.update.newL02, InpSwingRange);
|
|
||||||
if(lOk && hOk)
|
|
||||||
{
|
|
||||||
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
|
|
||||||
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newL02, InpSwingRange);
|
|
||||||
CSwingEngine::RollBearChochCase2(ctx.swings,
|
|
||||||
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
|
|
||||||
FinishRoll(ctx);
|
|
||||||
Dbg("Bear CHoCH case2 — bull confirmed, rolled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnHtfBarClose(HtfContext &ctx)
|
static void OnHtfBarClose(HtfContext &ctx)
|
||||||
|
|||||||
@@ -61,6 +61,17 @@ SignalEvalResult Signal_EvaluateBuyAt(const int shift, const int rates_total, co
|
|||||||
const bool phaseFilterEnabled,
|
const bool phaseFilterEnabled,
|
||||||
const bool trendFilterEnabled,
|
const bool trendFilterEnabled,
|
||||||
const bool atrFilterEnabled,
|
const bool atrFilterEnabled,
|
||||||
|
const bool adxFilterEnabled,
|
||||||
|
const bool adxOkAtBar,
|
||||||
|
const double adxVal,
|
||||||
|
const double plusDi,
|
||||||
|
const double minusDi,
|
||||||
|
const string adxFailWhy,
|
||||||
|
const bool swingFilterEnabled,
|
||||||
|
const bool swingOkAtBar,
|
||||||
|
const double swingBodyOld,
|
||||||
|
const double swingBodyNew,
|
||||||
|
const string swingFailWhy,
|
||||||
const bool rsiObOsFilterEnabled,
|
const bool rsiObOsFilterEnabled,
|
||||||
const double rsiOverbought,
|
const double rsiOverbought,
|
||||||
const double rsiOversold,
|
const double rsiOversold,
|
||||||
@@ -149,6 +160,30 @@ SignalEvalResult Signal_EvaluateBuyAt(const int shift, const int rates_total, co
|
|||||||
if(atrFilterEnabled && !atrExpAtBar)
|
if(atrFilterEnabled && !atrExpAtBar)
|
||||||
SignalEval_SetFail(r.failTag, "ATR co");
|
SignalEval_SetFail(r.failTag, "ATR co");
|
||||||
|
|
||||||
|
if(!adxFilterEnabled)
|
||||||
|
SignalEval_Append(r.detail, "ADX:OFF");
|
||||||
|
else if(adxOkAtBar)
|
||||||
|
SignalEval_Append(r.detail, StringFormat("ADX:OK(%.1f +DI=%.1f -DI=%.1f)", adxVal, plusDi, minusDi));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalEval_Append(r.detail, StringLen(adxFailWhy) > 0
|
||||||
|
? StringFormat("ADX:FAIL(%.1f %s)", adxVal, adxFailWhy)
|
||||||
|
: StringFormat("ADX:FAIL(%.1f)", adxVal));
|
||||||
|
SignalEval_SetFail(r.failTag, "ADX yếu");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!swingFilterEnabled)
|
||||||
|
SignalEval_Append(r.detail, "Swing2:OFF");
|
||||||
|
else if(swingOkAtBar)
|
||||||
|
SignalEval_Append(r.detail, StringFormat("Swing2:OK(đáy %.5f→%.5f)", swingBodyOld, swingBodyNew));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalEval_Append(r.detail, StringLen(swingFailWhy) > 0
|
||||||
|
? "Swing2:FAIL " + swingFailWhy
|
||||||
|
: "Swing2:FAIL");
|
||||||
|
SignalEval_SetFail(r.failTag, "2 đáy");
|
||||||
|
}
|
||||||
|
|
||||||
bool rsiOk = true;
|
bool rsiOk = true;
|
||||||
if(!rsiObOsFilterEnabled)
|
if(!rsiObOsFilterEnabled)
|
||||||
SignalEval_Append(r.detail, "RSI OB/OS:OFF");
|
SignalEval_Append(r.detail, "RSI OB/OS:OFF");
|
||||||
@@ -178,7 +213,10 @@ SignalEvalResult Signal_EvaluateBuyAt(const int shift, const int rates_total, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.signalOk = cross && coreOk && trendUp
|
r.signalOk = cross && coreOk && trendUp
|
||||||
&& (!atrFilterEnabled || atrExpAtBar) && rsiOk && envBar;
|
&& (!atrFilterEnabled || atrExpAtBar)
|
||||||
|
&& (!adxFilterEnabled || adxOkAtBar)
|
||||||
|
&& (!swingFilterEnabled || swingOkAtBar)
|
||||||
|
&& rsiOk && envBar;
|
||||||
r.tradeOk = r.signalOk;
|
r.tradeOk = r.signalOk;
|
||||||
|
|
||||||
if(r.signalOk && r.tradeOk)
|
if(r.signalOk && r.tradeOk)
|
||||||
@@ -200,6 +238,17 @@ SignalEvalResult Signal_EvaluateSellAt(const int shift, const int rates_total, c
|
|||||||
const bool phaseFilterEnabled,
|
const bool phaseFilterEnabled,
|
||||||
const bool trendFilterEnabled,
|
const bool trendFilterEnabled,
|
||||||
const bool atrFilterEnabled,
|
const bool atrFilterEnabled,
|
||||||
|
const bool adxFilterEnabled,
|
||||||
|
const bool adxOkAtBar,
|
||||||
|
const double adxVal,
|
||||||
|
const double plusDi,
|
||||||
|
const double minusDi,
|
||||||
|
const string adxFailWhy,
|
||||||
|
const bool swingFilterEnabled,
|
||||||
|
const bool swingOkAtBar,
|
||||||
|
const double swingBodyOld,
|
||||||
|
const double swingBodyNew,
|
||||||
|
const string swingFailWhy,
|
||||||
const bool rsiObOsFilterEnabled,
|
const bool rsiObOsFilterEnabled,
|
||||||
const double rsiOverbought,
|
const double rsiOverbought,
|
||||||
const double rsiOversold,
|
const double rsiOversold,
|
||||||
@@ -287,6 +336,30 @@ SignalEvalResult Signal_EvaluateSellAt(const int shift, const int rates_total, c
|
|||||||
if(atrFilterEnabled && !atrExpAtBar)
|
if(atrFilterEnabled && !atrExpAtBar)
|
||||||
SignalEval_SetFail(r.failTag, "ATR co");
|
SignalEval_SetFail(r.failTag, "ATR co");
|
||||||
|
|
||||||
|
if(!adxFilterEnabled)
|
||||||
|
SignalEval_Append(r.detail, "ADX:OFF");
|
||||||
|
else if(adxOkAtBar)
|
||||||
|
SignalEval_Append(r.detail, StringFormat("ADX:OK(%.1f +DI=%.1f -DI=%.1f)", adxVal, plusDi, minusDi));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalEval_Append(r.detail, StringLen(adxFailWhy) > 0
|
||||||
|
? StringFormat("ADX:FAIL(%.1f %s)", adxVal, adxFailWhy)
|
||||||
|
: StringFormat("ADX:FAIL(%.1f)", adxVal));
|
||||||
|
SignalEval_SetFail(r.failTag, "ADX yếu");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!swingFilterEnabled)
|
||||||
|
SignalEval_Append(r.detail, "Swing2:OFF");
|
||||||
|
else if(swingOkAtBar)
|
||||||
|
SignalEval_Append(r.detail, StringFormat("Swing2:OK(đỉnh %.5f→%.5f)", swingBodyOld, swingBodyNew));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalEval_Append(r.detail, StringLen(swingFailWhy) > 0
|
||||||
|
? "Swing2:FAIL " + swingFailWhy
|
||||||
|
: "Swing2:FAIL");
|
||||||
|
SignalEval_SetFail(r.failTag, "2 đỉnh");
|
||||||
|
}
|
||||||
|
|
||||||
bool rsiOk = true;
|
bool rsiOk = true;
|
||||||
if(!rsiObOsFilterEnabled)
|
if(!rsiObOsFilterEnabled)
|
||||||
SignalEval_Append(r.detail, "RSI OB/OS:OFF");
|
SignalEval_Append(r.detail, "RSI OB/OS:OFF");
|
||||||
@@ -315,7 +388,11 @@ SignalEvalResult Signal_EvaluateSellAt(const int shift, const int rates_total, c
|
|||||||
SignalEval_SetFail(r.failTag, "Phiên");
|
SignalEval_SetFail(r.failTag, "Phiên");
|
||||||
}
|
}
|
||||||
|
|
||||||
r.signalOk = cross && coreOk && trendDown && (!atrFilterEnabled || atrExpAtBar) && rsiOk && envBar;
|
r.signalOk = cross && coreOk && trendDown
|
||||||
|
&& (!atrFilterEnabled || atrExpAtBar)
|
||||||
|
&& (!adxFilterEnabled || adxOkAtBar)
|
||||||
|
&& (!swingFilterEnabled || swingOkAtBar)
|
||||||
|
&& rsiOk && envBar;
|
||||||
r.tradeOk = r.signalOk;
|
r.tradeOk = r.signalOk;
|
||||||
|
|
||||||
if(r.signalOk && r.tradeOk)
|
if(r.signalOk && r.tradeOk)
|
||||||
|
|||||||
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user