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 |
//+------------------------------------------------------------------+
#property copyright "NANDR"
#property version "1.33"
#property version "1.34"
#property strict
#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 bool InpUseOBRetestEntry = true; // OB Retest Entry (enter on OB boundary retest)
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 ---
input group "═══ Lot Size & Risk ═══"
@@ -155,19 +156,21 @@ struct SSession
int breakoutBarsAgo;
int tradesThisSession;
bool retestFiredThisBar; // blocks duplicate entries on consecutive ticks within same bar
void Reset()
{
orbHigh = 0;
orbLow = DBL_MAX;
orbBarCount = 0;
orbComplete = false;
orbStartTime = 0;
breakoutDir = 0;
inBreakout = false;
inRetest = false;
breakoutBarsAgo= 0;
tradesThisSession = 0;
orbHigh = 0;
orbLow = DBL_MAX;
orbBarCount = 0;
orbComplete = false;
orbStartTime = 0;
breakoutDir = 0;
inBreakout = false;
inRetest = false;
breakoutBarsAgo = 0;
tradesThisSession = 0;
retestFiredThisBar = false;
}
};
@@ -209,6 +212,11 @@ SOrderBlock g_BearOBs[];
int g_BullOBCount = 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
datetime g_LastDayReset = 0;
int g_TodayTrades = 0;
@@ -466,6 +474,10 @@ void UpdateORBSessions()
double bar1High = iHigh(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++)
{
if(!g_Sessions[s].enabled) continue;
@@ -590,19 +602,35 @@ void DetectBreakouts()
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].inBreakout = true;
g_Sessions[s].breakoutBarsAgo= 0;
PrintFormat("NANDR EA: [%s] Bullish ORB breakout at %.2f",
g_Sessions[s].name, g_Sessions[s].orbHigh);
if(g_GlobalBiasDir == 0) g_GlobalBiasDir = 1; // first breakout of the day sets bias
PrintFormat("NANDR EA: [%s] Bullish ORB breakout at %.2f [DayBias=%d]",
g_Sessions[s].name, g_Sessions[s].orbHigh, g_GlobalBiasDir);
}
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].inBreakout = true;
g_Sessions[s].breakoutBarsAgo= 0;
PrintFormat("NANDR EA: [%s] Bearish ORB breakout at %.2f",
g_Sessions[s].name, g_Sessions[s].orbLow);
if(g_GlobalBiasDir == 0) g_GlobalBiasDir = -1; // first breakout of the day sets bias
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;
// 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;
IsOBNearLevel(orbLevel, dir, obTop, obBottom);
if(InpOBRequireConf && obTop == 0) continue;
@@ -1066,8 +1098,9 @@ void CheckRetestEntriesTick()
if(!InpUseOBRetestEntry) return;
// ── Bearish OB Retest → SELL at wick touch ───────────────────────
// Fires the moment the current bar's live high reaches the OB bottom.
// prev bar[1] (or current open) must have been at/below OB bottom.
// Suppressed when day bias is bullish (session takes priority).
if(InpUseSessionBiasFilter && g_GlobalBiasDir == 1) return;
for(int i = 0; i < g_BearOBCount; i++)
{
if(!g_BearOBs[i].active || g_BearOBs[i].traded) continue;
@@ -1104,6 +1137,9 @@ void CheckRetestEntriesTick()
}
// ── 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++)
{
if(!g_BullOBs[i].active || g_BullOBs[i].traded) continue;
@@ -1275,6 +1311,7 @@ void CheckDailyReset()
g_TodayPnL = 0;
g_DayStartEquity = AccountInfoDouble(ACCOUNT_EQUITY);
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
for(int s = 0; s < g_SessionCount; s++)