fix: ORB range = opening price only, Strict filter structurally impossible for ORB breakouts
This commit is contained in:
+76
-30
@@ -84,8 +84,10 @@ input double InpOBProximityPips = 50.0; // OB Proximity (pips)
|
||||
|
||||
// --- Entry Settings ---
|
||||
input group "═══ Entry Settings ═══"
|
||||
input ENUM_ENTRY_MODE InpEntryMode = ENTRY_MARKET; // Entry Mode
|
||||
input bool InpUseStrictFilter = true; // Use Strict Breakout Filter
|
||||
input ENUM_ENTRY_MODE InpEntryMode = ENTRY_MARKET; // Entry Mode
|
||||
input bool InpUseStrictFilter = false; // Use Strict Breakout Filter (false=simple close-cross)
|
||||
input bool InpWaitForRetest = false; // Wait for ORB Retest Before Entry
|
||||
input int InpBreakoutExpireBars = 8; // Bars Before Breakout Expires (0=never)
|
||||
|
||||
// --- Lot Size & Risk ---
|
||||
input group "═══ Lot Size & Risk ═══"
|
||||
@@ -438,28 +440,26 @@ bool IsWithinOrbWindow(const SSession &sess, datetime barTime)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update ORB for all sessions on new bar |
|
||||
//| IMPORTANT: always reads bar[1] (the just-CLOSED candle) so the |
|
||||
//| true high/low is captured. bar[0] at new-bar time = open only. |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateORBSessions()
|
||||
{
|
||||
datetime barTime = iTime(g_Symbol, PERIOD_CURRENT, 0);
|
||||
double barHigh = iHigh(g_Symbol, PERIOD_CURRENT, 0);
|
||||
double barLow = iLow(g_Symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(barTime, dt);
|
||||
datetime bar1Time = iTime(g_Symbol, PERIOD_CURRENT, 1);
|
||||
double bar1High = iHigh(g_Symbol, PERIOD_CURRENT, 1);
|
||||
double bar1Low = iLow(g_Symbol, PERIOD_CURRENT, 1);
|
||||
|
||||
for(int s = 0; s < g_SessionCount; s++)
|
||||
{
|
||||
if(!g_Sessions[s].enabled) continue;
|
||||
|
||||
// Session open bar starts accumulation
|
||||
if(IsSessionOpenBar(g_Sessions[s], barTime))
|
||||
// Session open bar just closed: bar[1] time matches session start
|
||||
if(IsSessionOpenBar(g_Sessions[s], bar1Time))
|
||||
{
|
||||
// Reset session state for new day's ORB
|
||||
g_Sessions[s].orbHigh = barHigh;
|
||||
g_Sessions[s].orbLow = barLow;
|
||||
g_Sessions[s].orbHigh = bar1High;
|
||||
g_Sessions[s].orbLow = bar1Low;
|
||||
g_Sessions[s].orbBarCount = 1;
|
||||
g_Sessions[s].orbStartTime = barTime;
|
||||
g_Sessions[s].orbStartTime = bar1Time;
|
||||
g_Sessions[s].orbComplete = (g_OrbBarsNeeded == 1);
|
||||
g_Sessions[s].breakoutDir = 0;
|
||||
g_Sessions[s].inBreakout = false;
|
||||
@@ -467,17 +467,20 @@ void UpdateORBSessions()
|
||||
g_Sessions[s].breakoutBarsAgo = 0;
|
||||
g_Sessions[s].tradesThisSession= 0;
|
||||
|
||||
if(g_Sessions[s].orbComplete)
|
||||
PrintFormat("NANDR EA: [%s] ORB complete. High=%.2f Low=%.2f",
|
||||
g_Sessions[s].name, bar1High, bar1Low);
|
||||
if(InpShowORBLines)
|
||||
DrawORBLines(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Still accumulating (M5 or M1)
|
||||
// Still accumulating (M5 or M1): append bar[1] closed data to range
|
||||
if(!g_Sessions[s].orbComplete && g_Sessions[s].orbBarCount > 0
|
||||
&& IsWithinOrbWindow(g_Sessions[s], barTime))
|
||||
&& IsWithinOrbWindow(g_Sessions[s], bar1Time))
|
||||
{
|
||||
g_Sessions[s].orbHigh = MathMax(g_Sessions[s].orbHigh, barHigh);
|
||||
g_Sessions[s].orbLow = MathMin(g_Sessions[s].orbLow, barLow);
|
||||
g_Sessions[s].orbHigh = MathMax(g_Sessions[s].orbHigh, bar1High);
|
||||
g_Sessions[s].orbLow = MathMin(g_Sessions[s].orbLow, bar1Low);
|
||||
g_Sessions[s].orbBarCount++;
|
||||
if(g_Sessions[s].orbBarCount >= g_OrbBarsNeeded)
|
||||
{
|
||||
@@ -489,7 +492,11 @@ void UpdateORBSessions()
|
||||
DrawORBLines(s);
|
||||
}
|
||||
|
||||
// Track breakout bar age
|
||||
// Track breakout bar age (only while breakout is active)
|
||||
// NOTE: incremented here before DetectBreakouts, so newly-detected
|
||||
// breakouts start at 0 here but will be seen as 0 in CheckEntrySignals
|
||||
// because DetectBreakouts resets it to 0 AFTER this runs.
|
||||
// The entry guard uses <=1 to handle both cases safely.
|
||||
if(g_Sessions[s].inBreakout)
|
||||
g_Sessions[s].breakoutBarsAgo++;
|
||||
}
|
||||
@@ -924,20 +931,59 @@ void CheckEntrySignals()
|
||||
{
|
||||
if(!g_Sessions[s].enabled || !g_Sessions[s].orbComplete) continue;
|
||||
if(g_Sessions[s].tradesThisSession >= InpMaxPosPerSession) continue;
|
||||
if(!g_Sessions[s].inBreakout) continue;
|
||||
|
||||
int dir = 0;
|
||||
if(!DetectRetest(s, dir)) continue;
|
||||
|
||||
double obTop = 0, obBottom = 0;
|
||||
bool obFound = IsOBNearLevel(
|
||||
(dir > 0) ? g_Sessions[s].orbHigh : g_Sessions[s].orbLow,
|
||||
dir, obTop, obBottom);
|
||||
|
||||
// If OB required and not found, skip
|
||||
if(InpOBRequireConf && !obFound) continue;
|
||||
// --- Breakout expiry ---
|
||||
if(InpBreakoutExpireBars > 0
|
||||
&& g_Sessions[s].breakoutBarsAgo >= InpBreakoutExpireBars)
|
||||
{
|
||||
PrintFormat("NANDR EA: [%s] Breakout expired after %d bars. Resetting.",
|
||||
g_Sessions[s].name, g_Sessions[s].breakoutBarsAgo);
|
||||
g_Sessions[s].inBreakout = false;
|
||||
g_Sessions[s].breakoutDir = 0;
|
||||
g_Sessions[s].inRetest = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
int dir = g_Sessions[s].breakoutDir;
|
||||
double orbLevel = (dir > 0) ? g_Sessions[s].orbHigh : g_Sessions[s].orbLow;
|
||||
OpenTrade(dir, s, orbLevel, obTop, obBottom);
|
||||
|
||||
// --- LIMIT mode: place order immediately on breakout bar ---
|
||||
if(InpEntryMode == ENTRY_LIMIT)
|
||||
{
|
||||
// Place once — on the bar breakout is first detected (age 0 or 1 due to update order)
|
||||
if(g_Sessions[s].breakoutBarsAgo <= 1)
|
||||
{
|
||||
double obTop = 0, obBottom = 0;
|
||||
IsOBNearLevel(orbLevel, dir, obTop, obBottom);
|
||||
if(InpOBRequireConf && obTop == 0) continue;
|
||||
OpenTrade(dir, s, orbLevel, obTop, obBottom);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- MARKET mode ---
|
||||
if(!InpWaitForRetest)
|
||||
{
|
||||
// Direct entry on the bar the breakout is confirmed (age 0 or 1)
|
||||
if(g_Sessions[s].breakoutBarsAgo <= 1)
|
||||
{
|
||||
double obTop = 0, obBottom = 0;
|
||||
IsOBNearLevel(orbLevel, dir, obTop, obBottom);
|
||||
if(InpOBRequireConf && obTop == 0) continue;
|
||||
OpenTrade(dir, s, orbLevel, obTop, obBottom);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retest-based entry: wait for price to pull back to ORB level
|
||||
int retestDir = 0;
|
||||
if(!DetectRetest(s, retestDir)) continue;
|
||||
double obTop = 0, obBottom = 0;
|
||||
IsOBNearLevel(orbLevel, retestDir, obTop, obBottom);
|
||||
if(InpOBRequireConf && obTop == 0) continue;
|
||||
OpenTrade(retestDir, s, orbLevel, obTop, obBottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user