fix: 3 duplicate trades in 1 second, London SELL fired against established bullish bias

This commit is contained in:
Naji El Chemaly
2026-06-09 09:57:15 +03:00
parent 38a6ceec69
commit 80b1ce8743
+54 -17
View File
@@ -5,7 +5,7 @@
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector | //| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
#property copyright "NANDR" #property copyright "NANDR"
#property version "1.33" #property version "1.34"
#property strict #property strict
#include <Trade\Trade.mqh> #include <Trade\Trade.mqh>
@@ -90,6 +90,7 @@ input bool InpWaitForRetest = true; // Wait for ORB Retest
input int InpBreakoutExpireBars = 20; // Bars Before Breakout Expires (0=never) input int InpBreakoutExpireBars = 20; // Bars Before Breakout Expires (0=never)
input bool InpUseOBRetestEntry = true; // OB Retest Entry (enter on OB boundary retest) input bool InpUseOBRetestEntry = true; // OB Retest Entry (enter on OB boundary retest)
input int InpMaxRetestsPerSession = 3; // Max Retest Entries Per Session (per breakout) input int InpMaxRetestsPerSession = 3; // Max Retest Entries Per Session (per breakout)
input bool InpUseSessionBiasFilter = true; // Session Bias Filter: block signals against day bias
// --- Lot Size & Risk --- // --- Lot Size & Risk ---
input group "═══ Lot Size & Risk ═══" input group "═══ Lot Size & Risk ═══"
@@ -155,19 +156,21 @@ struct SSession
int breakoutBarsAgo; int breakoutBarsAgo;
int tradesThisSession; int tradesThisSession;
bool retestFiredThisBar; // blocks duplicate entries on consecutive ticks within same bar
void Reset() void Reset()
{ {
orbHigh = 0; orbHigh = 0;
orbLow = DBL_MAX; orbLow = DBL_MAX;
orbBarCount = 0; orbBarCount = 0;
orbComplete = false; orbComplete = false;
orbStartTime = 0; orbStartTime = 0;
breakoutDir = 0; breakoutDir = 0;
inBreakout = false; inBreakout = false;
inRetest = false; inRetest = false;
breakoutBarsAgo= 0; breakoutBarsAgo = 0;
tradesThisSession = 0; tradesThisSession = 0;
retestFiredThisBar = false;
} }
}; };
@@ -209,6 +212,11 @@ SOrderBlock g_BearOBs[];
int g_BullOBCount = 0; int g_BullOBCount = 0;
int g_BearOBCount = 0; int g_BearOBCount = 0;
// Day bias: direction of the first confirmed ORB breakout of the day (0=none, 1=bull, -1=bear).
// When InpUseSessionBiasFilter=true, subsequent session breakouts and all OB entries that
// contradict this bias are suppressed, preventing counter-trend trades.
int g_GlobalBiasDir = 0;
// Daily stats // Daily stats
datetime g_LastDayReset = 0; datetime g_LastDayReset = 0;
int g_TodayTrades = 0; int g_TodayTrades = 0;
@@ -466,6 +474,10 @@ void UpdateORBSessions()
double bar1High = iHigh(g_Symbol, PERIOD_CURRENT, 1); double bar1High = iHigh(g_Symbol, PERIOD_CURRENT, 1);
double bar1Low = iLow(g_Symbol, PERIOD_CURRENT, 1); double bar1Low = iLow(g_Symbol, PERIOD_CURRENT, 1);
// New bar: clear per-bar retest flag so the next wick can trigger a fresh entry
for(int s = 0; s < g_SessionCount; s++)
g_Sessions[s].retestFiredThisBar = false;
for(int s = 0; s < g_SessionCount; s++) for(int s = 0; s < g_SessionCount; s++)
{ {
if(!g_Sessions[s].enabled) continue; if(!g_Sessions[s].enabled) continue;
@@ -590,19 +602,35 @@ void DetectBreakouts()
if(bullBO) if(bullBO)
{ {
// Bias filter: suppress if day bias is already bearish
if(InpUseSessionBiasFilter && g_GlobalBiasDir == -1)
{
PrintFormat("NANDR EA: [%s] Bullish breakout SUPPRESSED — day bias is BEARISH",
g_Sessions[s].name);
continue;
}
g_Sessions[s].breakoutDir = 1; g_Sessions[s].breakoutDir = 1;
g_Sessions[s].inBreakout = true; g_Sessions[s].inBreakout = true;
g_Sessions[s].breakoutBarsAgo= 0; g_Sessions[s].breakoutBarsAgo= 0;
PrintFormat("NANDR EA: [%s] Bullish ORB breakout at %.2f", if(g_GlobalBiasDir == 0) g_GlobalBiasDir = 1; // first breakout of the day sets bias
g_Sessions[s].name, g_Sessions[s].orbHigh); PrintFormat("NANDR EA: [%s] Bullish ORB breakout at %.2f [DayBias=%d]",
g_Sessions[s].name, g_Sessions[s].orbHigh, g_GlobalBiasDir);
} }
else if(bearBO) else if(bearBO)
{ {
// Bias filter: suppress if day bias is already bullish
if(InpUseSessionBiasFilter && g_GlobalBiasDir == 1)
{
PrintFormat("NANDR EA: [%s] Bearish breakout SUPPRESSED — day bias is BULLISH",
g_Sessions[s].name);
continue;
}
g_Sessions[s].breakoutDir = -1; g_Sessions[s].breakoutDir = -1;
g_Sessions[s].inBreakout = true; g_Sessions[s].inBreakout = true;
g_Sessions[s].breakoutBarsAgo= 0; g_Sessions[s].breakoutBarsAgo= 0;
PrintFormat("NANDR EA: [%s] Bearish ORB breakout at %.2f", if(g_GlobalBiasDir == 0) g_GlobalBiasDir = -1; // first breakout of the day sets bias
g_Sessions[s].name, g_Sessions[s].orbLow); PrintFormat("NANDR EA: [%s] Bearish ORB breakout at %.2f [DayBias=%d]",
g_Sessions[s].name, g_Sessions[s].orbLow, g_GlobalBiasDir);
} }
} }
} }
@@ -1056,6 +1084,10 @@ void CheckRetestEntriesTick()
if(dir == 0) continue; if(dir == 0) continue;
// One entry per wick touch per bar — prevents 3 duplicate trades on consecutive ticks
if(g_Sessions[s].retestFiredThisBar) continue;
g_Sessions[s].retestFiredThisBar = true;
double obTop = 0, obBottom = 0; double obTop = 0, obBottom = 0;
IsOBNearLevel(orbLevel, dir, obTop, obBottom); IsOBNearLevel(orbLevel, dir, obTop, obBottom);
if(InpOBRequireConf && obTop == 0) continue; if(InpOBRequireConf && obTop == 0) continue;
@@ -1066,8 +1098,9 @@ void CheckRetestEntriesTick()
if(!InpUseOBRetestEntry) return; if(!InpUseOBRetestEntry) return;
// ── Bearish OB Retest → SELL at wick touch ─────────────────────── // ── Bearish OB Retest → SELL at wick touch ───────────────────────
// Fires the moment the current bar's live high reaches the OB bottom. // Suppressed when day bias is bullish (session takes priority).
// prev bar[1] (or current open) must have been at/below OB bottom. if(InpUseSessionBiasFilter && g_GlobalBiasDir == 1) return;
for(int i = 0; i < g_BearOBCount; i++) for(int i = 0; i < g_BearOBCount; i++)
{ {
if(!g_BearOBs[i].active || g_BearOBs[i].traded) continue; if(!g_BearOBs[i].active || g_BearOBs[i].traded) continue;
@@ -1104,6 +1137,9 @@ void CheckRetestEntriesTick()
} }
// ── Bullish OB Retest → BUY at wick touch ──────────────────────── // ── Bullish OB Retest → BUY at wick touch ────────────────────────
// Suppressed when day bias is bearish (session takes priority).
if(InpUseSessionBiasFilter && g_GlobalBiasDir == -1) return;
for(int i = 0; i < g_BullOBCount; i++) for(int i = 0; i < g_BullOBCount; i++)
{ {
if(!g_BullOBs[i].active || g_BullOBs[i].traded) continue; if(!g_BullOBs[i].active || g_BullOBs[i].traded) continue;
@@ -1275,6 +1311,7 @@ void CheckDailyReset()
g_TodayPnL = 0; g_TodayPnL = 0;
g_DayStartEquity = AccountInfoDouble(ACCOUNT_EQUITY); g_DayStartEquity = AccountInfoDouble(ACCOUNT_EQUITY);
g_TradingHalted = false; g_TradingHalted = false;
g_GlobalBiasDir = 0; // clear day bias — first session breakout will re-establish it
// Reset all session ORB data for the new day // Reset all session ORB data for the new day
for(int s = 0; s < g_SessionCount; s++) for(int s = 0; s < g_SessionCount; s++)