diff --git a/NANDR_ORB_OB_EA.mq5 b/NANDR_ORB_OB_EA.mq5 index b37049f..e91cad4 100644 --- a/NANDR_ORB_OB_EA.mq5 +++ b/NANDR_ORB_OB_EA.mq5 @@ -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 @@ -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++)