fix: Sweep Reversal Logic, Removed inBreakout = false from OpenTrade(), Added InpMaxRetestsPerSession = 3 parameter
This commit is contained in:
+70
-45
@@ -5,7 +5,7 @@
|
||||
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "NANDR"
|
||||
#property version "1.31"
|
||||
#property version "1.32"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
@@ -89,6 +89,7 @@ input bool InpUseStrictFilter = false; // Use Strict Breakout
|
||||
input bool InpWaitForRetest = true; // Wait for ORB Retest Before Entry
|
||||
input int InpBreakoutExpireBars = 20; // Bars Before Breakout Expires (0=never)
|
||||
input bool InpUseOBRetestEntry = true; // OB Retest Entry (enter on OB boundary retest)
|
||||
input int InpMaxRetestsPerSession = 3; // Max Retest Entries Per Session (per breakout)
|
||||
|
||||
// --- Lot Size & Risk ---
|
||||
input group "═══ Lot Size & Risk ═══"
|
||||
@@ -614,60 +615,80 @@ bool DetectRetest(int sessIdx, int &dir)
|
||||
if(!g_Sessions[sessIdx].inBreakout) return false;
|
||||
|
||||
// Use bar[1] (just-closed bar) as the retest candidate and bar[2] as context.
|
||||
// This mirrors Pine Script: decisions only on completed candles, never on bar[0]
|
||||
// which is only the opening price at new-bar time.
|
||||
double c0 = iClose(g_Symbol, PERIOD_CURRENT, 1); // just-closed bar
|
||||
double c1 = iClose(g_Symbol, PERIOD_CURRENT, 2); // bar before it
|
||||
double h0 = iHigh(g_Symbol, PERIOD_CURRENT, 1);
|
||||
double l0 = iLow(g_Symbol, PERIOD_CURRENT, 1);
|
||||
|
||||
double orbMid = (g_Sessions[sessIdx].orbHigh + g_Sessions[sessIdx].orbLow) / 2.0;
|
||||
double orbHigh = g_Sessions[sessIdx].orbHigh;
|
||||
double orbLow = g_Sessions[sessIdx].orbLow;
|
||||
double orbMid = (orbHigh + orbLow) / 2.0;
|
||||
|
||||
// Bullish retest: standard ORB high retest OR mid-level retest
|
||||
// ----------------------------------------------------------------
|
||||
// BULLISH BREAKOUT (breakoutDir == 1)
|
||||
// ----------------------------------------------------------------
|
||||
if(g_Sessions[sessIdx].breakoutDir == 1)
|
||||
{
|
||||
// Standard: previous bar above ORB high, current bar wick dips to ORB high, closes back above
|
||||
bool retestHigh = (c1 > g_Sessions[sessIdx].orbHigh)
|
||||
&& (l0 <= g_Sessions[sessIdx].orbHigh)
|
||||
&& (c0 >= g_Sessions[sessIdx].orbHigh);
|
||||
// Mid retest: price bounced back down to ORB mid after breakout, closes back above mid
|
||||
bool retestMid = (c1 > orbMid)
|
||||
&& (l0 <= orbMid)
|
||||
&& (c0 > orbMid);
|
||||
// Standard: prev bar above ORB high, current bar wick dips to ORB high, closes back above
|
||||
bool retestHigh = (c1 > orbHigh) && (l0 <= orbHigh) && (c0 >= orbHigh);
|
||||
// Mid retest: price dipped to ORB mid, closes back above mid
|
||||
bool retestMid = (c1 > orbMid) && (l0 <= orbMid) && (c0 > orbMid);
|
||||
if(retestHigh || retestMid) { dir = 1; return true; }
|
||||
|
||||
// Sweep reversal: wick swept BELOW ORB high (back into range), closed between orbMid and orbHigh.
|
||||
// Sell-side liquidity grab above orbHigh failed → flip to bearish.
|
||||
bool sweepRev = (c1 > orbHigh) && (l0 < orbHigh) && (c0 < orbHigh) && (c0 >= orbMid);
|
||||
if(sweepRev)
|
||||
{
|
||||
g_Sessions[sessIdx].breakoutDir = -1;
|
||||
PrintFormat("NANDR EA: [%s] Bullish sweep reversal at orbHigh %.2f — flipping to BEARISH",
|
||||
g_Sessions[sessIdx].name, orbHigh);
|
||||
dir = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Full invalidation: closed below mid
|
||||
if(c0 < orbMid && c1 > orbMid)
|
||||
{
|
||||
PrintFormat("NANDR EA: [%s] Failed bullish retest (closed below mid %.2f)",
|
||||
g_Sessions[sessIdx].name, orbMid);
|
||||
g_Sessions[sessIdx].inBreakout = false;
|
||||
g_Sessions[sessIdx].breakoutDir = 0;
|
||||
}
|
||||
}
|
||||
// Bearish retest: standard ORB low retest OR mid-level retest
|
||||
// ----------------------------------------------------------------
|
||||
// BEARISH BREAKOUT (breakoutDir == -1)
|
||||
// ----------------------------------------------------------------
|
||||
else if(g_Sessions[sessIdx].breakoutDir == -1)
|
||||
{
|
||||
// Standard: previous bar below ORB low, current bar wick ticks back to ORB low, closes back below
|
||||
bool retestLow = (c1 < g_Sessions[sessIdx].orbLow)
|
||||
&& (h0 >= g_Sessions[sessIdx].orbLow)
|
||||
&& (c0 <= g_Sessions[sessIdx].orbLow);
|
||||
// Mid retest: price bounced above ORB low up to the mid, closes back below mid
|
||||
// This captures entries like a 14:45 retest of the opening-day ORB mid after the break
|
||||
bool retestMid = (c1 < orbMid)
|
||||
&& (h0 >= orbMid)
|
||||
&& (c0 < orbMid);
|
||||
// Standard: prev bar below ORB low, current bar wick ticks back to ORB low, closes back below
|
||||
bool retestLow = (c1 < orbLow) && (h0 >= orbLow) && (c0 <= orbLow);
|
||||
// Mid retest: price bounced back up to ORB mid, closes back below mid
|
||||
bool retestMid = (c1 < orbMid) && (h0 >= orbMid) && (c0 < orbMid);
|
||||
if(retestLow || retestMid) { dir = -1; return true; }
|
||||
}
|
||||
|
||||
// Failed retest — threshold raised to orbMid so a simple bounce above orbLow/below orbHigh
|
||||
// does NOT cancel the breakout prematurely; only a close through the mid invalidates it.
|
||||
if(g_Sessions[sessIdx].breakoutDir == 1
|
||||
&& c0 < orbMid && c1 > orbMid)
|
||||
{
|
||||
PrintFormat("NANDR EA: [%s] Failed bullish retest (closed below mid %.2f)",
|
||||
g_Sessions[sessIdx].name, orbMid);
|
||||
g_Sessions[sessIdx].inBreakout = false;
|
||||
g_Sessions[sessIdx].breakoutDir = 0;
|
||||
}
|
||||
else if(g_Sessions[sessIdx].breakoutDir == -1
|
||||
&& c0 > orbMid && c1 < orbMid)
|
||||
{
|
||||
PrintFormat("NANDR EA: [%s] Failed bearish retest (closed above mid %.2f)",
|
||||
g_Sessions[sessIdx].name, orbMid);
|
||||
g_Sessions[sessIdx].inBreakout = false;
|
||||
g_Sessions[sessIdx].breakoutDir = 0;
|
||||
// Sweep reversal: wick swept ABOVE ORB low (back into range), closed between orbLow and orbMid.
|
||||
// Buy-side liquidity sweep below orbLow failed → flip to bullish.
|
||||
// This is the "liquidity sweep + reversal" pattern (e.g. 18:15 retest after 17:45 breakout).
|
||||
// After this flip, subsequent mid retests are detected as BUY signals (Entry 2 scenario).
|
||||
bool sweepRev = (c1 < orbLow) && (h0 > orbLow) && (c0 > orbLow) && (c0 < orbHigh);
|
||||
if(sweepRev)
|
||||
{
|
||||
g_Sessions[sessIdx].breakoutDir = 1;
|
||||
PrintFormat("NANDR EA: [%s] Bearish sweep reversal at orbLow %.2f — flipping to BULLISH",
|
||||
g_Sessions[sessIdx].name, orbLow);
|
||||
dir = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Full invalidation: closed above mid
|
||||
if(c0 > orbMid && c1 < orbMid)
|
||||
{
|
||||
PrintFormat("NANDR EA: [%s] Failed bearish retest (closed above mid %.2f)",
|
||||
g_Sessions[sessIdx].name, orbMid);
|
||||
g_Sessions[sessIdx].inBreakout = false;
|
||||
g_Sessions[sessIdx].breakoutDir = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -906,7 +927,7 @@ void OpenTrade(int dir, int sessIdx, double orbLevel, double obTop, double obBot
|
||||
{
|
||||
if(g_TradingHalted) return;
|
||||
if(g_TodayTrades >= InpMaxTradesPerDay) return;
|
||||
if(g_Sessions[sessIdx].tradesThisSession >= InpMaxPosPerSession) return;
|
||||
if(g_Sessions[sessIdx].tradesThisSession >= InpMaxRetestsPerSession) return;
|
||||
if(CountOpenPositions() + CountPendingOrders() >= InpMaxPosPerSession) return;
|
||||
|
||||
double ask = SymbolInfoDouble(g_Symbol, SYMBOL_ASK);
|
||||
@@ -942,8 +963,10 @@ void OpenTrade(int dir, int sessIdx, double orbLevel, double obTop, double obBot
|
||||
{
|
||||
g_TodayTrades++;
|
||||
g_Sessions[sessIdx].tradesThisSession++;
|
||||
g_Sessions[sessIdx].inBreakout = false;
|
||||
g_Sessions[sessIdx].inRetest = false;
|
||||
// Do NOT clear inBreakout — the ORB level remains valid for subsequent retests
|
||||
// until the breakout expires, fails, or the daily reset. This allows the EA to
|
||||
// take multiple retest entries on the same level (e.g. 18:45 AND 20:45 retests).
|
||||
// Re-entry is naturally gated by CountOpenPositions() checked at the top of OpenTrade.
|
||||
PrintFormat("NANDR EA: Trade opened. Dir=%s Lots=%.2f Entry=%.2f SL=%.2f TP=%.2f Session=%s",
|
||||
(dir > 0 ? "BUY" : "SELL"), lots, entry, sl, tp, g_Sessions[sessIdx].name);
|
||||
if(InpShowTradeLabels)
|
||||
@@ -1057,7 +1080,9 @@ void CheckEntrySignals()
|
||||
for(int s = 0; s < g_SessionCount; s++)
|
||||
{
|
||||
if(!g_Sessions[s].enabled || !g_Sessions[s].orbComplete) continue;
|
||||
if(g_Sessions[s].tradesThisSession >= InpMaxPosPerSession) continue;
|
||||
// Guard: allow up to InpMaxRetestsPerSession retest entries on the same breakout level.
|
||||
// Previously used InpMaxPosPerSession (=1) which permanently blocked the second retest.
|
||||
if(g_Sessions[s].tradesThisSession >= InpMaxRetestsPerSession) continue;
|
||||
if(!g_Sessions[s].inBreakout) continue;
|
||||
|
||||
// --- Breakout expiry ---
|
||||
|
||||
Reference in New Issue
Block a user